diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index a1279de754437..0000000000000 --- a/.eslintignore +++ /dev/null @@ -1,13 +0,0 @@ -build/ -l10n/ -docs/ -node_modules/ -external/bcmaps/ -external/builder/fixtures/ -external/builder/fixtures_babel/ -external/quickjs/ -external/openjpeg/ -test/tmp/ -test/pdfs/ -web/locale/ -*~/ diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 87715ac69acd0..0000000000000 --- a/.eslintrc +++ /dev/null @@ -1,265 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module", - }, - - "plugins": [ - "import", - "json", - "no-unsanitized", - "perfectionist", - "unicorn", - ], - - "extends": [ - "plugin:json/recommended", - "plugin:prettier/recommended" - ], - - "env": { - "browser": true, - "es2022": true, - "worker": true, - }, - - "globals": { - "PDFJSDev": "readonly", - "__non_webpack_import__": "readonly", - }, - - "rules": { - // Plugins - "import/export": "error", - "import/exports-last": "error", - "import/extensions": ["error", "always", { "ignorePackages": true, }], - "import/first": "error", - "import/named": "error", - "import/no-cycle": "error", - "import/no-empty-named-blocks": "error", - "import/no-commonjs": "error", - "import/no-mutable-exports": "error", - "import/no-self-import": "error", - "import/no-unresolved": ["error", { - "ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web", "fluent-bundle", "fluent-dom"], - }], - "no-unsanitized/method": "error", - "no-unsanitized/property": "error", - "perfectionist/sort-exports": "error", - "perfectionist/sort-named-exports": "error", - "unicorn/no-abusive-eslint-disable": "error", - "unicorn/no-array-push-push": "error", - "unicorn/no-instanceof-array": "error", - "unicorn/no-invalid-remove-event-listener": "error", - "unicorn/no-new-buffer": "error", - "unicorn/no-typeof-undefined": ["error", { - "checkGlobalVariables": false, - }], - "unicorn/no-useless-promise-resolve-reject": "error", - "unicorn/no-useless-spread": "error", - "unicorn/prefer-array-find": "error", - "unicorn/prefer-array-flat": "error", - "unicorn/prefer-array-flat-map": "error", - "unicorn/prefer-array-index-of": "error", - "unicorn/prefer-array-some": "error", - "unicorn/prefer-at": "error", - "unicorn/prefer-date-now": "error", - "unicorn/prefer-dom-node-append": "error", - "unicorn/prefer-dom-node-remove": "error", - "unicorn/prefer-includes": "error", - "unicorn/prefer-logical-operator-over-ternary": "error", - "unicorn/prefer-modern-dom-apis": "error", - "unicorn/prefer-modern-math-apis": "error", - "unicorn/prefer-negative-index": "error", - "unicorn/prefer-optional-catch-binding": "error", - "unicorn/prefer-regexp-test": "error", - "unicorn/prefer-string-replace-all": "error", - "unicorn/prefer-string-starts-ends-with": "error", - "unicorn/prefer-ternary": ["error", "only-single-line"], - "unicorn/throw-new-error": "error", - - // Possible errors - "for-direction": "error", - "getter-return": "error", - "no-async-promise-executor": "error", - "no-cond-assign": ["error", "except-parens"], - "no-constant-condition": ["error", { "checkLoops": false, }], - "no-debugger": "error", - "no-dupe-args": "error", - "no-dupe-else-if": "error", - "no-dupe-keys": "error", - "no-duplicate-case": "error", - "no-empty": ["error", { "allowEmptyCatch": true, }], - "no-empty-character-class": "error", - "no-ex-assign": "error", - "no-extra-boolean-cast": "error", - "no-func-assign": "error", - "no-inner-declarations": ["error", "functions"], - "no-invalid-regexp": "error", - "no-irregular-whitespace": "error", - "no-loss-of-precision": "error", - "no-obj-calls": "error", - "no-promise-executor-return": "error", - "no-regex-spaces": "error", - "no-setter-return": "error", - "no-sparse-arrays": "error", - "no-template-curly-in-string": "error", - "no-unexpected-multiline": "error", - "no-unreachable": "error", - "no-unsafe-finally": "error", - "no-unsafe-negation": "error", - "no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }], - "no-unused-private-class-members": "error", - "use-isnan": ["error", { "enforceForIndexOf": true, }], - "valid-typeof": ["error", { "requireStringLiterals": true, }], - - // Best Practices - "accessor-pairs": ["error", { - "setWithoutGet": true, - "enforceForClassMembers": true, - }], - "consistent-return": "error", - "curly": ["error", "all"], - "default-case-last": "error", - "dot-notation": "error", - "eqeqeq": ["error", "always"], - "grouped-accessor-pairs": ["error", "getBeforeSet"], - "no-alert": "error", - "no-caller": "error", - "no-else-return": "error", - "no-empty-pattern": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-fallthrough": "error", - "no-floating-decimal": "error", - "no-global-assign": "error", - "no-implied-eval": "error", - "no-iterator": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-multi-str": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-symbol": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-octal": "error", - "no-redeclare": "error", - "no-return-await": "error", - "no-self-assign": "error", - "no-self-compare": "error", - "no-throw-literal": "error", - "no-unused-expressions": "error", - "no-unused-labels": "error", - "no-useless-call": "error", - "no-useless-catch": "error", - "no-useless-concat": "error", - "no-useless-escape": "error", - "no-useless-return": "error", - "prefer-promise-reject-errors": "error", - "prefer-spread": "error", - "wrap-iife": ["error", "any"], - "yoda": ["error", "never", { - "exceptRange": true, - }], - - // Strict Mode - "strict": ["off", "global"], - - // Variables - "no-delete-var": "error", - "no-label-var": "error", - "no-shadow": "error", - "no-shadow-restricted-names": "error", - "no-undef-init": "error", - "no-undef": ["error", { "typeof": true, }], - "no-unused-vars": ["error", { - "vars": "all", - "args": "none", - }], - "no-use-before-define": ["error", { - "functions": false, - "classes": false, - "variables": false, - }], - - // Stylistic Issues - "lines-between-class-members": ["error", "always"], - "max-len": ["error", { - "code": 1000, - "comments": 80, - "ignoreUrls": true - }], - "new-cap": ["error", { "newIsCap": true, "capIsNew": false, }], - "no-array-constructor": "error", - "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0, "maxBOF": 1, }], - "no-nested-ternary": "error", - "no-new-object": "error", - "no-restricted-syntax": ["error", - { - "selector": "BinaryExpression[operator='instanceof'][right.name='Object']", - "message": "Use `typeof` rather than `instanceof Object`.", - }, - { - "selector": "CallExpression[callee.name='assert'][arguments.length!=2]", - "message": "`assert()` must always be invoked with two arguments.", - }, - { - "selector": "CallExpression[callee.name='isCmd'][arguments.length<2]", - "message": "Use `instanceof Cmd` rather than `isCmd()` with one argument.", - }, - { - "selector": "CallExpression[callee.name='isDict'][arguments.length<2]", - "message": "Use `instanceof Dict` rather than `isDict()` with one argument.", - }, - { - "selector": "CallExpression[callee.name='isName'][arguments.length<2]", - "message": "Use `instanceof Name` rather than `isName()` with one argument.", - }, - { - "selector": "NewExpression[callee.name='Cmd']", - "message": "Use `Cmd.get()` rather than `new Cmd()`.", - }, - { - "selector": "NewExpression[callee.name='Name']", - "message": "Use `Name.get()` rather than `new Name()`.", - }, - { - "selector": "NewExpression[callee.name='Ref']", - "message": "Use `Ref.get()` rather than `new Ref()`.", - }, - ], - "no-unneeded-ternary": "error", - "operator-assignment": "error", - "prefer-exponentiation-operator": "error", - "spaced-comment": ["error", "always", { - "block": { - "balanced": true, - } - }], - - // ECMAScript 6 - "arrow-body-style": ["error", "as-needed"], - "constructor-super": "error", - "no-class-assign": "error", - "no-const-assign": "error", - "no-dupe-class-members": "error", - "no-duplicate-imports": "error", - "no-this-before-super": "error", - "no-useless-computed-key": "error", - "no-useless-constructor": "error", - "no-useless-rename": "error", - "no-var": "error", - "object-shorthand": ["error", "always", { - "avoidQuotes": true, - }], - "prefer-const": "error", - "require-yield": "error", - "sort-imports": ["error", { - "ignoreCase": true, - }], - "template-curly-spacing": ["error", "never"], - }, -} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 39ba6a835d228..31eae7c1ec07c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: Need help? url: https://github.com/mozilla/pdf.js/discussions diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b60e0a31b1d8f..1f3cadbd47de0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18, lts/*, 22, 23] + node-version: [20, 22, 23] steps: - name: Checkout repository diff --git a/.github/workflows/fluent_linter.yml b/.github/workflows/fluent_linter.yml index e3137415c8263..786c1c9413f03 100644 --- a/.github/workflows/fluent_linter.yml +++ b/.github/workflows/fluent_linter.yml @@ -27,10 +27,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Use Python 3.12 + - name: Use Python 3.13 uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install Fluent dependencies diff --git a/.github/workflows/font_tests.yml b/.github/workflows/font_tests.yml index ff8353b371ae8..5ec02590d8d87 100644 --- a/.github/workflows/font_tests.yml +++ b/.github/workflows/font_tests.yml @@ -48,10 +48,10 @@ jobs: - name: Install dependencies run: npm ci - - name: Use Python 3.12 + - name: Use Python 3.13 uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' cache: 'pip' - name: Install Fonttools diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index 43355323f071a..0000000000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM gitpod/workspace-full-vnc - -USER gitpod - -RUN sudo apt-get update && \ - sudo apt-get install -yq firefox && \ - sudo rm -rf /var/lib/apt/lists/* diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 648e81e3f5a08..0000000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,13 +0,0 @@ -image: - file: .gitpod.Dockerfile -tasks: - - command: | - gp await-port 8888 && gp preview $(gp url 8888)/web/viewer.html && echo '[{"name": "Firefox","path": "/usr/bin/firefox"}]' | jq '.' > test/resources/browser_manifests/browser_manifest.json - - - init: npm install -g gulp-cli && npm install - command: gulp server -ports: - - port: 8888 - onOpen: ignore - - port: 6080 - onOpen: ignore diff --git a/.prettierignore b/.prettierignore index e33594bd7c3f8..90371a11625bd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,7 @@ external/bcmaps/ external/builder/fixtures/ external/builder/fixtures_babel/ external/quickjs/ +test/stats/results/ test/tmp/ test/pdfs/ web/locale/ diff --git a/.stylelintignore b/.stylelintignore index e33594bd7c3f8..90371a11625bd 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -6,6 +6,7 @@ external/bcmaps/ external/builder/fixtures/ external/builder/fixtures_babel/ external/quickjs/ +test/stats/results/ test/tmp/ test/pdfs/ web/locale/ diff --git a/.svglintrc.js b/.svglintrc.js index 25dad02764f52..68cf2ce57b64e 100644 --- a/.svglintrc.js +++ b/.svglintrc.js @@ -18,4 +18,18 @@ export default { }, ], }, + ignore: [ + "build/**", + "l10n/**", + "docs/**", + "node_modules/**", + "external/bcmaps/**", + "external/builder/fixtures/**", + "external/builder/fixtures_babel/**", + "external/quickjs/**", + "test/tmp/**", + "test/pdfs/**", + "web/locale/**", + "*~/**", + ], }; diff --git a/README.md b/README.md index 74d8706bdacec..15adbd23408e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PDF.js [![Build Status](https://github.com/mozilla/pdf.js/workflows/CI/badge.svg?branch=master)](https://github.com/mozilla/pdf.js/actions?query=workflow%3ACI+branch%3Amaster) +# PDF.js [![CI](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml/badge.svg?query=branch%3Amaster)](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml?query=branch%3Amaster) [PDF.js](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5. @@ -57,9 +57,6 @@ all dependencies for PDF.js: $ npm install -> [!NOTE] -> On MacOS M1/M2 you may see some `node-gyp`-related errors when running `npm install`. This is because one of our dependencies, `"canvas"`, does not provide pre-built binaries for this platform and instead `npm` will try to build it from source. Please make sure to first install the necessary native dependencies using `brew`: https://github.com/Automattic/node-canvas#compiling. - Finally, you need to start a local web server as some browsers do not allow opening PDF files using a `file://` URL. Run: @@ -93,7 +90,7 @@ be loaded by `pdf.js`. The PDF.js files are large and should be minified for pro ## Using PDF.js in a web application To use PDF.js in a web application you can choose to use a pre-built version of the library -or to build it from source. We supply pre-built versions for usage with NPM and Bower under +or to build it from source. We supply pre-built versions for usage with NPM under the `pdfjs-dist` name. For more information and examples please refer to the [wiki page](https://github.com/mozilla/pdf.js/wiki/Setup-pdf.js-in-a-website) on this subject. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000000..954b23a050989 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,452 @@ +import globals from "globals"; + +import import_ from "eslint-plugin-import"; +import jasmine from "eslint-plugin-jasmine"; +import json from "eslint-plugin-json"; +import noUnsanitized from "eslint-plugin-no-unsanitized"; +import perfectionist from "eslint-plugin-perfectionist"; +import prettierRecommended from "eslint-plugin-prettier/recommended"; +import unicorn from "eslint-plugin-unicorn"; + +const jsFiles = folder => { + const prefix = folder === "." ? "" : folder + "/"; + return [prefix + "**/*.js", prefix + "**/*.jsm", prefix + "**/*.mjs"]; +}; + +// Include all files referenced in extensions/chromium/background.js +const chromiumExtensionServiceWorkerFiles = [ + "extensions/chromium/extension-router.js", + "extensions/chromium/options/migration.js", + "extensions/chromium/pdfHandler.js", + "extensions/chromium/preserve-referer.js", + "extensions/chromium/suppress-update.js", + "extensions/chromium/telemetry.js", +]; + +export default [ + { + ignores: [ + "**/build/", + "**/l10n/", + "**/docs/", + "**/node_modules/", + "external/bcmaps/", + "external/builder/fixtures/", + "external/builder/fixtures_babel/", + "external/quickjs/", + "external/openjpeg/", + "test/stats/results/", + "test/tmp/", + "test/pdfs/", + "web/locale/", + "web/wasm/", + "**/*~/", + ], + }, + + /* ======================================================================== *\ + Base configuration + \* ======================================================================== */ + + prettierRecommended, + { + files: ["**/*.json"], + ...json.configs.recommended, + }, + { + files: jsFiles("."), + ignores: chromiumExtensionServiceWorkerFiles, + languageOptions: { + globals: globals.browser, + }, + }, + { + files: jsFiles("."), + + plugins: { + import: import_.flatConfigs.recommended.plugins.import, + json, + "no-unsanitized": noUnsanitized, + perfectionist, + unicorn, + }, + + languageOptions: { + globals: { + ...globals.worker, + PDFJSDev: "readonly", + __non_webpack_import__: "readonly", + }, + + ecmaVersion: 2025, + sourceType: "module", + }, + + rules: { + "import/export": "error", + "import/exports-last": "error", + "import/extensions": ["error", "always", { ignorePackages: true }], + "import/first": "error", + "import/named": "error", + "import/no-cycle": "error", + "import/no-empty-named-blocks": "error", + "import/no-commonjs": "error", + "import/no-mutable-exports": "error", + "import/no-restricted-paths": [ + "error", + { + zones: [ + { + target: "./web", + from: "./src", + }, + ], + }, + ], + "import/no-self-import": "error", + "import/no-unresolved": [ + "error", + { + ignore: [ + "display", + "pdfjs", + "pdfjs-lib", + "pdfjs-web", + "web", + "fluent-bundle", + "fluent-dom", + // See https://github.com/firebase/firebase-admin-node/discussions/1359. + "eslint-plugin-perfectionist", + ], + }, + ], + "no-unsanitized/method": "error", + "no-unsanitized/property": "error", + "perfectionist/sort-exports": "error", + "perfectionist/sort-named-exports": "error", + "unicorn/no-abusive-eslint-disable": "error", + "unicorn/no-array-push-push": "error", + "unicorn/no-console-spaces": "error", + "unicorn/no-instanceof-builtins": "error", + "unicorn/no-invalid-remove-event-listener": "error", + "unicorn/no-new-buffer": "error", + "unicorn/no-single-promise-in-promise-methods": "error", + "unicorn/no-typeof-undefined": ["error", { checkGlobalVariables: false }], + "unicorn/no-useless-promise-resolve-reject": "error", + "unicorn/no-useless-spread": "error", + "unicorn/prefer-array-find": "error", + "unicorn/prefer-array-flat": "error", + "unicorn/prefer-array-flat-map": "error", + "unicorn/prefer-array-index-of": "error", + "unicorn/prefer-array-some": "error", + "unicorn/prefer-at": "error", + "unicorn/prefer-date-now": "error", + "unicorn/prefer-dom-node-append": "error", + "unicorn/prefer-dom-node-remove": "error", + "unicorn/prefer-includes": "error", + "unicorn/prefer-logical-operator-over-ternary": "error", + "unicorn/prefer-modern-dom-apis": "error", + "unicorn/prefer-modern-math-apis": "error", + "unicorn/prefer-negative-index": "error", + "unicorn/prefer-optional-catch-binding": "error", + "unicorn/prefer-regexp-test": "error", + "unicorn/prefer-string-replace-all": "error", + "unicorn/prefer-string-starts-ends-with": "error", + "unicorn/prefer-ternary": ["error", "only-single-line"], + "unicorn/throw-new-error": "error", + + // Possible errors + "for-direction": "error", + "getter-return": "error", + "no-async-promise-executor": "error", + "no-cond-assign": ["error", "except-parens"], + "no-constant-condition": ["error", { checkLoops: false }], + "no-debugger": "error", + "no-dupe-args": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": ["error", { allowEmptyCatch: true }], + "no-empty-character-class": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-func-assign": "error", + "no-inner-declarations": ["error", "functions"], + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-obj-calls": "error", + "no-promise-executor-return": "error", + "no-regex-spaces": "error", + "no-setter-return": "error", + "no-sparse-arrays": "error", + "no-template-curly-in-string": "error", + "no-unexpected-multiline": "error", + "no-unreachable": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unsafe-optional-chaining": [ + "error", + { disallowArithmeticOperators: true }, + ], + "no-unused-private-class-members": "error", + "use-isnan": ["error", { enforceForIndexOf: true }], + "valid-typeof": ["error", { requireStringLiterals: true }], + + // Best Practices + "accessor-pairs": [ + "error", + { setWithoutGet: true, enforceForClassMembers: true }, + ], + "consistent-return": "error", + curly: ["error", "all"], + "default-case-last": "error", + "dot-notation": "error", + eqeqeq: ["error", "always"], + "grouped-accessor-pairs": ["error", "getBeforeSet"], + "no-alert": "error", + "no-caller": "error", + "no-else-return": "error", + "no-empty-pattern": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-fallthrough": "error", + "no-floating-decimal": "error", + "no-global-assign": "error", + "no-implied-eval": "error", + "no-iterator": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-multi-str": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-symbol": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-octal": "error", + "no-redeclare": "error", + "no-return-await": "error", + "no-self-assign": "error", + "no-self-compare": "error", + "no-throw-literal": "error", + "no-unused-expressions": "error", + "no-unused-labels": "error", + "no-useless-call": "error", + "no-useless-catch": "error", + "no-useless-concat": "error", + "no-useless-escape": "error", + "no-useless-return": "error", + "prefer-promise-reject-errors": "error", + "prefer-spread": "error", + "wrap-iife": ["error", "any"], + yoda: ["error", "never", { exceptRange: true }], + + // Strict Mode + strict: ["off", "global"], + + // Variables + "no-delete-var": "error", + "no-label-var": "error", + "no-shadow": "error", + "no-shadow-restricted-names": "error", + "no-undef-init": "error", + "no-undef": ["error", { typeof: true }], + "no-unused-vars": ["error", { vars: "all", args: "none" }], + "no-use-before-define": [ + "error", + { functions: false, classes: false, variables: false }, + ], + + // Stylistic Issues + "lines-between-class-members": ["error", "always"], + "max-len": ["error", { code: 1000, comments: 80, ignoreUrls: true }], + "new-cap": ["error", { newIsCap: true, capIsNew: false }], + "no-array-constructor": "error", + "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 0, maxBOF: 1 }], + "no-nested-ternary": "error", + "no-new-object": "error", + "no-restricted-syntax": [ + "error", + { + selector: + "BinaryExpression[operator='instanceof'][right.name='Object']", + message: "Use `typeof` rather than `instanceof Object`.", + }, + { + selector: "CallExpression[callee.name='assert'][arguments.length!=2]", + message: "`assert()` must always be invoked with two arguments.", + }, + { + selector: "CallExpression[callee.name='isCmd'][arguments.length<2]", + message: + "Use `instanceof Cmd` rather than `isCmd()` with one argument.", + }, + { + selector: "CallExpression[callee.name='isDict'][arguments.length<2]", + message: + "Use `instanceof Dict` rather than `isDict()` with one argument.", + }, + { + selector: "CallExpression[callee.name='isName'][arguments.length<2]", + message: + "Use `instanceof Name` rather than `isName()` with one argument.", + }, + { + selector: "NewExpression[callee.name='Cmd']", + message: "Use `Cmd.get()` rather than `new Cmd()`.", + }, + { + selector: "NewExpression[callee.name='Name']", + message: "Use `Name.get()` rather than `new Name()`.", + }, + { + selector: "NewExpression[callee.name='Ref']", + message: "Use `Ref.get()` rather than `new Ref()`.", + }, + ], + "no-unneeded-ternary": "error", + "operator-assignment": "error", + "prefer-exponentiation-operator": "error", + "spaced-comment": ["error", "always", { block: { balanced: true } }], + + // ECMAScript 6 + "arrow-body-style": ["error", "as-needed"], + "constructor-super": "error", + "no-class-assign": "error", + "no-const-assign": "error", + "no-dupe-class-members": "error", + "no-duplicate-imports": "error", + "no-this-before-super": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-var": "error", + "object-shorthand": ["error", "always", { avoidQuotes: true }], + "prefer-const": "error", + "require-yield": "error", + "sort-imports": ["error", { ignoreCase: true }], + "template-curly-spacing": ["error", "never"], + }, + }, + { + files: jsFiles("src"), + rules: { + "no-console": "error", + }, + }, + + /* ======================================================================== *\ + Test-specific rules + \* ======================================================================== */ + + { + files: jsFiles("test"), + + plugins: { jasmine }, + + languageOptions: { + globals: { + ...globals.node, + ...globals.jasmine, + }, + }, + rules: { + ...jasmine.configs.recommended.rules, + "jasmine/new-line-before-expect": "off", + "jasmine/new-line-between-declarations": "off", + "jasmine/no-focused-tests": "error", + "jasmine/no-pending-tests": "off", + "jasmine/no-spec-dupes": ["error", "branch"], + "jasmine/no-suite-dupes": ["error", "branch"], + "jasmine/prefer-jasmine-matcher": "off", + "jasmine/prefer-toHaveBeenCalledWith": "off", + }, + }, + { + files: jsFiles("test/unit"), + rules: { + "import/no-unresolved": ["error", { ignore: ["pdfjs/"] }], + "no-console": ["error", { allow: ["warn", "error"] }], + }, + }, + { + files: jsFiles("test/integration"), + rules: { + "no-console": ["error", { allow: ["warn", "error"] }], + "no-restricted-syntax": [ + "error", + { + selector: "CallExpression[callee.name='waitForTimeout']", + message: + "`waitForTimeout` can cause intermittent failures and should not be used (see issue #17656 for replacements).", + }, + ], + }, + }, + + /* ======================================================================== *\ + External libraries + \* ======================================================================== */ + + { + files: jsFiles("external"), + + languageOptions: { globals: globals.node }, + }, + + /* ======================================================================== *\ + Examples + \* ======================================================================== */ + + { + files: jsFiles("examples"), + + languageOptions: { + globals: { + pdfjsImageDecoders: false, + pdfjsLib: false, + pdfjsViewer: false, + }, + }, + }, + { + files: [...jsFiles("examples/node"), ...jsFiles("examples/webpack")], + + languageOptions: { globals: globals.node }, + }, + + /* ======================================================================== *\ + Chromium extension + \* ======================================================================== */ + + { + files: jsFiles("extensions/chromium"), + + languageOptions: { + globals: globals.webextensions, + sourceType: "script", + }, + + rules: { + "no-var": "off", + }, + }, + { + files: chromiumExtensionServiceWorkerFiles, + + languageOptions: { + globals: globals.serviceworker, + sourceType: "script", + }, + }, + + /* ======================================================================== *\ + Other + \* ======================================================================== */ + { + files: ["gulpfile.mjs"], + languageOptions: { globals: globals.node }, + }, +]; diff --git a/examples/.eslintrc b/examples/.eslintrc deleted file mode 100644 index 433abdbfab2c4..0000000000000 --- a/examples/.eslintrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "globals": { - "pdfjsImageDecoders": false, - "pdfjsLib": false, - "pdfjsViewer": false, - }, -} diff --git a/examples/mobile-viewer/viewer.css b/examples/mobile-viewer/viewer.css index b58dc91419481..bd27157a46132 100644 --- a/examples/mobile-viewer/viewer.css +++ b/examples/mobile-viewer/viewer.css @@ -113,8 +113,8 @@ footer { background-color: rgb(0 0 0 / 0); font-size: 1.2rem; color: rgb(255 255 255 / 1); - background-image: url(images/div_line_left.png), - url(images/div_line_right.png); + background-image: + url(images/div_line_left.png), url(images/div_line_right.png); background-repeat: no-repeat; background-position: left, right; background-size: 0.2rem, 0.2rem; diff --git a/examples/mobile-viewer/viewer.mjs b/examples/mobile-viewer/viewer.mjs index fedd5d8e23ead..bf496c91e62aa 100644 --- a/examples/mobile-viewer/viewer.mjs +++ b/examples/mobile-viewer/viewer.mjs @@ -91,10 +91,10 @@ const PDFViewerApplication = { let key = "pdfjs-loading-error"; if (reason instanceof pdfjsLib.InvalidPDFException) { key = "pdfjs-invalid-file-error"; - } else if (reason instanceof pdfjsLib.MissingPDFException) { - key = "pdfjs-missing-file-error"; - } else if (reason instanceof pdfjsLib.UnexpectedResponseException) { - key = "pdfjs-unexpected-response-error"; + } else if (reason instanceof pdfjsLib.ResponseException) { + key = reason.missing + ? "pdfjs-missing-file-error" + : "pdfjs-unexpected-response-error"; } self.l10n.get(key).then(msg => { self.error(msg, { message: reason?.message }); diff --git a/examples/node/.eslintrc b/examples/node/.eslintrc deleted file mode 100644 index 46f120c21a4f5..0000000000000 --- a/examples/node/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "env": { - "node": true, - }, -} diff --git a/examples/node/pdf2png/README.md b/examples/node/pdf2png/README.md index 00b388b9f606e..8b114061b2361 100644 --- a/examples/node/pdf2png/README.md +++ b/examples/node/pdf2png/README.md @@ -9,9 +9,7 @@ Install the dependencies and build the PDF.js library: $ npm install $ gulp dist-install -Install the Node canvas library and run the example to convert the first page of a -PDF file to a PNG image: +Run the example to convert the first page of a PDF file to a PNG image: - $ npm install canvas $ cd examples/node/pdf2png - $ node pdf2png.js + $ node pdf2png.mjs diff --git a/examples/node/pdf2png/pdf2png.mjs b/examples/node/pdf2png/pdf2png.mjs index 359fc807bea97..ed01834b07571 100644 --- a/examples/node/pdf2png/pdf2png.mjs +++ b/examples/node/pdf2png/pdf2png.mjs @@ -57,7 +57,7 @@ try { const renderTask = page.render(renderContext); await renderTask.promise; // Convert the canvas to an image buffer. - const image = canvasAndContext.canvas.toBuffer(); + const image = canvasAndContext.canvas.toBuffer("image/png"); fs.writeFile("output.png", image, function (error) { if (error) { console.error("Error: " + error); diff --git a/examples/webpack/.eslintrc b/examples/webpack/.eslintrc deleted file mode 100644 index 46f120c21a4f5..0000000000000 --- a/examples/webpack/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "env": { - "node": true, - }, -} diff --git a/extensions/chromium/.eslintrc b/extensions/chromium/.eslintrc deleted file mode 100644 index dd74b7b7c64ab..0000000000000 --- a/extensions/chromium/.eslintrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "extends": [ - ../../.eslintrc - ], - - "env": { - "webextensions": true - }, - - "parserOptions": { - "sourceType": "script" - }, - - "rules": { - "no-var": "off", - }, - - "overrides": [ - { - // Include all files referenced in background.js - "files": [ - "options/migration.js", - "preserve-referer.js", - "pdfHandler.js", - "extension-router.js", - "suppress-update.js", - "telemetry.js" - ], - "env": { - // Background script is a service worker. - "browser": false, - "serviceworker": true - } - } - ] -} diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index 886cc87b44029..54e5b2897e71c 100644 --- a/extensions/chromium/preferences_schema.json +++ b/extensions/chromium/preferences_schema.json @@ -71,6 +71,10 @@ "type": "string", "default": "" }, + "enableSignatureEditor": { + "type": "boolean", + "default": false + }, "enableUpdatedAddImage": { "type": "boolean", "default": false @@ -220,6 +224,11 @@ "description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode", "type": "string", "default": "CanvasText" + }, + "enableAutoLinking": { + "description": "Enable creation of hyperlinks from text that look like URLs.", + "type": "boolean", + "default": false } } } diff --git a/external/.eslintrc b/external/.eslintrc deleted file mode 100644 index e3523dce526ed..0000000000000 --- a/external/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - ../.eslintrc - ], - - "env": { - "node": true, - }, -} diff --git a/external/builder/babel-plugin-pdfjs-preprocessor.mjs b/external/builder/babel-plugin-pdfjs-preprocessor.mjs index 8f8be7a724e75..ff92a7a765022 100644 --- a/external/builder/babel-plugin-pdfjs-preprocessor.mjs +++ b/external/builder/babel-plugin-pdfjs-preprocessor.mjs @@ -1,10 +1,7 @@ import { types as t, transformSync } from "@babel/core"; -import fs from "fs"; -import { join as joinPaths } from "path"; import vm from "vm"; const PDFJS_PREPROCESSOR_NAME = "PDFJSDev"; -const ROOT_PREFIX = "$ROOT/"; function isPDFJSPreprocessor(obj) { return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME; @@ -40,18 +37,6 @@ function handlePreprocessorAction(ctx, actionName, args, path) { return result; } break; - case "json": - if (!t.isStringLiteral(arg)) { - throw new Error("Path to JSON is not provided"); - } - let jsonPath = arg.value; - if (jsonPath.startsWith(ROOT_PREFIX)) { - jsonPath = joinPaths( - ctx.rootPath, - jsonPath.substring(ROOT_PREFIX.length) - ); - } - return JSON.parse(fs.readFileSync(jsonPath, "utf8")); } throw new Error("Unsupported action"); } catch (e) { diff --git a/external/builder/fixtures_babel/evals-expected.js b/external/builder/fixtures_babel/evals-expected.js index 510e62f1bb523..71eee73d1e89d 100644 --- a/external/builder/fixtures_babel/evals-expected.js +++ b/external/builder/fixtures_babel/evals-expected.js @@ -10,9 +10,6 @@ var g = { }, j: 2 }; -var h = { - test: "test" -}; var i = '0'; var j = { i: 1 diff --git a/external/builder/fixtures_babel/evals.js b/external/builder/fixtures_babel/evals.js index 94ec543b04d19..cabeb0746a949 100644 --- a/external/builder/fixtures_babel/evals.js +++ b/external/builder/fixtures_babel/evals.js @@ -5,7 +5,6 @@ var d = PDFJSDev.test('FALSE'); var e = PDFJSDev.eval('TRUE'); var f = PDFJSDev.eval('TEXT'); var g = PDFJSDev.eval('OBJ'); -var h = PDFJSDev.json('$ROOT/external/builder/fixtures_babel/evals.json'); var i = typeof PDFJSDev === 'undefined' ? PDFJSDev.eval('FALSE') : '0'; var j = typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('OBJ.obj') : '0'; var k = !PDFJSDev.test('TRUE'); diff --git a/external/builder/fixtures_babel/evals.json b/external/builder/fixtures_babel/evals.json deleted file mode 100644 index 000642c425cbf..0000000000000 --- a/external/builder/fixtures_babel/evals.json +++ /dev/null @@ -1 +0,0 @@ -{ "test": "test" } \ No newline at end of file diff --git a/external/openjpeg/LICENSE_OPENJPEG b/external/openjpeg/LICENSE_OPENJPEG new file mode 100644 index 0000000000000..e8fa41040dfbe --- /dev/null +++ b/external/openjpeg/LICENSE_OPENJPEG @@ -0,0 +1,39 @@ +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium + * Copyright (c) 2002-2014, Professor Benoit Macq + * Copyright (c) 2003-2014, Antonin Descampe + * Copyright (c) 2003-2009, Francois-Olivier Devaux + * Copyright (c) 2005, Herve Drolon, FreeImage Team + * Copyright (c) 2002-2003, Yannick Verschueren + * Copyright (c) 2001-2003, David Janssens + * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France + * Copyright (c) 2012, CS Systemes d'Information, France + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/external/openjpeg/LICENSE_PDFJS_OPENJPEG b/external/openjpeg/LICENSE_PDFJS_OPENJPEG new file mode 100644 index 0000000000000..623929b849547 --- /dev/null +++ b/external/openjpeg/LICENSE_PDFJS_OPENJPEG @@ -0,0 +1,22 @@ +Copyright (c) 2024, Mozilla Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/openjpeg/openjpeg.js b/external/openjpeg/openjpeg.js index 52196d07941d9..535c2539c0284 100644 --- a/external/openjpeg/openjpeg.js +++ b/external/openjpeg/openjpeg.js @@ -1,12 +1,12 @@ - +/* THIS FILE IS GENERATED - DO NOT EDIT */ var OpenJPEG = (() => { var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; return ( -function(moduleArg = {}) { +async function(moduleArg = {}) { var moduleRtn; -var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;Module.decode=function(bytes,{numComponents:numComponents=4,isIndexedColormap:isIndexedColormap=false,smaskInData:smaskInData=false}){const size=bytes.length;const ptr=Module._malloc(size);Module.HEAPU8.set(bytes,ptr);const ret=Module._jp2_decode(ptr,size,numComponents>0?numComponents:0,!!isIndexedColormap,!!smaskInData);Module._free(ptr);if(ret){const{errorMessages:errorMessages}=Module;if(errorMessages){delete Module.errorMessages;return errorMessages}return"Unknown error"}const{imageData:imageData}=Module;Module.imageData=null;return imageData};var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";var read_,readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))}).then(onload,onerror)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];function intArrayFromBase64(s){var decoded=atob(s);var bytes=new Uint8Array(decoded.length);for(var i=0;ifilename.startsWith(dataURIPrefix);function findWasmBinary(){var f="data:application/octet-stream;base64,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A0kNAAsLAkAgA0F8cSIEQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgACADQQRrIgRLBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAswAQF/AkAgAEUNACABRQ0AQQggACABbCIBECUiAARAIABBACABEBUaCyAAIQILIAILEQAgAEUEQEEADwtBCCAAECUL8gICAn8BfgJAIAJFDQAgACABOgAAIAAgAmoiA0EBayABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBA2sgAToAACADQQJrIAE6AAAgAkEHSQ0AIAAgAToAAyADQQRrIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBBGsgATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQQhrIAE2AgAgAkEMayABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkEQayABNgIAIAJBFGsgATYCACACQRhrIAE2AgAgAkEcayABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa1CgYCAgBB+IQUgAyAEaiEBA0AgASAFNwMYIAEgBTcDECABIAU3AwggASAFNwMAIAFBIGohASACQSBrIgJBH0sNAAsLIAALJwEBfyMAQRBrIgMkACADIAI2AgwgACABIAJBAEEAEGsgA0EQaiQAC+gFAQl/IAFFBEBBAA8LAn8gAEUE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ACAEIE5BCmpBAiAFEBpBAkcEQCAFQQFBAiAAKAK4ARtBlhJBABAPIAAoArgBRSFnDAELIE5BCmogTkEMakECEBEgTigCDCIBQZD/A0YNACABQdn/A0YEQCAAQYACNgIIIABBADYCzAEMAQsgBCkDCCKGAVAEfkIABSCGASAEKQM4fQtQBEAgAEHAADYCCCAFQQJBrD9BABAPDAELQQAhZyAFQQFB7D5BABAPCyBOQRBqJAAgZwsLACAABEAgABAQCwu0AQEBfyAAKAIMRQRAIAIgACgCJCABEQMADwsCQEEIEBQiA0UNACADIAI2AgQgAyABNgIAQQgQFCIBRQRAIAMQEA8LIAEgAzYCACAAIAAoAgRB5ABsIgI2AigDQCAAKAIYIAJKDQALIAEgACgCFDYCBCAAIAE2AhQgACAAKAIYQQFqNgIYIAAoAhwiAUUNACABKAIAQQA2AgggACABKAIENgIcIAAgACgCIEEBazYCICABEBALC/oCAQR/AkAgAEUNACAAKAKsKCIBBEAgACgCqCgiAgRAQQAhAQNAIAAoAqwoIAFBA3RqKAIAIgMEQCADEBAgACgCqCghAgsgAUEBaiIBIAJJDQALIAAoAqwoIQELIABBADYCqCggARAQIABBADYCrCgLIAAoArQoIgEEQCABEBAgAEEANgK0KAsgACgC0CsiAQRAIAEQECAAQQA2AtArCyAAKALsKyIBBEAgARAQIABBADYC7CsLIAAoAugrIgEEQCABEBAgAEEANgLoKwsgACgC/CsiAQRAIAEQECAAQQA2AoQsIABCADcC/CsLIAAoAvArIgEEQCAAKAL0KyIDBH9BACECA0AgASgCDCIEBEAgBBAQIAFBADYCDCAAKAL0KyEDCyABQRRqIQEgAkEBaiICIANJDQALIAAoAvArBSABCxAQIABBADYC8CsLIAAoAuQrIgEEQCABEBAgAEEANgLkKwsgACgC3CsiAUUNACABEBAgAEIANwLcKwsLyAcCEX8BfiAAKAIQIghBIE8EQCAAKQMIpw8LAkAgACgCFCIDQQROBEAgACgCACICQQNrKAIAIQEgACADQQRrIgM2AhQgACACQQRrNgIADAELIANBAEwEQAwBCyADQQFxIQ0gACgCACECAkAgA0EBRgRAQRghBAwBCyADQf7///8HcSEJQRghBANAIAAgAkEB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IABBADYCiAEgACgCZBAQIABBADYCZCAALQC8AUECcUUEQCAAKAKoARAQCyAAQdAAakEAQfAAEBUaIAAoAsABEDIgAEEANgLAASAAKALEARAyIABBADYCwAEgACgCyAEiAQRAIAEoAhwiAgRAIAIQECABQQA2AhwLIAEoAigiAgRAIAEoAiQEQANAIAIgBUEobCIDaigCJCIEBEAgBBAQIAEoAigiAiADakEANgIkCyACIANqKAIQIgQEQCAEEBAgASgCKCICIANqQQA2AhALIAIgA2ooAhgiBARAIAQQECABKAIoIgIgA2pBADYCGAsgBUEBaiIFIAEoAiRJDQALCyACEBAgAUEANgIoCyABEBALIABBADYCyAEgACgCSBAhIABBADYCSCAAKAJMECEgAEEANgJMIAAoAtQBIgMEQAJAIAMoAghFDQAgAygCDARAIANBADYCKANAIAMoAhhBAEoNAAsLIANBATYCECADKAIAEBAgAygCHCICRQ0AA0AgAigCBCEBIAIQECADIAE2AhwgASICDQALCyADKAIkIgIEQCACKAIEIgVBAEoEQEEAIQEDQCACKAIAIAFBDGxqIgQoAggiBgRAIAQoAgQgBhECACACKAIEIQULIAFBAWoiASAFSA0ACwsgAigCABAQIAIQEAsgAxAQCyAAQQA2AtQBIAAQEAsL5gMCCH8EfiAAKAIUKAIAKAIUIAFBzABsaiIJKAIMIgggACgCGCgCGCABQTRsaiIKNQIEIhBCAX0iEiAANQI8fCAQgKciCyAIIAtJGyEMIAkoAggiCCAKNQIAIhFCAX0iEyAANQI4fCARgKciCiAIIApJGyEKIAkoAgQiCCASIAA1AjR8IBCApyILIAggC0sbIQsgCSgCACIIIBMgADUCMHwgEYCnIg0gCCANSxshDUEAIQggACgCICgC0CsgAUG4CGxqKAIUIQ4CQCAJKAIUQQAgAmtBfyACG2oiAkUEQCAKIQAgDSEIIAshAQwBCyADQQFxIAJBAWsiD3QiCSANSQRAIA0gCWutQn8gAq0iEIZCf4V8IBCIpyEIC0EAIQBBACEBIANBAXYgD3QiAyALSQRAIAsgA2utQn8gAq0iEIZCf4V8IBCIpyEBCyAJIApJBEAgCiAJa61CfyACrSIQhkJ/hXwgEIinIQALIAMgDE8EQEEAIQwMAQsgDCAD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IABBADYCPCAAQucHNwI0IABBADYCLCAAQucHNwIkIABBADYCHCAAQucHNwIUIABBADYCDCAAQucHNwIEIABBQGshACACQQRqIgIgA0cNAAsLIAFBA3EiAUUNAEEAIQIDQCAAQQA2AgwgAELnBzcCBCAAQRBqIQAgAkEBaiICIAFHDQALCwv7BQEQfyMAQYACayIIJAACf0EBQRQQEyIGRQRAIAJBAUH0MEEAEA9BAAwBCyAGIAE2AgQgBiAANgIAIAggATYCACAIIAA2AoABA0AgCCAFIg1BAWoiBUECdCIHaiABQQFqQQJtIgM2AgAgCEGAAWogB2ogAEEBakECbSIHNgIAIAQgACABbCIJaiEEIAMhASAHIQAgCUEBSw0ACyAGIAQ2AgggBEUEQCAGEBBBAAwBCyAGIARBEBATIgM2AgwgA0UEQCACQQFB2hpBABAPIAYQEEEADAELIAYgBigCCCILQQR0NgIQIAMhACANBEAgAyAGKAIEIAYoAgBsQQR0aiIEIQEDQAJAIAggDkECdCICaigCACIJQQBMDQAgCUEBayEMQQAhBwJAIAhBgAFqIAJqKAIAIgJBAEwEQEEAIQUgCUEBRwRAIAlB/v///wdxIQoDQCAFIAxGIQ8gBUECaiEFIAEgBCAPGyIEIQEgB0ECaiIHIApHDQALCyAJQQFxDQEgBCEBDAILA0AgBCEFIAIhBANAAkAgACABNgIAIARBAUYEQCAAQRBqIQAgAUEQaiEBDAELIAAgATYCECABQRBqIQEgAEEgaiEAIARBAkohECAEQQJrIQQgEA0BCwsgASAFIAJBBHRqIAcgByAMRnJBAXEiChshBCABIAUgChshASAHQQFqIgcgCUcNAAsMAQsgASAEIAJBBHRqIAUgBSAMRnJBAXEiBRshESABIAQgBRshASARIQQLIA5BAWoiDiANRw0ACwsgAEEANgIAAkAgC0UNACALQQRPBEAgC0F8cSEAQQAhAQNAIANBADYCPCADQucHNwI0IANBADYCLCADQucHNwIkIANBADYCHCADQucHNwIUIANBADYCDCADQucHNwIEIANBQGshAyABQQRqIgEgAEcNAAsLIAtBA3EiAEUNAEEAIQEDQCADQQA2AgwgA0LnBzcCBCADQRBqIQMgAUEBaiIBIABHDQALCyAGCyESIAhBgAJqJAAgEgtTAQF/An8gAC0ADEH/AUYEQCAAQoD+g4DwADcCDEEAIAAoAggiASAAKAIETw0BGiAAIAFBAWo2AgggACABLQAAQYD+A3I2AgwLIABBADYCEEEBCwt+AgF/AX4gAL0iA0I0iKdB/w9xIgJB/w9HBHwgAkUEQCABIABEAAAAAAAAAABhBH9BAAUgAEQAAAAAAADwQ6IgARBlIQAgASgCAEFAags2AgAgAA8LIAEgAkH+B2s2AgAgA0L/////////h4B/g0KAgICAgICA8D+EvwUgAAsLSQEBfwJAQQFBLBATIgEEQCABQQA2AhACQCAAQQBMBEAgAUEBQQgQEyIANgIkIABFDQEMAwsgAUEANgIMCyABEBALQQAhAQsgAQuRAgAgAEUEQEEADwsCfwJAIAFB/wBNDQACQEGU0AEoAgAoAgBFBEAgAUGAf3FBgL8DRg0CDAELIAFB/w9NBEAgACABQT9xQYABcjoAASAAIAFBBnZBwAFyOgAAQQIMAwsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMMAwsgAUGAgARrQf//P00EQCAAIAFBP3FBgAFyOgADIAAgAUESdkHwAXI6AAAgACABQQZ2QT9xQYABcjoAAiAAIAFBDHZBP3FBgAFyOgABQQQMAwsLQZTHAUEZNgIAQX8MAQsgACABOgAAQQELC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAwALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC3MBBn8gACgCACIDLAAAQTBrIgFBCUsE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AyAAQRh2NgIUIAMgBDYCECADIABBCHZB/wFxNgIcIAMgAEEQdkH/AXE2AhggAkEBQc31ACADQRBqEA8MBwsgBSAITQRAIAYhBAwECyAFIQggBiAFEBciBA0DIAYQECACQQFB/w9BABAPQQAMBwsgBEECcUUEQCACQQFBwg9BABAPIAYQEEEADAcLIAAgBEH/////B3I2AmQgASAHIAVrrSINIAIgASgCKBEIACANUQ0DIAAtAGRBCHFFDQEgAkECQZIcQQAQDwsgBhAQQQEMBQsgAkEBQZIcQQAQDyAGEBBBAAwECyABIAQgBSACEBogBUcEQCACQQFBxBxBABAPIAQQEEEADAQLIAAgBCIGIAUgAiAKKAIEEQEADQALIAQQEEEADAILIAJBAUGiJUEAEA9BAAwBCyAGEBBBAAshDCADQfAAaiQAIAwL5gEBBn8gACgCCEE1IAIQJARAAkAgACgCCCIGKAIAIQMgBigCCCEFAkACQAJ/AkAgAwRAQQEhBCADQQFxIQcgA0EBRw0BQQAMAgsgBkEANgIADAILIANBfnEhAwNAAn9BACAERQ0AGkEAIAAgASACIAUoAgARAABFDQAaIAAgASACIAUoAgQRAABBAEcLIQQgBUEIaiEFIAhBAmoiCCADRw0ACyAERQshAyAHBEAgAw0CIAAgASACIAUoAgARAABBAEchBAsgBkEANgIAIARFDQILIAAoAgAaQQEPCyAGQQA2AgALC0EACwoAIAAoAgAaQQALFAAgACgCACIABEAgACABNgK4AQsLIQAgACgCACABEFMgAEEAOgB8IAAgASgCuEBBAXE2AoABCzIAIAJFBEBBAA8LIAAoAgAgASACIAMQSEUEQCADQQFBqS9BABAPQQAPCyAAIAIgAxBxC2kCAn8BfCMAQRBrIgMkACACBEADQCAAIANBCGoQRCABAn8gAysDCCIFmUQAAAAAAADgQWMEQCAFqgwBC0GAgICAeAs2AgAgAUEEaiEBIABBCGohACAEQQFqIgQgAkcNAAsLIANBEGokAAuEAQICfwF9IwBBEGsiAyQAIAIEQANAIAMgAC0AADoADyADIAAtAAE6AA4gAyAALQACOgANIAMgAC0AAzoADCABAn8gAyoCDCIFi0MAAABPXQRAIAWoDAELQYCAgIB4CzYCACABQQRqIQEgAEEEaiEAIARBAWoiBCACRw0ACwsgA0EQaiQAC0sBAn8jAEEQayIDJAAgAgRAA0AgACADQQxqQQQQESABIAMoAgw2AgAgAUEEaiEBIABBBGohACAEQQFqIgQgAkcNAAsLIANBEGokAAtLAQJ/IwBBEGsiAyQAIAIEQANAIAAgA0EMakECEBEgASADKAIMNgIAIAFBBGohASAAQQJqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALSgECfyMAQRBrIgMkACACBEADQCAAIANBCGoQRCABIAMrAwi2OAIAIAFBBGohASAAQQhqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALaAECfyMAQRBrIgMkACACBEADQCADIAAtAAA6AA8gAyAALQABOgAOIAMgAC0AAjoADSADIAAtAAM6AAwgASADKgIMOAIAIAFBBGohASAAQQRqIQAgBEEBaiIEIAJHDQALCyADQRBqJAALTAECfyMAQRBrIgMkACACBEADQCAAIANBDGpBBBARIAEgAygCDLM4AgAgAUEEaiEBIABBBGohACAEQQFqIgQgAkcNAAsLIANBEGokAAtMAQJ/IwBBEGsiAyQAIAIEQANAIAAgA0EMakECEBEgASADKAIMszgCACABQQRqIQEgAEECaiEAIARBAWoiBCACRw0ACwsgA0EQaiQAC6oIAg1/AXsjAEEQayIIJAACfyAAKAIIQRBGBEAgACgCnAEgACgCzAFBjCxsagwBCyAAKAIMCyEJAkAgAkUEQCADQQFB8B9BABAPDAELIAAoAkghBkEBIQQgASAIQQhqQQEQESAIKAIIIgVBAk8EQCADQQJBxsgAQQAQDwwBCyACIAVBAWpHBEBBACEEIANBAkHwH0EAEA8MAQsCQCAGKAIQIgNFDQAgCSgC0CshBCADQQhPBEAgA0F4cSEGQQAhAgNAIARBADYCvEMgBEEANgKEOyAEQQA2AswyIARBADYClCogBEEANgLcISAEQQA2AqQZIARBADYC7BAgBEEANgK0CCAEQcDDAGohBCACQQhqIgIgBkcNAAsLIANBB3EiA0UNAEEAIQIDQCAEQQA2ArQIIARBuAhqIQQgAkEBaiICIANHDQALCyAJKALoKyICBH8gAhAQIAlBADYC6CsgCCgCCAUgBQtFBEBBASEEDAELA0AgAUEBaiIBIAhBDGpBARARAkAgCSgCgCxF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KT0ALgAobnVsbCkAKCVkLCVkKSAAJXN9CgAJCSB9CgBbREVWXSBEdW1wIGFuIGltYWdlX2NvbXBfaGVhZGVyIHN0cnVjdCB7CgBbREVWXSBEdW1wIGFuIGltYWdlX2hlYWRlciBzdHJ1Y3QgewoASW1hZ2UgaW5mbyB7CgAJIGRlZmF1bHQgdGlsZSB7CgAlcwkgY29tcG9uZW50ICVkIHsKAAkJIGNvbXAgJWQgewoACSBUaWxlIGluZGV4OiB7CgAJIE1hcmtlciBsaXN0OiB7CgBDb2Rlc3RyZWFtIGluZGV4IGZyb20gbWFpbiBoZWFkZXI6IHsKAENvZGVzdHJlYW0gaW5mbyBmcm9tIG1haW4gaGVhZGVyOiB7CgBTdHJlYW0gZXJyb3Igd2hpbGUgcmVhZGluZyBKUDIgSGVhZGVyIGJveAoARm91bmQgYSBtaXNwbGFjZWQgJyVjJWMlYyVjJyBib3ggb3V0c2lkZSBqcDJoIGJveAoATWFsZm9ybWVkIEpQMiBmaWxlIGZvcm1hdDogZmlyc3QgYm94IG11c3QgYmUgSlBFRyAyMDAwIHNpZ25hdHVyZSBib3gKAE1hbGZvcm1lZCBKUDIgZmlsZSBmb3JtYXQ6IHNlY29uZCBib3ggbXVzdCBiZSBmaWxlIHR5cGUgYm94CgBOb3QgZW5vdWdoIG1lbW9yeSB0byBoYW5kbGUganBlZzIwMDAgYm94CgBOb3QgZW5vdWdoIG1lbW9yeSB3aXRoIEZUWVAgQm94CgBBIG1hcmtlciBJRCB3YXMgZXhwZWN0ZWQgKDB4ZmYtLSkgaW5zdGVhZCBvZiAlLjh4CgAJCSBtY3Q9JXgKAAkJCSBjYmxrc3R5PSUjeAoACQkJIGNzdHk9JSN4CgAJCSBwcmc9JSN4CgBJbnRlZ2VyIG92ZXJmbG93CgAJIHRkeD0ldSwgdGR5PSV1CgAJIHR3PSV1LCB0aD0ldQoACSB0eDA9JXUsIHR5MD0ldQoASW52YWxpZCBjb21wb25lbnQgaW5kZXg6ICV1CgBTdHJlYW0gdG9vIHNob3J0CgBNYXJrZXIgaGFuZGxlciBmdW5jdGlvbiBmYWlsZWQgdG8gcmVhZCB0aGUgbWFya2VyIHNlZ21lbnQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjdXJyZW50IHByZWNpbmN0IGNvZGVibG9jayBlbGVtZW50CgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQKAEVycm9yIHJlYWRpbmcgU1FjZCBvciBTUWNjIGVsZW1lbnQKAEEgQlBDQyBoZWFkZXIgYm94IGlzIGF2YWlsYWJsZSBhbHRob3VnaCBCUEMgZ2l2ZW4gYnkgdGhlIElIRFIgYm94ICglZCkgaW5kaWNhdGUgY29tcG9uZW50cyBiaXQgZGVwdGggaXMgY29uc3RhbnQKAEVycm9yIHdpdGggU0laIG1hcmtlcjogaWxsZWdhbCB0aWxlIG9mZnNldAoASW52YWxpZCBwcmVjaW5jdAoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGJhbmQgcHJlY2ludHMKAEZhaWxlZCB0byBkZWNvZGUgYWxsIHVzZWQgY29tcG9uZW50cwoAU2l6ZSBvZiBjb2RlIGJsb2NrIGRhdGEgZXhjZWVkcyBzeXN0ZW0gbGltaXRzCgBTaXplIG9mIHRpbGUgZGF0YSBleGNlZWRzIHN5c3RlbSBsaW1pdHMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtdWx0aXBsZSBNQ1QgbWFya2VycwoAQ29ycnVwdGVkIFBQTSBtYXJrZXJzCgBOb3QgZW5vdWdoIG1lbW9yeSBmb3IgdGlsZSByZXNvbHV0aW9ucwoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIGNvbGxlY3Rpb25zCgBJbnZhbGlkIFBDTFIgYm94LiBSZXBvcnRzIDAgcGFsZXR0ZSBjb2x1bW5zCgBXZSBkbyBub3Qgc3VwcG9ydCBST0kgaW4gZGVjb2RpbmcgSFQgY29kZWJsb2NrcwoAQ2Fubm90IGhhbmRsZSBib3ggb2YgdW5kZWZpbmVkIHNpemVzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgd2l0aG91dCBzYW1lIG51bWJlciBvZiBpbmRpeGVzCgBJbnZhbGlkIHRpbGVjLT53aW5feHh4IHZhbHVlcwoAQ2Fubm90IGhhbmRsZSBib3ggb2YgbGVzcyB0aGFuIDggYnl0ZXMKAENhbm5vdCBoYW5kbGUgWEwgYm94IG9mIGxlc3MgdGhhbiAxNiBieXRlcwoAQ29tcG9uZW50IGluZGV4ICV1IHVzZWQgc2V2ZXJhbCB0aW1lcwoASW52YWxpZCBQQ0xSIGJveC4gUmVwb3J0cyAlZCBlbnRyaWVzCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBjcmVhdGUgVGFnLXRyZWUgbm9kZXMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtY3QgZGF0YSB3aXRoaW4gbXVsdGlwbGUgTUNUIHJlY29yZHMKAENhbm5vdCBkZWNvZGUgdGlsZSwgbWVtb3J5IGVycm9yCgBvcGpfajJrX2FwcGx5X25iX3RpbGVfcGFydHNfY29ycmVjdGlvbiBlcnJvcgoAUHJvYmxlbSB3aXRoIHNraXBwaW5nIEpQRUcyMDAwIGJveCwgc3RyZWFtIGVycm9yCgBQcm9ibGVtIHdpdGggcmVhZGluZyBKUEVHMjAwMCBib3gsIHN0cmVhbSBlcnJvcgoAVW5rbm93biBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCB0bCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCBtaCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHRha2UgaW4gY2hhcmdlIFNJWiBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUFBUIG1hcmtlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBQUFQgbWFya2VyCgBFcnJvciByZWFkaW5nIFNPVCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUExUIG1hcmtlcgoARXJyb3IgcmVhZGluZyBNQ1QgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIE1DVCBtYXJrZXIKAE5vdCBlbm91Z2ggc3BhY2UgZm9yIGV4cGVjdGVkIFNPUCBtYXJrZXIKAEV4cGVjdGVkIFNPUCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgTUNPIG1hcmtlcgoARXJyb3IgcmVhZGluZyBSR04gbWFya2VyCgBFcnJvciByZWFkaW5nIFBQTSBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgUFBNIG1hcmtlcgoARXJyb3IgcmVhZGluZyBUTE0gbWFya2VyCgBFcnJvciByZWFkaW5nIFBMTSBtYXJrZXIKAE5vdCBlbm91Z2ggc3BhY2UgZm9yIGV4cGVjdGVkIEVQSCBtYXJrZXIKAEV4cGVjdGVkIEVQSCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgQ1JHIG1hcmtlcgoAVW5rbm93biBwcm9ncmVzc2lvbiBvcmRlciBpbiBDT0QgbWFya2VyCgBVbmtub3duIFNjb2QgdmFsdWUgaW4gQ09EIG1hcmtlcgoARXJyb3IgcmVhZGluZyBDT0QgbWFya2VyCgBFcnJvciByZWFkaW5nIFFDRCBtYXJrZXIKAENycm9yIHJlYWRpbmcgQ0JEIG1hcmtlcgoARXJyb3IgcmVhZGluZyBQT0MgbWFya2VyCgBFcnJvciByZWFkaW5nIENPQyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUUNDIG1hcmtlcgoARXJyb3IgcmVhZGluZyBNQ0MgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIE1DQyBtYXJrZXIKAHJlcXVpcmVkIFNJWiBtYXJrZXIgbm90IGZvdW5kIGluIG1haW4gaGVhZGVyCgByZXF1aXJlZCBDT0QgbWFya2VyIG5vdCBmb3VuZCBpbiBtYWluIGhlYWRlcgoAcmVxdWlyZWQgUUNEIG1hcmtlciBub3QgZm91bmQgaW4gbWFpbiBoZWFkZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGhhbmRsZSBqcGVnMjAwMCBmaWxlIGhlYWRlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBoZWFkZXIKAEVycm9yIHdpdGggSlAgU2lnbmF0dXJlIDogYmFkIG1hZ2ljIG51bWJlcgoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgY3VycmVudCBudW1iZXIgb2YgdGlsZS1wYXJ0ICglZCksIGdpdmluZyB1cAoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgcHJldmlvdXMgbnVtYmVyIG9mIHRpbGUtcGFydCAoJWQpLCBnaXZpbmcgdXAKAEluIFNPVCBtYXJrZXIsIFRQU290ICglZCkgaXMgbm90IHZhbGlkIHJlZ2FyZHMgdG8gdGhlIGN1cnJlbnQgbnVtYmVyIG9mIHRpbGUtcGFydCAoaGVhZGVyKSAoJWQpLCBnaXZpbmcgdXAKAHRpbGVzIHJlcXVpcmUgYXQgbGVhc3Qgb25lIHJlc29sdXRpb24KAE1hcmtlciBpcyBub3QgY29tcGxpYW50IHdpdGggaXRzIHBvc2l0aW9uCgBQcm9ibGVtIHdpdGggc2VlayBmdW5jdGlvbgoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50LCBJbnZhbGlkIGNibGt3L2NibGtoIGNvbWJpbmF0aW9uCgBJbnZhbGlkIG11bHRpcGxlIGNvbXBvbmVudCB0cmFuc2Zvcm1hdGlvbgoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIGNvbGxlY3Rpb25zIG90aGVyIHRoYW4gYXJyYXkgZGVjb3JyZWxhdGlvbgoAVG9vIGxhcmdlIHZhbHVlIGZvciBOcHBtCgBOb3QgZW5vdWdoIGJ5dGVzIHRvIHJlYWQgTnBwbQoAYmFkIHBsYWNlZCBqcGVnIGNvZGVzdHJlYW0KAAkgTWFpbiBoZWFkZXIgc3RhcnQgcG9zaXRpb249JWxsaQoJIE1haW4gaGVhZGVyIGVuZCBwb3NpdGlvbj0lbGxpCgBNYXJrZXIgc2l6ZSBpbmNvbnNpc3RlbnQgd2l0aCBzdHJlYW0gbGVuZ3RoCgBUaWxlIHBhcnQgbGVuZ3RoIHNpemUgaW5jb25zaXN0ZW50IHdpdGggc3RyZWFtIGxlbmd0aAoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIGRhdGEgc3Bhbm5pbmcKAFdyb25nIGZsYWcKAEVycm9yIHdpdGggRlRZUCBzaWduYXR1cmUgQm94IHNpemUKAEVycm9yIHdpdGggSlAgc2lnbmF0dXJlIEJveCBzaXplCgBJbnZhbGlkIHByZWNpbmN0IHNpemUKAEluY29uc2lzdGVudCBtYXJrZXIgc2l6ZQoASW52YWxpZCBtYXJrZXIgc2l6ZQoARXJyb3Igd2l0aCBTSVogbWFya2VyIHNpemUKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGFkZCBhIG5ldyB2YWxpZGF0aW9uIHByb2NlZHVyZQoATm90IGVub3VnaCBtZW1vcnkgdG8gZGVjb2RlIHRpbGUKAEZhaWxlZCB0byBkZWNvZGUgdGhlIGNvZGVzdHJlYW0gaW4gdGhlIEpQMiBmaWxlCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgd2l0aCBpbmRpeCBzaHVmZmxlCgBDYW5ub3QgYWxsb2NhdGUgVGllciAxIGhhbmRsZQoATm8gZGVjb2RlZCBhcmVhIHBhcmFtZXRlcnMsIHNldCB0aGUgZGVjb2RlZCBhcmVhIHRvIHRoZSB3aG9sZSBpbWFnZQoATm90IGVub3VnaCBtZW1vcnkgdG8gY3JlYXRlIFRhZy10cmVlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWluaXRpYWxpemUgdGhlIHRhZyB0cmVlCgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQsIEludmFsaWQgdHJhbnNmb3JtYXRpb24gZm91bmQKAEVycm9yIHJlYWRpbmcgU1BDb2QgU1BDb2MgZWxlbWVudC4gVW5zdXBwb3J0ZWQgTWl4ZWQgSFQgY29kZS1ibG9jayBzdHlsZSBmb3VuZAoAVGlsZSBZIGNvb3JkaW5hdGVzIGFyZSBub3Qgc3VwcG9ydGVkCgBUaWxlIFggY29vcmRpbmF0ZXMgYXJlIG5vdCBzdXBwb3J0ZWQKAEltYWdlIGNvb3JkaW5hdGVzIGFib3ZlIElOVF9NQVggYXJlIG5vdCBzdXBwb3J0ZWQKAEpQRUcyMDAwIEhlYWRlciBib3ggbm90IHJlYWQgeWV0LCAnJWMlYyVjJWMnIGJveCB3aWxsIGJlIGlnbm9yZWQKAG9wal9qMmtfbWVyZ2VfcHB0KCkgaGFzIGFscmVhZHkgYmVlbiBjYWxsZWQKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgU09UIG1hcmtlci4gVGlsZSBpbmRleCBhbGxvY2F0aW9uIGZhaWxlZAoASWdub3JpbmcgaWhkciBib3guIEZpcnN0IGloZHIgYm94IGFscmVhZHkgcmVhZAoAWnBwdCAldSBhbHJlYWR5IHJlYWQKAFpwcG0gJXUgYWxyZWFkeSByZWFkCgBQVEVSTSBjaGVjayBmYWlsdXJlOiAlZCBzeW50aGV0aXplZCAweEZGIG1hcmtlcnMgcmVhZAoACQkJIGNibGt3PTJeJWQKAAkJCSBjYmxraD0yXiVkCgAJCQkgcW50c3R5PSVkCgAlcyBkeD0lZCwgZHk9JWQKAAkJCSByb2lzaGlmdD0lZAoACQkJIG51bWdiaXRzPSVkCgAJCSBudW1sYXllcnM9JWQKACVzIG51bWNvbXBzPSVkCgBvcGpfanAyX2FwcGx5X2NkZWY6IGFjbj0lZCwgbnVtY29tcHM9JWQKAG9wal9qcDJfYXBwbHlfY2RlZjogY249JWQsIG51bWNvbXBzPSVkCgAJCQkgbnVtcmVzb2x1dGlvbnM9JWQKAAkJIHR5cGU9JSN4LCBwb3M9JWxsaSwgbGVuPSVkCgAlcyBzZ25kPSVkCgAJCQkgcW1mYmlkPSVkCgAlcyBwcmVjPSVkCgAJCSBuYiBvZiB0aWxlLXBhcnQgaW4gdGlsZSBbJWRdPSVkCgAlcyB4MT0lZCwgeTE9JWQKACVzIHgwPSVkLCB5MD0lZAoARmFpbGVkIHRvIGRlY29kZSB0aWxlICVkLyVkCgBTZXR0aW5nIGRlY29kaW5nIGFyZWEgdG8gJWQsJWQsJWQsJWQKAEZhaWxlZCB0byBkZWNvZGUgY29tcG9uZW50ICVkCgBJbnZhbGlkIHZhbHVlIGZvciBudW1yZXNvbHV0aW9ucyA6ICVkLCBtYXggdmFsdWUgaXMgc2V0IGluIG9wZW5qcGVnLmggYXQgJWQKAEludmFsaWQgY29tcG9uZW50IG51bWJlcjogJWQsIHJlZ2FyZGluZyB0aGUgbnVtYmVyIG9mIGNvbXBvbmVudHMgJWQKAFRvbyBtYW55IFBPQ3MgJWQKAEludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBwYXJ0IGluZGV4IGZvciB0aWxlIG51bWJlciAlZC4gR290ICVkLCBleHBlY3RlZCAlZAoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBudW1iZXIgb2YgY29tcG9uZW50IGlzIGlsbGVnYWwgLT4gJWQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjaWVsYWIKAENhbm5vdCBhbGxvY2F0ZSBjYmxrLT5kZWNvZGVkX2RhdGEKAEZhaWxlZCB0byBtZXJnZSBQUFQgZGF0YQoARmFpbGVkIHRvIG1lcmdlIFBQTSBkYXRhCgBJbnZhbGlkIG51bWJlciBvZiBsYXllcnMgaW4gQ09EIG1hcmtlciA6ICVkIG5vdCBpbiByYW5nZSBbMS02NTUzNV0KACVzOiVkOmNvbG9yX2NteWtfdG9fcmdiCglDQU4gTk9UIENPTlZFUlQKACVzOiVkOmNvbG9yX2VzeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgAlczolZDpjb2xvcl9zeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgBTdHJlYW0gdG9vIHNob3J0LCBleHBlY3RlZCBTT1QKAFVuYWJsZSB0byBzZXQgdDEgaGFuZGxlIGFzIFRMUwoAU3RyZWFtIGRvZXMgbm90IGVuZCB3aXRoIEVPQwoAQ2Fubm90IGhhbmRsZSBib3ggc2l6ZXMgaGlnaGVyIHRoYW4gMl4zMgoAb3BqX3BpX25leHRfbHJjcCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcmxjcCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfY3BybCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcGNybCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3BpX25leHRfcnBjbCgpOiBpbnZhbGlkIGNvbXBubzAvY29tcG5vMQoAb3BqX3QxX2RlY29kZV9jYmxrKCk6IHVuc3VwcG9ydGVkIGJwbm9fcGx1c19vbmUgPSAlZCA+PSAzMQoARmFpbGVkIHRvIGRlY29kZSB0aWxlIDEvMQoASW5zdWZmaWNpZW50IGRhdGEgZm9yIENNQVAgYm94LgoATmVlZCB0byByZWFkIGEgUENMUiBib3ggYmVmb3JlIHRoZSBDTUFQIGJveC4KAEluc3VmZmljaWVudCBkYXRhIGZvciBDREVGIGJveC4KAE51bWJlciBvZiBjaGFubmVsIGRlc2NyaXB0aW9uIGlzIGVxdWFsIHRvIHplcm8gaW4gQ0RFRiBib3guCgBTdHJlYW0gZXJyb3Igd2hpbGUgcmVhZGluZyBKUDIgSGVhZGVyIGJveDogbm8gJ2loZHInIGJveC4KAE5vbiBjb25mb3JtYW50IGNvZGVzdHJlYW0gVFBzb3Q9PVROc290LgoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3g6IGJveCBsZW5ndGggaXMgaW5jb25zaXN0ZW50LgoAQm94IGxlbmd0aCBpcyBpbmNvbnNpc3RlbnQuCgBSZXNvbHV0aW9uIGZhY3RvciBpcyBncmVhdGVyIHRoYW4gdGhlIG1heGltdW0gcmVzb2x1dGlvbiBpbiB0aGUgY29tcG9uZW50LgoAQ29tcG9uZW50IG1hcHBpbmcgc2VlbXMgd3JvbmcuIFRyeWluZyB0byBjb3JyZWN0LgoASW5jb21wbGV0ZSBjaGFubmVsIGRlZmluaXRpb25zLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gSW52YWxpZCBjb2RlYmxvY2sgbGVuZ3RoIHZhbHVlcy4KAFdlIGRvIG5vdCBzdXBwb3J0IG1vcmUgdGhhbiAzIGNvZGluZyBwYXNzZXMgaW4gYW4gSFQgY29kZWJsb2NrOyBUaGlzIGNvZGVibG9ja3MgaGFzICVkIHBhc3Nlcy4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFRoZXJlIGFyZSAlZCB6ZXJvIGJpdHBsYW5lcyBpbiAlZCBiaXRwbGFuZXMuCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgdHJhbnNmb3JtYXRpb24gc3RhZ2VzLgoAVW5rbm93biBtYXJrZXIgaGFzIGJlZW4gZGV0ZWN0ZWQgYW5kIGdlbmVyYXRlZCBlcnJvci4KAENvZGVjIHByb3ZpZGVkIHRvIHRoZSBvcGpfc2V0dXBfZGVjb2RlciBmdW5jdGlvbiBpcyBub3QgYSBkZWNvbXByZXNzb3IgaGFuZGxlci4KAENvZGVjIHByb3ZpZGVkIHRvIHRoZSBvcGpfcmVhZF9oZWFkZXIgZnVuY3Rpb24gaXMgbm90IGEgZGVjb21wcmVzc29yIGhhbmRsZXIuCgBUaWxlcyBkb24ndCBhbGwgaGF2ZSB0aGUgc2FtZSBkaW1lbnNpb24uIFNraXAgdGhlIE1DVCBzdGVwLgoATnVtYmVyIG9mIGNvbXBvbmVudHMgKCVkKSBpcyBpbmNvbnNpc3RlbnQgd2l0aCBhIE1DVC4gU2tpcCB0aGUgTUNUIHN0ZXAuCgBKUDIgYm94IHdoaWNoIGFyZSBhZnRlciB0aGUgY29kZXN0cmVhbSB3aWxsIG5vdCBiZSByZWFkIGJ5IHRoaXMgZnVuY3Rpb24uCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBXaGVuIHRoZSBudW1iZXIgb2YgemVybyBwbGFuZXMgYml0cGxhbmVzIGlzIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgYml0cGxhbmVzLCBvbmx5IHRoZSBjbGVhbnVwIHBhc3MgbWFrZXMgc2Vuc2UsIGJ1dCB3ZSBoYXZlICVkIHBhc3NlcyBpbiB0aGlzIGNvZGVibG9jay4gVGhlcmVmb3JlLCBvbmx5IHRoZSBjbGVhbnVwIHBhc3Mgd2lsbCBiZSBkZWNvZGVkLiBUaGlzIG1lc3NhZ2Ugd2lsbCBub3QgYmUgZGlzcGxheWVkIGFnYWluLgoASW1hZ2UgaGFzIGxlc3MgY29tcG9uZW50cyB0aGFuIGNvZGVzdHJlYW0uCgBOZWVkIHRvIGRlY29kZSB0aGUgbWFpbiBoZWFkZXIgYmVmb3JlIGJlZ2luIHRvIGRlY29kZSB0aGUgcmVtYWluaW5nIGNvZGVzdHJlYW0uCgBQc290IHZhbHVlIG9mIHRoZSBjdXJyZW50IHRpbGUtcGFydCBpcyBlcXVhbCB0byB6ZXJvLCB3ZSBhc3N1bWluZyBpdCBpcyB0aGUgbGFzdCB0aWxlLXBhcnQgb2YgdGhlIGNvZGVzdHJlYW0uCgBBIG1hbGZvcm1lZCBjb2RlYmxvY2sgdGhhdCBoYXMgbW9yZSB0aGFuIG9uZSBjb2RpbmcgcGFzcywgYnV0IHplcm8gbGVuZ3RoIGZvciAybmQgYW5kIHBvdGVudGlhbGx5IHRoZSAzcmQgcGFzcyBpbiBhbiBIVCBjb2RlYmxvY2suCgAJCQkgdGlsZS1wYXJ0WyVkXTogc3Rhcl9wb3M9JWxsaSwgZW5kX2hlYWRlcj0lbGxpLCBlbmRfcG9zPSVsbGkuCgBUaWxlICV1IGhhcyBUUHNvdCA9PSAwIGFuZCBUTnNvdCA9PSAwLCBidXQgbm8gb3RoZXIgdGlsZS1wYXJ0cyB3ZXJlIGZvdW5kLiBFT0MgaXMgYWxzbyBtaXNzaW5nLgoAQ29tcG9uZW50ICVkIGRvZXNuJ3QgaGF2ZSBhIG1hcHBpbmcuCgBBIGNvbmZvcm1pbmcgSlAyIHJlYWRlciBzaGFsbCBpZ25vcmUgYWxsIENvbG91ciBTcGVjaWZpY2F0aW9uIGJveGVzIGFmdGVyIHRoZSBmaXJzdCwgc28gd2UgaWdub3JlIHRoaXMgb25lLgoAVGhlIHNpZ25hdHVyZSBib3ggbXVzdCBiZSB0aGUgZmlyc3QgYm94IGluIHRoZSBmaWxlLgoAVGhlICBib3ggbXVzdCBiZSB0aGUgZmlyc3QgYm94IGluIHRoZSBmaWxlLgoAVGhlIGZ0eXAgYm94IG11c3QgYmUgdGhlIHNlY29uZCBib3ggaW4gdGhlIGZpbGUuCgBGYWlsZWQgdG8gZGVjb2RlLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gSW5jb3JyZWN0IE1FTCBzZWdtZW50IHNlcXVlbmNlLgoAQ29tcG9uZW50ICVkIGlzIG1hcHBlZCB0d2ljZS4KAE9ubHkgb25lIENNQVAgYm94IGlzIGFsbG93ZWQuCgBXZSBuZWVkIGFuIGltYWdlIHByZXZpb3VzbHkgY3JlYXRlZC4KAElIRFIgYm94X21pc3NpbmcuIFJlcXVpcmVkLgoASlAySCBib3ggbWlzc2luZy4gUmVxdWlyZWQuCgBOb3Qgc3VyZSBob3cgdGhhdCBoYXBwZW5lZC4KAE1haW4gaGVhZGVyIGhhcyBiZWVuIGNvcnJlY3RseSBkZWNvZGVkLgoAVGlsZSAlZC8lZCBoYXMgYmVlbiBkZWNvZGVkLgoASGVhZGVyIG9mIHRpbGUgJWQgLyAlZCBoYXMgYmVlbiByZWFkLgoARW1wdHkgU09UIG1hcmtlciBkZXRlY3RlZDogUHNvdD0lZC4KAERpcmVjdCB1c2UgYXQgIyVkIGhvd2V2ZXIgcGNvbD0lZC4KAEltcGxlbWVudGF0aW9uIGxpbWl0YXRpb246IGZvciBwYWxldHRlIG1hcHBpbmcsIHBjb2xbJWRdIHNob3VsZCBiZSBlcXVhbCB0byAlZCwgYnV0IGlzIGVxdWFsIHRvICVkLgoASW52YWxpZCBjb21wb25lbnQvcGFsZXR0ZSBpbmRleCBmb3IgZGlyZWN0IG1hcHBpbmcgJWQuCgBJbnZhbGlkIHZhbHVlIGZvciBjbWFwWyVkXS5tdHlwID0gJWQuCgBQc290IHZhbHVlIGlzIG5vdCBjb3JyZWN0IHJlZ2FyZHMgdG8gdGhlIEpQRUcyMDAwIG5vcm06ICVkLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gVkxDIGNvZGUgcHJvZHVjZXMgc2lnbmlmaWNhbnQgc2FtcGxlcyBvdXRzaWRlIHRoZSBjb2RlYmxvY2sgYXJlYS4KAFVuZXhwZWN0ZWQgT09NLgoAMzIgYml0cyBhcmUgbm90IGVub3VnaCB0byBkZWNvZGUgdGhpcyBjb2RlYmxvY2ssIHNpbmNlIHRoZSBudW1iZXIgb2YgYml0cGxhbmUsICVkLCBpcyBsYXJnZXIgdGhhbiAzMC4KAEJvdHRvbSBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTE9JWQpIHNob3VsZCBiZSA+IDAuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIHNob3VsZCBiZSA+IDAuCgBVcCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTA9JWQpIHNob3VsZCBiZSA+PSAwLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIHNob3VsZCBiZSA+PSAwLgoARXJyb3IgcmVhZGluZyBQUFQgbWFya2VyOiBwYWNrZXQgaGVhZGVyIGhhdmUgYmVlbiBwcmV2aW91c2x5IGZvdW5kIGluIHRoZSBtYWluIGhlYWRlciAoUFBNIG1hcmtlcikuCgBTdGFydCB0byByZWFkIGoyayBtYWluIGhlYWRlciAoJWxsZCkuCgBCb3R0b20gcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kxPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZc2l6PSVkKS4KAFVwIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MD0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWXNpej0lZCkuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhzaXo9JWQpLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhzaXo9JWQpLgoAQm90dG9tIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWU9zaXo9JWQpLgoAVXAgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZT3Npej0lZCkuCgBSaWdodCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhPc2l6PSVkKS4KAExlZnQgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3gwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChYT3Npej0lZCkuCgBTaXplIHggb2YgdGhlIGRlY29kZWQgY29tcG9uZW50IGltYWdlIGlzIGluY29ycmVjdCAoY29tcFslZF0udz0lZCkuCgBTaXplIHkgb2YgdGhlIGRlY29kZWQgY29tcG9uZW50IGltYWdlIGlzIGluY29ycmVjdCAoY29tcFslZF0uaD0lZCkuCgBUaWxlIHJlYWQsIGRlY29kZWQgYW5kIHVwZGF0ZWQgaXMgbm90IHRoZSBkZXNpcmVkIG9uZSAoJWQgdnMgJWQpLgoASW52YWxpZCBjb21wb25lbnQgaW5kZXggJWQgKD49ICVkKS4KAG9wal9yZWFkX2hlYWRlcigpIHNob3VsZCBiZSBjYWxsZWQgYmVmb3JlIG9wal9zZXRfZGVjb2RlZF9jb21wb25lbnRzKCkuCgBNZW1vcnkgYWxsb2NhdGlvbiBmYWlsdXJlIGluIG9wal9qcDJfYXBwbHlfcGNscigpLgoAaW1hZ2UtPmNvbXBzWyVkXS5kYXRhID09IE5VTEwgaW4gb3BqX2pwMl9hcHBseV9wY2xyKCkuCgBpbnZhbGlkIGJveCBzaXplICVkICgleCkKAEZhaWwgdG8gcmVhZCB0aGUgY3VycmVudCBtYXJrZXIgc2VnbWVudCAoJSN4KQoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBJSERSIHcoJXUpIGgoJXUpIHZzLiBTSVogdygldSkgaCgldSkKAEVycm9yIHJlYWRpbmcgQ09DIG1hcmtlciAoYmFkIG51bWJlciBvZiBjb21wb25lbnRzKQoASW52YWxpZCBudW1iZXIgb2YgdGlsZXMgOiAldSB4ICV1IChtYXhpbXVtIGZpeGVkIGJ5IGpwZWcyMDAwIG5vcm0gaXMgNjU1MzUgdGlsZXMpCgBJbnZhbGlkIG51bWJlciBvZiBjb21wb25lbnRzIChpaGRyKQoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGltYWdlIGhlYWRlciAoaWhkcikKAFdyb25nIHZhbHVlcyBmb3I6IHcoJWQpIGgoJWQpIG51bWNvbXBzKCVkKSAoaWhkcikKAEludmFsaWQgdmFsdWVzIGZvciBjb21wID0gJWQgOiBkeD0ldSBkeT0ldSAoc2hvdWxkIGJlIGJldHdlZW4gMSBhbmQgMjU1IGFjY29yZGluZyB0byB0aGUgSlBFRzIwMDAgbm9ybSkKAEJhZCBpbWFnZSBoZWFkZXIgYm94IChiYWQgc2l6ZSkKAEJhZCBDT0xSIGhlYWRlciBib3ggKGJhZCBzaXplKQoAQmFkIEJQQ0MgaGVhZGVyIGJveCAoYmFkIHNpemUpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IG5lZ2F0aXZlIG9yIHplcm8gaW1hZ2Ugc2l6ZSAoJWxsZCB4ICVsbGQpCgBza2lwOiBzZWdtZW50IHRvbyBsb25nICglZCkgd2l0aCBtYXggKCVkKSBmb3IgY29kZWJsb2NrICVkIChwPSVkLCBiPSVkLCByPSVkLCBjPSVkKQoAcmVhZDogc2VnbWVudCB0b28gbG9uZyAoJWQpIHdpdGggbWF4ICglZCkgZm9yIGNvZGVibG9jayAlZCAocD0lZCwgYj0lZCwgcj0lZCwgYz0lZCkKAERlc3BpdGUgSlAyIEJQQyE9MjU1LCBwcmVjaXNpb24gYW5kL29yIHNnbmQgdmFsdWVzIGZvciBjb21wWyVkXSBpcyBkaWZmZXJlbnQgdGhhbiBjb21wWzBdOgogICAgICAgIFswXSBwcmVjKCVkKSBzZ25kKCVkKSBbJWRdIHByZWMoJWQpIHNnbmQoJWQpCgBiYWQgY29tcG9uZW50IG51bWJlciBpbiBSR04gKCVkIHdoZW4gdGhlcmUgYXJlIG9ubHkgJWQpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IG51bWJlciBvZiBjb21wb25lbnQgaXMgbm90IGNvbXBhdGlibGUgd2l0aCB0aGUgcmVtYWluaW5nIG51bWJlciBvZiBwYXJhbWV0ZXJzICggJWQgdnMgJWQpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IGludmFsaWQgdGlsZSBzaXplICh0ZHg6ICVkLCB0ZHk6ICVkKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoYmFkIHNpemU6ICVkKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoQ0lFTGFiLCBiYWQgc2l6ZTogJWQpCgBQVEVSTSBjaGVjayBmYWlsdXJlOiAlZCByZW1haW5pbmcgYnl0ZXMgaW4gY29kZSBibG9jayAoJWQgdXNlZCAvICVkKQoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gT25lIG9mIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uIGlzIG5vdCBtZXQ6IDIgPD0gU2N1cCA8PSBtaW4oTGN1cCwgNDA3OSkKAEludmFsaWQgdmFsdWVzIGZvciBjb21wID0gJWQgOiBwcmVjPSV1IChzaG91bGQgYmUgYmV0d2VlbiAxIGFuZCAzOCBhY2NvcmRpbmcgdG8gdGhlIEpQRUcyMDAwIG5vcm0uIE9wZW5KcGVnIG9ubHkgc3VwcG9ydHMgdXAgdG8gMzEpCgBJbnZhbGlkIGJpdCBudW1iZXIgJWQgaW4gb3BqX3QyX3JlYWRfcGFja2V0X2hlYWRlcigpCgBTdHJlYW0gZXJyb3IhCgBFcnJvciBvbiB3cml0aW5nIHN0cmVhbSEKAFN0cmVhbSByZWFjaGVkIGl0cyBlbmQgIQoARXhwZWN0ZWQgYSBTT0MgbWFya2VyIAoASW52YWxpZCBib3ggc2l6ZSAlZCBmb3IgYm94ICclYyVjJWMlYycuIE5lZWQgJWQgYnl0ZXMsICVkIGJ5dGVzIHJlbWFpbmluZyAKAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFVfcSBpcyBsYXJnZXIgdGhhbiB6ZXJvIGJpdHBsYW5lcyArIDEgCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBEZWNvZGluZyB0aGlzIGNvZGVibG9jayBpcyBzdG9wcGVkLiBVX3EgaXNsYXJnZXIgdGhhbiBiaXRwbGFuZXMgKyAxIAoAQ09MUiBCT1ggbWV0aCB2YWx1ZSBpcyBub3QgYSByZWd1bGFyIHZhbHVlICglZCksIHNvIHdlIHdpbGwgaWdub3JlIHRoZSBlbnRpcmUgQ29sb3VyIFNwZWNpZmljYXRpb24gYm94LiAKAFdoaWxlIHJlYWRpbmcgQ0NQX1FOVFNUWSBlbGVtZW50IGluc2lkZSBRQ0Qgb3IgUUNDIG1hcmtlciBzZWdtZW50LCBudW1iZXIgb2Ygc3ViYmFuZHMgKCVkKSBpcyBncmVhdGVyIHRvIE9QSl9KMktfTUFYQkFORFMgKCVkKS4gU28gd2UgbGltaXQgdGhlIG51bWJlciBvZiBlbGVtZW50cyBzdG9yZWQgdG8gT1BKX0oyS19NQVhCQU5EUyAoJWQpIGFuZCBza2lwIHRoZSByZXN0LiAKAEpQMiBJSERSIGJveDogY29tcHJlc3Npb24gdHlwZSBpbmRpY2F0ZSB0aGF0IHRoZSBmaWxlIGlzIG5vdCBhIGNvbmZvcm1pbmcgSlAyIGZpbGUgKCVkKSAKAFRpbGUgaW5kZXggcHJvdmlkZWQgYnkgdGhlIHVzZXIgaXMgaW5jb3JyZWN0ICVkIChtYXggPSAlZCkgCgBFcnJvciBkZWNvZGluZyBjb21wb25lbnQgJWQuClRoZSBudW1iZXIgb2YgcmVzb2x1dGlvbnMgdG8gcmVtb3ZlICglZCkgaXMgZ3JlYXRlciBvciBlcXVhbCB0aGFuIHRoZSBudW1iZXIgb2YgcmVzb2x1dGlvbnMgb2YgdGhpcyBjb21wb25lbnQg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AAABVAAAAQAAAABQAADAUAAAAUgAAAAAAAAQUAAAsFAAAAFIAAABAAAAIFAAAMBQAAABOAAAAAAAADBQAACwUAAAATgAAAEAAABAUAAAwFAAAAEwAAAAAAAAUFAAABBRAAABMAAAAQAAAGBQAAAgUQAAASQAAAAAAABwUAAAMFEAAAEkAAABAAAAgFAAAEBRAAABHAAAAAAAAJBQAABwUQAAARwAAAEAAACgUAAAgFEAAAEWAAAAAAAAkFIAAJBRAAABFgAAAQAAAKBSAACgUQAAAVYAAAAAAADQUAAAwFAAAAFWAAABAAAA4FAAALBQAAABVAAAAAAAAPBQAACwUAAAAVQAAAEAAAAAUQAAwFAAAAFRAAAAAAAAEFEAANBQAAABUQAAAQAAACBRAADgUAAAAUgAAAAAAAAwUQAA8FAAAAFIAAABAAAAQFEAAABRAAABOAAAAAAAAFBRAAAQUQAAATgAAAEAAABgUQAAIFEAAAE0AAAAAAAAcFEAADBRAAABNAAAAQAAAIBRAABAUQAAATAAAAAAAACQUQAAUFEAAAEwAAABAAAAoFEAAGBRAAABKAAAAAAAALBRAABQUQAAASgAAAEAAADAUQAAYFEAAAEkAAAAAAAA0FEAAHBRAAABJAAAAQAAAOBRAACAUQAAASIAAAAAAADwUQAAkFEAAAEiAAABAAAAAFIAAKBRAAABHAAAAAAAABBSAACwUQAAARwAAAEAAAAgUgAAwFEAAAEYAAAAAAAAMFIAANBRAAABGAAAAQAAAEBSAADgUQAAARYAAAAAAABQUgAA8FEAAAEWAAABAAAAYFIAAABSAAABFAAAAAAAAHBSAAAQUgAAARQAAAEAAACAUgAAIFIAAAESAAAAAAAAkFIAADBSAAABEgAAAQAAAKBSAABAUgAAAREAAAAAAACwUgAAUFIAAAERAAABAAAAwFIAAGBSAADBCgAAAAAAANBSAABwUgAAwQoAAAEAAADgUgAAgFIAAMEJAAAAAAAA8FIAAJBSAADBCQAAAQAAAABTAACgUgAAoQgAAAAAAAAQUwAAsFIAAKEIAAABAAAAIFMAAMBSAAAhBQAAAAAAADBTAADQUgAAIQUAAAEAAABAUwAA4FIAAEEEAAAAAAAAUFMAAPBSAABBBAAAAQAAAGBTAAAAUwAAoQIAAAAAAABwUwAAEFMAAKECAAABAAAAgFMAACBTAAAhAgAAAAAAAJBTAAAwUwAAIQIAAAEAAACgUwAAQFMAAEEBAAAAAAAAsFMAAFBTAABBAQAAAQAAAMBTAABgUwAAEQEAAAAAAADQUwAAcFMAABEBAAABAAAA4FMAAIBTAACFAAAAAAAAAPBTAACQUwAAhQAAAAEAAAAAVAAAoFMAAEkAAAAAAAAAEFQAALBTAABJAAAAAQAAACBUAADAUwAAJQAAAAAAAAAwVAAA0FMAACUAAAABAAAAQFQAAOBTAAAVAAAAAAAAAFBUAADwUwAAFQAAAAEAAABgVAAAAFQAAAkAAAAAAAAAcFQAABBUAAAJAAAAAQAAAIBUAAAgVAAABQAAAAAAAACQVAAAMFQAAAUAAAABAAAAoFQAAEBUAAABAAAAAAAAAJBUAABQVAAAAQAAAAEAAACgVAAAYFQAAAFWAAAAAAAAsFQAALBUAAABVgAAAQAAAMBUAADAVAAAAAEDAwECAwMFBgcHBgYHBwABAwMBAgMDBQYHBwYGBwcFBgcHBgYHBwgICAgICAgIBQYHBwYGBwcICAgICAgICAECAwMCAgMDBgYHBwYGBwcBAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAECAwMCAgMDBgYHBwYGBwcBAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgCAgMDAgIDAwYGBwcGBgcHAgIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgAAQUGAQIGBgMDBwcDAwcHAAEFBgECBgYDAwcHAwMHBwMDBwcDAwcHBAQHBwQEBwcDAwcHAwMHBwQEBwcEBAcHAQIGBgICBgYDAwcHAwMHBwECBgYCAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwUGCAgGBggIBwcICAcHCAgFBggIBgYICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgGBggIBgYICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIAQIGBgICBgYDAwcHAwMHBwECBgYCAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwICBgYCAgYGAwMHBwMDBwcCAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcGBggIBgYICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAABAwMBAgMDBQYHBwYGBwcAAQMDAQIDAwUGBwcGBgcHBQYHBwYGBwcICAgICAgICAUGBwcGBgcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAgIDAwICAwMGBgcHBgYHBwICAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAAMBBAMGBAcBBAIFBAcFBwADAQQDBgQHAQQCBQQHBQcBBAIFBAcFBwIFAgUFBwUHAQQCBQQHBQcCBQIFBQcFBwMGBAcGCAcIBAcFBwcIBwgDBgQHBggHCAQHBQcHCAcIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgBBAIFBAcFBwIFAgUFBwUHAQQCBQQHBQcCBQIFBQcFBwIFAgUFBwUHAgUCBQUHBQcCBQIFBQcFBwIFAgUFBwUHBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAMGBAcGCAcIBAcFBwcIBwgDBgQHBggHCAQHBQcHCAcIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgGCAcICAgICAcIBwgICAgIBggHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBAcFBwcIBwgFBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgHCAcICAgICAcIBwgICAgIBwgHCAgICAgJCQoKCQkKCgwMDQsMDA0LCQkKCgkJCgoMDAsNDAwLDQwMDQ0MDAsLDAkNCgkMCgsMDAsLDAwNDQwJCwoJDAoNCQkKCgkJCgoMDA0LDAwNCwkJCgoJCQoKDAwLDQwMCw0MDA0NDAwLCwwJDQoJDAoLDAwLCwwMDQ0MCQsKCQwKDQoKCgoKCgoKDQsNCw0LDQsKCgkJCgoJCQ0LDAwNCwwMDQ0NDQsLCwsNCg0KCgsKCw0NDAwLCwwMDQoMCQoLCQwKCgkJCgoJCQsNDAwLDQwMCgoKCgoKCgoLDQsNCw0LDQsLDAwNDQwMCwoMCQoNCQwLCwsLDQ0NDQsKCwoKDQoNAEHZuwELNwEAAQABAAEAAAEBAAABAQABAAEAAQABAAAAAAEBAQEAAAAAAAEAAQAAAAABAQEBAAAAAQABAQEAQZm8AQs3AQABAAEAAQAAAQEAAAEBAAEAAQABAAEAAAAAAQEBAQAAAAAAAQABAAAAAAEBAQEAAAABAAEBAQBB2bwBCwcBAAEAAQABAEHpvAELlQIBAAEAAQABAAAAAAEBAQEAAAAAAAEAAQAAAAABAQEBAAAAAAABAAEBAQAAAQEAAAABAAEAAQABAQEBAQEBAQEAAQABAAEAAQAAAAABAQEBAAEAAAEBAAEAAAAAAQEBAQABAAEBAQEBAgAAAAQAAAAEAAAACAAAAJD/AAAMAAAAGAAAAFL/AAAUAAAAGQAAAFP/AAAUAAAAGgAAAF7/AAAUAAAAGwAAAFz/AAAUAAAAHAAAAF3/AAAUAAAAHQAAAF//AAAUAAAAHgAAAFH/AAACAAAAHwAAAFX/AAAEAAAAIAAAAFf/AAAEAAAAIQAAAFj/AAAQAAAAIgAAAGD/AAAEAAAAIwAAAGH/AAAQAAAAJAAAAJH/AEGIvwELZWP/AAAEAAAAJQAAAGT/AAAUAAAAJgAAAHT/AAAUAAAAJwAAAHj/AAAEAAAAKAAAAFD/AAAEAAAAKQAAAFn/AAAEAAAAKgAAAHX/AAAUAAAAKwAAAHf/AAAUAAAALAAAAAAAAAAUAEGAwAELNS0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAAICBQajYAAABweXRmNwAAAGgycGo4AEHAwAELMnJkaGk5AAAAcmxvYzoAAABjY3BiOwAAAHJsY3A8AAAAcGFtYz0AAABmZWRjPgAAAPhiAEGAwQELQRkACwAZGRkAAAAABQAAAAAAAAkAAAAACwAAAAAAAAAAGQAKChkZGQMKBwABAAkLGAAACQYLAAALAAYZAAAAGRkZAEHRwQELIQ4AAAAAAAAAABkACw0ZGRkADQAAAgAJDgAAAAkADgAADgBBi8IBCwEMAEGXwgELFRMAAAAAEwAAAAAJDAAAAAAADAAADABBxcIBCwEQAEHRwgELFQ8AAAAEDwAAAAAJEAAAAAAAEAAAEABB/8IBCwESAEGLwwELHhEAAAAAEQAAAAAJEgAAAAAAEgAAEgAAGgAAABoaGgBBwsMBCw4aAAAAGhoaAAAAAAAACQBB88MBCwEUAEH/wwELFRcAAAAAFwAAAAAJFAAAAAAAFAAAFABBrcQBCwEWAEG5xAELJxUAAAAAFQAAAAAJFgAAAAAAFgAAFgAAMDEyMzQ1Njc4OUFCQ0RFRgBB4MQBCwmQbAEAAAAAAAUAQfTEAQsBaQBBjMUBCwpqAAAAawAAAHhoAEGkxQELAQIAQbTFAQsI//////////8AQfjFAQsBBQBBhMYBCwFsAEGcxgELDmoAAABtAAAAiGgAAAAEAEG0xgELAQEAQcTGAQsF/////wo=";return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}var binary=tryParseAsDataURI(file);if(binary){return binary}if(readBinary){return readBinary(file)}throw'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)'}function instantiateSync(file,info){var module;var binary=getBinarySync(file);module=new WebAssembly.Module(binary);var instance=new WebAssembly.Instance(module,info);return[instance,module]}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["p"];updateMemoryViews();addOnInit(wasmExports["q"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();var result=instantiateSync(wasmBinaryFile,info);return receiveInstance(result[0])}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);function _copy_pixels_1(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);imageData.set(compG)}function _copy_pixels_3(compR_ptr,compG_ptr,compB_ptr,nb_pixels){compR_ptr>>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*3);const compR=Module.HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=Module.HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=Module.HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=Module.HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);const compA=Module.HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var _fd_close=fd=>52;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};function _gray_to_rgba(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);for(let i=0;i>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compA=Module.HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=Module.HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=Module.HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=Module.HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=Module; +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.slice(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["t"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("openjpeg.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["s"];updateMemoryViews();removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{receiveInstance(mod,inst);resolve(mod.exports)})})}wasmBinaryFile??=findWasmBinary();try{var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}catch(e){readyPromiseReject(e);return Promise.reject(e)}}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.unshift(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.unshift(cb);var noExitRuntime=Module["noExitRuntime"]||true;var __abort_js=()=>abort("");var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};function _copy_pixels_1(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);imageData.set(compG)}function _copy_pixels_3(compR_ptr,compG_ptr,compB_ptr,nb_pixels){compR_ptr>>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*3);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var _fd_close=fd=>52;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};function _gray_to_rgba(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);for(let i=0;i>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; return moduleRtn; diff --git a/external/openjpeg/openjpeg.wasm b/external/openjpeg/openjpeg.wasm new file mode 100644 index 0000000000000..056fb2790e7cd Binary files /dev/null and b/external/openjpeg/openjpeg.wasm differ diff --git a/external/openjpeg/openjpeg_nowasm_fallback.js b/external/openjpeg/openjpeg_nowasm_fallback.js new file mode 100644 index 0000000000000..0ac13e000eff1 --- /dev/null +++ b/external/openjpeg/openjpeg_nowasm_fallback.js @@ -0,0 +1,30 @@ +/* THIS FILE IS GENERATED - DO NOT EDIT */ +var OpenJPEG = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.slice(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var WebAssembly={Memory:function(opts){this.buffer=new ArrayBuffer(opts["initial"]*65536)},Module:function(binary){},Instance:function(module,info){this.exports=( +// EMSCRIPTEN_START_ASM +function instantiate(Ea){function c(d){d.set=function(a,b){this[a]=b};d.get=function(a){return this[a]};return d}var e;var f=new Uint8Array(123);for(var a=25;a>=0;--a){f[48+a]=52+a;f[65+a]=a;f[97+a]=26+a}f[43]=62;f[47]=63;function l(m,n,o){var g,h,a=0,i=n,j=o.length,k=n+(j*3>>2)-(o[j-2]=="=")-(o[j-1]=="=");for(;a>4;if(i>2;if(i>>0;D=D>>>0;if(C+D>e.length)throw"trap: invalid memory.fill";e.fill(y,C,C+D)}function E(C,F,D){e.copyWithin(C,F,F+D)}function G(){throw new Error("abort")}function Da(q){var H=new ArrayBuffer(16908288);var I=new Int8Array(H);var J=new Int16Array(H);var K=new Int32Array(H);var L=new Uint8Array(H);var M=new Uint16Array(H);var N=new Uint32Array(H);var O=new Float32Array(H);var P=new Float64Array(H);var Q=Math.imul;var R=Math.fround;var S=Math.abs;var T=Math.clz32;var U=Math.min;var V=Math.max;var W=Math.floor;var X=Math.ceil;var Y=Math.trunc;var Z=Math.sqrt;var _=q.a;var $=_.a;var aa=_.b;var ba=_.c;var ca=_.d;var da=_.e;var ea=_.f;var fa=_.g;var ga=_.h;var ha=_.i;var ia=_.j;var ja=_.k;var ka=_.l;var la=_.m;var ma=_.n;var na=_.o;var oa=_.p;var pa=_.q;var qa=_.r;var ra=94304;var sa=0;var ta=0;var ua=0; +// EMSCRIPTEN_START_FUNCS +function jd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,C=0,D=0,F=0,G=0,H=0,P=0,S=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=R(0),ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,sa=0,ta=0,ua=0,wa=0;$=ra-96|0;ra=$;D=K[a+8>>2];a:{b:{c:{if(!K[a>>2]){g=Q(K[D+16>>2]-K[D+8>>2]|0,K[D+20>>2]-K[D+12>>2]|0)<<2;c=Ma(g);K[D+60>>2]=c;if(!c){Fa(K[a+32>>2],1,7986,0);d=a+28|0;break b}if(!g){break c}B(c,0,g);break c}c=K[D+60>>2];if(!c){break c}Ga(c);K[D+60>>2]=0}if(!K[K[a+28>>2]>>2]){break a}pa=K[a+16>>2];c=K[pa+28>>2]+Q(K[pa+24>>2],152)|0;ua=K[c-152>>2];wa=K[c-144>>2];qa=K[a+20>>2];sa=K[a+12>>2];ta=K[a+4>>2];d=a+28|0;d:{q=K[b+4>>2];e=0;e:{if((q|0)<=0){break e}l=K[b>>2];c=0;f:{while(1){g=l+Q(c,12)|0;if(!K[g>>2]){break f}c=c+1|0;if((q|0)!=(c|0)){continue}break}e=0;break e}e=K[g+4>>2]}if(e){break d}e=Ia(1,156);if(!e){Fa(K[a+32>>2],1,6276,0);break b}K[e+140>>2]=0;c=0;l=K[b+4>>2];g:{if((l|0)==2147483647){break g}g=K[b>>2];if((l|0)>0){while(1){q=g+Q(c,12)|0;if(!K[q>>2]){l=K[q+8>>2];if(l){va[l|0](K[q+4>>2]);g=K[b>>2]}b=g+Q(c,12)|0;K[b+8>>2]=15;K[b+4>>2]=e;c=1;break g}c=c+1|0;if((l|0)!=(c|0)){continue}break}}g=La(g,Q(l,12)+12|0);c=0;if(!g){break g}K[b>>2]=g;c=K[b+4>>2];g=g+Q(c,12)|0;K[g+8>>2]=15;K[g+4>>2]=e;K[g>>2]=0;K[b+4>>2]=c+1;c=1}if(c){break d}Fa(K[a+32>>2],1,8301,0);b=K[e+116>>2];if(b){Ga(b);K[e+116>>2]=0}b=K[e+120>>2];if(b){Ga(b);K[e+120>>2]=0}Ga(K[e+148>>2]);Ga(e);break b}K[e+144>>2]=K[a+24>>2];S=K[a+40>>2];ba=K[a+36>>2];P=K[a+32>>2];o=K[qa+808>>2];b=K[sa+16>>2];h:{_=K[qa+16>>2];i:{if(_&64){l=ra-304|0;ra=l;j:{if(o){if(ba){Fa(P,1,3182,0);break j}Fa(P,1,3182,0);break j}j=K[e+116>>2];c=K[D+20>>2]-K[D+12>>2]|0;b=K[D+16>>2]-K[D+8>>2]|0;g=Q(c,b);k:{l:{if(g>>>0>N[e+132>>2]){Ga(j);f=g<<2;j=Ma(f);K[e+116>>2]=j;if(!j){j=0;break j}K[e+132>>2]=g;break l}if(!j){break k}f=g<<2}if(!f){break k}B(j,0,f)}j=K[e+120>>2];m:{if(N[e+136>>2]>2639){break m}Ga(j);j=Ma(10560);K[e+120>>2]=j;if(j){break m}j=0;break j}K[e+136>>2]=2640;B(j,0,10560);K[e+128>>2]=c;K[e+124>>2]=b;m=K[D+24>>2];if(!m){j=1;break j}q=K[D+28>>2];j=1;n:{o:{p:{q:{f=K[D+52>>2];r:{if(f){c=K[D+4>>2];j=0;if(f>>>0>=4){b=f&-4;while(1){g=c+(n<<3)|0;j=K[g+28>>2]+(K[g+20>>2]+(K[g+12>>2]+(K[g+4>>2]+j|0)|0)|0)|0;n=n+4|0;r=r+4|0;if((b|0)!=(r|0)){continue}break}}b=f&3;if(b){while(1){j=K[(c+(n<<3)|0)+4>>2]+j|0;n=n+1|0;k=k+1|0;if((b|0)!=(k|0)){continue}break}}if(!K[e+144>>2]&(f|0)==1){break o}if(N[e+152>>2]>=j>>>0){break r}r=La(K[e+148>>2],j);if(r){break q}j=0;break j}if(!K[e+144>>2]){break j}}r=K[e+148>>2];if(r){break p}j=0;break j}K[e+152>>2]=j;K[e+148>>2]=r}if(!K[D+52>>2]){j=0;break n}f=K[D+4>>2];j=0;n=0;while(1){g=n<<3;c=g+f|0;b=K[c+4>>2];if(b){E(j+r|0,K[c>>2],b)}f=K[D+4>>2];j=K[(g+f|0)+4>>2]+j|0;n=n+1|0;if(n>>>0>2]){continue}break}break n}r=K[K[D+4>>2]>>2]}n=0;f=0;c=K[D+40>>2];g=0;s:{if(!c){break s}b=K[D>>2];f=K[b+8>>2];g=0;if((c|0)==1){break s}g=K[b+32>>2]}c=m-q|0;f=f+g|0;t:{if(!f){k=0;break t}n=1;b=K[D>>2];p=K[b>>2];k=0;if((f|0)==1){n=0;break t}k=K[b+24>>2]}Z=c+1|0;ia=K[e+116>>2];aa=K[e+120>>2];S=K[D+12>>2];W=K[D+20>>2];ja=K[D+8>>2];ka=K[D+16>>2];u:{v:{w:{x:{y:{z:{A:{B:{if(!(!n|k)){if(!ba){break B}Fa(P,2,10769,0);f=1;break A}if(f>>>0<4){break A}if(ba){K[l+112>>2]=f;Fa(P,1,9553,l+112|0);break u}K[l+96>>2]=f;Fa(P,1,9553,l+96|0);j=0;break j}Fa(P,2,10769,0);n=K[D+24>>2];if(n>>>0>30){break z}t=1;if(n>>>0>=Z>>>0){break x}break v}n=K[D+24>>2];if(n>>>0<=30){break y}if(!ba){break z}K[l+32>>2]=K[D+24>>2];Fa(P,1,12265,l+32|0);break u}K[l>>2]=n;Fa(P,1,12265,l);j=0;break j}if(n>>>0>>0){break w}if(f>>>0<2){t=f;break x}if((n|0)!=(Z|0)){t=f;break x}t=1;if(L[26336]){break x}if(!ba){I[26336]=1;K[l+64>>2]=f;Fa(P,2,10262,l- -64|0);break x}if(!L[26336]){I[26336]=1;K[l+80>>2]=f;Fa(P,2,10262,l+80|0)}}if(!(!(p>>>0<2|j>>>0

>>0)&k+p>>>0<=j>>>0)){if(ba){j=0;Fa(P,1,9495,0);break j}j=0;Fa(P,1,9495,0);break j}G=p+r|0;b=L[G-1|0];n=b<<4|L[G-2|0]&15;if(!(!(n>>>0<2|(b|0)==255)&(n|0)<=(p|0))){if(ba){j=0;Fa(P,1,15268,0);break j}j=0;Fa(P,1,15268,0);break j}U=K[D+28>>2];K[l+272>>2]=0;K[l+280>>2]=0;K[l+264>>2]=0;K[l+268>>2]=0;K[l+296>>2]=0;K[l+300>>2]=0;K[l+284>>2]=0;K[l+288>>2]=0;b=n-1|0;K[l+276>>2]=b;o=(p+r|0)-n|0;K[l+256>>2]=o;j=L[o|0];c=8;K[l+272>>2]=8;f=o+1|0;K[l+256>>2]=f;g=n-2|0;K[l+276>>2]=g;m=(b|0)==1?j|15:j;b=0;q=b;K[l+264>>2]=m;K[l+268>>2]=b;K[l+280>>2]=!b&(m|0)==255;h=o&3;C:{D:{if((h|0)==3){break D}j=0;if(!((m|0)!=255|(b|0)!=0|L[f|0]<=143)){break C}b=255;b=n>>>0>=3?L[f|0]:b;i=n-3|0;K[l+276>>2]=i;j=!q&(m|0)==255;c=j?15:16;K[l+272>>2]=c;y=f+(n>>>0>2)|0;K[l+256>>2]=y;b=(g|0)==1?b|15:b;g=0;K[l+280>>2]=!g&(b|0)==255;g=b;f=m;b=j?7:8;j=b&31;if((b&63)>>>0>=32){x=f<>>32-j|q<>2]=m;K[l+268>>2]=b;if((h|0)==2){break D}f=255;j=0;if(!((g|0)!=255|(w|0)!=0|L[y|0]<=143)){break C}f=n>>>0>=4?L[y|0]:f;o=n-4|0;K[l+276>>2]=o;y=y+(n>>>0>3)|0;K[l+256>>2]=y;b=(i|0)==1?f|15:f;j=0;i=j;K[l+280>>2]=!i&(b|0)==255;j=!w&(g|0)==255;c=(j?7:8)+c|0;K[l+272>>2]=c;g=b;f=m;b=j?7:8;j=b&31;if((b&63)>>>0>=32){w=f<>>32-j|q<>2]=m;K[l+268>>2]=b;if((h|0)==1){break D}j=0;if(!((g|0)!=255|(i|0)!=0|L[y|0]<=143)){break C}b=255;b=n>>>0>=5?L[y|0]:b;K[l+276>>2]=n-5;K[l+256>>2]=y+(n>>>0>4);j=0;b=(o|0)==1?b|15:b;K[l+280>>2]=!j&(b|0)==255;g=!i&(g|0)==255;c=(g?7:8)+c|0;K[l+272>>2]=c;f=m;g=g?7:8;o=g&31;if((g&63)>>>0>=32){i=f<>>32-o|q<>2]=m;K[l+268>>2]=b}b=m;c=64-c|0;g=c&31;if((c&63)>>>0>=32){i=b<>>32-g|q<>2]=b;K[l+268>>2]=i;j=1}if(!j){if(ba){j=0;Fa(P,1,11433,0);break j}j=0;Fa(P,1,11433,0);break j}A=ka-ja|0;v=n-2|0;K[l+244>>2]=v;y=p+r|0;b=y-3|0;K[l+224>>2]=b;c=L[y-2|0];f=c>>>0>143;K[l+248>>2]=f;q=0;m=c>>>4|0;K[l+232>>2]=m;K[l+236>>2]=0;o=(m&7)==7?3:4;K[l+240>>2]=o;c=(b&3)+1|0;F=c>>>0>>0?c:v;E:{F:{if(!v){j=0;K[l+244>>2]=v-F;break F}c=y-4|0;K[l+224>>2]=c;g=L[b|0];j=g>>>0>143;K[l+248>>2]=j;q=o&31;if((o&63)>>>0>=32){w=g<>>32-q;q=g<>2]=m;q=w;K[l+236>>2]=q;o=(f?(g&127)==127?7:8:8)+o|0;K[l+240>>2]=o;G:{if(F>>>0<2){f=j;break G}j=y-5|0;K[l+224>>2]=j;i=L[c|0];f=i>>>0>143;K[l+248>>2]=f;b=o&31;if((o&63)>>>0>=32){x=i<>>32-b;b=i<>2]=m;K[l+236>>2]=b;o=(g>>>0<=143?8:(i&127)==127?7:8)+o|0;K[l+240>>2]=o;if((F|0)==2){b=c;c=j;break G}g=y-6|0;K[l+224>>2]=g;b=L[j|0];h=b;f=b>>>0>143;K[l+248>>2]=f;c=o&31;if((o&63)>>>0>=32){w=b<>>32-c;c=b<>2]=m;K[l+236>>2]=c;o=(i>>>0<=143?8:(b&127)==127?7:8)+o|0;K[l+240>>2]=o;if((F|0)==3){b=j;c=g;break G}c=y-7|0;K[l+224>>2]=c;b=L[g|0];f=b>>>0>143;K[l+248>>2]=f;j=o&31;if((o&63)>>>0>=32){x=b<>>32-j;j=b<>2]=m;K[l+236>>2]=j;o=(h>>>0<=143?8:(b&127)==127?7:8)+o|0;K[l+240>>2]=o;b=g}g=v-F|0;K[l+244>>2]=g;if(o>>>0>32){break E}if((g|0)>=4){j=K[b-4>>2];K[l+224>>2]=b-5;K[l+244>>2]=g-4;break F}if((g|0)<=0){j=0;break F}x=g&1;H:{if((F|0)==(n-3|0)){h=24;j=0;break H}v=g&2147483646;h=24;j=0;b=c;i=0;while(1){y=b-1|0;K[l+224>>2]=y;w=L[b|0];c=b-2|0;K[l+224>>2]=c;K[l+244>>2]=g-1;b=L[y|0];g=g-2|0;K[l+244>>2]=g;j=w<>2]=c-1;b=L[c|0];K[l+244>>2]=g-1;j=b<>2]=h>>>0>143;g=f?(j&2130706432)==2130706432?7:8:8;c=g+(j>>>0<=2415919103?8:(j&8323072)==8323072?7:8)|0;i=j>>>16&255;b=c+(i>>>0<=143?8:(j&32512)==32512?7:8)|0;w=j>>>8&255;K[l+240>>2]=b+((w>>>0<=143?8:(j&127)==127?7:8)+o|0);b=i<>>24|w<>>0>=32){i=b<>>32-c;b=b<>2]=b|m;K[l+236>>2]=i|q}nc(l+192|0,r,p-n|0,255);h=0;I:{if(t>>>0<2){break I}nc(l+160|0,G,k,0);h=0;if((t|0)==2){break I}m=0;q=0;f=0;K[l+152>>2]=1;K[l+144>>2]=0;K[l+136>>2]=0;K[l+140>>2]=0;c=k-1|0;K[l+148>>2]=c;b=(p+r|0)+k|0;g=b-1|0;K[l+128>>2]=g;n=g&3;J:{if((k|0)<=0){b=g;break J}b=b-2|0;K[l+128>>2]=b;m=L[g|0]}K[l+136>>2]=m;K[l+140>>2]=0;h=m>>>0>143;K[l+152>>2]=h;o=(m&127)==127?7:8;K[l+144>>2]=o;K:{if(!n){break K}p=k-2|0;K[l+148>>2]=p;L:{if((k|0)<2){j=b;break L}j=b-1|0;K[l+128>>2]=j;f=L[b|0]}h=f>>>0>143;K[l+152>>2]=h;b=o&31;if((o&63)>>>0>=32){i=f<>>32-b;b=f<>2]=q;b=i;K[l+140>>2]=b;o=(m>>>0<=143?8:(f&127)==127?7:8)+o|0;K[l+144>>2]=o;if((n|0)==1){b=j;m=q;q=i;k=c;c=p;break K}i=k-3|0;K[l+148>>2]=i;M:{if((k|0)<3){g=j;break M}g=j-1|0;K[l+128>>2]=g;s=L[j|0]}h=s>>>0>143;K[l+152>>2]=h;c=o&31;if((o&63)>>>0>=32){x=s<>>32-c;c=s<>2]=m;K[l+140>>2]=b;o=(f>>>0<=143?8:(s&127)==127?7:8)+o|0;K[l+144>>2]=o;if((n|0)==2){b=g;k=p;c=i;break K}c=k-4|0;K[l+148>>2]=c;f=0;N:{if((k|0)<4){b=g;break N}b=g-1|0;K[l+128>>2]=b;f=L[g|0]}h=f>>>0>143;K[l+152>>2]=h;g=o&31;if((o&63)>>>0>=32){w=f<>>32-g;g=f<>2]=m;K[l+140>>2]=g;o=(s>>>0<=143?8:(f&127)==127?7:8)+o|0;K[l+144>>2]=o;k=i}if(o>>>0<=32){O:{if((k|0)>=5){j=K[b-3>>2];K[l+148>>2]=k-5;K[l+128>>2]=b-4;break O}j=0;if((k|0)<2){break O}k=24;while(1){f=b-1|0;K[l+128>>2]=f;b=L[b|0];g=c-1|0;K[l+148>>2]=g;j=b<>>0>1;b=f;k=k-8|0;c=g;if(i){continue}break}}i=j&255;K[l+152>>2]=i>>>0>143;g=h?(j&2130706432)==2130706432?7:8:8;c=g+(j>>>0<=2415919103?8:(j&8323072)==8323072?7:8)|0;k=j>>>16&255;b=c+(k>>>0<=143?8:(j&32512)==32512?7:8)|0;f=j>>>8&255;K[l+144>>2]=b+((f>>>0<=143?8:(j&127)==127?7:8)+o|0);b=k<>>24|f<>>0>=32){i=b<>>32-c;b=b<>2]=b|m;K[l+140>>2]=i|q}h=1}ca=W-S|0;ea=Z+1|0;I[aa+2112|0]=0;s=aa+2112|0;g=cb(l+256|0);if((A|0)>0){F=U-1|0;b=aa;f=s;o=0;c=ia;r=0;while(1){p=r;n=M[(o<<8|(pb(l+224|0)&127)<<1)+16608>>1];P:{if(o){break P}j=g-2|0;n=(j|0)==-1?n:0;if((g|0)>1){g=j;break P}g=cb(l+256|0)}q=K[l+236>>2];m=K[l+232>>2];j=K[l+240>>2];W=n>>>4|0;v=K[b>>2]|(W&3|n>>>2&48)<>2]=v;y=n&16;o=n>>>5&7|y>>>4;k=j;j=n&7;r=k-j|0;m=((1<>>j;q=q>>>j|0;k=m;j=0;if((A|0)>(p|2)){j=M[(o<<8|(k&127)<<1)+16608>>1];Q:{if(o){break Q}k=g-2|0;j=(k|0)==-1?j:0;if((g|0)>1){g=k;break Q}g=cb(l+256|0)}k=j&7;r=r-k|0;o=j>>>4&1|j>>>5&7;m=((1<>>k;q=q>>>k|0;k=m}K[b>>2]=v|(j<<2&768|j&48)<>>2&2|n>>>3&1;R:{if((x|0)!=3){break R}i=g-2|0;x=(i|0)==-1?4:3;if((g|0)>1){g=i;break R}g=cb(l+256|0)}S:{if(!x){K[l+120>>2]=1;K[l+124>>2]=1;k=0;break S}if(x>>>0<=2){i=L[(k&7)+20756|0];v=i>>>2&7;w=i&3;i=(((-1<>>w)+(i>>>5|0)|0)+1|0;k=(x|0)==1;K[l+124>>2]=k?1:i;K[l+120>>2]=k?i:1;k=v+w|0;break S}u=k;k=L[(k&7)+20756|0];G=k&3;i=u>>>G|0;if((x|0)==3){S=(k>>>5|0)+1|0;if((G|0)==3){K[l+124>>2]=i&1|2;k=k>>>2&7;K[l+120>>2]=S+((-1<>>1);k=k+4|0;break S}v=L[(i&7)+20756|0];w=v&3;i=i>>>w|0;x=k>>>2&7;K[l+120>>2]=S+(i&(-1<>>2&7;K[l+124>>2]=(((-1<>>x)+(v>>>5|0)|0)+1;k=k+(w+(x+G|0)|0)|0;break S}v=L[(i&7)+20756|0];w=v&3;i=i>>>w|0;x=k>>>2&7;K[l+120>>2]=((i&(-1<>>5|0)|0)+3;k=v>>>2&7;K[l+124>>2]=(((-1<>>x)+(v>>>5|0)|0)+3;k=k+(x+(w+G|0)|0)|0}T:{x=K[l+120>>2];if(x>>>0<=ea>>>0){v=K[l+124>>2];if(v>>>0<=ea>>>0){break T}}if(ba){j=0;Fa(P,1,15719,0);break j}j=0;Fa(P,1,15719,0);break j}K[l+240>>2]=r-k;w=k&31;if((k&63)>>>0>=32){i=0;q=q>>>w|0}else{i=q>>>w|0;q=((1<>>w}K[l+232>>2]=q;K[l+236>>2]=i;r=p+4|0;q=(r|0)<=(A|0)?255:255>>>(r-A<<1)|0;S=(ca|0)>1?q:q&85;if((j&240|W&15)&(S^-1)){if(ba){j=0;Fa(P,1,12157,0);break j}j=0;Fa(P,1,12157,0);break j}U:{V:{if(y){m=Qa(l+192|0);w=x+(n<<19>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;V=(m&(-1<>>8&1)<>2]=V}W:{if(n&32){m=Qa(l+192|0);w=x+(n<<18>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;q=m&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?m:q)|128;break W}if(!(S&2)){break W}K[(A<<2)+c>>2]=0}w=c+4|0;X:{Y:{if(n&64){m=Qa(l+192|0);y=x+(n<<17>>31)|0;K[l+208>>2]=K[l+208>>2]-y;k=K[l+204>>2];q=K[l+200>>2];W=y&31;if((y&63)>>>0>=32){i=0;q=k>>>W|0}else{i=k>>>W|0;q=((1<>>W}K[l+200>>2]=q;K[l+204>>2]=i;k=(m&(-1<>>10&1)<>2]=k}I[f+1|0]=0;Z:{if(n&128){m=Qa(l+192|0);y=x+(n<<16>>31)|0;K[l+208>>2]=K[l+208>>2]-y;k=K[l+204>>2];q=K[l+200>>2];x=y&31;if((y&63)>>>0>=32){i=0;q=k>>>x|0}else{i=k>>>x|0;q=((1<>>x}K[l+200>>2]=q;K[l+204>>2]=i;q=m&(-1<>>11&1)<>2]=q+2<>2]=0}n=c+8|0;_:{$:{if(j&16){m=Qa(l+192|0);w=v+(j<<19>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;x=(m&(-1<>>8&1)<>2]=x}aa:{if(j&32){m=Qa(l+192|0);w=v+(j<<18>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;q=m&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?m:q)|128;break aa}if(!(S&32)){break aa}K[n+(A<<2)>>2]=0}n=c+12|0;ba:{ca:{if(j&64){m=Qa(l+192|0);w=v+(j<<17>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;x=(m&(-1<>>10&1)<>2]=x}f=f+2|0;I[f|0]=0;da:{if(j&128){m=Qa(l+192|0);w=v+(j<<16>>31)|0;K[l+208>>2]=K[l+208>>2]-w;k=K[l+204>>2];q=K[l+200>>2];y=w&31;if((w&63)>>>0>=32){i=0;q=k>>>y|0}else{i=k>>>y|0;q=((1<>>y}K[l+200>>2]=q;K[l+204>>2]=i;j=m&(-1<>>11&1)<>2]=j+2<>>0<128){break da}K[n+(A<<2)>>2]=0}X=X^16;b=(p&4)+b|0;c=c+16|0;if((r|0)<(A|0)){continue}break}}la=_&8;ma=aa+1584|0;na=aa+1056|0;ha=aa+528|0;if((ca|0)>=3){oa=Q(A,12);u=A<<3;fa=U-1|0;b=U-2|0;C=3<>>1&2147483644)+4|0;x=2;while(1){y=x;V=L[s|0];I[s|0]=0;X=X&-17^2;ea:{if((A|0)<=0){x=y+2|0;break ea}o=y&4?ha:aa;x=y+2|0;f=ia+(Q(y,A)<<2)|0;v=0;c=s;p=0;while(1){_=p;G=V&255;b=L[c+1|0]>>>5&4|(G>>>7|v);n=M[(b<<8|(pb(l+224|0)&127)<<1)+18656>>1];fa:{if(b){break fa}b=g-2|0;n=(b|0)==-1?n:0;if((g|0)>1){g=b;break fa}g=cb(l+256|0)}j=K[l+236>>2];q=K[l+232>>2];b=K[l+240>>2];k=K[o>>2]|(n>>>4&3|n>>>2&48)<>2]=k;S=n&64;W=n&128;v=S>>>5|W>>>6;m=b;b=n&7;z=m-b|0;m=((1<>>b;q=j>>>b|0;p=m;j=0;if((A|0)>(_|2)){b=L[c+2|0]>>>5&4|L[c+1|0]>>>7|v;j=M[(b<<8|(m&127)<<1)+18656>>1];ga:{if(b){break ga}b=g-2|0;j=(b|0)==-1?j:0;if((g|0)>1){g=b;break ga}g=cb(l+256|0)}b=j&7;z=z-b|0;v=(j>>>5|j>>>6)&2;m=((1<>>b;p=m;q=q>>>b|0}K[o>>2]=k|(j<<2&768|j&48)<>>2&2|n>>>3&1;switch(r|0){case 0:break ha;case 3:break ia;default:break ja}}b=L[(p&7)+20756|0];w=b>>>2&7;k=p;p=b&3;i=(((-1<>>p)+(b>>>5|0)|0)+1|0;b=(r|0)==1;k=b?1:i;b=b?i:1;r=p+w|0;break ha}Z=L[(p&7)+20756|0];k=Z&3;b=p>>>k|0;F=L[(b&7)+20756|0];w=F&3;i=F>>>2&7;p=Z>>>2&7;r=i+(p+(k+w|0)|0)|0;k=b>>>w|0;b=((k&(-1<>>5|0)|0)+1|0;k=(((-1<>>p)+(F>>>5|0)|0)+1|0}K[l+240>>2]=z-r;i=r&31;if((r&63)>>>0>=32){w=0;q=q>>>i|0}else{w=q>>>i|0;q=((1<>>i}K[l+232>>2]=q;K[l+236>>2]=w;p=n&240;if(p-1&p){m=b;q=G&127;b=L[c+1|0]&127;q=b>>>0>>0?q:b;b=q-2|0;b=m+(b>>>0<=q>>>0?b:0)|0}i=j&240;if(i-1&i){m=L[c+1|0]&127;q=L[c+2|0]&127;q=m>>>0>q>>>0?m:q;k=(q>>>0>2?q-2|0:0)+k|0}if(!(b>>>0<=ea>>>0&k>>>0<=ea>>>0)){if(ba){j=0;Fa(P,1,15819,0);break j}j=0;Fa(P,1,15819,0);break j}V=L[c+2|0];I[c+1|0]=0;I[c+2|0]=0;m=i|p>>>4;p=_+4|0;q=(p|0)<=(A|0)?255:255>>>(p-A<<1)|0;F=(x|0)>(ca|0)?q&85:q;if(m&(F^-1)){if(ba){j=0;Fa(P,1,12157,0);break j}j=0;Fa(P,1,12157,0);break j}ka:{la:{if(n&16){m=Qa(l+192|0);r=(n<<19>>31)+b|0;K[l+208>>2]=K[l+208>>2]-r;i=K[l+204>>2];q=K[l+200>>2];G=r&31;if((r&63)>>>0>=32){w=0;q=i>>>G|0}else{w=i>>>G|0;q=((1<>>G}K[l+200>>2]=q;K[l+204>>2]=w;z=(m&(-1<>>8&1)<>2]=z}ma:{if(n&32){m=Qa(l+192|0);r=(n<<18>>31)+b|0;K[l+208>>2]=K[l+208>>2]-r;i=K[l+204>>2];q=K[l+200>>2];G=r&31;if((r&63)>>>0>=32){w=0;q=i>>>G|0}else{w=i>>>G|0;q=((1<>>G}K[l+200>>2]=q;K[l+204>>2]=w;q=m&(-1<>>9&1)<>2]=q+2<>>0>q>>>0?m:q)|128;break ma}if(!(F&2)){break ma}K[(A<<2)+f>>2]=0}r=f+4|0;na:{oa:{if(S){m=Qa(l+192|0);S=(n<<17>>31)+b|0;K[l+208>>2]=K[l+208>>2]-S;i=K[l+204>>2];q=K[l+200>>2];G=S&31;if((S&63)>>>0>=32){w=0;q=i>>>G|0}else{w=i>>>G|0;q=((1<>>G}K[l+200>>2]=q;K[l+204>>2]=w;Y=(m&(-1<>>10&1)<>2]=Y}pa:{if(W){q=Qa(l+192|0);i=(n<<16>>31)+b|0;K[l+208>>2]=K[l+208>>2]-i;m=K[l+204>>2];b=K[l+200>>2];W=i&31;if((i&63)>>>0>=32){w=0;b=m>>>W|0}else{w=m>>>W|0;b=((1<>>W}K[l+200>>2]=b;K[l+204>>2]=w;b=q&(-1<>>11&1)<>2]=b+2<>2]=0}i=f+8|0;qa:{ra:{if(j&16){q=Qa(l+192|0);n=(j<<19>>31)+k|0;K[l+208>>2]=K[l+208>>2]-n;m=K[l+204>>2];b=K[l+200>>2];r=n&31;if((n&63)>>>0>=32){w=0;b=m>>>r|0}else{w=m>>>r|0;b=((1<>>r}K[l+200>>2]=b;K[l+204>>2]=w;b=(q&(-1<>>8&1)<>2]=b}sa:{if(j&32){q=Qa(l+192|0);n=(j<<18>>31)+k|0;K[l+208>>2]=K[l+208>>2]-n;m=K[l+204>>2];b=K[l+200>>2];r=n&31;if((n&63)>>>0>=32){w=0;b=m>>>r|0}else{w=m>>>r|0;b=((1<>>r}K[l+200>>2]=b;K[l+204>>2]=w;b=q&(-1<>>9&1)<>2]=b+2<>>0>>0?q:b)|128;break sa}if(!(F&32)){break sa}K[i+(A<<2)>>2]=0}i=f+12|0;ta:{ua:{if(j&64){q=Qa(l+192|0);n=(j<<17>>31)+k|0;K[l+208>>2]=K[l+208>>2]-n;m=K[l+204>>2];b=K[l+200>>2];r=n&31;if((n&63)>>>0>=32){w=0;b=m>>>r|0}else{w=m>>>r|0;b=((1<>>r}K[l+200>>2]=b;K[l+204>>2]=w;b=(q&(-1<>>10&1)<>2]=b}c=c+2|0;va:{if(j&128){q=Qa(l+192|0);k=(j<<16>>31)+k|0;K[l+208>>2]=K[l+208>>2]-k;m=K[l+204>>2];b=K[l+200>>2];n=k&31;if((k&63)>>>0>=32){w=0;b=m>>>n|0}else{w=m>>>n|0;b=((1<>>n}K[l+200>>2]=b;K[l+204>>2]=w;b=q&(-1<>>11&1)<>2]=b+2<>>0<128){break va}K[i+(A<<2)>>2]=0}X=X^16;o=(_&4)+o|0;f=f+16|0;if((p|0)<(A|0)){continue}break}}wa:{if(!(y&2)|t>>>0<2){break wa}o=x&4;xa:{ya:{if(h){i=o?aa:ha;r=0;if((A|0)<=0){break ya}q=ia+(Q(A,y-2|0)<<2)|0;while(1){j=pb(l+128|0);n=0;f=K[i>>2];if(f){n=q+(r<<2)|0;k=0;c=15;while(1){za:{if(!(c&f)){break za}m=c&286331153;if(m&f){K[n>>2]=H|K[n>>2]^((j^-1)&1)<>>1|0}if(f&m<<1){b=(A<<2)+n|0;K[b>>2]=H|K[b>>2]^((j^-1)&1)<>>1|0}if(f&m<<2){b=n+u|0;K[b>>2]=H|K[b>>2]^((j^-1)&1)<>>1|0}if(!(f&m<<3)){break za}b=n+oa|0;K[b>>2]=H|K[b>>2]^((j^-1)&1)<>>1|0}n=n+4|0;c=c<<4;k=k+1|0;if((k|0)!=8){continue}break}n=Pe(f)}i=i+4|0;K[l+144>>2]=K[l+144>>2]-n;c=K[l+140>>2];b=K[l+136>>2];j=n&31;if((n&63)>>>0>=32){w=0;b=c>>>j|0}else{w=c>>>j|0;b=((1<>>j}K[l+136>>2]=b;K[l+140>>2]=w;r=r+8|0;if((A|0)>(r|0)){continue}break}}r=!o;b=0;j=0;Y=o?na:ma;n=Y;i=o?aa:ha;c=i;if((A|0)<=0){break xa}while(1){q=j>>>28|0;j=K[c>>2];q=j|(q|j<<4|j>>>4);K[n>>2]=q;q=q|K[c+4>>2]<<28;K[n>>2]=(q>>>1&2004318071|q<<1&-286331154|q)&(j^-1);n=n+4|0;c=c+4|0;b=b+8|0;if((A|0)>(b|0)){continue}break}break xa}r=!o;Y=o?na:ma}if(y>>>0<6){break wa}k=0;o=0;n=i;z=r?na:ma;j=z;m=r?aa:ha;c=m;if((A|0)>0){while(1){q=n+4|0;b=K[j>>2];f=K[n>>2];if(!la){b=b|(f|(f<<4|o>>>28|f>>>4|K[q>>2]<<28))<<3&-2004318072}K[j>>2]=(K[c>>2]^-1)&b;c=c+4|0;j=j+4|0;o=f;n=q;k=k+8|0;if((A|0)>(k|0)){continue}break}S=ia+(Q(A,y-6|0)<<2)|0;V=0;o=m;while(1){f=0;c=K[z>>2];if(c){W=V|4;y=A-V|0;j=0;v=0;while(1){q=j;j=Qa(l+160|0);G=(A|0)>(v+W|0)?v+4|0:y;Aa:{if((G|0)<=(v|0)){n=0;break Aa}Z=K[o>>2]^-1;r=((v|V)<<2)+S|0;n=0;k=v;_=k<<2;p=15<<_;b=p;while(1){Ba:{if(!(b&c)){break Ba}F=b&286331153;if(F&c){if(j&1){f=f|F;c=Z&50<<(k<<2)|c}n=n+1|0;j=j>>>1|0}w=F<<1;if(w&c){if(j&1){f=f|w;c=Z&116<<(k<<2)|c}n=n+1|0;j=j>>>1|0}w=F<<2;if(w&c){if(j&1){f=f|w;c=Z&232<<(k<<2)|c}n=n+1|0;j=j>>>1|0}w=F<<3;if(!(w&c)){break Ba}if(j&1){f=f|w;c=Z&192<<(k<<2)|c}n=n+1|0;j=j>>>1|0}b=b<<4;k=k+1|0;if((G|0)>(k|0)){continue}break}if(!(f>>>_&65535)){break Aa}while(1){Ca:{if(!(f&p)){break Ca}k=p&286331153;if(k&f){K[r>>2]=C|(K[r>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(k<<1&f){b=(A<<2)+r|0;K[b>>2]=C|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(k<<2&f){b=r+u|0;K[b>>2]=C|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(!(k<<3&f)){break Ca}b=r+oa|0;K[b>>2]=C|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}p=p<<4;r=r+4|0;v=v+1|0;if((G|0)>(v|0)){continue}break}}K[l+176>>2]=K[l+176>>2]-n;j=K[l+172>>2];b=K[l+168>>2];k=n&31;if((n&63)>>>0>=32){w=0;b=j>>>k|0}else{w=j>>>k|0;b=((1<>>k}K[l+168>>2]=b;K[l+172>>2]=w;j=1;v=4;if(!(q&1)){continue}break}K[z+4>>2]=K[z+4>>2]|(f>>>27&14|f>>>29|f>>>28)&(K[o+4>>2]^-1)}j=K[o>>2]|f;q=j>>>3&286331153;c=q>>>4|q<<4|q;if(V){b=Y-4|0;K[b>>2]=K[b>>2]|(K[i-4>>2]^-1)&q<<28}K[Y>>2]=K[Y>>2]|c&(K[i>>2]^-1);K[Y+4>>2]=K[Y+4>>2]|(K[i+4>>2]^-1)&j>>>31;z=z+4|0;o=o+4|0;Y=Y+4|0;i=i+4|0;V=V+8|0;if((A|0)>(V|0)){continue}break}}if(!da){break wa}B(m,0,da)}if((x|0)<(ca|0)){continue}break}}Da:{if(t>>>0<2){break Da}f=(ca&3)-1|0;Ea:{if(h&f>>>0<2){if((A|0)<=0){break Ea}p=1<>2];if(s){n=m+(v<<2)|0;c=15;k=0;while(1){Fa:{if(!(c&s)){break Fa}o=c&286331153;if(o&s){K[n>>2]=p|K[n>>2]^((j^-1)&1)<>>1|0}if(s&o<<1){b=(A<<2)+n|0;K[b>>2]=p|K[b>>2]^((j^-1)&1)<>>1|0}if(s&o<<2){b=g+n|0;K[b>>2]=p|K[b>>2]^((j^-1)&1)<>>1|0}if(!(s&o<<3)){break Fa}b=n+q|0;K[b>>2]=p|K[b>>2]^((j^-1)&1)<>>1|0}n=n+4|0;c=c<<4;k=k+1|0;if((k|0)!=8){continue}break}n=Pe(s)}da=da+4|0;K[l+144>>2]=K[l+144>>2]-n;c=K[l+140>>2];b=K[l+136>>2];j=n&31;if((n&63)>>>0>=32){w=0;b=c>>>j|0}else{w=c>>>j|0;b=((1<>>j}K[l+136>>2]=b;K[l+140>>2]=w;v=v+8|0;if((A|0)>(v|0)){continue}break}}if((A|0)<=0|f>>>0>1){break Ea}b=ca&4;n=b?ha:aa;c=b?ma:na;b=0;j=0;while(1){g=j>>>28|0;j=K[n>>2];g=j|(g|j<<4|j>>>4);K[c>>2]=g;g=g|K[n+4>>2]<<28;K[c>>2]=(g>>>1&2004318071|g<<1&-286331154|g)&(j^-1);c=c+4|0;n=n+4|0;b=b+8|0;if((A|0)>(b|0)){continue}break}}o=(ca|0)>6?(ca-(ca+1&3)|0)-3|0:0;if((ca|0)<=(o|0)){break Da}W=Q(A,12);v=A<<3;Z=3<>>0>=3){p=-1;if((c|0)<5){break Ha}if(S){break Ga}b=o&4;n=b?ha:aa;j=b?ma:na;c=0;if(!la){c=b?aa:ha;b=0;f=0;while(1){g=f>>>28|0;f=K[c>>2];K[j>>2]=(K[j>>2]|(f|(g|f<<4|f>>>4|K[c+4>>2]<<28))<<3&-2004318072)&(K[n>>2]^-1);n=n+4|0;j=j+4|0;c=c+4|0;b=b+8|0;if((A|0)>(b|0)){continue}break}break Ha}while(1){K[j>>2]=K[j>>2]&(K[n>>2]^-1);n=n+4|0;j=j+4|0;c=c+8|0;if((A|0)>(c|0)){continue}break}break Ha}p=K[(b<<2)+20764>>2]}if(S){break Ga}b=o&4;X=b?ha:aa;V=b?ma:na;g=b?aa:ha;t=b?na:ma;y=ia+(Q(o,A)<<2)|0;x=0;while(1){f=0;c=K[V>>2]&p;if(c){_=x|4;s=A-x|0;j=0;h=0;while(1){q=j;j=Qa(l+160|0);w=(A|0)>(h+_|0)?h+4|0:s;Ia:{if((w|0)<=(h|0)){n=0;break Ia}F=(K[X>>2]^-1)&p;r=y+((h|x)<<2)|0;n=0;k=h;i=k<<2;da=15<>>1|0}m=G<<1;if(m&c){if(j&1){f=f|m;c=F&116<<(k<<2)|c}n=n+1|0;j=j>>>1|0}m=G<<2;if(m&c){if(j&1){f=f|m;c=F&232<<(k<<2)|c}n=n+1|0;j=j>>>1|0}m=G<<3;if(!(m&c)){break Ja}if(j&1){f=f|m;c=F&192<<(k<<2)|c}n=n+1|0;j=j>>>1|0}b=b<<4;k=k+1|0;if((w|0)>(k|0)){continue}break}if(!(f>>>i&65535)){break Ia}while(1){Ka:{if(!(f&da)){break Ka}m=da&286331153;if(m&f){K[r>>2]=Z|(K[r>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(m<<1&f){b=(A<<2)+r|0;K[b>>2]=Z|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(m<<2&f){b=r+v|0;K[b>>2]=Z|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}if(!(m<<3&f)){break Ka}b=r+W|0;K[b>>2]=Z|(K[b>>2]|j<<31);n=n+1|0;j=j>>>1|0}da=da<<4;r=r+4|0;h=h+1|0;if((w|0)>(h|0)){continue}break}}K[l+176>>2]=K[l+176>>2]-n;j=K[l+172>>2];b=K[l+168>>2];m=n&31;if((n&63)>>>0>=32){w=0;b=j>>>m|0}else{w=j>>>m|0;b=((1<>>m}K[l+168>>2]=b;K[l+172>>2]=w;j=1;h=4;if(!(q&1)){continue}break}K[V+4>>2]=K[V+4>>2]|(f>>>27&14|f>>>29|f>>>28)&(K[X+4>>2]^-1)}j=K[X>>2]|f;q=j>>>3&286331153;c=q>>>4|q<<4|q;if(x){b=t-4|0;K[b>>2]=K[b>>2]|(K[g-4>>2]^-1)&q<<28}K[t>>2]=K[t>>2]|c&(K[g>>2]^-1);K[t+4>>2]=K[t+4>>2]|(K[g+4>>2]^-1)&j>>>31;V=V+4|0;X=X+4|0;t=t+4|0;g=g+4|0;x=x+8|0;if((A|0)>(x|0)){continue}break}}o=o+4|0;if((ca|0)>(o|0)){continue}break}}j=1;if((ca|0)<=0|(A|0)<=0){break j}q=A&2147483644;m=A&3;g=ja-ka>>>0>4294967292;o=0;while(1){j=ia+(Q(o,A)<<2)|0;n=0;if(!g){while(1){c=K[j>>2];b=c&2147483647;K[j>>2]=(c|0)<0?0-b|0:b;c=K[j+4>>2];b=c&2147483647;K[j+4>>2]=(c|0)<0?0-b|0:b;c=K[j+8>>2];b=c&2147483647;K[j+8>>2]=(c|0)<0?0-b|0:b;c=K[j+12>>2];b=c&2147483647;K[j+12>>2]=(c|0)<0?0-b|0:b;j=j+16|0;n=n+4|0;if((q|0)!=(n|0)){continue}break}}n=0;if(m){while(1){c=K[j>>2];b=c&2147483647;K[j>>2]=(c|0)<0?0-b|0:b;j=j+4|0;n=n+1|0;if((m|0)!=(n|0)){continue}break}}j=1;o=o+1|0;if((ca|0)!=(o|0)){continue}break}break j}if(!ba){break v}K[l+52>>2]=K[D+24>>2];K[l+48>>2]=Z;Fa(P,1,9649,l+48|0);break u}K[l+20>>2]=n;K[l+16>>2]=Z;Fa(P,1,9649,l+16|0);j=0;break j}j=0}ra=l+304|0;if(j){break i}break b}K[e+108>>2]=(b<<9)+22288;c=0;b=K[e+116>>2];La:{Ma:{i=K[D+16>>2]-K[D+8>>2]|0;k=K[D+20>>2]-K[D+12>>2]|0;g=Q(i,k);Na:{Oa:{Pa:{if(g>>>0>N[e+132>>2]){Ga(b);b=Ma(g<<2);K[e+116>>2]=b;if(!b){break Na}K[e+132>>2]=g;break Pa}if(!b){break Oa}}g=g<<2;if(!g){break Oa}B(b,0,g)}b=K[e+120>>2];p=i+2|0;m=k+3>>>2|0;g=Q(p,m+2|0);if(g>>>0<=N[e+136>>2]){x=g<<2;break Ma}Ga(b);x=g<<2;b=Ma(x);K[e+120>>2]=b;if(b){break Ma}}b=0;break La}K[e+136>>2]=g;if(x){B(b,0,x)}Qa:{if(!p){break Qa}q=K[e+120>>2];b=q;l=i+1|0;if(l>>>0>=7){g=p&-8;while(1){K[b+24>>2]=1226833920;K[b+28>>2]=1226833920;K[b+16>>2]=1226833920;K[b+20>>2]=1226833920;K[b+8>>2]=1226833920;K[b+12>>2]=1226833920;K[b>>2]=1226833920;K[b+4>>2]=1226833920;b=b+32|0;c=c+8|0;if((g|0)!=(c|0)){continue}break}}g=p&7;if(g){c=0;while(1){K[b>>2]=1226833920;b=b+4|0;c=c+1|0;if((g|0)!=(c|0)){continue}break}}b=q+(Q(p,m+1|0)<<2)|0;if(l>>>0>=7){g=p&-8;c=0;while(1){K[b+24>>2]=1226833920;K[b+28>>2]=1226833920;K[b+16>>2]=1226833920;K[b+20>>2]=1226833920;K[b+8>>2]=1226833920;K[b+12>>2]=1226833920;K[b>>2]=1226833920;K[b+4>>2]=1226833920;b=b+32|0;c=c+8|0;if((g|0)!=(c|0)){continue}break}}g=p&7;if(g){c=0;while(1){K[b>>2]=1226833920;b=b+4|0;c=c+1|0;if((g|0)!=(c|0)){continue}break}}b=k&3;if(!b){break Qa}g=(b|0)==1?1224736768:(b|0)==2?1207959552:1073741824;b=q+(Q(m,p)<<2)|0;if(l>>>0>=7){c=p&-8;x=0;while(1){K[b+28>>2]=g;K[b+24>>2]=g;K[b+20>>2]=g;K[b+16>>2]=g;K[b+12>>2]=g;K[b+8>>2]=g;K[b+4>>2]=g;K[b>>2]=g;b=b+32|0;x=x+8|0;if((c|0)!=(x|0)){continue}break}}c=p&7;if(!c){break Qa}x=0;while(1){K[b>>2]=g;b=b+4|0;x=x+1|0;if((c|0)!=(x|0)){continue}break}}K[e+128>>2]=k;K[e+124>>2]=i;b=1}if(!b){break b}x=o+K[D+28>>2]|0;if((x|0)>=31){if(!ba){break h}K[$+16>>2]=x;Fa(P,2,8679,$+16|0);break b}mc(e);bb(e,18,46);bb(e,17,3);bb(e,0,4);if(K[D+64>>2]){break i}q=K[D+52>>2];Ra:{if(!(q>>>0<=1&(!K[e+144>>2]|(q|0)!=1))){b=K[D+4>>2];g=0;if(q-1>>>0>=3){c=q&-4;while(1){l=(r<<3)+b|0;g=K[l+28>>2]+(K[l+20>>2]+(K[l+12>>2]+(K[l+4>>2]+g|0)|0)|0)|0;r=r+4|0;f=f+4|0;if((c|0)!=(f|0)){continue}break}}c=q&3;if(c){while(1){g=K[((r<<3)+b|0)+4>>2]+g|0;r=r+1|0;j=j+1|0;if((c|0)!=(j|0)){continue}break}}ja=K[e+148>>2];c=g+2|0;if(c>>>0>N[e+152>>2]){b=La(ja,c);if(!b){break b}K[e+148>>2]=b;b=b+g|0;I[b|0]=0;I[b+1|0]=0;K[e+152>>2]=c;ja=K[e+148>>2];if(!K[D+52>>2]){break Ra}b=K[D+4>>2]}g=0;r=0;while(1){l=r<<3;c=l+b|0;b=K[c+4>>2];if(b){E(g+ja|0,K[c>>2],b)}b=K[D+4>>2];g=K[(l+b|0)+4>>2]+g|0;r=r+1|0;if(r>>>0>2]){continue}break}break Ra}if((q|0)!=1){break i}ja=K[K[D+4>>2]>>2]}b=K[D+60>>2];if(b){W=K[e+116>>2];K[e+116>>2]=b}if(K[D+44>>2]){aa=_&2;da=_&8;ea=e+28|0;ia=!(_&1);ka=2;while(1){l=Z+ja|0;la=K[D>>2]+Q(F,24)|0;c=K[la>>2];oa=ia|((K[D+28>>2]-4|0)<(x|0)|ka>>>0>1);Sa:{if(!oa){K[e+20>>2]=l;b=c+l|0;K[e+24>>2]=b;J[e+112>>1]=L[b|0]|L[b+1|0]<<8;I[b|0]=255;I[K[e+24>>2]+1|0]=255;K[e+8>>2]=0;K[e>>2]=0;K[e+16>>2]=l;break Sa}K[e+20>>2]=l;b=c+l|0;K[e+24>>2]=b;J[e+112>>1]=L[b|0]|L[b+1|0]<<8;I[b|0]=255;I[K[e+24>>2]+1|0]=255;K[e+104>>2]=e+28;K[e+16>>2]=l;K[e+12>>2]=0;b=c?L[l|0]<<16:16711680;K[e>>2]=b;j=1;c=l+1|0;g=L[l+1|0];Ta:{if(L[l|0]==255){if(g>>>0>=144){K[e+12>>2]=1;b=b|65280;break Ta}K[e+16>>2]=c;j=0;b=b+(g<<9)|0;break Ta}K[e+16>>2]=c;b=b|g<<8}K[e+8>>2]=j;K[e+4>>2]=32768;K[e>>2]=b<<7}y=K[la>>2];Ua:{if(!K[la+8>>2]|(x|0)<=0){break Ua}G=0;w=oa&(aa|0)!=0;while(1){Va:{Wa:{Xa:{switch(ka-1|0){default:if(!oa){b=1<>>1|b;i=K[e+124>>2];m=i<<2;b=(m+K[e+120>>2]|0)+12|0;g=K[e+116>>2];n=0;c=K[e+128>>2];if(c>>>0>=4){if(!i){break Va}d=Q(i,12);q=i<<3;f=0-s|0;while(1){c=0;while(1){l=b;b=K[b>>2];Ya:{if(!b){break Ya}if(!(!(b&495)|b&2097168)){b=K[e>>2];j=K[e+8>>2];Za:{if(j){break Za}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];_a:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break _a}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break Za}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;$a:{if(!(b>>>j&1)){break $a}ab:{if(j){break ab}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];bb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break bb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break ab}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;k=b>>>j&1;K[g>>2]=k?f:s;j=K[e+124>>2];b=l-4|0;K[b>>2]=K[b>>2]|32;K[l+4>>2]=K[l+4>>2]|8;K[l>>2]=K[l>>2]|k<<19|16;if(da){break $a}b=l+(-2-j<<2)|0;K[b+4>>2]=K[b+4>>2]|32768;K[b>>2]=K[b>>2]|k<<31|65536;b=b-4|0;K[b>>2]=K[b>>2]|131072}b=K[l>>2]|2097152;K[l>>2]=b}if(!(!(b&3960)|b&16777344)){b=K[e>>2];j=K[e+8>>2];cb:{if(j){break cb}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];db:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break db}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break cb}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;if(b>>>j&1){eb:{if(j){break eb}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];fb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break fb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break eb}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;j=b>>>j&1;K[g+m>>2]=j?f:s;b=l-4|0;K[b>>2]=K[b>>2]|256;K[l+4>>2]=K[l+4>>2]|64;b=K[l>>2]|j<<22|128}else{b=K[l>>2]}b=b|16777216;K[l>>2]=b}if(!(!(b&31680)|b&134218752)){b=K[e>>2];j=K[e+8>>2];gb:{if(j){break gb}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];hb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break hb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break gb}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;if(b>>>j&1){ib:{if(j){break ib}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];jb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break jb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break ib}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;j=b>>>j&1;K[g+q>>2]=j?f:s;b=l-4|0;K[b>>2]=K[b>>2]|2048;K[l+4>>2]=K[l+4>>2]|512;b=K[l>>2]|j<<25|1024}else{b=K[l>>2]}b=b|134217728;K[l>>2]=b}if(!(b&253440)|b&1073750016){break Ya}b=K[e>>2];j=K[e+8>>2];kb:{if(j){break kb}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];lb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break lb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break kb}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;if(b>>>j&1){mb:{if(j){break mb}j=(b|0)==255;k=K[e+16>>2];b=L[k|0];nb:{if(!j){K[e>>2]=b;K[e+16>>2]=k+1;break nb}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=k+1;j=7;break mb}b=255;K[e>>2]=255}j=8}j=j-1|0;K[e+8>>2]=j;k=b>>>j&1;K[d+g>>2]=k?f:s;j=K[e+124>>2];b=l-4|0;K[b>>2]=K[b>>2]|16384;K[l+4>>2]=K[l+4>>2]|4096;K[l>>2]=K[l>>2]|k<<28|8192;b=l+(j<<2)|0;K[b+4>>2]=K[b+4>>2]|4;K[b+12>>2]=K[b+12>>2]|1;K[b+8>>2]=K[b+8>>2]|k<<18|2}K[l>>2]=K[l>>2]|1073741824}g=g+4|0;b=l+4|0;c=c+1|0;if((i|0)!=(c|0)){continue}break}g=d+g|0;b=l+12|0;n=n+4|0;c=K[e+128>>2];if(n>>>0<(c&-4)>>>0){continue}break}}if(!i|c>>>0<=n>>>0){break Wa}p=0;q=0-s|0;j=c;while(1){ob:{if((j|0)==(n|0)){j=n;break ob}d=b-4|0;k=K[b>>2];r=0;while(1){o=Q(r,3);l=k>>>o|0;if(!(l&2097168|!(l&495))){c=K[e>>2];f=K[e+8>>2];pb:{if(f){break pb}l=(c|0)!=255;j=K[e+16>>2];c=L[j|0];qb:{if(!l){if(c>>>0>=144){c=255;K[e>>2]=255;break qb}K[e>>2]=c;K[e+16>>2]=j+1;f=7;break pb}K[e>>2]=c;K[e+16>>2]=j+1}f=8}f=f-1|0;K[e+8>>2]=f;rb:{if(!(c>>>f&1)){break rb}j=(Q(i,r)<<2)+g|0;sb:{if(f){break sb}l=(c|0)!=255;m=K[e+16>>2];c=L[m|0];tb:{if(!l){if(c>>>0>=144){c=255;K[e>>2]=255;break tb}K[e>>2]=c;K[e+16>>2]=m+1;f=7;break sb}K[e>>2]=c;K[e+16>>2]=m+1}f=8}l=f-1|0;K[e+8>>2]=l;m=j;j=c>>>l&1;K[m>>2]=j?q:s;l=K[e+124>>2];K[d>>2]=K[d>>2]|32<>2]=K[b>>2]|(j<<19|16)<>2]=K[b+4>>2]|8<>2]=K[c+4>>2]|32768;K[c>>2]=K[c>>2]|j<<31|65536;c=c-4|0;K[c>>2]=K[c>>2]|131072}if((r|0)!=3){break rb}c=(l<<2)+b|0;K[c+4>>2]=K[c+4>>2]|4;K[c+12>>2]=K[c+12>>2]|1;K[c+8>>2]=K[c+8>>2]|j<<18|2}k=K[b>>2]|2097152<>2]=k;c=K[e+128>>2]}j=c;r=r+1|0;if(r>>>0>>0){continue}break}}g=g+4|0;b=b+4|0;p=p+1|0;if((i|0)!=(p|0)){continue}break}break Wa}j=0;v=0;p=0;ub:{vb:{wb:{z=K[e+124>>2];if(!((z|0)!=64|K[e+128>>2]!=64)){b=1<>>1|b;l=0-j|0;h=e+28|0;g=K[e+120>>2]+268|0;f=K[e+8>>2];c=K[e+4>>2];k=K[e>>2];n=K[e+104>>2];b=K[e+116>>2];if(_&8){break wb}while(1){p=0;while(1){q=b;m=g;g=K[g>>2];if(g){xb:{if(g&2097168){break xb}b=g&495;if(!b){break xb}n=h+(L[b+K[e+108>>2]|0]<<2)|0;i=K[n>>2];b=K[i>>2];c=c-b|0;yb:{if(k>>>16>>>0>>0){o=K[i+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[i+(d?8:12)>>2];while(1){zb:{if(f){break zb}f=K[e+16>>2];c=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break zb}K[e+16>>2]=c;k=(i<<9)+k|0;f=7;break zb}K[e+16>>2]=c;f=8;k=(i<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?o:!o;break yb}k=k-(b<<16)|0;if(!(c&32768)){o=K[i+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[i+(b?12:8)>>2];while(1){Ab:{if(f){break Ab}f=K[e+16>>2];d=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Ab}K[e+16>>2]=d;k=(i<<9)+k|0;f=7;break Ab}K[e+16>>2]=d;f=8;k=(i<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!o:o;break yb}b=K[i+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>17&4|(K[s>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];Bb:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){Cb:{if(f){break Cb}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Cb}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break Cb}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?i:!i;break Bb}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){Db:{if(f){break Db}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Db}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break Db}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!i:i;break Bb}d=K[t+4>>2]}K[q>>2]=(o|0)==(d|0)?j:l;K[s>>2]=K[s>>2]|32;K[m+4>>2]=K[m+4>>2]|8;b=m-268|0;K[b>>2]=K[b>>2]|131072;b=m-260|0;K[b>>2]=K[b>>2]|32768;b=m-264|0;u=b;i=K[b>>2];b=d^o;K[u>>2]=i|b<<31|65536;g=b<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){o=g>>>3|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;Eb:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){Fb:{if(f){break Fb}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Fb}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break Fb}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Eb}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){Gb:{if(f){break Gb}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Gb}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break Gb}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Eb}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>20&4|(K[s>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];Hb:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){Ib:{if(f){break Ib}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Ib}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break Ib}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Hb}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){Jb:{if(f){break Jb}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Jb}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break Jb}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Hb}b=K[t+4>>2]}K[q+256>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|256;K[m+4>>2]=K[m+4>>2]|64;g=(b^o)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){o=g>>>6|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;Kb:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){Lb:{if(f){break Lb}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Lb}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break Lb}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Kb}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){Mb:{if(f){break Mb}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Mb}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break Mb}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Kb}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>23&4|(K[s>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];Nb:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){Ob:{if(f){break Ob}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Ob}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break Ob}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Nb}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){Pb:{if(f){break Pb}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Pb}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break Pb}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Nb}b=K[t+4>>2]}K[q+512>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|2048;K[m+4>>2]=K[m+4>>2]|512;g=(b^o)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){o=g>>>9|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;Qb:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){Rb:{if(f){break Rb}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Rb}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break Rb}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Qb}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){Sb:{if(f){break Sb}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Sb}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break Sb}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Qb}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>26&4|(K[s>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];Tb:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){Ub:{if(f){break Ub}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Ub}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break Ub}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Tb}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){Vb:{if(f){break Vb}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Vb}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break Vb}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Tb}b=K[t+4>>2]}K[q+768>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|16384;K[m+4>>2]=K[m+4>>2]|4096;K[m+260>>2]=K[m+260>>2]|4;K[m+268>>2]=K[m+268>>2]|1;b=b^o;K[m+264>>2]=K[m+264>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}K[m>>2]=g}g=m+4|0;b=q+4|0;p=p+1|0;if((p|0)!=64){continue}break}g=m+12|0;b=q+772|0;q=v>>>0<60;v=v+4|0;if(q){continue}break}break vb}b=1<>>1|b;q=K[e+120>>2];g=(q+(z<<2)|0)+12|0;b=K[e+128>>2];f=K[e+8>>2];c=K[e+4>>2];k=K[e>>2];n=K[e+104>>2];o=K[e+116>>2];Wb:{if(_&8){Xb:{if(b>>>0<4){break Xb}if(z){r=Q(z,12);t=z<<3;q=0-l|0;H=e+28|0;while(1){C=0;while(1){m=g;g=K[g>>2];if(g){Yb:{if(g&2097168){break Yb}b=g&495;if(!b){break Yb}n=H+(L[b+K[e+108>>2]|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;Zb:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[s+4>>2];if(c&32768){break Zb}i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){_b:{if(f){break _b}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(s<<8)+k|0;break _b}if(s>>>0<=143){K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break _b}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!i:i;break Zb}i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){$b:{if(f){break $b}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(s<<8)+k|0;break $b}if(s>>>0<=143){K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break $b}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?i:!i}if(u){h=m-4|0;d=K[m+4>>2]>>>17&4|(K[h>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));n=H+(L[d+24336|0]<<2)|0;v=K[n>>2];b=K[v>>2];c=c-b|0;i=L[d+24592|0];ac:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[v+4>>2];if(c&32768){break ac}s=K[v+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[v+(b?12:8)>>2];while(1){bc:{if(f){break bc}f=K[e+16>>2];d=f+1|0;v=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(v<<8)+k|0;break bc}if(v>>>0<=143){K[e+16>>2]=d;k=(v<<9)+k|0;f=7;break bc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break ac}s=K[v+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[v+(d?8:12)>>2];while(1){cc:{if(f){break cc}f=K[e+16>>2];c=f+1|0;v=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(v<<8)+k|0;break cc}if(v>>>0<=143){K[e+16>>2]=c;k=(v<<9)+k|0;f=7;break cc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}b=u;K[o>>2]=(i|0)==(b|0)?l:q;K[h>>2]=K[h>>2]|32;K[m+4>>2]=K[m+4>>2]|8;g=(b^i)<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){i=g>>>3|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;dc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break dc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){ec:{if(f){break ec}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break ec}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break ec}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break dc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){fc:{if(f){break fc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break fc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break fc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>20&4|(K[v>>2]>>>22&1|(g>>>15&16|(g>>>19&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=(z<<2)+o|0;i=L[d+24592|0];gc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break gc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){hc:{if(f){break hc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break hc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break hc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break gc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){ic:{if(f){break ic}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break ic}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break ic}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|256;K[m+4>>2]=K[m+4>>2]|64;g=(b^i)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){i=g>>>6|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;jc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break jc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){kc:{if(f){break kc}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break kc}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break kc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break jc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){lc:{if(f){break lc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break lc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break lc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>23&4|(K[v>>2]>>>25&1|(g>>>18&16|(g>>>22&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=o+t|0;i=L[d+24592|0];mc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break mc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){nc:{if(f){break nc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break nc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break nc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break mc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){oc:{if(f){break oc}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break oc}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break oc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|2048;K[m+4>>2]=K[m+4>>2]|512;g=(b^i)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){i=g>>>9|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;pc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break pc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){qc:{if(f){break qc}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break qc}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break qc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break pc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){rc:{if(f){break rc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break rc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break rc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>26&4|(K[v>>2]>>>28&1|(g>>>21&16|(g>>>25&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=o+r|0;i=L[d+24592|0];sc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break sc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){tc:{if(f){break tc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break tc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break tc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break sc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){uc:{if(f){break uc}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break uc}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break uc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|16384;K[m+4>>2]=K[m+4>>2]|4096;d=m+(K[e+124>>2]<<2)|0;K[d+4>>2]=K[d+4>>2]|4;K[d+12>>2]=K[d+12>>2]|1;b=b^i;K[d+8>>2]=K[d+8>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}K[m>>2]=g}g=m+4|0;o=o+4|0;C=C+1|0;if((z|0)!=(C|0)){continue}break}g=m+12|0;o=o+r|0;j=j+4|0;b=K[e+128>>2];if(j>>>0<(b&-4)>>>0){continue}break}break Xb}g=(b&-4)-1|0;j=(g&-4)+4|0;g=(q+(g<<1&-8)|0)+20|0}K[e+8>>2]=f;K[e+4>>2]=c;K[e>>2]=k;K[e+104>>2]=n;if(!z|b>>>0<=j>>>0){break Wb}while(1){c=(b|0)==(j|0);f=0;b=j;if(!c){while(1){lc(e,g,(Q(f,z)<<2)+o|0,l,f,K[e+124>>2]+2|0,1);f=f+1|0;b=K[e+128>>2];if(f>>>0>>0){continue}break}}g=g+4|0;o=o+4|0;p=p+1|0;if((z|0)!=(p|0)){continue}break}break Wb}vc:{if(b>>>0<4){break vc}if(z){r=Q(z,12);t=z<<3;q=0-l|0;H=e+28|0;while(1){C=0;while(1){m=g;g=K[g>>2];if(g){wc:{if(g&2097168){break wc}b=g&495;if(!b){break wc}n=H+(L[b+K[e+108>>2]|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;xc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[s+4>>2];if(c&32768){break xc}i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){yc:{if(f){break yc}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(s<<8)+k|0;break yc}if(s>>>0<=143){K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break yc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!i:i;break xc}i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){zc:{if(f){break zc}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(s<<8)+k|0;break zc}if(s>>>0<=143){K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break zc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?i:!i}if(u){h=m-4|0;d=K[m+4>>2]>>>17&4|(K[h>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));n=H+(L[d+24336|0]<<2)|0;v=K[n>>2];b=K[v>>2];c=c-b|0;i=L[d+24592|0];Ac:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[v+4>>2];if(c&32768){break Ac}s=K[v+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[v+(b?12:8)>>2];while(1){Bc:{if(f){break Bc}f=K[e+16>>2];d=f+1|0;v=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(v<<8)+k|0;break Bc}if(v>>>0<=143){K[e+16>>2]=d;k=(v<<9)+k|0;f=7;break Bc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!s:s;break Ac}s=K[v+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[v+(d?8:12)>>2];while(1){Cc:{if(f){break Cc}f=K[e+16>>2];c=f+1|0;v=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(v<<8)+k|0;break Cc}if(v>>>0<=143){K[e+16>>2]=c;k=(v<<9)+k|0;f=7;break Cc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?s:!s}K[o>>2]=(i|0)==(d|0)?l:q;K[h>>2]=K[h>>2]|32;K[m+4>>2]=K[m+4>>2]|8;b=m+(-2-K[e+124>>2]<<2)|0;K[b+4>>2]=K[b+4>>2]|32768;d=d^i;K[b>>2]=K[b>>2]|d<<31|65536;b=b-4|0;K[b>>2]=K[b>>2]|131072;g=d<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){i=g>>>3|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;Dc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break Dc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){Ec:{if(f){break Ec}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break Ec}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break Ec}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break Dc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){Fc:{if(f){break Fc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break Fc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break Fc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>20&4|(K[v>>2]>>>22&1|(g>>>15&16|(g>>>19&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=(z<<2)+o|0;i=L[d+24592|0];Gc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break Gc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){Hc:{if(f){break Hc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break Hc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break Hc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break Gc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){Ic:{if(f){break Ic}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break Ic}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break Ic}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|256;K[m+4>>2]=K[m+4>>2]|64;g=(b^i)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){i=g>>>6|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;Jc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break Jc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){Kc:{if(f){break Kc}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break Kc}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break Kc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break Jc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){Lc:{if(f){break Lc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break Lc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break Lc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>23&4|(K[v>>2]>>>25&1|(g>>>18&16|(g>>>22&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=o+t|0;i=L[d+24592|0];Mc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break Mc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){Nc:{if(f){break Nc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break Nc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break Nc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break Mc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){Oc:{if(f){break Oc}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break Oc}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break Oc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|2048;K[m+4>>2]=K[m+4>>2]|512;g=(b^i)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){i=g>>>9|0;n=H+(L[K[e+108>>2]+(i&495)|0]<<2)|0;h=K[n>>2];b=K[h>>2];c=c-b|0;Pc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;u=K[h+4>>2];if(c&32768){break Pc}s=K[h+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[h+(b?12:8)>>2];while(1){Qc:{if(f){break Qc}f=K[e+16>>2];d=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(h<<8)+k|0;break Qc}if(h>>>0<=143){K[e+16>>2]=d;k=(h<<9)+k|0;f=7;break Qc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}u=b?!s:s;break Pc}s=K[h+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[h+(d?8:12)>>2];while(1){Rc:{if(f){break Rc}f=K[e+16>>2];c=f+1|0;h=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(h<<8)+k|0;break Rc}if(h>>>0<=143){K[e+16>>2]=c;k=(h<<9)+k|0;f=7;break Rc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;u=d?s:!s}if(u){v=m-4|0;d=K[m+4>>2]>>>26&4|(K[v>>2]>>>28&1|(g>>>21&16|(g>>>25&64|i&170)));n=H+(L[d+24336|0]<<2)|0;u=K[n>>2];b=K[u>>2];c=c-b|0;s=o+r|0;i=L[d+24592|0];Sc:{if(k>>>16>>>0>=b>>>0){k=k-(b<<16)|0;d=K[u+4>>2];if(c&32768){break Sc}h=K[u+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[u+(b?12:8)>>2];while(1){Tc:{if(f){break Tc}f=K[e+16>>2];d=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=d;f=8;k=(u<<8)+k|0;break Tc}if(u>>>0<=143){K[e+16>>2]=d;k=(u<<9)+k|0;f=7;break Tc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}d=b?!h:h;break Sc}h=K[u+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[u+(d?8:12)>>2];while(1){Uc:{if(f){break Uc}f=K[e+16>>2];c=f+1|0;u=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=c;f=8;k=(u<<8)+k|0;break Uc}if(u>>>0<=143){K[e+16>>2]=c;k=(u<<9)+k|0;f=7;break Uc}K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;d=d?h:!h}b=d;K[s>>2]=(i|0)==(b|0)?l:q;K[v>>2]=K[v>>2]|16384;K[m+4>>2]=K[m+4>>2]|4096;d=m+(K[e+124>>2]<<2)|0;K[d+4>>2]=K[d+4>>2]|4;K[d+12>>2]=K[d+12>>2]|1;b=b^i;K[d+8>>2]=K[d+8>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}K[m>>2]=g}g=m+4|0;o=o+4|0;C=C+1|0;if((z|0)!=(C|0)){continue}break}g=m+12|0;o=o+r|0;j=j+4|0;b=K[e+128>>2];if(j>>>0<(b&-4)>>>0){continue}break}break vc}g=(b&-4)-1|0;j=(g&-4)+4|0;g=(q+(g<<1&-8)|0)+20|0}K[e+8>>2]=f;K[e+4>>2]=c;K[e>>2]=k;K[e+104>>2]=n;if(!z|b>>>0<=j>>>0){break Wb}while(1){c=(b|0)==(j|0);f=0;b=j;if(!c){while(1){lc(e,g,(Q(f,z)<<2)+o|0,l,f,K[e+124>>2]+2|0,0);f=f+1|0;b=K[e+128>>2];if(f>>>0>>0){continue}break}}g=g+4|0;o=o+4|0;p=p+1|0;if((z|0)!=(p|0)){continue}break}}break ub}while(1){p=0;while(1){q=b;m=g;g=K[g>>2];if(g){Vc:{if(g&2097168){break Vc}b=g&495;if(!b){break Vc}n=h+(L[b+K[e+108>>2]|0]<<2)|0;i=K[n>>2];b=K[i>>2];c=c-b|0;Wc:{if(k>>>16>>>0>>0){o=K[i+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[i+(d?8:12)>>2];while(1){Xc:{if(f){break Xc}f=K[e+16>>2];c=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Xc}K[e+16>>2]=c;k=(i<<9)+k|0;f=7;break Xc}K[e+16>>2]=c;f=8;k=(i<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?o:!o;break Wc}k=k-(b<<16)|0;if(!(c&32768)){o=K[i+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[i+(b?12:8)>>2];while(1){Yc:{if(f){break Yc}f=K[e+16>>2];d=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break Yc}K[e+16>>2]=d;k=(i<<9)+k|0;f=7;break Yc}K[e+16>>2]=d;f=8;k=(i<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!o:o;break Wc}b=K[i+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>17&4|(K[s>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];Zc:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){_c:{if(f){break _c}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break _c}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break _c}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break Zc}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){$c:{if(f){break $c}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break $c}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break $c}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break Zc}b=K[t+4>>2]}K[q>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|32;K[m+4>>2]=K[m+4>>2]|8;g=(b^o)<<19|g|16}g=g|2097152}if(!(!(g&3960)|g&16777344)){o=g>>>3|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;ad:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){bd:{if(f){break bd}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break bd}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break bd}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break ad}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){cd:{if(f){break cd}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break cd}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break cd}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break ad}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>20&4|(K[s>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];dd:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){ed:{if(f){break ed}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break ed}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break ed}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break dd}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){fd:{if(f){break fd}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break fd}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break fd}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break dd}b=K[t+4>>2]}K[q+256>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|256;K[m+4>>2]=K[m+4>>2]|64;g=(b^o)<<22|g|128}g=g|16777216}if(!(!(g&31680)|g&134218752)){o=g>>>6|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;gd:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){hd:{if(f){break hd}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break hd}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break hd}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break gd}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){id:{if(f){break id}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break id}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break id}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break gd}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>23&4|(K[s>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];jd:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){kd:{if(f){break kd}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break kd}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break kd}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break jd}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){ld:{if(f){break ld}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break ld}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break ld}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break jd}b=K[t+4>>2]}K[q+512>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|2048;K[m+4>>2]=K[m+4>>2]|512;g=(b^o)<<25|g|1024}g=g|134217728}if(!(!(g&253440)|g&1073750016)){o=g>>>9|0;n=h+(L[K[e+108>>2]+(o&495)|0]<<2)|0;s=K[n>>2];b=K[s>>2];c=c-b|0;md:{if(k>>>16>>>0>>0){i=K[s+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[s+(d?8:12)>>2];while(1){nd:{if(f){break nd}f=K[e+16>>2];c=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break nd}K[e+16>>2]=c;k=(s<<9)+k|0;f=7;break nd}K[e+16>>2]=c;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break md}k=k-(b<<16)|0;if(!(c&32768)){i=K[s+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[s+(b?12:8)>>2];while(1){od:{if(f){break od}f=K[e+16>>2];d=f+1|0;s=L[f+1|0];if(L[f|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break od}K[e+16>>2]=d;k=(s<<9)+k|0;f=7;break od}K[e+16>>2]=d;f=8;k=(s<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break md}b=K[s+4>>2]}if(b){s=m-4|0;d=K[m+4>>2]>>>26&4|(K[s>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));n=h+(L[d+24336|0]<<2)|0;t=K[n>>2];b=K[t>>2];c=c-b|0;o=L[d+24592|0];pd:{if(k>>>16>>>0>>0){i=K[t+4>>2];d=b>>>0>c>>>0;K[n>>2]=K[t+(d?8:12)>>2];while(1){qd:{if(f){break qd}f=K[e+16>>2];c=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break qd}K[e+16>>2]=c;k=(t<<9)+k|0;f=7;break qd}K[e+16>>2]=c;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;b=b<<1;if(b>>>0<32768){continue}break}c=b;b=d?i:!i;break pd}k=k-(b<<16)|0;if(!(c&32768)){i=K[t+4>>2];b=b>>>0>c>>>0;K[n>>2]=K[t+(b?12:8)>>2];while(1){rd:{if(f){break rd}f=K[e+16>>2];d=f+1|0;t=L[f+1|0];if(L[f|0]==255){if(t>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;k=k+65280|0;f=8;break rd}K[e+16>>2]=d;k=(t<<9)+k|0;f=7;break rd}K[e+16>>2]=d;f=8;k=(t<<8)+k|0}f=f-1|0;k=k<<1;c=c<<1;if(c>>>0<32768){continue}break}b=b?!i:i;break pd}b=K[t+4>>2]}K[q+768>>2]=(o|0)==(b|0)?j:l;K[s>>2]=K[s>>2]|16384;K[m+4>>2]=K[m+4>>2]|4096;K[m+260>>2]=K[m+260>>2]|4;K[m+268>>2]=K[m+268>>2]|1;b=b^o;K[m+264>>2]=K[m+264>>2]|b<<18|2;g=b<<28|g|8192}g=g|1073741824}K[m>>2]=g}g=m+4|0;b=q+4|0;p=p+1|0;if((p|0)!=64){continue}break}g=m+12|0;b=q+772|0;q=v>>>0<60;v=v+4|0;if(q){continue}break}}K[e+8>>2]=f;K[e+4>>2]=c;K[e>>2]=k;K[e+104>>2]=n}break Wa;case 0:if(!oa){t=1<>>1|0;s=K[e+124>>2];d=s<<2;b=(d+K[e+120>>2]|0)+12|0;g=K[e+116>>2];k=0;c=K[e+128>>2];if(c>>>0>=4){if(!s){break Va}o=Q(s,12);m=s<<3;i=0-t|0;while(1){c=0;while(1){l=b;b=K[b>>2];sd:{if(!b){break sd}if((b&2097168)==16){b=K[e>>2];h=K[e+8>>2];td:{if(h){break td}j=(b|0)==255;q=K[e+16>>2];b=L[q|0];ud:{if(!j){K[e>>2]=b;K[e+16>>2]=q+1;break ud}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=q+1;h=7;break td}b=255;K[e>>2]=255}h=8}j=h-1|0;K[e+8>>2]=j;j=b>>>j&1;b=K[g>>2];K[g>>2]=((j|0)==(b>>>31|0)?i:t)+b;b=K[l>>2]|1048576;K[l>>2]=b}if((b&16777344)==128){b=K[e>>2];h=K[e+8>>2];vd:{if(h){break vd}j=(b|0)==255;q=K[e+16>>2];b=L[q|0];wd:{if(!j){K[e>>2]=b;K[e+16>>2]=q+1;break wd}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=q+1;h=7;break vd}b=255;K[e>>2]=255}h=8}q=h-1|0;K[e+8>>2]=q;j=d+g|0;f=K[j>>2];K[j>>2]=f+((b>>>q&1)==(f>>>31|0)?i:t);b=K[l>>2]|8388608;K[l>>2]=b}if((b&134218752)==1024){b=K[e>>2];h=K[e+8>>2];xd:{if(h){break xd}j=(b|0)==255;q=K[e+16>>2];b=L[q|0];yd:{if(!j){K[e>>2]=b;K[e+16>>2]=q+1;break yd}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=q+1;h=7;break xd}b=255;K[e>>2]=255}h=8}q=h-1|0;K[e+8>>2]=q;j=g+m|0;f=K[j>>2];K[j>>2]=f+((b>>>q&1)==(f>>>31|0)?i:t);b=K[l>>2]|67108864;K[l>>2]=b}if((b&1073750016)!=8192){break sd}b=K[e>>2];h=K[e+8>>2];zd:{if(h){break zd}j=(b|0)==255;q=K[e+16>>2];b=L[q|0];Ad:{if(!j){K[e>>2]=b;K[e+16>>2]=q+1;break Ad}if(b>>>0<=143){K[e>>2]=b;K[e+16>>2]=q+1;h=7;break zd}b=255;K[e>>2]=255}h=8}q=h-1|0;K[e+8>>2]=q;j=g+o|0;f=K[j>>2];K[j>>2]=f+((b>>>q&1)==(f>>>31|0)?i:t);K[l>>2]=K[l>>2]|536870912}g=g+4|0;b=l+4|0;c=c+1|0;if((s|0)!=(c|0)){continue}break}g=g+o|0;b=l+12|0;k=k+4|0;c=K[e+128>>2];if(k>>>0<(c&-4)>>>0){continue}break}}if(!s|c>>>0<=k>>>0){break Wa}p=0;j=0-t|0;d=c;while(1){Bd:{if((d|0)==(k|0)){d=k;break Bd}h=K[b>>2];r=0;while(1){d=Q(r,3);if((2097168<>2];n=K[e+8>>2];Cd:{if(n){break Cd}l=(c|0)!=255;q=K[e+16>>2];c=L[q|0];Dd:{if(!l){if(c>>>0>=144){c=255;K[e>>2]=255;break Dd}K[e>>2]=c;K[e+16>>2]=q+1;n=7;break Cd}K[e>>2]=c;K[e+16>>2]=q+1}n=8}l=n-1|0;K[e+8>>2]=l;l=c>>>l&1;c=K[m>>2];K[m>>2]=((l|0)==(c>>>31|0)?j:t)+c;h=K[b>>2]|1048576<>2]=h;c=K[e+128>>2]}r=r+1|0;d=c;if(r>>>0>>0){continue}break}}g=g+4|0;b=b+4|0;p=p+1|0;if((s|0)!=(p|0)){continue}break}break Wa}j=K[e+120>>2];d=K[e+116>>2];u=K[e+124>>2];c=K[e+128>>2];if(!((u|0)!=64|(c|0)!=64)){c=j+268|0;v=0;s=1<>>1|0;p=0-s|0;r=K[e+8>>2];g=K[e+4>>2];b=K[e>>2];k=K[e+104>>2];while(1){n=0;while(1){q=d;j=c;d=K[c>>2];if(d){l=c;if((d&2097168)==16){k=ea+((d&1048576?16:d&495?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Ed:{if(b>>>16>>>0>>0){o=K[f+4>>2];m=c>>>0>g>>>0;K[k>>2]=K[f+(m?8:12)>>2];while(1){Fd:{if(r){break Fd}f=K[e+16>>2];g=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Fd}K[e+16>>2]=g;b=(i<<9)+b|0;r=7;break Fd}K[e+16>>2]=g;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;m=m?o:!o;break Ed}b=b-(c<<16)|0;if(!(g&32768)){o=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Gd:{if(r){break Gd}f=K[e+16>>2];m=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Gd}K[e+16>>2]=m;b=(i<<9)+b|0;r=7;break Gd}K[e+16>>2]=m;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}m=c?!o:o;break Ed}m=K[f+4>>2]}c=K[q>>2];K[q>>2]=((m|0)==(c>>>31|0)?p:s)+c;d=d|1048576}if((d&16777344)==128){k=ea+((d&8388608?16:d&3960?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Hd:{if(b>>>16>>>0>>0){o=K[f+4>>2];m=c>>>0>g>>>0;K[k>>2]=K[f+(m?8:12)>>2];while(1){Id:{if(r){break Id}f=K[e+16>>2];g=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Id}K[e+16>>2]=g;b=(i<<9)+b|0;r=7;break Id}K[e+16>>2]=g;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;m=m?o:!o;break Hd}b=b-(c<<16)|0;if(!(g&32768)){o=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Jd:{if(r){break Jd}f=K[e+16>>2];m=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Jd}K[e+16>>2]=m;b=(i<<9)+b|0;r=7;break Jd}K[e+16>>2]=m;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}m=c?!o:o;break Hd}m=K[f+4>>2]}c=K[q+256>>2];K[q+256>>2]=((m|0)==(c>>>31|0)?p:s)+c;d=d|8388608}if((d&134218752)==1024){k=ea+((d&67108864?16:d&31680?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Kd:{if(b>>>16>>>0>>0){o=K[f+4>>2];m=c>>>0>g>>>0;K[k>>2]=K[f+(m?8:12)>>2];while(1){Ld:{if(r){break Ld}f=K[e+16>>2];g=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Ld}K[e+16>>2]=g;b=(i<<9)+b|0;r=7;break Ld}K[e+16>>2]=g;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;m=m?o:!o;break Kd}b=b-(c<<16)|0;if(!(g&32768)){o=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Md:{if(r){break Md}f=K[e+16>>2];m=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Md}K[e+16>>2]=m;b=(i<<9)+b|0;r=7;break Md}K[e+16>>2]=m;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}m=c?!o:o;break Kd}m=K[f+4>>2]}c=K[q+512>>2];K[q+512>>2]=((m|0)==(c>>>31|0)?p:s)+c;d=d|67108864}if((d&1073750016)==8192){k=ea+((d&536870912?16:d&253440?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Nd:{if(b>>>16>>>0>>0){o=K[f+4>>2];m=c>>>0>g>>>0;K[k>>2]=K[f+(m?8:12)>>2];while(1){Od:{if(r){break Od}f=K[e+16>>2];g=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Od}K[e+16>>2]=g;b=(i<<9)+b|0;r=7;break Od}K[e+16>>2]=g;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;m=m?o:!o;break Nd}b=b-(c<<16)|0;if(!(g&32768)){o=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Pd:{if(r){break Pd}f=K[e+16>>2];m=f+1|0;i=L[f+1|0];if(L[f|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8;break Pd}K[e+16>>2]=m;b=(i<<9)+b|0;r=7;break Pd}K[e+16>>2]=m;r=8;b=(i<<8)+b|0}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}m=c?!o:o;break Nd}m=K[f+4>>2]}c=K[q+768>>2];K[q+768>>2]=((m|0)==(c>>>31|0)?p:s)+c;d=d|536870912}K[l>>2]=d}c=j+4|0;d=q+4|0;n=n+1|0;if((n|0)!=64){continue}break}c=j+12|0;d=q+772|0;l=v>>>0<60;v=v+4|0;if(l){continue}break}K[e+8>>2]=r;K[e+4>>2]=g;K[e>>2]=b;K[e+104>>2]=k;break Wa}v=1<>>1|0;i=u<<2;f=(i+j|0)+12|0;r=K[e+8>>2];g=K[e+4>>2];b=K[e>>2];k=K[e+104>>2];o=0;Qd:{if(c>>>0<4){break Qd}if(u){s=Q(u,12);m=u<<3;h=0-v|0;while(1){n=0;while(1){l=f;j=K[f>>2];if(j){if((j&2097168)==16){k=ea+((j&1048576?16:j&495?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Rd:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=K[f+4>>2];if(g&32768){break Rd}p=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Sd:{if(r){break Sd}f=K[e+16>>2];q=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=q;r=8;b=(t<<8)+b|0;break Sd}if(t>>>0<=143){K[e+16>>2]=q;b=(t<<9)+b|0;r=7;break Sd}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!p:p;break Rd}p=K[f+4>>2];q=c>>>0>g>>>0;K[k>>2]=K[f+(q?8:12)>>2];while(1){Td:{if(r){break Td}f=K[e+16>>2];g=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=g;r=8;b=(t<<8)+b|0;break Td}if(t>>>0<=143){K[e+16>>2]=g;b=(t<<9)+b|0;r=7;break Td}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?p:!p}c=K[d>>2];K[d>>2]=((q|0)==(c>>>31|0)?h:v)+c;j=j|1048576}if((j&16777344)==128){k=ea+((j&8388608?16:j&3960?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Ud:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=K[f+4>>2];if(g&32768){break Ud}p=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Vd:{if(r){break Vd}f=K[e+16>>2];q=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=q;r=8;b=(t<<8)+b|0;break Vd}if(t>>>0<=143){K[e+16>>2]=q;b=(t<<9)+b|0;r=7;break Vd}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!p:p;break Ud}p=K[f+4>>2];q=c>>>0>g>>>0;K[k>>2]=K[f+(q?8:12)>>2];while(1){Wd:{if(r){break Wd}f=K[e+16>>2];g=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=g;r=8;b=(t<<8)+b|0;break Wd}if(t>>>0<=143){K[e+16>>2]=g;b=(t<<9)+b|0;r=7;break Wd}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?p:!p}f=q;c=d+i|0;q=K[c>>2];K[c>>2]=q+((f|0)==(q>>>31|0)?h:v);j=j|8388608}if((j&134218752)==1024){k=ea+((j&67108864?16:j&31680?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;Xd:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=K[f+4>>2];if(g&32768){break Xd}p=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){Yd:{if(r){break Yd}f=K[e+16>>2];q=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=q;r=8;b=(t<<8)+b|0;break Yd}if(t>>>0<=143){K[e+16>>2]=q;b=(t<<9)+b|0;r=7;break Yd}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!p:p;break Xd}p=K[f+4>>2];q=c>>>0>g>>>0;K[k>>2]=K[f+(q?8:12)>>2];while(1){Zd:{if(r){break Zd}f=K[e+16>>2];g=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=g;r=8;b=(t<<8)+b|0;break Zd}if(t>>>0<=143){K[e+16>>2]=g;b=(t<<9)+b|0;r=7;break Zd}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?p:!p}f=q;c=d+m|0;q=K[c>>2];K[c>>2]=q+((f|0)==(q>>>31|0)?h:v);j=j|67108864}if((j&1073750016)==8192){k=ea+((j&536870912?16:j&253440?15:14)<<2)|0;f=K[k>>2];c=K[f>>2];g=g-c|0;_d:{if(b>>>16>>>0>=c>>>0){b=b-(c<<16)|0;q=K[f+4>>2];if(g&32768){break _d}p=K[f+4>>2];c=c>>>0>g>>>0;K[k>>2]=K[f+(c?12:8)>>2];while(1){$d:{if(r){break $d}f=K[e+16>>2];q=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=q;r=8;b=(t<<8)+b|0;break $d}if(t>>>0<=143){K[e+16>>2]=q;b=(t<<9)+b|0;r=7;break $d}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;g=g<<1;if(g>>>0<32768){continue}break}q=c?!p:p;break _d}p=K[f+4>>2];q=c>>>0>g>>>0;K[k>>2]=K[f+(q?8:12)>>2];while(1){ae:{if(r){break ae}f=K[e+16>>2];g=f+1|0;t=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=g;r=8;b=(t<<8)+b|0;break ae}if(t>>>0<=143){K[e+16>>2]=g;b=(t<<9)+b|0;r=7;break ae}K[e+12>>2]=K[e+12>>2]+1;b=b+65280|0;r=8}r=r-1|0;b=b<<1;c=c<<1;if(c>>>0<32768){continue}break}g=c;q=q?p:!p}f=q;c=d+s|0;q=K[c>>2];K[c>>2]=q+((f|0)==(q>>>31|0)?h:v);j=j|536870912}K[l>>2]=j}f=l+4|0;d=d+4|0;n=n+1|0;if((u|0)!=(n|0)){continue}break}f=l+12|0;d=d+s|0;o=o+4|0;c=K[e+128>>2];if(o>>>0<(c&-4)>>>0){continue}break}break Qd}l=(c&-4)-1|0;o=(l&-4)+4|0;f=(j+(l<<1&-8)|0)+20|0}K[e+8>>2]=r;K[e+4>>2]=g;K[e>>2]=b;K[e+104>>2]=k;if(!u|c>>>0<=o>>>0){break Wa}C=0;l=0-v|0;b=c;while(1){be:{if((b|0)==(o|0)){b=o;break be}r=K[f>>2];h=0;while(1){p=Q(h,3);if((2097168<>>p|0;j=ea+((b&1048576?16:b&495?15:14)<<2)|0;K[e+104>>2]=j;q=K[j>>2];b=K[q>>2];c=K[e+4>>2]-b|0;K[e+4>>2]=c;g=K[e>>2];ce:{if(g>>>16>>>0>>0){m=K[q+4>>2];K[e+4>>2]=b;c=b>>>0>c>>>0;K[j>>2]=K[q+(c?8:12)>>2];r=K[e+8>>2];while(1){de:{if(r){break de}q=K[e+16>>2];j=q+1|0;i=L[q+1|0];if(L[q|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;g=g+65280|0;r=8;break de}K[e+16>>2]=j;g=(i<<9)+g|0;r=7;break de}K[e+16>>2]=j;r=8;g=(i<<8)+g|0}r=r-1|0;K[e+8>>2]=r;g=g<<1;K[e>>2]=g;b=b<<1;K[e+4>>2]=b;if(b>>>0<32768){continue}break}c=c?m:!m;break ce}g=g-(b<<16)|0;K[e>>2]=g;if(!(c&32768)){m=K[q+4>>2];b=b>>>0>c>>>0;K[j>>2]=K[q+(b?12:8)>>2];r=K[e+8>>2];while(1){ee:{if(r){break ee}q=K[e+16>>2];j=q+1|0;i=L[q+1|0];if(L[q|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;g=g+65280|0;r=8;break ee}K[e+16>>2]=j;g=(i<<9)+g|0;r=7;break ee}K[e+16>>2]=j;r=8;g=(i<<8)+g|0}r=r-1|0;K[e+8>>2]=r;g=g<<1;K[e>>2]=g;c=c<<1;K[e+4>>2]=c;if(c>>>0<32768){continue}break}c=b?!m:m;break ce}c=K[q+4>>2]}b=K[k>>2];K[k>>2]=((c|0)==(b>>>31|0)?l:v)+b;r=K[f>>2]|1048576<>2]=r;c=K[e+128>>2]}h=h+1|0;b=c;if(h>>>0>>0){continue}break}}f=f+4|0;d=d+4|0;C=C+1|0;if((u|0)!=(C|0)){continue}break};break Wa;case 1:break Xa}}H=0;v=0;fe:{ge:{he:{U=K[e+124>>2];if(!((U|0)!=64|K[e+128>>2]!=64)){b=1<>>1|b;u=0-C|0;q=e+100|0;l=e+96|0;z=e+28|0;g=K[e+120>>2]+268|0;h=K[e+8>>2];b=K[e+4>>2];d=K[e>>2];j=K[e+104>>2];c=K[e+116>>2];if(_&8){break he}while(1){t=0;while(1){k=c;f=g;g=K[g>>2];ie:{je:{ke:{if(!g){j=K[l>>2];g=K[j>>2];b=b-g|0;le:{if(d>>>16>>>0>>0){m=K[j+4>>2];c=b>>>0>>0;K[l>>2]=K[j+(c?8:12)>>2];while(1){me:{if(h){break me}j=K[e+16>>2];b=j+1|0;o=L[j+1|0];if(L[j|0]==255){if(o>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break me}K[e+16>>2]=b;d=(o<<9)+d|0;h=7;break me}K[e+16>>2]=b;h=8;d=(o<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?m:!m;break le}d=d-(g<<16)|0;if(!(b&32768)){m=K[j+4>>2];c=b>>>0>>0;K[l>>2]=K[j+(c?12:8)>>2];while(1){ne:{if(h){break ne}j=K[e+16>>2];g=j+1|0;o=L[j+1|0];if(L[j|0]==255){if(o>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ne}K[e+16>>2]=g;d=(o<<9)+d|0;h=7;break ne}K[e+16>>2]=g;h=8;d=(o<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break le}c=K[j+4>>2]}if(!c){j=l;break ie}c=K[q>>2];g=K[c>>2];b=b-g|0;oe:{if(d>>>16>>>0>>0){o=K[c+4>>2];j=b>>>0>>0;c=K[(j?8:12)+c>>2];K[q>>2]=c;while(1){pe:{if(h){break pe}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break pe}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break pe}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;m=j?o:!o;break oe}d=d-(g<<16)|0;if(!(b&32768)){o=K[c+4>>2];g=b>>>0>>0;c=K[(g?12:8)+c>>2];K[q>>2]=c;while(1){qe:{if(h){break qe}m=K[e+16>>2];j=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break qe}K[e+16>>2]=j;d=(i<<9)+d|0;h=7;break qe}K[e+16>>2]=j;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=g?!o:o;break oe}m=K[c+4>>2]}g=K[c>>2];b=b-g|0;re:{if(d>>>16>>>0>>0){o=K[c+4>>2];j=c;c=b>>>0>>0;K[q>>2]=K[j+(c?8:12)>>2];while(1){se:{if(h){break se}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break se}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break se}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?o:!o;break re}d=d-(g<<16)|0;if(!(b&32768)){o=K[c+4>>2];j=c;c=b>>>0>>0;K[q>>2]=K[j+(c?12:8)>>2];while(1){te:{if(h){break te}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break te}K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break te}K[e+16>>2]=g;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break re}c=K[c+4>>2]}g=0;j=q;ue:{ve:{we:{xe:{ye:{switch(c|m<<1){case 0:i=f-4|0;j=K[f+4>>2]>>>17&4|K[i>>2]>>>19&1;c=z+(L[j+24336|0]<<2)|0;m=K[c>>2];g=K[m>>2];b=b-g|0;ze:{if(d>>>16>>>0>>0){o=K[m+4>>2];p=c;c=b>>>0>>0;K[p>>2]=K[m+(c?8:12)>>2];while(1){Ae:{if(h){break Ae}m=K[e+16>>2];b=m+1|0;p=L[m+1|0];if(L[m|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ae}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Ae}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;m=c?o:!o;break ze}d=d-(g<<16)|0;if(!(b&32768)){o=K[m+4>>2];p=c;c=b>>>0>>0;K[p>>2]=K[m+(c?12:8)>>2];while(1){Be:{if(h){break Be}m=K[e+16>>2];g=m+1|0;p=L[m+1|0];if(L[m|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Be}K[e+16>>2]=g;d=(p<<9)+d|0;h=7;break Be}K[e+16>>2]=g;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!o:o;break ze}m=K[m+4>>2]}g=L[j+24592|0];K[k>>2]=(m|0)==(g|0)?C:u;K[i>>2]=K[i>>2]|32;K[f+4>>2]=K[f+4>>2]|8;c=f-268|0;K[c>>2]=K[c>>2]|131072;c=f-260|0;K[c>>2]=K[c>>2]|32768;c=f-264|0;j=c;i=K[c>>2];c=g^m;K[j>>2]=i|c<<31|65536;j=c<<19;r=K[e+108>>2];c=z+(L[r+2|0]<<2)|0;m=K[c>>2];g=K[m>>2];b=b-g|0;Ce:{if(d>>>16>>>0>>0){o=K[m+4>>2];i=c;c=b>>>0>>0;K[i>>2]=K[m+(c?8:12)>>2];while(1){De:{if(h){break De}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break De}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break De}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?o:!o;break Ce}d=d-(g<<16)|0;if(!(b&32768)){o=K[m+4>>2];i=c;c=b>>>0>>0;K[i>>2]=K[m+(c?12:8)>>2];while(1){Ee:{if(h){break Ee}m=K[e+16>>2];g=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ee}K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break Ee}K[e+16>>2]=g;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break Ce}c=K[m+4>>2]}g=j|16;if(!c){break xe}break;case 1:break ye;case 2:break we;case 3:break ue;default:break je}}p=f-4|0;m=K[f+4>>2]>>>20&4|(K[p>>2]>>>22&1|(g>>>15&16|(g>>>19&64|g>>>3&170)));j=z+(L[m+24336|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;Fe:{if(d>>>16>>>0>>0){i=K[o+4>>2];n=j;j=b>>>0>>0;K[n>>2]=K[o+(j?8:12)>>2];while(1){Ge:{if(h){break Ge}o=K[e+16>>2];b=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ge}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Ge}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=j?i:!i;break Fe}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){He:{if(h){break He}o=K[e+16>>2];j=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break He}K[e+16>>2]=j;d=(n<<9)+d|0;h=7;break He}K[e+16>>2]=j;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!i:i;break Fe}j=K[o+4>>2]}c=L[m+24592|0];K[k+256>>2]=(j|0)==(c|0)?C:u;K[p>>2]=K[p>>2]|256;K[f+4>>2]=K[f+4>>2]|64;r=K[e+108>>2];g=(c^j)<<22|g|128}j=z+(L[(g>>>6&495)+r|0]<<2)|0;m=K[j>>2];c=K[m>>2];b=b-c|0;Ie:{if(d>>>16>>>0>>0){o=K[m+4>>2];i=j;j=b>>>0>>0;K[i>>2]=K[m+(j?8:12)>>2];while(1){Je:{if(h){break Je}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Je}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break Je}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=j?o:!o;break Ie}d=d-(c<<16)|0;if(!(b&32768)){o=K[m+4>>2];c=b>>>0>>0;K[j>>2]=K[m+(c?12:8)>>2];while(1){Ke:{if(h){break Ke}m=K[e+16>>2];j=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ke}K[e+16>>2]=j;d=(i<<9)+d|0;h=7;break Ke}K[e+16>>2]=j;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break Ie}c=K[m+4>>2]}if(!c){break ve}}p=f-4|0;m=K[f+4>>2]>>>23&4|(K[p>>2]>>>25&1|(g>>>18&16|(g>>>22&64|g>>>6&170)));j=z+(L[m+24336|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;Le:{if(d>>>16>>>0>>0){i=K[o+4>>2];n=j;j=b>>>0>>0;K[n>>2]=K[o+(j?8:12)>>2];while(1){Me:{if(h){break Me}o=K[e+16>>2];b=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Me}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Me}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=j?i:!i;break Le}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){Ne:{if(h){break Ne}o=K[e+16>>2];j=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ne}K[e+16>>2]=j;d=(n<<9)+d|0;h=7;break Ne}K[e+16>>2]=j;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!i:i;break Le}j=K[o+4>>2]}c=L[m+24592|0];K[k+512>>2]=(j|0)==(c|0)?C:u;K[p>>2]=K[p>>2]|2048;K[f+4>>2]=K[f+4>>2]|512;g=(c^j)<<25|g|1024;r=K[e+108>>2]}j=z+(L[(g>>>9&495)+r|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;Oe:{if(d>>>16>>>0>>0){i=K[o+4>>2];m=b>>>0>>0;K[j>>2]=K[o+(m?8:12)>>2];while(1){Pe:{if(h){break Pe}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Pe}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Pe}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?i:!i;break Oe}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){Qe:{if(h){break Qe}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Qe}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break Qe}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!i:i;break Oe}c=K[o+4>>2]}if(!c){break je}}H=f-4|0;n=K[f+4>>2]>>>26&4|(K[H>>2]>>>28&1|(g>>>21&16|(g>>>25&64|g>>>9&170)));j=z+(L[n+24336|0]<<2)|0;r=K[j>>2];c=K[r>>2];b=b-c|0;break ke}Re:{if(g&2097168){break Re}j=z+(L[K[e+108>>2]+(g&495)|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;Se:{if(d>>>16>>>0>>0){i=K[o+4>>2];m=b>>>0>>0;K[j>>2]=K[o+(m?8:12)>>2];while(1){Te:{if(h){break Te}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Te}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Te}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?i:!i;break Se}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){Ue:{if(h){break Ue}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ue}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break Ue}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!i:i;break Se}c=K[o+4>>2]}if(!c){break Re}n=f-4|0;o=K[f+4>>2]>>>17&4|(K[n>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Ve:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){We:{if(h){break We}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break We}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break We}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=m?p:!p;break Ve}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Xe:{if(h){break Xe}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Xe}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break Xe}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!p:p;break Ve}i=K[i+4>>2]}m=L[o+24592|0];K[k>>2]=(i|0)==(m|0)?C:u;K[n>>2]=K[n>>2]|32;K[f+4>>2]=K[f+4>>2]|8;c=f-268|0;K[c>>2]=K[c>>2]|131072;c=f-260|0;K[c>>2]=K[c>>2]|32768;c=f-264|0;o=c;p=K[c>>2];c=i^m;K[o>>2]=p|c<<31|65536;g=c<<19|g|16}Ye:{if(g&16777344){break Ye}o=g>>>3|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Ze:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){_e:{if(h){break _e}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break _e}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break _e}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break Ze}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){$e:{if(h){break $e}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break $e}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break $e}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break Ze}c=K[i+4>>2]}if(!c){break Ye}n=f-4|0;o=K[f+4>>2]>>>20&4|(K[n>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;af:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){bf:{if(h){break bf}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break bf}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break bf}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?p:!p;break af}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){cf:{if(h){break cf}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break cf}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break cf}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!p:p;break af}m=K[i+4>>2]}c=L[o+24592|0];K[k+256>>2]=(m|0)==(c|0)?C:u;K[n>>2]=K[n>>2]|256;K[f+4>>2]=K[f+4>>2]|64;g=(c^m)<<22|g|128}df:{if(g&134218752){break df}o=g>>>6|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;ef:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){ff:{if(h){break ff}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ff}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break ff}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break ef}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){gf:{if(h){break gf}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break gf}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break gf}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break ef}c=K[i+4>>2]}if(!c){break df}n=f-4|0;o=K[f+4>>2]>>>23&4|(K[n>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;hf:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){jf:{if(h){break jf}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break jf}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break jf}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?p:!p;break hf}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){kf:{if(h){break kf}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break kf}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break kf}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!p:p;break hf}m=K[i+4>>2]}c=L[o+24592|0];K[k+512>>2]=(m|0)==(c|0)?C:u;K[n>>2]=K[n>>2]|2048;K[f+4>>2]=K[f+4>>2]|512;g=(c^m)<<25|g|1024}if(g&1073750016){break je}o=g>>>9|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;lf:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){mf:{if(h){break mf}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break mf}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break mf}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break lf}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){nf:{if(h){break nf}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break nf}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break nf}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break lf}c=K[i+4>>2]}if(!c){break je}H=f-4|0;n=K[f+4>>2]>>>26&4|(K[H>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));j=z+(L[n+24336|0]<<2)|0;r=K[j>>2];c=K[r>>2];b=b-c|0}of:{if(d>>>16>>>0>>0){i=K[r+4>>2];m=b>>>0>>0;K[j>>2]=K[(m?8:12)+r>>2];while(1){pf:{if(h){break pf}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break pf}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break pf}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?i:!i;break of}d=d-(c<<16)|0;if(!(b&32768)){i=K[r+4>>2];c=b>>>0>>0;K[j>>2]=K[(c?12:8)+r>>2];while(1){qf:{if(h){break qf}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break qf}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break qf}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!i:i;break of}m=K[r+4>>2]}c=L[n+24592|0];K[k+768>>2]=(m|0)==(c|0)?C:u;K[H>>2]=K[H>>2]|16384;K[f+4>>2]=K[f+4>>2]|4096;K[f+260>>2]=K[f+260>>2]|4;K[f+268>>2]=K[f+268>>2]|1;c=c^m;K[f+264>>2]=K[f+264>>2]|c<<18|2;g=c<<28|g|8192}K[f>>2]=g&-1226833921}g=f+4|0;c=k+4|0;t=t+1|0;if((t|0)!=64){continue}break}g=f+12|0;c=k+772|0;m=v>>>0<60;v=v+4|0;if(m){continue}break}break ge}b=1<>>1|b;l=K[e+120>>2];c=(l+(U<<2)|0)+12|0;g=K[e+128>>2];h=K[e+8>>2];b=K[e+4>>2];d=K[e>>2];j=K[e+104>>2];o=K[e+116>>2];if(_&8){rf:{if(g>>>0<4){break rf}if(U){m=e+100|0;q=e+96|0;t=Q(U,12);s=U<<3;u=0-z|0;C=e+28|0;while(1){V=0;while(1){k=c;c=K[c>>2];sf:{tf:{uf:{if(c){vf:{if(c&2097168){break vf}j=C+(L[K[e+108>>2]+(c&495)|0]<<2)|0;f=K[j>>2];g=K[f>>2];b=b-g|0;wf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[f+4>>2];if(b&32768){break wf}i=K[f+4>>2];g=b>>>0>>0;K[j>>2]=K[f+(g?12:8)>>2];while(1){xf:{if(h){break xf}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break xf}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break xf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break wf}i=K[f+4>>2];l=b>>>0>>0;K[j>>2]=K[f+(l?8:12)>>2];while(1){yf:{if(h){break yf}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break yf}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break yf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}if(!l){break vf}n=k-4|0;f=K[k+4>>2]>>>17&4|(K[n>>2]>>>19&1|(c>>>14&16|(c>>>16&64|c&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;zf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break zf}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Af:{if(h){break Af}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Af}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Af}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break zf}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Bf:{if(h){break Bf}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break Bf}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break Bf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[o>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|32;K[k+4>>2]=K[k+4>>2]|8;c=(g^l)<<19|c|16}Cf:{if(c&16777344){break Cf}f=c>>>3|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Df:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Df}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Ef:{if(h){break Ef}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Ef}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Ef}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Df}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Ff:{if(h){break Ff}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Ff}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Ff}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break Cf}n=k-4|0;f=K[k+4>>2]>>>20&4|(K[n>>2]>>>22&1|(c>>>15&16|(c>>>19&64|f&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Y=(U<<2)+o|0;Gf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Gf}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Hf:{if(h){break Hf}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Hf}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Hf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Gf}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){If:{if(h){break If}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break If}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break If}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|256;K[k+4>>2]=K[k+4>>2]|64;c=(g^l)<<22|c|128}Jf:{if(c&134218752){break Jf}f=c>>>6|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Kf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Kf}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Lf:{if(h){break Lf}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Lf}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Lf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Kf}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Mf:{if(h){break Mf}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Mf}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Mf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break Jf}n=k-4|0;f=K[k+4>>2]>>>23&4|(K[n>>2]>>>25&1|(c>>>18&16|(c>>>22&64|f&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Y=o+s|0;Nf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Nf}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Of:{if(h){break Of}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Of}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Of}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Nf}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Pf:{if(h){break Pf}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break Pf}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break Pf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|2048;K[k+4>>2]=K[k+4>>2]|512;c=(g^l)<<25|c|1024}if(c&1073750016){break tf}f=c>>>9|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Qf:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Qf}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Rf:{if(h){break Rf}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Rf}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Rf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Qf}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Sf:{if(h){break Sf}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Sf}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Sf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break tf}n=k-4|0;X=K[k+4>>2]>>>26&4|(K[n>>2]>>>28&1|(c>>>21&16|(c>>>25&64|f&170)));j=C+(L[X+24336|0]<<2)|0;r=K[j>>2];g=K[r>>2];b=b-g|0;break uf}l=K[q>>2];c=K[l>>2];b=b-c|0;Tf:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;g=K[l+4>>2];if(b&32768){break Tf}j=K[l+4>>2];c=b>>>0>>0;K[q>>2]=K[l+(c?12:8)>>2];while(1){Uf:{if(h){break Uf}l=K[e+16>>2];g=l+1|0;f=L[l+1|0];if(L[l|0]!=255){K[e+16>>2]=g;h=8;d=(f<<8)+d|0;break Uf}if(f>>>0<=143){K[e+16>>2]=g;d=(f<<9)+d|0;h=7;break Uf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!j:j;break Tf}j=K[l+4>>2];g=b>>>0>>0;K[q>>2]=K[l+(g?8:12)>>2];while(1){Vf:{if(h){break Vf}l=K[e+16>>2];b=l+1|0;f=L[l+1|0];if(L[l|0]!=255){K[e+16>>2]=b;h=8;d=(f<<8)+d|0;break Vf}if(f>>>0<=143){K[e+16>>2]=b;d=(f<<9)+d|0;h=7;break Vf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;g=g?j:!j}if(!g){j=q;break sf}g=K[m>>2];c=K[g>>2];b=b-c|0;Wf:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;l=K[g+4>>2];if(b&32768){break Wf}f=K[g+4>>2];c=b>>>0>>0;g=K[(c?12:8)+g>>2];K[m>>2]=g;while(1){Xf:{if(h){break Xf}j=K[e+16>>2];l=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=l;h=8;d=(i<<8)+d|0;break Xf}if(i>>>0<=143){K[e+16>>2]=l;d=(i<<9)+d|0;h=7;break Xf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=c?!f:f;break Wf}f=K[g+4>>2];l=b>>>0>>0;g=K[(l?8:12)+g>>2];K[m>>2]=g;while(1){Yf:{if(h){break Yf}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break Yf}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break Yf}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;l=l?f:!f}c=K[g>>2];b=b-c|0;Zf:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;j=K[g+4>>2];if(b&32768){break Zf}f=K[g+4>>2];c=b>>>0>>0;K[m>>2]=K[(c?12:8)+g>>2];while(1){_f:{if(h){break _f}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(i<<8)+d|0;break _f}if(i>>>0<=143){K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break _f}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!f:f;break Zf}f=K[g+4>>2];j=g;g=b>>>0>>0;K[m>>2]=K[j+(g?8:12)>>2];while(1){$f:{if(h){break $f}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break $f}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break $f}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=g?f:!f}g=j;c=0;j=m;ag:{bg:{cg:{dg:{eg:{switch(g|l<<1){case 0:i=k-4|0;l=K[k+4>>2]>>>17&4|K[i>>2]>>>19&1;g=C+(L[l+24336|0]<<2)|0;j=K[g>>2];c=K[j>>2];b=b-c|0;fg:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;f=K[j+4>>2];if(b&32768){break fg}f=K[j+4>>2];c=b>>>0>>0;K[g>>2]=K[j+(c?12:8)>>2];while(1){gg:{if(h){break gg}j=K[e+16>>2];g=j+1|0;p=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(p<<8)+d|0;break gg}if(p>>>0<=143){K[e+16>>2]=g;d=(p<<9)+d|0;h=7;break gg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}f=c?!f:f;break fg}f=K[j+4>>2];p=g;g=b>>>0>>0;K[p>>2]=K[j+(g?8:12)>>2];while(1){hg:{if(h){break hg}j=K[e+16>>2];b=j+1|0;p=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break hg}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break hg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;f=g?f:!f}g=f;c=L[l+24592|0];K[o>>2]=(g|0)==(c|0)?z:u;K[i>>2]=K[i>>2]|32;K[k+4>>2]=K[k+4>>2]|8;l=(c^g)<<19;r=K[e+108>>2];g=C+(L[r+2|0]<<2)|0;j=K[g>>2];c=K[j>>2];b=b-c|0;ig:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;i=K[j+4>>2];if(b&32768){break ig}f=K[j+4>>2];c=b>>>0>>0;K[g>>2]=K[j+(c?12:8)>>2];while(1){jg:{if(h){break jg}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(i<<8)+d|0;break jg}if(i>>>0<=143){K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break jg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!f:f;break ig}f=K[j+4>>2];i=g;g=b>>>0>>0;K[i>>2]=K[j+(g?8:12)>>2];while(1){kg:{if(h){break kg}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break kg}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break kg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=g?f:!f}g=i;c=l|16;if(!g){break dg}break;case 1:break eg;case 2:break cg;case 3:break ag;default:break tf}}p=k-4|0;j=K[k+4>>2]>>>20&4|(K[p>>2]>>>22&1|(c>>>15&16|(c>>>19&64|c>>>3&170)));l=C+(L[j+24336|0]<<2)|0;f=K[l>>2];g=K[f>>2];b=b-g|0;r=(U<<2)+o|0;lg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[f+4>>2];if(b&32768){break lg}i=K[f+4>>2];g=b>>>0>>0;K[l>>2]=K[f+(g?12:8)>>2];while(1){mg:{if(h){break mg}f=K[e+16>>2];l=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break mg}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break mg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!i:i;break lg}i=K[f+4>>2];n=l;l=b>>>0>>0;K[n>>2]=K[f+(l?8:12)>>2];while(1){ng:{if(h){break ng}f=K[e+16>>2];b=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break ng}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break ng}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?i:!i}l=i;g=L[j+24592|0];K[r>>2]=(l|0)==(g|0)?z:u;K[p>>2]=K[p>>2]|256;K[k+4>>2]=K[k+4>>2]|64;r=K[e+108>>2];c=(g^l)<<22|c|128}l=C+(L[(c>>>6&495)+r|0]<<2)|0;j=K[l>>2];g=K[j>>2];b=b-g|0;og:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[j+4>>2];if(b&32768){break og}f=K[j+4>>2];g=b>>>0>>0;K[l>>2]=K[j+(g?12:8)>>2];while(1){pg:{if(h){break pg}j=K[e+16>>2];l=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=l;h=8;d=(i<<8)+d|0;break pg}if(i>>>0<=143){K[e+16>>2]=l;d=(i<<9)+d|0;h=7;break pg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!f:f;break og}f=K[j+4>>2];i=l;l=b>>>0>>0;K[i>>2]=K[j+(l?8:12)>>2];while(1){qg:{if(h){break qg}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break qg}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break qg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?f:!f}if(!i){break bg}}p=k-4|0;j=K[k+4>>2]>>>23&4|(K[p>>2]>>>25&1|(c>>>18&16|(c>>>22&64|c>>>6&170)));l=C+(L[j+24336|0]<<2)|0;f=K[l>>2];g=K[f>>2];b=b-g|0;r=o+s|0;rg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[f+4>>2];if(b&32768){break rg}i=K[f+4>>2];g=b>>>0>>0;K[l>>2]=K[f+(g?12:8)>>2];while(1){sg:{if(h){break sg}f=K[e+16>>2];l=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break sg}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break sg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!i:i;break rg}i=K[f+4>>2];n=l;l=b>>>0>>0;K[n>>2]=K[f+(l?8:12)>>2];while(1){tg:{if(h){break tg}f=K[e+16>>2];b=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break tg}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break tg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?i:!i}l=i;g=L[j+24592|0];K[r>>2]=(l|0)==(g|0)?z:u;K[p>>2]=K[p>>2]|2048;K[k+4>>2]=K[k+4>>2]|512;c=(g^l)<<25|c|1024;r=K[e+108>>2]}j=C+(L[(c>>>9&495)+r|0]<<2)|0;f=K[j>>2];g=K[f>>2];b=b-g|0;ug:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[f+4>>2];if(b&32768){break ug}i=K[f+4>>2];g=b>>>0>>0;K[j>>2]=K[f+(g?12:8)>>2];while(1){vg:{if(h){break vg}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break vg}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break vg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break ug}i=K[f+4>>2];l=b>>>0>>0;K[j>>2]=K[f+(l?8:12)>>2];while(1){wg:{if(h){break wg}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break wg}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break wg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}if(!l){break tf}}n=k-4|0;X=K[k+4>>2]>>>26&4|(K[n>>2]>>>28&1|(c>>>21&16|(c>>>25&64|c>>>9&170)));j=C+(L[X+24336|0]<<2)|0;r=K[j>>2];g=K[r>>2];b=b-g|0}Y=o+t|0;xg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[r+4>>2];if(b&32768){break xg}i=K[r+4>>2];g=b>>>0>>0;K[j>>2]=K[(g?12:8)+r>>2];while(1){yg:{if(h){break yg}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break yg}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break yg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break xg}i=K[r+4>>2];l=b>>>0>>0;K[j>>2]=K[(l?8:12)+r>>2];while(1){zg:{if(h){break zg}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break zg}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break zg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}g=L[X+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|16384;K[k+4>>2]=K[k+4>>2]|4096;f=k+(K[e+124>>2]<<2)|0;K[f+4>>2]=K[f+4>>2]|4;K[f+12>>2]=K[f+12>>2]|1;g=g^l;K[f+8>>2]=K[f+8>>2]|g<<18|2;c=g<<28|c|8192}K[k>>2]=c&-1226833921}c=k+4|0;o=o+4|0;V=V+1|0;if((U|0)!=(V|0)){continue}break}c=k+12|0;o=o+t|0;v=v+4|0;g=K[e+128>>2];if(v>>>0<(g&-4)>>>0){continue}break}break rf}c=(g&-4)-1|0;v=(c&-4)+4|0;c=(l+(c<<1&-8)|0)+20|0}K[e+8>>2]=h;K[e+4>>2]=b;K[e>>2]=d;K[e+104>>2]=j;if(!U|g>>>0<=v>>>0){break fe}while(1){h=0;if(K[e+128>>2]!=(v|0)){while(1){kc(e,c,(Q(h,U)<<2)+o|0,z,h,1);h=h+1|0;if(h>>>0>2]-v>>>0){continue}break}}K[c>>2]=K[c>>2]&-1226833921;o=o+4|0;c=c+4|0;H=H+1|0;if((U|0)!=(H|0)){continue}break}break fe}Ag:{if(g>>>0<4){break Ag}if(U){m=e+100|0;q=e+96|0;t=Q(U,12);s=U<<3;u=0-z|0;C=e+28|0;while(1){V=0;while(1){k=c;c=K[c>>2];Bg:{Cg:{Dg:{if(c){Eg:{if(c&2097168){break Eg}j=C+(L[K[e+108>>2]+(c&495)|0]<<2)|0;f=K[j>>2];g=K[f>>2];b=b-g|0;Fg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[f+4>>2];if(b&32768){break Fg}i=K[f+4>>2];g=b>>>0>>0;K[j>>2]=K[f+(g?12:8)>>2];while(1){Gg:{if(h){break Gg}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break Gg}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break Gg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break Fg}i=K[f+4>>2];l=b>>>0>>0;K[j>>2]=K[f+(l?8:12)>>2];while(1){Hg:{if(h){break Hg}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break Hg}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Hg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}if(!l){break Eg}n=k-4|0;f=K[k+4>>2]>>>17&4|(K[n>>2]>>>19&1|(c>>>14&16|(c>>>16&64|c&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Ig:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Ig}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Jg:{if(h){break Jg}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Jg}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Jg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Ig}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Kg:{if(h){break Kg}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break Kg}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break Kg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[o>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|32;K[k+4>>2]=K[k+4>>2]|8;f=k+(-2-K[e+124>>2]<<2)|0;K[f+4>>2]=K[f+4>>2]|32768;l=g^l;K[f>>2]=K[f>>2]|l<<31|65536;g=f-4|0;K[g>>2]=K[g>>2]|131072;c=l<<19|c|16}Lg:{if(c&16777344){break Lg}f=c>>>3|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Mg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Mg}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Ng:{if(h){break Ng}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Ng}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Ng}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Mg}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Og:{if(h){break Og}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Og}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Og}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break Lg}n=k-4|0;f=K[k+4>>2]>>>20&4|(K[n>>2]>>>22&1|(c>>>15&16|(c>>>19&64|f&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Y=(U<<2)+o|0;Pg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Pg}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Qg:{if(h){break Qg}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Qg}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Qg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Pg}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Rg:{if(h){break Rg}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break Rg}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break Rg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|256;K[k+4>>2]=K[k+4>>2]|64;c=(g^l)<<22|c|128}Sg:{if(c&134218752){break Sg}f=c>>>6|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Tg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Tg}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Ug:{if(h){break Ug}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Ug}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Ug}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Tg}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Vg:{if(h){break Vg}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Vg}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Vg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break Sg}n=k-4|0;f=K[k+4>>2]>>>23&4|(K[n>>2]>>>25&1|(c>>>18&16|(c>>>22&64|f&170)));j=C+(L[f+24336|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Y=o+s|0;Wg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Wg}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){Xg:{if(h){break Xg}i=K[e+16>>2];l=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(r<<8)+d|0;break Xg}if(r>>>0<=143){K[e+16>>2]=l;d=(r<<9)+d|0;h=7;break Xg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Wg}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){Yg:{if(h){break Yg}i=K[e+16>>2];b=i+1|0;r=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(r<<8)+d|0;break Yg}if(r>>>0<=143){K[e+16>>2]=b;d=(r<<9)+d|0;h=7;break Yg}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}g=L[f+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|2048;K[k+4>>2]=K[k+4>>2]|512;c=(g^l)<<25|c|1024}if(c&1073750016){break Cg}f=c>>>9|0;j=C+(L[K[e+108>>2]+(f&495)|0]<<2)|0;i=K[j>>2];g=K[i>>2];b=b-g|0;Zg:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[i+4>>2];if(b&32768){break Zg}p=K[i+4>>2];g=b>>>0>>0;K[j>>2]=K[i+(g?12:8)>>2];while(1){_g:{if(h){break _g}i=K[e+16>>2];l=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break _g}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break _g}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!p:p;break Zg}p=K[i+4>>2];l=b>>>0>>0;K[j>>2]=K[i+(l?8:12)>>2];while(1){$g:{if(h){break $g}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break $g}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break $g}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?p:!p}if(!l){break Cg}n=k-4|0;X=K[k+4>>2]>>>26&4|(K[n>>2]>>>28&1|(c>>>21&16|(c>>>25&64|f&170)));j=C+(L[X+24336|0]<<2)|0;r=K[j>>2];g=K[r>>2];b=b-g|0;break Dg}l=K[q>>2];c=K[l>>2];b=b-c|0;ah:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;g=K[l+4>>2];if(b&32768){break ah}j=K[l+4>>2];c=b>>>0>>0;K[q>>2]=K[l+(c?12:8)>>2];while(1){bh:{if(h){break bh}l=K[e+16>>2];g=l+1|0;f=L[l+1|0];if(L[l|0]!=255){K[e+16>>2]=g;h=8;d=(f<<8)+d|0;break bh}if(f>>>0<=143){K[e+16>>2]=g;d=(f<<9)+d|0;h=7;break bh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!j:j;break ah}j=K[l+4>>2];g=b>>>0>>0;K[q>>2]=K[l+(g?8:12)>>2];while(1){ch:{if(h){break ch}l=K[e+16>>2];b=l+1|0;f=L[l+1|0];if(L[l|0]!=255){K[e+16>>2]=b;h=8;d=(f<<8)+d|0;break ch}if(f>>>0<=143){K[e+16>>2]=b;d=(f<<9)+d|0;h=7;break ch}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;g=g?j:!j}if(!g){j=q;break Bg}g=K[m>>2];c=K[g>>2];b=b-c|0;dh:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;l=K[g+4>>2];if(b&32768){break dh}f=K[g+4>>2];c=b>>>0>>0;g=K[(c?12:8)+g>>2];K[m>>2]=g;while(1){eh:{if(h){break eh}j=K[e+16>>2];l=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=l;h=8;d=(i<<8)+d|0;break eh}if(i>>>0<=143){K[e+16>>2]=l;d=(i<<9)+d|0;h=7;break eh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=c?!f:f;break dh}f=K[g+4>>2];l=b>>>0>>0;g=K[(l?8:12)+g>>2];K[m>>2]=g;while(1){fh:{if(h){break fh}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break fh}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break fh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;l=l?f:!f}c=K[g>>2];b=b-c|0;gh:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;j=K[g+4>>2];if(b&32768){break gh}f=K[g+4>>2];c=b>>>0>>0;K[m>>2]=K[(c?12:8)+g>>2];while(1){hh:{if(h){break hh}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(i<<8)+d|0;break hh}if(i>>>0<=143){K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break hh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!f:f;break gh}f=K[g+4>>2];j=g;g=b>>>0>>0;K[m>>2]=K[j+(g?8:12)>>2];while(1){ih:{if(h){break ih}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break ih}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break ih}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=g?f:!f}g=j;c=0;j=m;jh:{kh:{lh:{mh:{nh:{switch(g|l<<1){case 0:i=k-4|0;l=K[k+4>>2]>>>17&4|K[i>>2]>>>19&1;g=C+(L[l+24336|0]<<2)|0;j=K[g>>2];c=K[j>>2];b=b-c|0;oh:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;f=K[j+4>>2];if(b&32768){break oh}f=K[j+4>>2];c=b>>>0>>0;K[g>>2]=K[j+(c?12:8)>>2];while(1){ph:{if(h){break ph}j=K[e+16>>2];g=j+1|0;p=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(p<<8)+d|0;break ph}if(p>>>0<=143){K[e+16>>2]=g;d=(p<<9)+d|0;h=7;break ph}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}f=c?!f:f;break oh}f=K[j+4>>2];p=g;g=b>>>0>>0;K[p>>2]=K[j+(g?8:12)>>2];while(1){qh:{if(h){break qh}j=K[e+16>>2];b=j+1|0;p=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break qh}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break qh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;f=g?f:!f}g=f;c=L[l+24592|0];K[o>>2]=(g|0)==(c|0)?z:u;K[i>>2]=K[i>>2]|32;K[k+4>>2]=K[k+4>>2]|8;l=k+(-2-K[e+124>>2]<<2)|0;K[l+4>>2]=K[l+4>>2]|32768;g=c^g;K[l>>2]=K[l>>2]|g<<31|65536;c=l-4|0;K[c>>2]=K[c>>2]|131072;l=g<<19;r=K[e+108>>2];g=C+(L[r+2|0]<<2)|0;j=K[g>>2];c=K[j>>2];b=b-c|0;rh:{if(d>>>16>>>0>=c>>>0){d=d-(c<<16)|0;i=K[j+4>>2];if(b&32768){break rh}f=K[j+4>>2];c=b>>>0>>0;K[g>>2]=K[j+(c?12:8)>>2];while(1){sh:{if(h){break sh}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=g;h=8;d=(i<<8)+d|0;break sh}if(i>>>0<=143){K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break sh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=c?!f:f;break rh}f=K[j+4>>2];i=g;g=b>>>0>>0;K[i>>2]=K[j+(g?8:12)>>2];while(1){th:{if(h){break th}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break th}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break th}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;i=g?f:!f}g=i;c=l|16;if(!g){break mh}break;case 1:break nh;case 2:break lh;case 3:break jh;default:break Cg}}p=k-4|0;j=K[k+4>>2]>>>20&4|(K[p>>2]>>>22&1|(c>>>15&16|(c>>>19&64|c>>>3&170)));l=C+(L[j+24336|0]<<2)|0;f=K[l>>2];g=K[f>>2];b=b-g|0;r=(U<<2)+o|0;uh:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[f+4>>2];if(b&32768){break uh}i=K[f+4>>2];g=b>>>0>>0;K[l>>2]=K[f+(g?12:8)>>2];while(1){vh:{if(h){break vh}f=K[e+16>>2];l=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break vh}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break vh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!i:i;break uh}i=K[f+4>>2];n=l;l=b>>>0>>0;K[n>>2]=K[f+(l?8:12)>>2];while(1){wh:{if(h){break wh}f=K[e+16>>2];b=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break wh}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break wh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?i:!i}l=i;g=L[j+24592|0];K[r>>2]=(l|0)==(g|0)?z:u;K[p>>2]=K[p>>2]|256;K[k+4>>2]=K[k+4>>2]|64;r=K[e+108>>2];c=(g^l)<<22|c|128}l=C+(L[(c>>>6&495)+r|0]<<2)|0;j=K[l>>2];g=K[j>>2];b=b-g|0;xh:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[j+4>>2];if(b&32768){break xh}f=K[j+4>>2];g=b>>>0>>0;K[l>>2]=K[j+(g?12:8)>>2];while(1){yh:{if(h){break yh}j=K[e+16>>2];l=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=l;h=8;d=(i<<8)+d|0;break yh}if(i>>>0<=143){K[e+16>>2]=l;d=(i<<9)+d|0;h=7;break yh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!f:f;break xh}f=K[j+4>>2];i=l;l=b>>>0>>0;K[i>>2]=K[j+(l?8:12)>>2];while(1){zh:{if(h){break zh}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]!=255){K[e+16>>2]=b;h=8;d=(i<<8)+d|0;break zh}if(i>>>0<=143){K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break zh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?f:!f}if(!i){break kh}}p=k-4|0;j=K[k+4>>2]>>>23&4|(K[p>>2]>>>25&1|(c>>>18&16|(c>>>22&64|c>>>6&170)));l=C+(L[j+24336|0]<<2)|0;f=K[l>>2];g=K[f>>2];b=b-g|0;r=o+s|0;Ah:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;i=K[f+4>>2];if(b&32768){break Ah}i=K[f+4>>2];g=b>>>0>>0;K[l>>2]=K[f+(g?12:8)>>2];while(1){Bh:{if(h){break Bh}f=K[e+16>>2];l=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(n<<8)+d|0;break Bh}if(n>>>0<=143){K[e+16>>2]=l;d=(n<<9)+d|0;h=7;break Bh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}i=g?!i:i;break Ah}i=K[f+4>>2];n=l;l=b>>>0>>0;K[n>>2]=K[f+(l?8:12)>>2];while(1){Ch:{if(h){break Ch}f=K[e+16>>2];b=f+1|0;n=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(n<<8)+d|0;break Ch}if(n>>>0<=143){K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Ch}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;i=l?i:!i}l=i;g=L[j+24592|0];K[r>>2]=(l|0)==(g|0)?z:u;K[p>>2]=K[p>>2]|2048;K[k+4>>2]=K[k+4>>2]|512;c=(g^l)<<25|c|1024;r=K[e+108>>2]}j=C+(L[(c>>>9&495)+r|0]<<2)|0;f=K[j>>2];g=K[f>>2];b=b-g|0;Dh:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[f+4>>2];if(b&32768){break Dh}i=K[f+4>>2];g=b>>>0>>0;K[j>>2]=K[f+(g?12:8)>>2];while(1){Eh:{if(h){break Eh}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break Eh}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break Eh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break Dh}i=K[f+4>>2];l=b>>>0>>0;K[j>>2]=K[f+(l?8:12)>>2];while(1){Fh:{if(h){break Fh}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break Fh}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Fh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}if(!l){break Cg}}n=k-4|0;X=K[k+4>>2]>>>26&4|(K[n>>2]>>>28&1|(c>>>21&16|(c>>>25&64|c>>>9&170)));j=C+(L[X+24336|0]<<2)|0;r=K[j>>2];g=K[r>>2];b=b-g|0}Y=o+t|0;Gh:{if(d>>>16>>>0>=g>>>0){d=d-(g<<16)|0;l=K[r+4>>2];if(b&32768){break Gh}i=K[r+4>>2];g=b>>>0>>0;K[j>>2]=K[(g?12:8)+r>>2];while(1){Hh:{if(h){break Hh}f=K[e+16>>2];l=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=l;h=8;d=(p<<8)+d|0;break Hh}if(p>>>0<=143){K[e+16>>2]=l;d=(p<<9)+d|0;h=7;break Hh}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}l=g?!i:i;break Gh}i=K[r+4>>2];l=b>>>0>>0;K[j>>2]=K[(l?8:12)+r>>2];while(1){Ih:{if(h){break Ih}f=K[e+16>>2];b=f+1|0;p=L[f+1|0];if(L[f|0]!=255){K[e+16>>2]=b;h=8;d=(p<<8)+d|0;break Ih}if(p>>>0<=143){K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Ih}K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;l=l?i:!i}g=L[X+24592|0];K[Y>>2]=(l|0)==(g|0)?z:u;K[n>>2]=K[n>>2]|16384;K[k+4>>2]=K[k+4>>2]|4096;f=k+(K[e+124>>2]<<2)|0;K[f+4>>2]=K[f+4>>2]|4;K[f+12>>2]=K[f+12>>2]|1;g=g^l;K[f+8>>2]=K[f+8>>2]|g<<18|2;c=g<<28|c|8192}K[k>>2]=c&-1226833921}c=k+4|0;o=o+4|0;V=V+1|0;if((U|0)!=(V|0)){continue}break}c=k+12|0;o=o+t|0;v=v+4|0;g=K[e+128>>2];if(v>>>0<(g&-4)>>>0){continue}break}break Ag}c=(g&-4)-1|0;v=(c&-4)+4|0;c=(l+(c<<1&-8)|0)+20|0}K[e+8>>2]=h;K[e+4>>2]=b;K[e>>2]=d;K[e+104>>2]=j;if(!U|g>>>0<=v>>>0){break fe}while(1){h=0;if(K[e+128>>2]!=(v|0)){while(1){kc(e,c,(Q(h,U)<<2)+o|0,z,h,0);h=h+1|0;if(h>>>0>2]-v>>>0){continue}break}}K[c>>2]=K[c>>2]&-1226833921;o=o+4|0;c=c+4|0;H=H+1|0;if((U|0)!=(H|0)){continue}break}break fe}while(1){t=0;while(1){k=c;f=g;g=K[g>>2];Jh:{Kh:{Lh:{if(!g){j=K[l>>2];g=K[j>>2];b=b-g|0;Mh:{if(d>>>16>>>0>>0){m=K[j+4>>2];c=b>>>0>>0;K[l>>2]=K[j+(c?8:12)>>2];while(1){Nh:{if(h){break Nh}j=K[e+16>>2];b=j+1|0;o=L[j+1|0];if(L[j|0]==255){if(o>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Nh}K[e+16>>2]=b;d=(o<<9)+d|0;h=7;break Nh}K[e+16>>2]=b;h=8;d=(o<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?m:!m;break Mh}d=d-(g<<16)|0;if(!(b&32768)){m=K[j+4>>2];c=b>>>0>>0;K[l>>2]=K[j+(c?12:8)>>2];while(1){Oh:{if(h){break Oh}j=K[e+16>>2];g=j+1|0;o=L[j+1|0];if(L[j|0]==255){if(o>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Oh}K[e+16>>2]=g;d=(o<<9)+d|0;h=7;break Oh}K[e+16>>2]=g;h=8;d=(o<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!m:m;break Mh}c=K[j+4>>2]}if(!c){j=l;break Jh}c=K[q>>2];g=K[c>>2];b=b-g|0;Ph:{if(d>>>16>>>0>>0){o=K[c+4>>2];j=b>>>0>>0;c=K[(j?8:12)+c>>2];K[q>>2]=c;while(1){Qh:{if(h){break Qh}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Qh}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break Qh}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;m=j?o:!o;break Ph}d=d-(g<<16)|0;if(!(b&32768)){o=K[c+4>>2];g=b>>>0>>0;c=K[(g?12:8)+c>>2];K[q>>2]=c;while(1){Rh:{if(h){break Rh}m=K[e+16>>2];j=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Rh}K[e+16>>2]=j;d=(i<<9)+d|0;h=7;break Rh}K[e+16>>2]=j;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=g?!o:o;break Ph}m=K[c+4>>2]}g=K[c>>2];b=b-g|0;Sh:{if(d>>>16>>>0>>0){o=K[c+4>>2];j=c;c=b>>>0>>0;K[q>>2]=K[j+(c?8:12)>>2];while(1){Th:{if(h){break Th}j=K[e+16>>2];b=j+1|0;i=L[j+1|0];if(L[j|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Th}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break Th}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?o:!o;break Sh}d=d-(g<<16)|0;if(!(b&32768)){o=K[c+4>>2];j=c;c=b>>>0>>0;K[q>>2]=K[j+(c?12:8)>>2];while(1){Uh:{if(h){break Uh}j=K[e+16>>2];g=j+1|0;i=L[j+1|0];if(L[j|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Uh}K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break Uh}K[e+16>>2]=g;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break Sh}c=K[c+4>>2]}g=0;j=q;Vh:{Wh:{Xh:{Yh:{Zh:{switch(c|m<<1){case 0:i=f-4|0;j=K[f+4>>2]>>>17&4|K[i>>2]>>>19&1;c=z+(L[j+24336|0]<<2)|0;m=K[c>>2];g=K[m>>2];b=b-g|0;_h:{if(d>>>16>>>0>>0){o=K[m+4>>2];p=c;c=b>>>0>>0;K[p>>2]=K[m+(c?8:12)>>2];while(1){$h:{if(h){break $h}m=K[e+16>>2];b=m+1|0;p=L[m+1|0];if(L[m|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break $h}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break $h}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;g=c?o:!o;break _h}d=d-(g<<16)|0;if(!(b&32768)){o=K[m+4>>2];p=c;c=b>>>0>>0;K[p>>2]=K[m+(c?12:8)>>2];while(1){ai:{if(h){break ai}m=K[e+16>>2];g=m+1|0;p=L[m+1|0];if(L[m|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ai}K[e+16>>2]=g;d=(p<<9)+d|0;h=7;break ai}K[e+16>>2]=g;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}g=c?!o:o;break _h}g=K[m+4>>2]}c=L[j+24592|0];K[k>>2]=(g|0)==(c|0)?C:u;K[i>>2]=K[i>>2]|32;K[f+4>>2]=K[f+4>>2]|8;j=(c^g)<<19;r=K[e+108>>2];c=z+(L[r+2|0]<<2)|0;m=K[c>>2];g=K[m>>2];b=b-g|0;bi:{if(d>>>16>>>0>>0){o=K[m+4>>2];i=c;c=b>>>0>>0;K[i>>2]=K[m+(c?8:12)>>2];while(1){ci:{if(h){break ci}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ci}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break ci}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;g=g<<1;if(g>>>0<32768){continue}break}b=g;c=c?o:!o;break bi}d=d-(g<<16)|0;if(!(b&32768)){o=K[m+4>>2];i=c;c=b>>>0>>0;K[i>>2]=K[m+(c?12:8)>>2];while(1){di:{if(h){break di}m=K[e+16>>2];g=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break di}K[e+16>>2]=g;d=(i<<9)+d|0;h=7;break di}K[e+16>>2]=g;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break bi}c=K[m+4>>2]}g=j|16;if(!c){break Yh}break;case 1:break Zh;case 2:break Xh;case 3:break Vh;default:break Kh}}p=f-4|0;m=K[f+4>>2]>>>20&4|(K[p>>2]>>>22&1|(g>>>15&16|(g>>>19&64|g>>>3&170)));j=z+(L[m+24336|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;ei:{if(d>>>16>>>0>>0){i=K[o+4>>2];n=j;j=b>>>0>>0;K[n>>2]=K[o+(j?8:12)>>2];while(1){fi:{if(h){break fi}o=K[e+16>>2];b=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break fi}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break fi}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=j?i:!i;break ei}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){gi:{if(h){break gi}o=K[e+16>>2];j=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break gi}K[e+16>>2]=j;d=(n<<9)+d|0;h=7;break gi}K[e+16>>2]=j;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!i:i;break ei}j=K[o+4>>2]}c=L[m+24592|0];K[k+256>>2]=(j|0)==(c|0)?C:u;K[p>>2]=K[p>>2]|256;K[f+4>>2]=K[f+4>>2]|64;r=K[e+108>>2];g=(c^j)<<22|g|128}j=z+(L[(g>>>6&495)+r|0]<<2)|0;m=K[j>>2];c=K[m>>2];b=b-c|0;hi:{if(d>>>16>>>0>>0){o=K[m+4>>2];i=j;j=b>>>0>>0;K[i>>2]=K[m+(j?8:12)>>2];while(1){ii:{if(h){break ii}m=K[e+16>>2];b=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ii}K[e+16>>2]=b;d=(i<<9)+d|0;h=7;break ii}K[e+16>>2]=b;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=j?o:!o;break hi}d=d-(c<<16)|0;if(!(b&32768)){o=K[m+4>>2];c=b>>>0>>0;K[j>>2]=K[m+(c?12:8)>>2];while(1){ji:{if(h){break ji}m=K[e+16>>2];j=m+1|0;i=L[m+1|0];if(L[m|0]==255){if(i>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ji}K[e+16>>2]=j;d=(i<<9)+d|0;h=7;break ji}K[e+16>>2]=j;h=8;d=(i<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!o:o;break hi}c=K[m+4>>2]}if(!c){break Wh}}p=f-4|0;m=K[f+4>>2]>>>23&4|(K[p>>2]>>>25&1|(g>>>18&16|(g>>>22&64|g>>>6&170)));j=z+(L[m+24336|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;ki:{if(d>>>16>>>0>>0){i=K[o+4>>2];n=j;j=b>>>0>>0;K[n>>2]=K[o+(j?8:12)>>2];while(1){li:{if(h){break li}o=K[e+16>>2];b=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break li}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break li}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;j=j?i:!i;break ki}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){mi:{if(h){break mi}o=K[e+16>>2];j=o+1|0;n=L[o+1|0];if(L[o|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break mi}K[e+16>>2]=j;d=(n<<9)+d|0;h=7;break mi}K[e+16>>2]=j;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}j=c?!i:i;break ki}j=K[o+4>>2]}c=L[m+24592|0];K[k+512>>2]=(j|0)==(c|0)?C:u;K[p>>2]=K[p>>2]|2048;K[f+4>>2]=K[f+4>>2]|512;g=(c^j)<<25|g|1024;r=K[e+108>>2]}j=z+(L[(g>>>9&495)+r|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;ni:{if(d>>>16>>>0>>0){i=K[o+4>>2];m=b>>>0>>0;K[j>>2]=K[o+(m?8:12)>>2];while(1){oi:{if(h){break oi}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break oi}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break oi}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?i:!i;break ni}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){pi:{if(h){break pi}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break pi}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break pi}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!i:i;break ni}c=K[o+4>>2]}if(!c){break Kh}}H=f-4|0;n=K[f+4>>2]>>>26&4|(K[H>>2]>>>28&1|(g>>>21&16|(g>>>25&64|g>>>9&170)));j=z+(L[n+24336|0]<<2)|0;r=K[j>>2];c=K[r>>2];b=b-c|0;break Lh}qi:{if(g&2097168){break qi}j=z+(L[K[e+108>>2]+(g&495)|0]<<2)|0;o=K[j>>2];c=K[o>>2];b=b-c|0;ri:{if(d>>>16>>>0>>0){i=K[o+4>>2];m=b>>>0>>0;K[j>>2]=K[o+(m?8:12)>>2];while(1){si:{if(h){break si}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break si}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break si}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?i:!i;break ri}d=d-(c<<16)|0;if(!(b&32768)){i=K[o+4>>2];c=b>>>0>>0;K[j>>2]=K[o+(c?12:8)>>2];while(1){ti:{if(h){break ti}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break ti}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break ti}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!i:i;break ri}c=K[o+4>>2]}if(!c){break qi}n=f-4|0;o=K[f+4>>2]>>>17&4|(K[n>>2]>>>19&1|(g>>>14&16|(g>>>16&64|g&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;ui:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){vi:{if(h){break vi}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break vi}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break vi}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?p:!p;break ui}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){wi:{if(h){break wi}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break wi}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break wi}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!p:p;break ui}m=K[i+4>>2]}c=L[o+24592|0];K[k>>2]=(m|0)==(c|0)?C:u;K[n>>2]=K[n>>2]|32;K[f+4>>2]=K[f+4>>2]|8;g=(c^m)<<19|g|16}xi:{if(g&16777344){break xi}o=g>>>3|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;yi:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){zi:{if(h){break zi}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break zi}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break zi}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break yi}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Ai:{if(h){break Ai}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ai}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break Ai}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break yi}c=K[i+4>>2]}if(!c){break xi}n=f-4|0;o=K[f+4>>2]>>>20&4|(K[n>>2]>>>22&1|(g>>>15&16|(g>>>19&64|o&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Bi:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){Ci:{if(h){break Ci}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ci}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break Ci}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?p:!p;break Bi}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Di:{if(h){break Di}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Di}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break Di}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!p:p;break Bi}m=K[i+4>>2]}c=L[o+24592|0];K[k+256>>2]=(m|0)==(c|0)?C:u;K[n>>2]=K[n>>2]|256;K[f+4>>2]=K[f+4>>2]|64;g=(c^m)<<22|g|128}Ei:{if(g&134218752){break Ei}o=g>>>6|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Fi:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){Gi:{if(h){break Gi}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Gi}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Gi}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break Fi}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Hi:{if(h){break Hi}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Hi}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break Hi}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break Fi}c=K[i+4>>2]}if(!c){break Ei}n=f-4|0;o=K[f+4>>2]>>>23&4|(K[n>>2]>>>25&1|(g>>>18&16|(g>>>22&64|o&170)));j=z+(L[o+24336|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Ii:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){Ji:{if(h){break Ji}i=K[e+16>>2];b=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ji}K[e+16>>2]=b;d=(s<<9)+d|0;h=7;break Ji}K[e+16>>2]=b;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?p:!p;break Ii}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Ki:{if(h){break Ki}i=K[e+16>>2];m=i+1|0;s=L[i+1|0];if(L[i|0]==255){if(s>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ki}K[e+16>>2]=m;d=(s<<9)+d|0;h=7;break Ki}K[e+16>>2]=m;h=8;d=(s<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!p:p;break Ii}m=K[i+4>>2]}c=L[o+24592|0];K[k+512>>2]=(m|0)==(c|0)?C:u;K[n>>2]=K[n>>2]|2048;K[f+4>>2]=K[f+4>>2]|512;g=(c^m)<<25|g|1024}if(g&1073750016){break Kh}o=g>>>9|0;j=z+(L[K[e+108>>2]+(o&495)|0]<<2)|0;i=K[j>>2];c=K[i>>2];b=b-c|0;Li:{if(d>>>16>>>0>>0){p=K[i+4>>2];m=b>>>0>>0;K[j>>2]=K[i+(m?8:12)>>2];while(1){Mi:{if(h){break Mi}i=K[e+16>>2];b=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Mi}K[e+16>>2]=b;d=(n<<9)+d|0;h=7;break Mi}K[e+16>>2]=b;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;c=m?p:!p;break Li}d=d-(c<<16)|0;if(!(b&32768)){p=K[i+4>>2];c=b>>>0>>0;K[j>>2]=K[i+(c?12:8)>>2];while(1){Ni:{if(h){break Ni}i=K[e+16>>2];m=i+1|0;n=L[i+1|0];if(L[i|0]==255){if(n>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Ni}K[e+16>>2]=m;d=(n<<9)+d|0;h=7;break Ni}K[e+16>>2]=m;h=8;d=(n<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}c=c?!p:p;break Li}c=K[i+4>>2]}if(!c){break Kh}H=f-4|0;n=K[f+4>>2]>>>26&4|(K[H>>2]>>>28&1|(g>>>21&16|(g>>>25&64|o&170)));j=z+(L[n+24336|0]<<2)|0;r=K[j>>2];c=K[r>>2];b=b-c|0}Oi:{if(d>>>16>>>0>>0){i=K[r+4>>2];m=b>>>0>>0;K[j>>2]=K[(m?8:12)+r>>2];while(1){Pi:{if(h){break Pi}o=K[e+16>>2];b=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Pi}K[e+16>>2]=b;d=(p<<9)+d|0;h=7;break Pi}K[e+16>>2]=b;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;c=c<<1;if(c>>>0<32768){continue}break}b=c;m=m?i:!i;break Oi}d=d-(c<<16)|0;if(!(b&32768)){i=K[r+4>>2];c=b>>>0>>0;K[j>>2]=K[(c?12:8)+r>>2];while(1){Qi:{if(h){break Qi}o=K[e+16>>2];m=o+1|0;p=L[o+1|0];if(L[o|0]==255){if(p>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;d=d+65280|0;h=8;break Qi}K[e+16>>2]=m;d=(p<<9)+d|0;h=7;break Qi}K[e+16>>2]=m;h=8;d=(p<<8)+d|0}h=h-1|0;d=d<<1;b=b<<1;if(b>>>0<32768){continue}break}m=c?!i:i;break Oi}m=K[r+4>>2]}c=L[n+24592|0];K[k+768>>2]=(m|0)==(c|0)?C:u;K[H>>2]=K[H>>2]|16384;K[f+4>>2]=K[f+4>>2]|4096;K[f+260>>2]=K[f+260>>2]|4;K[f+268>>2]=K[f+268>>2]|1;c=c^m;K[f+264>>2]=K[f+264>>2]|c<<18|2;g=c<<28|g|8192}K[f>>2]=g&-1226833921}g=f+4|0;c=k+4|0;t=t+1|0;if((t|0)!=64){continue}break}g=f+12|0;c=k+772|0;m=v>>>0<60;v=v+4|0;if(m){continue}break}}K[e+8>>2]=h;K[e+4>>2]=b;K[e>>2]=d;K[e+104>>2]=j}Ri:{if(!(_&32)){break Ri}K[e+104>>2]=e+100;g=K[e+100>>2];b=K[g>>2];d=K[e+4>>2]-b|0;K[e+4>>2]=d;h=K[e>>2];Si:{if(h>>>16>>>0>>0){K[e+4>>2]=b;g=K[(b>>>0>d>>>0?8:12)+g>>2];K[e+100>>2]=g;d=K[e+8>>2];while(1){Ti:{if(d){break Ti}l=K[e+16>>2];c=l+1|0;j=L[l+1|0];if(L[l|0]==255){if(j>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;d=8;break Ti}K[e+16>>2]=c;h=(j<<9)+h|0;d=7;break Ti}K[e+16>>2]=c;d=8;h=(j<<8)+h|0}d=d-1|0;K[e+8>>2]=d;h=h<<1;K[e>>2]=h;b=b<<1;K[e+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break Si}h=h-(b<<16)|0;K[e>>2]=h;if(d&32768){break Si}g=K[(b>>>0>d>>>0?12:8)+g>>2];K[e+100>>2]=g;b=K[e+8>>2];while(1){Ui:{if(b){break Ui}c=K[e+16>>2];b=c+1|0;l=L[c+1|0];if(L[c|0]==255){if(l>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;b=8;break Ui}K[e+16>>2]=b;h=(l<<9)+h|0;b=7;break Ui}K[e+16>>2]=b;b=8;h=(l<<8)+h|0}b=b-1|0;K[e+8>>2]=b;h=h<<1;K[e>>2]=h;d=d<<1;K[e+4>>2]=d;if(d>>>0<32768){continue}break}}b=K[g>>2];d=d-b|0;K[e+4>>2]=d;Vi:{if(h>>>16>>>0>>0){K[e+4>>2]=b;g=K[(b>>>0>d>>>0?8:12)+g>>2];K[e+100>>2]=g;d=K[e+8>>2];while(1){Wi:{if(d){break Wi}l=K[e+16>>2];c=l+1|0;j=L[l+1|0];if(L[l|0]==255){if(j>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;d=8;break Wi}K[e+16>>2]=c;h=(j<<9)+h|0;d=7;break Wi}K[e+16>>2]=c;d=8;h=(j<<8)+h|0}d=d-1|0;K[e+8>>2]=d;h=h<<1;K[e>>2]=h;b=b<<1;K[e+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break Vi}h=h-(b<<16)|0;K[e>>2]=h;if(d&32768){break Vi}g=K[(b>>>0>d>>>0?12:8)+g>>2];K[e+100>>2]=g;b=K[e+8>>2];while(1){Xi:{if(b){break Xi}c=K[e+16>>2];b=c+1|0;l=L[c+1|0];if(L[c|0]==255){if(l>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;b=8;break Xi}K[e+16>>2]=b;h=(l<<9)+h|0;b=7;break Xi}K[e+16>>2]=b;b=8;h=(l<<8)+h|0}b=b-1|0;K[e+8>>2]=b;h=h<<1;K[e>>2]=h;d=d<<1;K[e+4>>2]=d;if(d>>>0<32768){continue}break}}b=K[g>>2];d=d-b|0;K[e+4>>2]=d;Yi:{if(h>>>16>>>0>>0){K[e+4>>2]=b;g=K[(b>>>0>d>>>0?8:12)+g>>2];K[e+100>>2]=g;d=K[e+8>>2];while(1){Zi:{if(d){break Zi}l=K[e+16>>2];c=l+1|0;j=L[l+1|0];if(L[l|0]==255){if(j>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;d=8;break Zi}K[e+16>>2]=c;h=(j<<9)+h|0;d=7;break Zi}K[e+16>>2]=c;d=8;h=(j<<8)+h|0}d=d-1|0;K[e+8>>2]=d;h=h<<1;K[e>>2]=h;b=b<<1;K[e+4>>2]=b;if(b>>>0<32768){continue}break}d=b;break Yi}h=h-(b<<16)|0;K[e>>2]=h;if(d&32768){break Yi}g=K[(b>>>0>d>>>0?12:8)+g>>2];K[e+100>>2]=g;b=K[e+8>>2];while(1){_i:{if(b){break _i}c=K[e+16>>2];b=c+1|0;l=L[c+1|0];if(L[c|0]==255){if(l>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;b=8;break _i}K[e+16>>2]=b;h=(l<<9)+h|0;b=7;break _i}K[e+16>>2]=b;b=8;h=(l<<8)+h|0}b=b-1|0;K[e+8>>2]=b;h=h<<1;K[e>>2]=h;d=d<<1;K[e+4>>2]=d;if(d>>>0<32768){continue}break}}b=K[g>>2];d=d-b|0;K[e+4>>2]=d;if(h>>>16>>>0>>0){K[e+4>>2]=b;K[e+100>>2]=K[(b>>>0>d>>>0?8:12)+g>>2];d=K[e+8>>2];while(1){$i:{if(d){break $i}g=K[e+16>>2];c=g+1|0;l=L[g+1|0];if(L[g|0]==255){if(l>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;h=h+65280|0;d=8;break $i}K[e+16>>2]=c;h=(l<<9)+h|0;d=7;break $i}K[e+16>>2]=c;d=8;h=(l<<8)+h|0}d=d-1|0;K[e+8>>2]=d;h=h<<1;K[e>>2]=h;b=b<<1;K[e+4>>2]=b;if(b>>>0<32768){continue}break}break Ri}c=h-(b<<16)|0;K[e>>2]=c;if(d&32768){break Ri}K[e+100>>2]=K[(b>>>0>d>>>0?12:8)+g>>2];h=K[e+8>>2];while(1){aj:{if(h){break aj}g=K[e+16>>2];b=g+1|0;l=L[g+1|0];if(L[g|0]==255){if(l>>>0>=144){K[e+12>>2]=K[e+12>>2]+1;c=c+65280|0;h=8;break aj}K[e+16>>2]=b;c=(l<<9)+c|0;h=7;break aj}K[e+16>>2]=b;h=8;c=(l<<8)+c|0}h=h-1|0;K[e+8>>2]=h;c=c<<1;K[e>>2]=c;d=d<<1;K[e+4>>2]=d;if(d>>>0<32768){continue}break}}}if(!w){break Va}mc(e);bb(e,18,46);bb(e,17,3);bb(e,0,4)}b=ka+1|0;c=(b|0)==3;ka=c?0:b;x=x-c|0;G=G+1|0;if(G>>>0>=N[la+8>>2]){break Ua}if((x|0)>0){continue}break}}Z=y+Z|0;c=K[e+24>>2];b=M[e+112>>1];I[c|0]=b;I[c+1|0]=b>>>8;F=F+1|0;if(F>>>0>2]){continue}break}}bj:{if(!S){break bj}cj:{c=K[e+24>>2];g=K[e+16>>2];if(c>>>0>g+2>>>0){if(!ba){break cj}g=K[e+16>>2];c=K[e+24>>2];b=K[e+20>>2];K[$+56>>2]=c-b;K[$+52>>2]=g-b;K[$+48>>2]=(c-g|0)-2;Fa(P,2,15198,$+48|0);break bj}b=K[e+12>>2];if(b>>>0<3){break bj}if(ba){K[$+80>>2]=K[e+12>>2];Fa(P,2,7070,$+80|0);break bj}K[$+64>>2]=b;Fa(P,2,7070,$- -64|0);break bj}b=K[e+20>>2];K[$+40>>2]=c-b;K[$+36>>2]=g-b;K[$+32>>2]=(c-g|0)-2;Fa(P,2,15198,$+32|0)}if(!K[D+60>>2]){break i}K[e+116>>2]=W}l=K[sa+4>>2];g=K[D+12>>2];n=K[D+8>>2]-K[sa>>2]|0;c=K[sa+16>>2];if(c&1){b=K[pa+28>>2]+Q(ta,152)|0;n=(K[b-144>>2]+n|0)-K[b-152>>2]|0}j=g-l|0;if(c&2){b=K[pa+28>>2]+Q(ta,152)|0;j=(K[b-140>>2]+j|0)-K[b-148>>2]|0}k=K[D+60>>2];r=k?k:K[e+116>>2];p=K[e+128>>2];s=K[e+124>>2];m=K[qa+808>>2];dj:{if(!m){break dj}b=!p|!s;if((m|0)<=30){if(b){break dj}h=0;while(1){l=(Q(h,s)<<2)+r|0;b=0;while(1){g=l+(b<<2)|0;q=K[g>>2];c=q>>31;c=(c^q)-c|0;if(c>>>m|0){c=c>>>K[qa+808>>2]|0;K[g>>2]=(q|0)<0?0-c|0:c}b=b+1|0;if((s|0)!=(b|0)){continue}break}h=h+1|0;if((p|0)!=(h|0)){continue}break}break dj}if(b){break dj}b=Q(p,s)<<2;if(!b){break dj}B(r,0,b)}if(k){j=Q(p,s);if(K[qa+20>>2]==1){if(!j){break a}b=0;if((j|0)!=1){c=j&-2;g=0;while(1){l=(b<<2)+r|0;K[l>>2]=K[l>>2]/2;K[l+4>>2]=K[l+4>>2]/2;b=b+2|0;g=g+2|0;if((c|0)!=(g|0)){continue}break}}if(!(j&1)){break a}b=(b<<2)+r|0;K[b>>2]=K[b>>2]/2;break a}if(!j){break a}ga=R(O[sa+32>>2]*R(.5));if(j>>>0>=4){c=j&-4;b=0;while(1){O[r>>2]=ga*R(K[r>>2]);O[r+4>>2]=ga*R(K[r+4>>2]);O[r+8>>2]=ga*R(K[r+8>>2]);O[r+12>>2]=ga*R(K[r+12>>2]);r=r+16|0;b=b+4|0;if((c|0)!=(b|0)){continue}break}}c=j&3;if(!c){break a}b=0;while(1){O[r>>2]=ga*R(K[r>>2]);r=r+4|0;b=b+1|0;if((c|0)!=(b|0)){continue}break}break a}i=wa-ua|0;if(K[qa+20>>2]==1){if(!p){break a}f=(K[pa+36>>2]+(Q(j,i)<<2)|0)+(n<<2)|0;d=s&-4;j=0;while(1){b=0;if(d){k=f+(Q(j,i)<<2)|0;m=(Q(j,s)<<2)+r|0;while(1){q=b<<2;o=q+m|0;l=K[o+4>>2];g=K[o+8>>2];c=K[o+12>>2];q=k+q|0;K[q>>2]=K[o>>2]/2;K[q+12>>2]=(c|0)/2;K[q+8>>2]=(g|0)/2;K[q+4>>2]=(l|0)/2;b=b+4|0;if(d>>>0>b>>>0){continue}break}}ej:{if(b>>>0>=s>>>0){break ej}c=b+1|0;l=f+(Q(j,i)<<2)|0;g=(Q(j,s)<<2)+r|0;if(s-b&1){b=b<<2;K[b+l>>2]=K[b+g>>2]/2;b=c}if((c|0)==(s|0)){break ej}while(1){c=b<<2;K[c+l>>2]=K[c+g>>2]/2;c=c+4|0;K[c+l>>2]=K[c+g>>2]/2;b=b+2|0;if((s|0)!=(b|0)){continue}break}}j=j+1|0;if((p|0)!=(j|0)){continue}break}break a}if(!p|!s){break a}ga=R(O[sa+32>>2]*R(.5));j=(K[pa+36>>2]+(Q(j,i)<<2)|0)+(n<<2)|0;g=s&-4;l=s&3;f=0;c=s-1>>>0<3;while(1){b=j;e=0;if(!c){while(1){O[b>>2]=ga*R(K[r>>2]);O[b+4>>2]=ga*R(K[r+4>>2]);O[b+8>>2]=ga*R(K[r+8>>2]);O[b+12>>2]=ga*R(K[r+12>>2]);b=b+16|0;r=r+16|0;e=e+4|0;if((g|0)!=(e|0)){continue}break}}e=0;if(l){while(1){O[b>>2]=ga*R(K[r>>2]);b=b+4|0;r=r+4|0;e=e+1|0;if((l|0)!=(e|0)){continue}break}}j=(i<<2)+j|0;f=f+1|0;if((p|0)!=(f|0)){continue}break}break a}K[$>>2]=x;Fa(P,2,8679,$)}K[K[d>>2]>>2]=0}Ga(a);ra=$+96|0} +function jb(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,C=0,D=0,F=0,G=0,H=0,M=0,P=0,T=0,U=0,V=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=R(0),ma=0,na=0,oa=0,pa=0,qa=0,sa=0,ta=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Ka=0,Oa=0,Pa=0,Qa=0,Ra=0,Sa=0,Ta=0,Wa=0,Ya=0,$a=0,ab=0,bb=0,cb=0,eb=0,fb=0,gb=0,hb=0,ib=0,jb=0,mb=0,ob=0,pb=0,qb=0,tb=R(0),ub=0,vb=0,wb=R(0),xb=0,yb=0,zb=0,Ab=0,Bb=0,Cb=0,Db=0,Eb=0,Ib=0,Jb=0,Kb=0,Lb=R(0),Mb=0,Nb=0,Ob=0,Pb=0,Qb=0,Rb=0,Sb=0,Tb=0,Ub=0,Vb=0,Wb=0,Xb=0,Yb=0,Zb=0,_b=0,$b=0;$a=ra-16|0;ra=$a;a:{if(!(L[a+8|0]&128)|K[a+228>>2]!=(b|0)){break a}Ya=K[a+180>>2]+Q(b,5644)|0;y=K[Ya+5596>>2];if(!y){nb(Ya);break a}t=K[a+100>>2];if(!t){t=K[a+96>>2]}k=K[t>>2];m=K[t+4>>2];h=K[t+8>>2];i=K[t+12>>2];o=K[a+60>>2];s=K[a+64>>2];l=K[Ya+5600>>2];Ea=ra-16|0;ra=Ea;C=K[a+232>>2];K[C+36>>2]=b;g=K[K[C+28>>2]+76>>2];K[C+64>>2]=1;K[C+60>>2]=i;K[C+56>>2]=h;K[C+52>>2]=m;K[C+48>>2]=k;K[C+32>>2]=g+Q(b,5644);Ga(K[C+68>>2]);K[C+68>>2]=0;b:{if(o){n=Ia(4,K[K[C+24>>2]+16>>2]);if(!n){break b}t=0;if(o>>>0>=4){g=o&-4;b=0;while(1){i=s+(Z<<2)|0;K[(K[i>>2]<<2)+n>>2]=1;K[(K[i+4>>2]<<2)+n>>2]=1;K[(K[i+8>>2]<<2)+n>>2]=1;K[(K[i+12>>2]<<2)+n>>2]=1;Z=Z+4|0;b=b+4|0;if((g|0)!=(b|0)){continue}break}}b=o&3;if(b){while(1){K[(K[s+(Z<<2)>>2]<<2)+n>>2]=1;Z=Z+1|0;t=t+1|0;if((b|0)!=(t|0)){continue}break}}K[C+68>>2]=n}c:{D=K[C+24>>2];aa=K[D+16>>2];d:{if(!aa){break d}Z=0;e:{while(1){f:{if(K[(Z<<2)+n>>2]?0:n){break f}h=K[D+24>>2]+Q(Z,52)|0;p=K[h+4>>2];k=p-1|0;i=K[C+60>>2];g=k+i|0;t=0-!p|0;b=t;m=Ne(g,g>>>0>>0?b+1|0:b,p,0);o=K[h>>2];h=o-1|0;i=K[C+56>>2];g=h+i|0;s=0-!o|0;b=s;i=Ne(g,g>>>0>>0?b+1|0:b,o,0);g=K[C+52>>2];b=g+k|0;k=Ne(b,b>>>0>>0?t+1|0:t,p,0);p=K[K[K[C+20>>2]>>2]+20>>2]+Q(Z,76)|0;t=K[p+20>>2]-K[p+24>>2]|0;if(t>>>0>31){break f}g=K[C+48>>2];b=g+h|0;g=Ne(b,b>>>0>>0?s+1|0:s,o,0);b=g-K[p>>2]|0;g:{if((b>>>0<=g>>>0?b:0)>>>t|0){break g}b=k-K[p+4>>2]|0;if((b>>>0<=k>>>0?b:0)>>>t|0){break g}g=K[p+8>>2];b=g-i|0;if((b>>>0<=g>>>0?b:0)>>>t|0){break g}g=K[p+12>>2];b=g-m|0;if(!((b>>>0<=g>>>0?b:0)>>>t|0)){break f}}K[C+64>>2]=0;break e}Z=Z+1|0;if((aa|0)!=(Z|0)){continue}break}if(!K[C+64>>2]){break e}t=0;while(1){k=K[K[K[C+20>>2]>>2]+20>>2]+Q(t,76)|0;b=K[k+28>>2]+Q(K[k+24>>2],152)|0;n=K[b-148>>2];m=K[b-140>>2];h=K[b-152>>2];i=K[b-144>>2];b=K[C+68>>2];h:{if(K[b+(t<<2)>>2]?0:b){break h}g=m-n|0;b=i-h|0;Le(g,0,b);if(!(!ua|(m|0)==(n|0))){Z=0;Fa(f,1,2945,0);break b}b=Q(b,g);if(b>>>0>=1073741824){Z=0;Fa(f,1,2945,0);break b}g=b<<2;K[k+44>>2]=g;i:{j:{k:{b=K[k+36>>2];if(b){if(g>>>0<=N[k+48>>2]){break h}if(K[k+40>>2]){break k}}b=Ma(g);K[k+36>>2]=b;g=b;b=K[k+44>>2];if(!(b?g:1)){break j}K[k+40>>2]=1;K[k+48>>2]=b;break h}Ga(b);b=Ma(K[k+44>>2]);K[k+36>>2]=b;if(b){break i}K[k+48>>2]=0;K[k+40>>2]=0;K[k+44>>2]=0}Z=0;Fa(f,1,2945,0);break b}K[k+40>>2]=1;K[k+48>>2]=K[k+44>>2]}t=t+1|0;D=K[C+24>>2];if(t>>>0>2]){continue}break}break d}$=K[D+24>>2];H=K[K[K[C+20>>2]>>2]+20>>2];b=0;while(1){l:{if(K[(b<<2)+n>>2]?0:n){break l}ga=H+Q(b,76)|0;k=K[ga>>2];m=$+Q(b,52)|0;v=K[m>>2];s=v-1|0;h=K[C+48>>2];i=s+h|0;o=0-!v|0;g=o;g=Ne(i,h>>>0>i>>>0?g+1|0:g,v,0);ea=g>>>0>>0?k:g;K[ga+56>>2]=ea;k=K[ga+4>>2];p=K[m+4>>2];m=p-1|0;h=K[C+52>>2];i=m+h|0;t=0-!p|0;g=t;g=Ne(i,h>>>0>i>>>0?g+1|0:g,p,0);T=g>>>0>>0?k:g;K[ga+60>>2]=T;h=K[ga+8>>2];i=K[C+56>>2];g=i+s|0;g=Ne(g,g>>>0>>0?o+1|0:o,v,0);k=g>>>0>h>>>0?h:g;K[ga+64>>2]=k;h=K[ga+12>>2];i=K[C+60>>2];g=m+i|0;g=Ne(g,g>>>0>>0?t+1|0:t,p,0);g=g>>>0>h>>>0?h:g;K[ga+68>>2]=g;if(g>>>0>>0|k>>>0>>0){break c}ka=K[ga+20>>2];if(!ka){break l}_=g-1|0;x=0-!g|0;r=k-1|0;v=0-!k|0;p=T-1|0;t=0-!T|0;s=ea-1|0;k=0-!ea|0;m=K[ga+28>>2];ga=0;h=0;while(1){ea=m+Q(ga,152)|0;ba=ka+(ga^-1)|0;i=ba&31;if((ba&63)>>>0>=32){o=1<>>32-i}ha=g;g=_+ha|0;i=o+x|0;i=g>>>0<_>>>0?i+1|0:i;T=ba&31;if((ba&63)>>>0>=32){g=i>>>T|0}else{g=((1<>>T}K[ea+148>>2]=g;g=o+v|0;F=g+1|0;i=g;g=r+ha|0;i=g>>>0>>0?F:i;T=ba&31;if((ba&63)>>>0>=32){g=i>>>T|0}else{g=((1<>>T}K[ea+144>>2]=g;g=o+t|0;F=g+1|0;i=g;g=p+ha|0;i=g>>>0>>0?F:i;T=ba&31;if((ba&63)>>>0>=32){g=i>>>T|0}else{g=((1<>>T}K[ea+140>>2]=g;g=k+o|0;o=g+1|0;i=g;g=s+ha|0;i=g>>>0>>0?o:i;o=ba&31;if((ba&63)>>>0>=32){g=i>>>o|0}else{g=((1<>>o}K[ea+136>>2]=g;ga=ga+1|0;h=ga?h:h+1|0;if(h|(ga|0)!=(ka|0)){continue}break}}b=b+1|0;if((aa|0)!=(b|0)){continue}break}}Z=0;K[Ea+8>>2]=0;b=K[C+28>>2];ma=Ia(1,8);if(ma){K[ma+4>>2]=b;K[ma>>2]=D}if(!ma){break b}va=K[K[C+20>>2]>>2];w=ra-144|0;ra=w;r=K[C+36>>2];b=Q(r,5644);x=K[ma+4>>2];xa=b+K[x+76>>2]|0;ha=K[xa+420>>2];m=0;i=0;P=ra-32|0;ra=P;pa=b+K[x+76>>2]|0;M=K[pa+420>>2];ia=K[ma>>2];ca=K[ia+16>>2];k=Ja(Q(ca,528));m:{if(!k){break m}b=Ja(ca<<2);n:{if(!b){b=k;break n}o=K[x+76>>2]+Q(r,5644)|0;v=K[o+420>>2];h=v+1|0;g=Ia(h,240);o:{if(g){p:{if(h){F=K[ia+16>>2];t=g;while(1){K[t+236>>2]=f;h=Ia(F,16);K[t+200>>2]=h;if(!h){break p}h=K[ia+16>>2];K[t+196>>2]=h;p=0;F=0;if(h){while(1){s=K[t+200>>2]+(p<<4)|0;n=K[o+5584>>2]+Q(p,1080)|0;h=Ia(K[n+4>>2],16);K[s+12>>2]=h;if(!h){break p}K[s+8>>2]=K[n+4>>2];p=p+1|0;F=K[ia+16>>2];if(p>>>0>>0){continue}break}}t=t+240|0;h=(j|0)==(v|0);j=j+1|0;if(!h){continue}break}}break o}h=K[g+4>>2];if(h){Ga(h);K[g+4>>2]=0}t=g;n=0;while(1){p=K[t+200>>2];if(p){F=0;j=K[t+196>>2];if(j){while(1){h=K[p+12>>2];if(h){Ga(h);K[p+12>>2]=0;j=K[t+196>>2]}p=p+16|0;F=F+1|0;if(j>>>0>F>>>0){continue}break}p=K[t+200>>2]}Ga(p);K[t+200>>2]=0}t=t+240|0;h=(n|0)==(v|0);n=n+1|0;if(!h){continue}break}Ga(g)}g=0}if(g){q:{if(!ca){break q}n=k;if(ca>>>0>=8){h=ca&-8;while(1){j=(q<<2)+b|0;K[j>>2]=n;K[j+4>>2]=n+528;K[j+8>>2]=n+1056;K[j+12>>2]=n+1584;K[j+16>>2]=n+2112;K[j+20>>2]=n+2640;K[j+24>>2]=n+3168;K[j+28>>2]=n+3696;q=q+8|0;n=n+4224|0;m=m+8|0;if((h|0)!=(m|0)){continue}break}}h=ca&7;if(!h){break q}while(1){K[(q<<2)+b>>2]=n;q=q+1|0;n=n+528|0;G=G+1|0;if((h|0)!=(G|0)){continue}break}}p=b;F=0;t=K[(K[x+76>>2]+Q(r,5644)|0)+5584>>2];n=K[ia+24>>2];b=K[x+24>>2];j=(r>>>0)/(b>>>0)|0;h=K[x+4>>2]+Q(K[x+12>>2],r-Q(b,j)|0)|0;b=K[ia>>2];K[P+20>>2]=b>>>0>>0?h:b;b=h+K[x+12>>2]|0;h=b>>>0>>0?-1:b;b=K[ia+8>>2];K[P+16>>2]=b>>>0>h>>>0?h:b;h=K[x+8>>2]+Q(j,K[x+16>>2])|0;b=K[ia+4>>2];K[P+12>>2]=b>>>0>>0?h:b;b=h+K[x+16>>2]|0;h=b>>>0>>0?-1:b;b=K[ia+12>>2];K[P+8>>2]=b>>>0>h>>>0?h:b;K[P+24>>2]=0;K[P+28>>2]=0;K[P+4>>2]=2147483647;K[P>>2]=2147483647;if(K[ia+16>>2]){while(1){b=p?K[p+(F<<2)>>2]:0;$=K[n+4>>2];s=$-1|0;m=K[P+8>>2];j=s+m|0;x=0-!$|0;h=x;r=Ne(j,j>>>0>>0?h+1|0:h,$,0);H=K[n>>2];q=H-1|0;m=K[P+16>>2];j=q+m|0;v=0-!H|0;h=v;o=Ne(j,j>>>0>>0?h+1|0:h,H,0);j=K[P+12>>2];h=j+s|0;m=Ne(h,h>>>0>>0?x+1|0:x,$,0);j=K[P+20>>2];h=j+q|0;j=Ne(h,h>>>0>>0?v+1|0:v,H,0);h=K[t+4>>2];if(h>>>0>N[P+28>>2]){K[P+28>>2]=h;h=K[t+4>>2]}if(h){ga=t+944|0;G=t+812|0;qa=r-1|0;ea=0-!r|0;T=o-1|0;aa=0-!o|0;ka=m-1|0;_=0-!m|0;$=j-1|0;H=0-!j|0;j=0;while(1){m=j<<2;da=K[m+ga>>2];z=K[m+G>>2];q=0;if(b){K[b+4>>2]=da;K[b>>2]=z;q=b+8|0}h=h-1|0;s=z+h|0;r:{if(s>>>0>31){break r}b=K[n>>2];if(b>>>0>-1>>>s>>>0){break r}m=K[P+4>>2];b=b<>2]=b>>>0>m>>>0?m:b}s=h+da|0;s:{if(s>>>0>31){break s}b=K[n+4>>2];if(b>>>0>-1>>>s>>>0){break s}m=K[P>>2];b=b<>2]=b>>>0>m>>>0?m:b}b=0;s=h&31;if((h&63)>>>0>=32){o=1<>>32-s}V=m;m=qa+V|0;s=o;o=ea+o|0;r=m>>>0>>0?o+1|0:o;ba=h&31;o=da&31;if((da&63)>>>0>=32){o=1<>>32-o;x=v}if((h&63)>>>0>=32){r=r>>>ba|0}else{r=((1<>>ba}m=x+r|0;v=m-1|0;x=(m>>>0>>0?o+1|0:o)-!m|0;ba=da&31;m=s+_|0;wa=m+1|0;o=m;m=V+ka|0;o=m>>>0>>0?wa:o;if((da&63)>>>0>=32){x=x>>>ba|0}else{x=((1<>>ba}v=h&31;if((h&63)>>>0>=32){m=o>>>v|0}else{m=((1<>>v}ba=(m|0)!=(r|0)?x-(m>>>da|0)&-1>>>da:0;m=s+aa|0;r=m+1|0;x=m;m=T+V|0;r=m>>>0>>0?r:x;da=h&31;o=z&31;if((z&63)>>>0>=32){o=1<>>32-o;x=v}if((h&63)>>>0>=32){r=r>>>da|0}else{r=((1<>>da}m=x+r|0;v=m-1|0;o=(m>>>0>>0?o+1|0:o)-!m|0;x=z&31;m=s+H|0;wa=m+1|0;s=m;m=V+$|0;s=m>>>0>>0?wa:s;if((z&63)>>>0>=32){x=o>>>x|0}else{x=((1<>>x}o=h&31;if((h&63)>>>0>=32){m=s>>>o|0}else{m=((1<>>o}m=(m|0)!=(r|0)?x-(m>>>z|0)&-1>>>z:0;if(q){K[q+4>>2]=ba;K[q>>2]=m;b=q+8|0}m=Q(m,ba);if(m>>>0>N[P+24>>2]){K[P+24>>2]=m}j=j+1|0;if(j>>>0>2]){continue}break}}n=n+52|0;t=t+1080|0;F=F+1|0;if(F>>>0>2]){continue}break}}s=M+1|0;aa=K[P+28>>2];T=K[P+24>>2];K[g+4>>2]=0;b=K[pa+8>>2]+1|0;$=Q(T,ca);ka=Q($,aa);Le(b,0,ka);t:{if(!ua){b=Q(b,ka);K[g+8>>2]=b;b=Ia(b,2);K[g+4>>2]=b;if(b){break t}}Ga(k);Ga(p);b=K[g+4>>2];if(b){Ga(b);K[g+4>>2]=0}if(!s){b=g;break n}b=0;m=g;while(1){n=K[m+200>>2];if(n){r=0;q=K[m+196>>2];if(q){while(1){h=K[n+12>>2];if(h){Ga(h);K[n+12>>2]=0;q=K[m+196>>2]}n=n+16|0;r=r+1|0;if(q>>>0>r>>>0){continue}break}n=K[m+200>>2]}Ga(n);K[m+200>>2]=0}m=m+240|0;h=(b|0)==(M|0);b=b+1|0;if(!h){continue}break}b=g;break n}j=K[ia+24>>2];H=K[P+20>>2];K[g+204>>2]=H;x=K[P+12>>2];K[g+208>>2]=x;v=K[P+16>>2];K[g+212>>2]=v;o=K[P+8>>2];K[g+216>>2]=o;K[g+12>>2]=ka;K[g+16>>2]=$;K[g+20>>2]=T;i=1;K[g+24>>2]=1;if(ca){m=K[g+200>>2];t=0;b=j;while(1){n=K[p+(t<<2)>>2];K[m>>2]=K[b>>2];K[m+4>>2]=K[b+4>>2];_=K[m+8>>2];u:{if(!_){break u}r=K[m+12>>2];if((_|0)!=1){h=_&-2;q=0;while(1){K[r>>2]=K[n>>2];K[r+4>>2]=K[n+4>>2];K[r+8>>2]=K[n+8>>2];K[r+12>>2]=K[n+12>>2];K[r+16>>2]=K[n+16>>2];K[r+20>>2]=K[n+20>>2];K[r+24>>2]=K[n+24>>2];K[r+28>>2]=K[n+28>>2];r=r+32|0;n=n+32|0;q=q+2|0;if((h|0)!=(q|0)){continue}break}}if(!(_&1)){break u}K[r>>2]=K[n>>2];K[r+4>>2]=K[n+4>>2];K[r+8>>2]=K[n+8>>2];K[r+12>>2]=K[n+12>>2]}b=b+52|0;m=m+16|0;t=t+1|0;if((ca|0)!=(t|0)){continue}break}}if(s>>>0>1){h=g;while(1){K[h+456>>2]=o;K[h+452>>2]=v;K[h+448>>2]=x;K[h+444>>2]=H;K[h+264>>2]=1;K[h+260>>2]=T;K[h+256>>2]=$;K[h+252>>2]=ka;if(ca){m=K[h+440>>2];t=0;b=j;while(1){n=K[p+(t<<2)>>2];K[m>>2]=K[b>>2];K[m+4>>2]=K[b+4>>2];_=K[m+8>>2];v:{if(!_){break v}r=K[m+12>>2];if((_|0)!=1){s=_&-2;q=0;while(1){K[r>>2]=K[n>>2];K[r+4>>2]=K[n+4>>2];K[r+8>>2]=K[n+8>>2];K[r+12>>2]=K[n+12>>2];K[r+16>>2]=K[n+16>>2];K[r+20>>2]=K[n+20>>2];K[r+24>>2]=K[n+24>>2];K[r+28>>2]=K[n+28>>2];r=r+32|0;n=n+32|0;q=q+2|0;if((s|0)!=(q|0)){continue}break}}if(!(_&1)){break v}K[r>>2]=K[n>>2];K[r+4>>2]=K[n+4>>2];K[r+8>>2]=K[n+8>>2];K[r+12>>2]=K[n+12>>2]}b=b+52|0;m=m+16|0;t=t+1|0;if((ca|0)!=(t|0)){continue}break}}b=K[h+8>>2];K[h+244>>2]=K[h+4>>2];K[h+248>>2]=b;b=(i|0)!=(M|0);h=h+240|0;i=i+1|0;if(b){continue}break}}Ga(k);Ga(p);j=K[pa+420>>2];w:{if(L[pa+5640|0]&4){if((j|0)==-1){break w}r=pa+424|0;i=K[pa+8>>2];q=0;n=g;while(1){b=K[r+36>>2];K[n+44>>2]=1;K[n+84>>2]=b;K[n+48>>2]=K[r>>2];b=K[r+4>>2];K[n+68>>2]=0;K[n+72>>2]=0;K[n+52>>2]=b;K[n+60>>2]=K[r+12>>2];K[n+64>>2]=K[r+16>>2];b=K[r+8>>2];K[n+76>>2]=T;K[n+56>>2]=b>>>0>>0?b:i;r=r+148|0;n=n+240|0;b=(j|0)==(q|0);q=q+1|0;if(!b){continue}break}break w}if((j|0)==-1){break w}h=K[pa+8>>2];i=K[pa+4>>2];n=g;if(j){b=j+1&-2;G=0;while(1){K[n+68>>2]=0;K[n+72>>2]=0;K[n+52>>2]=0;K[n+44>>2]=1;K[n+48>>2]=0;K[n+84>>2]=i;K[n+60>>2]=aa;K[n+324>>2]=i;K[n+76>>2]=T;K[n+56>>2]=h;K[n+308>>2]=0;K[n+312>>2]=0;K[n+292>>2]=0;K[n+284>>2]=1;K[n+288>>2]=0;K[n+300>>2]=aa;K[n+296>>2]=h;K[n+316>>2]=T;K[n+64>>2]=K[n+196>>2];K[n+304>>2]=K[n+436>>2];n=n+480|0;G=G+2|0;if((b|0)!=(G|0)){continue}break}}if(j&1){break w}K[n+68>>2]=0;K[n+72>>2]=0;K[n+52>>2]=0;K[n+44>>2]=1;K[n+48>>2]=0;K[n+84>>2]=i;K[n+60>>2]=aa;K[n+76>>2]=T;K[n+56>>2]=h;K[n+64>>2]=K[n+196>>2]}i=g;break m}Ga(k)}Ga(b)}ra=P+32|0;k=i;x:{y:{if(!i){break y}ka=ha+1|0;t=y;v=i;z:{A:{while(1){if(K[v+84>>2]==-1){break z}aa=Ja(K[ia+16>>2]<<2);if(!aa){break z}b=K[ia+16>>2]<<2;if(b){B(aa,1,b)}if(jc(v)){while(1){p=K[va+20>>2];B:{C:{if(N[v+40>>2]>=N[xa+12>>2]){break C}g=K[v+32>>2];b=Q(K[v+28>>2],76)+p|0;if(g>>>0>=N[b+24>>2]){break C}i=K[b+28>>2]+Q(g,152)|0;if(!K[i+24>>2]){break C}g=i+28|0;m=0;D:{while(1){b=g+Q(m,36)|0;h=K[b+20>>2]+Q(K[v+36>>2],40)|0;if(!Fb(C,K[v+28>>2],K[v+32>>2],K[b+16>>2],K[h>>2],K[h+4>>2],K[h+8>>2],K[h+12>>2])){m=m+1|0;if(m>>>0>2]){continue}break D}break}K[aa+(K[v+28>>2]<<2)>>2]=0;K[w+136>>2]=0;if(!ic(K[ma+4>>2],K[va+20>>2],xa,v,w+140|0,t,w+136|0,l,f)){break A}m=K[v+32>>2];s=K[v+28>>2];r=K[w+136>>2];if(K[w+140>>2]){K[w+136>>2]=0;$=K[(K[va+20>>2]+Q(s,76)|0)+28>>2]+Q(m,152)|0;p=K[$+24>>2];if(p){_=l-r|0;H=l+t|0;s=$+28|0;q=0;j=0;x=r+t|0;h=x;while(1){E:{if(K[s+8>>2]==K[s>>2]|K[s+12>>2]==K[s+4>>2]){break E}b=K[s+20>>2]+Q(K[v+36>>2],40)|0;o=Q(K[b+20>>2],K[b+16>>2]);if(!o){break E}p=K[b+24>>2];F=0;while(1){n=K[p+36>>2];if(n){F:{if(j|K[p+64>>2]){K[p+52>>2]=0;m=1;b=64;break F}m=K[p>>2];b=K[p+40>>2];G:{if(b){m=Q(b,24)+m|0;if(K[m-20>>2]!=K[m-12>>2]){m=m-24|0;break G}b=b+1|0}else{b=1}K[p+40>>2]=b}b=K[m+20>>2];H:{I:{if(b>>>0>(h^-1)>>>0){break I}i=m+20|0;while(1){if(H>>>0>>0){break I}V=K[p+4>>2];j=K[p+52>>2];if((j|0)!=K[p+56>>2]){g=n}else{b=j<<1|1;V=La(V,b<<3);if(!V){Fa(f,1,1024,0);break A}K[p+56>>2]=b;K[p+4>>2]=V;j=K[p+52>>2];b=K[i>>2];g=K[p+36>>2]}i=(j<<3)+V|0;K[i+4>>2]=b;K[i>>2]=h;K[p+52>>2]=j+1;K[m>>2]=K[m>>2]+b;j=K[m+16>>2];i=j+K[m+4>>2]|0;K[m+4>>2]=i;n=g-j|0;K[p+36>>2]=n;K[m+8>>2]=i;h=b+h|0;b=0;if((g|0)==(j|0)){break H}K[p+40>>2]=K[p+40>>2]+1;i=m+44|0;b=K[m+44>>2];m=m+24|0;if((h^-1)>>>0>=b>>>0){continue}break}}j=K[v+28>>2];i=K[v+32>>2];g=K[v+36>>2];if(K[K[ma+4>>2]+104>>2]){K[w+120>>2]=j;K[w+116>>2]=i;K[w+112>>2]=q;K[w+108>>2]=g;K[w+104>>2]=F;K[w+100>>2]=_;K[w+96>>2]=b;Fa(f,1,14656,w+96|0);break A}K[w+88>>2]=j;K[w+84>>2]=i;K[w+80>>2]=q;K[w+76>>2]=g;K[w+72>>2]=F;K[w+68>>2]=_;K[w+64>>2]=b;Fa(f,2,14656,w- -64|0);K[p+52>>2]=0;K[p+64>>2]=1;b=1}j=b;m=K[p+40>>2];b=44}K[b+p>>2]=m}p=p+68|0;F=F+1|0;if((o|0)!=(F|0)){continue}break}p=K[$+24>>2]}s=s+36|0;q=q+1|0;if(q>>>0

>>0){continue}break}m=K[v+32>>2];s=K[v+28>>2];b=j?_:h-x|0}else{b=0}r=b+r|0}b=K[ia+24>>2]+Q(s,52)|0;g=K[b+36>>2];K[b+36>>2]=g>>>0>>0?m:g;break B}p=K[va+20>>2]}K[w+136>>2]=0;if(!ic(K[ma+4>>2],p,xa,v,w+140|0,t,w+136|0,l,f)){break A}s=K[v+28>>2];r=K[w+136>>2];if(!K[w+140>>2]){break B}x=K[v+32>>2];b=K[(K[va+20>>2]+Q(s,76)|0)+28>>2]+Q(x,152)|0;o=K[b+24>>2];if(!o){break B}$=l-r|0;V=b+28|0;H=K[v+36>>2];m=0;j=0;J:{K:{while(1){L:{if(K[V+8>>2]==K[V>>2]|K[V+12>>2]==K[V+4>>2]){break L}b=K[V+20>>2]+Q(H,40)|0;i=Q(K[b+20>>2],K[b+16>>2]);if(!i){break L}n=K[b+24>>2];q=0;while(1){b=K[n+36>>2];if(b){p=K[n>>2];F=K[n+40>>2];M:{if(F){p=Q(F,24)+p|0;if(K[p-20>>2]!=K[p-12>>2]){p=p-24|0;break M}F=F+1|0}else{F=1}K[n+40>>2]=F}h=K[p+20>>2];m=m+h|0;if($>>>0>>0|h>>>0>m>>>0){break J}while(1){N:{h=K[p+16>>2];K[p+4>>2]=h+K[p+4>>2];g=b-h|0;if((b|0)==(h|0)){break N}F=F+1|0;K[n+40>>2]=F;h=K[p+44>>2];m=m+h|0;if(h>>>0>m>>>0){break K}p=p+24|0;b=g;if(m>>>0<=$>>>0){continue}break K}break}K[n+36>>2]=g}n=n+68|0;q=q+1|0;if((i|0)!=(q|0)){continue}break}}V=V+36|0;j=j+1|0;if((o|0)!=(j|0)){continue}break}r=m+r|0;break B}K[n+36>>2]=g}if(!K[K[ma+4>>2]+104>>2]){K[w+24>>2]=s;K[w+20>>2]=x;K[w+16>>2]=j;K[w+12>>2]=H;K[w+8>>2]=q;K[w+4>>2]=$;K[w>>2]=h;Fa(f,2,14571,w);s=K[v+28>>2];r=r+$|0;break B}K[w+56>>2]=s;K[w+52>>2]=x;K[w+48>>2]=j;K[w+44>>2]=H;K[w+40>>2]=q;K[w+36>>2]=$;K[w+32>>2]=h;Fa(f,1,14571,w+32|0);break A}O:{if(!K[aa+(s<<2)>>2]){break O}b=K[ia+24>>2]+Q(s,52)|0;if(K[b+36>>2]){break O}K[b+36>>2]=K[(K[va+20>>2]+Q(s,76)|0)+24>>2]-1}l=l-r|0;t=r+t|0;if(jc(v)){continue}break}}Ga(aa);v=v+240|0;fa=fa+1|0;if(fa>>>0<=N[xa+420>>2]){continue}break}Gb(k,ka);K[Ea+8>>2]=t-y;b=1;break x}Gb(k,ka);Ga(aa);break y}Gb(k,ka)}b=0}ra=w+144|0;kb(ma);if(!b){break b}Z=K[K[C+32>>2]+5584>>2];q=K[K[C+20>>2]>>2];A=K[q+20>>2];K[Ea+12>>2]=1;t=0;b=K[C+32>>2];m=K[Z+16>>2]>>>4&1&K[b+12>>2]==K[b+8>>2];D=K[q+16>>2];P:{if(!D){break P}while(1){b=K[C+68>>2];if(!(K[b+(t<<2)>>2]?0:b)){i=Ea+12|0;D=0;b=K[A+24>>2];Q:{if(!b){break Q}k=K[C+44>>2];while(1){o=K[A+28>>2]+Q(D,152)|0;n=K[o+24>>2];if(n){l=o+28|0;b=K[o+20>>2];v=K[o+16>>2];j=0;while(1){if(Q(b,v)){s=l+Q(j,36)|0;p=0;while(1){r=K[s+20>>2]+Q(p,40)|0;b=Fb(C,K[A+16>>2],D,K[s+16>>2],K[r>>2],K[r+4>>2],K[r+8>>2],K[r+12>>2]);h=K[r+16>>2];n=K[r+20>>2];g=Q(h,n);R:{if(b){if(!g){break R}h=0;while(1){g=K[r+24>>2]+Q(h,68)|0;S:{if(!Fb(C,K[A+16>>2],D,K[s+16>>2],K[g+8>>2],K[g+12>>2],K[g+16>>2],K[g+20>>2])){b=K[g+60>>2];if(!b){break S}Ga(b);K[g+60>>2]=0;break S}if(!K[C+64>>2]){if(K[g+60>>2]|K[g+16>>2]==K[g+8>>2]|K[g+20>>2]==K[g+12>>2]){break S}}n=Ia(1,44);if(!n){K[Ea+12>>2]=0;break Q}b=K[C+64>>2];K[n+36>>2]=0;K[n+28>>2]=i;K[n+20>>2]=Z;K[n+16>>2]=A;K[n+12>>2]=s;K[n+8>>2]=g;K[n+4>>2]=D;K[n>>2]=b;K[n+40>>2]=m;K[n+32>>2]=f;K[n+24>>2]=K[k+4>>2]>1;lb(k,14,n);if(!K[Ea+12>>2]){break Q}}h=h+1|0;if(h>>>0>2],K[r+16>>2])>>>0){continue}break}break R}if(!g){break R}v=0;while(1){g=K[r+24>>2]+Q(v,68)|0;b=K[g+60>>2];if(b){Ga(b);K[g+60>>2]=0;n=K[r+20>>2];h=K[r+16>>2]}v=v+1|0;if(v>>>0>>0){continue}break}}p=p+1|0;b=K[o+20>>2];v=K[o+16>>2];if(p>>>0>>0){continue}break}n=K[o+24>>2]}j=j+1|0;if(n>>>0>j>>>0){continue}break}b=K[A+24>>2]}D=D+1|0;if(D>>>0>>0){continue}break}}if(!K[Ea+12>>2]){break P}D=K[q+16>>2]}Z=Z+1080|0;A=A+76|0;t=t+1|0;if(D>>>0>t>>>0){continue}break}}Z=0;Xa(K[C+44>>2]);if(!K[Ea+12>>2]){break b}T:{if(K[C+64>>2]){break T}t=K[C+24>>2];if(!K[t+16>>2]){break T}A=0;while(1){j=K[K[K[C+20>>2]>>2]+20>>2]+Q(A,76)|0;b=K[j+28>>2]+Q(K[(K[t+24>>2]+Q(A,52)|0)+36>>2],152)|0;h=K[b+136>>2];i=K[b+144>>2];l=K[b+140>>2];g=K[b+148>>2];Ga(K[j+52>>2]);K[j+52>>2]=0;U:{b=K[C+68>>2];if((h|0)==(i|0)|(g|0)==(l|0)|(K[b+(A<<2)>>2]?0:b)){break U}g=g-l|0;b=i-h|0;Le(g,0,b);if(ua){Fa(f,1,2945,0);break b}b=Q(b,g);if(b>>>0>=1073741824){Fa(f,1,2945,0);break b}b=Ma(b<<2);K[j+52>>2]=b;if(b){break U}Fa(f,1,2945,0);break b}A=A+1|0;t=K[C+24>>2];if(A>>>0>2]){continue}break}}t=K[C+32>>2];v=K[K[C+20>>2]>>2];if(K[v+16>>2]){A=K[v+20>>2];t=K[t+5584>>2];D=K[K[C+24>>2]+24>>2];n=0;while(1){V:{b=K[C+68>>2];if(K[b+(n<<2)>>2]?0:b){break V}i=K[D+36>>2]+1|0;if(K[t+20>>2]==1){M=i;b=0;ta=ra-32|0;ra=ta;W:{X:{if(K[C+64>>2]){g=1;if((i|0)==1){break W}j=K[A+28>>2];b=j+Q(K[A+24>>2],152)|0;s=K[b-144>>2];q=K[b-152>>2];if((s|0)==(q|0)){break W}l=i-1|0;k=l&1;$=K[C+44>>2];x=K[$+4>>2];Y:{if((i|0)==2){b=0;h=j;break Y}m=l&-2;b=0;h=j;g=0;while(1){i=K[h+160>>2]-K[h+152>>2]|0;i=b>>>0>i>>>0?b:i;b=K[h+164>>2]-K[h+156>>2]|0;i=b>>>0>>0?i:b;b=K[h+312>>2]-K[h+304>>2]|0;i=b>>>0>>0?i:b;b=K[h+316>>2]-K[h+308>>2]|0;b=b>>>0>>0?i:b;h=h+304|0;g=g+2|0;if((m|0)!=(g|0)){continue}break}}g=0;if(k){i=K[h+160>>2]-K[h+152>>2]|0;i=b>>>0>i>>>0?b:i;b=K[h+164>>2]-K[h+156>>2]|0;b=b>>>0>>0?i:b}if(b>>>0>134217727){break W}k=K[j+4>>2];i=K[j+12>>2];m=K[j>>2];h=K[j+8>>2];p=b<<5;H=sb(p);K[ta+16>>2]=H;if(!H){break W}K[ta>>2]=H;if(l){y=s-q|0;i=i-k|0;g=h-m|0;while(1){r=K[A+36>>2];m=i;K[ta+8>>2]=i;b=g;K[ta+24>>2]=b;k=K[j+156>>2];h=K[j+164>>2];i=K[j+160>>2];g=K[j+152>>2];K[ta+28>>2]=(g|0)%2;g=i-g|0;K[ta+20>>2]=g-b;q=(x|0)<2;i=h-k|0;Z:{if(!(!q&i>>>0>1)){h=0;if(!i){break Z}while(1){pc(ta+16|0,r+(Q(h,y)<<2)|0);h=h+1|0;if((i|0)!=(h|0)){continue}break}break Z}o=i>>>0>>0?i:x;k=o-1|0;s=(i>>>0)/(o>>>0)|0;b=0;while(1){_=Ja(36);if(!_){break X}h=K[ta+20>>2];K[_>>2]=K[ta+16>>2];K[_+4>>2]=h;h=K[ta+28>>2];K[_+8>>2]=K[ta+24>>2];K[_+12>>2]=h;K[_+28>>2]=Q(b,s);K[_+24>>2]=r;K[_+20>>2]=y;K[_+16>>2]=g;h=(b|0)==(k|0);b=b+1|0;K[_+32>>2]=h?i:Q(s,b);h=sb(p);K[_>>2]=h;if(!h){g=0;Xa($);Ga(_);Ga(H);break W}lb($,10,_);if((b|0)!=(o|0)){continue}break}Xa($)}K[ta+4>>2]=i-m;K[ta+12>>2]=K[j+156>>2]%2;_:{if(!(!q&g>>>0>1)){b=8;h=0;if(g>>>0>=8){while(1){rb(ta,r+(h<<2)|0,y,8);h=b;b=b+8|0;if(g>>>0>=b>>>0){continue}break}}if(g>>>0<=h>>>0){break _}rb(ta,r+(h<<2)|0,y,g-h|0);break _}q=g>>>0>>0?g:x;m=q-1|0;k=(g>>>0)/(q>>>0)|0;b=0;while(1){s=Ja(36);if(!s){break X}h=K[ta+4>>2];K[s>>2]=K[ta>>2];K[s+4>>2]=h;h=K[ta+12>>2];K[s+8>>2]=K[ta+8>>2];K[s+12>>2]=h;K[s+28>>2]=Q(b,k);K[s+24>>2]=r;K[s+20>>2]=y;K[s+16>>2]=i;h=(b|0)==(m|0);b=b+1|0;K[s+32>>2]=h?g:Q(k,b);h=sb(p);K[s>>2]=h;if(!h){g=0;Xa($);Ga(s);Ga(H);break W}lb($,11,s);if((b|0)!=(q|0)){continue}break}Xa($)}j=j+152|0;l=l-1|0;if(l){continue}break}}g=1;Ga(H);break W}g=1;m=K[A+28>>2];Wa=m+Q(M,152)|0;yb=Wa-152|0;if(K[yb>>2]==K[Wa-144>>2]){break W}zb=Wa-148|0;if(K[zb>>2]==K[Wa-140>>2]){break W}o=K[m+4>>2];s=K[m+12>>2];q=K[m>>2];k=K[m+8>>2];qa=K[A+68>>2];ba=K[A+64>>2];ha=K[A+60>>2];ga=K[A+56>>2];Qa=oc(A,M);if(!Qa){g=0;break W}$:{aa:{if((M|0)!=1){g=M-1|0;j=g&1;ba:{if((M|0)==2){h=m;break ba}i=g&-2;h=m;g=0;while(1){l=K[h+160>>2]-K[h+152>>2]|0;l=b>>>0>l>>>0?b:l;b=K[h+164>>2]-K[h+156>>2]|0;l=b>>>0>>0?l:b;b=K[h+312>>2]-K[h+304>>2]|0;l=b>>>0>>0?l:b;b=K[h+316>>2]-K[h+308>>2]|0;b=b>>>0>>0?l:b;h=h+304|0;g=g+2|0;if((i|0)!=(g|0)){continue}break}}if(j){g=K[h+160>>2]-K[h+152>>2]|0;g=b>>>0>g>>>0?b:g;b=K[h+164>>2]-K[h+156>>2]|0;b=b>>>0>>0?g:b}if(b>>>0>=268435456){break $}u=sb(b<<4);if(!u){break $}ca:{if(!M){break ca}r=s-o|0;V=k-q|0;Ra=u-4|0;Nb=u+44|0;Ob=u+40|0;Pb=u+36|0;Ab=u+28|0;_=u+24|0;$=u+20|0;hb=u-12|0;oa=u+12|0;na=u+8|0;ib=u-16|0;ab=u-8|0;ja=u+4|0;cb=1;da:while(1){g=K[m+156>>2];jb=(g|0)%2|0;b=K[m+152>>2];Sa=(b|0)%2|0;G=K[m+164>>2]-g|0;Da=G-r|0;ea=K[m+160>>2]-b|0;ya=ea-V|0;h=ga;g=h;i=ha;q=i;b=ba;wa=b;l=qa;F=l;j=K[A+20>>2];ea:{if((j|0)==(cb|0)){break ea}y=j-cb|0;q=0;g=0;if(h){g=y&31;if((y&63)>>>0>=32){o=-1<>>32-g}b=h+(b^-1)|0;g=o^-1;g=b>>>0>>0?g+1|0:g;l=y&31;if((y&63)>>>0>=32){g=g>>>l|0}else{g=((1<>>l}}if(ha){l=y&31;if((y&63)>>>0>=32){o=-1<>>32-l}b=ha+(b^-1)|0;l=o^-1;l=b>>>0>>0?l+1|0:l;i=y&31;if((y&63)>>>0>=32){q=l>>>i|0}else{q=((1<>>i}}l=0;b=0;if(ba){i=y&31;if((y&63)>>>0>=32){o=-1<>>32-i}b=ba+(b^-1)|0;i=o^-1;i=b>>>0>>0?i+1|0:i;h=y&31;if((y&63)>>>0>=32){b=i>>>h|0}else{b=((1<>>h}}if(qa){i=y&31;if((y&63)>>>0>=32){o=-1<>>32-i}l=qa+(l^-1)|0;i=o^-1;i=l>>>0>>0?i+1|0:i;h=y&31;if((y&63)>>>0>=32){l=i>>>h|0}else{l=((1<>>h}}wa=0;h=0;p=1<>>0>>0){h=y&31;if((y&63)>>>0>=32){o=-1<>>32-h}j=i^-1;i=j+(ga-p|0)|0;h=o^-1;h=i>>>0>>0?h+1|0:h;j=y&31;if((y&63)>>>0>=32){h=h>>>j|0}else{h=((1<>>j}}if(p>>>0>>0){j=y&31;if((y&63)>>>0>=32){o=-1<>>32-j}k=i^-1;i=k+(ba-p|0)|0;j=o^-1;j=i>>>0>>0?j+1|0:j;k=y&31;if((y&63)>>>0>=32){wa=j>>>k|0}else{wa=((1<>>k}}F=0;i=0;if(p>>>0>>0){j=y&31;if((y&63)>>>0>=32){o=-1<>>32-j}k=i^-1;i=k+(ha-p|0)|0;j=o^-1;j=i>>>0>>0?j+1|0:j;k=y&31;if((y&63)>>>0>=32){i=j>>>k|0}else{i=((1<>>k}}if(p>>>0>=qa>>>0){break ea}k=y&31;if((y&63)>>>0>=32){o=-1<>>32-k}s=j^-1;j=s+(qa-p|0)|0;k=o^-1;k=j>>>0>>0?k+1|0:k;s=y&31;if((y&63)>>>0>=32){F=k>>>s|0}else{F=((1<>>s}}s=K[m+180>>2];j=wa-s|0;k=j>>>0<=wa>>>0?j:0;j=k+2|0;j=j>>>0>>0?-1:j;Ka=j>>>0>>0?j:ya;k=K[m+216>>2];j=b-k|0;j=b>>>0>=j>>>0?j:0;b=j+2|0;b=b>>>0>>0?-1:b;Oa=b>>>0>>0?b:V;j=(Sa?Ka:Oa)<<1;b=(Sa?Oa:Ka)<<1|1;eb=b>>>0>>0?j:b;T=eb>>>0>>0;b=h-s|0;h=b>>>0<=h>>>0?b:0;b=h-2|0;o=b>>>0<=h>>>0?b:0;b=g-k|0;g=b>>>0<=g>>>0?b:0;b=g-2|0;p=b>>>0<=g>>>0?b:0;aa=(Sa?o:p)<<1;ka=(Sa?p:o)<<1|1;y=aa>>>0>>0;s=K[m+184>>2];b=q-s|0;g=b>>>0<=q>>>0?b:0;b=g-2|0;k=b>>>0<=g>>>0?b:0;fa=k;h=K[m+220>>2];b=i-h|0;g=b>>>0<=i>>>0?b:0;b=g-2|0;j=b>>>0<=g>>>0?b:0;H=j;b=l-s|0;g=b>>>0<=l>>>0?b:0;b=g+2|0;b=b>>>0>>0?-1:b;da=b>>>0>>0?b:r;x=da;b=F-h|0;g=b>>>0<=F>>>0?b:0;b=g+2|0;b=b>>>0>>0?-1:b;Ta=b>>>0>>0?b:Da;q=Ta;if(jb){H=k;x=q;fa=j;q=da}mb=T?eb:ea;s=y?aa:ka;Bb=r+Ta|0;Cb=j+r|0;if(G){bb=(p<<3)+u|0;l=ya<<3;sa=l+Ra|0;g=(p|0)<(ya|0);za=g?bb+4|0:sa;aa=(V|0)>(Ka|0)?Ka:V-1|0;F=0;Ba=(V|0)>1|(ya|0)>0;b=Sa<<2;w=(ja-b|0)+(o<<3)|0;P=b+bb|0;ka=(ya|0)>(Oa|0)?Oa:ya;y=p+1|0;ia=V+Ka|0;ma=o+V|0;ca=(s<<2)+u|0;b=V<<3;ob=b+ab|0;fb=b+Ra|0;U=l+ab|0;z=!V&(ya|0)==1;b=mb<<2;pa=b+u|0;va=b+Ra|0;xa=((g?p:ya)<<3)+Ra|0;while(1){fa:{if(!(F>>>0>>0&k>>>0<=F>>>0|F>>>0>>0&F>>>0>=Cb>>>0)){T=F+1|0;break fa}if(ea>>>0>eb>>>0){K[va>>2]=0;K[pa>>2]=0}T=F+1|0;Ua(Qa,p,F,Oa,T,P,2,0);Ua(Qa,ma,F,ia,T,w,2,0);ga:{ha:{ia:{if(!Sa){if(!Ba){break ga}if((p|0)>=(Oa|0)){break ha}ja:{ka:{if((p|0)>0){b=K[xa>>2];break ka}b=K[ja>>2];g=b;if((p|0)<0){break ja}}g=b;b=K[za>>2]}K[bb>>2]=K[bb>>2]-((b+g|0)+2>>2);l=p;b=y;g=b;if((b|0)>=(ka|0)){break ia}while(1){b=(g<<3)+u|0;K[b>>2]=K[b>>2]-((K[((l<<3)+u|0)+4>>2]+K[b+4>>2]|0)+2>>2);l=g;g=g+1|0;if((ka|0)!=(g|0)){continue}break}b=ka;break ia}la:{if(!z){b=p;if((Oa|0)<=(b|0)){break la}while(1){g=(b<<3)+u|0;i=g;l=K[g+4>>2];ma:{na:{if((b|0)>=0){wa=K[((b|0)<(ya|0)?g:U)>>2];g=b+1|0;break na}wa=K[u>>2];g=0;b=b+1|0;h=u;if(b){break ma}}if((g|0)>=(ya|0)){b=g;h=U;break ma}b=g;h=(b<<3)+u|0}g=h;K[i+4>>2]=l-((K[g>>2]+wa|0)+2>>2);if((b|0)<(Oa|0)){continue}break}break la}K[u>>2]=K[u>>2]/2;break ga}b=o;if((Ka|0)<=(b|0)){break ga}while(1){g=b<<3;h=g+u|0;l=K[h>>2];oa:{if((b|0)<0){i=K[ja>>2];wa=ja;break oa}i=K[((b|0)<(V|0)?((b<<3)+u|0)+4|0:fb)>>2];wa=ja;if(!b){break oa}wa=fb;if((b|0)>(V|0)){break oa}wa=g+Ra|0}g=wa;K[h>>2]=l+(K[g>>2]+i>>1);b=b+1|0;if((Ka|0)!=(b|0)){continue}break}break ga}if((b|0)>=(Oa|0)){break ha}while(1){g=(b<<3)+u|0;h=g;i=K[g>>2];pa:{qa:{if((b|0)>0){l=K[(((b|0)<(ya|0)?b:ya)<<3)+Ra>>2];break qa}l=K[ja>>2];g=ja;if((b|0)<0){break pa}}g=sa;if((b|0)>=(ya|0)){break pa}g=((b<<3)+u|0)+4|0}K[h>>2]=i-((K[g>>2]+l|0)+2>>2);b=b+1|0;if((Oa|0)!=(b|0)){continue}break}}if((o|0)>=(Ka|0)){break ga}g=o;b=g;if((aa|0)>(b|0)){while(1){g=(b<<3)+u|0;b=b+1|0;K[g+4>>2]=K[g+4>>2]+(K[(b<<3)+u>>2]+K[g>>2]>>1);if((b|0)!=(aa|0)){continue}break}g=aa}if((g|0)>=(Ka|0)){break ga}while(1){b=g;ra:{sa:{if((b|0)>=0){h=K[((b|0)<(V|0)?(b<<3)+u|0:ob)>>2];l=b+1|0;break sa}h=K[u>>2];l=0;g=b+1|0;i=u;if(g){break ra}}if((l|0)>=(V|0)){g=l;i=ob;break ra}g=l;i=(g<<3)+u|0}l=i;b=(b<<3)+u|0;K[b+4>>2]=K[b+4>>2]+(K[l>>2]+h>>1);if((g|0)<(Ka|0)){continue}break}}if(!db(Qa,s,F,mb,T,ca,1,0)){break aa}}F=T;if((G|0)!=(T|0)){continue}break}}m=m+152|0;g=x<<1;b=q<<1|1;b=b>>>0>>0?g:b;Qb=b>>>0>>0?b:G;q=k<<5;g=q|16;b=Da<<5;h=(k|0)<(Da|0);Rb=h?g+oa|0:b+Ra|0;Sb=h?g+na|0:b+ab|0;Tb=h?g+ja|0:b+hb|0;Ub=h?g+u|0:b+ib|0;x=(r|0)>(Ta|0)?Ta:r-1|0;b=(Da|0)>0;Vb=b|(r|0)>1;gb=q+u|0;Wb=gb+(jb<<4)|0;i=r<<3;ub=i-8|0;g=((r|0)<=0?ub:0)<<2;Xb=g+oa|0;Yb=g+na|0;Zb=g+ja|0;_b=g+u|0;l=Da<<3;vb=l-8|0;b=(b?0:vb)<<2;$b=b+oa|0;ya=b+na|0;Ka=b+ja|0;Oa=b+u|0;wa=((4-(jb<<2)<<2)+u|0)+(j<<5)|0;y=(da|0)<(Da|0)?da:Da;o=k+1|0;g=fa<<1;b=H<<1|1;Sa=b>>>0>g>>>0?g:b;bb=(Sa<<4)+u|0;Db=q+oa|0;pb=q+na|0;qb=q+ja|0;b=r<<5;eb=b+oa|0;Eb=i-1|0;ob=b+na|0;Ib=i-2|0;fb=b+ja|0;Jb=i-3|0;U=b+u|0;Kb=i-4|0;sa=l-5|0;za=l-6|0;Ba=l-7|0;w=!r&(Da|0)==1;b=ub<<2;P=b+oa|0;ia=b+na|0;ma=b+ja|0;ca=b+u|0;z=l-4|0;b=z<<2;pa=b+oa|0;va=b+na|0;xa=b+ja|0;F=b+u|0;b=(h?k:Da)<<5;V=b+Ra|0;q=b+ab|0;h=b+hb|0;fa=b+ib|0;b=vb<<2;T=b+oa|0;aa=b+na|0;ka=b+ja|0;H=b+u|0;while(1){ta:{ua:{va:{wa:{p=s;if(p>>>0>>0){b=mb-p|0;s=p+(b>>>0>=4?4:b)|0;Ua(Qa,p,k,s,da,Wb,1,8);Ua(Qa,p,Cb,s,Bb,wa,1,8);if(!jb){if(!Vb){break ta}if((k|0)>=(da|0)){break ua}xa:{if((k|0)>0){b=K[fa>>2];i=q;l=h;g=V;break xa}b=K[u+16>>2];if((k|0)<0){break wa}i=_;l=$;g=Ab}K[gb>>2]=K[gb>>2]-((K[Ub>>2]+b|0)+2>>2);K[qb>>2]=K[qb>>2]-((K[l>>2]+K[Tb>>2]|0)+2>>2);K[pb>>2]=K[pb>>2]-((K[i>>2]+K[Sb>>2]|0)+2>>2);b=K[Rb>>2];g=K[g>>2];break va}if(w){K[u>>2]=K[u>>2]/2;K[u+4>>2]=K[u+4>>2]/2;K[na>>2]=K[na>>2]/2;K[oa>>2]=K[oa>>2]/2;break ta}b=k;if((da|0)>(b|0)){while(1){i=b<<3;ya:{za:{if((b|0)<0){if((b|0)==-1){break za}g=(i<<2)+u|0;K[g+16>>2]=K[g+16>>2]-((K[u>>2]<<1)+2>>2);K[g+20>>2]=K[g+20>>2]-((K[u+4>>2]<<1)+2>>2);K[g+24>>2]=K[g+24>>2]-((K[na>>2]<<1)+2>>2);K[g+28>>2]=K[g+28>>2]-((K[oa>>2]<<1)+2>>2);break ya}Y=(i<<2)+u|0;l=K[Y+16>>2];g=b+1|0;if((g|0)>=(Da|0)){g=(b|0)<(Da|0);K[Y+16>>2]=l-((K[((g?i:vb)<<2)+u>>2]+K[H>>2]|0)+2>>2);K[Y+20>>2]=K[Y+20>>2]-((K[((g?i|1:Ba)<<2)+u>>2]+K[ka>>2]|0)+2>>2);K[Y+24>>2]=K[Y+24>>2]-((K[((g?i|2:za)<<2)+u>>2]+K[aa>>2]|0)+2>>2);K[Y+28>>2]=K[Y+28>>2]-((K[((g?i|3:sa)<<2)+u>>2]+K[T>>2]|0)+2>>2);break ya}g=(g<<5)+u|0;K[Y+16>>2]=l-((K[Y>>2]+K[g>>2]|0)+2>>2);K[Y+20>>2]=K[Y+20>>2]-((K[Y+4>>2]+K[g+4>>2]|0)+2>>2);K[Y+24>>2]=K[Y+24>>2]-((K[Y+8>>2]+K[g+8>>2]|0)+2>>2);K[Y+28>>2]=K[Y+28>>2]-((K[Y+12>>2]+K[g+12>>2]|0)+2>>2);break ya}K[ib>>2]=K[ib>>2]-((K[u>>2]+K[Oa>>2]|0)+2>>2);K[hb>>2]=K[hb>>2]-((K[u+4>>2]+K[Ka>>2]|0)+2>>2);K[ab>>2]=K[ab>>2]-((K[na>>2]+K[ya>>2]|0)+2>>2);K[Ra>>2]=K[Ra>>2]-((K[oa>>2]+K[$b>>2]|0)+2>>2)}b=b+1|0;if((da|0)!=(b|0)){continue}break}}b=j;if((Ta|0)<=(b|0)){break ta}while(1){Y=b<<3;Aa:{if((b|0)<0){g=(Y<<2)+u|0;K[g>>2]=K[g>>2]+(K[u+16>>2]<<1>>1);K[g+4>>2]=K[g+4>>2]+(K[u+20>>2]<<1>>1);K[g+8>>2]=K[g+8>>2]+(K[u+24>>2]<<1>>1);K[g+12>>2]=K[g+12>>2]+(K[u+28>>2]<<1>>1);break Aa}if(b){i=Y<<2;Aa=i+u|0;l=(b|0)>(r|0);g=(b|0)<(r|0);K[Aa>>2]=K[Aa>>2]+(K[(l?U:Aa)-16>>2]+K[((g?Y|4:Kb)<<2)+u>>2]>>1);K[Aa+4>>2]=K[Aa+4>>2]+(K[(l?fb:i+ja|0)-16>>2]+K[((g?Y|5:Jb)<<2)+u>>2]>>1);K[Aa+8>>2]=K[Aa+8>>2]+(K[(l?ob:i+na|0)-16>>2]+K[((g?Y|6:Ib)<<2)+u>>2]>>1);K[Aa+12>>2]=K[Aa+12>>2]+(K[(l?eb:i+oa|0)-16>>2]+K[((g?Y|7:Eb)<<2)+u>>2]>>1);break Aa}g=(b|0)<(r|0);K[u>>2]=K[u>>2]+(K[u+16>>2]+K[((g?4:Kb)<<2)+u>>2]>>1);K[u+4>>2]=K[u+4>>2]+(K[u+20>>2]+K[((g?5:Jb)<<2)+u>>2]>>1);K[na>>2]=K[na>>2]+(K[u+24>>2]+K[((g?6:Ib)<<2)+u>>2]>>1);K[oa>>2]=K[oa>>2]+(K[u+28>>2]+K[((g?7:Eb)<<2)+u>>2]>>1)}b=b+1|0;if((Ta|0)!=(b|0)){continue}break}break ta}V=ea;r=G;cb=cb+1|0;if((M|0)!=(cb|0)){continue da}break ca}K[gb>>2]=K[gb>>2]-((b<<1)+2>>2);K[qb>>2]=K[qb>>2]-((K[$>>2]<<1)+2>>2);K[pb>>2]=K[pb>>2]-((K[_>>2]<<1)+2>>2);b=K[Ab>>2];g=b}K[Db>>2]=K[Db>>2]-((b+g|0)+2>>2);l=k;g=o;b=g;if((y|0)>(b|0)){while(1){i=(g<<5)+u|0;b=l<<5|16;K[i>>2]=K[i>>2]-((K[b+u>>2]+K[i+16>>2]|0)+2>>2);K[i+4>>2]=K[i+4>>2]-((K[b+ja>>2]+K[i+20>>2]|0)+2>>2);K[i+8>>2]=K[i+8>>2]-((K[b+na>>2]+K[i+24>>2]|0)+2>>2);K[i+12>>2]=K[i+12>>2]-((K[b+oa>>2]+K[i+28>>2]|0)+2>>2);l=g;g=g+1|0;if((y|0)!=(g|0)){continue}break}b=y}if((b|0)>=(da|0)){break ua}while(1){Pa=b<<3;Y=Pa|4;Aa=(b|0)<(Da|0);Ba:{if((b|0)<=0){i=K[u+16>>2];if((b|0)>=0){l=Pa<<2;g=l+u|0;Y=(Aa?Y:z)<<2;K[g>>2]=K[g>>2]-((i+K[Y+u>>2]|0)+2>>2);g=l+ja|0;K[g>>2]=K[g>>2]-((K[u+20>>2]+K[Y+ja>>2]|0)+2>>2);g=l+na|0;K[g>>2]=K[g>>2]-((K[u+24>>2]+K[Y+na>>2]|0)+2>>2);l=(K[u+28>>2]+K[Y+oa>>2]|0)+2|0;break Ba}l=Pa<<2;g=l+u|0;K[g>>2]=K[g>>2]-((i<<1)+2>>2);g=l+ja|0;K[g>>2]=K[g>>2]-((K[u+20>>2]<<1)+2>>2);g=l+na|0;K[g>>2]=K[g>>2]-((K[u+24>>2]<<1)+2>>2);l=(K[u+28>>2]<<1)+2|0;break Ba}Ca=((Aa?b:Da)<<3)-4<<2;i=K[Ca+u>>2];if(!Aa){l=Pa<<2;g=l+u|0;K[g>>2]=K[g>>2]-((i+K[F>>2]|0)+2>>2);g=l+ja|0;K[g>>2]=K[g>>2]-((K[ja+Ca>>2]+K[xa>>2]|0)+2>>2);g=l+na|0;K[g>>2]=K[g>>2]-((K[na+Ca>>2]+K[va>>2]|0)+2>>2);l=(K[oa+Ca>>2]+K[pa>>2]|0)+2|0;break Ba}l=Pa<<2;g=l+u|0;Y=Y<<2;K[g>>2]=K[g>>2]-((i+K[Y+u>>2]|0)+2>>2);g=l+ja|0;K[g>>2]=K[g>>2]-((K[ja+Ca>>2]+K[Y+ja>>2]|0)+2>>2);g=l+na|0;K[g>>2]=K[g>>2]-((K[na+Ca>>2]+K[Y+na>>2]|0)+2>>2);l=(K[oa+Ca>>2]+K[Y+oa>>2]|0)+2|0}g=(Pa<<2)+oa|0;K[g>>2]=K[g>>2]-(l>>2);b=b+1|0;if((da|0)!=(b|0)){continue}break}}if((j|0)>=(Ta|0)){break ta}i=j;b=i;if((x|0)>(b|0)){while(1){b=i<<5;g=b+u|0;K[g+16>>2]=K[g+16>>2]+(K[g+32>>2]+K[g>>2]>>1);K[g+20>>2]=K[g+20>>2]+(K[b+Pb>>2]+K[g+4>>2]>>1);K[g+24>>2]=K[g+24>>2]+(K[b+Ob>>2]+K[g+8>>2]>>1);K[g+28>>2]=K[g+28>>2]+(K[b+Nb>>2]+K[g+12>>2]>>1);i=i+1|0;if((x|0)!=(i|0)){continue}break}b=x}if((b|0)>=(Ta|0)){break ta}while(1){g=b<<3;Pa=g|4;i=(Pa<<2)+oa|0;Ca:{if((b|0)<0){Y=K[u>>2];if((b|0)!=-1){l=Pa<<2;g=l+u|0;K[g>>2]=Y+K[g>>2];g=l+ja|0;K[g>>2]=K[g>>2]+K[ja>>2];g=l+na|0;K[g>>2]=K[g>>2]+K[na>>2];g=K[oa>>2];break Ca}l=Pa<<2;g=l+u|0;K[g>>2]=K[g>>2]+(Y+K[_b>>2]>>1);g=l+ja|0;K[g>>2]=K[g>>2]+(K[Zb>>2]+K[ja>>2]>>1);g=l+na|0;K[g>>2]=K[g>>2]+(K[Yb>>2]+K[na>>2]>>1);g=K[Xb>>2]+K[oa>>2]>>1;break Ca}Ca=(((b|0)<(r|0)?g:ub)<<2)+u|0;Aa=K[Ca>>2];l=b+1|0;if((l|0)>=(r|0)){l=Pa<<2;g=l+u|0;K[g>>2]=K[g>>2]+(Aa+K[ca>>2]>>1);g=l+ja|0;K[g>>2]=K[g>>2]+(K[ma>>2]+K[Ca+4>>2]>>1);g=l+na|0;K[g>>2]=K[g>>2]+(K[ia>>2]+K[Ca+8>>2]>>1);g=K[P>>2]+K[Ca+12>>2]>>1;break Ca}Y=Pa<<2;g=Y+u|0;l=(l<<5)+u|0;K[g>>2]=K[g>>2]+(Aa+K[l>>2]>>1);g=Y+ja|0;K[g>>2]=K[g>>2]+(K[l+4>>2]+K[Ca+4>>2]>>1);g=Y+na|0;K[g>>2]=K[g>>2]+(K[l+8>>2]+K[Ca+8>>2]>>1);g=K[l+12>>2]+K[Ca+12>>2]>>1}K[i>>2]=g+K[i>>2];b=b+1|0;if((Ta|0)!=(b|0)){continue}break}}if(db(Qa,p,Sa,s,Qb,bb,1,4)){continue}break}break}break aa}Ga(u);g=1}h=K[Wa-16>>2];i=K[yb>>2];l=K[zb>>2];b=K[Wa-8>>2];Ua(Qa,h-i|0,K[Wa-12>>2]-l|0,b-i|0,K[Wa-4>>2]-l|0,K[A+52>>2],1,b-h|0);_a(Qa);break W}_a(Qa);Ga(u);g=0;break W}_a(Qa);g=0;break W}g=0;Xa($);Ga(H)}ra=ta+32|0;if(g){break V}break b}g=0;p=0;U=ra+-64|0;ra=U;Da:{Ea:{Fa:{if(K[C+64>>2]){h=K[A+28>>2];k=h+Q(K[A+24>>2],152)|0;q=K[k-152>>2];j=1;ia=K[C+44>>2];xa=K[ia+4>>2];if((i|0)==1){break Da}s=i-1|0;m=s&1;Ga:{if((i|0)==2){i=0;b=h;break Ga}j=s&-2;i=0;b=h;while(1){g=K[b+160>>2]-K[b+152>>2]|0;l=g>>>0>>0?i:g;g=K[b+164>>2]-K[b+156>>2]|0;l=g>>>0>>0?l:g;g=K[b+312>>2]-K[b+304>>2]|0;l=g>>>0>>0?l:g;g=K[b+316>>2]-K[b+308>>2]|0;i=g>>>0>>0?l:g;b=b+304|0;p=p+2|0;if((j|0)!=(p|0)){continue}break}}j=0;if(m){g=K[b+160>>2]-K[b+152>>2]|0;g=g>>>0>>0?i:g;b=K[b+164>>2]-K[b+156>>2]|0;i=b>>>0>>0?g:b}if(i>>>0>134217727){break Da}k=K[k-144>>2];m=K[h+4>>2];l=K[h+12>>2];g=K[h>>2];b=K[h+8>>2];qa=i<<5;i=Ma(qa);K[U+32>>2]=i;if(!i){break Da}K[U>>2]=i;if(!s){j=1;Ga(i);break Da}m=l-m|0;i=b-g|0;b=xa>>>1|0;ba=b>>>0<=2?2:b;w=k-q|0;ga=w<<5;G=Q(w,28);ea=Q(w,24);T=Q(w,20);aa=w<<4;ka=Q(w,12);_=w<<3;l=K[A+36>>2];while(1){K[U+8>>2]=m;b=i;K[U+40>>2]=b;pa=K[h+156>>2];va=K[h+164>>2];g=K[h+160>>2];j=K[h+152>>2];K[U+56>>2]=0;K[U+52>>2]=b;K[U+48>>2]=0;q=(j|0)%2|0;K[U+44>>2]=q;i=g-j|0;x=i-b|0;K[U+60>>2]=x;K[U+36>>2]=x;r=(xa|0)<2;m=va-pa|0;Ha:{if(!(!r&m>>>0>15)){j=0;g=l;if(m>>>0<8){break Ha}p=0;k=K[U+32>>2];while(1){b=U+32|0;Hb(b,g,w,8);Za(b);b=0;if(i){while(1){q=(b<<2)+g|0;j=k+(b<<5)|0;O[q>>2]=O[j>>2];O[q+(w<<2)>>2]=O[j+4>>2];O[q+_>>2]=O[j+8>>2];O[q+ka>>2]=O[j+12>>2];b=b+1|0;if((i|0)!=(b|0)){continue}break}b=0;while(1){q=(b<<2)+g|0;j=k+(b<<5)|0;O[q+aa>>2]=O[j+16>>2];O[q+T>>2]=O[j+20>>2];O[q+ea>>2]=O[j+24>>2];O[q+G>>2]=O[j+28>>2];b=b+1|0;if((i|0)!=(b|0)){continue}break}}g=g+ga|0;b=p+15|0;j=p+8|0;p=j;if(b>>>0>>0){continue}break}break Ha}g=m>>>3|0;y=g>>>0>>0?g:xa;o=(m>>>0)/(y>>>0)&-8;j=m&-8;p=0;g=l;while(1){H=Ja(48);if(!H){break Fa}k=Ma(qa);K[H>>2]=k;if(!k){Xa(ia);Ga(H);j=0;break Ea}K[H+40>>2]=g;K[H+36>>2]=w;K[H+32>>2]=i;K[H+28>>2]=x;K[H+24>>2]=0;K[H+20>>2]=b;K[H+16>>2]=0;K[H+12>>2]=q;K[H+8>>2]=b;K[H+4>>2]=x;k=j-Q(o,p)|0;p=p+1|0;k=(y|0)==(p|0)?k:o;K[H+44>>2]=k;lb(ia,12,H);g=(Q(k,w)<<2)+g|0;if((p|0)!=(y|0)){continue}break}Xa(ia)}Ia:{if(j>>>0>=m>>>0){break Ia}b=U+32|0;k=m-j|0;Hb(b,g,w,k);Za(b);if(!i){break Ia}p=k&-4;y=k&3;fa=0;o=K[U+32>>2];q=pa+(j-va|0)>>>0>4294967292;while(1){H=(fa<<2)+g|0;x=o+(fa<<5)|0;b=0;j=0;if(!q){while(1){O[H+(Q(b,w)<<2)>>2]=O[x+(b<<2)>>2];k=b|1;O[H+(Q(k,w)<<2)>>2]=O[x+(k<<2)>>2];k=b|2;O[H+(Q(k,w)<<2)>>2]=O[x+(k<<2)>>2];k=b|3;O[H+(Q(k,w)<<2)>>2]=O[x+(k<<2)>>2];b=b+4|0;j=j+4|0;if((p|0)!=(j|0)){continue}break}}j=0;if(y){while(1){O[H+(Q(b,w)<<2)>>2]=O[x+(b<<2)>>2];b=b+1|0;j=j+1|0;if((y|0)!=(j|0)){continue}break}}fa=fa+1|0;if((fa|0)!=(i|0)){continue}break}}P=K[U+8>>2];ma=m-P|0;K[U+4>>2]=ma;b=K[h+156>>2];K[U+16>>2]=0;K[U+20>>2]=P;K[U+24>>2]=0;K[U+28>>2]=ma;V=(b|0)%2|0;K[U+12>>2]=V;Ja:{if(!(!r&i>>>0>15)){g=l;if(i>>>0<8){break Ja}$=m&-2;H=m&1;x=ma&-2;y=ma&1;r=P&-2;o=P&1;ha=va+(pa^-1)|0;F=K[U>>2];b=V<<5;fa=F+b|0;da=(F-b|0)+32|0;q=Q(w,P)<<2;p=i;while(1){b=0;j=0;Ka:{La:{switch(P|0){default:while(1){z=(Q(b,w)<<2)+g|0;k=K[z+4>>2];M=fa+(b<<6)|0;K[M>>2]=K[z>>2];K[M+4>>2]=k;k=K[z+28>>2];K[M+24>>2]=K[z+24>>2];K[M+28>>2]=k;k=K[z+20>>2];K[M+16>>2]=K[z+16>>2];K[M+20>>2]=k;k=K[z+12>>2];K[M+8>>2]=K[z+8>>2];K[M+12>>2]=k;k=b|1;z=fa+(k<<6)|0;M=(Q(k,w)<<2)+g|0;k=K[M+28>>2];K[z+24>>2]=K[M+24>>2];K[z+28>>2]=k;k=K[M+20>>2];K[z+16>>2]=K[M+16>>2];K[z+20>>2]=k;k=K[M+12>>2];K[z+8>>2]=K[M+8>>2];K[z+12>>2]=k;k=K[M+4>>2];K[z>>2]=K[M>>2];K[z+4>>2]=k;b=b+2|0;j=j+2|0;if((r|0)!=(j|0)){continue}break};break;case 0:break Ka;case 1:break La}}if(!o){break Ka}k=fa+(b<<6)|0;j=(Q(b,w)<<2)+g|0;b=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=b;b=K[j+28>>2];K[k+24>>2]=K[j+24>>2];K[k+28>>2]=b;b=K[j+20>>2];K[k+16>>2]=K[j+16>>2];K[k+20>>2]=b;b=K[j+12>>2];K[k+8>>2]=K[j+8>>2];K[k+12>>2]=b}Ma:{if((m|0)==(P|0)){break Ma}M=g+q|0;b=0;j=0;if((P|0)!=(ha|0)){while(1){ca=M+(Q(b,w)<<2)|0;k=K[ca+4>>2];z=da+(b<<6)|0;K[z>>2]=K[ca>>2];K[z+4>>2]=k;k=K[ca+28>>2];K[z+24>>2]=K[ca+24>>2];K[z+28>>2]=k;k=K[ca+20>>2];K[z+16>>2]=K[ca+16>>2];K[z+20>>2]=k;k=K[ca+12>>2];K[z+8>>2]=K[ca+8>>2];K[z+12>>2]=k;k=b|1;ca=da+(k<<6)|0;z=M+(Q(k,w)<<2)|0;k=K[z+28>>2];K[ca+24>>2]=K[z+24>>2];K[ca+28>>2]=k;k=K[z+20>>2];K[ca+16>>2]=K[z+16>>2];K[ca+20>>2]=k;k=K[z+12>>2];K[ca+8>>2]=K[z+8>>2];K[ca+12>>2]=k;k=K[z+4>>2];K[ca>>2]=K[z>>2];K[ca+4>>2]=k;b=b+2|0;j=j+2|0;if((x|0)!=(j|0)){continue}break}}if(!y){break Ma}k=da+(b<<6)|0;j=M+(Q(b,w)<<2)|0;b=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=b;b=K[j+28>>2];K[k+24>>2]=K[j+24>>2];K[k+28>>2]=b;b=K[j+20>>2];K[k+16>>2]=K[j+16>>2];K[k+20>>2]=b;b=K[j+12>>2];K[k+8>>2]=K[j+8>>2];K[k+12>>2]=b}Za(U);Na:{if(!m){break Na}b=0;j=0;if(ha){while(1){z=F+(b<<5)|0;k=K[z+4>>2];M=(Q(b,w)<<2)+g|0;K[M>>2]=K[z>>2];K[M+4>>2]=k;k=K[z+28>>2];K[M+24>>2]=K[z+24>>2];K[M+28>>2]=k;k=K[z+20>>2];K[M+16>>2]=K[z+16>>2];K[M+20>>2]=k;k=K[z+12>>2];K[M+8>>2]=K[z+8>>2];K[M+12>>2]=k;k=b|1;z=(Q(k,w)<<2)+g|0;M=F+(k<<5)|0;k=K[M+28>>2];K[z+24>>2]=K[M+24>>2];K[z+28>>2]=k;k=K[M+20>>2];K[z+16>>2]=K[M+16>>2];K[z+20>>2]=k;k=K[M+12>>2];K[z+8>>2]=K[M+8>>2];K[z+12>>2]=k;k=K[M+4>>2];K[z>>2]=K[M>>2];K[z+4>>2]=k;b=b+2|0;j=j+2|0;if(($|0)!=(j|0)){continue}break}}if(!H){break Na}k=(Q(b,w)<<2)+g|0;j=F+(b<<5)|0;b=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=b;b=K[j+28>>2];K[k+24>>2]=K[j+24>>2];K[k+28>>2]=b;b=K[j+20>>2];K[k+16>>2]=K[j+16>>2];K[k+20>>2]=b;b=K[j+12>>2];K[k+8>>2]=K[j+8>>2];K[k+12>>2]=b}g=g+32|0;p=p-8|0;if(p>>>0>7){continue}break}break Ja}b=i>>>3|0;o=b>>>0>>0?b:ba;k=o>>>0<=1?1:o;q=(i>>>0)/(o>>>0)&-8;j=i&-8;p=0;g=l;while(1){r=Ja(48);if(!r){break Fa}b=Ma(qa);K[r>>2]=b;if(!b){Xa(ia);Ga(r);j=0;break Ea}K[r+40>>2]=g;K[r+36>>2]=w;K[r+32>>2]=m;K[r+28>>2]=ma;K[r+24>>2]=0;K[r+20>>2]=P;K[r+16>>2]=0;K[r+12>>2]=V;K[r+8>>2]=P;K[r+4>>2]=ma;b=j-Q(p,q)|0;p=p+1|0;b=(o|0)==(p|0)?b:q;K[r+44>>2]=b;lb(ia,13,r);g=(b<<2)+g|0;if((k|0)!=(p|0)){continue}break}Xa(ia)}r=i&7;Oa:{if(!r){break Oa}o=V<<5;x=K[U>>2];Pa:{if(!P){break Pa}p=o+x|0;y=r<<2;b=0;if((P|0)!=1){q=P&-2;j=0;while(1){k=!y;if(!k){E(p+(b<<6)|0,(Q(b,w)<<2)+g|0,y)}if(!k){k=b|1;E(p+(k<<6)|0,(Q(k,w)<<2)+g|0,y)}b=b+2|0;j=j+2|0;if((q|0)!=(j|0)){continue}break}}if(!(P&1)|!y){break Pa}E(p+(b<<6)|0,(Q(b,w)<<2)+g|0,y)}Qa:{if((m|0)==(P|0)){break Qa}p=(x-o|0)+32|0;o=(Q(w,P)<<2)+g|0;y=r<<2;b=0;if((P|0)!=(va+(pa^-1)|0)){q=ma&-2;j=0;while(1){k=!y;if(!k){E(p+(b<<6)|0,o+(Q(b,w)<<2)|0,y)}if(!k){k=b|1;E(p+(k<<6)|0,o+(Q(k,w)<<2)|0,y)}b=b+2|0;j=j+2|0;if((q|0)!=(j|0)){continue}break}}if(!(ma&1)|!y){break Qa}E(p+(b<<6)|0,o+(Q(b,w)<<2)|0,y)}Za(U);if(!m){break Oa}o=r<<2;b=0;if((va|0)!=(pa+1|0)){q=m&-2;j=0;while(1){k=!o;if(!k){E((Q(b,w)<<2)+g|0,x+(b<<5)|0,o)}if(!k){k=b|1;E((Q(k,w)<<2)+g|0,x+(k<<5)|0,o)}b=b+2|0;j=j+2|0;if((q|0)!=(j|0)){continue}break}}if(!(m&1)|!o){break Oa}E((Q(b,w)<<2)+g|0,x+(b<<5)|0,o)}h=h+152|0;s=s-1|0;if(s){continue}break}j=1;break Ea}j=1;l=K[A+28>>2];za=l+Q(i,152)|0;pa=za-152|0;if(K[pa>>2]==K[za-144>>2]){break Da}va=za-148|0;if(K[va>>2]==K[za-140>>2]){break Da}p=K[l+4>>2];o=K[l+12>>2];s=K[l>>2];q=K[l+8>>2];ka=K[A+68>>2];_=K[A+64>>2];$=K[A+60>>2];H=K[A+56>>2];sa=oc(A,i);if(!sa){j=0;break Da}if((i|0)==1){i=K[za-16>>2];l=K[pa>>2];g=K[va>>2];b=K[za-8>>2];Ua(sa,i-l|0,K[za-12>>2]-g|0,b-l|0,K[za-4>>2]-g|0,K[A+52>>2],1,b-i|0);_a(sa);break Da}b=i-1|0;k=b&1;Ra:{if((i|0)==2){j=0;b=l;break Ra}m=b&-2;j=0;b=l;while(1){h=K[b+160>>2]-K[b+152>>2]|0;j=h>>>0>>0?j:h;h=K[b+164>>2]-K[b+156>>2]|0;j=h>>>0>>0?j:h;h=K[b+312>>2]-K[b+304>>2]|0;j=h>>>0>>0?j:h;h=K[b+316>>2]-K[b+308>>2]|0;j=h>>>0>>0?j:h;b=b+304|0;g=g+2|0;if((m|0)!=(g|0)){continue}break}}if(k){g=K[b+160>>2]-K[b+152>>2]|0;g=g>>>0>>0?j:g;b=K[b+164>>2]-K[b+156>>2]|0;j=b>>>0>>0?g:b}Sa:{if(j>>>0>=134217728){break Sa}Ba=Ma(j<<5);K[U+32>>2]=Ba;if(!Ba){break Sa}K[U>>2]=Ba;Ta:{if(i){m=o-p|0;b=q-s|0;xa=Ba+32|0;y=i;F=K[A+20>>2];w=1;ma=0;while(1){K[U+8>>2]=m;K[U+40>>2]=b;h=K[l+164>>2];i=K[l+160>>2];j=K[l+156>>2];g=K[l+152>>2];qa=(g|0)%2|0;K[U+44>>2]=qa;V=(j|0)%2|0;K[U+12>>2]=V;x=i-g|0;T=x-b|0;K[U+36>>2]=T;aa=h-j|0;ba=aa-m|0;K[U+4>>2]=ba;s=H;g=s;h=$;i=h;j=_;fa=j;p=ka;r=p;Ua:{if(!ma&(w|0)==(F|0)){break Ua}G=F-w|0;i=0;g=0;if(s){h=G&31;if((G&63)>>>0>=32){o=-1<>>32-h}g=s+(g^-1)|0;h=o^-1;h=g>>>0>>0?h+1|0:h;j=G&31;if((G&63)>>>0>=32){g=h>>>j|0}else{g=((1<>>j}}if($){h=G&31;if((G&63)>>>0>=32){o=-1<>>32-h}i=$+(i^-1)|0;h=o^-1;h=i>>>0<$>>>0?h+1|0:h;j=G&31;if((G&63)>>>0>=32){i=h>>>j|0}else{i=((1<>>j}}p=0;j=0;if(_){j=G&31;if((G&63)>>>0>=32){o=-1<>>32-j}h=_+(h^-1)|0;j=o^-1;j=h>>>0<_>>>0?j+1|0:j;k=G&31;if((G&63)>>>0>=32){j=j>>>k|0}else{j=((1<>>k}}if(ka){k=G&31;if((G&63)>>>0>=32){o=-1<>>32-k}h=ka+(h^-1)|0;k=o^-1;k=h>>>0>>0?k+1|0:k;q=G&31;if((G&63)>>>0>=32){p=k>>>q|0}else{p=((1<>>q}}fa=0;s=0;ea=1<>>0>>0){k=G&31;if((G&63)>>>0>=32){o=-1<>>32-k}q=h^-1;h=q+(H-ea|0)|0;k=o^-1;k=h>>>0>>0?k+1|0:k;q=G&31;if((G&63)>>>0>=32){s=k>>>q|0}else{s=((1<>>q}}if(_>>>0>ea>>>0){k=G&31;if((G&63)>>>0>=32){o=-1<>>32-k}q=h^-1;h=q+(_-ea|0)|0;k=o^-1;k=h>>>0>>0?k+1|0:k;q=G&31;if((G&63)>>>0>=32){fa=k>>>q|0}else{fa=((1<>>q}}r=0;h=0;if($>>>0>ea>>>0){k=G&31;if((G&63)>>>0>=32){o=-1<>>32-k}q=h^-1;h=q+($-ea|0)|0;k=o^-1;k=h>>>0>>0?k+1|0:k;q=G&31;if((G&63)>>>0>=32){h=k>>>q|0}else{h=((1<>>q}}if(ea>>>0>=ka>>>0){break Ua}q=G&31;if((G&63)>>>0>=32){o=-1<>>32-q}r=k^-1;k=r+(ka-ea|0)|0;q=o^-1;q=k>>>0>>0?q+1|0:q;o=G&31;if((G&63)>>>0>=32){r=q>>>o|0}else{r=((1<>>o}}o=K[l+180>>2];k=fa-o|0;q=k>>>0<=fa>>>0?k:0;k=q+4|0;k=k>>>0>>0?-1:k;M=k>>>0>>0?k:T;q=K[l+216>>2];k=j-q|0;k=j>>>0>=k>>>0?k:0;j=k+4|0;j=j>>>0>>0?-1:j;z=b>>>0>j>>>0?j:b;k=(qa?M:z)<<1;j=(qa?z:M)<<1|1;ha=j>>>0>>0?k:j;ga=ha>>>0>>0;j=s-o|0;k=j>>>0<=s>>>0?j:0;j=k-4|0;da=j>>>0<=k>>>0?j:0;j=g-q|0;j=g>>>0>=j>>>0?j:0;g=j-4|0;ca=g>>>0<=j>>>0?g:0;G=(qa?da:ca)<<1;ea=(qa?ca:da)<<1|1;T=G>>>0>>0;q=K[l+184>>2];g=i-q|0;i=g>>>0<=i>>>0?g:0;g=i-4|0;P=g>>>0<=i>>>0?g:0;j=P;o=K[l+220>>2];g=h-o|0;i=g>>>0<=h>>>0?g:0;g=i-4|0;k=g>>>0<=i>>>0?g:0;s=k;g=p-q|0;i=g>>>0<=p>>>0?g:0;g=i+4|0;g=g>>>0>>0?-1:g;h=g>>>0>>0?g:m;q=h;g=r-o|0;i=g>>>0<=r>>>0?g:0;g=i+4|0;g=g>>>0>>0?-1:g;g=g>>>0>>0?g:ba;r=g;if(V){s=j;q=g;r=h;j=k}ia=ga?ha:x;fa=T?G:ea;K[U+60>>2]=M;K[U+56>>2]=da;K[U+52>>2]=z;K[U+48>>2]=ca;Va:{if(aa>>>0<8){b=7;i=0;break Va}i=qa<<5;qa=(xa-i|0)+(da<<6)|0;ba=(i+Ba|0)+(ca<<6)|0;ha=b+M|0;ga=b+da|0;G=g+m|0;ea=k+m|0;T=Ba+(fa<<5)|0;i=0;while(1){b=i|7;Wa:{if(!(h>>>0>i>>>0&b>>>0>=P>>>0|i>>>0>>0&b>>>0>=ea>>>0)){i=i+8|0;break Wa}b=aa-i|0;p=b>>>0>=8?8:b;b=0;while(1){da=b+i|0;M=da+1|0;o=b<<2;Ua(sa,ca,da,z,M,o+ba|0,16,0);Ua(sa,ga,da,ha,M,o+qa|0,16,0);b=b+1|0;if((p|0)!=(b|0)){continue}break}Za(U+32|0);b=i;i=i+8|0;if(!db(sa,fa,b,ia,i,T,8,1)){break Ta}}b=i|7;if(aa>>>0>b>>>0){continue}break}}if(!(!(h>>>0>i>>>0&b>>>0>=P>>>0)&(g+m>>>0<=i>>>0|k+m>>>0>b>>>0)|i>>>0>=aa>>>0)){ha=U+32|0;ga=0;T=aa-i|0;if(T){while(1){G=i+ga|0;ea=G+1|0;b=K[ha+16>>2];p=ga<<2;Ua(sa,b,G,K[ha+20>>2],ea,p+((K[ha>>2]+(K[ha+12>>2]<<5)|0)+(b<<6)|0)|0,16,0);o=K[ha+24>>2];b=K[ha+8>>2];Ua(sa,o+b|0,G,b+K[ha+28>>2]|0,ea,(p+((K[ha>>2]-(K[ha+12>>2]<<5)|0)+(o<<6)|0)|0)+32|0,16,0);ga=ga+1|0;if((T|0)!=(ga|0)){continue}break}}Za(ha);if(!db(sa,fa,i,ia,aa,Ba+(fa<<5)|0,8,1)){break Ta}}K[U+28>>2]=g;K[U+24>>2]=k;K[U+20>>2]=h;K[U+16>>2]=P;if(fa>>>0>>0){i=q<<1;b=r<<1|1;b=b>>>0>>0?i:b;r=b>>>0>>0?b:aa;b=V<<5;p=(xa-b|0)+(k<<6)|0;o=(b+Ba|0)+(P<<6)|0;q=g+m|0;m=k+m|0;g=j<<1;b=s<<1|1;i=b>>>0>g>>>0?g:b;g=Ba+(i<<5)|0;while(1){b=ia-fa|0;b=(b>>>0>=8?8:b)+fa|0;Ua(sa,fa,P,b,h,o,1,16);Ua(sa,fa,m,b,q,p,1,16);Za(U);if(!db(sa,fa,i,b,r,g,1,8)){break Ta}fa=fa+8|0;if(ia>>>0>fa>>>0){continue}break}}l=l+152|0;b=x;m=aa;w=w+1|0;ma=w?ma:ma+1|0;if(ma|(w|0)!=(y|0)){continue}break}}j=1;i=K[za-16>>2];l=K[pa>>2];g=K[va>>2];b=K[za-8>>2];Ua(sa,i-l|0,K[za-12>>2]-g|0,b-l|0,K[za-4>>2]-g|0,K[A+52>>2],1,b-i|0);_a(sa);Ga(Ba);break Da}_a(sa);Ga(Ba);j=0;break Da}_a(sa);j=0;break Da}Xa(ia);j=0}Ga(K[U+32>>2])}ra=U- -64|0;if(j){break V}break b}t=t+1080|0;D=D+52|0;A=A+76|0;n=n+1|0;if(n>>>0>2]){continue}break}v=K[K[C+20>>2]>>2];t=K[C+32>>2]}i=K[t+16>>2];Xa:{if(K[C+68>>2]|!i){break Xa}D=K[v+20>>2];j=K[D+28>>2];Ya:{Za:{h=K[C+64>>2];if(h){n=K[v+16>>2];if(n>>>0<3){break Ya}l=K[D+24>>2];if(!((l|0)==K[D+100>>2]&(l|0)==K[D+176>>2])){Fa(f,1,10052,0);break b}g=K[K[C+24>>2]+24>>2];b=K[g+36>>2];_a:{if((b|0)!=K[g+88>>2]|(b|0)!=K[g+140>>2]){break _a}g=Q(l,152);b=g+j|0;b=Q(K[b-140>>2]-K[b-148>>2]|0,K[b-144>>2]-K[b-152>>2]|0);l=g+K[D+104>>2]|0;if((b|0)!=(Q(K[l-140>>2]-K[l-148>>2]|0,K[l-144>>2]-K[l-152>>2]|0)|0)){break _a}g=g+K[D+180>>2]|0;if((Q(K[g-140>>2]-K[g-148>>2]|0,K[g-144>>2]-K[g-152>>2]|0)|0)==(b|0)){break Za}}Fa(f,1,10052,0);break b}n=K[v+16>>2];if(n>>>0<3){break Ya}b=K[K[C+24>>2]+24>>2];g=K[b+36>>2];$a:{if((g|0)!=K[b+88>>2]){break $a}l=K[b+140>>2];if((l|0)!=(g|0)){break $a}g=Q(g,152);b=j+g|0;b=Q(K[b+148>>2]-K[b+140>>2]|0,K[b+144>>2]-K[b+136>>2]|0);g=g+K[D+104>>2]|0;if((b|0)!=(Q(K[g+148>>2]-K[g+140>>2]|0,K[g+144>>2]-K[g+136>>2]|0)|0)){break $a}g=K[D+180>>2]+Q(l,152)|0;if((Q(K[g+148>>2]-K[g+140>>2]|0,K[g+144>>2]-K[g+136>>2]|0)|0)==(b|0)){break Za}}Fa(f,1,10052,0);break b}if((i|0)==2){if(!K[t+5608>>2]){break Xa}r=Ja(n<<2);if(!r){break b}y=K[v+16>>2];ab:{if(!y){break ab}bb:{cb:{if(K[C+64>>2]){i=y&3;g=0;if(y>>>0>=4){break cb}A=0;break bb}i=y&3;g=0;db:{if(y>>>0<4){A=0;break db}l=y&-4;A=0;h=0;while(1){j=r+(A<<2)|0;K[j>>2]=K[D+52>>2];K[j+4>>2]=K[D+128>>2];K[j+8>>2]=K[D+204>>2];K[j+12>>2]=K[D+280>>2];A=A+4|0;D=D+304|0;h=h+4|0;if((l|0)!=(h|0)){continue}break}}if(!i){break ab}while(1){K[r+(A<<2)>>2]=K[D+52>>2];A=A+1|0;D=D+76|0;g=g+1|0;if((i|0)!=(g|0)){continue}break}break ab}l=y&-4;A=0;h=0;while(1){j=r+(A<<2)|0;K[j>>2]=K[D+36>>2];K[j+4>>2]=K[D+112>>2];K[j+8>>2]=K[D+188>>2];K[j+12>>2]=K[D+264>>2];A=A+4|0;D=D+304|0;h=h+4|0;if((l|0)!=(h|0)){continue}break}}if(!i){break ab}while(1){K[r+(A<<2)>>2]=K[D+36>>2];A=A+1|0;D=D+76|0;g=g+1|0;if((i|0)!=(g|0)){continue}break}}m=K[t+5608>>2];j=0;p=Ja(y<<3);g=0;eb:{if(!p){break eb}if(!(!b|!y)){n=p+(y<<2)|0;q=y&-4;t=y&3;k=y-1|0;while(1){v=0;l=0;if(k>>>0>=3){while(1){i=v<<2;O[i+p>>2]=O[K[i+r>>2]>>2];g=i|4;O[g+p>>2]=O[K[g+r>>2]>>2];g=i|8;O[g+p>>2]=O[K[g+r>>2]>>2];g=i|12;O[g+p>>2]=O[K[g+r>>2]>>2];v=v+4|0;l=l+4|0;if((q|0)!=(l|0)){continue}break}}g=0;if(t){while(1){l=v<<2;O[l+p>>2]=O[K[l+r>>2]>>2];v=v+1|0;g=g+1|0;if((t|0)!=(g|0)){continue}break}}h=0;v=m;while(1){l=h<<2;o=l+n|0;K[o>>2]=0;la=R(0);g=0;s=0;if(k>>>0>2){while(1){i=p+(g<<2)|0;la=R(R(O[v>>2]*O[i>>2])+la);O[o>>2]=la;la=R(R(O[v+4>>2]*O[i+4>>2])+la);O[o>>2]=la;la=R(R(O[v+8>>2]*O[i+8>>2])+la);O[o>>2]=la;la=R(R(O[v+12>>2]*O[i+12>>2])+la);O[o>>2]=la;g=g+4|0;v=v+16|0;s=s+4|0;if((q|0)!=(s|0)){continue}break}}i=0;if(t){while(1){la=R(R(O[v>>2]*O[p+(g<<2)>>2])+la);O[o>>2]=la;g=g+1|0;v=v+4|0;i=i+1|0;if((t|0)!=(i|0)){continue}break}}g=l+r|0;l=K[g>>2];K[g>>2]=l+4;O[l>>2]=la;h=h+1|0;if((y|0)!=(h|0)){continue}break}j=j+1|0;if((j|0)!=(b|0)){continue}break}}Ga(p);g=1}b=g;Ga(r);if(b){break Xa}break b}if(K[K[t+5584>>2]+20>>2]==1){if(h){sc(K[D+36>>2],K[D+112>>2],K[D+188>>2],b);break Xa}sc(K[D+52>>2],K[D+128>>2],K[D+204>>2],b);break Xa}if(h){rc(K[D+36>>2],K[D+112>>2],K[D+188>>2],b);break Xa}rc(K[D+52>>2],K[D+128>>2],K[D+204>>2],b);break Xa}K[Ea>>2]=n;Fa(f,1,10113,Ea)}r=K[K[C+20>>2]>>2];if(!K[r+16>>2]){Z=1;break b}p=K[C+68>>2];i=K[r+20>>2];b=K[K[C+32>>2]+5584>>2];m=K[K[C+24>>2]+24>>2];l=0;while(1){fb:{if(K[p+(l<<2)>>2]?0:p){break fb}g=K[i+28>>2];n=g+Q(K[m+36>>2],152)|0;gb:{if(!K[C+64>>2]){h=K[n+148>>2]-K[n+140>>2]|0;v=K[n+144>>2]-K[n+136>>2]|0;j=0;n=52;break gb}g=g+Q(K[i+24>>2],152)|0;v=K[n+8>>2]-K[n>>2]|0;j=K[g-144>>2]-(v+K[g-152>>2]|0)|0;h=K[n+12>>2]-K[n+4>>2]|0;n=36}g=K[m+24>>2];hb:{if(K[m+32>>2]){g=1<>2];if(K[b+20>>2]==1){s=v&-2;q=v&1;D=0;g=j<<2;while(1){n=0;if((v|0)!=1){while(1){j=K[b+1076>>2]+K[Z>>2]|0;K[Z>>2]=(j|0)<(k|0)?k:(j|0)<(A|0)?j:A;j=K[b+1076>>2]+K[Z+4>>2]|0;K[Z+4>>2]=(j|0)<(k|0)?k:(j|0)<(A|0)?j:A;Z=Z+8|0;n=n+2|0;if((s|0)!=(n|0)){continue}break}}if(q){j=K[b+1076>>2]+K[Z>>2]|0;K[Z>>2]=(j|0)<(k|0)?k:(j|0)<(A|0)?j:A;Z=Z+4|0}Z=Z+g|0;D=D+1|0;if((D|0)!=(h|0)){continue}break}break fb}o=k>>31;g=0;while(1){n=0;while(1){la=O[Z>>2];q=A;ib:{if(la>R(2147483648)){break ib}q=k;if(la>2];s=q;q=q>>31;tb=R(W(la));Lb=R(la-tb);if(LbR(.5)){break jb}la=R(tb*R(.5));wb=R(la-R(W(la)))==R(0)?tb:wb}la=wb}if(R(S(la))>31)|0;ea=q+1|0;x=q;q=s+t|0;s=t>>>0>q>>>0?ea:x;q=k>>>0>q>>>0&(o|0)>=(s|0)|(o|0)>(s|0)?k:q>>>0>>0&(s|0)<=0|(s|0)<0?q:A}K[Z>>2]=q;Z=Z+4|0;n=n+1|0;if((v|0)!=(n|0)){continue}break}Z=(j<<2)+Z|0;g=g+1|0;if((h|0)!=(g|0)){continue}break}}i=i+76|0;b=b+1080|0;m=m+52|0;Z=1;l=l+1|0;if(l>>>0>2]){continue}break}break b}Z=0;Fa(f,1,3335,0)}ra=Ea+16|0;if(!Z){nb(Ya);K[a+8>>2]=K[a+8>>2]|32768;Fa(f,1,11414,0);break a}kb:{if(!c){break kb}b=0;k=K[a+232>>2];g=fc(k,1);if(!((g|0)==-1|d>>>0>>0)){lb:{b=1;d=K[k+24>>2];if(!K[d+16>>2]){break lb}j=K[d+24>>2];m=K[K[K[k+20>>2]>>2]+20>>2];while(1){b=K[j+24>>2];l=b&7;g=b>>>3|0;b=K[m+28>>2];i=b+Q(K[j+36>>2],152)|0;mb:{if(K[k+64>>2]){b=b+Q(K[m+24>>2],152)|0;d=K[i+8>>2]-K[i>>2]|0;n=K[b-144>>2]-(d+K[b-152>>2]|0)|0;s=K[i+12>>2]-K[i+4>>2]|0;b=36;break mb}s=K[i+148>>2]-K[i+140>>2]|0;d=K[i+144>>2]-K[i+136>>2]|0;n=0;b=52}b=K[b+m>>2];nb:{ob:{pb:{qb:{g=g+((l|0)!=0)|0;switch(((g|0)==3?4:g)-1|0){case 0:break pb;case 1:break ob;case 3:break qb;default:break nb}}if(!s){break nb}h=d<<2;if((s|0)!=1){l=s&-2;t=0;while(1){g=!h;if(!g){E(c,b,h)}d=n<<2;i=d+(b+h|0)|0;b=c+h|0;if(!g){E(b,i,h)}c=b+h|0;b=d+(h+i|0)|0;t=t+2|0;if((l|0)!=(t|0)){continue}break}}if(!(s&1)){break nb}if(h){E(c,b,h)}c=c+h|0;break nb}g=!s|!d;if(K[j+32>>2]){if(g){break nb}l=d&-8;i=d&7;t=0;g=d-1>>>0<7;while(1){d=0;if(!g){while(1){I[c|0]=K[b>>2];I[c+1|0]=K[b+4>>2];I[c+2|0]=K[b+8>>2];I[c+3|0]=K[b+12>>2];I[c+4|0]=K[b+16>>2];I[c+5|0]=K[b+20>>2];I[c+6|0]=K[b+24>>2];I[c+7|0]=K[b+28>>2];c=c+8|0;b=b+32|0;d=d+8|0;if((l|0)!=(d|0)){continue}break}}d=0;if(i){while(1){I[c|0]=K[b>>2];c=c+1|0;b=b+4|0;d=d+1|0;if((i|0)!=(d|0)){continue}break}}b=(n<<2)+b|0;t=t+1|0;if((s|0)!=(t|0)){continue}break}break nb}if(g){break nb}i=d&-8;h=d&7;t=0;l=d-1>>>0<7;g=n<<2;while(1){d=0;if(!l){while(1){I[c|0]=K[b>>2];I[c+1|0]=K[b+4>>2];I[c+2|0]=K[b+8>>2];I[c+3|0]=K[b+12>>2];I[c+4|0]=K[b+16>>2];I[c+5|0]=K[b+20>>2];I[c+6|0]=K[b+24>>2];I[c+7|0]=K[b+28>>2];c=c+8|0;b=b+32|0;d=d+8|0;if((i|0)!=(d|0)){continue}break}}d=0;if(h){while(1){I[c|0]=K[b>>2];c=c+1|0;b=b+4|0;d=d+1|0;if((h|0)!=(d|0)){continue}break}}b=b+g|0;t=t+1|0;if((s|0)!=(t|0)){continue}break}break nb}g=!s|!d;if(K[j+32>>2]){if(g){break nb}l=d&-8;i=d&7;t=0;g=d-1>>>0<7;while(1){d=0;if(!g){while(1){J[c>>1]=K[b>>2];J[c+2>>1]=K[b+4>>2];J[c+4>>1]=K[b+8>>2];J[c+6>>1]=K[b+12>>2];J[c+8>>1]=K[b+16>>2];J[c+10>>1]=K[b+20>>2];J[c+12>>1]=K[b+24>>2];J[c+14>>1]=K[b+28>>2];c=c+16|0;b=b+32|0;d=d+8|0;if((l|0)!=(d|0)){continue}break}}d=0;if(i){while(1){J[c>>1]=K[b>>2];c=c+2|0;b=b+4|0;d=d+1|0;if((i|0)!=(d|0)){continue}break}}b=(n<<2)+b|0;t=t+1|0;if((s|0)!=(t|0)){continue}break}break nb}if(g){break nb}l=d&-8;i=d&7;t=0;g=d-1>>>0<7;while(1){d=0;if(!g){while(1){J[c>>1]=K[b>>2];J[c+2>>1]=K[b+4>>2];J[c+4>>1]=K[b+8>>2];J[c+6>>1]=K[b+12>>2];J[c+8>>1]=K[b+16>>2];J[c+10>>1]=K[b+20>>2];J[c+12>>1]=K[b+24>>2];J[c+14>>1]=K[b+28>>2];c=c+16|0;b=b+32|0;d=d+8|0;if((l|0)!=(d|0)){continue}break}}d=0;if(i){while(1){J[c>>1]=K[b>>2];c=c+2|0;b=b+4|0;d=d+1|0;if((i|0)!=(d|0)){continue}break}}b=(n<<2)+b|0;t=t+1|0;if((s|0)!=(t|0)){continue}break}}m=m+76|0;j=j+52|0;b=1;Mb=Mb+1|0;if(Mb>>>0>2]+16>>2]){continue}break}}}if(!b){break a}b=K[Ya+5596>>2];if(!b){break kb}Ga(b);K[Ya+5596>>2]=0;K[Ya+5600>>2]=0}I[a+92|0]=L[a+92|0]&254;K[a+8>>2]=K[a+8>>2]&-129;xb=1;c=Va(e);b=K[a+8>>2];if(!(c|ua)&(b|0)==64|(b|0)==256){break a}if((Na(e,$a+10|0,2,f)|0)!=2){Fa(f,K[a+208>>2]?1:2,2435,0);xb=!K[a+208>>2];break a}Ha($a+10|0,$a+12|0,2);b=K[$a+12>>2];if((b|0)==65424){break a}if((b|0)==65497){K[a+8>>2]=256;K[a+228>>2]=0;break a}if(!(Va(e)|ua)){K[a+8>>2]=64;Fa(f,2,8382,0);break a}xb=0;Fa(f,1,8269,0)}ra=$a+16|0;return xb|0}function ab(a,b,c,d,e,f,g,h,i,j,k){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,A=0,C=0,D=0,F=0,G=0,H=0,M=0,P=0,S=0,T=0,U=0,V=0,W=0,X=R(0),Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,sa=0,ta=0,wa=0,xa=R(0);s=ra-80|0;ra=s;K[s+40>>2]=65424;w=Q(K[a+132>>2],K[a+128>>2]);a:{b:{c:{l=K[a+8>>2];d:{if((l|0)!=8){j=0;if((l|0)!=256){break a}K[s+40>>2]=65497;break d}if(I[a+92|0]&1){break d}A=w&-2;D=w&1;P=s+77|0;S=s+76|0;T=s+72|0;n=65424;e:{f:{while(1){g:{h:{i:{j:{k:{l:{m:{n:{l=K[a+84>>2];if(!l){break n}p=l;l=K[a+80>>2];if(p>>>0<=l>>>0){break n}o=K[a+88>>2]+(l<<3)|0;n=K[o>>2];o=K[o+4>>2];K[a+80>>2]=l+1;if(!ib(j,n,o,k)){Fa(k,1,5403,0);j=0;break a}if((Na(j,K[a+16>>2],2,k)|0)!=2){Fa(k,1,2435,0);j=0;break a}Ha(K[a+16>>2],s+40|0,2);if(K[s+40>>2]==65424){break m}Fa(k,1,4036,0);j=0;break a}if((n|0)==65427){break l}}while(1){if(!(Va(j)|ua)){K[a+8>>2]=64;break l}if((Na(j,K[a+16>>2],2,k)|0)!=2){Fa(k,1,2435,0);j=0;break a}Ha(K[a+16>>2],s+36|0,2);if(N[s+36>>2]<=1){Fa(k,1,6011,0);j=0;break a}o:{if(K[s+40>>2]!=32896){break o}if(Va(j)|ua){break o}K[a+8>>2]=64;break l}r=K[a+8>>2];p:{if(!(r&16)){n=K[s+36>>2];break p}n=K[s+36>>2];l=K[a+24>>2];if(!l){break p}o=n+2|0;if(o>>>0>l>>>0){Fa(k,1,8333,0);j=0;break a}K[a+24>>2]=l-o}o=n-2|0;K[s+36>>2]=o;l=24864;t=K[s+40>>2];while(1){n=l;m=K[l>>2];if(m){l=l+12|0;if((m|0)!=(t|0)){continue}}break}if(!(r&K[n+4>>2])){Fa(k,1,5360,0);j=0;break a}q:{if(N[a+20>>2]>=o>>>0){l=K[a+16>>2];break q}l=Va(j);r=ua;if((r|0)<0){l=1}else{l=l>>>0>>0&(r|0)<=0}if(l){Fa(k,1,5760,0);j=0;break a}l=La(K[a+16>>2],K[s+36>>2]);if(!l){Ga(K[a+16>>2]);K[a+16>>2]=0;K[a+20>>2]=0;Fa(k,1,4936,0);j=0;break a}K[a+16>>2]=l;o=K[s+36>>2];K[a+20>>2]=o}l=Na(j,l,o,k);if((l|0)!=K[s+36>>2]){Fa(k,1,2435,0);j=0;break a}o=K[n+8>>2];if(!o){Fa(k,1,11688,0);j=0;break a}if(!(va[o|0](a,K[a+16>>2],l,k)|0)){K[s+32>>2]=K[s+40>>2];Fa(k,1,13922,s+32|0);j=0;break a}n=K[j+56>>2];t=K[s+36>>2];y=K[a+224>>2];o=K[y+40>>2];p=K[a+228>>2];v=Q(p,40);l=o+v|0;G=K[l+20>>2];r=G+1|0;q=K[l+28>>2];if(r>>>0>q>>>0){X=R(R(q>>>0)+R(100));if(X=R(0)){o=~~X>>>0}else{o=0}K[l+28>>2]=o;r=La(K[l+24>>2],Q(o,24));o=K[y+40>>2];l=v+o|0;if(!r){break k}K[l+24>>2]=r;G=K[l+20>>2];r=G+1|0}o=o+v|0;l=K[o+24>>2]+Q(G,24)|0;K[l+16>>2]=t+4;n=(n-t|0)-4|0;K[l+8>>2]=n;K[l+12>>2]=n>>31;J[l>>1]=m;K[o+20>>2]=r;r:{if((m|0)!=65424){break r}l=K[o+16>>2];s:{if(!l){break s}p=K[o+4>>2];o=K[o+12>>2];if(p>>>0<=o>>>0){break s}l=l+Q(o,24)|0;K[l>>2]=n;K[l+4>>2]=0}l=(K[j+56>>2]-K[s+36>>2]|0)-4|0;o=K[a+48>>2];n=K[a+52>>2];if((n|0)>0){p=1}else{p=l>>>0<=o>>>0&(n|0)>=0}if(p){break r}K[a+48>>2]=l;K[a+52>>2]=0}if(L[a+92|0]&4){if((vb(j,K[a+24>>2],k)|0)!=K[a+24>>2]|ua){Fa(k,1,2435,0);j=0;break a}K[s+40>>2]=65427;break l}if((Na(j,K[a+16>>2],2,k)|0)!=2){Fa(k,1,2435,0);j=0;break a}Ha(K[a+16>>2],s+40|0,2);if(K[s+40>>2]!=65427){continue}break}}if(!(!(Va(j)|ua)&K[a+8>>2]==64)){l=L[a+92|0];if(!(l&4)){l=Q(K[a+228>>2],5644);o=K[a+180>>2];t:{u:{if(K[a+56>>2]){m=Va(j);break u}m=K[a+24>>2];if(m>>>0<2){break t}}m=m-2|0;K[a+24>>2]=m}y=l+o|0;if(!m){break j}l=Va(j);o=ua;if((o|0)<0){l=1}else{l=l>>>0>>0&(o|0)<=0}if(l){if(K[a+208>>2]){Fa(k,1,5805,0);j=0;break a}Fa(k,2,5805,0)}l=K[a+24>>2];if(l>>>0>=4294967294){Fa(k,1,1443,0);j=0;break a}o=K[y+5596>>2];v:{if(o){n=K[y+5600>>2];if(n>>>0>-3-l>>>0){Fa(k,1,1174,0);j=0;break a}l=La(o,(l+n|0)+2|0);if(l){K[y+5596>>2]=l;break j}Ga(K[y+5596>>2]);K[y+5596>>2]=0;break v}l=Ja(l+2|0);K[y+5596>>2]=l;if(l){break j}}Fa(k,1,6139,0);j=0;break a}K[a+8>>2]=8;I[a+92|0]=l&250;break i}n=K[s+40>>2];break g}Ga(K[l+24>>2]);a=K[y+40>>2]+Q(p,40)|0;K[a+28>>2]=0;K[a+20>>2]=0;K[a+24>>2]=0;Fa(k,1,3826,0);j=0;break a}v=K[j+56>>2];n=v-2|0;t=K[j+60>>2];r=t-(v>>>0<2)|0;p=K[a+224>>2];H=K[p+40>>2];C=K[a+228>>2];q=Q(C,40);o=H+q|0;l=K[o+16>>2]+Q(K[o+12>>2],24)|0;K[l+8>>2]=n;K[l+12>>2]=r;r=l;l=t;u=K[a+24>>2];v=u+v|0;K[r+16>>2]=v;K[r+20>>2]=u>>>0>v>>>0?l+1|0:l;t=K[a+24>>2];G=K[o+20>>2];r=G+1|0;l=K[o+28>>2];w:{if(r>>>0<=l>>>0){l=K[o+24>>2];break w}X=R(R(l>>>0)+R(100));if(X=R(0)){l=~~X>>>0}else{l=0}K[o+28>>2]=l;l=La(K[o+24>>2],Q(l,24));H=K[p+40>>2];o=q+H|0;if(!l){break f}K[o+24>>2]=l;G=K[o+20>>2];r=G+1|0}l=Q(G,24)+l|0;K[l+16>>2]=t+2;K[l+8>>2]=n;K[l+12>>2]=n>>31;J[l>>1]=65427;K[(q+H|0)+20>>2]=r;x:{if(m){m=Na(j,K[y+5596>>2]+K[y+5600>>2]|0,K[a+24>>2],k);l=8;if((m|0)==K[a+24>>2]){break x}l=64;if((m|0)!=-1){break x}Fa(k,1,2435,0);j=0;break a}m=0;l=K[a+24>>2]?64:8}K[a+8>>2]=l;K[y+5600>>2]=K[y+5600>>2]+m;y:{if(I[a+92|0]&1){break y}l=K[a+44>>2];if(K[a+76>>2]|((l|0)<0|(l|0)!=K[a+228>>2])){break y}if(!Ib(j)){break y}o=K[a+228>>2];n=K[a+180>>2]+Q(o,5644)|0;l=K[n+5592>>2];o=K[K[a+224>>2]+40>>2]+Q(o,40)|0;if((l|0)!=K[o+4>>2]){break y}p=l;l=K[n+5588>>2]+1|0;if(p>>>0<=l>>>0){break y}z:{o=K[o+16>>2]+Q(l,24)|0;l=K[o>>2];o=K[o+4>>2];if((l|0)==K[j+56>>2]&(o|0)==K[j+60>>2]){break z}if(ib(j,l,o,k)){break z}Fa(k,1,5403,0);j=0;break a}if((Na(j,K[a+16>>2],2,k)|0)!=2){Fa(k,1,2435,0);j=0;break a}Ha(K[a+16>>2],s+40|0,2);if(K[s+40>>2]==65424){break h}Fa(k,1,4036,0);j=0;break a}l=L[a+92|0];if((l&9)!=1){break i}I[a+92|0]=l|8;r=K[a+228>>2];if(K[(K[a+180>>2]+Q(r,5644)|0)+5592>>2]==1){break i}if(!Ib(j)){break i}n=K[j+60>>2];t=n;o=K[j+56>>2];if((n&o)==-1){break i}A:{while(1){l=1;n=s+70|0;if((Na(j,n,2,k)|0)!=2){break A}Ha(n,s- -64|0,2);if(K[s+64>>2]!=65424){break A}m=2435;if((Na(j,n,2,k)|0)!=2){break c}Ha(n,s+60|0,2);if(K[s+60>>2]!=10){m=6011;break c}K[s+60>>2]=8;n=Na(j,s+70|0,8,k);if((n|0)!=K[s+60>>2]){break c}if((n|0)!=8){m=4010;break c}Ha(s+70|0,s+56|0,2);Ha(T,s+52|0,4);Ha(S,s+48|0,1);Ha(P,s+44|0,1);if((r|0)!=K[s+56>>2]){n=K[s+52>>2];if(n>>>0<14){break A}n=n-12|0;K[s+52>>2]=n;n=vb(j,n,k);if(!ua&K[s+52>>2]==(n|0)){continue}break A}break}l=K[s+48>>2]!=K[s+44>>2]}if(!Dc(j,o,t,k)){break b}if(l){break i}I[a+92|0]=L[a+92|0]&238|16;B:{if(!w){break B}o=K[a+180>>2];n=0;l=0;if((w|0)!=1){while(1){m=o+Q(n,5644)|0;r=K[m+5592>>2];if(r){K[m+5592>>2]=r+1}m=o+Q(n|1,5644)|0;r=K[m+5592>>2];if(r){K[m+5592>>2]=r+1}n=n+2|0;l=l+2|0;if((A|0)!=(l|0)){continue}break}}if(!D){break B}l=o+Q(n,5644)|0;o=K[l+5592>>2];if(!o){break B}K[l+5592>>2]=o+1}Fa(k,2,8998,0)}if(I[a+92|0]&1){break h}if((Na(j,K[a+16>>2],2,k)|0)!=2){if(!(!w|(w|0)!=(K[a+228>>2]+1|0))){j=K[a+180>>2];n=0;while(1){l=j+Q(n,5644)|0;if(!(K[l+5588>>2]|K[l+5592>>2])){break e}n=n+1|0;if((w|0)!=(n|0)){continue}break}}Fa(k,1,2435,0);j=0;break a}Ha(K[a+16>>2],s+40|0,2)}n=K[s+40>>2];if(I[a+92|0]&1){break g}if((n|0)!=65497){continue}}break}if(K[a+8>>2]==256|(n|0)!=65497){break d}K[a+8>>2]=256;K[a+228>>2]=0;break d}Ga(K[o+24>>2]);a=K[p+40>>2]+Q(C,40)|0;K[a+28>>2]=0;K[a+20>>2]=0;K[a+24>>2]=0;Fa(k,1,3826,0);j=0;break a}K[s+16>>2]=n;Fa(k,4,10967,s+16|0);K[a+228>>2]=n;K[s+40>>2]=65497;K[a+8>>2]=256}n=K[a+228>>2];j=K[a+180>>2];C:{D:{if(I[a+92|0]&1){break D}E:{F:{if(n>>>0>=w>>>0){break F}m=j+Q(n,5644)|0;while(1){if(K[m+5596>>2]){break F}n=n+1|0;K[a+228>>2]=n;m=m+5644|0;if((n|0)!=(w|0)){continue}break}break E}if((n|0)!=(w|0)){break D}}K[i>>2]=0;break C}G:{H:{l=j+Q(n,5644)|0;if(K[l+5172>>2]){a=6800}else{if(!(L[l+5640|0]&2)){break G}r=K[l+5160>>2];I:{if(!r){m=0;break I}w=K[l+5164>>2];j=0;m=0;n=0;if(r>>>0>=4){y=r&-4;o=0;while(1){t=w+(n<<3)|0;m=K[t+28>>2]+(K[t+20>>2]+(K[t+12>>2]+(K[t+4>>2]+m|0)|0)|0)|0;n=n+4|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}o=r&3;if(!o){break I}while(1){m=K[(w+(n<<3)|0)+4>>2]+m|0;n=n+1|0;j=j+1|0;if((o|0)!=(j|0)){continue}break}}j=Ja(m);K[l+5172>>2]=j;if(j){break H}a=3972}Fa(k,1,a,0);Fa(k,1,8022,0);j=0;break a}K[l+5180>>2]=m;m=K[l+5164>>2];j=K[l+5160>>2];if(j){o=0;n=0;while(1){r=n<<3;t=r+m|0;w=K[t>>2];if(w){j=K[t+4>>2];if(j){E(K[l+5172>>2]+o|0,w,j)}j=r+K[l+5164>>2]|0;t=K[j+4>>2];Ga(K[j>>2]);m=K[l+5164>>2];j=r+m|0;K[j>>2]=0;K[j+4>>2]=0;o=o+t|0;j=K[l+5160>>2]}n=n+1|0;if(n>>>0>>0){continue}break}}K[l+5160>>2]=0;Ga(m);K[l+5164>>2]=0;K[l+5168>>2]=K[l+5172>>2];K[l+5176>>2]=K[l+5180>>2]}l=K[a+232>>2];Y=K[l+28>>2];o=K[a+228>>2];G=K[(K[Y+76>>2]+Q(o,5644)|0)+5584>>2];j=K[l+24>>2];Z=K[j+24>>2];n=K[Y+24>>2];m=(o>>>0)/(n>>>0)|0;U=K[K[l+20>>2]>>2];l=o-Q(m,n)|0;n=K[Y+12>>2];l=K[Y+4>>2]+Q(l,n)|0;o=K[j>>2];o=l>>>0>o>>>0?l:o;K[U>>2]=o;n=l+n|0;l=l>>>0>n>>>0?-1:n;n=K[j+8>>2];l=l>>>0>>0?l:n;K[U+8>>2]=l;J:{K:{if(!((l|0)>(o|0)&(o|0)>=0)){Fa(k,1,6645,0);break K}n=K[U+20>>2];l=m;m=K[Y+16>>2];l=K[Y+8>>2]+Q(l,m)|0;o=K[j+4>>2];o=l>>>0>o>>>0?l:o;K[U+4>>2]=o;m=l+m|0;l=l>>>0>m>>>0?-1:m;j=K[j+12>>2];j=j>>>0>l>>>0?l:j;K[U+12>>2]=j;if(!((j|0)>(o|0)&(o|0)>=0)){Fa(k,1,6607,0);break K}L:{if(K[G+4>>2]){if(K[U+16>>2]){break L}j=1;break J}Fa(k,1,5321,0);break K}M:{N:{while(1){K[Z+36>>2]=0;j=K[Z>>2];m=j>>31;w=j-1|0;l=K[U>>2];r=l;o=w+l|0;v=m-!j|0;l=v+(l>>31)|0;ta=n,wa=Me(o,o>>>0>>0?l+1|0:l,j,m),K[ta>>2]=wa;o=K[Z+4>>2];t=o>>31;r=o-1|0;l=K[U+4>>2];p=l;y=r+l|0;q=t-!o|0;l=q+(l>>31)|0;ta=n,wa=Me(y,p>>>0>y>>>0?l+1|0:l,o,t),K[ta+4>>2]=wa;l=K[U+8>>2];y=l;w=l+w|0;l=(l>>31)+v|0;ta=n,wa=Me(w,w>>>0>>0?l+1|0:l,j,m),K[ta+8>>2]=wa;j=K[U+12>>2];K[n+16>>2]=ga;l=q+(j>>31)|0;j=j+r|0;l=j>>>0>>0?l+1|0:l;ta=n,wa=Me(j,l,o,t),K[ta+12>>2]=wa;j=K[G+4>>2];K[n+20>>2]=j;l=K[Y+80>>2];K[n+24>>2]=j>>>0>>0?1:j-l|0;Ga(K[n+52>>2]);K[n+68>>2]=0;K[n+60>>2]=0;K[n+64>>2]=0;K[n+52>>2]=0;K[n+56>>2]=0;j=Q(j,152);l=K[n+28>>2];O:{if(!l){l=Ja(j);K[n+28>>2]=l;if(!l){break K}K[n+32>>2]=j;if(!j){break O}B(l,0,j);break O}if(j>>>0<=N[n+32>>2]){break O}l=La(l,j);if(!l){Fa(k,1,3053,0);Ga(K[n+28>>2]);K[n+28>>2]=0;K[n+32>>2]=0;break K}K[n+28>>2]=l;o=K[n+32>>2];m=j-o|0;if(m){B(l+o|0,0,m)}K[n+32>>2]=j}j=K[n+20>>2];if(j){ja=G+944|0;ka=G+812|0;ea=G+28|0;o=K[n+28>>2];_=0;while(1){t=j-1|0;m=t&31;if((t&63)>>>0>=32){l=-1<>>32-m}w=r^-1;r=K[n>>2];m=w+r|0;y=l^-1;l=y+(r>>31)|0;l=m>>>0>>0?l+1|0:l;r=m;m=t&31;if((t&63)>>>0>=32){p=l>>m}else{p=((1<>>m}K[o>>2]=p;l=K[n+4>>2];r=l;m=l+w|0;l=(l>>31)+y|0;l=m>>>0>>0?l+1|0:l;r=m;m=t&31;if((t&63)>>>0>=32){q=l>>m}else{q=((1<>>m}K[o+4>>2]=q;l=K[n+8>>2];r=l;m=l+w|0;l=(l>>31)+y|0;l=m>>>0>>0?l+1|0:l;r=m;m=t&31;if((t&63)>>>0>=32){r=l>>m}else{r=((1<>>m}K[o+8>>2]=r;l=K[n+12>>2];v=l;m=l+w|0;l=(l>>31)+y|0;l=m>>>0>>0?l+1|0:l;v=m;m=t&31;if((t&63)>>>0>=32){v=l>>m}else{v=((1<>>m}K[o+12>>2]=v;A=r>>31;D=_<<2;P=K[D+ka>>2];m=P&31;if((P&63)>>>0>=32){l=1<>>32-m}H=u;m=H+r|0;l=l+A|0;A=m-1|0;m=(m>>>0>>0?l+1|0:l)-!m|0;l=P&31;if((P&63)>>>0>=32){l=m>>l}else{l=((1<>>l}A=l<>31;H=K[D+ja>>2];m=H&31;if((H&63)>>>0>=32){l=-1<>>32-m;m=-1<>>0>>0?l+1|0:l;D=m;m=H&31;if((H&63)>>>0>=32){l=l>>m}else{l=((1<>>m}l=l<>H:0;K[o+20>>2]=v;aa=p&-1<>P:0;K[o+16>>2]=m;Le(m,0,v);if(!(!m|!ua)){break N}ca=Q(m,v);if(ca>>>0>=107374183){break N}V=Q(ca,40);if(_){H=H-1|0;P=P-1|0;l=$>>31;m=$+1|0;$=((m?l:l+1|0)&1)<<31|m>>>1;l=aa>>31;m=aa+1|0;aa=((m?l:l+1|0)&1)<<31|m>>>1;l=3}else{l=1}K[o+24>>2]=l;m=o+28|0;v=j;r=j&31;if((j&63)>>>0>=32){l=1<>>32-r}ia=j;r=l;j=K[G+12>>2];S=j>>>0>>0?j:H;j=S&31;if((S&63)>>>0>=32){l=-1<>>32-j;j=-1<>2];T=j>>>0

>>0?j:P;j=T&31;if((T&63)>>>0>=32){l=-1<>>32-j;j=-1<>2];p=l;j=l+w|0;l=(l>>31)+y|0;l=j>>>0

>>0?l+1|0:l;p=j;j=t&31;if((t&63)>>>0>=32){M=l>>j}else{M=((1<>>j}l=K[n>>2];p=l;j=l+w|0;l=(l>>31)+y|0;l=j>>>0

>>0?l+1|0:l;p=j;j=t&31;if((t&63)>>>0>=32){ba=l>>j}else{ba=((1<>>j}j=0;p=w;A=p;q=y;D=q;l=t;break P}j=fa+1|0;p=j>>>1|0;q=t&31;if((t&63)>>>0>=32){l=p<>>32-q;p=p<>>0>A>>>0?l+1|0:l;q=K[n+4>>2];p=q+A|0;D=l;l=l+(q>>31)|0;l=p>>>0>>0?l+1|0:l;q=p;p=v&31;if((v&63)>>>0>=32){M=l>>p}else{M=((1<>>p}p=j&1;q=t&31;if((t&63)>>>0>=32){l=p<>>32-q;p=p<>2];C=u+p|0;q=p>>>0>>0?l+1|0:l;l=q+(u>>31)|0;l=u>>>0>C>>>0?l+1|0:l;u=C;C=v&31;if((v&63)>>>0>=32){ba=l>>C}else{ba=((1<>>C}l=v}C=l;u=K[n+8>>2];ha=u>>31;F=K[n+12>>2];K[m+4>>2]=M;K[m>>2]=ba;K[m+16>>2]=j;l=(F>>31)+D|0;A=A+F|0;l=A>>>0>>0?l+1|0:l;D=A;A=C&31;if((C&63)>>>0>=32){l=l>>A}else{l=((1<>>A}K[m+12>>2]=l;l=q+ha|0;p=p+u|0;l=p>>>0>>0?l+1|0:l;q=p;p=C&31;if((C&63)>>>0>=32){l=l>>p}else{l=((1<>>p}K[m+8>>2]=l;da=1;p=K[ea>>2];j=(K[Z+24>>2]+(!K[G+20>>2]|!j?0:(j|0)==3?2:1)|0)-p|0;Q:{if((j|0)>=1024){da=898846567431158e293;if(j>>>0<2047){j=j-1023|0;break Q}da=Infinity;j=(j>>>0>=3069?3069:j)-2046|0;break Q}if((j|0)>-1023){break Q}da=2004168360008973e-307;if(j>>>0>4294965304){j=j+969|0;break Q}da=0;j=(j>>>0<=4294964336?-2960:j)+1938|0}pa=+K[ea+4>>2]*.00048828125+1;x(0,0);x(1,j+1023<<20);ta=m,xa=R(pa*(da*+z())),O[ta+32>>2]=xa;K[m+28>>2]=(p+K[G+804>>2]|0)-1;j=K[m+20>>2];R:{S:{if(!(j|!ca)){j=Ja(V);K[m+20>>2]=j;if(!j){Fa(k,1,2817,0);break K}if(V){B(j,0,V)}K[m+24>>2]=V;break S}if(V>>>0>N[m+24>>2]){j=La(j,V);if(!j){Fa(k,1,2817,0);Ga(K[m+20>>2]);K[m+20>>2]=0;K[m+24>>2]=0;break K}K[m+20>>2]=j;l=K[m+24>>2];p=V-l|0;if(p){B(j+l|0,0,p)}K[m+24>>2]=V}if(!ca){break R}}j=K[m+20>>2];A=0;while(1){p=K[o+16>>2];l=(A>>>0)/(p>>>0)|0;p=A-Q(l,p)|0;q=(p<>2];D=(q|0)>(D|0)?q:D;K[j>>2]=D;q=(l<>2];C=(q|0)>(C|0)?q:C;K[j+4>>2]=C;p=(p+1<>2];p=(p|0)<(q|0)?p:q;K[j+8>>2]=p;l=(l+1<>2];q=(l|0)<(q|0)?l:q;K[j+12>>2]=q;l=(p>>31)+oa|0;u=p;p=p+na|0;l=u>>>0>p>>>0?l+1|0:l;D=D>>T;u=p;p=T&31;if((T&63)>>>0>=32){l=l>>p}else{l=((1<>>p}u=l-D<>T;K[j+16>>2]=u;l=(q>>31)+ma|0;p=q+la|0;l=p>>>0>>0?l+1|0:l;C=C>>S;q=p;p=S&31;if((S&63)>>>0>=32){l=l>>p}else{l=((1<>>p}l=l-C<>S;K[j+20>>2]=l;p=Q(l,u);Le(p,0,68);if(ua){Fa(k,1,2898,0);break K}l=Q(p,68);q=K[j+24>>2];T:{U:{if(!(q|!p)){q=Ja(l);K[j+24>>2]=q;if(!q){break K}if(!l){break U}B(q,0,l);break U}if(l>>>0<=N[j+28>>2]){break T}q=La(q,l);if(!q){Ga(K[j+24>>2]);K[j+24>>2]=0;K[j+28>>2]=0;Fa(k,1,2512,0);break K}K[j+24>>2]=q;u=K[j+28>>2];F=l-u|0;if(!F){break U}B(q+u|0,0,F)}K[j+28>>2]=l}l=K[j+20>>2];q=K[j+16>>2];u=K[j+32>>2];V:{if(!u){l=wc(q,l,k);break V}l=uc(u,q,l,k)}K[j+32>>2]=l;l=K[j+20>>2];q=K[j+16>>2];u=K[j+36>>2];W:{if(!u){l=wc(q,l,k);break W}l=uc(u,q,l,k)}K[j+36>>2]=l;if(p){ba=C+1|0;ha=D+1|0;q=0;while(1){W=K[j+16>>2];u=(q>>>0)/(W>>>0)|0;l=K[j+24>>2]+Q(q,68)|0;M=K[l>>2];X:{if(M){qa=K[l+56>>2];F=K[l+48>>2];sa=K[l+4>>2];Ga(K[l+60>>2]);K[l+48>>2]=0;K[l+52>>2]=0;K[l- -64>>2]=0;K[l+56>>2]=0;K[l+60>>2]=0;K[l+40>>2]=0;K[l+44>>2]=0;K[l+32>>2]=0;K[l+36>>2]=0;K[l+24>>2]=0;K[l+28>>2]=0;K[l+16>>2]=0;K[l+20>>2]=0;K[l+8>>2]=0;K[l+12>>2]=0;K[l>>2]=M;K[l+48>>2]=F;Y:{if(!F){break Y}F=Q(F,24);if(!F){break Y}B(M,0,F)}K[l+56>>2]=qa;K[l+4>>2]=sa;break X}F=Ia(10,24);K[l>>2]=F;if(!F){break K}K[l+48>>2]=10}F=q-Q(u,W)|0;M=F+D<>2];K[l+8>>2]=(M|0)>(W|0)?M:W;M=u+C<>2];K[l+12>>2]=(M|0)>(W|0)?M:W;F=F+ha<>2];K[l+16>>2]=(F|0)<(M|0)?F:M;M=l;l=u+ba<>2];K[M+20>>2]=(l|0)<(u|0)?l:u;q=q+1|0;if((p|0)!=(q|0)){continue}break}}j=j+40|0;A=A+1|0;if((A|0)!=(ca|0)){continue}break}}ea=ea+8|0;m=m+36|0;fa=fa+1|0;if(fa>>>0>2]){continue}break}o=o+152|0;j=t;_=_+1|0;if(_>>>0>2]){continue}break}}Z=Z+52|0;n=n+76|0;G=G+1080|0;ga=ga+1|0;if(ga>>>0>2]){continue}break}j=1;break J}Fa(k,1,2945,0);break K}Fa(k,1,2336,0)}j=0}if(!j){Fa(k,1,3631,0);j=0;break a}j=K[a+228>>2];K[s+4>>2]=Q(K[a+128>>2],K[a+132>>2]);K[s>>2]=j+1;Fa(k,4,11788,s);K[b>>2]=K[a+228>>2];K[i>>2]=1;if(c){b=fc(K[a+232>>2],0);K[c>>2]=b;j=0;if((b|0)==-1){break a}}b=K[K[K[a+232>>2]+20>>2]>>2];K[d>>2]=K[b>>2];K[e>>2]=K[b+4>>2];K[f>>2]=K[b+8>>2];K[g>>2]=K[b+12>>2];K[h>>2]=K[b+16>>2];K[a+8>>2]=K[a+8>>2]|128}j=1;break a}Fa(k,1,m,0)}Fa(k,1,3665,0);j=0}ra=s+80|0;return j|0}function jc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,O=0,P=0,R=0,S=0,T=0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{switch(K[a+84>>2]){case 0:k:{c=K[a+52>>2];b=K[a+196>>2];if(c>>>0>>0){q=K[a+64>>2];if(q>>>0>>0){break k}}Fa(K[a+236>>2],1,8454,0);break b}if(!K[a+44>>2]){k=K[a+36>>2];b=0;break i}K[a+44>>2]=0;i=K[a+68>>2];b=1;break i;case 1:l:{c=K[a+52>>2];b=K[a+196>>2];if(c>>>0>>0){q=K[a+64>>2];if(q>>>0>>0){break l}}Fa(K[a+236>>2],1,8499,0);break b}if(!K[a+44>>2]){e=K[a+36>>2];b=0;break e}K[a+44>>2]=0;i=K[a+48>>2];b=1;break e;case 2:m:{A=K[a+52>>2];x=K[a+196>>2];if(A>>>0>>0){r=K[a+64>>2];if(r>>>0>>0){break m}}Fa(K[a+236>>2],1,8634,0);break b}if(!K[a+44>>2]){y=K[a+40>>2];break f}K[a+228>>2]=0;K[a+232>>2]=0;K[a+44>>2]=0;j=K[a+200>>2];while(1){I=j+(u<<4)|0;l=K[I+8>>2];if(l){q=K[I+12>>2];b=0;while(1){g=l+(b^-1)|0;d=q+(b<<4)|0;s=g+K[d>>2]|0;n:{if(s>>>0>31){break n}c=K[I>>2];if(c>>>0>-1>>>s>>>0){break n}c=c<>>0>k>>>0?k:c:c;K[a+228>>2]=k}g=g+K[d+4>>2]|0;o:{if(g>>>0>31){break o}c=K[I+4>>2];if(c>>>0>-1>>>g>>>0){break o}c=c<>>0>i>>>0?i:c:c;K[a+232>>2]=i}b=b+1|0;if((l|0)!=(b|0)){continue}break}}u=u+1|0;if((x|0)!=(u|0)){continue}break};if(!k|!i){break d}if(!L[a|0]){K[a+108>>2]=K[a+208>>2];K[a+100>>2]=K[a+204>>2];K[a+112>>2]=K[a+216>>2];K[a+104>>2]=K[a+212>>2]}o=K[a+48>>2];b=1;break f;case 3:p:{A=K[a+52>>2];l=K[a+196>>2];if(A>>>0>>0){O=K[a+64>>2];if(O>>>0>>0){break p}}Fa(K[a+236>>2],1,8589,0);break b}if(!K[a+44>>2]){B=K[a+200>>2];e=K[a+28>>2];y=B+(e<<4)|0;E=K[a+40>>2];break g}K[a+228>>2]=0;K[a+232>>2]=0;K[a+44>>2]=0;B=K[a+200>>2];while(1){x=(p<<4)+B|0;s=K[x+8>>2];if(s){q=K[x+12>>2];b=0;while(1){g=s+(b^-1)|0;d=q+(b<<4)|0;j=g+K[d>>2]|0;q:{if(j>>>0>31){break q}c=K[x>>2];if(c>>>0>-1>>>j>>>0){break q}c=c<>>0>k>>>0?k:c:c;K[a+228>>2]=k}g=g+K[d+4>>2]|0;r:{if(g>>>0>31){break r}c=K[x+4>>2];if(c>>>0>-1>>>g>>>0){break r}c=c<>>0>i>>>0?i:c:c;K[a+232>>2]=i}b=b+1|0;if((s|0)!=(b|0)){continue}break}}p=p+1|0;if((l|0)!=(p|0)){continue}break};if(!k|!i){break d}s:{if(L[a|0]){p=K[a+108>>2];break s}p=K[a+208>>2];K[a+108>>2]=p;K[a+100>>2]=K[a+204>>2];K[a+112>>2]=K[a+216>>2];K[a+104>>2]=K[a+212>>2]}b=1;break g;case 4:break j;default:break d}}t:{p=K[a+52>>2];b=K[a+196>>2];if(p>>>0>>0){r=K[a+64>>2];if(r>>>0>>0){break t}}Fa(K[a+236>>2],1,8544,0);break d}if(!K[a+44>>2]){p=K[a+28>>2];o=K[a+200>>2]+(p<<4)|0;u=K[a+40>>2];b=0;break h}K[a+28>>2]=p;K[a+44>>2]=0;b=1;break h}u:while(1){v:{w:{if(!b){k=k+1|0;break w}K[a+40>>2]=i;if(N[a+56>>2]<=i>>>0){break b}e=K[a+48>>2];b=0;break v}b=1}x:while(1){y:{z:{A:{B:{if(!b){K[a+32>>2]=e;if(N[a+60>>2]<=e>>>0){break B}K[a+28>>2]=c;b=c;o=0;break y}K[a+36>>2]=k;if(N[a+76>>2]<=k>>>0){b=K[a+28>>2];o=1;break y}b=((Q(K[a+16>>2],K[a+32>>2])+Q(K[a+12>>2],K[a+40>>2])|0)+Q(K[a+20>>2],K[a+28>>2])|0)+Q(K[a+24>>2],k)|0;if(b>>>0>=N[a+8>>2]){break c}b=K[a+4>>2]+(b<<1)|0;if(M[b>>1]){break A}break a}i=K[a+40>>2]+1|0;break z}b=0;continue u}b=1;continue u}while(1){C:{D:{E:{if(!o){if(b>>>0>=q>>>0){break E}g=K[a+32>>2];d=K[a+200>>2]+(b<<4)|0;if(g>>>0>=N[d+8>>2]){break C}if(!L[a|0]){b=K[d+12>>2]+(g<<4)|0;K[a+76>>2]=Q(K[b+12>>2],K[b+8>>2])}k=K[a+72>>2];b=1;continue x}b=b+1|0;K[a+28>>2]=b;break D}e=K[a+32>>2]+1|0;b=0;continue x}o=0;continue}o=1;continue}}}}F:while(1){G:{H:{if(!b){u=u+1|0;K[a+40>>2]=u;break H}if(p>>>0>=r>>>0){break b}K[a+228>>2]=0;K[a+232>>2]=0;o=K[a+200>>2]+(p<<4)|0;s=K[o+8>>2];if(!s){break b}q=K[o+12>>2];k=0;e=0;b=0;while(1){g=s+(b^-1)|0;d=q+(b<<4)|0;j=g+K[d>>2]|0;I:{if(j>>>0>31){break I}c=K[o>>2];if(c>>>0>-1>>>j>>>0){break I}c=c<>>0>e>>>0?e:c:c;K[a+228>>2]=e}g=g+K[d+4>>2]|0;J:{if(g>>>0>31){break J}c=K[o+4>>2];if(c>>>0>-1>>>g>>>0){break J}c=c<>>0>k>>>0?k:c:c;K[a+232>>2]=k}b=b+1|0;if((s|0)!=(b|0)){continue}break}if(!e|!k){break d}K:{if(L[a|0]){k=K[a+108>>2];break K}k=K[a+208>>2];K[a+108>>2]=k;K[a+100>>2]=K[a+204>>2];K[a+112>>2]=K[a+216>>2];K[a+104>>2]=K[a+212>>2]}b=0;break G}b=1}L:while(1){M:{N:{O:{P:{if(!b){K[a+224>>2]=k;if(N[a+112>>2]<=k>>>0){break P}B=K[a+100>>2];b=0;break M}if(N[a+56>>2]<=u>>>0){i=K[a+32>>2];b=1;break M}b=((Q(K[a+16>>2],K[a+32>>2])+Q(K[a+12>>2],u)|0)+Q(K[a+20>>2],p)|0)+Q(K[a+24>>2],K[a+36>>2])|0;if(b>>>0>=N[a+8>>2]){break c}b=K[a+4>>2]+(b<<1)|0;if(M[b>>1]){break O}break a}p=p+1|0;K[a+28>>2]=p;break N}b=0;continue F}b=1;continue F}while(1){Q:{R:{S:{T:{if(!b){K[a+220>>2]=B;if(N[a+104>>2]<=B>>>0){break S}i=K[a+48>>2];break T}i=i+1|0}K[a+32>>2]=i;b=K[a+60>>2];d=K[o+8>>2];if((b>>>0>>0?b:d)>>>0>i>>>0){g=K[o>>2];c=g;n=d+(i^-1)|0;m=n;d=m&31;if((m&63)>>>0>=32){b=c<>>32-d;v=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break Q}b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=K[o+4>>2];if((b&c)!=(c|0)){break Q}d=m&31;if((m&63)>>>0>=32){b=c<>>32-d;w=c<>2];j=F+d|0;I=Ne(j,d>>>0>j>>>0?h+1|0:h,w,b);b=h;G=K[a+208>>2];d=F+G|0;b=G>>>0>d>>>0?b+1|0:b;s=Ne(d,b,w,C);A=v-1|0;j=K[a+212>>2];l=A+j|0;d=f-!v|0;b=d;x=Ne(l,l>>>0>>0?b+1|0:b,v,f);D=K[a+204>>2];j=A+D|0;b=D>>>0>j>>>0?b+1|0:b;j=Ne(j,b,v,f);z=K[o+12>>2]+(i<<4)|0;H=K[z>>2];t=H+n|0;b=t&31;if((t&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break Q}h=c;O=K[z+4>>2];n=O+n|0;e=n&31;if((n&63)>>>0>=32){b=c<>>32-e;e=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break Q}l=K[a+224>>2];e=!!(Oe(l,e,b)|ua);b=n&31;if((n&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=n<>>32-e|b<>2];if((t&63)>>>0>=32){b=g<>>32-n;e=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=j<>>32-t|b<>2];if(!n|(!K[z+12>>2]|(j|0)==(x|0))){break Q}if((s|0)==(I|0)){break Q}u=K[a+68>>2];K[a+40>>2]=u;b=d;c=c+A|0;b=c>>>0>>0?b+1|0:b;g=(Ne(c,b,v,f)>>>H)-(j>>>H)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=Q(n,(Ne(c,b,w,C)>>>O)-(s>>>O)|0)+g|0,K[S+36>>2]=T;b=1;continue L}c=K[a+220>>2];b=K[a+228>>2];B=c+b-(c>>>0)%(b>>>0)|0;break R}c=K[a+224>>2];b=K[a+232>>2];k=c+b-(c>>>0)%(b>>>0)|0;b=0;continue L}b=0;continue}b=1;continue}}}}U:while(1){V:{W:{if(!b){E=E+1|0;K[a+40>>2]=E;break W}K[a+224>>2]=p;if(N[a+112>>2]<=p>>>0){break b}v=K[a+100>>2];b=0;break V}b=1}X:while(1){Y:{Z:{_:{$:{if(!b){K[a+220>>2]=v;if(N[a+104>>2]<=v>>>0){break $}K[a+28>>2]=A;e=A;b=0;break Y}if(N[a+56>>2]<=E>>>0){u=K[a+32>>2];b=1;break Y}b=((Q(K[a+16>>2],K[a+32>>2])+Q(K[a+12>>2],E)|0)+Q(K[a+20>>2],e)|0)+Q(K[a+24>>2],K[a+36>>2])|0;if(b>>>0>=N[a+8>>2]){break c}b=K[a+4>>2]+(b<<1)|0;if(M[b>>1]){break _}break a}c=K[a+224>>2];b=K[a+232>>2];p=c+b-(c>>>0)%(b>>>0)|0;break Z}b=0;continue U}b=1;continue U}while(1){aa:{ba:{ca:{da:{if(!b){if(e>>>0>=O>>>0){break ca}u=K[a+48>>2];K[a+32>>2]=u;y=(e<<4)+B|0;break da}u=u+1|0;K[a+32>>2]=u}b=K[a+60>>2];d=K[y+8>>2];if((b>>>0>>0?b:d)>>>0>u>>>0){g=K[y>>2];c=g;f=d+(u^-1)|0;i=f;d=f&31;if((f&63)>>>0>=32){b=c<>>32-d;k=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break aa}b=i&31;if((i&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=K[y+4>>2];if((b&c)!=(c|0)){break aa}d=i&31;if((i&63)>>>0>=32){b=c<>>32-d;o=c<>2];j=F+d|0;I=Ne(j,d>>>0>j>>>0?h+1|0:h,o,b);b=h;w=K[a+208>>2];d=w+F|0;b=w>>>0>d>>>0?b+1|0:b;s=Ne(d,b,o,n);C=k-1|0;j=K[a+212>>2];l=C+j|0;d=t-!k|0;b=d;x=Ne(l,l>>>0>>0?b+1|0:b,k,t);G=K[a+204>>2];j=C+G|0;b=G>>>0>j>>>0?b+1|0:b;j=Ne(j,b,k,t);D=K[y+12>>2]+(u<<4)|0;z=K[D>>2];m=z+f|0;b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break aa}h=c;H=K[D+4>>2];f=H+f|0;r=f&31;if((f&63)>>>0>=32){b=c<>>32-r;r=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break aa}l=K[a+224>>2];r=!!(Oe(l,r,b)|ua);b=f&31;if((f&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-r|b<>2];if((m&63)>>>0>=32){b=g<>>32-f;f=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-m|b<>2];if(!f|(!K[D+12>>2]|(j|0)==(x|0))){break aa}if((s|0)==(I|0)){break aa}E=K[a+68>>2];K[a+40>>2]=E;b=d;c=c+C|0;b=c>>>0>>0?b+1|0:b;g=(Ne(c,b,k,t)>>>z)-(j>>>z)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=Q(f,(Ne(c,b,o,n)>>>H)-(s>>>H)|0)+g|0,K[S+36>>2]=T;b=1;continue X}e=e+1|0;K[a+28>>2]=e;break ba}c=K[a+220>>2];b=K[a+228>>2];v=c+b-(c>>>0)%(b>>>0)|0;b=0;continue X}b=0;continue}b=1;continue}}}}ea:while(1){fa:{ga:{if(!b){y=y+1|0;K[a+40>>2]=y;break ga}K[a+32>>2]=o;if(N[a+60>>2]<=o>>>0){break b}E=K[a+108>>2];b=0;break fa}b=1}ha:while(1){ia:{ja:{ka:{la:{if(!b){K[a+224>>2]=E;if(N[a+112>>2]<=E>>>0){break la}B=K[a+100>>2];b=0;break ia}if(N[a+56>>2]<=y>>>0){p=K[a+28>>2];b=1;break ia}b=((Q(K[a+16>>2],K[a+32>>2])+Q(K[a+12>>2],y)|0)+Q(K[a+20>>2],K[a+28>>2])|0)+Q(K[a+24>>2],K[a+36>>2])|0;if(b>>>0>=N[a+8>>2]){break c}b=K[a+4>>2]+(b<<1)|0;if(M[b>>1]){break ka}break a}o=K[a+32>>2]+1|0;break ja}b=0;continue ea}b=1;continue ea}while(1){ma:{na:{oa:{pa:{if(!b){K[a+220>>2]=B;if(N[a+104>>2]<=B>>>0){break oa}K[a+28>>2]=A;p=A;break pa}p=p+1|0;K[a+28>>2]=p}if(p>>>0>>0){m=K[a+32>>2];e=K[a+200>>2]+(p<<4)|0;b=K[e+8>>2];if(m>>>0>=b>>>0){break ma}g=K[e>>2];c=g;f=b+(m^-1)|0;i=f;d=f&31;if((f&63)>>>0>=32){b=c<>>32-d;v=g<>>0>=32){b=b>>>d|0}else{b=((1<>>d}if((q|0)!=(b|0)){break ma}b=i&31;if((i&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}c=K[e+4>>2];if((b&c)!=(c|0)){break ma}d=i&31;if((i&63)>>>0>=32){b=c<>>32-d;w=c<>2];j=F+d|0;I=Ne(j,d>>>0>j>>>0?h+1|0:h,w,b);b=h;G=K[a+208>>2];d=F+G|0;b=G>>>0>d>>>0?b+1|0:b;s=Ne(d,b,w,n);C=v-1|0;j=K[a+212>>2];l=C+j|0;d=t-!v|0;b=d;x=Ne(l,l>>>0>>0?b+1|0:b,v,t);D=K[a+204>>2];j=C+D|0;b=D>>>0>j>>>0?b+1|0:b;j=Ne(j,b,v,t);z=K[e+12>>2]+(m<<4)|0;H=K[z>>2];m=H+f|0;b=m&31;if((m&63)>>>0>=32){b=-1>>>b|0}else{b=(1<>>b}if((g|0)!=(b&g)){break ma}h=c;O=K[z+4>>2];f=O+f|0;e=f&31;if((f&63)>>>0>=32){b=c<>>32-e;e=c<>>0>=32){c=b>>>l|0}else{c=((1<>>l}if((h|0)!=(c|0)){break ma}l=K[a+224>>2];e=!!(Oe(l,e,b)|ua);b=f&31;if((f&63)>>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-e|b<>2];if((m&63)>>>0>=32){b=g<>>32-f;f=g<>>0>=32){h=-1<>>32-b;b=-1<>>0>=32){h=f<>>32-m|b<>2];if(!f|(!K[z+12>>2]|(j|0)==(x|0))){break ma}if((s|0)==(I|0)){break ma}y=K[a+68>>2];K[a+40>>2]=y;b=d;c=c+C|0;b=c>>>0>>0?b+1|0:b;g=(Ne(c,b,v,t)>>>H)-(j>>>H)|0;b=q;c=l+F|0;b=c>>>0>>0?b+1|0:b;S=a,T=Q(f,(Ne(c,b,w,n)>>>O)-(s>>>O)|0)+g|0,K[S+36>>2]=T;b=1;continue ha}c=K[a+220>>2];b=K[a+228>>2];B=c+b-(c>>>0)%(b>>>0)|0;break na}c=K[a+224>>2];b=K[a+232>>2];E=c+b-(c>>>0)%(b>>>0)|0;b=0;continue ha}b=0;continue}b=1;continue}}}}qa:while(1){ra:{sa:{if(!b){e=e+1|0;break sa}K[a+32>>2]=i;if(N[a+60>>2]<=i>>>0){break b}k=K[a+68>>2];b=0;break ra}b=1}ta:while(1){ua:{va:{wa:{xa:{if(!b){K[a+40>>2]=k;if(N[a+56>>2]<=k>>>0){break xa}K[a+28>>2]=c;b=c;o=0;break ua}K[a+36>>2]=e;if(N[a+76>>2]<=e>>>0){b=K[a+28>>2];o=1;break ua}b=((Q(K[a+16>>2],K[a+32>>2])+Q(K[a+12>>2],K[a+40>>2])|0)+Q(K[a+20>>2],K[a+28>>2])|0)+Q(K[a+24>>2],e)|0;if(b>>>0>=N[a+8>>2]){break c}b=K[a+4>>2]+(b<<1)|0;if(M[b>>1]){break wa}break a}i=K[a+32>>2]+1|0;break va}b=0;continue qa}b=1;continue qa}while(1){ya:{za:{Aa:{if(!o){if(b>>>0>=q>>>0){break Aa}g=K[a+32>>2];d=K[a+200>>2]+(b<<4)|0;if(g>>>0>=N[d+8>>2]){break ya}if(!L[a|0]){b=K[d+12>>2]+(g<<4)|0;K[a+76>>2]=Q(K[b+12>>2],K[b+8>>2])}e=K[a+72>>2];b=1;continue ta}b=b+1|0;K[a+28>>2]=b;break za}k=K[a+40>>2]+1|0;b=0;continue ta}o=0;continue}o=1;continue}}}}return 0}Fa(K[a+236>>2],1,1306,0)}return 0}J[b>>1]=1;return 1}function Cd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=R(0),s=0,t=0,u=0,v=0,w=R(0),x=0,y=0,z=0,A=R(0),C=R(0),D=R(0),F=0,G=0,H=0,J=0,M=0,N=R(0),O=0,P=0,T=0;m=ra-8320|0;ra=m;K[m+64>>2]=0;i=2;f=K[a>>2];a:{b:{if((f|0)==176622093){break b}if((f|0)!=1375686655){if(!((f|0)!=201326592|K[a+4>>2]!=538988650)&K[a+8>>2]==176622093){break b}$(1101);i=1;break a}i=0}f=Ia(1,96);g=0;c:{if(!f){break c}K[f+76>>2]=1;d:{e:{f:{switch(i|0){case 0:K[f+88>>2]=68;K[f+84>>2]=69;K[f+80>>2]=70;K[f+16>>2]=71;K[f+4>>2]=72;K[f+28>>2]=73;K[f+24>>2]=74;K[f+20>>2]=75;K[f>>2]=76;K[f+92>>2]=77;K[f+44>>2]=78;K[f+40>>2]=79;K[f+36>>2]=80;K[f+32>>2]=81;K[f+12>>2]=82;K[f+8>>2]=83;g=Yb();K[f+48>>2]=g;if(g){break e}break d;case 2:break f;default:break d}}K[f+88>>2]=84;K[f+84>>2]=85;K[f+80>>2]=86;K[f+16>>2]=87;K[f+4>>2]=88;K[f+92>>2]=89;K[f+44>>2]=90;K[f+40>>2]=91;K[f+36>>2]=92;K[f+32>>2]=93;K[f+28>>2]=94;K[f+24>>2]=95;K[f+20>>2]=96;K[f+12>>2]=97;K[f+8>>2]=98;K[f>>2]=99;g=Ia(1,136);g:{if(g){j=Yb();K[g>>2]=j;h:{if(!j){break h}K[g+108>>2]=0;K[g+112>>2]=0;I[g+124|0]=0;K[g+116>>2]=0;K[g+120>>2]=0;j=ub();K[g+4>>2]=j;if(!j){break h}j=ub();K[g+8>>2]=j;if(!j){break h}break g}Tc(g)}g=0}K[f+48>>2]=g;if(!g){break d}}K[f+72>>2]=1;K[f+64>>2]=1;K[f+60>>2]=0;K[f+52>>2]=0;K[f+56>>2]=0;K[f+68>>2]=1;g=f;break c}Ga(f);g=0}f=g;if(f){K[f+60>>2]=0;K[f+72>>2]=100}if(f){K[f+56>>2]=0;K[f+68>>2]=101}if(f){K[f+52>>2]=0;K[f+64>>2]=102}g=m+68|0;if(g){B(g,0,8248);K[g+8248>>2]=0;K[g+8200>>2]=-1;K[g+8204>>2]=-1}if(d){K[m+8316>>2]=K[m+8316>>2]|1}K[m+60>>2]=b;K[m+56>>2]=a;K[m+52>>2]=a;i=1;b=0;g=m+52|0;i:{if(!g){break i}a=Ia(1,72);if(a){j:{K[a+64>>2]=1048576;j=Ja(1048576);K[a+32>>2]=j;if(!j){Ga(a);a=0;break j}K[a+36>>2]=j;K[a+28>>2]=2;K[a+24>>2]=3;K[a+20>>2]=4;K[a+16>>2]=5;K[a+44>>2]=6;K[a+40>>2]=8;K[a+68>>2]=K[a+68>>2]|2}}else{a=0}if(!a){break i}if(a){K[a+4>>2]=0;K[a>>2]=g}b=K[g+8>>2];if(a){K[a+8>>2]=b;K[a+12>>2]=0}if(!(!a|!(L[a+68|0]&2))){K[a+16>>2]=64}if(a){K[a+24>>2]=66}if(a){K[a+28>>2]=67}b=a}a=f;f=m+68|0;if(!a|!f){f=0}else{k:{if(!K[a+76>>2]){Fa(a+52|0,1,9865,0);f=0;break k}va[K[a+24>>2]](K[a+48>>2],f);f=1}}if(!f){$(1116);zb(b);Cb(a);break a}if(!b|!a){f=0}else{l:{if(!K[a+76>>2]){Fa(a+52|0,1,9946,0);f=0;break l}f=va[K[a>>2]](b,K[a+48>>2],m- -64|0,a+52|0)|0}}if(!f){$(1144);zb(b);Cb(a);Ya(K[m+64>>2]);break a}g=K[m+64>>2];f=0;m:{if(!K[a+76>>2]|(!a|!b)){g=f}else{g=va[K[a+4>>2]](K[a+48>>2],b,g,a+52|0)|0}if(g){if(!(!K[a+76>>2]|(!a|!b))){f=va[K[a+16>>2]](K[a+48>>2],b,a+52|0)|0}if(f){break m}}$(1279);Cb(a);zb(b);Ya(K[m+64>>2]);break a}zb(b);Cb(a);l=K[m+64>>2];a=K[l+28>>2];if(a){Ga(a);l=K[m+64>>2];K[l+28>>2]=0;K[l+32>>2]=0}v=K[l+16>>2];n:{o:{if(!c){if(!(!e|(v|0)!=4)){k=1;v=4;break n}p:{b=K[l+20>>2];if(!((b|0)==3|(v|0)!=3)){a=K[l+24>>2];if(K[a>>2]!=K[a+4>>2]|K[a+52>>2]==1){break p}K[l+20>>2]=3;break o}if(v>>>0>2){break p}K[l+20>>2]=2;break n}q:{switch(b-3|0){case 2:r:{s:{if(v>>>0<4){break s}f=K[l+24>>2];a=K[f>>2];if((a|0)!=K[f+52>>2]|(a|0)!=K[f+104>>2]|(a|0)!=K[f+156>>2]){break s}a=K[f+4>>2];if((a|0)!=K[f+56>>2]|(a|0)!=K[f+108>>2]){break s}if((a|0)==K[f+160>>2]){break r}}K[m+20>>2]=1053;K[m+16>>2]=1336;Ka(26032,8142,m+16|0);break n}j=Q(K[f+12>>2],K[f+8>>2]);A=R(R(1)/R((-1<>2]^-1)>>>0));C=R(R(1)/R((-1<>2]^-1)>>>0));w=R(R(1)/R((-1<>2]^-1)>>>0));N=R(R(1)/R((-1<>2]^-1)>>>0));a=0;while(1){if((a|0)!=(j|0)){g=a<<2;b=g+K[f+148>>2]|0;p=K[b>>2];c=g+K[f+96>>2]|0;i=K[c>>2];k=g+K[f+44>>2]|0;r=R(R(1)-R(A*R(K[g+K[f+200>>2]>>2])));D=R(R(R(R(1)-R(N*R(K[k>>2])))*R(255))*r);if(R(S(D))>2]=g;D=R(R(R(R(1)-R(w*R(i|0)))*R(255))*r);if(R(S(D))>2]=g;r=R(R(R(R(1)-R(C*R(p|0)))*R(255))*r);if(R(S(r))>2]=c;a=a+1|0;continue}break};Ga(K[f+200>>2]);a=K[l+24>>2];K[a+128>>2]=8;K[a+76>>2]=8;K[a+24>>2]=8;k=0;K[a+200>>2]=0;K[l+20>>2]=1;a=K[l+16>>2]-1|0;K[l+16>>2]=a;h=3;while(1){if(a>>>0<=h>>>0){break n}a=K[l+24>>2]+Q(h,52)|0;E(a,a+52|0,52);h=h+1|0;a=K[l+16>>2];continue};case 0:break o;case 1:break q;default:break n}}j=K[l+24>>2];a=K[j>>2];t:{u:{if((a|0)!=K[j+52>>2]|(a|0)!=K[j+104>>2]){break u}a=K[j+4>>2];if((a|0)!=K[j+56>>2]){break u}if((a|0)==K[j+108>>2]){break t}}K[m+36>>2]=1115;K[m+32>>2]=1336;Ka(26032,8184,m+32|0);break n}a=K[j+24>>2];b=-1<>2]?0:a;i=K[j+84>>2]?0:a;k=Q(K[j+12>>2],K[j+8>>2]);a=0;while(1){if((a|0)!=(k|0)){c=a<<2;h=c+K[j+44>>2]|0;f=c+K[j+148>>2]|0;r=R(K[f>>2]-p|0);g=c+K[j+96>>2]|0;A=R(K[g>>2]-i|0);C=R(K[h>>2]);w=R(R(R(r*R(1.4019900560379028))+R(R(A*R(-3680000008898787e-20))+C))+R(.5));if(R(S(w))>2]=(b|0)<(c|0)?b:(c|0)>0?c:0;w=R(R(R(r*R(-.7141128182411194))+R(R(C*R(1.0003000497817993))+R(A*R(-.34412500262260437))))+R(.5));if(R(S(w))>2]=(b|0)<(c|0)?b:(c|0)>0?c:0;r=R(R(R(r*R(-7999999979801942e-21))+R(R(C*R(.9998229742050171))+R(A*R(1.7720400094985962))))+R(.5));if(R(S(r))>2]=(b|0)<(c|0)?b:(c|0)>0?c:0;a=a+1|0;continue}break}K[l+20>>2]=1;k=0;break n}v=c>>>0>v>>>0?v:c;k=1;break n}v:{w:{c=K[l+24>>2];if(K[c>>2]!=1){break w}x:{switch(K[c+52>>2]-1|0){case 1:if(K[c+104>>2]!=2){break w}if(!(K[c+4>>2]!=1|K[c+56>>2]!=2|K[c+108>>2]!=2)){b=K[c+24>>2];h=K[c+148>>2];a=K[c+96>>2];i=K[c+44>>2];F=K[c+60>>2];q=K[c+8>>2];f=K[c+12>>2];c=Q(q,f)<<2;g=Ma(c);j=Ma(c);p=Ma(c);if(!(!g|!j|!p)){n=-1<>2]&1;J=f-b|0;G=K[l>>2]&1;x=q-G|0;if(!b){c=p;b=j;f=g;break v}c=p;b=j;f=g;while(1){if((k|0)==(q|0)){break v}Oa(o,n,K[i>>2],0,0,f,b,c);k=k+1|0;c=c+4|0;b=b+4|0;f=f+4|0;i=i+4|0;continue}}Ga(g);Ga(j);Ga(p);break n}if(K[c+4>>2]!=1|K[c+56>>2]!=1|K[c+108>>2]!=1){break w}a=K[c+24>>2];b=K[c+148>>2];f=K[c+96>>2];h=K[c+44>>2];s=K[c+60>>2];g=K[c+8>>2];u=K[c+12>>2];c=Q(g,u)<<2;j=Ma(c);p=Ma(c);k=Ma(c);if(!(!j|!p|!k)){n=-1<>2]&1;a=g-x|0;y=a&1;t=a>>>1|0;F=a&-2;a=k;i=p;c=j;while(1){if((q|0)!=(u|0)){if(x){Oa(o,n,K[h>>2],0,0,c,i,a);i=i+4|0;c=c+4|0;h=h+4|0;a=a+4|0}g=0;while(1){if(g>>>0>>0){Oa(o,n,K[h>>2],K[f>>2],K[b>>2],c,i,a);Oa(o,n,K[h+4>>2],K[f>>2],K[b>>2],c+4|0,i+4|0,a+4|0);g=g+2|0;b=b+4|0;f=f+4|0;a=a+8|0;i=i+8|0;c=c+8|0;h=h+8|0;continue}break}y:{if(!y){break y}g=K[h>>2];z:{if((s|0)==(t|0)){Oa(o,n,g,0,0,c,i,a);break z}Oa(o,n,g,K[f>>2],K[b>>2],c,i,a)}a=a+4|0;i=i+4|0;c=c+4|0;h=h+4|0;if(s>>>0<=t>>>0){break y}b=b+4|0;f=f+4|0}q=q+1|0;continue}break}Ga(K[K[l+24>>2]+44>>2]);a=K[l+24>>2];K[a+44>>2]=j;Ga(K[a+96>>2]);a=K[l+24>>2];K[a+96>>2]=p;Ga(K[a+148>>2]);a=K[l+24>>2];K[a+148>>2]=k;b=K[a+8>>2];K[a+112>>2]=b;K[a+60>>2]=b;b=K[a+12>>2];K[a+116>>2]=b;K[a+64>>2]=b;b=K[a>>2];K[a+104>>2]=b;K[a+52>>2]=b;b=K[a+4>>2];K[a+108>>2]=b;K[a+56>>2]=b;K[l+20>>2]=1;k=0;break n}Ga(j);Ga(p);Ga(k);k=0;break n;case 0:break x;default:break w}}if(K[c+104>>2]!=1|K[c+4>>2]!=1|(K[c+56>>2]!=1|K[c+108>>2]!=1)){break w}b=K[c+24>>2];h=K[c+148>>2];a=K[c+96>>2];i=K[c+44>>2];n=Q(K[c+12>>2],K[c+8>>2]);c=n<<2;j=Ma(c);p=Ma(c);k=Ma(c);if(!(!j|!p|!k)){o=-1<>2],K[a>>2],K[h>>2],g,f,b);c=c+1|0;b=b+4|0;f=f+4|0;g=g+4|0;h=h+4|0;a=a+4|0;i=i+4|0;continue}break}Ga(K[K[l+24>>2]+44>>2]);a=K[l+24>>2];K[a+44>>2]=j;Ga(K[a+96>>2]);a=K[l+24>>2];K[a+96>>2]=p;Ga(K[a+148>>2]);K[K[l+24>>2]+148>>2]=k;K[l+20>>2]=1;k=0;break n}Ga(j);Ga(p);Ga(k);k=0;break n}K[m+4>>2]=463;K[m>>2]=1336;Ka(26032,8227,m);break n}H=x>>>1|0;y=x&-2;O=J&-2;u=q<<2;while(1){if(M>>>0>>0){s=c+u|0;q=b+u|0;t=f+u|0;k=i+u|0;if(G){Oa(o,n,K[i>>2],0,0,f,b,c);Oa(o,n,K[k>>2],K[a>>2],K[h>>2],t,q,s);s=s+4|0;q=q+4|0;t=t+4|0;k=k+4|0;c=c+4|0;f=f+4|0;i=i+4|0;b=b+4|0}z=0;while(1){if(y>>>0>z>>>0){Oa(o,n,K[i>>2],K[a>>2],K[h>>2],f,b,c);Oa(o,n,K[i+4>>2],K[a>>2],K[h>>2],f+4|0,b+4|0,c+4|0);Oa(o,n,K[k>>2],K[a>>2],K[h>>2],t,q,s);Oa(o,n,K[k+4>>2],K[a>>2],K[h>>2],t+4|0,q+4|0,s+4|0);z=z+2|0;h=h+4|0;a=a+4|0;s=s+8|0;q=q+8|0;t=t+8|0;k=k+8|0;c=c+8|0;b=b+8|0;f=f+8|0;i=i+8|0;continue}break}A:{if((x|0)==(y|0)){break A}z=K[i>>2];B:{if((F|0)==(H|0)){Oa(o,n,z,0,0,f,b,c);Oa(o,n,K[k>>2],0,0,t,q,s);break B}Oa(o,n,z,K[a>>2],K[h>>2],f,b,c);Oa(o,n,K[k>>2],K[a>>2],K[h>>2],t,q,s)}c=c+4|0;b=b+4|0;f=f+4|0;i=i+4|0;if(F>>>0<=H>>>0){break A}h=h+4|0;a=a+4|0}M=M+2|0;c=c+u|0;b=b+u|0;f=f+u|0;i=i+u|0;continue}break}C:{if(!(J&1)){break C}if(G){Oa(o,n,K[i>>2],0,0,f,b,c);c=c+4|0;f=f+4|0;i=i+4|0;b=b+4|0}k=0;while(1){if(k>>>0>>0){Oa(o,n,K[i>>2],K[a>>2],K[h>>2],f,b,c);Oa(o,n,K[i+4>>2],K[a>>2],K[h>>2],f+4|0,b+4|0,c+4|0);k=k+2|0;h=h+4|0;a=a+4|0;c=c+8|0;b=b+8|0;f=f+8|0;i=i+8|0;continue}break}if((x|0)==(y|0)){break C}i=K[i>>2];if((F|0)==(H|0)){Oa(o,n,i,0,0,f,b,c);break C}Oa(o,n,i,K[a>>2],K[h>>2],f,b,c)}Ga(K[K[l+24>>2]+44>>2]);a=K[l+24>>2];K[a+44>>2]=g;Ga(K[a+96>>2]);a=K[l+24>>2];K[a+96>>2]=j;Ga(K[a+148>>2]);a=K[l+24>>2];K[a+148>>2]=p;b=K[a+8>>2];K[a+112>>2]=b;K[a+60>>2]=b;b=K[a+12>>2];K[a+116>>2]=b;K[a+64>>2]=b;b=K[a>>2];K[a+104>>2]=b;K[a+52>>2]=b;b=K[a+4>>2];K[a+108>>2]=b;K[a+56>>2]=b;K[l+20>>2]=1;k=0}c=K[m+64>>2];D:{if(d){break D}f=0;while(1){if((f|0)==(v|0)){break D}d=K[c+24>>2]+Q(f,52)|0;a=K[d+24>>2];if((a|0)!=8){E:{if(a>>>0<=7){g=Q(K[d+12>>2],K[d+8>>2]);j=K[d+44>>2];if(K[d+32>>2]){b=1<>2];i=a>>31<<7|a>>>25;P=p,T=Me(a<<7,i,b,0),K[P>>2]=T;h=h+1|0;continue}}a=-1<>2],0,255),ua,a,0);K[b>>2]=p;h=h+1|0;continue}}a=a-8|0;b=Q(K[d+12>>2],K[d+8>>2]);g=K[d+44>>2];h=0;if(K[d+32>>2]){while(1){if((b|0)==(h|0)){break E}j=g+(h<<2)|0;K[j>>2]=K[j>>2]>>a;h=h+1|0;continue}}while(1){if((b|0)==(h|0)){break E}j=g+(h<<2)|0;K[j>>2]=K[j>>2]>>>a;h=h+1|0;continue}}K[d+24>>2]=8}f=f+1|0;continue}}a=Q(K[c+12>>2],K[c+8>>2]);F:{if(!k){if(K[c+20>>2]==2){if(K[c+16>>2]==1){qa(K[K[c+24>>2]+44>>2],a|0);break F}if(!e){break F}b=K[c+24>>2];ha(K[b+44>>2],K[b+96>>2],a|0);break F}b=K[c+24>>2];ga(K[b+44>>2],K[b+96>>2],K[b+148>>2],a|0);break F}G:{switch(v-1|0){case 0:fa(K[K[c+24>>2]+44>>2],a|0);break F;case 2:b=K[c+24>>2];ea(K[b+44>>2],K[b+96>>2],K[b+148>>2],a|0);break F;case 3:break G;default:break F}}b=K[c+24>>2];da(K[b+44>>2],K[b+96>>2],K[b+148>>2],K[b+200>>2],a|0)}Ya(K[m+64>>2]);i=0}ra=m+8320|0;return i|0}function qc(a,b,c,d,e,f,g,h,i){var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,C=0,D=0,F=0,G=0,H=0,I=0,J=0,L=0;j=K[a>>2];a:{if(j>>>0>>0|b>>>0>=d>>>0|b>>>0>=j>>>0){break a}j=K[a+4>>2];if(j>>>0>>0|c>>>0>=e>>>0|c>>>0>=j>>>0){break a}A=(c>>>0)/N[a+12>>2]|0;s=K[a+8>>2];F=(b>>>0)/(s>>>0)|0;I=(Q(s,F)-b|0)+s|0;x=c;while(1){k=K[a+12>>2];j=k;j=(c|0)==(x|0)?j-((c>>>0)%(j>>>0)|0)|0:j;u=e-x|0;r=j>>>0>>0?j:u;y=r&-4;v=r&3;J=r&-8;G=r&7;w=r-1|0;L=(g|0)==2&(r|0)==1;H=Q(k-j|0,s);z=(Q(x-c|0,h)<<2)+f|0;C=F;u=b;while(1){j=(b|0)==(u|0)?I:s;k=d-u|0;q=j>>>0>>0?j:k;k=s-j|0;l=C<<2;j=K[l+(K[a+24>>2]+(Q(K[a+16>>2],A)<<2)|0)>>2];b:{c:{d:{e:{f:{g:{if(i){h:{i:{j:{k:{if(j){l=((H<<2)+j|0)+(k<<2)|0;j=u-b|0;if((g|0)==1){break h}m=(Q(g,j)<<2)+z|0;if((q|0)==1){break i}if(L){break j}if((g|0)!=8|q>>>0<=7){break k}if(!r){break b}o=q&-4;k=0;while(1){j=0;while(1){K[(j<<5)+m>>2]=K[(j<<2)+l>>2];n=j|1;K[(n<<5)+m>>2]=K[(n<<2)+l>>2];n=j|2;K[(n<<5)+m>>2]=K[(n<<2)+l>>2];n=j|3;K[(n<<5)+m>>2]=K[(n<<2)+l>>2];j=j+4|0;if(o>>>0>j>>>0){continue}break}if(j>>>0>>0){while(1){K[(j<<5)+m>>2]=K[(j<<2)+l>>2];j=j+1|0;if((q|0)!=(j|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;k=k+1|0;if((r|0)!=(k|0)){continue}break}break b}if((g|0)!=1){if(!r){break b}p=q&-4;n=q&3;l=(Q(u-b|0,g)<<2)+z|0;o=0;while(1){l:{if(!q){break l}m=0;j=0;k=0;if(q>>>0>=4){while(1){K[(Q(g,j)<<2)+l>>2]=0;K[(Q(j|1,g)<<2)+l>>2]=0;K[(Q(j|2,g)<<2)+l>>2]=0;K[(Q(j|3,g)<<2)+l>>2]=0;j=j+4|0;k=k+4|0;if((p|0)!=(k|0)){continue}break}}if(!n){break l}while(1){K[(Q(g,j)<<2)+l>>2]=0;j=j+1|0;m=m+1|0;if((n|0)!=(m|0)){continue}break}}l=(h<<2)+l|0;o=o+1|0;if((r|0)!=(o|0)){continue}break}break b}if(!r){break b}l=q<<2;k=(u-b<<2)+z|0;o=0;if(w>>>0>=7){break g}break f}if(!r){break b}D=q&-4;p=q&3;n=0;break c}j=0;k=q&-4;if(k){while(1){K[(j<<3)+m>>2]=K[(j<<2)+l>>2];o=j|1;K[(o<<3)+m>>2]=K[(o<<2)+l>>2];o=j|2;K[(o<<3)+m>>2]=K[(o<<2)+l>>2];o=j|3;K[(o<<3)+m>>2]=K[(o<<2)+l>>2];j=j+4|0;if(k>>>0>j>>>0){continue}break}}if(j>>>0>=q>>>0){break b}o=0;k=j;n=q-j&3;if(n){while(1){K[(k<<3)+m>>2]=K[(k<<2)+l>>2];k=k+1|0;o=o+1|0;if((n|0)!=(o|0)){continue}break}}if(j-q>>>0>4294967292){break b}while(1){K[(k<<3)+m>>2]=K[(k<<2)+l>>2];j=k+1|0;K[(j<<3)+m>>2]=K[(j<<2)+l>>2];j=k+2|0;K[(j<<3)+m>>2]=K[(j<<2)+l>>2];j=k+3|0;K[(j<<3)+m>>2]=K[(j<<2)+l>>2];k=k+4|0;if((q|0)!=(k|0)){continue}break}break b}if(!r){break b}k=0;if(w>>>0>=3){while(1){K[m>>2]=K[l>>2];j=h<<2;m=j+m|0;p=l;l=s<<2;o=p+l|0;K[m>>2]=K[o>>2];m=j+m|0;o=l+o|0;K[m>>2]=K[o>>2];m=j+m|0;o=l+o|0;K[m>>2]=K[o>>2];l=l+o|0;m=j+m|0;k=k+4|0;if((y|0)!=(k|0)){continue}break}}j=0;if(!v){break b}while(1){K[m>>2]=K[l>>2];l=(s<<2)+l|0;m=(h<<2)+m|0;j=j+1|0;if((v|0)!=(j|0)){continue}break}break b}j=(j<<2)+z|0;if((q|0)!=4){if(!r){break b}m=q<<2;o=0;if(w>>>0>=3){break e}break d}if(!r){break b}o=0;if(w>>>0>=3){while(1){k=K[l+4>>2];K[j>>2]=K[l>>2];K[j+4>>2]=k;k=K[l+12>>2];K[j+8>>2]=K[l+8>>2];K[j+12>>2]=k;k=l;l=s<<2;k=k+l|0;n=K[k+12>>2];m=h<<2;j=m+j|0;K[j+8>>2]=K[k+8>>2];K[j+12>>2]=n;n=K[k+4>>2];K[j>>2]=K[k>>2];K[j+4>>2]=n;k=l+k|0;n=K[k+12>>2];j=j+m|0;K[j+8>>2]=K[k+8>>2];K[j+12>>2]=n;n=K[k+4>>2];K[j>>2]=K[k>>2];K[j+4>>2]=n;k=l+k|0;n=K[k+12>>2];j=j+m|0;K[j+8>>2]=K[k+8>>2];K[j+12>>2]=n;n=K[k+4>>2];K[j>>2]=K[k>>2];K[j+4>>2]=n;l=l+k|0;j=j+m|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}m=0;if(!v){break b}while(1){k=K[l+4>>2];K[j>>2]=K[l>>2];K[j+4>>2]=k;k=K[l+12>>2];K[j+8>>2]=K[l+8>>2];K[j+12>>2]=k;l=(s<<2)+l|0;j=(h<<2)+j|0;m=m+1|0;if((v|0)!=(m|0)){continue}break}break b}if(!j){j=Ia(1,Q(K[a+8>>2],K[a+12>>2])<<2);if(!j){return 0}K[l+(K[a+24>>2]+(Q(K[a+16>>2],A)<<2)|0)>>2]=j}l=((H<<2)+j|0)+(k<<2)|0;j=u-b|0;m:{n:{o:{p:{q:{r:{if((g|0)!=1){m=(Q(g,j)<<2)+z|0;if((q|0)==1){break r}if((g|0)!=8|q>>>0<=7){break q}if(!r){break b}o=q&-4;k=0;while(1){j=0;while(1){K[(j<<2)+l>>2]=K[(j<<5)+m>>2];n=j|1;K[(n<<2)+l>>2]=K[(n<<5)+m>>2];n=j|2;K[(n<<2)+l>>2]=K[(n<<5)+m>>2];n=j|3;K[(n<<2)+l>>2]=K[(n<<5)+m>>2];j=j+4|0;if(o>>>0>j>>>0){continue}break}if(j>>>0>>0){while(1){K[(j<<2)+l>>2]=K[(j<<5)+m>>2];j=j+1|0;if((q|0)!=(j|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;k=k+1|0;if((r|0)!=(k|0)){continue}break}break b}j=(j<<2)+z|0;if((q|0)==4){break p}if(!r){break b}m=q<<2;o=0;if(w>>>0>=3){break o}break n}if(!r){break b}o=0;if(w>>>0>=3){while(1){K[l>>2]=K[m>>2];j=s<<2;l=j+l|0;k=h<<2;m=k+m|0;K[l>>2]=K[m>>2];l=j+l|0;m=k+m|0;K[l>>2]=K[m>>2];l=j+l|0;m=k+m|0;K[l>>2]=K[m>>2];l=j+l|0;m=k+m|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}j=0;if(!v){break b}while(1){K[l>>2]=K[m>>2];l=(s<<2)+l|0;m=(h<<2)+m|0;j=j+1|0;if((v|0)!=(j|0)){continue}break}break b}if(!r){break b}D=q&-4;p=q&3;n=0;break m}if(!r){break b}o=0;if(w>>>0>=3){while(1){k=K[j+4>>2];K[l>>2]=K[j>>2];K[l+4>>2]=k;k=K[j+12>>2];K[l+8>>2]=K[j+8>>2];K[l+12>>2]=k;m=h<<2;j=m+j|0;n=K[j+12>>2];k=l;l=s<<2;k=k+l|0;K[k+8>>2]=K[j+8>>2];K[k+12>>2]=n;n=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=n;j=j+m|0;n=K[j+12>>2];k=l+k|0;K[k+8>>2]=K[j+8>>2];K[k+12>>2]=n;n=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=n;j=j+m|0;n=K[j+12>>2];k=l+k|0;K[k+8>>2]=K[j+8>>2];K[k+12>>2]=n;n=K[j+4>>2];K[k>>2]=K[j>>2];K[k+4>>2]=n;j=j+m|0;l=l+k|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}m=0;if(!v){break b}while(1){k=K[j+4>>2];K[l>>2]=K[j>>2];K[l+4>>2]=k;k=K[j+12>>2];K[l+8>>2]=K[j+8>>2];K[l+12>>2]=k;j=(h<<2)+j|0;l=(s<<2)+l|0;m=m+1|0;if((v|0)!=(m|0)){continue}break}break b}while(1){k=!m;if(!k){E(l,j,m)}p=j;j=h<<2;n=p+j|0;p=l;l=s<<2;p=p+l|0;if(!k){E(p,n,m)}n=j+n|0;p=l+p|0;if(!k){E(p,n,m)}n=j+n|0;p=l+p|0;if(!k){E(p,n,m)}j=j+n|0;l=l+p|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}k=0;if(!v){break b}while(1){if(m){E(l,j,m)}j=(h<<2)+j|0;l=(s<<2)+l|0;k=k+1|0;if((v|0)!=(k|0)){continue}break}break b}while(1){s:{if(!q){break s}k=0;j=0;o=0;if(q>>>0>=4){while(1){K[(j<<2)+l>>2]=K[(Q(g,j)<<2)+m>>2];t=j|1;K[(t<<2)+l>>2]=K[(Q(g,t)<<2)+m>>2];t=j|2;K[(t<<2)+l>>2]=K[(Q(g,t)<<2)+m>>2];t=j|3;K[(t<<2)+l>>2]=K[(Q(g,t)<<2)+m>>2];j=j+4|0;o=o+4|0;if((D|0)!=(o|0)){continue}break}}if(!p){break s}while(1){K[(j<<2)+l>>2]=K[(Q(g,j)<<2)+m>>2];j=j+1|0;k=k+1|0;if((p|0)!=(k|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;n=n+1|0;if((r|0)!=(n|0)){continue}break}break b}while(1){j=!l;if(!j){B(k,0,l)}p=k;k=h<<2;m=p+k|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}m=k+m|0;if(!j){B(m,0,l)}k=k+m|0;o=o+8|0;if((J|0)!=(o|0)){continue}break}}j=0;if(!G){break b}while(1){if(l){B(k,0,l)}k=(h<<2)+k|0;j=j+1|0;if((G|0)!=(j|0)){continue}break}break b}while(1){k=!m;if(!k){E(j,l,m)}p=l;l=s<<2;n=p+l|0;p=j;j=h<<2;p=p+j|0;if(!k){E(p,n,m)}n=l+n|0;p=j+p|0;if(!k){E(p,n,m)}n=l+n|0;p=j+p|0;if(!k){E(p,n,m)}l=l+n|0;j=j+p|0;o=o+4|0;if((y|0)!=(o|0)){continue}break}}k=0;if(!v){break b}while(1){if(m){E(j,l,m)}l=(s<<2)+l|0;j=(h<<2)+j|0;k=k+1|0;if((v|0)!=(k|0)){continue}break}break b}while(1){t:{if(!q){break t}k=0;j=0;o=0;if(q>>>0>=4){while(1){K[(Q(g,j)<<2)+m>>2]=K[(j<<2)+l>>2];t=j|1;K[(Q(t,g)<<2)+m>>2]=K[(t<<2)+l>>2];t=j|2;K[(Q(t,g)<<2)+m>>2]=K[(t<<2)+l>>2];t=j|3;K[(Q(t,g)<<2)+m>>2]=K[(t<<2)+l>>2];j=j+4|0;o=o+4|0;if((D|0)!=(o|0)){continue}break}}if(!p){break t}while(1){K[(Q(g,j)<<2)+m>>2]=K[(j<<2)+l>>2];j=j+1|0;k=k+1|0;if((p|0)!=(k|0)){continue}break}}l=(s<<2)+l|0;m=(h<<2)+m|0;n=n+1|0;if((r|0)!=(n|0)){continue}break}}C=C+1|0;u=q+u|0;if(u>>>0>>0){continue}break}A=A+1|0;x=r+x|0;if(x>>>0>>0){continue}break}}return 1}function Uc(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;h=ra-240|0;ra=h;r=1;a:{if(K[K[a>>2]+60>>2]|K[a+128>>2]){break a}b:{k=K[a+116>>2];c:{if(!k){d=K[a+120>>2];break c}f=K[b+16>>2];g=M[k+4>>1];d=K[a+120>>2];if(!(!d|!K[d+12>>2])){f=L[d+18|0]}d:{if(g){k=K[k>>2];while(1){i=k+Q(e,6)|0;j=M[i>>1];if(j>>>0>=f>>>0){K[h+180>>2]=f;K[h+176>>2]=j;Fa(c,1,13678,h+176|0);r=0;break a}e:{i=M[i+4>>1];if(!i|(i|0)==65535){break e}i=i-1|0;if(i>>>0>>0){break e}K[h+164>>2]=f;K[h+160>>2]=i;Fa(c,1,13678,h+160|0);r=0;break a}e=e+1|0;if((g|0)!=(e|0)){continue}break}break d}if(f){break b}break c}while(1){f=f-1|0;e=0;while(1){if(M[k+Q(e,6)>>1]!=(f|0)){e=e+1|0;if((g|0)!=(e|0)){continue}break b}break}if(f){continue}break}}f:{if(!d){break f}k=K[d+12>>2];if(!k){break f}g:{d=L[d+18|0];h:{if(d){e=0;j=1;while(1){g=K[b+16>>2];f=M[k+(e<<2)>>1];if(g>>>0<=f>>>0){K[h+148>>2]=g;K[h+144>>2]=f;Fa(c,1,13678,h+144|0);j=0}e=e+1|0;if((d|0)!=(e|0)){continue}break}g=Ia(d,4);if(!g){break h}e=0;while(1){f=k+(e<<2)|0;i=L[f+2|0];i:{if(i>>>0>=2){K[h+68>>2]=i;K[h+64>>2]=e;Fa(c,1,12057,h- -64|0);j=0;break i}f=L[f+3|0];if(f>>>0>=d>>>0){K[h+128>>2]=f;Fa(c,1,12001,h+128|0);j=0;break i}l=(i|0)!=1;m=(f<<2)+g|0;if(!(l|!K[m>>2])){K[h+80>>2]=f;Fa(c,1,11490,h+80|0);j=0;break i}if(!(i|!f)){K[h+100>>2]=f;K[h+96>>2]=e;Fa(c,1,11864,h+96|0);j=0;break i}if(!(l|(e|0)==(f|0))){K[h+120>>2]=f;K[h+116>>2]=e;K[h+112>>2]=e;Fa(c,1,11900,h+112|0);j=0;break i}K[m>>2]=1}e=e+1|0;if((d|0)!=(e|0)){continue}break}j=!j;e=0;while(1){j:{f=e<<2;if(L[(f+k|0)+2|0]?K[f+g>>2]:1){e=e+1|0;if((d|0)!=(e|0)){continue}if(j&1){break j}if(K[b+16>>2]!=1){break g}e=0;while(1){if(K[(e<<2)+g>>2]){e=e+1|0;if((d|0)!=(e|0)){continue}break g}break}i=0;Fa(c,2,9216,0);e=0;if(d>>>0>=4){j=d&252;f=0;while(1){m=k+(e<<2)|0;I[m+3|0]=e;I[m+2|0]=1;m=e|1;l=k+(m<<2)|0;I[l+3|0]=m;I[l+2|0]=1;m=e|2;l=k+(m<<2)|0;I[l+3|0]=m;I[l+2|0]=1;m=e|3;l=k+(m<<2)|0;I[l+3|0]=m;I[l+2|0]=1;e=e+4|0;f=f+4|0;if((j|0)!=(f|0)){continue}break}}d=d&3;if(!d){break g}while(1){f=k+(e<<2)|0;I[f+3|0]=e;I[f+2|0]=1;e=e+1|0;i=i+1|0;if((d|0)!=(i|0)){continue}break}break g}K[h+48>>2]=e;j=1;Fa(c,1,11064,h+48|0);e=e+1|0;if((d|0)!=(e|0)){continue}}break}Ga(g);r=0;break a}g=Ia(d,4);if(g){break g}}r=0;Fa(c,1,12248,0);break a}Ga(g)}d=K[a+120>>2];k:{if(!d){break k}t=K[d+12>>2];if(!t){Ga(K[d+4>>2]);Ga(K[K[a+120>>2]+8>>2]);Ga(K[K[a+120>>2]>>2]);d=K[a+120>>2];g=K[d+12>>2];if(g){Ga(g);d=K[a+120>>2]}Ga(d);K[a+120>>2]=0;break k}m=K[b+24>>2];l:{k=L[d+18|0];m:{if(k){v=K[d>>2];j=K[d+4>>2];l=K[d+8>>2];e=0;n:{while(1){if(K[(m+Q(M[t+(e<<2)>>1],52)|0)+44>>2]){e=e+1|0;if((k|0)!=(e|0)){continue}break n}break}K[h+32>>2]=e;Fa(c,1,13840,h+32|0);r=0;break a}g=Ja(Q(k,52));if(!g){break m}i=0;while(1){d=t+(i<<2)|0;e=M[d>>1];f=Q(L[d+2|0]?L[d+3|0]:i,52)+g|0;d=m+Q(e,52)|0;e=K[d+4>>2];K[f>>2]=K[d>>2];K[f+4>>2]=e;K[f+48>>2]=K[d+48>>2];e=K[d+44>>2];K[f+40>>2]=K[d+40>>2];K[f+44>>2]=e;e=K[d+36>>2];K[f+32>>2]=K[d+32>>2];K[f+36>>2]=e;e=K[d+28>>2];K[f+24>>2]=K[d+24>>2];K[f+28>>2]=e;e=K[d+20>>2];K[f+16>>2]=K[d+16>>2];K[f+20>>2]=e;e=K[d+12>>2];K[f+8>>2]=K[d+8>>2];K[f+12>>2]=e;f=Q(i,52)+g|0;d=Ma(Q(K[d+8>>2],K[d+12>>2])<<2);K[f+44>>2]=d;if(!d){if(i){a=i&65535;while(1){Ga(K[(Q(a,52)+g|0)-8>>2]);a=a-1|0;if(a){continue}break}}Ga(g);r=0;Fa(c,1,13788,0);break a}K[f+24>>2]=L[i+l|0];K[f+32>>2]=L[i+j|0];i=i+1|0;if((k|0)!=(i|0)){continue}break}u=M[K[a+120>>2]+16>>1];n=u-1|0;while(1){d=Q(o,52)+g|0;i=Q(K[d+12>>2],K[d+8>>2]);f=t+(o<<2)|0;e=K[(m+Q(M[f>>1],52)|0)+44>>2];o:{if(!L[f+2|0]){if(!i){break o}l=K[d+44>>2];j=0;f=0;if(i>>>0>=4){q=i&-4;d=0;while(1){p=f<<2;K[p+l>>2]=K[e+p>>2];s=p|4;K[s+l>>2]=K[e+s>>2];s=p|8;K[s+l>>2]=K[e+s>>2];p=p|12;K[p+l>>2]=K[e+p>>2];f=f+4|0;d=d+4|0;if((q|0)!=(d|0)){continue}break}}d=i&3;if(!d){break o}while(1){i=f<<2;K[i+l>>2]=K[e+i>>2];f=f+1|0;j=j+1|0;if((d|0)!=(j|0)){continue}break}break o}if(!i){break o}d=L[f+3|0];j=(d<<2)+v|0;l=K[(Q(d,52)+g|0)+44>>2];f=0;if((i|0)!=1){s=i&-2;d=0;while(1){q=f<<2;p=K[q+e>>2];K[l+q>>2]=K[j+(Q(k,(p|0)>=0?(p|0)<(u|0)?p:n:0)<<2)>>2];q=q|4;p=K[q+e>>2];K[l+q>>2]=K[j+(Q(k,(p|0)>=0?(p|0)<(u|0)?p:n:0)<<2)>>2];f=f+2|0;d=d+2|0;if((s|0)!=(d|0)){continue}break}}if(!(i&1)){break o}f=f<<2;d=K[f+e>>2];K[f+l>>2]=K[j+(Q(k,(d|0)>=0?(d|0)<(u|0)?d:n:0)<<2)>>2]}o=o+1|0;if((k|0)!=(o|0)){continue}break}break l}g=Ja(Q(k,52));if(g){break l}}r=0;Fa(c,1,13788,0);break a}d=K[b+16>>2];if(d){e=0;while(1){f=K[(m+Q(e,52)|0)+44>>2];if(f){Ga(f)}e=e+1|0;if((d|0)!=(e|0)){continue}break}}Ga(m);K[b+16>>2]=k;K[b+24>>2]=g}e=K[a+116>>2];if(!e){break a}j=K[e>>2];l=M[e+4>>1];if(l){t=j+6|0;e=0;u=l-2&65535;i=1;while(1){d=K[b+16>>2];p=Q(e,6)+j|0;f=M[p>>1];p:{if(d>>>0<=f>>>0){K[h+20>>2]=d;K[h+16>>2]=f;Fa(c,2,7297,h+16|0);break p}g=M[p+4>>1];if((g+1&65535)>>>0<=1){J[(K[b+24>>2]+Q(f,52)|0)+48>>1]=M[p+2>>1];break p}k=g-1|0;m=k&65535;if(m>>>0>=d>>>0){K[h+4>>2]=d;K[h>>2]=m;Fa(c,2,7256,h);break p}q:{if(M[p+2>>1]|(f|0)==(m|0)){break q}g=K[b+24>>2];d=g+Q(f,52)|0;K[h+232>>2]=K[d+48>>2];n=K[d+44>>2];K[h+224>>2]=K[d+40>>2];K[h+228>>2]=n;n=K[d+36>>2];K[h+216>>2]=K[d+32>>2];K[h+220>>2]=n;n=K[d+28>>2];K[h+208>>2]=K[d+24>>2];K[h+212>>2]=n;n=K[d+20>>2];K[h+200>>2]=K[d+16>>2];K[h+204>>2]=n;n=K[d+12>>2];K[h+192>>2]=K[d+8>>2];K[h+196>>2]=n;n=K[d+4>>2];K[h+184>>2]=K[d>>2];K[h+188>>2]=n;n=Q(m,52);g=n+g|0;K[d+48>>2]=K[g+48>>2];o=K[g+44>>2];K[d+40>>2]=K[g+40>>2];K[d+44>>2]=o;o=K[g+36>>2];K[d+32>>2]=K[g+32>>2];K[d+36>>2]=o;o=K[g+28>>2];K[d+24>>2]=K[g+24>>2];K[d+28>>2]=o;o=K[g+20>>2];K[d+16>>2]=K[g+16>>2];K[d+20>>2]=o;o=K[g+12>>2];K[d+8>>2]=K[g+8>>2];K[d+12>>2]=o;o=K[g+4>>2];K[d>>2]=K[g>>2];K[d+4>>2]=o;g=K[h+188>>2];d=n+K[b+24>>2]|0;K[d>>2]=K[h+184>>2];K[d+4>>2]=g;K[d+48>>2]=K[h+232>>2];g=K[h+228>>2];K[d+40>>2]=K[h+224>>2];K[d+44>>2]=g;g=K[h+220>>2];K[d+32>>2]=K[h+216>>2];K[d+36>>2]=g;g=K[h+212>>2];K[d+24>>2]=K[h+208>>2];K[d+28>>2]=g;g=K[h+204>>2];K[d+16>>2]=K[h+200>>2];K[d+20>>2]=g;g=K[h+196>>2];K[d+8>>2]=K[h+192>>2];K[d+12>>2]=g;if(l>>>0<=e+1>>>0){break q}g=i;if(!(e-l&1)){g=k;d=Q(i,6)+j|0;n=M[d>>1];r:{if((n|0)!=(f|0)){g=f;if((n|0)!=(m|0)){break r}}J[d>>1]=g}g=i+1|0}if((u|0)==(e&65535)){break q}while(1){d=k;n=Q(g,6);o=n+j|0;q=M[o>>1];s:{if((q|0)!=(f|0)){d=f;if((m|0)!=(q|0)){break s}}J[o>>1]=d}d=k;n=n+t|0;o=M[n>>1];t:{if((o|0)!=(f|0)){d=f;if((m|0)!=(o|0)){break t}}J[n>>1]=d}g=g+2|0;if((l|0)!=(g&65535)){continue}break}}J[(K[b+24>>2]+Q(f,52)|0)+48>>1]=M[p+2>>1]}i=i+1|0;e=e+1|0;if((l|0)!=(e|0)){continue}break}e=K[a+116>>2];j=K[e>>2]}if(j){Ga(j);e=K[a+116>>2]}Ga(e);K[a+116>>2]=0;break a}r=0;Fa(c,1,9462,0)}ra=h+240|0;return r}function dd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=R(0);k=ra-48|0;ra=k;K[a+8>>2]=1;a:{b:{d=k+40|0;c:{if((Na(b,d,2,c)|0)!=2){break c}Ha(d,k+44|0,2);if(K[k+44>>2]!=65359){break c}K[a+8>>2]=2;d=K[b+56>>2];e=d-2|0;d=K[b+60>>2]-(d>>>0<2)|0;g=K[a+224>>2];K[g>>2]=e;K[g+4>>2]=d;K[k+16>>2]=e;K[k+20>>2]=d;Fa(c,4,12732,k+16|0);f=K[a+224>>2];j=K[f>>2];e=K[f+24>>2];d=e+1|0;g=K[f+32>>2];if(d>>>0<=g>>>0){g=K[f+28>>2];break b}o=R(R(g>>>0)+R(100));if(o=R(0)){d=~~o>>>0}else{d=0}K[f+32>>2]=d;g=La(K[f+28>>2],Q(d,24));if(g){K[f+28>>2]=g;e=K[f+24>>2];d=e+1|0;break b}Ga(K[f+28>>2]);K[f+32>>2]=0;K[f+24>>2]=0;K[f+28>>2]=0;Fa(c,1,3862,0)}Fa(c,1,15619,0);a=0;break a}e=Q(e,24)+g|0;K[e+16>>2]=2;K[e+8>>2]=j;K[e+12>>2]=j>>31;J[e>>1]=65359;K[f+24>>2]=d;if((Na(b,K[a+16>>2],2,c)|0)!=2){Fa(c,1,2435,0);a=0;break a}Ha(K[a+16>>2],k+40|0,2);d:{e:{g=K[k+40>>2];if((g|0)!=65424){while(1){e=24864;if(g>>>0<=65279){K[k>>2]=g;Fa(c,1,2231,k);a=0;break a}while(1){d=e;f=K[d>>2];if(f){e=d+12|0;if((f|0)!=(g|0)){continue}}break}f:{g:{if(f){break g}h=2;Fa(c,2,3810,0);e=2435;h:{i:{if((Na(b,K[a+16>>2],2,c)|0)!=2){break i}while(1){Ha(K[a+16>>2],k+44|0,2);f=24864;g=K[k+44>>2];if(g>>>0>=65280){while(1){d=f;i=K[d>>2];if(i){f=d+12|0;if((g|0)!=(i|0)){continue}}break}if(!(K[d+4>>2]&K[a+8>>2])){e=5360;break i}if(i){if((i|0)==65424){K[k+40>>2]=65424;break f}j=K[b+56>>2];f=K[a+224>>2];d=K[f+24>>2];g=d+1|0;e=K[f+32>>2];if(g>>>0<=e>>>0){e=K[f+28>>2];break h}o=R(R(e>>>0)+R(100));if(o=R(0)){d=~~o>>>0}else{d=0}K[f+32>>2]=d;e=La(K[f+28>>2],Q(d,24));if(e){K[f+28>>2]=e;d=K[f+24>>2];g=d+1|0;break h}Ga(K[f+28>>2]);K[f+32>>2]=0;K[f+24>>2]=0;K[f+28>>2]=0;e=3862;break i}h=h+2|0}if((Na(b,K[a+16>>2],2,c)|0)==2){continue}break}}Fa(c,1,e,0);Fa(c,1,9810,0);a=0;break a}d=Q(d,24)+e|0;K[d+16>>2]=h;e=j-h|0;K[d+8>>2]=e;K[d+12>>2]=e>>31;J[d>>1]=0;K[f+24>>2]=g;K[k+40>>2]=i;g=24864;while(1){d=g;f=K[d>>2];if(!f){break g}g=d+12|0;if((f|0)!=(i|0)){continue}break}}if(!(K[d+4>>2]&K[a+8>>2])){Fa(c,1,5360,0);a=0;break a}if((Na(b,K[a+16>>2],2,c)|0)!=2){Fa(c,1,2435,0);a=0;break a}Ha(K[a+16>>2],k+36|0,2);e=K[k+36>>2];if(e>>>0<=1){Fa(c,1,6037,0);a=0;break a}e=e-2|0;K[k+36>>2]=e;g=K[a+16>>2];if(N[a+20>>2]>>0){g=La(g,e);if(!g){Ga(K[a+16>>2]);K[a+16>>2]=0;K[a+20>>2]=0;Fa(c,1,4936,0);a=0;break a}K[a+16>>2]=g;e=K[k+36>>2];K[a+20>>2]=e}e=Na(b,g,e,c);if((e|0)!=K[k+36>>2]){Fa(c,1,2435,0);a=0;break a}if(!(va[K[d+8>>2]](a,K[a+16>>2],e,c)|0)){Fa(c,1,2453,0);a=0;break a}j=K[b+56>>2];i=K[k+36>>2];d=K[a+224>>2];h=K[d+24>>2];e=h+1|0;g=K[d+32>>2];j:{if(e>>>0<=g>>>0){g=K[d+28>>2];break j}o=R(R(g>>>0)+R(100));if(o=R(0)){e=~~o>>>0}else{e=0}K[d+32>>2]=e;g=La(K[d+28>>2],Q(e,24));if(!g){break d}K[d+28>>2]=g;h=K[d+24>>2];e=h+1|0}g=Q(h,24)+g|0;K[g+16>>2]=i+4;j=(j-i|0)-4|0;K[g+8>>2]=j;K[g+12>>2]=j>>31;J[g>>1]=f;K[d+24>>2]=e;if((Na(b,K[a+16>>2],2,c)|0)!=2){Fa(c,1,2435,0);a=0;break a}m=(f|0)==65372?1:m;l=(f|0)==65362?1:l;n=(f|0)==65361?1:n;Ha(K[a+16>>2],k+40|0,2);g=K[k+40>>2];if((g|0)!=65424){continue}}break}if(n){break e}}Fa(c,1,4748,0);a=0;break a}if(!l){Fa(c,1,4794,0);a=0;break a}if(!m){Fa(c,1,4840,0);a=0;break a}d=0;e=0;h=0;j=ra-16|0;ra=j;m=1;k:{if(!(I[a+212|0]&1)){break k}l:{f=K[a+136>>2];if(!f){break l}m:{while(1){g=K[a+140>>2]+(h<<3)|0;i=K[g>>2];if(i){l=K[g+4>>2];g=d-l|0;g=d>>>0>=g>>>0?g:0;if(d>>>0>>0){f=l-d|0;l=d+i|0;while(1){if(f>>>0<4){d=5634;break m}Ha(l,j+12|0,4);d=K[j+12>>2];if((d^-1)>>>0>>0){d=5608;break m}i=f-4|0;n=i>>>0>>0;g=n?d-i|0:g;e=d+e|0;f=i-d|0;l=((n?0:d)+l|0)+4|0;if(d>>>0>>0){continue}break}f=K[a+136>>2]}d=g}h=h+1|0;if(h>>>0>>0){continue}break}if(!d){break l}m=0;Fa(c,1,3030,0);break k}m=0;Fa(c,1,d,0);break k}d=Ja(e);K[a+160>>2]=d;if(!d){m=0;Fa(c,1,4300,0);break k}K[a+148>>2]=e;h=K[a+140>>2];n:{f=K[a+136>>2];if(f){e=0;d=0;g=0;while(1){i=g<<3;n=i+h|0;l=K[n>>2];if(l){h=K[a+160>>2]+d|0;f=K[n+4>>2];o:{if(f>>>0<=e>>>0){if(f){E(h,l,f)}d=d+f|0;e=e-f|0;break o}if(e){E(h,l,e)}d=d+e|0;h=f-e|0;e=e+l|0;while(1){if(h>>>0<4){break n}Ha(e,j+8|0,4);e=e+4|0;l=K[a+160>>2]+d|0;f=h-4|0;h=K[j+8>>2];if(f>>>0>>0){if(f){E(l,e,f)}d=d+f|0;e=K[j+8>>2]-f|0;break o}if(h){E(l,e,h)}h=K[j+8>>2];d=h+d|0;e=e+h|0;h=f-h|0;if(h){continue}break}e=0}Ga(K[i+K[a+140>>2]>>2]);h=K[a+140>>2];f=i+h|0;K[f>>2]=0;K[f+4>>2]=0;f=K[a+136>>2]}g=g+1|0;if(g>>>0>>0){continue}break}e=K[a+148>>2];d=K[a+160>>2]}K[a+168>>2]=e;K[a+144>>2]=d;K[a+136>>2]=0;Ga(h);K[a+140>>2]=0;break k}m=0;Fa(c,1,5634,0)}ra=j+16|0;if(!m){Fa(c,1,8048,0);a=0;break a}Fa(c,4,11717,0);d=K[a+224>>2];e=K[b+56>>2];e=e-2|0;K[d+8>>2]=e;K[d+12>>2]=0;b=0;h=0;l=ra-16|0;ra=l;g=K[a+68>>2];p:{if(!g){K[a+76>>2]=1;break p}if(K[a+76>>2]){break p}d=K[a+72>>2];j=K[a+224>>2];e=K[j+40>>2];if((g|0)!=1){m=g&-2;while(1){i=(b<<3)+d|0;n=M[i>>1];f=e+Q(n,40)|0;K[f>>2]=n;K[f+8>>2]=K[f+8>>2]+1;i=M[i+8>>1];f=e+Q(i,40)|0;K[f>>2]=i;K[f+8>>2]=K[f+8>>2]+1;b=b+2|0;h=h+2|0;if((m|0)!=(h|0)){continue}break}}if(g&1){f=M[(b<<3)+d>>1];b=e+Q(f,40)|0;K[b>>2]=f;K[b+8>>2]=K[b+8>>2]+1}f=K[j+36>>2];q:{if(f){b=0;while(1){if(!K[(e+Q(b,40)|0)+8>>2]){K[l>>2]=b;Fa(c,1,9267,l);break q}b=b+1|0;if((f|0)!=(b|0)){continue}break}}f=K[j+8>>2];b=K[j+12>>2];e=0;while(1){r:{i=e<<3;m=K[K[a+224>>2]+40>>2]+Q(M[i+d>>1],40)|0;h=K[m+16>>2];if(!h){h=Ia(K[m+8>>2],24);K[m+16>>2]=h;if(!h){break r}g=K[a+68>>2];d=K[a+72>>2]}n=h;h=K[m+4>>2];j=n+Q(h,24)|0;K[j>>2]=f;K[j+4>>2]=b;i=K[(d+i|0)+4>>2];f=i+f|0;K[j+16>>2]=f;b=f>>>0>>0?b+1|0:b;K[j+20>>2]=b;K[m+4>>2]=h+1;e=e+1|0;if(e>>>0>>0){continue}break p}break}Fa(c,1,6845,0)}K[a+76>>2]=1;if(!K[a+68>>2]){break p}d=K[K[a+224>>2]+40>>2];b=0;while(1){c=Q(M[K[a+72>>2]+(b<<3)>>1],40);d=c+d|0;K[d+8>>2]=0;Ga(K[d+16>>2]);d=K[K[a+224>>2]+40>>2];K[(c+d|0)+16>>2]=0;b=b+1|0;if(b>>>0>2]){continue}break}}ra=l+16|0;K[a+8>>2]=8;a=1;break a}Ga(K[d+28>>2]);K[d+32>>2]=0;K[d+24>>2]=0;K[d+28>>2]=0;Fa(c,1,3862,0);a=0}ra=k+48|0;return a|0}function ze(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;f=ra-160|0;ra=f;a:{if(c>>>0<=35){c=0;Fa(d,1,6058,0);break a}c=c-36|0;h=(c>>>0)/3|0;if((Q(h,3)|0)!=(c|0)){c=0;Fa(d,1,6058,0);break a}j=K[a+96>>2];c=f+156|0;Ha(b,c,2);J[a+104>>1]=K[f+156>>2];Ha(b+2|0,j+8|0,4);Ha(b+6|0,j+12|0,4);Ha(b+10|0,j,4);Ha(b+14|0,j+4|0,4);Ha(b+18|0,a+116|0,4);Ha(b+22|0,a+120|0,4);Ha(b+26|0,a+108|0,4);Ha(b+30|0,a+112|0,4);Ha(b+34|0,c,2);b:{c:{d:{c=K[f+156>>2];if(c>>>0<=16384){K[j+16>>2]=c;if((c|0)!=(h|0)){K[f+132>>2]=h;K[f+128>>2]=c;Fa(d,1,14943,f+128|0);c=0;break a}c=K[j+4>>2];g=K[j+12>>2];l=K[j+8>>2];e=K[j>>2];if(!(c>>>0>>0&l>>>0>e>>>0)){K[f+120>>2]=g-c;K[f+124>>2]=0-(c>>>0>g>>>0);K[f+112>>2]=l-e;K[f+116>>2]=0-(e>>>0>l>>>0);Fa(d,1,14505,f+112|0);c=0;break a}i=K[a+116>>2];k=K[a+120>>2];if(!(k?i:0)){K[f+4>>2]=k;K[f>>2]=i;Fa(d,1,15057,f);c=0;break a}e:{n=K[a+108>>2];f:{if(n>>>0>e>>>0){break f}i=i+n|0;if(e>>>0>=(i>>>0>>0?-1:i)>>>0){break f}i=K[a+112>>2];if(i>>>0>c>>>0){break f}k=i+k|0;if(c>>>0<(i>>>0>k>>>0?-1:k)>>>0){break e}}c=0;Fa(d,1,2755,0);break a}g:{if(K[a+248>>2]){break g}i=K[a+240>>2];if(!i){break g}k=K[a+244>>2];if(!k){break g}e=l-e|0;c=g-c|0;if((e|0)==(i|0)&(c|0)==(k|0)){break g}K[f+108>>2]=c;K[f+104>>2]=e;K[f+100>>2]=k;K[f+96>>2]=i;Fa(d,1,13969,f+96|0);c=0;break a}e=Ia(h,52);K[j+24>>2]=e;if(!e){break d}h:{if(!K[j+16>>2]){break h}c=f+152|0;Ha(b+36|0,c,1);h=K[f+152>>2];k=h>>>7|0;K[e+32>>2]=k;n=(h&127)+1|0;K[e+24>>2]=n;l=K[a+248>>2];Ha(b+37|0,c,1);K[e>>2]=K[f+152>>2];Ha(b+38|0,c,1);g=K[f+152>>2];K[e+4>>2]=g;c=0;i=K[e>>2];if(i-256>>>0<4294967041){h=0;break b}h=0;if(g-256>>>0<4294967041){break b}g=K[e+24>>2];if(g>>>0>31){break c}K[e+36>>2]=0;K[e+40>>2]=K[a+184>>2];h=1;if(N[j+16>>2]<=1){break h}k=l?0:k;l=l?0:n;b=b+39|0;while(1){Ha(b,f+152|0,1);i=K[f+152>>2];g=i>>>7|0;K[e+84>>2]=g;i=(i&127)+1|0;K[e+76>>2]=i;if(!(K[a+248>>2]|(L[a+212|0]&4|(i|0)==(l|0)&(g|0)==(k|0)))){K[f+84>>2]=g;K[f+80>>2]=i;K[f+76>>2]=h;K[f+72>>2]=k;K[f+68>>2]=l;K[f+64>>2]=h;Fa(d,2,14741,f- -64|0)}g=f+152|0;Ha(b+1|0,g,1);K[e+52>>2]=K[f+152>>2];Ha(b+2|0,g,1);g=K[f+152>>2];K[e+56>>2]=g;i=K[e+52>>2];if(i-256>>>0<4294967041|g-256>>>0<=4294967040){break b}g=K[e+76>>2];if(g>>>0>=32){break c}b=b+3|0;K[e+88>>2]=0;K[e+92>>2]=K[a+184>>2];e=e+52|0;h=h+1|0;if(h>>>0>2]){continue}break}}c=0;h=K[a+116>>2];if(!h){break a}g=K[a+120>>2];if(!g){break a}l=0-!h|0;e=l;p=K[a+108>>2];k=K[j+8>>2]-p|0;i=h-1|0;b=k+i|0;e=k>>>0>b>>>0?e+1|0:e;b=Ne(b,e,h,0);K[a+128>>2]=b;n=0-!g|0;e=n;q=K[a+112>>2];o=K[j+12>>2]-q|0;m=o;k=g-1|0;o=o+k|0;e=m>>>0>o>>>0?e+1|0:e;e=Ne(o,e,g,0);K[a+132>>2]=e;i:{if(!(!b|!e)){if(b>>>0<=65535/(e>>>0)>>>0){break i}}K[f+20>>2]=e;K[f+16>>2]=b;Fa(d,1,14083,f+16|0);break a}o=Q(b,e);j:{if(L[a+92|0]&2){K[a+28>>2]=(K[a+28>>2]-p>>>0)/(h>>>0);K[a+32>>2]=(K[a+32>>2]-q>>>0)/(g>>>0);e=l;b=K[a+36>>2]-p|0;m=b;b=b+i|0;e=m>>>0>b>>>0?e+1|0:e;v=a,w=Ne(b,e,h,0),K[v+36>>2]=w;e=n;b=K[a+40>>2]-q|0;m=b;b=b+k|0;e=m>>>0>b>>>0?e+1|0:e;v=a,w=Ne(b,e,g,0),K[v+40>>2]=w;break j}K[a+40>>2]=e;K[a+36>>2]=b;K[a+28>>2]=0;K[a+32>>2]=0}b=Ia(o,5644);K[a+180>>2]=b;if(!b){Fa(d,1,3898,0);break a}b=Ia(K[j+16>>2],1080);K[K[a+12>>2]+5584>>2]=b;if(!K[K[a+12>>2]+5584>>2]){Fa(d,1,3898,0);break a}b=Ia(10,20);K[K[a+12>>2]+5616>>2]=b;b=K[a+12>>2];if(!K[b+5616>>2]){Fa(d,1,3898,0);break a}K[b+5624>>2]=10;b=Ia(10,20);K[K[a+12>>2]+5628>>2]=b;b=K[a+12>>2];if(!K[b+5628>>2]){Fa(d,1,3898,0);break a}K[b+5636>>2]=10;h=K[j+16>>2];k:{if(!h){break k}g=K[j+24>>2];b=0;if((h|0)!=1){l=h&-2;e=0;while(1){i=g+Q(b,52)|0;if(!K[i+32>>2]){K[(K[K[a+12>>2]+5584>>2]+Q(b,1080)|0)+1076>>2]=1<>2]-1}i=b|1;k=g+Q(i,52)|0;if(!K[k+32>>2]){K[(K[K[a+12>>2]+5584>>2]+Q(i,1080)|0)+1076>>2]=1<>2]-1}b=b+2|0;e=e+2|0;if((l|0)!=(e|0)){continue}break}}if(!(h&1)){break k}e=g+Q(b,52)|0;if(K[e+32>>2]){break k}K[(K[K[a+12>>2]+5584>>2]+Q(b,1080)|0)+1076>>2]=1<>2]-1}if(o){b=K[a+180>>2];e=0;while(1){h=Ia(K[j+16>>2],1080);K[b+5584>>2]=h;if(!h){Fa(d,1,3898,0);break a}b=b+5644|0;e=e+1|0;if(o>>>0>e>>>0){continue}break}}b=Q(K[a+132>>2],K[a+128>>2]);K[K[a+224>>2]+36>>2]=b;b=Ia(b,40);d=K[a+224>>2];K[d+40>>2]=b;e=0;l:{if(!b){break l}e=1;if(!K[d+36>>2]){break l}d=0;while(1){m:{e=0;g=Q(d,40);b=g+b|0;K[b+20>>2]=0;K[b+28>>2]=100;h=Ia(100,24);l=K[a+224>>2];b=K[l+40>>2];K[(g+b|0)+24>>2]=h;if(!h){break m}e=1;d=d+1|0;if(d>>>0>2]){continue}}break}}if(!e){break a}K[a+8>>2]=4;r=K[j+16>>2];if(r){b=K[a+112>>2];d=K[a+120>>2];c=b+Q(d,K[a+132>>2]-1|0)|0;d=c+d|0;c=c>>>0>d>>>0?-1:d;d=K[j+12>>2];c=c>>>0>>0?c:d;l=c-1|0;k=0-!c|0;c=K[a+108>>2];d=K[a+116>>2];a=c+Q(d,K[a+128>>2]-1|0)|0;d=a+d|0;a=a>>>0>d>>>0?-1:d;d=K[j+8>>2];a=a>>>0>>0?a:d;i=a-1|0;n=0-!a|0;a=K[j+4>>2];b=a>>>0>>0?b:a;o=b-1|0;p=0-!b|0;a=K[j>>2];b=a>>>0>>0?c:a;q=b-1|0;u=0-!b|0;a=K[j+24>>2];b=0;while(1){e=p;d=K[a+4>>2];c=d+o|0;j=Ne(c,c>>>0>>0?e+1|0:e,d,0);K[a+20>>2]=j;e=u;h=K[a>>2];c=h+q|0;s=Ne(c,c>>>0>>0?e+1|0:e,h,0);K[a+16>>2]=s;c=K[a+40>>2];g=c&31;if((c&63)>>>0>=32){e=-1<>>32-g}g=m^-1;e=e^-1;m=e;e=k;t=d+l|0;e=t>>>0>>0?e+1|0:e;e=Ne(t,e,d,0)-j|0;d=m;j=e;e=e+g|0;d=j>>>0>e>>>0?d+1|0:d;j=e;e=c&31;if((c&63)>>>0>=32){d=d>>>e|0}else{d=((1<>>e}K[a+12>>2]=d;e=n;d=h+i|0;e=d>>>0>>0?e+1|0:e;d=Ne(d,e,h,0)-s|0;e=m;d=d+g|0;e=d>>>0>>0?e+1|0:e;h=d;d=c&31;if((c&63)>>>0>=32){c=e>>>d|0}else{c=((1<>>d}K[a+8>>2]=c;a=a+52|0;b=b+1|0;if((r|0)!=(b|0)){continue}break}}c=1;break a}K[f+144>>2]=c;Fa(d,1,7895,f+144|0);c=0;break a}c=0;K[j+16>>2]=0;Fa(d,1,3898,0);break a}K[f+52>>2]=g;K[f+48>>2]=h;Fa(d,1,15365,f+48|0);break a}K[f+40>>2]=g;K[f+36>>2]=i;K[f+32>>2]=h;Fa(d,1,14303,f+32|0)}ra=f+160|0;return c|0}function Jc(a,b,c,d,e,f,g){var h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;j=ra+-64|0;ra=j;K[j+60>>2]=b;z=j+39|0;t=j+40|0;a:{b:{c:{d:{e:while(1){h=0;f:while(1){k=b;if((o^2147483647)<(h|0)){break d}o=h+o|0;g:{h:{i:{j:{h=b;i=L[h|0];if(i){while(1){k:{b=i&255;l:{if(!b){b=h;break l}if((b|0)!=37){break k}i=h;while(1){if(L[i+1|0]!=37){b=i;break l}h=h+1|0;n=L[i+2|0];b=i+2|0;i=b;if((n|0)==37){continue}break}}h=h-k|0;y=o^2147483647;if((h|0)>(y|0)){break d}if(a){Pa(a,k,h)}if(h){continue f}K[j+60>>2]=b;h=b+1|0;q=-1;i=I[b+1|0]-48|0;if(!(L[b+2|0]!=36|i>>>0>9)){x=1;q=i;h=b+3|0}K[j+60>>2]=h;l=0;i=I[h|0];b=i-32|0;m:{if(b>>>0>31){n=h;break m}n=h;b=1<>2]=n;l=b|l;i=I[h+1|0];b=i-32|0;if(b>>>0>=32){break m}h=n;b=1<>>0>9)){p:{if(!a){K[(b<<2)+e>>2]=10;b=0;break p}b=K[(b<<3)+d>>2]}p=b;b=n+3|0;i=1;break o}if(x){break j}b=n+1|0;if(!a){K[j+60>>2]=b;x=0;p=0;break n}h=K[c>>2];K[c>>2]=h+4;p=K[h>>2];i=0}x=i;K[j+60>>2]=b;if((p|0)>=0){break n}p=0-p|0;l=l|8192;break n}p=Ic(j+60|0);if((p|0)<0){break d}b=K[j+60>>2]}h=0;m=-1;u=0;q:{if(L[b|0]!=46){break q}if(L[b+1|0]==42){i=I[b+2|0]-48|0;r:{if(!(L[b+3|0]!=36|i>>>0>9)){b=b+4|0;s:{if(!a){K[(i<<2)+e>>2]=10;m=0;break s}m=K[(i<<3)+d>>2]}break r}if(x){break j}b=b+2|0;m=0;if(!a){break r}i=K[c>>2];K[c>>2]=i+4;m=K[i>>2]}K[j+60>>2]=b;u=(m|0)>=0;break q}K[j+60>>2]=b+1;m=Ic(j+60|0);b=K[j+60>>2];u=1}while(1){v=h;n=28;r=b;i=I[b|0];if(i-123>>>0<4294967238){break c}b=b+1|0;h=L[(i+Q(h,58)|0)+25215|0];if((h-1&255)>>>0<8){continue}break}K[j+60>>2]=b;t:{if((h|0)!=27){if(!h){break c}if((q|0)>=0){if(!a){K[(q<<2)+e>>2]=h;continue e}h=(q<<3)+d|0;i=K[h+4>>2];K[j+48>>2]=K[h>>2];K[j+52>>2]=i;break t}if(!a){break g}Hc(j+48|0,h,c,g);break t}if((q|0)>=0){break c}h=0;if(!a){continue f}}if(L[a|0]&32){break b}i=l&-65537;l=l&8192?i:l;q=0;w=1072;n=t;u:{v:{w:{x:{y:{z:{A:{B:{C:{D:{E:{F:{G:{H:{I:{J:{K:{r=L[r|0];h=r<<24>>24;h=v?(r&15)==3?h&-45:h:h;switch(h-88|0){case 0:case 32:break G;case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 10:case 16:case 18:case 19:case 20:case 21:case 25:case 26:case 28:case 30:case 31:break h;case 9:case 13:case 14:case 15:break u;case 11:break B;case 12:case 17:break E;case 22:break I;case 23:break F;case 24:break H;case 27:break A;case 29:break J;default:break K}}L:{switch(h-65|0){case 1:case 3:break h;case 0:case 4:case 5:case 6:break u;case 2:break z;default:break L}}if((h|0)==83){break y}break h}i=K[j+48>>2];r=K[j+52>>2];w=1072;break D}h=0;M:{switch(v|0){case 0:K[K[j+48>>2]>>2]=o;continue f;case 1:K[K[j+48>>2]>>2]=o;continue f;case 2:k=K[j+48>>2];K[k>>2]=o;K[k+4>>2]=o>>31;continue f;case 3:J[K[j+48>>2]>>1]=o;continue f;case 4:I[K[j+48>>2]]=o;continue f;case 6:K[K[j+48>>2]>>2]=o;continue f;case 7:break M;default:continue f}}k=K[j+48>>2];K[k>>2]=o;K[k+4>>2]=o>>31;continue f}m=m>>>0<=8?8:m;l=l|8;h=120}b=t;k=K[j+52>>2];r=k;i=K[j+48>>2];s=i;if(i|k){A=h&32;while(1){b=b-1|0;I[b|0]=A|L[(s&15)+25744|0];v=!k&s>>>0>15|(k|0)!=0;s=(k&15)<<28|s>>>4;k=k>>>4|0;if(v){continue}break}}k=b;if(!(l&8)|!(i|r)){break C}w=(h>>>4|0)+1072|0;q=2;break C}b=t;k=K[j+52>>2];r=k;i=K[j+48>>2];s=i;if(i|k){while(1){b=b-1|0;I[b|0]=s&7|48;v=!k&s>>>0>7|(k|0)!=0;s=(k&7)<<29|s>>>3;k=k>>>3|0;if(v){continue}break}}k=b;if(!(l&8)){break C}b=t-b|0;m=(b|0)<(m|0)?m:b+1|0;break C}i=K[j+48>>2];b=K[j+52>>2];r=b;if((b|0)<0){h=0-(b+((i|0)!=0)|0)|0;r=h;i=0-i|0;K[j+48>>2]=i;K[j+52>>2]=h;q=1;w=1072;break D}if(l&2048){q=1;w=1073;break D}q=l&1;w=q?1074:1072}k=fb(i,r,t)}if((m|0)<0&u){break d}l=u?l&-65537:l;if(!((i|r)!=0|m)){k=t;m=0;break h}b=!(i|r)+(t-k|0)|0;m=(b|0)<(m|0)?m:b;break h}h=L[j+48|0];break i}h=m>>>0>=2147483647?2147483647:m;l=h;n=(h|0)!=0;b=K[j+48>>2];k=b?b:1649;b=k;N:{O:{P:{Q:{if(!(b&3)|!h){break Q}while(1){if(!L[b|0]){break P}l=l-1|0;n=(l|0)!=0;b=b+1|0;if(!(b&3)){break Q}if(l){continue}break}}if(!n){break O}if(!(!L[b|0]|l>>>0<4)){while(1){n=K[b>>2];if(((16843008-n|n)&-2139062144)!=-2139062144){break P}b=b+4|0;l=l-4|0;if(l>>>0>3){continue}break}}if(!l){break O}}while(1){if(!L[b|0]){break N}b=b+1|0;l=l-1|0;if(l){continue}break}}b=0}b=b?b-k|0:h;n=b+k|0;if((m|0)>=0){l=i;m=b;break h}l=i;m=b;if(L[n|0]){break d}break h}h=K[j+48>>2];if(h|K[j+52>>2]){break x}h=0;break i}if(m){i=K[j+48>>2];break w}h=0;Ra(a,32,p,0,l);break v}K[j+12>>2]=0;K[j+8>>2]=h;i=j+8|0;K[j+48>>2]=i;m=-1}h=0;while(1){R:{k=K[i>>2];if(!k){break R}k=Gc(j+4|0,k);if((k|0)<0){break b}if(k>>>0>m-h>>>0){break R}i=i+4|0;h=h+k|0;if(m>>>0>h>>>0){continue}}break}n=61;if((h|0)<0){break c}Ra(a,32,p,h,l);if(!h){h=0;break v}n=0;i=K[j+48>>2];while(1){k=K[i>>2];if(!k){break v}m=j+4|0;k=Gc(m,k);n=k+n|0;if(n>>>0>h>>>0){break v}Pa(a,m,k);i=i+4|0;if(h>>>0>n>>>0){continue}break}}Ra(a,32,p,h,l^8192);h=(h|0)<(p|0)?p:h;continue f}if((m|0)<0&u){break d}n=61;h=va[f|0](a,P[j+48>>3],p,m,l,h)|0;if((h|0)>=0){continue f}break c}i=L[h+1|0];h=h+1|0;continue}}if(a){break a}if(!x){break g}h=1;while(1){a=K[(h<<2)+e>>2];if(a){Hc((h<<3)+d|0,a,c,g);o=1;h=h+1|0;if((h|0)!=10){continue}break a}break}if(h>>>0>=10){o=1;break a}while(1){if(K[(h<<2)+e>>2]){break j}o=1;h=h+1|0;if((h|0)!=10){continue}break}break a}n=28;break c}I[j+39|0]=h;m=1;k=z;l=i}i=n-k|0;m=(i|0)<(m|0)?m:i;if((m|0)>(q^2147483647)){break d}n=61;b=m+q|0;h=(b|0)<(p|0)?p:b;if((y|0)<(h|0)){break c}Ra(a,32,h,b,l);Pa(a,w,q);Ra(a,48,h,b,l^65536);Ra(a,48,m,i,0);Pa(a,k,i);Ra(a,32,h,b,l^8192);b=K[j+60>>2];continue}break}break}o=0;break a}n=61}K[6585]=n}o=-1}ra=j- -64|0;return o}function ud(a,b,c,d,e,f){a=a|0;b=+b;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0;n=ra-560|0;ra=n;K[n+44>>2]=0;A(+b);h=v(1)|0;v(0)|0;a:{if((h|0)<0){t=1;y=1082;b=-b;A(+b);h=v(1)|0;v(0)|0;break a}if(e&2048){t=1;y=1085;break a}t=e&1;y=t?1088:1083;z=!t}b:{if((h&2146435072)==2146435072){h=t+3|0;Ra(a,32,c,h,e&-65537);Pa(a,y,t);d=f&32;Pa(a,b!=b?d?1170:1398:d?1275:1439,3);Ra(a,32,c,h,e^8192);m=(c|0)>(h|0)?c:h;break b}w=n+16|0;c:{d:{e:{b=Fc(b,n+44|0);b=b+b;if(b!=0){h=K[n+44>>2];K[n+44>>2]=h-1;x=f|32;if((x|0)!=97){break e}break c}x=f|32;if((x|0)==97){break c}l=K[n+44>>2];break d}l=h-29|0;K[n+44>>2]=l;b=b*268435456}k=(d|0)<0?6:d;r=(n+48|0)+((l|0)>=0?288:0)|0;h=r;while(1){d=b<4294967295&b>=0?~~b>>>0:0;K[h>>2]=d;h=h+4|0;b=(b-+(d>>>0))*1e9;if(b!=0){continue}break}f:{if((l|0)<=0){i=l;g=h;j=r;break f}j=r;i=l;while(1){o=i>>>0>=29?29:i;g=h-4|0;g:{if(j>>>0>g>>>0){break g}p=0;while(1){q=0;d=K[g>>2];i=o&31;m=p;if((o&63)>>>0>=32){p=d<>>32-i;d=d<>>0>i>>>0?q+1|0:q;p=Ne(i,q,1e9,0);m=Le(p,ua,-1e9);d=q;q=i+m|0;K[g>>2]=q;g=g-4|0;if(j>>>0<=g>>>0){continue}break}if(!d&i>>>0<1e9){break g}j=j-4|0;K[j>>2]=p}while(1){g=h;if(j>>>0>>0){h=g-4|0;if(!K[h>>2]){continue}}break}i=K[n+44>>2]-o|0;K[n+44>>2]=i;h=g;if((i|0)>0){continue}break}}if((i|0)<0){u=((k+25>>>0)/9|0)+1|0;p=(x|0)==102;while(1){d=0-i|0;m=d>>>0>=9?9:d;h:{if(g>>>0<=j>>>0){h=!K[j>>2]<<2;break h}q=1e9>>>m|0;o=-1<>2];K[h>>2]=(d>>>m|0)+i;i=Q(q,d&o);h=h+4|0;if(h>>>0>>0){continue}break}h=!K[j>>2]<<2;if(!i){break h}K[g>>2]=i;g=g+4|0}i=m+K[n+44>>2]|0;K[n+44>>2]=i;j=h+j|0;d=p?r:j;g=g-d>>2>(u|0)?d+(u<<2)|0:g;if((i|0)<0){continue}break}}i=0;i:{if(g>>>0<=j>>>0){break i}i=Q(r-j>>2,9);h=10;d=K[j>>2];if(d>>>0<10){break i}while(1){i=i+1|0;h=Q(h,10);if(d>>>0>=h>>>0){continue}break}}d=(k-((x|0)!=102?i:0)|0)-((x|0)==103&(k|0)!=0)|0;if((d|0)<(Q(g-r>>2,9)-9|0)){h=(n+48|0)+((l|0)<0?-4092:-3804)|0;l=d+9216|0;d=(l|0)/9|0;m=h+(d<<2)|0;h=10;d=l+Q(d,-9)|0;if((d|0)<=7){while(1){h=Q(h,10);d=d+1|0;if((d|0)!=8){continue}break}}l=K[m>>2];u=(l>>>0)/(h>>>0)|0;o=Q(u,h);d=m+4|0;j:{if((l|0)==(o|0)&(d|0)==(g|0)){break j}l=l-o|0;k:{if(!(u&1)){b=9007199254740992;if(!(I[m-4|0]&1)|((h|0)!=1e9|j>>>0>=m>>>0)){break k}}b=9007199254740994}s=(d|0)==(g|0)?1:1.5;d=h>>>1|0;s=d>>>0>l>>>0?.5:(d|0)==(l|0)?s:1.5;if(!(L[y|0]!=45|z)){s=-s;b=-b}K[m>>2]=o;if(b+s==b){break j}d=h+o|0;K[m>>2]=d;if(d>>>0>=1e9){while(1){K[m>>2]=0;m=m-4|0;if(m>>>0>>0){j=j-4|0;K[j>>2]=0}d=K[m>>2]+1|0;K[m>>2]=d;if(d>>>0>999999999){continue}break}}i=Q(r-j>>2,9);h=10;d=K[j>>2];if(d>>>0<10){break j}while(1){i=i+1|0;h=Q(h,10);if(d>>>0>=h>>>0){continue}break}}d=m+4|0;g=d>>>0>>0?d:g}while(1){l=g;o=g>>>0<=j>>>0;if(!o){g=g-4|0;if(!K[g>>2]){continue}}break}l:{if((x|0)!=103){p=e&8;break l}h=k?k:1;d=(h|0)>(i|0)&(i|0)>-5;k=(d?i^-1:-1)+h|0;f=(d?-1:-2)+f|0;p=e&8;if(p){break l}g=-9;m:{if(o){break m}o=K[l-4>>2];if(!o){break m}d=10;g=0;if((o>>>0)%10|0){break m}while(1){h=g;g=g+1|0;d=Q(d,10);if(!((o>>>0)%(d>>>0)|0)){continue}break}g=h^-1}d=Q(l-r>>2,9);if((f&-33)==70){p=0;d=(d+g|0)-9|0;d=(d|0)>0?d:0;k=(d|0)>(k|0)?k:d;break l}p=0;d=((d+i|0)+g|0)-9|0;d=(d|0)>0?d:0;k=(d|0)>(k|0)?k:d}m=-1;o=k|p;if(((o?2147483645:2147483646)|0)<(k|0)){break b}q=(((o|0)!=0)+k|0)+1|0;h=f&-33;n:{if((h|0)==70){if((q^2147483647)<(i|0)){break b}g=(i|0)>0?i:0;break n}d=i>>31;g=fb((d^i)-d|0,0,w);if((w-g|0)<=1){while(1){g=g-1|0;I[g|0]=48;if((w-g|0)<2){continue}break}}u=g-2|0;I[u|0]=f;I[g-1|0]=(i|0)<0?45:43;g=w-u|0;if((g|0)>(q^2147483647)){break b}}d=g+q|0;if((d|0)>(t^2147483647)){break b}i=d+t|0;Ra(a,32,c,i,e);Pa(a,y,t);Ra(a,48,c,i,e^65536);o:{p:{q:{if((h|0)==70){h=n+16|9;f=j>>>0>r>>>0?r:j;j=f;while(1){g=fb(K[j>>2],0,h);r:{if((f|0)!=(j|0)){if(n+16>>>0>=g>>>0){break r}while(1){g=g-1|0;I[g|0]=48;if(n+16>>>0>>0){continue}break}break r}if((g|0)!=(h|0)){break r}g=g-1|0;I[g|0]=48}Pa(a,g,h-g|0);j=j+4|0;if(r>>>0>=j>>>0){continue}break}if(o){Pa(a,1647,1)}if((k|0)<=0|j>>>0>=l>>>0){break q}while(1){g=fb(K[j>>2],0,h);if(g>>>0>n+16>>>0){while(1){g=g-1|0;I[g|0]=48;if(n+16>>>0>>0){continue}break}}Pa(a,g,(k|0)>=9?9:k);g=k-9|0;j=j+4|0;if(l>>>0<=j>>>0){break p}d=(k|0)>9;k=g;if(d){continue}break}break p}s:{if((k|0)<0){break s}f=j>>>0>>0?l:j+4|0;l=n+16|9;h=j;while(1){g=fb(K[h>>2],0,l);if((l|0)==(g|0)){g=g-1|0;I[g|0]=48}t:{if((h|0)!=(j|0)){if(n+16>>>0>=g>>>0){break t}while(1){g=g-1|0;I[g|0]=48;if(n+16>>>0>>0){continue}break}break t}Pa(a,g,1);g=g+1|0;if(!(k|p)){break t}Pa(a,1647,1)}d=l-g|0;Pa(a,g,(d|0)<(k|0)?d:k);k=k-d|0;h=h+4|0;if(f>>>0<=h>>>0){break s}if((k|0)>=0){continue}break}}Ra(a,48,k+18|0,18,0);Pa(a,u,w-u|0);break o}g=k}Ra(a,48,g+9|0,9,0)}Ra(a,32,c,i,e^8192);m=(c|0)>(i|0)?c:i;break b}i=(f<<26>>31&9)+y|0;u:{if(d>>>0>11){break u}g=12-d|0;s=16;while(1){s=s*16;g=g-1|0;if(g){continue}break}if(L[i|0]==45){b=-(s+(-b-s));break u}b=b+s-s}k=K[n+44>>2];h=k>>31;g=fb((h^k)-h|0,0,w);if((w|0)==(g|0)){g=g-1|0;I[g|0]=48}r=t|2;j=f&32;l=g-2|0;I[l|0]=f+15;I[g-1|0]=(k|0)<0?45:43;g=!(e&8)&(d|0)<=0;h=n+16|0;while(1){f=h;k=S(b)<2147483647?~~b:-2147483648;I[h|0]=j|L[k+25744|0];b=(b-+(k|0))*16;h=h+1|0;if(!(g&b==0|(h-(n+16|0)|0)!=1)){I[f+1|0]=46;h=f+2|0}if(b!=0){continue}break}m=-1;g=w-l|0;f=g+r|0;if((2147483645-f|0)<(d|0)){break b}k=f;f=n+16|0;j=h-f|0;d=d?(j-2|0)<(d|0)?d+2|0:j:j;h=k+d|0;Ra(a,32,c,h,e);Pa(a,i,r);Ra(a,48,c,h,e^65536);Pa(a,f,j);Ra(a,48,d-j|0,0,0);Pa(a,l,g);Ra(a,32,c,h,e^8192);m=(c|0)>(h|0)?c:h}ra=n+560|0;return m|0}function cb(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;e=K[a+32>>2];a:{if(e){break a}b:{h=K[a+16>>2];if((h|0)>5){e=h;break b}c=K[a+20>>2];c:{d:{if((c|0)>=5){b=K[a>>2];e=K[b>>2];K[a>>2]=b+4;i=c-4|0;break d}if((c|0)<=0){e=-1;break c}b=K[a>>2];e:{if((c|0)==1){g=-1;c=0;break e}g=-1;f=c-1|0;k=f&1;f:{if((c|0)==2){e=0;d=c;break f}j=f&-2;e=0;f=b;d=c;while(1){K[a>>2]=f+1;l=L[f|0];b=f+2|0;K[a>>2]=b;K[a+20>>2]=d-1;f=L[f+1|0];d=d-2|0;K[a+20>>2]=d;g=((255<>2]=f;b=L[b|0];K[a+20>>2]=d-1;g=(255<>2]=b+1;e=(255<>2]=i}b=K[a+24>>2];c=e>>>24|0;K[a+24>>2]=(c|0)==255;i=e>>>16&255;k=(i|0)==255;g=e&255;f=(g|0)==255;j=b+f|0;b=e>>>8&255;d=(b|0)==255;j=k+(j+d|0)|0;e=(h-j|0)+32|0;K[a+16>>2]=e;l=K[a+12>>2];b=c|(i|(b|g<<(f?7:8))<<(d?7:8))<<(k?7:8);c=(j-h|0)+32|0;g=c&31;if((c&63)>>>0>=32){c=b<>>32-g;f=b<>2]=f|K[a+8>>2];K[a+12>>2]=c|l;if((e|0)>=6){break b}e=0;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;g:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break g}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;h=f;d=c&31;if((c&63)>>>0>=32){c=f<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2]|b>>31;K[a+40>>2]=K[a+40>>2]&-64|b;K[a+44>>2]=g;if((e|0)<6){e=1;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;h:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break h}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;h=f;d=c&31;if((c&63)>>>0>=32){c=f<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2];K[a+40>>2]=K[a+40>>2]&-8065|b<<7;K[a+44>>2]=b>>31<<7|b>>>25|g;if((e|0)<6){e=2;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;i:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break i}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;h=f;d=c&31;if((c&63)>>>0>=32){c=f<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2];K[a+40>>2]=K[a+40>>2]&-1032193|b<<14;K[a+44>>2]=b>>31<<14|b>>>18|g;if((e|0)<6){e=3;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;j:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break j}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;h=f;d=c&31;if((c&63)>>>0>=32){c=f<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2];K[a+40>>2]=K[a+40>>2]&-132120577|b<<21;K[a+44>>2]=b>>31<<21|b>>>11|g;if((e|0)<6){e=4;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;k:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break k}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;h=f;d=c&31;if((c&63)>>>0>=32){c=f<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2]&-4;K[a+40>>2]=K[a+40>>2]&268435455|b<<28;K[a+44>>2]=b>>31<<28|b>>>4|g;if((e|0)<6){e=5;break a}h=K[a+28>>2];c=K[(h<<2)+20704>>2];d=K[a+8>>2];b=K[a+12>>2];l:{if((b|0)<0){f=e-1|0;g=(-1<=11?11:h)+1|0;break l}i=d;f=63-c|0;g=f&31;if((f&63)>>>0>=32){f=b>>>g|0}else{f=((1<>>g}g=(f&(-1<>2]=f;K[a+28>>2]=e;h=d;e=c&31;if((c&63)>>>0>=32){c=d<>>32-e|b<>2]=d;K[a+12>>2]=c;d=K[a+44>>2]&-505;K[a+40>>2]=K[a+40>>2];K[a+44>>2]=g<<3|d;e=6;if((f|0)<6){break a}h=K[a+28>>2];c=K[(h<<2)+20704>>2];d=K[a+8>>2];b=K[a+12>>2];m:{if((b|0)<0){e=f-1|0;g=(-1<=11?11:h)+1|0;break m}i=d;e=63-c|0;g=e&31;if((e&63)>>>0>=32){i=b>>>g|0}else{i=((1<>>g}g=(i&(-1<>2]=e;K[a+28>>2]=f;h=d;f=c&31;if((c&63)>>>0>=32){c=d<>>32-f|b<>2]=f;K[a+12>>2]=c;f=K[a+44>>2]&-64513;K[a+40>>2]=K[a+40>>2];K[a+44>>2]=g<<10|f;if((e|0)<6){e=7;break a}d=K[a+28>>2];c=K[(d<<2)+20704>>2];f=K[a+8>>2];b=K[a+12>>2];g=b;n:{if((b|0)<0){e=e-1|0;b=(-1<=11?11:d)+1|0;break n}i=f;h=63-c|0;b=h&31;if((h&63)>>>0>=32){b=g>>>b|0}else{b=((1<>>b}b=(b&(-1<>2]=e;K[a+28>>2]=d;e=f;d=c&31;if((c&63)>>>0>=32){c=e<>>32-d|g<>2]=f;K[a+12>>2]=c;g=K[a+44>>2]&-8257537;K[a+40>>2]=K[a+40>>2];K[a+44>>2]=b<<17|g;e=8}K[a+32>>2]=e-1;g=K[a+44>>2];c=g>>>7|0;b=K[a+40>>2];K[a+40>>2]=(g&127)<<25|b>>>7;K[a+44>>2]=c;return b&127}function bd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;j=ra-80|0;ra=j;K[j+76>>2]=1;a:{b:{if(K[a+128>>2]!=1|K[a+132>>2]!=1|(K[a+108>>2]|K[a+112>>2])){break b}k=K[a+100>>2];if(K[k>>2]|K[k+4>>2]|(K[k+8>>2]!=K[a+116>>2]|K[k+12>>2]!=K[a+120>>2])){break b}if(!ab(a,j+72|0,0,j+68|0,j- -64|0,j+60|0,j+56|0,j+52|0,j+76|0,b,c)){break a}c:{d:{if(!K[j+76>>2]){break d}if(!jb(a,K[j+72>>2],0,0,b,c)){break d}b=K[a+100>>2];if(K[b+16>>2]){break c}d=1;break a}Fa(c,1,8739,0);break a}e=K[b+24>>2];while(1){b=Q(h,52);Ga(K[(b+e|0)+44>>2]);c=K[a+100>>2];e=K[c+24>>2];k=b+e|0;d=K[a+232>>2];m=K[K[K[d+20>>2]>>2]+20>>2]+Q(h,76)|0;K[k+44>>2]=K[m+36>>2];K[k+36>>2]=K[(b+K[K[d+24>>2]+24>>2]|0)+36>>2];K[m+36>>2]=0;d=1;h=h+1|0;if(h>>>0>2]){continue}break}break a}K[a+80>>2]=0;K[a+84>>2]=0;Ga(K[a+88>>2]);K[a+88>>2]=0;e:{if(!(K[a+28>>2]|K[a+32>>2]|K[a+36>>2]!=K[a+128>>2])){k=2;if(K[a+40>>2]==K[a+132>>2]){break e}}k=2;if(K[a+76>>2]){break e}if(!Ib(b)){break e}q=K[a+128>>2];k=Q(q,K[a+132>>2]);if(k){i=k&1;g=K[K[a+224>>2]+40>>2];f:{if((k|0)==1){k=0;break f}o=k&-2;k=0;while(1){f=g+Q(d,40)|0;l=K[f+4>>2];if(l){l=(K[f+16>>2]+Q(l,24)|0)-8|0;f=K[l>>2];n=f;p=f>>>0>k>>>0;f=K[l+4>>2];l=p&(f|0)>=(m|0)|(f|0)>(m|0);k=l?n:k;m=l?f:m}f=g+Q(d|1,40)|0;l=K[f+4>>2];if(l){l=(K[f+16>>2]+Q(l,24)|0)-8|0;f=K[l>>2];n=f;p=f>>>0>k>>>0;f=K[l+4>>2];l=p&(f|0)>=(m|0)|(f|0)>(m|0);k=l?n:k;m=l?f:m}d=d+2|0;e=e+2|0;if((o|0)!=(e|0)){continue}break}}g:{if(!i){break g}d=g+Q(d,40)|0;g=K[d+4>>2];if(!g){break g}g=(K[d+16>>2]+Q(g,24)|0)-8|0;d=K[g>>2];f=d;n=d>>>0>k>>>0;d=K[g+4>>2];g=n&(d|0)>=(m|0)|(d|0)>(m|0);k=g?f:k;m=g?d:m}k=k+2|0;m=k>>>0<2?m+1|0:m}else{k=2;m=0}f=K[a+32>>2];t=K[a+40>>2];h:{if(f>>>0>=t>>>0){break h}g=K[a+28>>2];i=K[a+36>>2];if(g>>>0>=i>>>0){break h}o=i-g&3;r=K[K[a+224>>2]+40>>2];n=g-i>>>0>4294967292;while(1){l=r+Q(Q(f,q),40)|0;d=g;e=0;if(o){while(1){h=K[(l+Q(d,40)|0)+4>>2]+h|0;d=d+1|0;e=e+1|0;if((o|0)!=(e|0)){continue}break}}if(!n){while(1){e=l+Q(d,40)|0;h=K[e+124>>2]+(K[e+84>>2]+(K[e+44>>2]+(K[e+4>>2]+h|0)|0)|0)|0;d=d+4|0;if((i|0)!=(d|0)){continue}break}}f=f+1|0;if((t|0)!=(f|0)){continue}break}}f=Ja(h<<3);K[a+88>>2]=f;if(!h|!f){break e}h=0;d=K[a+40>>2];i=K[a+32>>2];i:{if(d>>>0<=i>>>0){break i}e=K[a+36>>2];if(e>>>0<=N[a+28>>2]){break i}while(1){f=K[a+28>>2];if(f>>>0>>0){t=K[K[a+224>>2]+40>>2]+Q(Q(K[a+128>>2],i),40)|0;while(1){g=t+Q(f,40)|0;d=K[g+4>>2];if(d){o=d&3;g=K[g+16>>2];l=0;j:{if(d>>>0<4){d=0;break j}r=d&-4;d=0;q=0;while(1){p=g+Q(d,24)|0;s=K[p+4>>2];e=h<<3;n=e+K[a+88>>2]|0;K[n>>2]=K[p>>2];K[n+4>>2]=s;p=g+Q(d|1,24)|0;s=K[p+4>>2];n=e+K[a+88>>2]|0;K[n+8>>2]=K[p>>2];K[n+12>>2]=s;p=g+Q(d|2,24)|0;s=K[p+4>>2];n=e+K[a+88>>2]|0;K[n+16>>2]=K[p>>2];K[n+20>>2]=s;n=g+Q(d|3,24)|0;p=K[n+4>>2];e=e+K[a+88>>2]|0;K[e+24>>2]=K[n>>2];K[e+28>>2]=p;d=d+4|0;h=h+4|0;q=q+4|0;if((r|0)!=(q|0)){continue}break}}if(o){while(1){q=g+Q(d,24)|0;r=K[q+4>>2];e=K[a+88>>2]+(h<<3)|0;K[e>>2]=K[q>>2];K[e+4>>2]=r;d=d+1|0;h=h+1|0;l=l+1|0;if((o|0)!=(l|0)){continue}break}}e=K[a+36>>2]}f=f+1|0;if(f>>>0>>0){continue}break}d=K[a+40>>2]}i=i+1|0;if(i>>>0>>0){continue}break}f=K[a+88>>2]}K[a+84>>2]=h;e=ra-208|0;ra=e;K[e+8>>2]=1;K[e+12>>2]=0;o=h<<3;k:{if(!o){break k}K[e+16>>2]=8;K[e+20>>2]=8;d=8;h=8;i=2;while(1){g=d;d=(h+8|0)+d|0;K[(e+16|0)+(i<<2)>>2]=d;i=i+1|0;h=g;if(d>>>0>>0){continue}break}g=(f+o|0)-8|0;l:{if(g>>>0<=f>>>0){i=1;d=1;g=0;break l}i=1;d=1;while(1){m:{if((i&3)==3){Jb(f,d,e+16|0);yb(e+8|0,2);d=d+2|0;break m}o=e+16|0;h=d-1|0;n:{if(N[o+(h<<2)>>2]>=g-f>>>0){xb(f,i,K[e+12>>2],d,0,o);break n}Jb(f,d,e+16|0)}if((d|0)==1){wb(e+8|0,1);d=0;break m}wb(e+8|0,h);d=1}i=K[e+8>>2]|1;K[e+8>>2]=i;f=f+8|0;if(g>>>0>f>>>0){continue}break}g=K[e+12>>2]}xb(f,i,g,d,0,e+16|0);h=K[e+12>>2];i=K[e+8>>2];if(!(h|((d|0)!=1|(i|0)!=1))){break k}while(1){o:{if((d|0)<=1){g=Nc(i,h);yb(e+8|0,g);d=d+g|0;break o}h=e+8|0;wb(h,2);K[e+8>>2]=K[e+8>>2]^7;yb(h,1);o=f-8|0;i=e+16|0;g=d-2|0;xb(o-K[i+(g<<2)>>2]|0,K[e+8>>2],K[e+12>>2],d-1|0,1,i);wb(h,1);d=K[e+8>>2]|1;K[e+8>>2]=d;xb(o,d,K[e+12>>2],g,1,i);d=g}f=f-8|0;h=K[e+12>>2];i=K[e+8>>2];if(h|((d|0)!=1|(i|0)!=1)){continue}break}}ra=e+208|0}d=K[a+128>>2];e=0;p:{while(1){q:{if(!(!K[K[a+180>>2]+5596>>2]|((d|0)!=1|K[a+132>>2]!=1))){K[j+72>>2]=0;K[a+228>>2]=0;K[a+8>>2]=K[a+8>>2]|128;d=0;break q}d=0;if(!ab(a,j+72|0,0,j+68|0,j- -64|0,j+60|0,j+56|0,j+52|0,j+76|0,b,c)){break a}if(!K[j+76>>2]){break p}d=K[j+72>>2]}g=d+1|0;f=jb(a,d,0,0,b,c);h=Q(K[a+128>>2],K[a+132>>2]);if(!f){K[j+4>>2]=h;K[j>>2]=g;Fa(c,1,7500,j);d=0;break a}K[j+36>>2]=h;K[j+32>>2]=g;Fa(c,4,11758,j+32|0);if(!Wc(K[a+232>>2],K[K[a+100>>2]+24>>2])){d=0;break a}r:{if(!(K[a+128>>2]!=1|K[a+132>>2]!=1)){h=K[a+100>>2];f=K[a+96>>2];if(K[h>>2]!=K[f>>2]|K[h+4>>2]!=K[f+4>>2]|(K[h+8>>2]!=K[f+8>>2]|K[h+12>>2]!=K[f+12>>2])){break r}}d=K[a+180>>2]+Q(d,5644)|0;h=K[d+5596>>2];if(!h){break r}Ga(h);K[d+5596>>2]=0;K[d+5600>>2]=0}K[j+16>>2]=g;Fa(c,4,16564,j+16|0);if(!(Va(b)|ua)&K[a+8>>2]==64){break p}e=e+1|0;d=K[a+128>>2];if((e|0)==(Q(d,K[a+132>>2])|0)){break p}g=K[a+84>>2];if(!g|(g|0)!=K[a+80>>2]){continue}break}Dc(b,k,m,c)}d=Vc(a,c)}ra=j+80|0;return d|0}function ic(a,b,c,d,e,f,g,h,i){var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;p=ra-32|0;ra=p;K[p+24>>2]=f;r=K[(Q(K[d+28>>2],76)+b|0)+28>>2]+Q(K[d+32>>2],152)|0;a:{if(!(K[d+40>>2]|!K[r+24>>2])){k=r+28|0;while(1){b:{if(ec(k)){break b}b=K[d+36>>2];if(b>>>0>=N[k+24>>2]/40>>>0){Fa(i,1,2799,0);break a}b=K[k+20>>2]+Q(b,40)|0;vc(K[b+32>>2]);vc(K[b+36>>2]);o=Q(K[b+20>>2],K[b+16>>2]);if(!o){break b}b=K[b+24>>2];if(o>>>0>=8){q=o&-8;j=0;while(1){K[b+516>>2]=0;K[b+520>>2]=0;K[b+448>>2]=0;K[b+452>>2]=0;K[b+380>>2]=0;K[b+384>>2]=0;K[b+312>>2]=0;K[b+316>>2]=0;K[b+244>>2]=0;K[b+248>>2]=0;K[b+176>>2]=0;K[b+180>>2]=0;K[b+108>>2]=0;K[b+112>>2]=0;K[b+40>>2]=0;K[b+44>>2]=0;b=b+544|0;j=j+8|0;if((q|0)!=(j|0)){continue}break}}j=0;o=o&7;if(!o){break b}while(1){K[b+40>>2]=0;K[b+44>>2]=0;b=b+68|0;j=j+1|0;if((o|0)!=(j|0)){continue}break}}k=k+36|0;n=n+1|0;if(n>>>0>2]){continue}break}}q=f;c:{if(!(L[c|0]&2)){break c}if(h>>>0<=5){Fa(i,2,4159,0);break c}if(!(L[f|0]==255&L[f+1|0]==145)){Fa(i,2,4201,0);break c}q=f+6|0;K[p+24>>2]=q}l=Ja(20);if(!l){break a}d:{if(I[a+108|0]&1){q=K[a+40>>2];o=a+44|0;h=a+40|0;break d}if(L[c+5640|0]&2){q=K[c+5168>>2];o=c+5180|0;h=c+5168|0;break d}K[p+28>>2]=(f+h|0)-q;o=p+28|0;h=p+24|0}a=K[o>>2];K[l+12>>2]=0;K[l+16>>2]=0;K[l+8>>2]=q;K[l>>2]=q;K[l+4>>2]=a+q;if(!Wa(l,1)){xc(l);a=yc(l);kb(l);a=a+q|0;b=K[h>>2];d=K[o>>2];if(L[c|0]&4){if(b+(d-a|0)>>>0<=1){Fa(i,1,4385,0);break a}if(!(L[a|0]==255&L[a+1|0]==146)){Fa(i,1,4364,0);break a}a=a+2|0}a=a-b|0;K[o>>2]=d-a;K[h>>2]=a+b;K[e>>2]=0;K[g>>2]=K[p+24>>2]-f;x=1;break a}if(K[r+24>>2]){t=r+28|0;while(1){a=K[d+36>>2];b=K[t+20>>2];e:{if(ec(t)){break e}u=b+Q(a,40)|0;y=Q(K[u+20>>2],K[u+16>>2]);if(!y){break e}k=K[u+24>>2];v=0;while(1){f:{g:{if(!K[k+40>>2]){a=tc(l,K[u+32>>2],v,K[d+40>>2]+1|0);break g}a=Wa(l,1)}if(!a){K[k+36>>2]=0;break f}if(!K[k+40>>2]){b=0;while(1){a=b;b=b+1|0;if(!tc(l,K[u+36>>2],v,a)){continue}break}b=K[t+28>>2];K[k+32>>2]=3;K[k+24>>2]=b;K[k+28>>2]=(b-a|0)+1}a=1;h:{if(!Wa(l,1)){break h}a=2;if(!Wa(l,1)){break h}a=Wa(l,2);if((a|0)!=3){a=a+3|0;break h}a=Wa(l,5);if((a|0)!=31){a=a+6|0;break h}a=Wa(l,7)+37|0}K[k+36>>2]=a;b=0;while(1){a=b;b=b+1|0;if(Wa(l,1)){continue}break}K[k+32>>2]=a+K[k+32>>2];i:{a=K[k+40>>2];j:{k:{if(!a){a=K[(K[c+5584>>2]+Q(K[d+28>>2],1080)|0)+16>>2];if(!K[k+48>>2]){b=La(K[k>>2],240);if(!b){break i}K[k>>2]=b;B(b+Q(K[k+48>>2],24)|0,0,240);K[k+48>>2]=10}j=K[k>>2];ob(j);b=a&4?1:a&1?10:109;a=0;break k}b=K[k>>2];n=a-1|0;j=b+Q(n,24)|0;if(K[j+4>>2]!=K[j+12>>2]){break j}n=K[(K[c+5584>>2]+Q(K[d+28>>2],1080)|0)+16>>2];j=K[k+48>>2];if(j>>>0>>0){j=j+10|0;b=La(b,Q(j,24));if(!b){break i}K[k>>2]=b;B(b+Q(K[k+48>>2],24)|0,0,240);K[k+48>>2]=j;b=K[k>>2]}j=Q(a,24)+b|0;ob(j);b=1;l:{if(n&4){break l}b=109;if(!(n&1)){break l}b=K[j-12>>2];b=(b|0)==1?2:(b|0)==10?2:1}}n=a;K[j+12>>2]=b}a=K[k+36>>2];if(L[(K[c+5584>>2]+Q(K[d+28>>2],1080)|0)+16|0]&64){while(1){m=Q(n,24);s=n?a:1;K[(m+K[k>>2]|0)+16>>2]=s;w=K[k+32>>2];j=0;b=a;if(s>>>0>=2){while(1){j=j+1|0;s=b>>>0>3;b=b>>>1|0;if(s){continue}break}}b=j+w|0;if(b>>>0>=33){K[p+16>>2]=b;Fa(i,1,15498,p+16|0);break i}j=Wa(l,b);b=K[k>>2];m=m+b|0;K[m+20>>2]=j;a=a-K[m+16>>2]|0;if((a|0)<=0){break f}j=K[(K[c+5584>>2]+Q(K[d+28>>2],1080)|0)+16>>2];m=K[k+48>>2];if(m>>>0>>0){m=m+10|0;b=La(b,Q(m,24));if(!b){break i}K[k>>2]=b;B(b+Q(K[k+48>>2],24)|0,0,240);K[k+48>>2]=m;b=K[k>>2]}n=n+1|0;b=b+Q(n,24)|0;ob(b);if(j&4){K[b+12>>2]=1;continue}if(j&1){j=b;b=K[b-12>>2];K[j+12>>2]=(b|0)==1?2:(b|0)==10?2:1}else{K[b+12>>2]=109}continue}}while(1){m=Q(n,24);j=m+K[k>>2]|0;b=K[j+12>>2]-K[j+4>>2]|0;b=(a|0)>(b|0)?b:a;K[j+16>>2]=b;s=K[k+32>>2];j=0;if(b>>>0>=2){while(1){j=j+1|0;w=b>>>0>3;b=b>>>1|0;if(w){continue}break}}b=j+s|0;if(b>>>0>=33){K[p>>2]=b;Fa(i,1,15498,p);break i}j=Wa(l,b);b=K[k>>2];m=m+b|0;K[m+20>>2]=j;a=a-K[m+16>>2]|0;if((a|0)<=0){break f}j=K[(K[c+5584>>2]+Q(K[d+28>>2],1080)|0)+16>>2];m=K[k+48>>2];if(m>>>0>>0){m=m+10|0;b=La(b,Q(m,24));if(!b){break i}K[k>>2]=b;B(b+Q(K[k+48>>2],24)|0,0,240);K[k+48>>2]=m;b=K[k>>2]}n=n+1|0;b=b+Q(n,24)|0;ob(b);if(j&4){K[b+12>>2]=1;continue}if(j&1){j=b;b=K[b-12>>2];K[j+12>>2]=(b|0)==1?2:(b|0)==10?2:1}else{K[b+12>>2]=109}continue}}kb(l);break a}k=k+68|0;v=v+1|0;if((y|0)!=(v|0)){continue}break}}t=t+36|0;z=z+1|0;if(z>>>0>2]){continue}break}}if(!xc(l)){kb(l);break a}a=yc(l);kb(l);b=a+q|0;a=K[h>>2];if(L[c|0]&4){if(a+(K[o>>2]-b|0)>>>0<=1){Fa(i,1,4385,0);break a}if(!(L[b|0]==255&L[b+1|0]==146)){Fa(i,1,4364,0);break a}b=b+2|0}if((a|0)==(b|0)){break a}K[o>>2]=K[o>>2]+(a-b|0);K[h>>2]=b;x=1;K[e>>2]=1;K[g>>2]=K[p+24>>2]-f}ra=p+32|0;return x}function Hb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;o=Q(c,5);j=(c<<2)+b|0;e=K[a>>2];f=K[a+12>>2]<<5;h=e+f|0;l=e-f|0;e=K[a+16>>2];k=K[a+28>>2];i=K[a+20>>2];q=K[a+8>>2];a:{b:{if(h&15|(b&15|d>>>0<8)){if(e>>>0>=i>>>0){break a}c:{switch(d-1|0){case 1:f=e+1|0;if(i-e&1){g=h+(e<<6)|0;e=(e<<2)+b|0;O[g>>2]=O[e>>2];O[g+4>>2]=O[e+(c<<2)>>2];e=f}if((f|0)==(i|0)){break a}while(1){f=h+(e<<6)|0;g=(e<<2)+b|0;O[f>>2]=O[g>>2];r=f;f=c<<2;O[r+4>>2]=O[f+g>>2];g=e+1|0;j=h+(g<<6)|0;g=(g<<2)+b|0;O[j>>2]=O[g>>2];O[j+4>>2]=O[f+g>>2];e=e+2|0;if((i|0)!=(e|0)){continue}break};break a;case 0:break c;default:break b}}f=e;j=i-e&3;if(j){while(1){O[h+(f<<6)>>2]=O[(f<<2)+b>>2];f=f+1|0;g=g+1|0;if((j|0)!=(g|0)){continue}break}}if(e-i>>>0>4294967292){break a}while(1){O[h+(f<<6)>>2]=O[(f<<2)+b>>2];e=f+1|0;O[h+(e<<6)>>2]=O[(e<<2)+b>>2];e=f+2|0;O[h+(e<<6)>>2]=O[(e<<2)+b>>2];e=f+3|0;O[h+(e<<6)>>2]=O[(e<<2)+b>>2];f=f+4|0;if((i|0)!=(f|0)){continue}break}break a}if(e>>>0>=i>>>0){break a}n=c<<4;m=Q(c,12);s=c<<3;while(1){f=h+(e<<6)|0;g=(e<<2)+b|0;O[f>>2]=O[g>>2];p=c<<2;O[f+4>>2]=O[p+g>>2];O[f+8>>2]=O[g+s>>2];O[f+12>>2]=O[g+m>>2];O[f+16>>2]=O[g+n>>2];g=e+o<<2;O[f+20>>2]=O[g+b>>2];g=g+j|0;O[f+24>>2]=O[g>>2];O[f+28>>2]=O[g+p>>2];e=e+1|0;if((i|0)!=(e|0)){continue}break}break a}n=c<<4;m=Q(c,12);s=c<<3;p=(d|0)==5;r=(d|0)==7;while(1){f=h+(e<<6)|0;g=(e<<2)+b|0;O[f>>2]=O[g>>2];t=c<<2;O[f+4>>2]=O[g+t>>2];O[f+8>>2]=O[g+s>>2];d:{if((d|0)==3){break d}O[f+12>>2]=O[g+m>>2];if((d|0)==4){break d}O[f+16>>2]=O[g+n>>2];if(p){break d}g=e+o<<2;O[f+20>>2]=O[g+b>>2];if((d|0)==6){break d}g=g+j|0;O[f+24>>2]=O[g>>2];if(r){break d}O[f+28>>2]=O[g+t>>2]}e=e+1|0;if((i|0)!=(e|0)){continue}break}}b=(q<<2)+b|0;i=b+(c<<2)|0;e=K[a+24>>2];h=l+32|0;e:{if(h&15|(b&15|d>>>0<8)){if(e>>>0>=k>>>0){break e}f:{switch(d-1|0){case 1:a=e+1|0;if(k-e&1){d=h+(e<<6)|0;e=b+(e<<2)|0;O[d>>2]=O[e>>2];O[d+4>>2]=O[e+(c<<2)>>2];e=a}if((a|0)==(k|0)){break e}while(1){a=h+(e<<6)|0;d=b+(e<<2)|0;O[a>>2]=O[d>>2];f=a;a=c<<2;O[f+4>>2]=O[a+d>>2];d=e+1|0;f=h+(d<<6)|0;d=b+(d<<2)|0;O[f>>2]=O[d>>2];O[f+4>>2]=O[a+d>>2];e=e+2|0;if((k|0)!=(e|0)){continue}break};break e;case 0:f=e;a=k-e&3;if(a){g=0;while(1){O[h+(f<<6)>>2]=O[b+(f<<2)>>2];f=f+1|0;g=g+1|0;if((a|0)!=(g|0)){continue}break}}if(e-k>>>0>4294967292){break e}while(1){O[h+(f<<6)>>2]=O[b+(f<<2)>>2];a=f+1|0;O[h+(a<<6)>>2]=O[b+(a<<2)>>2];a=f+2|0;O[h+(a<<6)>>2]=O[b+(a<<2)>>2];a=f+3|0;O[h+(a<<6)>>2]=O[b+(a<<2)>>2];f=f+4|0;if((k|0)!=(f|0)){continue}break};break e;default:break f}}g=c<<4;j=Q(c,12);l=c<<3;q=(d|0)==5;n=(d|0)==7;while(1){a=h+(e<<6)|0;f=b+(e<<2)|0;O[a>>2]=O[f>>2];m=c<<2;O[a+4>>2]=O[m+f>>2];O[a+8>>2]=O[f+l>>2];g:{if((d|0)==3){break g}O[a+12>>2]=O[f+j>>2];if((d|0)==4){break g}O[a+16>>2]=O[f+g>>2];if(q){break g}f=e+o<<2;O[a+20>>2]=O[f+b>>2];if((d|0)==6){break g}f=f+i|0;O[a+24>>2]=O[f>>2];if(n){break g}O[a+28>>2]=O[f+m>>2]}e=e+1|0;if((k|0)!=(e|0)){continue}break}break e}if(e>>>0>=k>>>0){break e}f=c<<4;g=Q(c,12);j=c<<3;while(1){a=h+(e<<6)|0;d=b+(e<<2)|0;O[a>>2]=O[d>>2];l=c<<2;O[a+4>>2]=O[l+d>>2];O[a+8>>2]=O[d+j>>2];O[a+12>>2]=O[d+g>>2];O[a+16>>2]=O[d+f>>2];d=e+o<<2;O[a+20>>2]=O[d+b>>2];d=d+i|0;O[a+24>>2]=O[d>>2];O[a+28>>2]=O[d+l>>2];e=e+1|0;if((k|0)!=(e|0)){continue}break}}}function Xb(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;d=ra-176|0;ra=d;a:{if(b&384){Sa(5906,11,c);break a}b:{if(!(b&1)){break b}e=K[a+96>>2];if(!e){break b}f=ra-80|0;ra=f;Sa(1755,13,c);I[f+79|0]=0;I[f+78|0]=9;g=K[e+4>>2];K[f+68>>2]=K[e>>2];K[f+72>>2]=g;j=f+78|0;K[f+64>>2]=j;Ka(c,7483,f- -64|0);g=K[e+12>>2];K[f+52>>2]=K[e+8>>2];K[f+56>>2]=g;K[f+48>>2]=j;Ka(c,7466,f+48|0);K[f+36>>2]=K[e+16>>2];K[f+32>>2]=j;Ka(c,7240,f+32|0);if(!(!K[e+24>>2]|!K[e+16>>2])){while(1){l=f+78|0;K[f+16>>2]=l;K[f+20>>2]=m;Ka(c,1787,f+16|0);j=K[e+24>>2];g=ra-48|0;ra=g;I[g+46|0]=9;I[g+47|0]=0;I[g+45|0]=9;n=Q(m,52)+j|0;j=K[n+4>>2];K[g+36>>2]=K[n>>2];K[g+40>>2]=j;j=g+45|0;K[g+32>>2]=j;Ka(c,7172,g+32|0);K[g+20>>2]=K[n+24>>2];K[g+16>>2]=j;Ka(c,7418,g+16|0);K[g+4>>2]=K[n+32>>2];K[g>>2]=j;Ka(c,7391,g);ra=g+48|0;K[f>>2]=l;Ka(c,1665,f);m=m+1|0;if(m>>>0>2]){continue}break}}Sa(1673,2,c);ra=f+80|0}if(!(!(b&2)|!K[a+96>>2])){Sa(1894,36,c);e=K[a+112>>2];K[d+160>>2]=K[a+108>>2];K[d+164>>2]=e;Ka(c,2388,d+160|0);e=K[a+120>>2];K[d+144>>2]=K[a+116>>2];K[d+148>>2]=e;Ka(c,2354,d+144|0);e=K[a+132>>2];K[d+128>>2]=K[a+128>>2];K[d+132>>2]=e;Ka(c,2372,d+128|0);Wb(K[a+12>>2],K[K[a+96>>2]+16>>2],c);Sa(1673,2,c)}c:{if(!(b&8)|!K[a+96>>2]){break c}e=Q(K[a+128>>2],K[a+132>>2]);if(!e){break c}h=K[a+180>>2];while(1){Wb(h,K[K[a+96>>2]+16>>2],c);h=h+5644|0;k=k+1|0;if((e|0)!=(k|0)){continue}break}}if(!(b&16)){break a}i=K[a+224>>2];Sa(1856,37,c);e=K[i>>2];b=K[i+4>>2];a=K[i+12>>2];K[d+120>>2]=K[i+8>>2];K[d+124>>2]=a;K[d+112>>2]=e;K[d+116>>2]=b;Ka(c,5693,d+112|0);Sa(1838,17,c);if(!(!K[i+28>>2]|!K[i+24>>2])){h=0;while(1){a=K[i+28>>2]+Q(h,24)|0;g=M[a>>1];e=K[a+8>>2];b=K[a+12>>2];K[d+96>>2]=K[a+16>>2];K[d+88>>2]=e;K[d+92>>2]=b;K[d+80>>2]=g;Ka(c,7360,d+80|0);h=h+1|0;if(h>>>0>2]){continue}break}}Sa(1671,4,c);j=K[i+40>>2];d:{if(!j){break d}g=K[i+36>>2];if(!g){break d}k=0;h=0;while(1){a=j+Q(h,40)|0;e=K[a+4>>2];e:{if(!e){break e}l=K[a+16>>2];if(!l){break e}b=K[l>>2];a=K[l+4>>2];if((a|0)<0){a=1}else{a=!b&(a|0)<=0}if(a|(K[l+8>>2]|K[l+12>>2])){break e}if(Oc(1402)){break d}}k=e+k|0;h=h+1|0;if((g|0)!=(h|0)){continue}break}if(!k){break d}Sa(1821,16,c);if(K[i+36>>2]){k=K[i+40>>2];n=0;while(1){f=Q(n,40);l=K[(f+k|0)+4>>2];K[d+68>>2]=l;K[d+64>>2]=n;Ka(c,7430,d- -64|0);k=K[i+40>>2];f:{if(!l){break f}h=0;if(!K[(f+k|0)+16>>2]){break f}while(1){m=K[(f+K[i+40>>2]|0)+16>>2]+Q(h,24)|0;j=K[m>>2];g=K[m+4>>2];e=K[m+8>>2];b=K[m+12>>2];a=K[m+20>>2];K[d+56>>2]=K[m+16>>2];K[d+60>>2]=a;K[d+48>>2]=e;K[d+52>>2]=b;K[d+40>>2]=j;K[d+44>>2]=g;K[d+32>>2]=h;Ka(c,10901,d+32|0);h=h+1|0;if((l|0)!=(h|0)){continue}break}k=K[i+40>>2]}a=f+k|0;g:{if(!K[a+24>>2]){break g}h=0;if(!K[a+20>>2]){break g}while(1){a=K[(f+k|0)+24>>2]+Q(h,24)|0;g=M[a>>1];e=K[a+8>>2];b=K[a+12>>2];K[d+16>>2]=K[a+16>>2];K[d+8>>2]=e;K[d+12>>2]=b;K[d>>2]=g;Ka(c,7360,d);h=h+1|0;k=K[i+40>>2];if(h>>>0>2]){continue}break}}n=n+1|0;if(n>>>0>2]){continue}break}}Sa(1671,4,c)}Sa(1673,2,c)}ra=d+176|0}function He(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=ra-128|0;ra=e;K[e+120>>2]=0;a:{if((c|0)!=8){Fa(d,1,4010,0);Fa(d,1,4010,0);break a}Ha(b,a+228|0,2);Ha(b+2|0,e+124|0,4);Ha(b+6|0,e+116|0,1);Ha(b+7|0,e+120|0,1);c=K[a+228>>2];i=K[a+128>>2];if(c>>>0>=Q(i,K[a+132>>2])>>>0){K[e+112>>2]=c;Fa(d,1,7806,e+112|0);break a}h=K[a+180>>2]+Q(c,5644)|0;j=(c>>>0)/(i>>>0)|0;b=K[e+116>>2];b:{f=K[a+44>>2];if((f|0)>=0&(c|0)!=(f|0)){break b}f=K[h+5588>>2]+1|0;if((f|0)==(b|0)){break b}K[e+104>>2]=f;K[e+100>>2]=b;K[e+96>>2]=c;Fa(d,1,7830,e+96|0);f=0;break a}K[h+5588>>2]=b;c:{b=K[e+124>>2];if(b-1>>>0<=12){if((b|0)!=12){break c}K[e+64>>2]=12;Fa(d,2,11827,e- -64|0);b=K[e+124>>2]}if(!b){Fa(d,4,10658,0);K[a+56>>2]=1}d:{e:{f:{g:{g=K[h+5592>>2];if(g){b=K[e+116>>2];if(b>>>0>>0){break g}K[e+52>>2]=g;K[e+48>>2]=b;Fa(d,1,5113,e+48|0);K[a+56>>2]=1;f=0;break a}f=K[e+120>>2];if(f){break f}break d}f=K[e+120>>2];if(!f){break e}}g=(L[a+92|0]>>>4&1)+f|0;K[e+120>>2]=g;b=K[e+116>>2];f=K[h+5592>>2];if(b>>>0>f-1>>>0){K[e+20>>2]=f;K[e+16>>2]=b;Fa(d,1,5014,e+16|0);K[a+56>>2]=1;f=0;break a}if(b>>>0>=g>>>0){K[e+36>>2]=g;K[e+32>>2]=b;Fa(d,1,5213,e+32|0);K[a+56>>2]=1;f=0;break a}K[h+5592>>2]=g}if((K[e+116>>2]+1|0)!=(g|0)){break d}I[a+92|0]=L[a+92|0]|1}b=K[e+124>>2];K[a+8>>2]=16;K[a+24>>2]=K[a+56>>2]?0:b-12|0;f=K[a+44>>2];h:{if((f|0)==-1){f=4;b=c-Q(j,i)|0;if(!(b>>>0>2]|b>>>0>=N[a+36>>2]|j>>>0>2])){f=(j>>>0>=N[a+40>>2])<<2}I[a+92|0]=L[a+92|0]&251|f;b=K[a+228>>2];break h}b=K[a+228>>2];I[a+92|0]=L[a+92|0]&251|((f|0)!=(b|0))<<2}c=K[K[a+224>>2]+40>>2]+Q(b,40)|0;K[c>>2]=b;K[c+12>>2]=K[e+116>>2];f=K[e+120>>2];if(!K[a+76>>2]){if(N[c+4>>2]>=f>>>0){f=1;break a}K[e>>2]=b;Fa(d,2,1575,e);K[a+76>>2]=1;f=K[e+120>>2]}b=K[a+228>>2];c=K[K[a+224>>2]+40>>2];if(f){b=Q(b,40)+c|0;K[b+4>>2]=f;c=K[e+120>>2];K[b+8>>2]=c;b=K[b+16>>2];if(!b){b=Ia(c,24);K[(K[K[a+224>>2]+40>>2]+Q(K[a+228>>2],40)|0)+16>>2]=b;if(b){f=1;break a}f=0;Fa(d,1,6910,0);break a}b=La(b,Q(c,24));c=K[K[a+224>>2]+40>>2]+Q(K[a+228>>2],40)|0;if(!b){Ga(K[c+16>>2]);f=0;K[(K[K[a+224>>2]+40>>2]+Q(K[a+228>>2],40)|0)+16>>2]=0;Fa(d,1,6910,0);break a}K[c+16>>2]=b;f=1;break a}i:{f=Q(b,40)+c|0;g=K[f+16>>2];if(g){break i}K[f+8>>2]=10;g=Ia(10,24);c=K[K[a+224>>2]+40>>2];b=K[a+228>>2];K[(c+Q(b,40)|0)+16>>2]=g;if(g){break i}f=0;K[(Q(b,40)+c|0)+8>>2]=0;Fa(d,1,6910,0);break a}b=Q(b,40)+c|0;c=K[e+116>>2];if(N[b+8>>2]>c>>>0){f=1;break a}f=1;h=b;b=c+1|0;K[h+8>>2]=b;b=La(g,Q(b,24));c=K[K[a+224>>2]+40>>2]+Q(K[a+228>>2],40)|0;if(!b){Ga(K[c+16>>2]);f=0;a=K[K[a+224>>2]+40>>2]+Q(K[a+228>>2],40)|0;K[a+8>>2]=0;K[a+16>>2]=0;Fa(d,1,6910,0);break a}K[c+16>>2]=b;break a}K[e+80>>2]=b;Fa(d,1,12096,e+80|0);f=0}ra=e+128|0;return f|0}function rb(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;g=K[a+8>>2];e=g+K[a+4>>2]|0;a:{if(!K[a+12>>2]){if((e|0)<2|(d|0)<=0){break a}q=e&2147483644;m=e&3;r=e&1;s=g+1|0;h=K[a>>2];o=h+(e<<2)|0;t=e-4>>>1|0;a=e-1|0;u=h+(a<<2)|0;v=Q(c,g)<<2;l=e>>>0<4;w=Q(a>>>1|0,c)<<2;while(1){g=K[b+v>>2];e=K[b>>2]-(g+1>>1)|0;i=0;a=0;if(!l){while(1){j=a+1|0;x=K[(Q(j,c)<<2)+b>>2];f=K[(Q(a+s|0,c)<<2)+b>>2];p=h+(i<<2)|0;K[p>>2]=e;k=e;e=x-((g+f|0)+2>>2)|0;K[p+4>>2]=(k+e>>1)+g;i=i+2|0;k=(a|0)!=(t|0);g=f;a=j;if(k){continue}break}}K[h+(i<<2)>>2]=e;if(r){a=K[b+w>>2]-(g+1>>1)|0;K[u>>2]=a;e=a+e>>1;a=-8}else{a=-4}K[a+o>>2]=e+g;e=0;a=0;g=0;if(!l){while(1){K[(Q(a,c)<<2)+b>>2]=K[h+(a<<2)>>2];f=a|1;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];f=a|2;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];f=a|3;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];a=a+4|0;g=g+4|0;if((q|0)!=(g|0)){continue}break}}if(m){while(1){K[(Q(a,c)<<2)+b>>2]=K[h+(a<<2)>>2];a=a+1|0;e=e+1|0;if((m|0)!=(e|0)){continue}break}}b=b+4|0;n=n+1|0;if((n|0)!=(d|0)){continue}break}break a}b:{switch(e-1|0){case 0:if((d|0)<=0){break a}if(d>>>0>=4){c=d&2147483644;a=0;while(1){K[b>>2]=K[b>>2]/2;K[b+4>>2]=K[b+4>>2]/2;K[b+8>>2]=K[b+8>>2]/2;K[b+12>>2]=K[b+12>>2]/2;b=b+16|0;a=a+4|0;if((c|0)!=(a|0)){continue}break}}c=d&3;if(!c){break a}a=0;while(1){K[b>>2]=K[b>>2]/2;b=b+4|0;a=a+1|0;if((c|0)!=(a|0)){continue}break};break a;case 1:if((d|0)<=0){break a}a=K[a>>2];e=0;g=Q(c,g)<<2;while(1){f=b+g|0;j=K[b>>2]-(K[f>>2]+1>>1)|0;K[a+4>>2]=j;f=j+K[f>>2]|0;K[a>>2]=f;K[b>>2]=f;K[(c<<2)+b>>2]=K[a+4>>2];b=b+4|0;e=e+1|0;if((e|0)!=(d|0)){continue}break};break a;default:break b}}if((e|0)<3|(d|0)<=0){break a}q=e&2147483644;m=e&3;h=K[a>>2];r=(h+(e<<2)|0)-4|0;a=e-2|0;s=h+(a<<2)|0;o=e&1;f=!o;t=((e-f|0)-4>>>1|0)+1|0;u=Q(c,g)<<2;v=a-f>>>0<2;w=Q((e>>>1|0)-1|0,c)<<2;x=e-1>>>0<3;while(1){l=b+u|0;g=K[l+(c<<2)>>2];a=K[l>>2];e=K[b>>2]-((g+a|0)+2>>2)|0;K[h>>2]=e+a;i=1;a=1;if(!v){while(1){p=K[(Q(a,c)<<2)+b>>2];j=a+1|0;f=K[l+(Q(j,c)<<2)>>2];y=h+(i<<2)|0;K[y>>2]=e;k=e;e=p-((g+f|0)+2>>2)|0;K[y+4>>2]=(k+e>>1)+g;i=i+2|0;k=(a|0)!=(t|0);a=j;g=f;if(k){continue}break}}K[h+(i<<2)>>2]=e;c:{if(!o){a=K[b+w>>2]-(g+1>>1)|0;K[s>>2]=(e+a>>1)+g;break c}a=e+g|0}K[r>>2]=a;e=0;a=0;g=0;if(!x){while(1){K[(Q(a,c)<<2)+b>>2]=K[h+(a<<2)>>2];f=a|1;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];f=a|2;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];f=a|3;K[(Q(f,c)<<2)+b>>2]=K[h+(f<<2)>>2];a=a+4|0;g=g+4|0;if((q|0)!=(g|0)){continue}break}}if(m){while(1){K[(Q(a,c)<<2)+b>>2]=K[h+(a<<2)>>2];a=a+1|0;e=e+1|0;if((m|0)!=(e|0)){continue}break}}b=b+4|0;n=n+1|0;if((n|0)!=(d|0)){continue}break}}}function Rb(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;r=ra-16|0;ra=r;a:{if(!c){Fa(d,1,11592,0);break a}t=K[c+16>>2];i=K[a+96>>2];if(t>>>0>2]){Fa(d,1,10533,0);break a}f=K[a+128>>2];g=Q(f,K[a+132>>2]);if(g>>>0<=e>>>0){K[r>>2]=e;K[r+4>>2]=g-1;Fa(d,1,16325,r);g=0;break a}j=(e>>>0)/(f>>>0)|0;f=e-Q(j,f)|0;h=K[a+108>>2]+Q(f,K[a+116>>2])|0;K[c>>2]=h;g=K[i>>2];l=g>>>0>>0?h:g;K[c>>2]=l;f=K[a+108>>2]+Q(K[a+116>>2],f+1|0)|0;K[c+8>>2]=f;g=K[K[a+96>>2]+8>>2];f=f>>>0>>0?f:g;K[c+8>>2]=f;i=K[a+112>>2]+Q(j,K[a+120>>2])|0;K[c+4>>2]=i;g=K[K[a+96>>2]+4>>2];h=g>>>0>>0?i:g;K[c+4>>2]=h;i=K[a+112>>2]+Q(K[a+120>>2],j+1|0)|0;K[c+12>>2]=i;g=K[K[a+96>>2]+12>>2];g=g>>>0>i>>>0?i:g;K[c+12>>2]=g;i=K[a+96>>2];m=K[i+16>>2];if(m){u=g-1|0;v=(g>>31)-!g|0;w=f-1|0;x=(f>>31)-!f|0;y=h-1|0;z=0-!h|0;A=l-1|0;B=0-!l|0;C=K[i+24>>2];g=K[c+24>>2];while(1){i=K[(C+Q(q,52)|0)+40>>2];K[g+40>>2]=i;f=B;l=K[g>>2];h=l+A|0;f=l>>>0>h>>>0?f+1|0:f;n=Ne(h,f,l,0);K[g+16>>2]=n;f=z;h=K[g+4>>2];j=h+y|0;f=h>>>0>j>>>0?f+1|0:f;f=Ne(j,f,h,0);K[g+20>>2]=f;j=f;p=i;f=i&31;if((i&63)>>>0>=32){k=-1<>>32-f;f=-1<>31)+(i>>>0>>0)|0)|0;j=o;o=p&31;if((p&63)>>>0>=32){o=k>>o}else{o=((1<>>o}k=h>>31;s=k+v|0;j=h+u|0;s=j>>>0>>0?s+1|0:s;j=Me(j,s,h,k);h=i-j|0;j=f-((j>>31)+(i>>>0>>0)|0)|0;k=p&31;if((p&63)>>>0>=32){j=j>>k}else{j=((1<>>k}K[g+12>>2]=o-j;j=f-((n>>31)+(i>>>0>>0)|0)|0;h=i-n|0;n=p&31;if((p&63)>>>0>=32){n=j>>n}else{n=((1<>>n}j=l>>31;k=j+x|0;h=l+w|0;k=h>>>0>>0?k+1|0:k;l=Me(h,k,l,j);h=i-l|0;i=f-((l>>31)+(i>>>0>>0)|0)|0;f=h;h=p&31;if((p&63)>>>0>=32){f=i>>h}else{f=((1<>>h}K[g+8>>2]=n-f;g=g+52|0;q=q+1|0;if((q|0)!=(m|0)){continue}break}}if(m>>>0>>0){g=K[c+24>>2];while(1){f=Q(m,52);Ga(K[(f+g|0)+44>>2]);g=K[c+24>>2];K[(f+g|0)+44>>2]=0;m=m+1|0;if(m>>>0>2]){continue}break}K[c+16>>2]=K[K[a+96>>2]+16>>2]}g=K[a+100>>2];if(g){Ya(g)}f=Bb();K[a+100>>2]=f;g=0;if(!f){break a}Ob(c,f);K[a+44>>2]=e;if(!$a(K[a+216>>2],24,d)){break a}h=K[a+216>>2];e=K[h>>2];m=K[h+8>>2];b:{if(e){g=1;i=e&1;if((e|0)==1){e=0}else{f=e&-2;q=0;while(1){e=0;c:{if(!g){break c}e=0;if(!(va[K[m>>2]](a,b,d)|0)){break c}e=(va[K[m+4>>2]](a,b,d)|0)!=0}g=e;m=m+8|0;q=q+2|0;if((f|0)!=(q|0)){continue}break}e=!g}g=i?0:g;if(!(e|!i)){g=(va[K[m>>2]](a,b,d)|0)!=0}Ta(h);if(g){break b}Ya(K[a+96>>2]);g=0;K[a+96>>2]=0;break a}Ta(h)}g=Sb(a,c)}ra=r+16|0;return g|0}function lc(a,b,c,d,e,f,g){var h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;a:{n=Q(e,3);h=K[b>>2]>>>n|0;if(h&2097168){break a}h=h&495;if(!h){break a}o=a+28|0;l=o+(L[h+K[a+108>>2]|0]<<2)|0;K[a+104>>2]=l;k=K[l>>2];i=K[k>>2];h=K[a+4>>2]-i|0;K[a+4>>2]=h;j=K[a>>2];b:{if(j>>>16>>>0>>0){m=K[k+4>>2];K[a+4>>2]=i;h=h>>>0>>0;K[l>>2]=K[k+(h?8:12)>>2];k=h?m:!m;h=K[a+8>>2];while(1){c:{if(h){break c}h=K[a+16>>2];m=h+1|0;l=L[h+1|0];if(L[h|0]==255){if(l>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;j=j+65280|0;h=8;break c}K[a+16>>2]=m;j=(l<<9)+j|0;h=7;break c}K[a+16>>2]=m;h=8;j=(l<<8)+j|0}h=h-1|0;K[a+8>>2]=h;j=j<<1;K[a>>2]=j;i=i<<1;K[a+4>>2]=i;if(i>>>0<32768){continue}break}h=i;break b}j=j-(i<<16)|0;K[a>>2]=j;if(!(h&32768)){m=K[k+4>>2];i=h>>>0>>0;K[l>>2]=K[k+(i?12:8)>>2];k=i?!m:m;i=K[a+8>>2];while(1){d:{if(i){break d}i=K[a+16>>2];m=i+1|0;l=L[i+1|0];if(L[i|0]==255){if(l>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;j=j+65280|0;i=8;break d}K[a+16>>2]=m;j=(l<<9)+j|0;i=7;break d}K[a+16>>2]=m;i=8;j=(l<<8)+j|0}i=i-1|0;K[a+8>>2]=i;j=j<<1;K[a>>2]=j;h=h<<1;K[a+4>>2]=h;if(h>>>0<32768){continue}break}break b}k=K[k+4>>2]}e:{if(!k){break e}p=b-4|0;i=K[b>>2];k=K[b+4>>2]>>>n+17&4|(K[p>>2]>>>n+19&1|(i>>>n+16&64|i>>>n&170|i>>>(e?n+12|0:14)&16));m=o+(L[k+24336|0]<<2)|0;K[a+104>>2]=m;l=K[m>>2];i=K[l>>2];h=h-i|0;K[a+4>>2]=h;o=L[k+24592|0];f:{if(j>>>16>>>0>>0){k=K[l+4>>2];K[a+4>>2]=i;h=h>>>0>>0;K[m>>2]=K[l+(h?8:12)>>2];l=h?k:!k;h=K[a+8>>2];while(1){g:{if(h){break g}h=K[a+16>>2];m=h+1|0;k=L[h+1|0];if(L[h|0]==255){if(k>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;j=j+65280|0;h=8;break g}K[a+16>>2]=m;j=(k<<9)+j|0;h=7;break g}K[a+16>>2]=m;h=8;j=(k<<8)+j|0}h=h-1|0;K[a+8>>2]=h;j=j<<1;K[a>>2]=j;i=i<<1;K[a+4>>2]=i;if(i>>>0<32768){continue}break}break f}k=j-(i<<16)|0;K[a>>2]=k;if(!(h&32768)){j=K[l+4>>2];i=h>>>0>>0;K[m>>2]=K[l+(i?12:8)>>2];l=i?!j:j;j=K[a+8>>2];while(1){h:{if(j){break h}j=K[a+16>>2];m=j+1|0;i=L[j+1|0];if(L[j|0]==255){if(i>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;k=k+65280|0;j=8;break h}K[a+16>>2]=m;k=(i<<9)+k|0;j=7;break h}K[a+16>>2]=m;j=8;k=(i<<8)+k|0}j=j-1|0;K[a+8>>2]=j;k=k<<1;K[a>>2]=k;h=h<<1;K[a+4>>2]=h;if(h>>>0<32768){continue}break}break f}l=K[l+4>>2]}K[c>>2]=(l|0)==(o|0)?d:0-d|0;K[p>>2]=K[p>>2]|32<>2]=K[b>>2]|(c<<19|16)<>2]=K[b+4>>2]|8<>2]=K[a+4>>2]|32768;K[a>>2]=K[a>>2]|c<<31|65536;a=a-4|0;K[a>>2]=K[a>>2]|131072}if((e|0)!=3){break e}a=(f<<2)+b|0;K[a+4>>2]=K[a+4>>2]|1;K[a>>2]=K[a>>2]|c<<18|2;a=a-4|0;K[a>>2]=K[a>>2]|4}K[b>>2]=K[b>>2]|2097152<>2];a:{if(!(!(K[K[a+180>>2]+5596>>2]?K[a+128>>2]!=1|K[a+132>>2]!=1:1)|K[a+8>>2]==8)){Fa(g,1,10577,0);break a}m=K[b+16>>2];b:{if(!m){break b}k=K[a+184>>2];l=K[b+24>>2];if(m>>>0>=8){p=m&-8;while(1){K[(Q(i,52)+l|0)+40>>2]=k;K[(Q(i|1,52)+l|0)+40>>2]=k;K[(Q(i|2,52)+l|0)+40>>2]=k;K[(Q(i|3,52)+l|0)+40>>2]=k;K[(Q(i|4,52)+l|0)+40>>2]=k;K[(Q(i|5,52)+l|0)+40>>2]=k;K[(Q(i|6,52)+l|0)+40>>2]=k;K[(Q(i|7,52)+l|0)+40>>2]=k;i=i+8|0;n=n+8|0;if((p|0)!=(n|0)){continue}break}}m=m&7;if(!m){break b}while(1){K[(Q(i,52)+l|0)+40>>2]=k;i=i+1|0;o=o+1|0;if((m|0)!=(o|0)){continue}break}}if(!(c|d|e|f)){Fa(g,4,6307,0);K[a+28>>2]=0;K[a+32>>2]=0;c=K[a+132>>2];K[a+36>>2]=K[a+128>>2];K[a+40>>2]=c;K[b>>2]=K[j>>2];K[b+4>>2]=K[j+4>>2];K[b+8>>2]=K[j+8>>2];K[b+12>>2]=K[j+12>>2];i=Db(b,g);break a}if((c|0)<0){K[h>>2]=c;Fa(g,1,12565,h);i=0;break a}i=K[j+8>>2];if(i>>>0>>0){K[h+20>>2]=i;K[h+16>>2]=c;Fa(g,1,13033,h+16|0);i=0;break a}i=K[j>>2];c:{if(i>>>0>c>>>0){K[h+196>>2]=i;K[h+192>>2]=c;Fa(g,2,13385,h+192|0);K[a+28>>2]=0;c=K[j>>2];break c}K[a+28>>2]=(c-K[a+108>>2]>>>0)/N[a+116>>2]}K[b>>2]=c;if((d|0)<0){K[h+32>>2]=d;Fa(g,1,12501,h+32|0);i=0;break a}c=K[j+12>>2];if(c>>>0>>0){K[h+52>>2]=c;K[h+48>>2]=d;Fa(g,1,12860,h+48|0);i=0;break a}c=K[j+4>>2];d:{if(c>>>0>d>>>0){K[h+180>>2]=c;K[h+176>>2]=d;Fa(g,2,13210,h+176|0);K[a+32>>2]=0;d=K[j+4>>2];break d}K[a+32>>2]=(d-K[a+112>>2]>>>0)/N[a+120>>2]}K[b+4>>2]=d;i=0;if((e|0)<=0){K[h+64>>2]=e;Fa(g,1,12435,h- -64|0);break a}c=K[j>>2];if(c>>>0>e>>>0){K[h+84>>2]=c;K[h+80>>2]=e;Fa(g,1,13296,h+80|0);break a}c=K[j+8>>2];e:{if(c>>>0>>0){K[h+164>>2]=c;K[h+160>>2]=e;Fa(g,2,12945,h+160|0);K[a+36>>2]=K[a+128>>2];e=K[j+8>>2];break e}k=0;d=e-K[a+108>>2]|0;l=d;c=K[a+116>>2];d=d+c|0;k=l>>>0>d>>>0?1:k;q=a,r=Ne(d-1|0,k-!d|0,c,0),K[q+36>>2]=r}K[b+8>>2]=e;if((f|0)<=0){K[h+96>>2]=f;Fa(g,1,12368,h+96|0);break a}c=K[j+4>>2];if(c>>>0>f>>>0){K[h+116>>2]=c;K[h+112>>2]=f;Fa(g,1,13120,h+112|0);break a}c=K[j+12>>2];f:{if(c>>>0>>0){K[h+148>>2]=c;K[h+144>>2]=f;Fa(g,2,12771,h+144|0);K[a+40>>2]=K[a+132>>2];f=K[j+12>>2];break f}e=0;d=f-K[a+112>>2]|0;l=d;c=K[a+120>>2];d=d+c|0;e=l>>>0>d>>>0?1:e;q=a,r=Ne(d-1|0,e-!d|0,c,0),K[q+40>>2]=r}K[b+12>>2]=f;I[a+92|0]=L[a+92|0]|2;if(!Db(b,g)){break a}a=K[b>>2];c=K[b+4>>2];d=K[b+12>>2];K[h+136>>2]=K[b+8>>2];K[h+140>>2]=d;K[h+128>>2]=a;K[h+132>>2]=c;Fa(g,4,7529,h+128|0);i=1}ra=h+208|0;return i|0}function kc(a,b,c,d,e,f){var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;a:{m=Q(e,3);g=K[b>>2]>>>m|0;if(g&2097168){break a}n=a+28|0;k=n+(L[K[a+108>>2]+(g&495)|0]<<2)|0;K[a+104>>2]=k;j=K[k>>2];h=K[j>>2];g=K[a+4>>2]-h|0;K[a+4>>2]=g;i=K[a>>2];b:{if(i>>>16>>>0>>0){l=K[j+4>>2];K[a+4>>2]=h;g=g>>>0>>0;K[k>>2]=K[j+(g?8:12)>>2];j=g?l:!l;g=K[a+8>>2];while(1){c:{if(g){break c}g=K[a+16>>2];l=g+1|0;k=L[g+1|0];if(L[g|0]==255){if(k>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;i=i+65280|0;g=8;break c}K[a+16>>2]=l;i=(k<<9)+i|0;g=7;break c}K[a+16>>2]=l;g=8;i=(k<<8)+i|0}g=g-1|0;K[a+8>>2]=g;i=i<<1;K[a>>2]=i;h=h<<1;K[a+4>>2]=h;if(h>>>0<32768){continue}break}g=h;break b}i=i-(h<<16)|0;K[a>>2]=i;if(!(g&32768)){l=K[j+4>>2];h=g>>>0>>0;K[k>>2]=K[j+(h?12:8)>>2];j=h?!l:l;h=K[a+8>>2];while(1){d:{if(h){break d}h=K[a+16>>2];l=h+1|0;k=L[h+1|0];if(L[h|0]==255){if(k>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;i=i+65280|0;h=8;break d}K[a+16>>2]=l;i=(k<<9)+i|0;h=7;break d}K[a+16>>2]=l;h=8;i=(k<<8)+i|0}h=h-1|0;K[a+8>>2]=h;i=i<<1;K[a>>2]=i;g=g<<1;K[a+4>>2]=g;if(g>>>0<32768){continue}break}break b}j=K[j+4>>2]}if(!j){break a}j=n;n=b-4|0;h=K[b>>2];o=K[b+4>>2]>>>m+17&4|(K[n>>2]>>>m+19&1|(h>>>m+16&64|h>>>m&170|h>>>(e?m+12|0:14)&16));l=j+(L[o+24336|0]<<2)|0;K[a+104>>2]=l;k=K[l>>2];h=K[k>>2];g=g-h|0;K[a+4>>2]=g;e:{if(i>>>16>>>0>>0){j=K[k+4>>2];K[a+4>>2]=h;g=g>>>0>>0;K[l>>2]=K[k+(g?8:12)>>2];k=g?j:!j;g=K[a+8>>2];while(1){f:{if(g){break f}g=K[a+16>>2];l=g+1|0;j=L[g+1|0];if(L[g|0]==255){if(j>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;i=i+65280|0;g=8;break f}K[a+16>>2]=l;i=(j<<9)+i|0;g=7;break f}K[a+16>>2]=l;g=8;i=(j<<8)+i|0}g=g-1|0;K[a+8>>2]=g;i=i<<1;K[a>>2]=i;h=h<<1;K[a+4>>2]=h;if(h>>>0<32768){continue}break}break e}j=i-(h<<16)|0;K[a>>2]=j;if(!(g&32768)){i=K[k+4>>2];h=g>>>0>>0;K[l>>2]=K[k+(h?12:8)>>2];k=h?!i:i;i=K[a+8>>2];while(1){g:{if(i){break g}i=K[a+16>>2];l=i+1|0;h=L[i+1|0];if(L[i|0]==255){if(h>>>0>=144){K[a+12>>2]=K[a+12>>2]+1;j=j+65280|0;i=8;break g}K[a+16>>2]=l;j=(h<<9)+j|0;i=7;break g}K[a+16>>2]=l;i=8;j=(h<<8)+j|0}i=i-1|0;K[a+8>>2]=i;j=j<<1;K[a>>2]=j;g=g<<1;K[a+4>>2]=g;if(g>>>0<32768){continue}break}break e}k=K[k+4>>2]}g=c;c=L[o+24592|0];K[g>>2]=(c|0)==(k|0)?d:0-d|0;K[n>>2]=K[n>>2]|32<>2]=K[b>>2]|(d<<19|16)<>2]=K[b+4>>2]|8<>2]<<2)+b|0;K[c+4>>2]=K[c+4>>2]|32768;K[c>>2]=K[c>>2]|d<<31|65536;c=c-4|0;K[c>>2]=K[c>>2]|131072}if((e|0)!=3){break a}a=(K[a+124>>2]<<2)+b|0;K[a+4>>2]=K[a+4>>2]|4;K[a+12>>2]=K[a+12>>2]|1;K[a+8>>2]=K[a+8>>2]|d<<18|2}}function be(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;e=ra-112|0;ra=e;j=1024;a:{b:{h=Ia(1,1024);if(h){l=e+92|0;k=e+108|0;while(1){c:{d:{d=e+104|0;e:{if((Na(b,d,8,c)|0)!=8){break e}Ha(d,e+88|0,4);Ha(k,l,4);f=8;f:{g:{h:{i:{switch(K[e+88>>2]){case 0:d=Va(b);g=ua;if((g|0)<0){g=1}else{g=d>>>0<4294967288&(g|0)<=0}if(g){break h}Fa(c,1,8412,0);break e;case 1:break i;default:break f}}d=e+104|0;if((Na(b,d,8,c)|0)!=8){break e}Ha(d,e+100|0,4);if(!K[e+100>>2]){break g}Fa(c,1,8412,0);break e}K[e+88>>2]=d+8;break f}Ha(k,e+88|0,4);f=16}d=K[e+92>>2];if((d|0)==1785737827){b=K[a+100>>2];if(b&4){K[a+100>>2]=b|8;break e}Fa(c,1,5665,0);Ga(h);a=0;break a}i=K[e+88>>2];if(!i){Fa(c,1,3231,0);Ga(h);a=0;break a}if(f>>>0>i>>>0){K[e+4>>2]=d;K[e>>2]=i;Fa(c,1,13896,e);break b}j:{k:{l:{m:{n:{o:{p:{q:{r:{s:{if((d|0)<=1668246641){if((d|0)==1651532643){break r}if((d|0)==1667523942){break p}if((d|0)!=1668112752){break s}g=25248;break n}if((d|0)<=1783635999){if((d|0)==1668246642){break o}g=25216;if((d|0)==1768449138){break n}if((d|0)!=1718909296){break s}g=25192;break l}if((d|0)==1885564018){break q}if((d|0)==1783636e3){break m}g=25200;if((d|0)==1785737832){break l}}d=K[a+100>>2];if(d&1){break j}Fa(c,1,2025,0);Ga(h);a=0;break a}g=25232;break n}g=25240;break n}g=25256;break n}g=25224}K[e+76>>2]=d&255;K[e+64>>2]=d>>>24;K[e+72>>2]=d>>>8&255;K[e+68>>2]=d>>>16&255;Fa(c,2,1974,e- -64|0);f=i-f|0;if(L[a+100|0]&4){break k}d=K[e+92>>2];K[e+48>>2]=d>>>24;K[e+60>>2]=d&255;K[e+52>>2]=d>>>16&255;K[e+56>>2]=d>>>8&255;Fa(c,2,6734,e+48|0);K[a+100>>2]=K[a+100>>2]|2147483647;d=vb(b,f,c);if(!ua&(d|0)==(f|0)){continue}Fa(c,1,3711,0);Ga(h);a=0;break a}g=25184}f=i-f|0}d=f;f=Va(b);i=ua;if((i|0)<0){f=1}else{f=(i|0)<=0&d>>>0>f>>>0}if(f){f=K[e+88>>2];a=K[e+92>>2];m=e,n=Va(b),K[m+40>>2]=n;K[e+36>>2]=d;K[e+32>>2]=a&255;K[e+20>>2]=a>>>24;K[e+16>>2]=f;K[e+28>>2]=a>>>8&255;K[e+24>>2]=a>>>16&255;Fa(c,1,15643,e+16|0);break b}if(d>>>0<=j>>>0){f=h;break c}j=d;f=La(h,d);if(f){break c}Ga(h);Fa(c,1,2156,0);a=0;break a}if(!(d&2)){Fa(c,1,2095,0);Ga(h);a=0;break a}K[a+100>>2]=d|2147483647;d=i-f|0;f=vb(b,d,c);if(!ua&(d|0)==(f|0)){continue}if(!(L[a+100|0]&8)){break d}Fa(c,2,3711,0)}Ga(h);a=1;break a}Fa(c,1,3711,0);Ga(h);a=0;break a}if((Na(b,f,d,c)|0)!=(d|0)){Fa(c,1,3761,0);Ga(f);a=0;break a}h=f;if(va[K[g+4>>2]](a,f,d,c)|0){continue}break}Ga(f);a=0;break a}Fa(c,1,4886,0);a=0;break a}Ga(h);a=0}ra=e+112|0;return a|0}function pe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;g=ra-16|0;ra=g;if(K[a+8>>2]==16){h=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{h=K[a+12>>2]}a:{if(c>>>0<=1){Fa(d,1,4684,0);a=0;break a}Ha(b,g+12|0,2);if(K[g+12>>2]){Fa(d,2,5860,0);a=1;break a}if(c>>>0<=6){Fa(d,1,4684,0);a=0;break a}Ha(b+2|0,g+8|0,1);j=K[h+5628>>2];a=j;b:{c:{d:{e=K[h+5632>>2];if(!e){break d}i=K[g+8>>2];while(1){if((i|0)==K[a>>2]){break d}a=a+20|0;f=f+1|0;if((e|0)!=(f|0)){continue}break}break c}if((e|0)!=(f|0)){break b}}if((e|0)==K[h+5636>>2]){a=e+10|0;K[h+5636>>2]=a;a=La(j,Q(a,20));if(!a){Ga(K[h+5628>>2]);K[h+5636>>2]=0;K[h+5628>>2]=0;K[h+5632>>2]=0;Fa(d,1,4710,0);a=0;break a}K[h+5628>>2]=a;e=K[h+5632>>2];f=Q(K[h+5636>>2]-e|0,20);if(f){B(a+Q(e,20)|0,0,f)}j=K[h+5628>>2];e=K[h+5632>>2]}a=Q(e,20)+j|0;n=1}K[a>>2]=K[g+8>>2];Ha(b+3|0,g+12|0,2);if(K[g+12>>2]){Fa(d,2,5860,0);a=1;break a}Ha(b+5|0,g+4|0,2);f=K[g+4>>2];if(f>>>0>=2){Fa(d,2,3093,0);a=1;break a}e=c-7|0;if(f){c=b+7|0;j=0;while(1){if(e>>>0<=2){Fa(d,1,4684,0);a=0;break a}Ha(c,g+12|0,1);if(K[g+12>>2]!=1){Fa(d,2,5542,0);a=1;break a}Ha(c+1|0,g,2);f=K[g>>2];b=f&32767;K[a+4>>2]=b;i=e-3|0;e=(f>>>15|0)+1|0;k=Q(e,b)+2|0;if(i>>>0>>0){Fa(d,1,4684,0);a=0;break a}c=c+3|0;f=0;if(b){while(1){Ha(c,g+12|0,e);if(K[g+12>>2]!=(f|0)){Fa(d,2,6222,0);a=1;break a}c=c+e|0;f=f+1|0;if(f>>>0>2]){continue}break}}Ha(c,g,2);e=K[g>>2];b=e&32767;K[g>>2]=b;if((b|0)!=K[a+4>>2]){Fa(d,2,3269,0);a=1;break a}e=(e>>>15|0)+1|0;l=Q(e,b)+3|0;k=i-k|0;if(l>>>0>k>>>0){Fa(d,1,4684,0);a=0;break a}c=c+2|0;f=0;if(b){while(1){Ha(c,g+12|0,e);if(K[g+12>>2]!=(f|0)){Fa(d,2,6222,0);a=1;break a}c=c+e|0;f=f+1|0;if(f>>>0>2]){continue}break}}Ha(c,g+12|0,3);e=K[g+12>>2];K[a+8>>2]=0;K[a+12>>2]=0;I[a+16|0]=!(e&65536)|L[a+16|0]&254;i=e&255;K[g+8>>2]=i;e:{if(!i){break e}m=K[h+5620>>2];if(m){f=K[h+5616>>2];b=0;while(1){if((i|0)==K[f+8>>2]){K[a+8>>2]=f;break e}f=f+20|0;b=b+1|0;if((m|0)!=(b|0)){continue}break}}Fa(d,1,4684,0);a=0;break a}e=e>>>8&255;K[g+8>>2]=e;f:{if(!e){break f}i=K[h+5620>>2];if(i){f=K[h+5616>>2];b=0;while(1){if((e|0)==K[f+8>>2]){K[a+12>>2]=f;break f}f=f+20|0;b=b+1|0;if((i|0)!=(b|0)){continue}break}}Fa(d,1,4684,0);a=0;break a}e=k-l|0;c=c+3|0;j=j+1|0;if(j>>>0>2]){continue}break}}if(e){Fa(d,1,4684,0);a=0;break a}a=1;if(!n){break a}K[h+5632>>2]=K[h+5632>>2]+1;a=1}ra=g+16|0;return a|0}function kd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;if(N[a+44>>2]>=8){i=K[a+40>>2];l=8;while(1){k=K[a+12>>2]<<5;e=K[a>>2];g=K[a+36>>2];b=K[a+16>>2];h=K[a+20>>2];a:{if(b>>>0>=h>>>0){break a}j=e+k|0;d=b+1|0;if(h-b&1){c=j+(b<<6)|0;b=(Q(b,g)<<2)+i|0;f=K[b+4>>2];K[c>>2]=K[b>>2];K[c+4>>2]=f;f=K[b+28>>2];K[c+24>>2]=K[b+24>>2];K[c+28>>2]=f;f=K[b+20>>2];K[c+16>>2]=K[b+16>>2];K[c+20>>2]=f;f=K[b+12>>2];K[c+8>>2]=K[b+8>>2];K[c+12>>2]=f;b=d}if((d|0)==(h|0)){break a}while(1){d=(Q(b,g)<<2)+i|0;f=K[d+4>>2];c=j+(b<<6)|0;K[c>>2]=K[d>>2];K[c+4>>2]=f;f=K[d+28>>2];K[c+24>>2]=K[d+24>>2];K[c+28>>2]=f;f=K[d+20>>2];K[c+16>>2]=K[d+16>>2];K[c+20>>2]=f;f=K[d+12>>2];K[c+8>>2]=K[d+8>>2];K[c+12>>2]=f;d=b+1|0;c=j+(d<<6)|0;d=(Q(d,g)<<2)+i|0;f=K[d+28>>2];K[c+24>>2]=K[d+24>>2];K[c+28>>2]=f;f=K[d+20>>2];K[c+16>>2]=K[d+16>>2];K[c+20>>2]=f;f=K[d+12>>2];K[c+8>>2]=K[d+8>>2];K[c+12>>2]=f;f=K[d+4>>2];K[c>>2]=K[d>>2];K[c+4>>2]=f;b=b+2|0;if((h|0)!=(b|0)){continue}break}}b=K[a+24>>2];h=K[a+28>>2];b:{if(b>>>0>=h>>>0){break b}j=(e-k|0)+32|0;k=(Q(g,K[a+8>>2])<<2)+i|0;d=b+1|0;if(h-b&1){c=j+(b<<6)|0;b=k+(Q(b,g)<<2)|0;e=K[b+4>>2];K[c>>2]=K[b>>2];K[c+4>>2]=e;e=K[b+28>>2];K[c+24>>2]=K[b+24>>2];K[c+28>>2]=e;e=K[b+20>>2];K[c+16>>2]=K[b+16>>2];K[c+20>>2]=e;e=K[b+12>>2];K[c+8>>2]=K[b+8>>2];K[c+12>>2]=e;b=d}if((d|0)==(h|0)){break b}while(1){d=k+(Q(b,g)<<2)|0;e=K[d+4>>2];c=j+(b<<6)|0;K[c>>2]=K[d>>2];K[c+4>>2]=e;e=K[d+28>>2];K[c+24>>2]=K[d+24>>2];K[c+28>>2]=e;e=K[d+20>>2];K[c+16>>2]=K[d+16>>2];K[c+20>>2]=e;e=K[d+12>>2];K[c+8>>2]=K[d+8>>2];K[c+12>>2]=e;d=b+1|0;c=j+(d<<6)|0;d=k+(Q(d,g)<<2)|0;e=K[d+28>>2];K[c+24>>2]=K[d+24>>2];K[c+28>>2]=e;e=K[d+20>>2];K[c+16>>2]=K[d+16>>2];K[c+20>>2]=e;e=K[d+12>>2];K[c+8>>2]=K[d+8>>2];K[c+12>>2]=e;e=K[d+4>>2];K[c>>2]=K[d>>2];K[c+4>>2]=e;b=b+2|0;if((h|0)!=(b|0)){continue}break}}Za(a);b=0;if(K[a+32>>2]){while(1){d=K[a>>2]+(b<<5)|0;c=K[d+4>>2];g=(Q(K[a+36>>2],b)<<2)+i|0;K[g>>2]=K[d>>2];K[g+4>>2]=c;c=K[d+28>>2];K[g+24>>2]=K[d+24>>2];K[g+28>>2]=c;c=K[d+20>>2];K[g+16>>2]=K[d+16>>2];K[g+20>>2]=c;c=K[d+12>>2];K[g+8>>2]=K[d+8>>2];K[g+12>>2]=c;b=b+1|0;if(b>>>0>2]){continue}break}}i=i+32|0;l=l+8|0;if(l>>>0<=N[a+44>>2]){continue}break}}Ga(K[a>>2]);Ga(a)}function td(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;c=K[b>>2]+7&-8;K[b>>2]=c+16;q=a;b=K[c>>2];a=K[c+4>>2];d=K[c+8>>2];c=K[c+12>>2];r=c;g=ra-32|0;ra=g;f=c&65535;e=d;d=0;c=c>>>16&32767;o=c;a:{if(c-15361>>>0<=2045){c=f<<4|e>>>28;d=e<<4|a>>>28;f=o-15360|0;a=a&268435455;b:{if((a|0)==134217728&(b|0)!=0|a>>>0>134217728){d=d+1|0;c=d?c:c+1|0;break b}if(b|(a|0)!=134217728){break b}a=d;d=d+(d&1)|0;c=a>>>0>d>>>0?c+1|0:c}a=d;d=c>>>0>1048575;b=d?0:a;a=d?0:c;c=0;e=f;f=d+f|0;e=e>>>0>f>>>0?1:c;break a}if(!(!(b|e|(a|f))|((c|0)!=32767|(d|0)!=0))){b=e;e=f<<4|b>>>28;b=b<<4|a>>>28;a=e|524288;f=2047;e=0;break a}if(o>>>0>17406){b=0;a=0;f=2047;e=0;break a}j=!(c|d);p=j?15360:15361;k=p-o|0;if((k|0)>112){b=0;a=0;f=0;e=0;break a}d=b;c=a;l=e;e=j?f:f|65536;f=e;h=l;m=128-k|0;c:{if(m&64){e=d;c=m+-64|0;d=c&31;if((c&63)>>>0>=32){c=b<>>32-d|a<>>0>=32){j=h<>>32-i|e<>>0>=32){e=0;h=c>>>h|0}else{e=c>>>h|0;h=((1<>>h}h=n|h;e=e|j;n=d;i=m&31;if((m&63)>>>0>=32){j=d<>>32-i|c<>2]=d;K[g+20>>2]=c;K[g+24>>2]=h;K[g+28>>2]=e;d:{if(k&64){c=l;b=k+-64|0;a=b&31;if((b&63)>>>0>=32){e=0;b=f>>>a|0}else{e=f>>>a|0;b=((1<>>a}a=e;l=0;f=0;break d}if(!k){break d}e=l;c=64-k|0;d=c&31;if((c&63)>>>0>=32){c=e<>>32-d|f<>>0>=32){j=0;a=a>>>b|0}else{j=a>>>b|0;a=((1<>>b}b=l|a;a=c|j;d=k&31;if((k&63)>>>0>=32){c=0;l=f>>>d|0}else{c=f>>>d|0;l=((1<>>d}f=c}K[g>>2]=b;K[g+4>>2]=a;K[g+8>>2]=l;K[g+12>>2]=f;a=K[g+8>>2];d=a<<4;a=K[g+12>>2]<<4|a>>>28;f=K[g>>2];b=K[g+4>>2];e=b;b=b>>>28|d;c=e&268435455;f=f|(o|0)!=(p|0)&(K[g+16>>2]|K[g+24>>2]|(K[g+20>>2]|K[g+28>>2]))!=0;e:{if((c|0)==134217728&(f|0)!=0|c>>>0>134217728){b=b+1|0;a=b?a:a+1|0;break e}if(f|(c|0)!=134217728){break e}c=a;a=b;b=b+(b&1)|0;a=a>>>0>b>>>0?c+1|0:c}f=a>>>0>1048575;a=f?a^1048576:a;e=0}ra=g+32|0;x(0,b|0);x(1,a|(r&-2147483648|f<<20));s=q,t=+z(),P[s>>3]=t}function Wc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;q=K[a+24>>2];if(!K[q+16>>2]){return 1}r=K[q+24>>2];o=K[K[K[a+20>>2]>>2]+20>>2];while(1){e=K[r+36>>2];K[b+36>>2]=e;c=Q(e,152);e=K[o+28>>2];d=c+e|0;u=K[a+64>>2];a:{if(u){e=e+Q(K[o+24>>2],152)|0;p=K[e-144>>2]-K[e-152>>2]|0;c=d+12|0;f=d+4|0;e=K[d+8>>2];h=K[d>>2];g=36;break a}c=d+148|0;f=d+140|0;e=K[d+144>>2];h=K[d+136>>2];p=e-h|0;g=52}v=K[g+o>>2];b:{c:{if(!v){break c}l=K[f>>2];n=K[c>>2];i=e-h|0;f=K[b+40>>2];c=f&31;if((f&63)>>>0>=32){d=-1<>>32-c;c=-1<>2];k=m+j|0;g=d^-1;c=g;c=k>>>0>>0?c+1|0:c;d=f&31;if((f&63)>>>0>=32){k=c>>>d|0}else{k=((1<>>d}d=K[b+8>>2];j=K[b+16>>2];m=j+m|0;c=g;c=m>>>0>>0?c+1|0:c;g=f&31;if((f&63)>>>0>=32){f=c>>>g|0}else{f=((1<>>g}c=f+d|0;d:{if(f>>>0>>0){s=h-f|0;g=0;if(c>>>0>=e>>>0){m=0;e=i;break d}e=c-h|0;m=i-e|0;break d}g=f-h|0;if(c>>>0>=e>>>0){e=i-g|0;s=0;m=0;break d}m=e-c|0;s=0;e=d}c=n-l|0;f=K[b+12>>2];i=f+k|0;e:{if(k>>>0>>0){t=l-k|0;k=0;j=0;if(i>>>0>=n>>>0){break e}j=c;c=i-l|0;j=j-c|0;break e}k=k-l|0;if(i>>>0>=n>>>0){c=c-k|0;t=0;j=0;break e}t=0;c=f;j=n-i|0}h=0;if((g|k|(m|j)|(c|e))<0){break b}i=Q(k,p)+g|0;g=K[b+44>>2];l=Q(d,t)+s|0;f:{g:{if(!(i|g|(l|(d|0)!=(p|0))|(d|0)!=(e|0))){if((c|0)!=(f|0)){break g}e=(u?36:52)+o|0;K[b+44>>2]=K[e>>2];K[e>>2]=0;break c}if(g){break f}}Le(f,0,d);if(ua|!f){break b}d=Q(d,f);if(d>>>0>1073741823){break b}d=Ma(d<<2);K[b+44>>2]=d;if(!d){break b}f=K[b+8>>2];g=K[b+12>>2];if((f|0)==(e|0)&(g|0)==(c|0)){break f}f=Q(f,g)<<2;if(!f){break f}B(d,0,f)}if(!c){break c}g=c&1;e=e<<2;h=K[b+44>>2]+(l<<2)|0;d=(i<<2)+v|0;if((c|0)!=1){i=c&2147483646;c=0;while(1){l=!e;if(!l){E(h,d,e)}n=p<<2;d=n+d|0;f=(K[b+8>>2]<<2)+h|0;if(!l){E(f,d,e)}d=d+n|0;h=f+(K[b+8>>2]<<2)|0;c=c+2|0;if((i|0)!=(c|0)){continue}break}}if(!g|!e){break c}E(h,d,e)}o=o+76|0;r=r+52|0;b=b+52|0;h=1;w=w+1|0;if(w>>>0>2]){continue}}break}return h}function Eb(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0;if(a){a:{if(K[a>>2]){b=K[a+12>>2];if(b){nb(b);Ga(K[a+12>>2]);K[a+12>>2]=0}b=K[a+16>>2];if(b){Ga(b);K[a+16>>2]=0;K[a+20>>2]=0}Ga(K[a+64>>2]);K[a+60>>2]=0;K[a+64>>2]=0;Ga(K[a+72>>2]);K[a+72>>2]=0;Ga(K[a+88>>2]);K[a+88>>2]=0;break a}b=K[a+44>>2];if(b){Ga(b);K[a+44>>2]=0}b=K[a+32>>2];if(b){Ga(b);K[a+32>>2]=0;K[a+36>>2]=0}b=K[a+52>>2];if(!b){break a}Ga(b);K[a+52>>2]=0;K[a+56>>2]=0}hc(K[a+232>>2]);b=K[a+180>>2];if(b){e=Q(K[a+128>>2],K[a+132>>2]);if(e){while(1){nb(b);b=b+5644|0;c=c+1|0;if((e|0)!=(c|0)){continue}break}b=K[a+180>>2]}Ga(b);K[a+180>>2]=0}b=K[a+140>>2];if(b){c=K[a+136>>2];if(c){b=0;while(1){e=K[K[a+140>>2]+(b<<3)>>2];if(e){Ga(e);c=K[a+136>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=K[a+140>>2]}K[a+136>>2]=0;Ga(b);K[a+140>>2]=0}Ga(K[a+160>>2]);K[a+144>>2]=0;K[a+160>>2]=0;Ga(K[a+124>>2]);K[a+124>>2]=0;if(!(L[a+212|0]&2)){Ga(K[a+192>>2])}B(a+104|0,0,112);tb(K[a+216>>2]);K[a+216>>2]=0;tb(K[a+220>>2]);K[a+216>>2]=0;d=K[a+224>>2];if(d){b=K[d+28>>2];if(b){Ga(b);K[d+28>>2]=0}c=K[d+40>>2];if(c){if(K[d+36>>2]){while(1){e=Q(g,40);b=K[(e+c|0)+36>>2];if(b){Ga(b);c=K[d+40>>2];K[(e+c|0)+36>>2]=0}b=K[(c+e|0)+16>>2];if(b){Ga(b);c=K[d+40>>2];K[(e+c|0)+16>>2]=0}b=K[(c+e|0)+24>>2];if(b){Ga(b);c=K[d+40>>2];K[(e+c|0)+24>>2]=0}g=g+1|0;if(g>>>0>2]){continue}break}}Ga(c);K[d+40>>2]=0}Ga(d)}K[a+224>>2]=0;Ya(K[a+96>>2]);K[a+96>>2]=0;Ya(K[a+100>>2]);K[a+100>>2]=0;f=K[a+236>>2];if(f){b:{if(!K[f+8>>2]){break b}if(K[f+12>>2]){K[f+40>>2]=0;while(1){if(K[f+24>>2]>0){continue}break}}K[f+16>>2]=1;Ga(K[f>>2]);c=K[f+28>>2];if(!c){break b}while(1){b=K[c+4>>2];Ga(c);K[f+28>>2]=b;c=b;if(b){continue}break}}d=K[f+36>>2];if(d){g=K[d+4>>2];if((g|0)>0){b=0;while(1){e=K[d>>2]+Q(b,12)|0;c=K[e+8>>2];if(c){va[c|0](K[e+4>>2]);g=K[d+4>>2]}b=b+1|0;if((g|0)>(b|0)){continue}break}}Ga(K[d>>2]);Ga(d)}Ga(f)}K[a+236>>2]=0;Ga(a)}}function oe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;g=ra-16|0;ra=g;if(K[a+8>>2]==16){h=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{h=K[a+12>>2]}a:{if(!c){Fa(d,1,4222,0);break a}i=K[a+96>>2];e=1;Ha(b,g+8|0,1);f=K[g+8>>2];if(f>>>0>=2){Fa(d,2,9755,0);break a}if((f+1|0)!=(c|0)){e=0;Fa(d,2,4222,0);break a}d=K[i+16>>2];b:{if(!d){break b}e=K[h+5584>>2];if(d>>>0>=8){i=d&-8;c=0;while(1){K[e+8636>>2]=0;K[e+7556>>2]=0;K[e+6476>>2]=0;K[e+5396>>2]=0;K[e+4316>>2]=0;K[e+3236>>2]=0;K[e+2156>>2]=0;K[e+1076>>2]=0;e=e+8640|0;c=c+8|0;if((i|0)!=(c|0)){continue}break}}d=d&7;if(!d){break b}c=0;while(1){K[e+1076>>2]=0;e=e+1080|0;c=c+1|0;if((d|0)!=(c|0)){continue}break}}c=K[h+5608>>2];if(c){Ga(c);K[h+5608>>2]=0;f=K[g+8>>2]}if(!f){e=1;break a}i=0;while(1){b=b+1|0;Ha(b,g+12|0,1);c:{if(!K[h+5632>>2]){break c}d=K[h+5628>>2];if(K[d>>2]!=K[g+12>>2]){break c}f=K[d+4>>2];j=K[a+96>>2];if((f|0)!=K[j+16>>2]){break c}c=K[d+8>>2];if(c){e=0;f=Q(f,f);if(K[c+16>>2]!=(Q(f,K[(K[c>>2]<<2)+24848>>2])|0)){break a}k=Ja(f<<2);K[h+5608>>2]=k;if(!k){break a}va[K[(K[c>>2]<<2)+25152>>2]](K[c+12>>2],k,f)}c=K[d+12>>2];if(!c){break c}e=0;d=K[j+16>>2];if(K[c+16>>2]!=(Q(d,K[(K[c>>2]<<2)+24848>>2])|0)){break a}f=Ja(d<<2);if(!f){break a}va[K[(K[c>>2]<<2)+25168>>2]](K[c+12>>2],f,d);c=K[j+16>>2];d:{if(!c){break d}j=c&7;e=K[h+5584>>2];e:{if(c>>>0<8){c=f;break e}k=c&-8;d=0;c=f;while(1){K[e+1076>>2]=K[c>>2];K[e+2156>>2]=K[c+4>>2];K[e+3236>>2]=K[c+8>>2];K[e+4316>>2]=K[c+12>>2];K[e+5396>>2]=K[c+16>>2];K[e+6476>>2]=K[c+20>>2];K[e+7556>>2]=K[c+24>>2];K[e+8636>>2]=K[c+28>>2];e=e+8640|0;c=c+32|0;d=d+8|0;if((k|0)!=(d|0)){continue}break}}d=0;if(!j){break d}while(1){K[e+1076>>2]=K[c>>2];e=e+1080|0;c=c+4|0;d=d+1|0;if((j|0)!=(d|0)){continue}break}}Ga(f)}e=1;i=i+1|0;if(i>>>0>2]){continue}break}}ra=g+16|0;return e|0}function Fb(a,b,c,d,e,f,g,h){var i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;m=K[K[a+24>>2]+24>>2]+Q(b,52)|0;l=K[m+4>>2];k=l-1|0;o=K[a+60>>2];j=k+o|0;p=0-!l|0;i=p;r=K[K[K[a+20>>2]>>2]+20>>2]+Q(b,76)|0;n=K[r+12>>2];i=Ne(j,j>>>0>>0?i+1|0:i,l,0);q=i>>>0>n>>>0?n:i;j=K[m>>2];m=j-1|0;s=K[a+56>>2];n=m+s|0;o=0-!j|0;i=o;t=K[r+8>>2];i=Ne(n,n>>>0>>0?i+1|0:i,j,0);n=i>>>0>t>>>0?t:i;i=p;t=K[r+4>>2];s=K[a+52>>2];k=s+k|0;i=Ne(k,k>>>0>>0?i+1|0:i,l,0);k=i>>>0>>0?t:i;i=o;p=K[r>>2];l=m;m=K[a+48>>2];l=l+m|0;i=Ne(l,l>>>0>>0?i+1|0:i,j,0);i=i>>>0

>>0?p:i;l=0;p=K[(K[K[a+32>>2]+5584>>2]+Q(b,1080)|0)+20>>2];c=K[r+20>>2]+(c?0-c|0:-1)|0;a:{if(!c){a=n;l=i;b=k;break a}m=c-1|0;j=(d&1)<>>0>>0){a=c&31;l=i-j|0;if((c&63)>>>0>=32){i=-1<>>32-a;a=-1<>>0>>0?i+1|0:i;b=a;a=c&31;if((c&63)>>>0>=32){l=i>>>a|0}else{l=((1<>>a}}a=0;b=0;d=d>>>1<>>0>>0){b=c&31;o=k-d|0;if((c&63)>>>0>=32){i=-1<>>32-b;b=-1<>>0>>0?i+1|0:i;k=b;b=c&31;if((c&63)>>>0>=32){b=i>>>b|0}else{b=((1<>>b}}if(j>>>0>>0){a=c&31;k=n-j|0;if((c&63)>>>0>=32){i=-1<>>32-a;a=-1<>>0>>0?i+1|0:i;j=a;a=c&31;if((c&63)>>>0>=32){a=i>>>a|0}else{a=((1<>>a}}if(d>>>0>=q>>>0){q=0;break a}k=q-d|0;d=c&31;if((c&63)>>>0>=32){i=-1<>>32-d;d=-1<>>0>>0?i+1|0:i;j=d;d=c&31;if((c&63)>>>0>=32){q=i>>>d|0}else{q=((1<>>d}}c=(p|0)==1?2:3;d=c+a|0;d=(a>>>0>d>>>0?-1:d)>>>0>e>>>0;a=c+q|0;d=d&(a>>>0>>0?-1:a)>>>0>f>>>0;a=l-c|0;d=d&(a>>>0<=l>>>0?a:0)>>>0>>0;a=b-c|0;return d&(a>>>0<=b>>>0?a:0)>>>0>>0}function Ie(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;e=ra-80|0;ra=e;K[e+76>>2]=1;k=K[a+44>>2];d=K[K[a+224>>2]+40>>2];a:{b:{if(!d|!K[d+16>>2]){break b}c:{d=d+Q(k,40)|0;if(!K[d+4>>2]){d=K[a+52>>2];f=K[a+48>>2]+2|0;d=f>>>0<2?d+1|0:d;if(ib(b,f,d,c)){break c}Fa(c,1,5403,0);break a}d=K[d+16>>2];if(!ib(b,K[d>>2],K[d+4>>2],c)){Fa(c,1,5403,0);break a}if((Na(b,K[a+16>>2],2,c)|0)!=2){Fa(c,1,2435,0);break a}Ha(K[a+16>>2],e+72|0,2);if(K[e+72>>2]==65424){break c}Fa(c,1,4036,0);break a}if(K[a+8>>2]!=256){break b}K[a+8>>2]=8}h=Q(K[a+132>>2],K[a+128>>2]);d:{if(!h){break d}f=K[a+180>>2];d=0;if(h>>>0>=8){i=h&-8;while(1){K[(f+Q(d,5644)|0)+5588>>2]=-1;K[(f+Q(d|1,5644)|0)+5588>>2]=-1;K[(f+Q(d|2,5644)|0)+5588>>2]=-1;K[(f+Q(d|3,5644)|0)+5588>>2]=-1;K[(f+Q(d|4,5644)|0)+5588>>2]=-1;K[(f+Q(d|5,5644)|0)+5588>>2]=-1;K[(f+Q(d|6,5644)|0)+5588>>2]=-1;K[(f+Q(d|7,5644)|0)+5588>>2]=-1;d=d+8|0;j=j+8|0;if((i|0)!=(j|0)){continue}break}}h=h&7;if(!h){break d}while(1){K[(f+Q(d,5644)|0)+5588>>2]=-1;d=d+1|0;g=g+1|0;if((h|0)!=(g|0)){continue}break}}g=0;if(!ab(a,e+72|0,0,e+68|0,e- -64|0,e+60|0,e+56|0,e+52|0,e+76|0,b,c)){break a}h=k+1|0;while(1){e:{if(!K[e+76>>2]){break e}d=K[e+72>>2];if(!jb(a,d,0,0,b,c)){break a}i=K[a+128>>2];j=K[a+132>>2];f=d+1|0;K[e+32>>2]=f;K[e+36>>2]=Q(i,j);Fa(c,4,11758,e+32|0);if(!Wc(K[a+232>>2],K[K[a+100>>2]+24>>2])){break a}g=K[a+180>>2]+Q(d,5644)|0;i=K[g+5596>>2];if(i){Ga(i);K[g+5596>>2]=0;K[g+5600>>2]=0}K[e+16>>2]=f;Fa(c,4,16564,e+16|0);if((d|0)==(k|0)){d=K[a+224>>2];f=K[d+8>>2];d=K[d+12>>2];f=f+2|0;d=f>>>0<2?d+1|0:d;if(ib(b,f,d,c)){break e}g=0;Fa(c,1,5403,0);break a}K[e+4>>2]=h;K[e>>2]=f;Fa(c,2,13611,e);g=0;if(ab(a,e+72|0,0,e+68|0,e- -64|0,e+60|0,e+56|0,e+52|0,e+76|0,b,c)){continue}break a}break}g=Vc(a,c)}ra=e+80|0;return g|0}function uc(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;j=ra-256|0;ra=j;a:{if(!a){a=0;break a}if(!(K[a>>2]==(b|0)&K[a+4>>2]==(c|0))){K[a+4>>2]=c;K[a>>2]=b;K[j>>2]=c;K[j+128>>2]=b;e=c;g=b;while(1){o=i;i=i+1|0;h=i<<2;n=(e+1|0)/2|0;K[h+j>>2]=n;k=h+(j+128|0)|0;h=(g+1|0)/2|0;K[k>>2]=h;m=Q(e,g);f=m+f|0;e=n;g=h;if(m>>>0>1){continue}break}K[a+8>>2]=f;b:{c:{d:{if(!f){b=K[a+12>>2];if(!b){break d}Ga(b);K[a+12>>2]=0;break d}e=f<<4;if(e>>>0<=N[a+16>>2]){break b}f=La(K[a+12>>2],e);if(f){break c}Fa(d,1,6414,0);b=K[a+12>>2];if(!b){break d}Ga(b);K[a+12>>2]=0}Ga(a);a=0;break a}K[a+12>>2]=f;c=K[a+16>>2];b=e-c|0;if(b){B(c+f|0,0,b)}K[a+16>>2]=e;c=K[a+4>>2];b=K[a>>2]}g=K[a+12>>2];if(o){d=0;e=(Q(b,c)<<4)+g|0;f=e;while(1){b=d<<2;k=K[b+j>>2];e:{if((k|0)<=0){break e}m=k-1|0;l=0;f:{g:{c=K[b+(j+128|0)>>2];if((c|0)<=0){n=k&1;i=0;if((k|0)!=1){break g}b=f;break f}while(1){b=f;f=c;while(1){h:{K[g>>2]=e;if((f|0)==1){g=g+16|0;e=e+16|0;break h}K[g+16>>2]=e;e=e+16|0;g=g+32|0;h=(f|0)>2;f=f-2|0;if(h){continue}}break}h=((l|0)==(m|0)|l)&1;f=h?e:b+(c<<4)|0;e=h?e:b;l=l+1|0;if((k|0)!=(l|0)){continue}break}break e}h=k&2147483646;while(1){b=(i|0)==(m|0);i=i+2|0;e=b?e:f;f=e;b=e;l=l+2|0;if((h|0)!=(l|0)){continue}break}}if(!n){f=e;break e}f=(c<<4)+b|0;c=((i|0)==(m|0)|i)&1;f=c?e:f;e=c?e:b}d=d+1|0;if((o|0)!=(d|0)){continue}break}}K[g>>2]=0}c=K[a+8>>2];if(!c){break a}e=K[a+12>>2];if(c>>>0>=4){b=c&-4;g=0;while(1){K[e+60>>2]=0;K[e+52>>2]=999;K[e+56>>2]=0;K[e+44>>2]=0;K[e+36>>2]=999;K[e+40>>2]=0;K[e+28>>2]=0;K[e+20>>2]=999;K[e+24>>2]=0;K[e+12>>2]=0;K[e+4>>2]=999;K[e+8>>2]=0;e=e- -64|0;g=g+4|0;if((b|0)!=(g|0)){continue}break}}b=c&3;if(!b){break a}g=0;while(1){K[e+12>>2]=0;K[e+4>>2]=999;K[e+8>>2]=0;e=e+16|0;g=g+1|0;if((b|0)!=(g|0)){continue}break}}ra=j+256|0;return a}function pb(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;g=K[a+16>>2];if(g>>>0>=32){return K[a+8>>2]}d=K[a+20>>2];a:{if((d|0)>=4){b=K[a>>2];c=K[b-3>>2];d=d-4|0;K[a+20>>2]=d;K[a>>2]=b-4;break a}if((d|0)<=0){break a}k=d&1;b=K[a>>2];b:{if((d|0)==1){e=24;break b}j=d&2147483646;e=24;while(1){h=b-1|0;K[a>>2]=h;i=L[b|0];b=b-2|0;K[a>>2]=b;K[a+20>>2]=d-1;h=L[h|0];d=d-2|0;K[a+20>>2]=d;c=i<>2]=b-1;b=L[b|0];K[a+20>>2]=d-1;c=b<>2];j=c&255;K[a+24>>2]=j>>>0>143;b=b?(c&2130706432)==2130706432?7:8:8;h=b+(c>>>0<=2415919103?8:(c&8323072)==8323072?7:8)|0;f=c>>>16&255;i=h+(f>>>0<=143?8:(c&32512)==32512?7:8)|0;e=c>>>8&255;k=i+(g+(e>>>0<=143?8:(c&127)==127?7:8)|0)|0;K[a+16>>2]=k;l=K[a+12>>2];b=f<>>24|e<>>0>=32){e=b<>>32-c;b=b<>2];b=e|l;h=b;K[a+8>>2]=g;K[a+12>>2]=b;if(k>>>0<=31){c:{if((d|0)>=4){b=K[a>>2];c=K[b-3>>2];K[a+20>>2]=d-4;K[a>>2]=b-4;break c}if((d|0)<=0){c=0;break c}i=d&1;b=K[a>>2];d:{if((d|0)==1){e=24;c=0;break d}l=d&2147483646;e=24;c=0;f=0;while(1){m=b-1|0;K[a>>2]=m;n=L[b|0];b=b-2|0;K[a>>2]=b;K[a+20>>2]=d-1;m=L[m|0];d=d-2|0;K[a+20>>2]=d;c=n<>2]=b-1;b=L[b|0];K[a+20>>2]=d-1;c=b<>2]=d>>>0>143;j=j>>>0<=143?8:(c&2130706432)==2130706432?7:8;i=j+(c>>>0<=2415919103?8:(c&8323072)==8323072?7:8)|0;f=c>>>16&255;l=i+(f>>>0<=143?8:(c&32512)==32512?7:8)|0;e=c>>>8&255;K[a+16>>2]=l+((e>>>0<=143?8:(c&127)==127?7:8)+k|0);b=a;a=f<>>24|e<>>0>=32){d=a<>>32-c;a=a<>2]=g;K[b+12>>2]=d|h}return g}function cd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;j=K[a+96>>2];l=Q(K[a+128>>2],K[a+132>>2]);a:{if(l){b=K[j+16>>2];m=Q(b,1080);k=Q(b,b)<<2;e=K[a+12>>2];b=K[a+180>>2];while(1){n=K[b+5584>>2];E(b,e,5644);K[b+5608>>2]=0;K[b+5588>>2]=-1;K[b+5168>>2]=0;K[b+5636>>2]=0;K[b+5616>>2]=0;K[b+5624>>2]=0;K[b+5628>>2]=0;K[b+5584>>2]=n;I[b+5640|0]=L[b+5640|0]&252;b:{if(!K[e+5608>>2]){break b}d=Ja(k);K[b+5608>>2]=d;if(!d){return 0}if(!k){break b}E(d,K[e+5608>>2],k)}d=Q(K[e+5624>>2],20);f=Ja(d);K[b+5616>>2]=f;i=0;if(!f){break a}if(d){E(f,K[e+5616>>2],d)}g=K[e+5620>>2];if(g){d=K[e+5616>>2];f=K[b+5616>>2];h=0;while(1){if(K[d+12>>2]){g=Ja(K[d+16>>2]);K[f+12>>2]=g;if(!g){return 0}o=K[d+16>>2];if(o){E(g,K[d+12>>2],o)}g=K[e+5620>>2]}K[b+5624>>2]=K[b+5624>>2]+1;f=f+20|0;d=d+20|0;h=h+1|0;if(h>>>0>>0){continue}break}}d=Q(K[e+5636>>2],20);f=Ja(d);K[b+5628>>2]=f;if(!f){break a}if(d){E(f,K[e+5628>>2],d)}i=K[e+5636>>2];K[b+5636>>2]=i;if(i){d=K[e+5628>>2];f=K[b+5628>>2];h=0;while(1){g=K[d+8>>2];if(g){K[f+8>>2]=K[b+5616>>2]+(g-K[e+5616>>2]|0)}g=K[d+12>>2];if(g){K[f+12>>2]=K[b+5616>>2]+(g-K[e+5616>>2]|0)}f=f+20|0;d=d+20|0;h=h+1|0;if((i|0)!=(h|0)){continue}break}}if(m){E(n,K[e+5584>>2],m)}b=b+5644|0;p=p+1|0;if((p|0)!=(l|0)){continue}break}}i=1;e=Ia(1,72);b=0;c:{if(!e){break c}I[e+40|0]=L[e+40|0]&254|1;d=Ia(1,4);K[e+20>>2]=d;b=e;if(d){break c}Ga(b);b=0}K[a+232>>2]=b;if(!b){return 0}f=K[a+236>>2];e=0;K[b+28>>2]=a+104;K[b+24>>2]=j;d=Ia(1,848);K[K[b+20>>2]>>2]=d;d:{if(!d){break d}d=Ia(K[j+16>>2],76);h=K[K[b+20>>2]>>2];K[h+20>>2]=d;if(!d){break d}K[h+16>>2]=K[j+16>>2];e=K[a+188>>2];K[b+44>>2]=f;K[b>>2]=e;e=1}if(e){break a}hc(K[a+232>>2]);i=0;K[a+232>>2]=0;Fa(c,1,3631,0)}return i|0}function Qa(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;h=K[a+16>>2];if(h>>>0>=32){return K[a+8>>2]}d=K[a+24>>2];a:{if((d|0)>=4){b=K[a>>2];c=K[b>>2];g=d-4|0;K[a+24>>2]=g;K[a>>2]=b+4;break a}c=K[a+28>>2]?-1:0;if((d|0)<=0){g=d;break a}j=d&1;b=K[a>>2];b:{if((d|0)==1){f=b;break b}i=d&2147483646;while(1){K[a>>2]=b+1;k=L[b|0];f=b+2|0;K[a>>2]=f;K[a+24>>2]=d-1;b=L[b+1|0];d=d-2|0;K[a+24>>2]=d;c=((255<>2]=f+1;b=L[f|0];K[a+24>>2]=d-1;c=(255<>2];i=c>>>24|0;K[a+20>>2]=(i|0)==255;f=c>>>16&255;d=c>>>8&255;b=b?7:8;c=c&255;e=b+((c|0)==255?7:8)|0;k=((d|0)==255?7:8)+e|0;j=(h+((f|0)==255?7:8)|0)+k|0;K[a+16>>2]=j;l=K[a+12>>2];b=c|(d<>>0>=32){f=b<>>32-c;b=b<>2];b=f|l;k=b;K[a+8>>2]=h;K[a+12>>2]=b;if(j>>>0<=31){c:{if((g|0)>=4){b=K[a>>2];d=K[b>>2];K[a+24>>2]=g-4;K[a>>2]=b+4;break c}e=0;d=K[a+28>>2]?-1:0;if((g|0)<=0){break c}l=g&1;b=K[a>>2];d:{if((g|0)==1){c=b;break d}m=g&2147483646;f=0;while(1){K[a>>2]=b+1;n=L[b|0];c=b+2|0;K[a>>2]=c;K[a+24>>2]=g-1;b=L[b+1|0];g=g-2|0;K[a+24>>2]=g;d=((255<>2]=c+1;b=L[c|0];K[a+24>>2]=g-1;d=(255<>>24|0;K[a+20>>2]=(c|0)==255;f=d>>>16&255;g=d>>>8&255;e=(i|0)==255?7:8;d=d&255;i=e+((d|0)==255?7:8)|0;l=((g|0)==255?7:8)+i|0;K[a+16>>2]=(((f|0)==255?7:8)+j|0)+l;b=a;a=d|(g<>>0>=32){f=a<>>32-c;a=a<>2]=h;K[b+12>>2]=f|k}return h}function _c(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0,k=0;i=ra-32|0;ra=i;if(K[a+8>>2]==16){f=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{f=K[a+12>>2]}a:{if(N[d>>2]<=4){a=0;Fa(e,1,2570,0);break a}f=K[f+5584>>2]+Q(b,1080)|0;Ha(c,f+4|0,1);h=K[f+4>>2]+1|0;K[f+4>>2]=h;if(h>>>0>=34){K[i+4>>2]=33;K[i>>2]=h;Fa(e,1,7598,i);a=0;break a}g=K[a+184>>2];if(g>>>0>=h>>>0){K[i+24>>2]=h;K[i+20>>2]=g;K[i+16>>2]=b;Fa(e,1,16386,i+16|0);K[a+8>>2]=K[a+8>>2]|32768;a=0;break a}Ha(c+1|0,f+8|0,1);K[f+8>>2]=K[f+8>>2]+2;Ha(c+2|0,f+12|0,1);a=K[f+12>>2]+2|0;K[f+12>>2]=a;b=K[f+8>>2];if(!(!(b>>>0>10|a>>>0>10)&a+b>>>0<13)){a=0;Fa(e,1,5431,0);break a}Ha(c+3|0,f+16|0,1);if(L[f+16|0]&128){a=0;Fa(e,1,6527,0);break a}Ha(c+4|0,f+20|0,1);if(N[f+20>>2]>=2){a=0;Fa(e,1,6462,0);break a}b=K[d>>2]-5|0;K[d>>2]=b;a=1;h=K[f+4>>2];if(!(I[f|0]&1)){if(!h){break a}d=f+944|0;e=f+812|0;b=0;c=0;if(h>>>0>=4){k=h&-4;g=0;while(1){f=c<<2;K[f+e>>2]=15;K[d+f>>2]=15;j=f|4;K[j+e>>2]=15;K[d+j>>2]=15;j=f|8;K[j+e>>2]=15;K[d+j>>2]=15;f=f|12;K[f+e>>2]=15;K[d+f>>2]=15;c=c+4|0;g=g+4|0;if((k|0)!=(g|0)){continue}break}}f=h&3;if(!f){break a}while(1){a=c<<2;K[a+e>>2]=15;K[a+d>>2]=15;a=1;c=c+1|0;b=b+1|0;if((f|0)!=(b|0)){continue}break}break a}if(b>>>0>=h>>>0){b:{if(!h){g=0;break b}Ha(c+5|0,i+28|0,1);a=K[i+28>>2];K[f+944>>2]=a>>>4;K[f+812>>2]=a&15;g=K[f+4>>2];if(g>>>0>=2){h=f+944|0;k=f+812|0;a=c+6|0;c=1;while(1){Ha(a,i+28|0,1);c:{b=K[i+28>>2];if(b>>>0>=16){g=b&15;if(g){break c}}a=0;Fa(e,1,5988,0);break a}j=c<<2;K[j+k>>2]=g;K[h+j>>2]=b>>>4;a=a+1|0;c=c+1|0;g=K[f+4>>2];if(c>>>0>>0){continue}break}}b=K[d>>2]}K[d>>2]=b-g;a=1;break a}a=0;Fa(e,1,2570,0)}ra=i+32|0;return a}function nc(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;K[a+8>>2]=0;K[a+12>>2]=0;K[a>>2]=b;K[a+28>>2]=d;K[a+16>>2]=0;K[a+20>>2]=0;h=c-1|0;K[a+24>>2]=h;n=b&3;a:{if((c|0)<=0){e=b;b=d;break a}e=b+1|0;K[a>>2]=e;b=L[b|0]}g=b;i=8;K[a+16>>2]=8;j=(g|0)==255;K[a+20>>2]=j;K[a+8>>2]=g;K[a+12>>2]=0;b:{if((n|0)==3){break b}k=c-2|0;K[a+24>>2]=k;c:{if((c|0)<2){b=e;e=d;break c}b=e+1|0;K[a>>2]=b;e=L[e|0]}j=(e|0)==255;K[a+20>>2]=j;i=(g|0)==255?15:16;K[a+16>>2]=i;g=g|e<<8;K[a+8>>2]=g;K[a+12>>2]=0;if((n|0)==2){e=b;c=h;h=k;break b}o=c-3|0;K[a+24>>2]=o;d:{if((c|0)<3){f=b;b=d;break d}f=b+1|0;K[a>>2]=f;b=L[b|0]}j=(b|0)==255;K[a+20>>2]=j;l=((e|0)==255?7:8)+i|0;K[a+16>>2]=l;e=i&31;if((i&63)>>>0>=32){m=b<>>32-e;e=b<>2]=g;K[a+12>>2]=m;if((n|0)==1){e=f;i=l;c=k;h=o;break b}h=c-4|0;K[a+24>>2]=h;e:{if((c|0)<4){e=f;c=d;break e}e=f+1|0;K[a>>2]=e;c=L[f|0]}j=(c|0)==255;K[a+20>>2]=j;i=l+((b|0)==255?7:8)|0;K[a+16>>2]=i;b=l&31;if((l&63)>>>0>=32){f=c<>>32-b;b=c<>2]=g;K[a+12>>2]=b;c=o}f:{if((c|0)>=5){d=K[e>>2];K[a+24>>2]=c-5;K[a>>2]=e+4;break f}b=0;d=d?-1:0;if((c|0)<2){break f}while(1){c=e+1|0;K[a>>2]=c;e=L[e|0];f=h-1|0;K[a+24>>2]=f;d=(255<>>0>1;e=c;h=f;if(k){continue}break}}b=d>>>24|0;K[a+20>>2]=(b|0)==255;c=d>>>16&255;e=d>>>8&255;h=j?7:8;d=d&255;f=h+((d|0)==255?7:8)|0;k=((e|0)==255?7:8)+f|0;K[a+16>>2]=(((c|0)==255?7:8)+i|0)+k;b=d|(e<>>0>=32){d=a<>>32-b;a=a<>2]=a|g;K[c+12>>2]=d|m}function Db(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;j=ra-32|0;ra=j;p=K[a+16>>2];a:{if(!p){k=1;break a}d=K[a>>2];c=d>>31;h=c;b:{if((c|0)<0){break b}e=K[a+4>>2];c=e>>31;l=c;if((c|0)<0){break b}f=K[a+8>>2];c=f>>31;m=c;if((c|0)<0){break b}i=K[a+12>>2];c=i>>31;if((c|0)<0){break b}a=K[a+24>>2];s=d-1|0;t=h-!d|0;u=e-1|0;v=l-!e|0;w=f-1|0;x=m-!f|0;y=i-1|0;z=c-!i|0;while(1){c=t;d=K[a>>2];e=d+s|0;c=d>>>0>e>>>0?c+1|0:c;h=Ne(e,c,d,0);K[a+16>>2]=h;c=v;e=K[a+4>>2];f=e+u|0;c=e>>>0>f>>>0?c+1|0:c;l=Ne(f,c,e,0);K[a+20>>2]=l;i=K[a+40>>2];f=i&31;if((i&63)>>>0>=32){c=1<>>32-f}n=g;k=c;f=n-1|0;c=c-!n|0;m=c;q=d>>31;g=q+x|0;r=d+w|0;g=r>>>0>>0?g+1|0:g;d=Me(r,g,d,q);c=(d>>31)+c|0;g=d;d=d+f|0;c=g>>>0>d>>>0?c+1|0:c;g=d;d=i&31;if((i&63)>>>0>=32){d=c>>d}else{d=((1<>>d}c=(h>>31)+m|0;g=h;h=f+h|0;c=g>>>0>h>>>0?c+1|0:c;g=d;d=i&31;if((i&63)>>>0>=32){c=c>>d}else{c=((1<>>d}c=g-c|0;if((c|0)<0){K[j+4>>2]=c;K[j>>2]=o;Fa(b,1,13473,j);k=0;break a}K[a+8>>2]=c;d=e>>31;c=d+z|0;h=e+y|0;c=h>>>0>>0?c+1|0:c;d=Me(h,c,e,d);c=(d>>31)+m|0;e=d;d=d+f|0;c=e>>>0>d>>>0?c+1|0:c;e=d;d=i&31;if((i&63)>>>0>=32){e=c>>d}else{e=((1<>>d}c=k+(l>>31)|0;d=l+n|0;c=d>>>0>>0?c+1|0:c;f=d-1|0;h=e;d=c-!d|0;e=f;c=i&31;if((i&63)>>>0>=32){c=d>>c}else{c=((1<>>c}c=h-c|0;if((c|0)<0){K[j+20>>2]=c;K[j+16>>2]=o;Fa(b,1,13542,j+16|0);k=0;break a}K[a+12>>2]=c;a=a+52|0;k=1;o=o+1|0;if((p|0)!=(o|0)){continue}break}break a}Fa(b,1,6683,0)}ra=j+32|0;return k}function Ge(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;g=ra-16|0;ra=g;K[g+12>>2]=c;h=K[a+96>>2];if(K[a+8>>2]==16){e=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{e=K[a+12>>2]}I[e+5640|0]=L[e+5640|0]|1;a:{if(c>>>0<=4){Fa(d,1,4528,0);break a}Ha(b,e,1);if(N[e>>2]>=8){Fa(d,1,4494,0);break a}Ha(b+1|0,g+8|0,1);c=K[g+8>>2];K[e+4>>2]=c;if((c|0)>=5){Fa(d,1,4453,0);K[e+4>>2]=-1}Ha(b+2|0,e+8|0,2);c=K[e+8>>2];if(c-65536>>>0<=4294901760){K[g>>2]=c;Fa(d,1,8074,g);break a}i=K[a+188>>2];K[e+12>>2]=i?i:c;Ha(b+4|0,e+16|0,1);if(N[e+16>>2]>=2){Fa(d,1,5499,0);break a}i=b+5|0;K[g+12>>2]=K[g+12>>2]-5;h=K[h+16>>2];b:{if(!h){break b}b=K[e>>2]&1;c=K[e+5584>>2];e=0;if(h>>>0>=8){k=h&-8;while(1){K[c+Q(f,1080)>>2]=b;K[c+Q(f|1,1080)>>2]=b;K[c+Q(f|2,1080)>>2]=b;K[c+Q(f|3,1080)>>2]=b;K[c+Q(f|4,1080)>>2]=b;K[c+Q(f|5,1080)>>2]=b;K[c+Q(f|6,1080)>>2]=b;K[c+Q(f|7,1080)>>2]=b;f=f+8|0;j=j+8|0;if((k|0)!=(j|0)){continue}break}}h=h&7;if(!h){break b}while(1){K[c+Q(f,1080)>>2]=b;f=f+1|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}}f=0;if(!_c(a,0,i,g+12|0,d)){Fa(d,1,4528,0);break a}if(K[g+12>>2]){Fa(d,1,4528,0);break a}if(K[a+8>>2]==16){b=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{b=K[a+12>>2]}if(N[K[a+96>>2]+16>>2]>=2){b=K[b+5584>>2];d=K[b+4>>2]<<2;f=b+944|0;h=b+812|0;e=1;c=b;while(1){K[c+1084>>2]=K[b+4>>2];K[c+1088>>2]=K[b+8>>2];K[c+1092>>2]=K[b+12>>2];K[c+1096>>2]=K[b+16>>2];K[c+1100>>2]=K[b+20>>2];i=!d;if(!i){E(c+1892|0,h,d)}if(!i){E(c+2024|0,f,d)}c=c+1080|0;e=e+1|0;if(e>>>0>2]+16>>2]){continue}break}}f=1}ra=g+16|0;return f|0}function wc(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;j=ra-256|0;ra=j;f=Ia(1,20);a:{if(!f){Fa(c,1,6376,0);f=0;break a}K[f+4>>2]=b;K[f>>2]=a;K[j>>2]=b;K[j+128>>2]=a;while(1){p=g;g=g+1|0;h=g<<2;d=(b+1|0)/2|0;K[h+j>>2]=d;m=h+(j+128|0)|0;h=(a+1|0)/2|0;K[m>>2]=h;i=Q(a,b);e=i+e|0;b=d;a=h;if(i>>>0>1){continue}break}K[f+8>>2]=e;if(!e){Ga(f);f=0;break a}d=Ia(e,16);K[f+12>>2]=d;if(!d){Fa(c,1,3527,0);Ga(f);f=0;break a}l=K[f+8>>2];K[f+16>>2]=l<<4;a=d;if(p){e=(Q(K[f+4>>2],K[f>>2])<<4)+d|0;b=e;while(1){c=n<<2;i=K[c+j>>2];b:{if((i|0)<=0){break b}o=i-1|0;h=0;c:{c=K[c+(j+128|0)>>2];if((c|0)<=0){g=0;if((i|0)!=1){k=i&2147483646;while(1){m=(g|0)==(o|0);g=g+2|0;e=m?b:e;b=e;h=h+2|0;if((k|0)!=(h|0)){continue}break}}if(i&1){break c}b=e;break b}while(1){g=e;e=c;while(1){d:{K[a>>2]=b;if((e|0)==1){a=a+16|0;b=b+16|0;break d}K[a+16>>2]=b;b=b+16|0;a=a+32|0;k=(e|0)>2;e=e-2|0;if(k){continue}}break}k=((h|0)==(o|0)|h)&1;e=k?b:g+(c<<4)|0;b=k?b:g;h=h+1|0;if((i|0)!=(h|0)){continue}break}break b}g=((g|0)==(o|0)|g)&1;c=g?b:(c<<4)+e|0;b=g?b:e;e=c}n=n+1|0;if((n|0)!=(p|0)){continue}break}}K[a>>2]=0;e:{if(!l){break e}if(l>>>0>=4){a=l&-4;b=0;while(1){K[d+60>>2]=0;K[d+52>>2]=999;K[d+56>>2]=0;K[d+44>>2]=0;K[d+36>>2]=999;K[d+40>>2]=0;K[d+28>>2]=0;K[d+20>>2]=999;K[d+24>>2]=0;K[d+12>>2]=0;K[d+4>>2]=999;K[d+8>>2]=0;d=d- -64|0;b=b+4|0;if((a|0)!=(b|0)){continue}break}}a=l&3;if(!a){break e}b=0;while(1){K[d+12>>2]=0;K[d+4>>2]=999;K[d+8>>2]=0;d=d+16|0;b=b+1|0;if((a|0)!=(b|0)){continue}break}}}ra=j+256|0;return f}function La(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;if(!b){return 0}a:{if(!a){a=mb(8,b);break a}if(!b){Ga(a);a=0;break a}b:{if(b>>>0>4294967239){break b}h=b>>>0<=8?8:b+3&-4;b=h+8|0;c:{d:{k=a-4|0;f=k;c=K[f>>2];e=c+f|0;j=K[e>>2];g=j+e|0;e:{f:{if(K[g-4>>2]!=(j|0)){d=b+f|0;if(d+16>>>0<=g>>>0){c=K[e+4>>2];e=K[e+8>>2];K[c+8>>2]=e;K[e+4>>2]=c;c=g-d|0;K[d>>2]=c;K[(d+(c&-4)|0)-4>>2]=c|1;e=K[d>>2]-8|0;g:{if(e>>>0<=127){c=(e>>>3|0)-1|0;break g}g=T(e);c=((e>>>29-g^4)-(g<<2)|0)+110|0;if(e>>>0<=4095){break g}c=((e>>>30-g^2)-(g<<1)|0)+71|0;c=c>>>0>=63?63:c}e=c<<4;K[d+4>>2]=e+26352;e=e+26360|0;K[d+8>>2]=K[e>>2];K[e>>2]=d;K[K[d+8>>2]+4>>2]=d;e=K[6847];d=c&31;if((c&63)>>>0>=32){c=1<>>32-d}K[6846]=g|K[6846];K[6847]=c|e;K[f>>2]=b;break d}if(d>>>0>g>>>0){break f}b=K[e+4>>2];d=K[e+8>>2];K[b+8>>2]=d;K[d+4>>2]=b;b=c+j|0;K[f>>2]=b;break d}if(c>>>0>=b+16>>>0){K[f>>2]=b;K[(f+(b&-4)|0)-4>>2]=b;d=b+f|0;b=c-b|0;K[d>>2]=b;K[(d+(b&-4)|0)-4>>2]=b|1;c=K[d>>2]-8|0;h:{if(c>>>0<=127){b=(c>>>3|0)-1|0;break h}f=T(c);b=((c>>>29-f^4)-(f<<2)|0)+110|0;if(c>>>0<=4095){break h}b=((c>>>30-f^2)-(f<<1)|0)+71|0;b=b>>>0>=63?63:b}c=b<<4;K[d+4>>2]=c+26352;c=c+26360|0;K[d+8>>2]=K[c>>2];K[c>>2]=d;K[K[d+8>>2]+4>>2]=d;c=K[6847];d=b&31;if((b&63)>>>0>=32){b=1<>>32-d}K[6846]=e|K[6846];K[6847]=b|c;d=1;break c}d=1;if(b>>>0<=c>>>0){break e}}d=0}break c}K[(f+(b&-4)|0)-4>>2]=b;d=1}if(d){break a}b=mb(8,h);if(!b){break b}i=K[k>>2]-8|0;hb(b,a,h>>>0>>0?h:i);Ga(a);i=b}a=i}return a}function Ub(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0;a:{d=Ia(1,48);if(d){b=K[a+224>>2];c=K[b+4>>2];K[d>>2]=K[b>>2];K[d+4>>2]=c;c=K[b+12>>2];K[d+8>>2]=K[b+8>>2];K[d+12>>2]=c;c=K[b+20>>2];K[d+16>>2]=K[b+16>>2];K[d+20>>2]=c;c=K[b+24>>2];K[d+24>>2]=c;f=Ja(Q(c,24));K[d+28>>2]=f;if(!f){Ga(d);return 0}b=K[K[a+224>>2]+28>>2];b:{if(b){c=Q(K[d+24>>2],24);if(!c){break b}E(f,b,c);break b}Ga(f);K[d+28>>2]=0}c=K[K[a+224>>2]+36>>2];K[d+36>>2]=c;b=Ia(c,40);K[d+40>>2]=b;if(!b){Ga(K[d+28>>2]);Ga(d);return 0}c:{if(K[K[a+224>>2]+40>>2]){if(!K[d+36>>2]){break c}while(1){e=Q(h,40);c=K[(e+K[K[a+224>>2]+40>>2]|0)+20>>2];K[(b+e|0)+20>>2]=c;g=Ja(Q(c,24));c=K[d+40>>2];f=c+e|0;K[f+24>>2]=g;if(!g){if(h){b=0;while(1){Ga(K[(K[d+40>>2]+Q(b,40)|0)+24>>2]);b=b+1|0;if((h|0)!=(b|0)){continue}break}c=K[d+40>>2]}break a}b=K[(e+K[K[a+224>>2]+40>>2]|0)+24>>2];d:{if(b){c=Q(K[f+20>>2],24);if(c){E(g,b,c)}b=K[d+40>>2];break d}Ga(g);b=K[d+40>>2];K[(e+b|0)+24>>2]=0}c=K[(e+K[K[a+224>>2]+40>>2]|0)+4>>2];K[(b+e|0)+4>>2]=c;g=Ja(Q(c,24));c=K[d+40>>2];f=c+e|0;K[f+16>>2]=g;if(!g){if(h){b=0;while(1){a=Q(b,40);Ga(K[(a+K[d+40>>2]|0)+24>>2]);Ga(K[(a+K[d+40>>2]|0)+16>>2]);b=b+1|0;if((h|0)!=(b|0)){continue}break}c=K[d+40>>2]}break a}b=K[(e+K[K[a+224>>2]+40>>2]|0)+16>>2];e:{if(b){c=Q(K[f+4>>2],24);if(c){E(g,b,c)}b=K[d+40>>2];break e}Ga(g);b=K[d+40>>2];K[(e+b|0)+16>>2]=0}c=b+e|0;K[c+32>>2]=0;K[c+36>>2]=0;h=h+1|0;if(h>>>0>2]){continue}break}break c}Ga(b);K[d+40>>2]=0}}else{d=0}return d|0}Ga(c);Ga(K[d+28>>2]);Ga(d);return 0}function mb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;a:{b:{while(1){if(a-1&a|b>>>0>4294967239){break b}j=a>>>0>8;a=j?a:8;d=K[6847];e=d;g=K[6846];b=b>>>0<=8?8:b+3&-4;c:{if(b>>>0<=127){i=(b>>>3|0)-1|0;break c}c=T(b);i=((b>>>29-c^4)-(c<<2)|0)+110|0;if(b>>>0<=4095){break c}c=((b>>>30-c^2)-(c<<1)|0)+71|0;i=c>>>0>=63?63:c}h=i;f=h&31;if((h&63)>>>0>=32){c=0;d=d>>>f|0}else{c=d>>>f|0;d=((1<>>f}if(d|c){while(1){f=c;d:{if(c|d){e=c-1|0;g=e+1|0;i=e;e=d-1|0;g=(e|0)!=-1?g:i;c=T(c^g);c=(c|0)==32?T(d^e)+32|0:c;e=63-c|0;ua=0-(c>>>0>63)|0;break d}ua=0;e=64}g=e;e=g&31;if((g&63)>>>0>=32){c=0;i=f>>>e|0}else{c=f>>>e|0;i=((1<>>e}h=g+h|0;d=h<<4;f=K[d+26360>>2];e=d+26352|0;e:{if((f|0)!=(e|0)){d=Lb(f,a,b);if(d){break a}d=K[f+4>>2];g=K[f+8>>2];K[d+8>>2]=g;K[g+4>>2]=d;K[f+8>>2]=e;K[f+4>>2]=K[e+4>>2];K[e+4>>2]=f;K[K[f+4>>2]+8>>2]=f;h=h+1|0;d=(c&1)<<31|i>>>1;c=c>>>1|0;break e}d=K[6847];k=27384,l=K[6846]&Qe(-2,-1,h),K[k>>2]=l;K[6847]=ua&d;d=i^1}if(c|d){continue}break}g=K[6846];e=K[6847]}c=T(e);f=63-((c|0)==32?T(g)+32|0:c)|0;f:{if(!(e|g)){c=0;break f}d=f<<4;c=K[d+26360>>2];if(!e&g>>>0<1073741824){break f}h=99;e=d+26352|0;if((e|0)==(c|0)){break f}while(1){if(!h){break f}d=Lb(c,a,b);if(d){break a}h=h-1|0;c=K[c+8>>2];if((e|0)!=(c|0)){continue}break}}if(Pc((j?a+48|0:48)+b|0)){continue}break}if(!c){break b}f=(f<<4)+26352|0;if((f|0)==(c|0)){break b}while(1){d=Lb(c,a,b);if(d){break a}c=K[c+8>>2];if((f|0)!=(c|0)){continue}break}}d=0}return d}function Jd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=K[a+48>>2];if(e>>>0>=b>>>0){K[a+48>>2]=e-b;K[a+36>>2]=K[a+36>>2]+b;e=c+K[a+60>>2]|0;d=b+K[a+56>>2]|0;e=d>>>0>>0?e+1|0:e;K[a+56>>2]=d;K[a+60>>2]=e;ua=c;return b|0}if(L[a+68|0]&4){K[a+48>>2]=0;K[a+36>>2]=e+K[a+36>>2];g=K[a+60>>2];c=K[a+56>>2];b=c+e|0;K[a+56>>2]=b;K[a+60>>2]=b>>>0>>0?g+1|0:g;ua=e?0:-1;return(e?e:-1)|0}if(e){K[a+48>>2]=0;K[a+36>>2]=K[a+32>>2];h=b;f=e;b=b-e|0;c=c-(e>>>0>h>>>0)|0}a:{if((c|0)>0){h=1}else{h=!!b&(c|0)>=0}if(h){while(1){h=K[a+12>>2];e=c+g|0;i=b+f|0;e=K[a+60>>2]+(i>>>0>>0?e+1|0:e)|0;j=i;i=i+K[a+56>>2]|0;e=j>>>0>i>>>0?e+1|0:e;if((e|0)==(h|0)&i>>>0>N[a+8>>2]|e>>>0>h>>>0){Fa(d,4,15593,0);K[a+48>>2]=0;K[a+36>>2]=K[a+32>>2];b=g+K[a+60>>2]|0;c=f+K[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;K[a+56>>2]=c;K[a+60>>2]=b;d=K[a+8>>2];f=d-c|0;e=K[a+12>>2];g=e-((c>>>0>d>>>0)+b|0)|0;h=va[K[a+28>>2]](d,e,K[a>>2])|0;i=K[a+68>>2];if(h){K[a+56>>2]=d;K[a+60>>2]=e}K[a+68>>2]=i|4;a=(c|0)==(d|0)&(b|0)==(e|0);b=a?-1:f;break a}e=va[K[a+24>>2]](b,c,K[a>>2])|0;h=ua;i=h;if((e&i)==-1){Fa(d,4,15593,0);K[a+68>>2]=K[a+68>>2]|4;e=g+K[a+60>>2]|0;b=f+K[a+56>>2]|0;e=b>>>0>>0?e+1|0:e;K[a+56>>2]=b;K[a+60>>2]=e;a=!(g|f);b=a?-1:f;break a}g=g+i|0;f=e+f|0;g=f>>>0>>0?g+1|0:g;h=b;b=b-e|0;c=c-((e>>>0>h>>>0)+i|0)|0;if(!!b&(c|0)>=0|(c|0)>0){continue}break}}b=g+K[a+60>>2]|0;c=f+K[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;K[a+56>>2]=c;K[a+60>>2]=b;ua=g;return f|0}ua=a?-1:g;return b|0}function Nd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=ra-80|0;ra=e;a:{if(c>>>0<=2){Fa(d,1,14441,0);break a}if(L[a+124|0]){Fa(d,4,11156,0);g=1;break a}g=1;Ha(b,a+40|0,1);Ha(b+1|0,a+52|0,1);Ha(b+2|0,a+44|0,1);f=b+3|0;b:{c:{d:{e:{f:{h=K[a+40>>2];switch(h-1|0){case 0:break f;case 1:break e;default:break d}}if(c>>>0<=6){K[e+16>>2]=c;Fa(d,1,15118,e+16|0);g=0;break a}if(!((c|0)==7|K[a+48>>2]==14)){K[e+48>>2]=c;Fa(d,2,15118,e+48|0)}Ha(f,a+48|0,4);if(K[a+48>>2]!=14){break b}f=Ja(36);if(!f){g=0;Fa(d,1,7956,0);break a}K[f>>2]=14;K[e+64>>2]=0;K[e+56>>2]=0;K[e+72>>2]=0;K[e+60>>2]=0;K[e+68>>2]=0;K[e+76>>2]=0;g=4470064;K[e+52>>2]=4470064;K[f+4>>2]=1145390592;g:{if((c|0)!=7){if((c|0)==35){Ha(b+7|0,e+76|0,4);Ha(b+11|0,e+72|0,4);Ha(b+15|0,e+68|0,4);Ha(b+19|0,e- -64|0,4);Ha(b+23|0,e+60|0,4);Ha(b+27|0,e+56|0,4);Ha(b+31|0,e+52|0,4);K[f+4>>2]=0;g=K[e+52>>2];c=K[e+56>>2];d=K[e+64>>2];i=K[e+68>>2];j=K[e+76>>2];h=K[e+72>>2];b=K[e+60>>2];break g}K[e+32>>2]=c;Fa(d,2,15154,e+32|0)}c=0;d=0;h=0;b=0}K[f+24>>2]=b;K[f+16>>2]=i;K[f+8>>2]=j;K[f+32>>2]=g;K[f+28>>2]=c;K[f+20>>2]=d;K[f+12>>2]=h;K[a+112>>2]=0;K[a+108>>2]=f;break b}b=c-3|0;K[a+112>>2]=b;d=Ia(1,b);K[a+108>>2]=d;if(!d){break c}if((c|0)<=3){break b}c=0;while(1){Ha(f,e+76|0,1);I[K[a+108>>2]+c|0]=K[e+76>>2];f=f+1|0;c=c+1|0;if((b|0)!=(c|0)){continue}break}break b}if(h>>>0<3){break a}K[e>>2]=h;Fa(d,4,15913,e);break a}g=0;K[a+112>>2]=0;break a}g=1;I[a+124|0]=1}ra=e+80|0;return g|0}function Na(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0;h=K[a+48>>2];if(h>>>0>=c>>>0){if(c){E(b,K[a+36>>2],c)}K[a+36>>2]=K[a+36>>2]+c;K[a+48>>2]=K[a+48>>2]-c;b=K[a+60>>2];d=K[a+56>>2]+c|0;b=d>>>0>>0?b+1|0:b;K[a+56>>2]=d;K[a+60>>2]=b;return c}if(L[a+68|0]&4){if(h){E(b,K[a+36>>2],h)}b=K[a+48>>2];K[a+48>>2]=0;K[a+36>>2]=b+K[a+36>>2];g=K[a+60>>2];c=b;b=K[a+56>>2]+b|0;g=c>>>0>b>>>0?g+1|0:g;K[a+56>>2]=b;K[a+60>>2]=g;return h?h:-1}a:{if(h){if(h){E(b,K[a+36>>2],h)}i=K[a+32>>2];K[a+36>>2]=i;e=K[a+48>>2];K[a+48>>2]=0;f=K[a+60>>2];g=K[a+56>>2]+e|0;f=g>>>0>>0?f+1|0:f;K[a+56>>2]=g;K[a+60>>2]=f;c=c-e|0;b=b+e|0;break a}i=K[a+32>>2];K[a+36>>2]=i}b:{while(1){c:{e=K[a>>2];f=K[a+16>>2];g=K[a+64>>2];d:{if(g>>>0>c>>>0){f=va[f|0](i,g,e)|0;K[a+48>>2]=f;if((f|0)==-1){break b}if(c>>>0>f>>>0){if(f){E(b,K[a+36>>2],f)}i=K[a+32>>2];K[a+36>>2]=i;e=K[a+48>>2];break d}if(c){E(b,K[a+36>>2],c)}K[a+36>>2]=K[a+36>>2]+c;K[a+48>>2]=K[a+48>>2]-c;b=K[a+60>>2];d=K[a+56>>2]+c|0;b=d>>>0>>0?b+1|0:b;K[a+56>>2]=d;K[a+60>>2]=b;return c+h|0}e=va[f|0](b,c,e)|0;K[a+48>>2]=e;if((e|0)==-1){break b}if(c>>>0<=e>>>0){break c}i=K[a+32>>2];K[a+36>>2]=i;f=e}K[a+48>>2]=0;g=K[a+60>>2];j=K[a+56>>2]+e|0;g=j>>>0>>0?g+1|0:g;K[a+56>>2]=j;K[a+60>>2]=g;b=b+e|0;c=c-e|0;h=f+h|0;continue}break}K[a+48>>2]=0;K[a+36>>2]=K[a+32>>2];f=K[a+60>>2];b=K[a+56>>2]+e|0;f=b>>>0>>0?f+1|0:f;K[a+56>>2]=b;K[a+60>>2]=f;return e+h|0}Fa(d,4,15593,0);K[a+48>>2]=0;K[a+68>>2]=K[a+68>>2]|4;return h?h:-1}function Vb(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;g=ra-16|0;ra=g;o=K[K[a+96>>2]+16>>2];b=Ia(1,56);K[g+12>>2]=b;a:{if(!b){break a}j=K[K[a+96>>2]+16>>2];K[b+24>>2]=j;K[b>>2]=K[a+108>>2];K[b+4>>2]=K[a+112>>2];K[b+8>>2]=K[a+116>>2];K[b+12>>2]=K[a+120>>2];K[b+16>>2]=K[a+128>>2];h=K[a+132>>2];K[b+52>>2]=0;K[b+20>>2]=h;i=K[a+12>>2];K[b+32>>2]=K[i>>2];K[b+36>>2]=K[i+4>>2];K[b+40>>2]=K[i+8>>2];K[b+44>>2]=K[i+16>>2];a=Ia(j,1080);K[b+48>>2]=a;if(a){if(o){while(1){a=Q(k,1080);d=a+K[b+48>>2]|0;c=a+K[i+5584>>2]|0;K[d+4>>2]=K[c>>2];a=K[c+4>>2];K[d+8>>2]=a;K[d+12>>2]=K[c+8>>2];K[d+16>>2]=K[c+12>>2];K[d+20>>2]=K[c+16>>2];K[d+24>>2]=K[c+20>>2];b:{if(a>>>0>32){break b}if(a){E(d+948|0,c+944|0,a)}a=K[c+4>>2];if(!a){break b}E(d+816|0,c+812|0,a)}a=K[c+24>>2];K[d+28>>2]=a;K[d+808>>2]=K[c+804>>2];f=1;c:{if((a|0)!=1){a=Q(K[c+4>>2],3);if(a-3>>>0>95){break c}f=a-2|0}p=f&1;l=d+420|0;m=d+32|0;n=c+28|0;a=0;if((f|0)!=1){j=f&-2;f=0;while(1){h=a<<2;e=(a<<3)+n|0;K[h+m>>2]=K[e+4>>2];K[h+l>>2]=K[e>>2];e=a|1;h=e<<2;e=(e<<3)+n|0;K[h+m>>2]=K[e+4>>2];K[h+l>>2]=K[e>>2];a=a+2|0;f=f+2|0;if((j|0)!=(f|0)){continue}break}}if(!p){break c}e=a<<2;a=(a<<3)+n|0;K[e+m>>2]=K[a+4>>2];K[e+l>>2]=K[a>>2]}K[d+812>>2]=K[c+808>>2];k=k+1|0;if((k|0)!=(o|0)){continue}break}}e=b;break a}if(g+12|0){a=K[g+12>>2];b=K[a+48>>2];if(b){Ga(b);a=K[g+12>>2]}Ga(a);K[g+12>>2]=0}}ra=g+16|0;return e|0}function oc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;f=K[a+28>>2]+Q(b,152)|0;d=K[f-144>>2]-K[f-152>>2]|0;e=K[f-140>>2]-K[f-148>>2]|0;c=e>>>0>=64?64:e;g=d>>>0>=64?64:d;a:{if(!(!d|!e|(!g|!c)|g>>>0>4294967295/(c>>>0)>>>2>>>0)){f=Ia(1,28);K[f+12>>2]=c;K[f+8>>2]=g;K[f+4>>2]=e;K[f>>2]=d;h=e;e=c+e|0;i=h>>>0>e>>>0?1:i;e=Ne(e-1|0,i-!e|0,c,0);K[f+20>>2]=e;c=0;h=d;d=d+g|0;c=h>>>0>d>>>0?1:c;c=Ne(d-1|0,c-!d|0,g,0);K[f+16>>2]=c;Le(e,0,c);b:{if(ua){break b}c=Ia(4,Q(c,e));K[f+24>>2]=c;if(!c){break b}break a}Ga(f)}f=0}if(!f){return 0}c:{if(b){while(1){o=Q(n,152);e=o+K[a+28>>2]|0;c=K[e+24>>2];if(c){r=e+28|0;d=K[e+20>>2];g=K[e+16>>2];l=0;while(1){if(Q(d,g)){i=Q(l,36)+r|0;m=0;while(1){k=K[i+20>>2]+Q(m,40)|0;c=K[k+20>>2];j=K[k+16>>2];if(Q(c,j)){g=0;while(1){d=K[k+24>>2]+Q(g,68)|0;p=K[d+60>>2];if(p){j=K[d+12>>2];s=K[d+20>>2];t=K[d+16>>2];q=K[d+8>>2];d=q-K[i>>2]|0;h=K[i+16>>2];if(h&1){c=K[a+28>>2]+o|0;d=(K[c-144>>2]+d|0)-K[c-152>>2]|0}c=j-K[i+4>>2]|0;if(h&2){h=c;c=K[a+28>>2]+o|0;c=(h+K[c-140>>2]|0)-K[c-148>>2]|0}h=d;d=t-q|0;if(!db(f,h,c,h+d|0,(s-j|0)+c|0,p,1,d)){break c}j=K[k+16>>2];c=K[k+20>>2]}g=g+1|0;if(g>>>0>>0){continue}break}g=K[e+16>>2];d=K[e+20>>2]}m=m+1|0;if(m>>>0>>0){continue}break}c=K[e+24>>2]}l=l+1|0;if(l>>>0>>0){continue}break}}n=n+1|0;if((n|0)!=(b|0)){continue}break}}return f}_a(f);return 0}function Sb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0;a:{b:{e=K[a+60>>2];if(!e){if(K[b+16>>2]){break b}return 1}i=Ja(Q(e,52));if(!i){break a}e=0;if(K[b+16>>2]){d=K[b+24>>2];while(1){e=Q(f,52);Ga(K[(e+d|0)+44>>2]);d=K[b+24>>2];K[(e+d|0)+44>>2]=0;f=f+1|0;e=K[b+16>>2];if(f>>>0>>0){continue}break}}if(K[a+60>>2]){f=K[K[a+100>>2]+24>>2];e=0;while(1){h=Q(K[K[a+64>>2]+(e<<2)>>2],52);d=h+f|0;c=K[d+4>>2];g=i+Q(e,52)|0;K[g>>2]=K[d>>2];K[g+4>>2]=c;K[g+48>>2]=K[d+48>>2];c=K[d+44>>2];K[g+40>>2]=K[d+40>>2];K[g+44>>2]=c;c=K[d+36>>2];K[g+32>>2]=K[d+32>>2];K[g+36>>2]=c;c=K[d+28>>2];K[g+24>>2]=K[d+24>>2];K[g+28>>2]=c;c=K[d+20>>2];K[g+16>>2]=K[d+16>>2];K[g+20>>2]=c;c=K[d+12>>2];K[g+8>>2]=K[d+8>>2];K[g+12>>2]=c;f=K[K[a+100>>2]+24>>2];c=h+f|0;K[g+36>>2]=K[c+36>>2];K[g+44>>2]=K[c+44>>2];K[c+44>>2]=0;e=e+1|0;c=K[a+60>>2];if(e>>>0>>0){continue}break}e=K[b+16>>2]}if(e){d=K[K[a+100>>2]+24>>2];f=0;while(1){c=Q(f,52);Ga(K[(c+d|0)+44>>2]);d=K[K[a+100>>2]+24>>2];K[(c+d|0)+44>>2]=0;f=f+1|0;if(f>>>0>2]){continue}break}c=K[a+60>>2]}K[b+16>>2]=c;Ga(K[b+24>>2]);K[b+24>>2]=i;return 1}e=K[b+24>>2];f=K[K[a+100>>2]+24>>2];while(1){h=Q(d,52);c=h+e|0;K[c+36>>2]=K[(f+h|0)+36>>2];Ga(K[c+44>>2]);e=K[b+24>>2];f=K[K[a+100>>2]+24>>2];c=h+f|0;K[(h+e|0)+44>>2]=K[c+44>>2];K[c+44>>2]=0;d=d+1|0;if(d>>>0>2]){continue}break}return 1}Ya(K[a+96>>2]);K[a+96>>2]=0;return 0}function se(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;h=ra-16|0;ra=h;if(K[a+8>>2]==16){f=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{f=K[a+12>>2]}a:{if(c>>>0<=1){Fa(d,1,4095,0);a=0;break a}Ha(b,h+12|0,2);b:{if(K[h+12>>2]){Fa(d,2,3571,0);break b}if(c>>>0<=6){Fa(d,1,4095,0);a=0;break a}Ha(b+2|0,h+12|0,2);e=K[f+5616>>2];k=L[h+12|0];c:{d:{e:{g=K[f+5620>>2];if(!g){a=e;break e}a=e;while(1){if(K[a+8>>2]==(k|0)){break e}a=a+20|0;i=i+1|0;if((i|0)!=(g|0)){continue}break}break d}if((g|0)!=(i|0)){break c}}if(K[f+5624>>2]==(g|0)){a=g+10|0;K[f+5624>>2]=a;a=La(e,Q(a,20));e=K[f+5616>>2];if(!a){Ga(e);K[f+5624>>2]=0;K[f+5616>>2]=0;K[f+5620>>2]=0;Fa(d,1,4121,0);a=0;break a}f:{if((a|0)==(e|0)){break f}l=K[f+5632>>2];if(!l){break f}m=K[f+5628>>2];i=0;while(1){g=Q(i,20)+m|0;j=K[g+8>>2];if(j){K[g+8>>2]=a+(j-e|0)}j=K[g+12>>2];if(j){K[g+12>>2]=a+(j-e|0)}i=i+1|0;if((l|0)!=(i|0)){continue}break}}K[f+5616>>2]=a;e=K[f+5620>>2];g=Q(K[f+5624>>2]-e|0,20);if(g){B(a+Q(e,20)|0,0,g)}g=K[f+5620>>2];e=K[f+5616>>2]}K[f+5620>>2]=g+1;a=Q(g,20)+e|0}e=K[a+12>>2];if(e){Ga(e);K[a+12>>2]=0;K[a+16>>2]=0}K[a+8>>2]=k;e=K[h+12>>2];K[a>>2]=e>>>10&3;K[a+4>>2]=e>>>8&3;Ha(b+4|0,h+12|0,2);if(K[h+12>>2]){Fa(d,2,2986,0);break b}c=c-6|0;e=Ja(c);K[a+12>>2]=e;if(!e){Fa(d,1,4095,0);a=0;break a}if(c){E(e,b+6|0,c)}K[a+16>>2]=c}a=1}ra=h+16|0;return a|0}function Za(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;a:{b:{if(!K[a+12>>2]){k=1;if(K[a+4>>2]>0|K[a+8>>2]>1){break b}break a}e=1;if(K[a+8>>2]>0){break b}if(K[a+4>>2]<2){break a}}b=K[a>>2];f=b+(e<<5)|0;g=K[a+16>>2];h=K[a+20>>2];if(g>>>0>>0){d=g;while(1){c=(d<<6)+f|0;O[c>>2]=O[c>>2]*R(1.2301740646362305);O[c+4>>2]=O[c+4>>2]*R(1.2301740646362305);O[c+8>>2]=O[c+8>>2]*R(1.2301740646362305);O[c+12>>2]=O[c+12>>2]*R(1.2301740646362305);O[c+16>>2]=O[c+16>>2]*R(1.2301740646362305);O[c+20>>2]=O[c+20>>2]*R(1.2301740646362305);O[c+24>>2]=O[c+24>>2]*R(1.2301740646362305);O[c+28>>2]=O[c+28>>2]*R(1.2301740646362305);d=d+1|0;if((h|0)!=(d|0)){continue}break}}i=b+(k<<5)|0;j=K[a+28>>2];c=K[a+24>>2];if(j>>>0>c>>>0){d=c;while(1){b=(d<<6)+i|0;O[b>>2]=O[b>>2]*R(1.625732421875);O[b+4>>2]=O[b+4>>2]*R(1.625732421875);O[b+8>>2]=O[b+8>>2]*R(1.625732421875);O[b+12>>2]=O[b+12>>2]*R(1.625732421875);O[b+16>>2]=O[b+16>>2]*R(1.625732421875);O[b+20>>2]=O[b+20>>2]*R(1.625732421875);O[b+24>>2]=O[b+24>>2]*R(1.625732421875);O[b+28>>2]=O[b+28>>2]*R(1.625732421875);d=d+1|0;if((j|0)!=(d|0)){continue}break}}b=f+32|0;d=K[a+8>>2];a=K[a+4>>2];e=a-e|0;e=(d|0)<(e|0)?d:e;qb(i,b,g,h,e,R(-.4435068666934967));l=i+32|0;d=d-k|0;a=(a|0)<(d|0)?a:d;qb(f,l,c,j,a,R(-.8829110860824585));qb(i,b,g,h,e,R(.05298011749982834));qb(f,l,c,j,a,R(1.5861343145370483))}}function hc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;if(a){b=K[a+20>>2];if(b){g=K[b>>2];if(g){d=K[g+20>>2];if(K[g+16>>2]){i=I[a+40|0]&1?16:17;while(1){c=K[d+28>>2];if(c){b=K[d+32>>2];l=(b>>>0)/152|0;j=0;if(b>>>0>=152){while(1){b=K[c+48>>2];if(b){f=K[c+52>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){eb(K[b+32>>2]);K[b+32>>2]=0;eb(K[b+36>>2]);K[b+36>>2]=0;va[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=K[c+48>>2]}Ga(b);K[c+48>>2]=0}b=K[c+84>>2];if(b){f=K[c+88>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){eb(K[b+32>>2]);K[b+32>>2]=0;eb(K[b+36>>2]);K[b+36>>2]=0;va[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=K[c+84>>2]}Ga(b);K[c+84>>2]=0}b=K[c+120>>2];if(b){f=K[c+124>>2];h=(f>>>0)/40|0;e=0;if(f>>>0>=40){while(1){eb(K[b+32>>2]);K[b+32>>2]=0;eb(K[b+36>>2]);K[b+36>>2]=0;va[i|0](b);b=b+40|0;e=e+1|0;if((h|0)!=(e|0)){continue}break}b=K[c+120>>2]}Ga(b);K[c+120>>2]=0}c=c+152|0;j=j+1|0;if((l|0)!=(j|0)){continue}break}c=K[d+28>>2]}Ga(c);K[d+28>>2]=0}a:{if(!K[d+40>>2]){break a}b=K[d+36>>2];if(!b){break a}Ga(b);K[d+44>>2]=0;K[d+48>>2]=0;K[d+36>>2]=0;K[d+40>>2]=0}Ga(K[d+52>>2]);d=d+76|0;k=k+1|0;if(k>>>0>2]){continue}break}d=K[g+20>>2]}Ga(d);K[g+20>>2]=0;Ga(K[K[a+20>>2]>>2]);b=K[a+20>>2];K[b>>2]=0}Ga(b);K[a+20>>2]=0}Ga(K[a+68>>2]);Ga(a)}}function pc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;c=K[a+8>>2];f=c+K[a+4>>2]|0;a:{if(!K[a+12>>2]){if((f|0)<2){break a}h=(c<<2)+b|0;d=K[h>>2];e=K[b>>2]-(d+1>>1)|0;i=K[a>>2];b:{if(f>>>0<4){c=d;break b}k=(f-4>>>1|0)+1|0;a=1;while(1){c=a<<2;m=K[c+b>>2];c=K[c+h>>2];l=i+(g<<2)|0;K[l>>2]=e;j=e;e=m-((c+d|0)+2>>2)|0;K[l+4>>2]=(j+e>>1)+d;g=g+2|0;j=(a|0)!=(k|0);d=c;a=a+1|0;if(j){continue}break}}K[i+(g<<2)>>2]=e;if(f&1){d=f-1|0;a=K[(d<<1)+b>>2]-(c+1>>1)|0;K[i+(d<<2)>>2]=a;e=a+e>>1;d=-8}else{d=-4}a=f<<2;K[d+(a+i|0)>>2]=c+e;if(!a){break a}E(b,i,a);return}c:{switch(f-1|0){case 0:K[b>>2]=K[b>>2]/2;return;case 1:a=K[a>>2];c=(c<<2)+b|0;d=K[b>>2]-(K[c>>2]+1>>1)|0;K[a+4>>2]=d;K[a>>2]=d+K[c>>2];c=K[a+4>>2];K[b>>2]=K[a>>2];K[b+4>>2]=c;return;default:break c}}if((f|0)<3){break a}h=K[a>>2];k=(c<<2)+b|0;d=K[k+4>>2];a=K[k>>2];e=K[b>>2]-((d+a|0)+2>>2)|0;K[h>>2]=e+a;g=1;m=f-2|0;l=f&1;a=!l;d:{if(m-a>>>0<2){c=d;break d}o=((f-a|0)-4>>>1|0)+1|0;a=1;while(1){p=K[(a<<2)+b>>2];j=a+1|0;c=K[k+(j<<2)>>2];n=h+(g<<2)|0;K[n>>2]=e;i=e;e=p-((c+d|0)+2>>2)|0;K[n+4>>2]=(i+e>>1)+d;g=g+2|0;i=(a|0)!=(o|0);d=c;a=j;if(i){continue}break}}K[h+(g<<2)>>2]=e;e:{if(!l){g=K[((f<<1)+b|0)-4>>2]-(c+1>>1)|0;K[h+(m<<2)>>2]=(g+e>>1)+c;break e}g=c+e|0}a=f<<2;K[(a+h|0)-4>>2]=g;if(!a){break a}E(b,h,a)}}function fc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;e=K[a+24>>2];j=K[e+16>>2];if(!j){return 0}f=K[e+24>>2];e=K[K[K[a+20>>2]>>2]+20>>2];a:{b:{if(!b){b=0;while(1){c=K[f+24>>2];a=K[e+28>>2]+Q(K[e+24>>2],152)|0;d=K[a-140>>2];g=K[a-144>>2]-K[a-152>>2]|0;a=K[a-148>>2];h=d-a|0;Le(g,0,h);if(!(!ua|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=Q(g,h);Le(c,0,d);if(!(!ua|a)){break a}a=-1;c=Q(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}break b}b=0;if(!K[a+64>>2]){while(1){c=K[f+24>>2];a=K[e+28>>2]+Q(K[e+24>>2],152)|0;d=K[a-4>>2];g=K[a-8>>2]-K[a-16>>2]|0;a=K[a-12>>2];h=d-a|0;Le(g,0,h);if(!(!ua|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=Q(g,h);Le(c,0,d);if(!(!ua|a)){break a}a=-1;c=Q(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}break b}while(1){c=K[f+24>>2];a=K[e+28>>2]+Q(K[e+24>>2],152)|0;d=K[a-140>>2];g=K[a-144>>2]-K[a-152>>2]|0;a=K[a-148>>2];h=d-a|0;Le(g,0,h);if(!(!ua|(a|0)==(d|0))){break a}a=(c>>>3|0)+((c&7)!=0)|0;c=(a|0)==3?4:a;a=!c;d=Q(g,h);Le(c,0,d);if(!(!ua|a)){break a}a=-1;c=Q(c,d);if(c>>>0>(b^-1)>>>0){break b}e=e+76|0;f=f+52|0;b=b+c|0;a=b;i=i+1|0;if((j|0)!=(i|0)){continue}break}}return a}return-1}function Wb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;d=ra-256|0;ra=d;if(a){Sa(1769,17,c);K[d+240>>2]=K[a>>2];Ka(c,2311,d+240|0);K[d+224>>2]=K[a+4>>2];Ka(c,2324,d+224|0);K[d+208>>2]=K[a+8>>2];Ka(c,7223,d+208|0);K[d+192>>2]=K[a+16>>2];Ka(c,2282,d+192|0);if((b|0)>0){while(1){e=K[a+5584>>2];K[d+176>>2]=h;Ka(c,1807,d+176|0);e=e+Q(h,1080)|0;K[d+160>>2]=K[e>>2];Ka(c,2310,d+160|0);K[d+144>>2]=K[e+4>>2];Ka(c,7337,d+144|0);K[d+128>>2]=K[e+8>>2];Ka(c,7125,d+128|0);K[d+112>>2]=K[e+12>>2];Ka(c,7141,d+112|0);K[d+96>>2]=K[e+16>>2];Ka(c,2293,d+96|0);K[d+80>>2]=K[e+20>>2];Ka(c,7403,d+80|0);Sa(1530,23,c);if(K[e+4>>2]){i=e+944|0;j=e+812|0;f=0;while(1){g=f<<2;k=K[j+g>>2];K[d+68>>2]=K[i+g>>2];K[d+64>>2]=k;Ka(c,1656,d- -64|0);f=f+1|0;if(f>>>0>2]){continue}break}}Qc(c);K[d+48>>2]=K[e+24>>2];Ka(c,7157,d+48|0);K[d+32>>2]=K[e+804>>2];Ka(c,7206,d+32|0);i=1;Sa(1554,20,c);a:{if(K[e+24>>2]!=1){f=K[e+4>>2];if((f|0)<=0){break a}i=Q(f,3)-2|0}j=e+28|0;f=0;while(1){g=j+(f<<3)|0;l=d,m=Qe(K[g>>2],K[g+4>>2],32),K[l+16>>2]=m;K[d+20>>2]=ua;Ka(c,1656,d+16|0);f=f+1|0;if((i|0)!=(f|0)){continue}break}}Qc(c);K[d>>2]=K[e+808>>2];Ka(c,7189,d);Sa(1670,5,c);h=h+1|0;if((h|0)!=(b|0)){continue}break}}Sa(1671,4,c)}ra=d+256|0}function Je(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{e=b;if(e){if(!c){break j}if(!d){break i}e=T(d)-T(e)|0;if(e>>>0<=31){break h}break b}if((d|0)==1|d>>>0>1){break b}b=(a>>>0)/(c>>>0)|0;sa=a-Q(b,c)|0;ta=0;ua=0;return b}if(!a){break g}if(!d){break f}f=d-1|0;if(f&d){break f}sa=a;ta=e&f;a=e>>>Ke(d)|0;ua=0;return a}f=c-1|0;if(!(f&c)){break e}k=(T(c)+33|0)-T(e)|0;g=0-k|0;break c}k=e+1|0;g=63-e|0;break c}sa=0;a=(e>>>0)/(d>>>0)|0;ta=e-Q(a,d)|0;ua=0;return a}e=T(d)-T(e)|0;if(e>>>0<31){break d}break b}sa=a&f;ta=0;if((c|0)==1){break a}c=Ke(c);d=c&31;if((c&63)>>>0>=32){e=0;a=b>>>d|0}else{e=b>>>d|0;a=((1<>>d}ua=e;return a}k=e+1|0;g=63-e|0}f=a;e=k&63;h=e&31;if((e&63)>>>0>=32){e=0;f=b>>>h|0}else{e=b>>>h|0;f=((1<>>h}h=g&63;g=a;i=h&31;if((h&63)>>>0>=32){j=a<>>32-i|b<>>31;f=f<<1|b>>>31;l=e;i=g-(e+(f>>>0>h>>>0)|0)|0;m=i>>31;j=m;e=f;i=c&j;f=e-i|0;e=l-((d&j)+(e>>>0>>0)|0)|0;j=b<<1|a>>>31;a=n|a<<1;b=j|o;l=m&1;n=l;k=k-1|0;if(k){continue}break}}sa=f;ta=e;j=b<<1|a>>>31;a=l|a<<1;ua=j|o;return a}sa=a;ta=b;a=0;b=0}ua=b;return a}function Zc(a,b,c,d,e){var f=0,g=0,h=0,i=0;h=ra-16|0;ra=h;if(K[a+8>>2]==16){a=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{a=K[a+12>>2]}f=K[d>>2];a:{if(!f){d=0;Fa(e,1,2605,0);break a}a=K[a+5584>>2];K[d>>2]=f-1;Ha(c,h+12|0,1);g=Q(b,1080)+a|0;a=K[h+12>>2];K[g+804>>2]=a>>>5;b=a&31;K[g+24>>2]=b;a=c+1|0;b:{c:{d:{e:{f:{switch(b|0){case 0:f=K[d>>2];break e;case 1:break d;default:break f}}f=K[d>>2]>>>1|0}if(f>>>0>=98){K[h+4>>2]=97;K[h+8>>2]=97;K[h>>2]=f;Fa(e,2,16019,h);b=K[g+24>>2]}if(b){b=f;if(b){break d}a=0;break c}if(f){b=g+28|0;c=0;while(1){Ha(a,h+12|0,1);if(c>>>0<=96){e=K[h+12>>2];i=b+(c<<3)|0;K[i+4>>2]=0;K[i>>2]=e>>>3}a=a+1|0;c=c+1|0;if((f|0)!=(c|0)){continue}break}}a=K[d>>2];if(a>>>0>>0){d=0;break a}a=a-f|0;break b}e=g+28|0;c=0;while(1){Ha(a,h+12|0,2);if(c>>>0<=96){f=e+(c<<3)|0;i=K[h+12>>2];K[f+4>>2]=i&2047;K[f>>2]=i>>>11}a=a+2|0;c=c+1|0;if((c|0)!=(b|0)){continue}break}a=b<<1}b=K[d>>2];if(a>>>0>b>>>0){d=0;break a}a=b-a|0}K[d>>2]=a;d=1;if(K[g+24>>2]!=1){break a}f=g+28|0;c=K[g+32>>2];e=K[g+28>>2];a=1;while(1){b=f+(a<<3)|0;K[b+4>>2]=c;K[b+12>>2]=c;g=e-((a>>>0)/3|0)|0;K[b+8>>2]=(g|0)>0?g:0;g=b;b=e-((a-1>>>0)/3|0)|0;K[g>>2]=(b|0)>0?b:0;a=a+2|0;if((a|0)!=97){continue}break}}ra=h+16|0;return d}function ye(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;f=ra-32|0;ra=f;g=1;a:{if(c>>>0<=1){g=0;Fa(d,1,10025,0);break a}if(K[a+76>>2]){break a}Ha(b,f+28|0,1);Ha(b+1|0,f+24|0,1);e=K[f+24>>2];i=e>>>4&3;if((i|0)==3){K[a+76>>2]=1;Fa(d,2,11521,0);break a}c=c-2|0;j=(e>>>5&2)+2|0;h=i+j|0;e=(c>>>0)/(h>>>0)|0;if((c|0)!=(Q(e,h)|0)){K[a+76>>2]=1;Fa(d,2,11102,0);break a}if(c>>>0>>0){break a}b:{c=K[a+68>>2];if(c>>>0<=(e^-1)>>>0){c=c+e|0;if(c>>>0<536870912){break b}}K[a+76>>2]=1;Fa(d,2,9363,0);break a}h=La(K[a+72>>2],c<<3);if(!h){K[a+76>>2]=1;Fa(d,2,9406,0);break a}c=b+2|0;K[a+72>>2]=h;c:{if(i){k=e>>>0<=1?1:e;e=0;while(1){Ha(c,f+20|0,i);b=K[f+20>>2];if(b>>>0>=Q(K[a+132>>2],K[a+128>>2])>>>0){break c}b=c+i|0;Ha(b,f+16|0,j);c=K[a+68>>2];g=h+(c<<3)|0;J[g>>1]=K[f+20>>2];K[g+4>>2]=K[f+16>>2];g=1;K[a+68>>2]=c+1;c=b+j|0;e=e+1|0;if((k|0)!=(e|0)){continue}break}break a}i=e>>>0<=1?1:e;b=K[a+68>>2];e=0;while(1){K[f+20>>2]=b;if(Q(K[a+132>>2],K[a+128>>2])>>>0<=b>>>0){break c}Ha(c,f+16|0,j);k=K[a+68>>2];g=h+(k<<3)|0;J[g>>1]=b;K[g+4>>2]=K[f+16>>2];g=1;b=k+1|0;K[a+68>>2]=b;c=c+j|0;e=e+1|0;if((i|0)!=(e|0)){continue}break}break a}K[a+76>>2]=1;K[f>>2]=b;Fa(d,2,7762,f)}ra=f+32|0;return g|0}function Pd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;h=ra-16|0;ra=h;a:{if(!(L[a+100|0]&2)){Fa(d,1,11319,0);a=0;break a}K[a+104>>2]=0;b:{c:{d:{if(c){while(1){if(c>>>0<=7){Fa(d,1,3366,0);break b}g=h+12|0;Ha(b,g,4);e=K[h+12>>2];Ha(b+4|0,g,4);f=8;g=K[h+12>>2];e:{f:{g:{switch(e|0){case 1:if(c>>>0<16){e=3406;break c}Ha(b+8|0,h+8|0,4);if(K[h+8>>2]){e=8412;break c}Ha(b+12|0,h+12|0,4);e=K[h+12>>2];if(e){break f}e=3231;break c;case 0:break g;default:break e}}Fa(d,1,3231,0);break b}f=16}if(e>>>0>>0){Fa(d,1,9111,0);break b}if(c>>>0>>0){Fa(d,1,9039,0);a=0;break a}h:{i:{j=b+f|0;k=e-f|0;j:{k:{l:{m:{if((g|0)<=1668246641){if((g|0)==1651532643){break m}if((g|0)==1667523942){break k}if((g|0)!=1668112752){break i}f=25248;break j}if((g|0)==1885564018){break l}f=25216;if((g|0)==1768449138){break j}if((g|0)!=1668246642){break i}f=25224;break j}f=25232;break j}f=25240;break j}f=25256}if(va[K[f+4>>2]](a,j,k,d)|0){break h}a=0;break a}K[a+104>>2]=K[a+104>>2]|2147483647}i=(g|0)==1768449138?1:i;b=b+e|0;c=c-e|0;if(c){continue}break}if(i){break d}}Fa(d,1,8939,0);a=0;break a}I[a+132|0]=1;K[a+100>>2]=K[a+100>>2]|4;a=1;break a}Fa(d,1,e,0)}Fa(d,1,1931,0);a=0}ra=h+16|0;return a|0}function Tb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;a:{if(!c){break a}b:{e=K[a+184>>2];if(!e){break b}g=K[a+96>>2];if(!g|!K[g+16>>2]|(e|0)!=K[K[g+24>>2]+40>>2]){break b}h=K[c+16>>2];if(!h){break b}f=K[c+24>>2];if(K[f+40>>2]|K[f+44>>2]){break b}g=0;if(h>>>0>=8){j=h&-8;while(1){K[(f+Q(g,52)|0)+40>>2]=e;K[(f+Q(g|1,52)|0)+40>>2]=e;K[(f+Q(g|2,52)|0)+40>>2]=e;K[(f+Q(g|3,52)|0)+40>>2]=e;K[(f+Q(g|4,52)|0)+40>>2]=e;K[(f+Q(g|5,52)|0)+40>>2]=e;K[(f+Q(g|6,52)|0)+40>>2]=e;K[(f+Q(g|7,52)|0)+40>>2]=e;g=g+8|0;k=k+8|0;if((j|0)!=(k|0)){continue}break}}h=h&7;if(h){while(1){K[(f+Q(g,52)|0)+40>>2]=e;g=g+1|0;l=l+1|0;if((h|0)!=(l|0)){continue}break}}if(Db(c,d)){break b}return 0}f=K[a+100>>2];if(!f){f=Bb();K[a+100>>2]=f;if(!f){break a}}Ob(c,f);if(!$a(K[a+216>>2],22,d)){break a}h=K[a+216>>2];e=K[h>>2];f=K[h+8>>2];c:{if(e){i=1;j=e&1;if((e|0)==1){e=0}else{k=e&-2;g=0;while(1){e=0;d:{if(!i){break d}e=0;if(!(va[K[f>>2]](a,b,d)|0)){break d}e=(va[K[f+4>>2]](a,b,d)|0)!=0}i=e;f=f+8|0;g=g+2|0;if((k|0)!=(g|0)){continue}break}e=!i}i=j?0:i;if(!(e|!j)){i=(va[K[f>>2]](a,b,d)|0)!=0}Ta(h);if(i){break c}Ya(K[a+96>>2]);K[a+96>>2]=0;return 0}Ta(h)}i=Sb(a,c)}return i|0}function ae(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;if(!$a(K[b+8>>2],54,d)){return 0}j=K[b+4>>2];e=K[j>>2];h=K[j+8>>2];a:{if(e){f=1;k=e&1;if((e|0)==1){e=0}else{e=e&-2;while(1){i=0;b:{if(!f){break b}i=0;if(!(va[K[h>>2]](b,a,d)|0)){break b}i=(va[K[h+4>>2]](b,a,d)|0)!=0}f=i;h=h+8|0;g=g+2|0;if((e|0)!=(g|0)){continue}break}e=!f}f=k?0:f;if(!(e|!k)){f=(va[K[h>>2]](b,a,d)|0)!=0}Ta(j);if(f){break a}return 0}Ta(j)}j=K[b+8>>2];e=K[j>>2];h=K[j+8>>2];c:{if(e){f=1;k=e&1;if((e|0)==1){e=0}else{e=e&-2;g=0;while(1){i=0;d:{if(!f){break d}i=0;if(!(va[K[h>>2]](b,a,d)|0)){break d}i=(va[K[h+4>>2]](b,a,d)|0)!=0}f=i;h=h+8|0;g=g+2|0;if((e|0)!=(g|0)){continue}break}e=!f}f=k?0:f;if(!(e|!k)){f=(va[K[h>>2]](b,a,d)|0)!=0}Ta(j);if(f){break c}return 0}Ta(j)}if(!L[b+132|0]){Fa(d,1,11659,0);return 0}if(!L[b+133|0]){Fa(d,1,11630,0);return 0}d=ac(a,K[b>>2],c,d);e:{if(!c){break e}a=K[c>>2];if(!a){break e}g=1;f:{g:{switch(K[b+48>>2]-12|0){case 5:g=2;break f;case 6:g=3;break f;case 12:g=4;break f;case 0:g=5;break f;case 4:break f;default:break g}}g=-1}K[a+20>>2]=g;c=K[b+108>>2];if(!c){break e}K[a+28>>2]=c;K[a+32>>2]=K[b+112>>2];K[b+108>>2]=0}return d|0}function Ob(a,b){var c=0,d=0,e=0,f=0,g=0;K[b>>2]=K[a>>2];K[b+4>>2]=K[a+4>>2];K[b+8>>2]=K[a+8>>2];K[b+12>>2]=K[a+12>>2];c=K[b+24>>2];if(c){d=K[b+16>>2];if(d){c=0;while(1){f=K[(K[b+24>>2]+Q(c,52)|0)+44>>2];if(f){Ga(f);d=K[b+16>>2]}c=c+1|0;if(d>>>0>c>>>0){continue}break}c=K[b+24>>2]}Ga(c);K[b+24>>2]=0}c=K[a+16>>2];K[b+16>>2]=c;c=Ja(Q(c,52));K[b+24>>2]=c;if(c){if(K[b+16>>2]){f=0;while(1){g=Q(f,52);c=g+c|0;d=K[a+24>>2]+g|0;e=K[d+4>>2];K[c>>2]=K[d>>2];K[c+4>>2]=e;K[c+48>>2]=K[d+48>>2];e=K[d+44>>2];K[c+40>>2]=K[d+40>>2];K[c+44>>2]=e;e=K[d+36>>2];K[c+32>>2]=K[d+32>>2];K[c+36>>2]=e;e=K[d+28>>2];K[c+24>>2]=K[d+24>>2];K[c+28>>2]=e;e=K[d+20>>2];K[c+16>>2]=K[d+16>>2];K[c+20>>2]=e;e=K[d+12>>2];K[c+8>>2]=K[d+8>>2];K[c+12>>2]=e;c=K[b+24>>2];K[(g+c|0)+44>>2]=0;f=f+1|0;if(f>>>0>2]){continue}break}}K[b+20>>2]=K[a+20>>2];c=K[a+32>>2];K[b+32>>2]=c;a:{if(c){c=Ja(c);K[b+28>>2]=c;if(!c){K[b+28>>2]=0;K[b+32>>2]=0;return}b=K[a+32>>2];if(!b){break a}E(c,K[a+28>>2],b);return}K[b+28>>2]=0}return}K[b+16>>2]=0;K[b+24>>2]=0}function ac(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;f=Bb();K[b+96>>2]=f;a:{b:{if(!f){break b}c:{if($a(K[b+220>>2],18,d)){if($a(K[b+220>>2],19,d)){break c}}break a}i=K[b+220>>2];e=K[i>>2];g=K[i+8>>2];d:{if(e){f=1;k=e&1;if((e|0)==1){e=0}else{e=e&-2;while(1){h=0;e:{if(!f){break e}h=0;if(!(va[K[g>>2]](b,a,d)|0)){break e}h=(va[K[g+4>>2]](b,a,d)|0)!=0}f=h;g=g+8|0;j=j+2|0;if((e|0)!=(j|0)){continue}break}e=!f}f=k?0:f;if(!(e|!k)){f=(va[K[g>>2]](b,a,d)|0)!=0}Ta(i);if(f){break d}break a}Ta(i)}f:{if($a(K[b+216>>2],20,d)){if($a(K[b+216>>2],21,d)){break f}}break a}i=K[b+216>>2];e=K[i>>2];g=K[i+8>>2];g:{if(e){f=1;k=e&1;if((e|0)==1){e=0}else{e=e&-2;j=0;while(1){h=0;h:{if(!f){break h}h=0;if(!(va[K[g>>2]](b,a,d)|0)){break h}h=(va[K[g+4>>2]](b,a,d)|0)!=0}f=h;g=g+8|0;j=j+2|0;if((e|0)!=(j|0)){continue}break}e=!f}f=k?0:f;if(!(e|!k)){f=(va[K[g>>2]](b,a,d)|0)!=0}Ta(i);if(f){break g}break a}Ta(i)}a=Bb();K[c>>2]=a;if(!a){break b}Ob(K[b+96>>2],a);l=1}return l|0}Ya(K[b+96>>2]);K[b+96>>2]=0;return 0}function qb(a,b,c,d,e,f){var g=0,h=R(0),i=0,j=R(0);g=(c<<6)+b|0;a=c?g+-64|0:a;i=d>>>0>>0?d:e;a:{if(i>>>0<=c>>>0){b=a;break a}h=O[a>>2];while(1){b=g;g=b-32|0;j=h;h=O[b>>2];O[g>>2]=R(R(j+h)*f)+O[g>>2];g=b-28|0;O[g>>2]=R(R(O[a+4>>2]+O[b+4>>2])*f)+O[g>>2];g=b-24|0;O[g>>2]=R(R(O[a+8>>2]+O[b+8>>2])*f)+O[g>>2];g=b-20|0;O[g>>2]=R(R(O[a+12>>2]+O[b+12>>2])*f)+O[g>>2];g=b-16|0;O[g>>2]=R(R(O[a+16>>2]+O[b+16>>2])*f)+O[g>>2];g=b-12|0;O[g>>2]=R(R(O[a+20>>2]+O[b+20>>2])*f)+O[g>>2];g=b-8|0;O[g>>2]=R(R(O[a+24>>2]+O[b+24>>2])*f)+O[g>>2];g=b-4|0;O[g>>2]=R(R(O[a+28>>2]+O[b+28>>2])*f)+O[g>>2];g=b- -64|0;a=b;c=c+1|0;if((i|0)!=(c|0)){continue}break}}if(d>>>0>e>>>0){a=g-32|0;f=R(f+f);O[a>>2]=R(O[b>>2]*f)+O[a>>2];a=g-28|0;O[a>>2]=R(O[b+4>>2]*f)+O[a>>2];a=g-24|0;O[a>>2]=R(O[b+8>>2]*f)+O[a>>2];a=g-20|0;O[a>>2]=R(O[b+12>>2]*f)+O[a>>2];a=g-16|0;O[a>>2]=R(O[b+16>>2]*f)+O[a>>2];a=g-12|0;O[a>>2]=R(O[b+20>>2]*f)+O[a>>2];a=g-8|0;O[a>>2]=R(O[b+24>>2]*f)+O[a>>2];a=g-4|0;O[a>>2]=R(O[b+28>>2]*f)+O[a>>2]}}function Ld(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;f=ra-16|0;ra=f;a:{if(K[a+120>>2]|c>>>0<3){break a}Ha(b,f+12|0,2);j=M[f+12>>1];if(j-1025>>>0<=4294966271){K[f>>2]=j;Fa(d,1,3489,f);break a}Ha(b+2|0,f+12|0,1);i=M[f+12>>1];if(!i){Fa(d,1,3137,0);break a}if(i+3>>>0>c>>>0){break a}h=Ja(Q(i,j)<<2);if(!h){break a}k=Ja(i);if(!k){Ga(h);break a}l=Ja(i);if(!l){Ga(h);Ga(k);break a}g=Ja(20);if(!g){Ga(h);Ga(k);Ga(l);break a}d=b+3|0;K[g+8>>2]=k;K[g+4>>2]=l;J[g+16>>1]=j;K[g>>2]=h;m=K[f+12>>2];K[g+12>>2]=0;I[g+18|0]=m;K[a+120>>2]=g;while(1){Ha(d,f+12|0,1);I[e+k|0]=(L[f+12|0]&127)+1;I[e+l|0]=(K[f+12>>2]&128)>>>7;d=d+1|0;e=e+1|0;if((i|0)!=(e|0)){continue}break}if(!j){e=1;break a}g=0;while(1){e=0;a=0;while(1){e=L[e+k|0]+7>>>3|0;e=e>>>0>=4?4:e;if((e+(d-b|0)|0)>(c|0)){e=0;break a}Ha(d,f+12|0,e);K[h>>2]=K[f+12>>2];h=h+4|0;d=d+e|0;a=a+1|0;e=a&65535;if(i>>>0>e>>>0){continue}break}e=1;g=g+1|0;if((g&65535)>>>0>>0){continue}break}}ra=f+16|0;return e|0}function Pc(a){var b=0,c=0,d=0,e=0,f=0;d=K[6506];b=a+7&-8;c=b+7&-8;a=d+c|0;a:{b:{if(!(a>>>0<=d>>>0?c:0)){if(a>>>0<=wa()<<16>>>0){break b}if(na(a|0)|0){break b}}K[6585]=48;d=-1;break a}K[6506]=a}if((d|0)!=-1){a=b+d|0;K[a-4>>2]=16;c=a-16|0;K[c>>2]=16;b=K[6844];if(b){f=K[b+8>>2]}else{f=0}c:{d:{if((f|0)==(d|0)){e=d-(K[d-4>>2]&-2)|0;f=K[e-4>>2];K[b+8>>2]=a;a=e-(f&-2)|0;if(I[(a+K[a>>2]|0)-4|0]&1){b=K[a+4>>2];e=K[a+8>>2];K[b+8>>2]=e;K[e+4>>2]=b;b=c-a|0;K[a>>2]=b;break c}a=d-16|0;break d}K[d>>2]=16;K[d+8>>2]=a;K[d+4>>2]=b;K[d+12>>2]=16;K[6844]=d;a=d+16|0}b=c-a|0;K[a>>2]=b}K[((b&-4)+a|0)-4>>2]=b|1;c=K[a>>2]-8|0;e:{if(c>>>0<=127){b=(c>>>3|0)-1|0;break e}e=T(c);b=((c>>>29-e^4)-(e<<2)|0)+110|0;if(c>>>0<=4095){break e}b=((c>>>30-e^2)-(e<<1)|0)+71|0;b=b>>>0>=63?63:b}c=b<<4;K[a+4>>2]=c+26352;c=c+26360|0;K[a+8>>2]=K[c>>2];K[c>>2]=a;K[K[a+8>>2]+4>>2]=a;c=K[6846];e=K[6847];a=b&31;if((b&63)>>>0>=32){b=1<>>32-a}K[6846]=f|c;K[6847]=b|e}return(d|0)!=-1}function Dd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;f=-1;e=-1;if(!(L[a+68|0]&8)){f=K[a+32>>2];K[a+36>>2]=f;a:{b:{c:{e=K[a+48>>2];if(e){while(1){e=va[K[a+20>>2]](f,e,K[a>>2])|0;if((e|0)==-1){break c}f=e+K[a+36>>2]|0;K[a+36>>2]=f;e=K[a+48>>2]-e|0;K[a+48>>2]=e;if(e){continue}break}f=K[a+32>>2]}K[a+36>>2]=f;if(!!b&(c|0)>=0|(c|0)>0){break b}f=0;e=0;break a}K[a+68>>2]=K[a+68>>2]|8;Fa(d,4,15567,0);K[a+48>>2]=0;K[a+68>>2]=K[a+68>>2]|8;ua=-1;return-1}f=0;e=0;while(1){g=va[K[a+24>>2]](b,c,K[a>>2])|0;h=ua;i=h;if((g&h)==-1){Fa(d,4,15552,0);K[a+68>>2]=K[a+68>>2]|8;b=e+K[a+60>>2]|0;c=f+K[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;K[a+56>>2]=c;K[a+60>>2]=b;a=!(e|f);b=a?-1:f;ua=a?-1:e;return b|0}e=e+i|0;f=f+g|0;e=f>>>0>>0?e+1|0:e;h=b;b=b-g|0;c=c-(i+(g>>>0>h>>>0)|0)|0;if(!!b&(c|0)>=0|(c|0)>0){continue}break}}b=e+K[a+60>>2]|0;c=f+K[a+56>>2]|0;b=c>>>0>>0?b+1|0:b;K[a+56>>2]=c;K[a+60>>2]=b}ua=e;return f|0}function Oc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;b=a;a:{if(b&3){while(1){c=L[b|0];if(!c|(c|0)==61){break a}b=b+1|0;if(b&3){continue}break}}b:{c:{d=K[b>>2];if(((d|16843008-d)&-2139062144)!=-2139062144){break c}while(1){c=d^1027423549;if(((16843008-c|c)&-2139062144)!=-2139062144){break c}d=K[b+4>>2];c=b+4|0;b=c;if(((16843008-d|d)&-2139062144)==-2139062144){continue}break}break b}c=b}while(1){b=c;d=L[b|0];if(!d){break a}c=b+1|0;if((d|0)!=61){continue}break}}if((a|0)==(b|0)){return 0}g=b-a|0;d:{if(L[g+a|0]){break d}f=K[6848];if(!f){break d}b=K[f>>2];if(!b){break d}while(1){e:{d=a;c=b;h=g;e=0;f:{if(!g){break f}e=L[d|0];if(e){g:{while(1){i=L[c|0];if((i|0)!=(e|0)|!i){break g}h=h-1|0;if(!h){break g}c=c+1|0;e=L[d+1|0];d=d+1|0;if(e){continue}break}e=0}}else{e=0}e=e-L[c|0]|0}if(!e){b=b+g|0;if(L[b|0]==61){break e}}b=K[f+4>>2];f=f+4|0;if(b){continue}break d}break}j=b+1|0}return j}function ue(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;g=ra-16|0;ra=g;a:{if(c>>>0<=1){Fa(d,1,3946,0);a=0;break a}if(I[a+212|0]&1){Fa(d,1,12631,0);a=0;break a}a=K[a+180>>2]+Q(K[a+228>>2],5644)|0;I[a+5640|0]=L[a+5640|0]|2;Ha(b,g+12|0,1);e=K[a+5164>>2];b:{if(!e){f=K[g+12>>2]+1|0;e=Ia(f,8);K[a+5164>>2]=e;if(!e){Fa(d,1,3972,0);a=0;break a}K[a+5160>>2]=f;break b}f=K[g+12>>2];if(f>>>0>2]){break b}h=e;e=f+1|0;f=La(h,e<<3);if(!f){Fa(d,1,3972,0);a=0;break a}K[a+5164>>2]=f;h=K[a+5160>>2];i=e-h<<3;if(i){B(f+(h<<3)|0,0,i)}K[a+5160>>2]=e;e=K[a+5164>>2]}h=e;e=K[g+12>>2];if(K[h+(e<<3)>>2]){K[g>>2]=e;Fa(d,1,7026,g);a=0;break a}c=c-1|0;e=Ja(c);a=K[a+5164>>2];f=K[g+12>>2];K[a+(f<<3)>>2]=e;if(!e){Fa(d,1,3972,0);a=0;break a}K[(a+(f<<3)|0)+4>>2]=c;if(c){E(K[a+(K[g+12>>2]<<3)>>2],b+1|0,c)}a=1}ra=g+16|0;return a|0}function Lb(a,b,c){var d=0,e=0,f=0,g=0;e=a+4|0;d=(e+b|0)-1&0-b;b=K[a>>2];if(d+c>>>0<=(b+a|0)-4>>>0){f=K[a+4>>2];g=K[a+8>>2];K[f+8>>2]=g;K[g+4>>2]=f;if((d|0)!=(e|0)){d=d-e|0;f=a-(K[a-4>>2]&-2)|0;e=d+K[f>>2]|0;K[f>>2]=e;K[(f+(e&-4)|0)-4>>2]=e;a=a+d|0;b=b-d|0;K[a>>2]=b}a:{if(c+24>>>0<=b>>>0){e=a+c|0;b=(b-c|0)-8|0;K[e+8>>2]=b;g=e+8|0;K[(g+(b&-4)|0)-4>>2]=b|1;d=K[e+8>>2]-8|0;b:{if(d>>>0<=127){b=(d>>>3|0)-1|0;break b}f=T(d);b=((d>>>29-f^4)-(f<<2)|0)+110|0;if(d>>>0<=4095){break b}b=((d>>>30-f^2)-(f<<1)|0)+71|0;b=b>>>0>=63?63:b}d=b<<4;K[e+12>>2]=d+26352;d=d+26360|0;K[e+16>>2]=K[d>>2];K[d>>2]=g;K[K[e+16>>2]+4>>2]=g;d=K[6846];f=K[6847];e=b&31;if((b&63)>>>0>=32){b=1<>>32-e}K[6846]=g|d;K[6847]=b|f;b=c+8|0;K[a>>2]=b;c=(b&-4)+a|0;break a}c=a+b|0}K[c-4>>2]=b;a=a+4|0}else{a=0}return a}function Ae(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;g=ra-16|0;ra=g;i=K[K[a+96>>2]+16>>2];h=i>>>0<257?1:2;e=(h<<1)+5|0;f=(c>>>0)/(e>>>0)|0;a:{if(!((Q(e,f)|0)==(c|0)&c>>>0>=e>>>0)){Fa(d,1,4606,0);a=0;break a}if(K[a+8>>2]==16){e=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{e=K[a+12>>2]}a=0;c=L[e+5640|0];a=c&4?K[e+420>>2]+1|0:a;f=f+a|0;if(f>>>0>=32){K[g>>2]=f;Fa(d,1,7744,g);a=0;break a}I[e+5640|0]=c|4;if(a>>>0>>0){c=(e+Q(a,148)|0)+424|0;while(1){Ha(b,c,1);b=b+1|0;Ha(b,c+4|0,h);b=b+h|0;Ha(b,c+8|0,2);d=K[c+8>>2];j=K[e+8>>2];K[c+8>>2]=d>>>0>>0?d:j;Ha(b+2|0,c+12|0,1);b=b+3|0;Ha(b,c+16|0,h);b=b+h|0;Ha(b,g+12|0,1);K[c+36>>2]=K[g+12>>2];d=K[c+16>>2];K[c+16>>2]=d>>>0>>0?d:i;c=c+148|0;b=b+1|0;a=a+1|0;if((f|0)!=(a|0)){continue}break}}K[e+420>>2]=f-1;a=1}ra=g+16|0;return a|0}function nb(a){var b=0,c=0,d=0,e=0;a:{if(!a){break a}b=K[a+5164>>2];if(b){c=K[a+5160>>2];if(c){b=0;while(1){d=K[K[a+5164>>2]+(b<<3)>>2];if(d){Ga(d);c=K[a+5160>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=K[a+5164>>2]}K[a+5160>>2]=0;Ga(b);K[a+5164>>2]=0}b=K[a+5172>>2];if(b){Ga(b);K[a+5172>>2]=0}b=K[a+5584>>2];if(b){Ga(b);K[a+5584>>2]=0}b=K[a+5612>>2];if(b){Ga(b);K[a+5612>>2]=0}b=K[a+5608>>2];if(b){Ga(b);K[a+5608>>2]=0}b=K[a+5628>>2];if(b){Ga(b);K[a+5636>>2]=0;K[a+5628>>2]=0;K[a+5632>>2]=0}b=K[a+5616>>2];if(b){e=K[a+5620>>2];if(e){c=0;while(1){d=K[b+12>>2];if(d){Ga(d);K[b+12>>2]=0;e=K[a+5620>>2]}b=b+20|0;c=c+1|0;if(e>>>0>c>>>0){continue}break}b=K[a+5616>>2]}Ga(b);K[a+5616>>2]=0}b=K[a+5604>>2];if(b){Ga(b);K[a+5604>>2]=0}b=K[a+5596>>2];if(!b){break a}Ga(b);K[a+5596>>2]=0;K[a+5600>>2]=0}}function Od(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=ra-32|0;ra=e;a:{if(K[a+72>>2]){Fa(d,2,6978,0);c=1;break a}if((c|0)!=14){c=0;Fa(d,1,14408,0);break a}Ha(b,a+16|0,4);Ha(b+4|0,a+12|0,4);Ha(b+8|0,a+20|0,2);f=K[a+12>>2];b:{g=K[a+16>>2];c=K[a+20>>2];c:{if(!g){break c}c=K[a+20>>2];if(!f){break c}if(c){break b}c=0}K[e+8>>2]=c;K[e+4>>2]=g;K[e>>2]=f;Fa(d,1,14252,e);c=0;break a}if(c-16385>>>0<=4294950911){c=0;Fa(d,1,14166,0);break a}c=Ia(c,12);K[a+72>>2]=c;if(!c){c=0;Fa(d,1,14203,0);break a}c=1;Ha(b+10|0,a+24|0,1);Ha(b+11|0,a+28|0,1);f=K[a+28>>2];if((f|0)!=7){K[e+16>>2]=f;Fa(d,4,16235,e+16|0)}Ha(b+12|0,a+32|0,1);Ha(b+13|0,a+36|0,1);b=K[a>>2];I[b+212|0]=L[b+212|0]&251|(K[a+24>>2]==255)<<2;b=K[a>>2];K[b+240>>2]=K[a+12>>2];K[b+244>>2]=K[a+16>>2];I[a+133|0]=1}ra=e+32|0;return c|0}function Hc(a,b,c,d){a:{switch(b-9|0){case 0:b=K[c>>2];K[c>>2]=b+4;K[a>>2]=K[b>>2];return;case 6:b=K[c>>2];K[c>>2]=b+4;b=J[b>>1];K[a>>2]=b;K[a+4>>2]=b>>31;return;case 7:b=K[c>>2];K[c>>2]=b+4;K[a>>2]=M[b>>1];K[a+4>>2]=0;return;case 8:b=K[c>>2];K[c>>2]=b+4;b=I[b|0];K[a>>2]=b;K[a+4>>2]=b>>31;return;case 9:b=K[c>>2];K[c>>2]=b+4;K[a>>2]=L[b|0];K[a+4>>2]=0;return;case 16:b=K[c>>2]+7&-8;K[c>>2]=b+8;P[a>>3]=P[b>>3];return;case 17:va[d|0](a,c);default:return;case 1:case 4:case 14:b=K[c>>2];K[c>>2]=b+4;b=K[b>>2];K[a>>2]=b;K[a+4>>2]=b>>31;return;case 2:case 5:case 11:case 15:b=K[c>>2];K[c>>2]=b+4;K[a>>2]=K[b>>2];K[a+4>>2]=0;return;case 3:case 10:case 12:case 13:break a}}b=K[c>>2]+7&-8;K[c>>2]=b+8;c=K[b+4>>2];K[a>>2]=K[b>>2];K[a+4>>2]=c}function ve(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;g=ra-16|0;ra=g;a:{if(c>>>0<=1){Fa(d,1,4274,0);a=0;break a}I[a+212|0]=L[a+212|0]|1;Ha(b,g+12|0,1);e=K[a+140>>2];b:{if(!e){f=K[g+12>>2]+1|0;e=Ia(f,8);K[a+140>>2]=e;if(!e){Fa(d,1,4300,0);a=0;break a}K[a+136>>2]=f;break b}f=K[g+12>>2];if(f>>>0>2]){break b}h=e;e=f+1|0;f=La(h,e<<3);if(!f){Fa(d,1,4300,0);a=0;break a}K[a+140>>2]=f;h=K[a+136>>2];i=e-h<<3;if(i){B(f+(h<<3)|0,0,i)}K[a+136>>2]=e;e=K[a+140>>2]}h=e;e=K[g+12>>2];if(K[h+(e<<3)>>2]){K[g>>2]=e;Fa(d,1,7048,g);a=0;break a}c=c-1|0;e=Ja(c);a=K[a+140>>2];f=K[g+12>>2];K[a+(f<<3)>>2]=e;if(!e){Fa(d,1,4300,0);a=0;break a}K[(a+(f<<3)|0)+4>>2]=c;if(c){E(K[a+(K[g+12>>2]<<3)>>2],b+1|0,c)}a=1}ra=g+16|0;return a|0}function yd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0;d=ra-32|0;ra=d;e=K[a+28>>2];K[d+16>>2]=e;f=K[a+20>>2];K[d+28>>2]=c;K[d+24>>2]=b;b=f-e|0;K[d+20>>2]=b;f=b+c|0;i=2;b=d+16|0;a:{while(1){b:{c:{d:{if(!Kb(ba(K[a+60>>2],b|0,i|0,d+12|0)|0)){g=K[d+12>>2];if((g|0)==(f|0)){break d}if((g|0)>=0){break c}break b}if((f|0)!=-1){break b}}b=K[a+44>>2];K[a+28>>2]=b;K[a+20>>2]=b;K[a+16>>2]=b+K[a+48>>2];a=c;break a}h=K[b+4>>2];j=h>>>0>>0;e=(j<<3)+b|0;h=g-(j?h:0)|0;K[e>>2]=h+K[e>>2];b=(j?12:4)+b|0;K[b>>2]=K[b>>2]-h;f=f-g|0;i=i-j|0;b=e;continue}break}K[a+28>>2]=0;K[a+16>>2]=0;K[a+20>>2]=0;K[a>>2]=K[a>>2]|32;a=0;if((i|0)==2){break a}a=c-K[b+4>>2]|0}ra=d+32|0;return a|0}function Ga(a){a=a|0;var b=0,c=0,d=0,e=0,f=0;if(a){b=a-4|0;f=K[b>>2];c=f;d=b;e=K[a-8>>2];a=e&-2;if((a|0)!=(e|0)){d=b-a|0;c=K[d+4>>2];e=K[d+8>>2];K[c+8>>2]=e;K[e+4>>2]=c;c=a+f|0}a=b+f|0;b=K[a>>2];if((b|0)!=K[(a+b|0)-4>>2]){f=K[a+4>>2];a=K[a+8>>2];K[f+8>>2]=a;K[a+4>>2]=f;c=b+c|0}K[d>>2]=c;K[((c&-4)+d|0)-4>>2]=c|1;b=K[d>>2]-8|0;a:{if(b>>>0<=127){a=(b>>>3|0)-1|0;break a}c=T(b);a=((b>>>29-c^4)-(c<<2)|0)+110|0;if(b>>>0<=4095){break a}a=((b>>>30-c^2)-(c<<1)|0)+71|0;a=a>>>0>=63?63:a}b=a<<4;K[d+4>>2]=b+26352;b=b+26360|0;K[d+8>>2]=K[b>>2];K[b>>2]=d;K[K[d+8>>2]+4>>2]=d;b=K[6846];c=K[6847];d=a&31;if((a&63)>>>0>=32){a=1<>>32-d}K[6846]=e|b;K[6847]=a|c}}function ld(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;if(N[a+44>>2]>=8){e=K[a+36>>2];j=e<<5;k=Q(e,28);l=Q(e,24);m=Q(e,20);n=e<<4;o=Q(e,12);p=e<<3;f=K[a+40>>2];g=8;while(1){Hb(a,f,K[a+36>>2],8);Za(a);h=K[a+32>>2];if(h){i=K[a>>2];b=0;while(1){c=(b<<2)+f|0;d=i+(b<<5)|0;O[c>>2]=O[d>>2];O[c+(e<<2)>>2]=O[d+4>>2];O[c+p>>2]=O[d+8>>2];O[c+o>>2]=O[d+12>>2];b=b+1|0;if((h|0)!=(b|0)){continue}break}i=K[a>>2];b=0;while(1){c=(b<<2)+f|0;d=i+(b<<5)|0;O[c+n>>2]=O[d+16>>2];O[c+m>>2]=O[d+20>>2];O[c+l>>2]=O[d+24>>2];O[c+k>>2]=O[d+28>>2];b=b+1|0;if((h|0)!=(b|0)){continue}break}}f=f+j|0;g=g+8|0;if(g>>>0<=N[a+44>>2]){continue}break}}Ga(K[a>>2]);Ga(a)}function Id(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;e=ra-16|0;ra=e;a:{if(K[a+116>>2]){break a}if(c>>>0<=1){Fa(d,1,8845,0);break a}Ha(b,e+12|0,2);f=K[e+12>>2];h=f&65535;if(!h){Fa(d,1,8878,0);break a}if(Q(h,6)+2>>>0>c>>>0){Fa(d,1,8845,0);break a}d=Ja(Q(f,6));if(!d){break a}c=Ja(8);K[a+116>>2]=c;if(!c){Ga(d);break a}K[c>>2]=d;f=c;c=M[e+12>>1];J[f+4>>1]=c;if(!c){g=1;break a}c=0;while(1){g=e+12|0;Ha(b+2|0,g,2);f=d+Q(c,6)|0;J[f>>1]=K[e+12>>2];Ha(b+4|0,g,2);J[f+2>>1]=K[e+12>>2];b=b+6|0;Ha(b,g,2);J[f+4>>1]=K[e+12>>2];g=1;c=c+1|0;if(c>>>0>2]+4>>1]){continue}break}}ra=e+16|0;return g|0}function $b(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;g=ra-32|0;ra=g;f=K[a+96>>2];a:{if(!f){Fa(d,1,13715,0);e=0;break a}f=Ia(4,K[f+16>>2]);e=0;if(!f){break a}if(b){j=K[a+96>>2];while(1){b:{e=K[(h<<2)+c>>2];c:{if(e>>>0>=N[j+16>>2]){K[g+16>>2]=e;Fa(d,1,2406,g+16|0);break c}i=f+(e<<2)|0;if(!K[i>>2]){break b}K[g>>2]=e;Fa(d,1,3450,g)}Ga(f);e=0;break a}K[i>>2]=1;h=h+1|0;if((h|0)!=(b|0)){continue}break}}Ga(f);Ga(K[a+64>>2]);d:{if(b){d=b<<2;e=Ja(d);K[a+64>>2]=e;if(!e){K[a+60>>2]=0;e=0;break a}if(!d){break d}E(e,c,d);break d}K[a+64>>2]=0}K[a+60>>2]=b;e=1}ra=g+32|0;return e|0}function Tc(a){a=a|0;var b=0,c=0;if(a){Eb(K[a>>2]);K[a>>2]=0;b=K[a+72>>2];if(b){Ga(b);K[a+72>>2]=0}b=K[a+68>>2];if(b){Ga(b);K[a+68>>2]=0}b=K[a+108>>2];if(b){Ga(b);K[a+108>>2]=0}b=K[a+116>>2];if(b){c=K[b>>2];if(c){Ga(c);b=K[a+116>>2];K[b>>2]=0}Ga(b);K[a+116>>2]=0}b=K[a+120>>2];if(b){c=K[b+12>>2];if(c){Ga(c);b=K[a+120>>2];K[b+12>>2]=0}c=K[b+4>>2];if(c){Ga(c);b=K[a+120>>2];K[b+4>>2]=0}c=K[b+8>>2];if(c){Ga(c);b=K[a+120>>2];K[b+8>>2]=0}c=K[b>>2];if(c){Ga(c);b=K[a+120>>2];K[b>>2]=0}Ga(b);K[a+120>>2]=0}b=K[a+4>>2];if(b){tb(b);K[a+4>>2]=0}b=K[a+8>>2];if(b){tb(b);K[a+8>>2]=0}Ga(a)}}function Yb(){var a=0,b=0,c=0;a:{a=Ia(1,256);if(a){K[a>>2]=1;K[a+208>>2]=1;I[a+212|0]=L[a+212|0]|6;b=Ia(1,5644);K[a+12>>2]=b;if(!b){break a}b=Ia(1,1e3);K[a+16>>2]=b;if(!b){break a}K[a+48>>2]=0;K[a+52>>2]=0;K[a+44>>2]=-1;K[a+20>>2]=1e3;b:{c=Ia(1,48);if(c){K[c+24>>2]=0;K[c+32>>2]=100;b=Ia(100,24);K[c+28>>2]=b;if(b){break b}Ga(c)}K[a+224>>2]=0;break a}K[c+40>>2]=0;K[a+224>>2]=c;b=ub();K[a+220>>2]=b;if(!b){break a}b=ub();K[a+216>>2]=b;if(!b){break a}c:{if(!Oc(1382)){break c}}b=zc();K[a+236>>2]=b;if(!b){b=zc();K[a+236>>2]=b;if(!b){break a}}}else{a=0}return a}Eb(a);return 0}function xb(a,b,c,d,e,f){var g=0,h=0,i=0,j=0,k=0,l=0;g=ra-240|0;ra=g;K[g+236>>2]=c;K[g+232>>2]=b;K[g>>2]=a;l=!e;a:{b:{c:{d:{if((b|0)!=1){h=a;i=1;break d}h=a;i=1;if(c){break d}e=a;break c}while(1){j=(d<<2)+f|0;e=h-K[j>>2]|0;if((gb(e,a)|0)<=0){e=h;break c}k=l^-1;l=1;e:{if(!((k|(d|0)<2)&1)){j=K[j-8>>2];k=h-8|0;if((gb(k,e)|0)>=0){break e}if((gb(k-j|0,e)|0)>=0){break e}}K[(i<<2)+g>>2]=e;b=Nc(b,c);yb(g+232|0,b);i=i+1|0;d=b+d|0;h=e;c=K[g+236>>2];b=K[g+232>>2];if(c|(b|0)!=1){continue}break b}break}e=h;break b}if(!l){break a}}Mc(g,i);Jb(e,d,f)}ra=g+240|0}function Kc(a,b,c,d,e){var f=0,g=0,h=0;f=ra-208|0;ra=f;K[f+204>>2]=c;c=f+160|0;B(c,0,40);K[f+200>>2]=K[f+204>>2];a:{if((Jc(0,b,f+200|0,f+80|0,c,d,e)|0)<0){break a}c=K[a+76>>2]<0;g=K[a>>2];K[a>>2]=g&-33;b:{c:{d:{if(!K[a+48>>2]){K[a+48>>2]=80;K[a+28>>2]=0;K[a+16>>2]=0;K[a+20>>2]=0;h=K[a+44>>2];K[a+44>>2]=f;break d}if(K[a+16>>2]){break c}}if(Nb(a)){break b}}Jc(a,b,f+200|0,f+80|0,f+160|0,d,e)}if(h){va[K[a+36>>2]](a,0,0)|0;K[a+48>>2]=0;K[a+44>>2]=h;K[a+28>>2]=0;K[a+16>>2]=0;K[a+20>>2]=0}K[a>>2]=K[a>>2]|g&32;if(c){break a}}ra=f+208|0}function Fe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;e=ra-16|0;ra=e;if(K[a+8>>2]==16){g=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{g=K[a+12>>2]}h=K[a+96>>2];f=N[h+16>>2]<257?1:2;a:{if(f>>>0>=c>>>0){c=0;Fa(d,1,4632,0);break a}K[e+12>>2]=(f^-1)+c;Ha(b,e+8|0,f);i=K[e+8>>2];if(i>>>0>=N[h+16>>2]){c=0;Fa(d,1,14030,0);break a}c=1;b=b+f|0;Ha(b,K[g+5584>>2]+Q(i,1080)|0,1);if(!_c(a,K[e+8>>2],b+1|0,e+12|0,d)){c=0;Fa(d,1,4632,0);break a}if(!K[e+12>>2]){break a}c=0;Fa(d,1,4632,0)}ra=e+16|0;return c|0}function Vc(a,b){var c=0,d=0,e=0,f=0,g=0;f=ra-32|0;ra=f;c=K[a+60>>2];a:{b:{if(c){g=1;while(1){e=K[K[a+64>>2]+(d<<2)>>2];if(!K[(K[K[a+100>>2]+24>>2]+Q(e,52)|0)+44>>2]){K[f+16>>2]=e;Fa(b,2,7567,f+16|0);g=0;c=K[a+60>>2]}d=d+1|0;if(c>>>0>d>>>0){continue}break}break b}g=1;c=K[a+100>>2];e=1;if(!K[c+16>>2]){break a}while(1){if(!K[(K[c+24>>2]+Q(d,52)|0)+44>>2]){K[f>>2]=d;Fa(b,2,7567,f);g=0;c=K[a+100>>2]}d=d+1|0;if(d>>>0>2]){continue}break}}e=1;if(g){break a}Fa(b,1,2860,0);e=0}ra=f+32|0;return e}function Kd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;f=ra-16|0;ra=f;e=K[a+120>>2];a:{if(!e){Fa(d,1,8799,0);c=0;break a}if(K[e+12>>2]){Fa(d,1,11561,0);c=0;break a}e=L[e+18|0];g=e<<2;if(g>>>0>c>>>0){Fa(d,1,8766,0);c=0;break a}g=Ja(g);c=0;if(!g){break a}if(e){d=0;while(1){c=f+12|0;Ha(b,c,2);h=g+(d<<2)|0;J[h>>1]=K[f+12>>2];Ha(b+2|0,c,1);I[h+2|0]=K[f+12>>2];Ha(b+3|0,c,1);I[h+3|0]=K[f+12>>2];b=b+4|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}}K[K[a+120>>2]+12>>2]=g;c=1}ra=f+16|0;return c|0}function qe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;e=ra-16|0;ra=e;g=K[K[a+96>>2]+16>>2];a:{if((g+2|0)!=(c|0)){Fa(d,1,4580,0);break a}Ha(b,e+12|0,2);if(K[e+12>>2]!=(g|0)){Fa(d,1,4580,0);break a}if(!g){f=1;break a}c=b+2|0;a=K[K[a+96>>2]+24>>2];b=0;while(1){Ha(c,e+8|0,1);f=K[e+8>>2];h=f&127;i=h+1|0;K[a+24>>2]=i;K[a+32>>2]=f>>>7&1;if(h>>>0>=31){K[e+4>>2]=i;K[e>>2]=b;Fa(d,1,15365,e);f=0;break a}a=a+52|0;f=1;c=c+1|0;b=b+1|0;if((g|0)!=(b|0)){continue}break}}ra=e+16|0;return f|0}function Ce(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0;e=ra-16|0;ra=e;a:{b:{h=e+8|0;c:{if(N[K[a+96>>2]+16>>2]<=256){if(c){f=-1;g=1;break c}Fa(d,1,4658,0);a=0;break a}if(c>>>0<=1){break b}f=-2;g=2}Ha(b,h,g);K[e+12>>2]=c+f;c=K[e+8>>2];f=K[K[a+96>>2]+16>>2];if(c>>>0>=f>>>0){K[e+4>>2]=f;K[e>>2]=c;Fa(d,1,7675,e);a=0;break a}if(!Zc(a,c,b+g|0,e+12|0,d)){Fa(d,1,4658,0);a=0;break a}a=1;if(!K[e+12>>2]){break a}Fa(d,1,4658,0);a=0;break a}Fa(d,1,4658,0);a=0}ra=e+16|0;return a|0}function tc(a,b,c,d){var e=0,f=0,g=0;g=ra-128|0;ra=g;f=g;c=K[b+12>>2]+(c<<4)|0;e=K[c>>2];a:{if(!e){b=c;break a}while(1){K[f>>2]=c;f=f+4|0;b=e;c=b;e=K[c>>2];if(e){continue}break}}e=0;while(1){c=K[b+8>>2];if((e|0)>(c|0)){K[b+8>>2]=e;c=e}b:{if((c|0)>=(d|0)){break b}while(1){if(K[b+4>>2]<=(c|0)){break b}c:{if(Wa(a,1)){K[b+4>>2]=c;break c}c=c+1|0}if((c|0)<(d|0)){continue}break}}K[b+8>>2]=c;if((f|0)!=(g|0)){f=f-4|0;b=K[f>>2];e=c;continue}break}ra=g+128|0;return K[b+4>>2]<(d|0)} +function Ud(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=K[a+32>>2];K[a+36>>2]=f;a:{e=K[a+48>>2];if(e){while(1){e=va[K[a+20>>2]](f,e,K[a>>2])|0;if((e|0)==-1){break a}f=e+K[a+36>>2]|0;K[a+36>>2]=f;e=K[a+48>>2]-e|0;K[a+48>>2]=e;if(e){continue}break}f=K[a+32>>2]}K[a+48>>2]=0;K[a+36>>2]=f;if(!(va[K[a+28>>2]](b,c,K[a>>2])|0)){K[a+68>>2]=K[a+68>>2]|8;return 0}K[a+56>>2]=b;K[a+60>>2]=c;return 1}K[a+68>>2]=K[a+68>>2]|8;Fa(d,4,15567,0);K[a+68>>2]=K[a+68>>2]|8;return 0}function Fa(a,b,c,d){var e=0,f=0;e=ra-528|0;ra=e;a:{if(!a){break a}b:{c:{switch(b-1|0){case 0:b=a+12|0;break b;case 1:b=a+16|0;a=a+4|0;break b;case 3:break c;default:break a}}b=a+20|0;a=a+8|0}b=K[b>>2];if(!b|!c){break a}f=K[a>>2];B(e,0,512);K[e+524>>2]=d;a=ra-160|0;ra=a;K[a+148>>2]=e;K[a+152>>2]=511;B(a,0,144);K[a+76>>2]=-1;K[a+36>>2]=103;K[a+80>>2]=-1;K[a+44>>2]=a+159;K[a+84>>2]=a+148;I[e|0]=0;Kc(a,c,d,104,105);ra=a+160|0;I[e+511|0]=0;va[b|0](e,f)}ra=e+528|0}function Qd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;if(K[a+100>>2]!=1){Fa(d,1,11364,0);return 0}a:{if(c>>>0<=7){break a}Ha(b,a+56|0,4);Ha(b+4|0,a+60|0,4);if(c&3){break a}c=c-8|0;e=c>>>2|0;K[a+64>>2]=e;b:{if(!c){break b}c=Ia(e,4);K[a+68>>2]=c;if(!c){Fa(d,1,2198,0);return 0}if(!K[a+64>>2]){break b}d=b+8|0;c=0;while(1){Ha(d,K[a+68>>2]+(c<<2)|0,4);d=d+4|0;c=c+1|0;if(c>>>0>2]){continue}break}}K[a+100>>2]=K[a+100>>2]|2;return 1}Fa(d,1,5918,0);return 0}function vc(a){var b=0,c=0,d=0;a:{if(!a){break a}b=K[a+8>>2];if(!b){break a}a=K[a+12>>2];if(b>>>0>=4){d=b&-4;while(1){K[a+60>>2]=0;K[a+52>>2]=999;K[a+56>>2]=0;K[a+44>>2]=0;K[a+36>>2]=999;K[a+40>>2]=0;K[a+28>>2]=0;K[a+20>>2]=999;K[a+24>>2]=0;K[a+12>>2]=0;K[a+4>>2]=999;K[a+8>>2]=0;a=a- -64|0;c=c+4|0;if((d|0)!=(c|0)){continue}break}}b=b&3;if(!b){break a}c=0;while(1){K[a+12>>2]=0;K[a+4>>2]=999;K[a+8>>2]=0;a=a+16|0;c=c+1|0;if((b|0)!=(c|0)){continue}break}}}function De(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=ra-16|0;ra=e;K[e+12>>2]=c;a:{if(!(!Zc(a,0,b,e+12|0,d)|K[e+12>>2])){if(K[a+8>>2]==16){b=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{b=K[a+12>>2]}f=1;if(N[K[a+96>>2]+16>>2]<2){break a}c=K[b+5584>>2];g=c+28|0;b=1;d=c;while(1){K[d+1104>>2]=K[c+24>>2];K[d+1884>>2]=K[c+804>>2];E(d+1108|0,g,776);d=d+1080|0;b=b+1|0;if(b>>>0>2]+16>>2]){continue}break}break a}Fa(d,1,4554,0)}ra=e+16|0;return f|0}function Gc(a,b){a:{b:{if(b>>>0<=127){break b}c:{if(!K[K[6873]>>2]){if((b&-128)==57216){break b}break c}if(b>>>0<=2047){I[a+1|0]=b&63|128;I[a|0]=b>>>6|192;a=2;break a}if(!((b&-8192)!=57344&b>>>0>=55296)){I[a+2|0]=b&63|128;I[a|0]=b>>>12|224;I[a+1|0]=b>>>6&63|128;a=3;break a}if(b-65536>>>0<=1048575){I[a+3|0]=b&63|128;I[a|0]=b>>>18|240;I[a+2|0]=b>>>6&63|128;I[a+1|0]=b>>>12&63|128;a=4;break a}}K[6585]=25;a=-1;break a}I[a|0]=b;a=1}return a}function ce(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0;if(!$a(K[a+8>>2],54,c)){return 0}h=K[a+8>>2];d=K[h>>2];f=K[h+8>>2];a:{if(d){e=1;i=d&1;if((d|0)==1){d=0}else{d=d&-2;while(1){g=0;b:{if(!e){break b}g=0;if(!(va[K[f>>2]](a,b,c)|0)){break b}g=(va[K[f+4>>2]](a,b,c)|0)!=0}e=g;f=f+8|0;j=j+2|0;if((d|0)!=(j|0)){continue}break}d=!e}e=i?0:e;if(!(d|!i)){e=(va[K[f>>2]](a,b,c)|0)!=0}Ta(h);if(e){break a}return 0}Ta(h)}return 1}function Ee(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=ra-16|0;ra=e;g=K[K[a+96>>2]+16>>2];f=g>>>0<257?1:2;a:{if((f+2|0)!=(c|0)){a=0;Fa(d,1,4248,0);break a}if(K[a+8>>2]==16){c=K[a+180>>2]+Q(K[a+228>>2],5644)|0}else{c=K[a+12>>2]}Ha(b,e+12|0,f);a=1;b=b+f|0;Ha(b,e+8|0,1);f=K[e+12>>2];if(f>>>0>=g>>>0){K[e+4>>2]=g;K[e>>2]=f;Fa(d,1,14886,e);a=0;break a}Ha(b+1|0,(K[c+5584>>2]+Q(f,1080)|0)+808|0,1)}ra=e+16|0;return a|0}function Mb(a,b,c){var d=0,e=0,f=0;d=K[c+16>>2];a:{if(!d){if(Nb(c)){break a}d=K[c+16>>2]}e=K[c+20>>2];if(d-e>>>0>>0){return va[K[c+36>>2]](c,a,b)|0}b:{c:{if(!b|K[c+80>>2]<0){break c}d=b;while(1){f=a+d|0;if(L[f-1|0]!=10){d=d-1|0;if(d){continue}break c}break}e=va[K[c+36>>2]](c,a,d)|0;if(e>>>0>>0){break a}b=b-d|0;e=K[c+20>>2];break b}f=a;d=0}hb(e,f,b);K[c+20>>2]=K[c+20>>2]+b;e=b+d|0}return e}function Qe(a,b,c){var d=0,e=0,f=0,g=0;g=c&63;f=g;e=f&31;if(f>>>0>=32){f=-1>>>e|0}else{d=-1>>>e|0;f=d|(1<>>0>=32){d=f<>>32-e|d<>>0>=32){d=-1<>>32-d}a=c&a;b=b&d;d=e&31;if(e>>>0>=32){c=0;a=b>>>d|0}else{c=b>>>d|0;a=((1<>>d}a=a|g;ua=c|f;return a} +function lb(a,b,c){var d=0;if(!K[a+12>>2]){va[b|0](c,K[a+36>>2]);return}d=Ja(8);a:{if(!d){break a}K[d+4>>2]=c;K[d>>2]=b;b=Ja(8);if(!b){Ga(d);return}K[b>>2]=d;c=Q(K[a+4>>2],100);K[a+40>>2]=c;while(1){if((c|0)>2]){continue}break}K[b+4>>2]=K[a+20>>2];K[a+20>>2]=b;K[a+24>>2]=K[a+24>>2]+1;b=K[a+28>>2];if(!b){break a}K[K[b>>2]+8>>2]=0;K[a+28>>2]=K[b+4>>2];K[a+32>>2]=K[a+32>>2]-1;Ga(b)}}function $c(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;K[a+184>>2]=b;d=K[a+96>>2];a:{if(!d){break a}f=K[d+24>>2];if(!f){break a}e=K[a+12>>2];if(!e|!K[e+5584>>2]){break a}e=K[d+16>>2];if(!e){return 1}d=0;while(1){if(N[(K[K[a+12>>2]+5584>>2]+Q(d,1080)|0)+4>>2]<=b>>>0){Fa(c,1,9140,0);return 0}K[(Q(d,52)+f|0)+40>>2]=b;g=1;d=d+1|0;if((e|0)!=(d|0)){continue}break}}return g|0}function Qc(a){var b=0,c=0;b=K[a+76>>2];if(!((b|0)>=0&(!b|K[6855]!=(b&1073741823)))){a:{if(K[a+80>>2]==10){break a}b=K[a+20>>2];if((b|0)==K[a+16>>2]){break a}K[a+20>>2]=b+1;I[b|0]=10;return}Rc(a);return}b=a+76|0;c=K[b>>2];K[b>>2]=c?c:1073741823;b:{c:{if(K[a+80>>2]==10){break c}c=K[a+20>>2];if((c|0)==K[a+16>>2]){break c}K[a+20>>2]=c+1;I[c|0]=10;break b}Rc(a)}K[b>>2]=0}function Qb(){var a=0,b=0,c=0;while(1){b=a<<4;c=b+26352|0;K[b+26356>>2]=c;K[b+26360>>2]=c;a=a+1|0;if((a|0)!=64){continue}break}Pc(48);a=ra-16|0;ra=a;a:{if(pa(a+12|0,a+8|0)|0){break a}b=Ab((K[a+12>>2]<<2)+4|0);K[6848]=b;if(!b){break a}b=Ab(K[a+8>>2]);if(b){c=K[6848];K[c+(K[a+12>>2]<<2)>>2]=0;if(!(oa(c|0,b|0)|0)){break a}}K[6848]=0}ra=a+16|0;K[6855]=42;K[6873]=27560}function Oa(a,b,c,d,e,f,g,h){var i=0,j=0;i=+R(e-a|0);j=i*1.402;if(S(j)<2147483647){e=~~j}else{e=-2147483648}e=e+c|0;K[f>>2]=(e|0)>=0?(b|0)>(e|0)?e:b:0;j=+R(d-a|0);i=j*.344+i*.714;if(S(i)<2147483647){a=~~i}else{a=-2147483648}a=c-a|0;K[g>>2]=(a|0)>=0?(a|0)<(b|0)?a:b:0;i=j*1.772;if(S(i)<2147483647){a=~~i}else{a=-2147483648}a=a+c|0;K[h>>2]=(a|0)>=0?(a|0)<(b|0)?a:b:0}function sd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;e=K[a+84>>2];f=K[e>>2];d=K[e+4>>2];h=K[a+28>>2];g=K[a+20>>2]-h|0;g=d>>>0>>0?d:g;if(g){hb(f,h,g);f=g+K[e>>2]|0;K[e>>2]=f;d=K[e+4>>2]-g|0;K[e+4>>2]=d}d=c>>>0>d>>>0?d:c;if(d){hb(f,b,d);f=d+K[e>>2]|0;K[e>>2]=f;K[e+4>>2]=K[e+4>>2]-d}I[f|0]=0;b=K[a+44>>2];K[a+28>>2]=b;K[a+20>>2]=b;return c|0}function Gb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;if(a){c=K[a+4>>2];if(c){Ga(c);K[a+4>>2]=0}if(b){c=a;while(1){d=K[c+200>>2];if(d){e=0;f=K[c+196>>2];if(f){while(1){g=K[d+12>>2];if(g){Ga(g);K[d+12>>2]=0;f=K[c+196>>2]}d=d+16|0;e=e+1|0;if(e>>>0>>0){continue}break}d=K[c+200>>2]}Ga(d);K[c+200>>2]=0}c=c+240|0;h=h+1|0;if((h|0)!=(b|0)){continue}break}}Ga(a)}}function Gd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0;e=K[c+8>>2];d=e>>>0<=1?1:e;f=K[c+4>>2];g=f-K[c>>2]|0;while(1){h=d;d=d<<1;if(h-g>>>0>>0){continue}break}if((e|0)!=(h|0)){d=Ja(h);if(!d){return-1}e=K[c>>2];if(e){if(g){E(d,e,g)}Ga(K[c>>2])}K[c+8>>2]=h;K[c>>2]=d;f=d+g|0;K[c+4>>2]=f}if(b){E(f,a,b)}K[c+4>>2]=K[c+4>>2]+b;return b|0}function mc(a){K[a+100>>2]=20784;K[a+96>>2]=20784;K[a+92>>2]=20784;K[a+88>>2]=20784;K[a+84>>2]=20784;K[a+80>>2]=20784;K[a+76>>2]=20784;K[a+72>>2]=20784;K[a+68>>2]=20784;K[a+64>>2]=20784;K[a+60>>2]=20784;K[a+56>>2]=20784;K[a+52>>2]=20784;K[a+48>>2]=20784;K[a+44>>2]=20784;K[a+40>>2]=20784;K[a+36>>2]=20784;K[a+32>>2]=20784;K[a+28>>2]=20784}function Wa(a,b){var c=0,d=0,e=0,f=0;if((b|0)<=0){return 0}c=K[a+12>>2];d=K[a+16>>2];while(1){e=b;a:{if(d){break a}c=c<<8&65280;K[a+12>>2]=c;d=(c|0)==65280?7:8;K[a+16>>2]=d;b=K[a+8>>2];if(b>>>0>=N[a+4>>2]){break a}K[a+8>>2]=b+1;c=L[b|0]|c;K[a+12>>2]=c}d=d-1|0;K[a+16>>2]=d;b=e-1|0;f=(c>>>d&1)<>>0>1){continue}break}return f}function Md(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;f=ra-16|0;ra=f;e=K[a+24>>2];if((e|0)!=255){K[f>>2]=e;Fa(d,2,2641,f)}a:{b:{if(K[a+20>>2]==(c|0)){if(c){break b}e=1;break a}e=0;Fa(d,1,14473,0);break a}c=0;while(1){e=1;Ha(b,(K[a+72>>2]+Q(c,12)|0)+8|0,1);b=b+1|0;c=c+1|0;if(c>>>0>2]){continue}break}}ra=f+16|0;return e|0}function Ha(a,b,c){var d=0,e=0;K[b>>2]=0;a:{if(!c){break a}d=c&3;b=b+c|0;if(c>>>0>=4){e=c&-4;c=0;while(1){I[b-1|0]=L[a|0];I[b-2|0]=L[a+1|0];I[b-3|0]=L[a+2|0];b=b-4|0;I[b|0]=L[a+3|0];a=a+4|0;c=c+4|0;if((e|0)!=(c|0)){continue}break}}if(!d){break a}c=0;while(1){b=b-1|0;I[b|0]=L[a|0];a=a+1|0;c=c+1|0;if((d|0)!=(c|0)){continue}break}}}function we(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;e=ra-16|0;ra=e;a:{if(!c){Fa(d,1,4069,0);a=0;break a}Ha(b,e+12|0,1);f=c-1|0;a=1;if(!f){break a}a=0;c=0;while(1){b=b+1|0;Ha(b,e+8|0,1);g=K[e+8>>2];c=g<<24>>31&(g&127|c)<<7;a=a+1|0;if((f|0)!=(a|0)){continue}break}a=1;if(!c){break a}Fa(d,1,4069,0);a=0}ra=e+16|0;return a|0}function rc(a,b,c,d){var e=0,f=0,g=R(0),h=0,i=R(0),j=0,k=R(0);if(d){while(1){e=f<<2;h=e+b|0;i=O[h>>2];j=a+e|0;g=O[j>>2];e=c+e|0;k=O[e>>2];O[j>>2]=R(k*R(1.4019999504089355))+g;O[h>>2]=R(g+R(i*R(-.3441300094127655)))+R(k*R(-.714139997959137));O[e>>2]=g+R(i*R(1.7719999551773071));f=f+1|0;if((f|0)!=(d|0)){continue}break}}}function Jb(a,b,c){var d=0,e=0,f=0,g=0,h=0,i=0;f=ra-240|0;ra=f;K[f>>2]=a;g=1;a:{if((b|0)<2){break a}d=a;while(1){d=d-8|0;h=b-2|0;e=d-K[(h<<2)+c>>2]|0;if((gb(a,e)|0)>=0){if((gb(a,d)|0)>=0){break a}}i=e;e=(gb(e,d)|0)>=0;d=e?i:d;K[(g<<2)+f>>2]=d;g=g+1|0;b=e?b-1|0:h;if((b|0)>1){continue}break}}Mc(f,g);ra=f+240|0}function Mc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;c=8;f=ra-256|0;ra=f;if((b|0)>=2){h=(b<<2)+a|0;K[h>>2]=f;while(1){e=c>>>0>=256?256:c;hb(K[h>>2],K[a>>2],e);d=0;while(1){g=(d<<2)+a|0;d=d+1|0;hb(K[g>>2],K[(d<<2)+a>>2],e);K[g>>2]=K[g>>2]+e;if((b|0)!=(d|0)){continue}break}c=c-e|0;if(c){continue}break}}ra=f+256|0}function gd(a){a=a|0;var b=0,c=0,d=0,e=0;b=K[a+24>>2];if(b){c=K[a+28>>2];e=(c>>>0)/52|0;if(c>>>0>=52){while(1){c=K[b>>2];if(c){Ga(c-1|0);K[b>>2]=0}c=K[b+4>>2];if(c){Ga(c);K[b+4>>2]=0}c=K[b+8>>2];if(c){Ga(c);K[b+8>>2]=0}b=b+52|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}b=K[a+24>>2]}Ga(b);K[a+24>>2]=0}}function hd(a){a=a|0;var b=0,c=0,d=0,e=0;b=K[a+24>>2];if(b){c=K[a+28>>2];e=(c>>>0)/68|0;if(c>>>0>=68){while(1){c=K[b>>2];if(c){Ga(c);K[b>>2]=0}c=K[b+4>>2];if(c){Ga(c);K[b+4>>2]=0}Ga(K[b+60>>2]);K[b+60>>2]=0;b=b+68|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}b=K[a+24>>2]}Ga(b);K[a+24>>2]=0}}function md(a,b){a=a|0;b=b|0;var c=0,d=0;c=K[a+32>>2];b=K[a+28>>2];d=b+8|0;if(c>>>0>=d>>>0){while(1){rb(a,K[a+24>>2]+(b<<2)|0,K[a+20>>2],8);c=K[a+32>>2];b=d;d=b+8|0;if(c>>>0>=d>>>0){continue}break}}if(b>>>0>>0){rb(a,K[a+24>>2]+(b<<2)|0,K[a+20>>2],c-b|0)}Ga(K[a>>2]);Ga(a)}function fb(a,b,c){var d=0,e=0,f=0;a:{if(!b){d=a;e=b;break a}while(1){d=Ne(a,b,10,0);e=ua;a=Le(d,e,246)+a|0;c=c-1|0;I[c|0]=a|48;f=b>>>0>9;a=d;b=e;if(f){continue}break}}if(d|e){while(1){c=c-1|0;a=(d>>>0)/10|0;I[c|0]=Q(a,246)+d|48;b=d>>>0>9;d=a;if(b){continue}break}}return c}function Rd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;e=ra-16|0;ra=e;a:{if(K[a+100>>2]){Fa(d,1,11265,0);a=0;break a}if((c|0)!=4){Fa(d,1,5954,0);a=0;break a}Ha(b,e+12|0,4);if(K[e+12>>2]!=218793738){Fa(d,1,4970,0);a=0;break a}K[a+100>>2]=K[a+100>>2]|1;a=1}ra=e+16|0;return a|0}function $a(a,b,c){var d=0,e=0;a:{d=K[a>>2];e=K[a+4>>2];b:{if((d|0)!=(e|0)){e=K[a+8>>2];break b}d=e+10|0;K[a+4>>2]=d;e=La(K[a+8>>2],d<<2);if(!e){break a}K[a+8>>2]=e;d=K[a>>2]}K[(d<<2)+e>>2]=b;K[a>>2]=d+1;return 1}Ga(K[a+8>>2]);K[a>>2]=0;K[a+4>>2]=0;Fa(c,1,6086,0);return 0}function Rc(a){var b=0,c=0,d=0;c=ra-16|0;ra=c;I[c+15|0]=10;b=K[a+16>>2];a:{if(!b){if(Nb(a)){break a}b=K[a+16>>2]}d=b;b=K[a+20>>2];if(!((d|0)==(b|0)|K[a+80>>2]==10)){K[a+20>>2]=b+1;I[b|0]=10;break a}if((va[K[a+36>>2]](a,c+15|0,1)|0)!=1){break a}}ra=c+16|0}function Ic(a){var b=0,c=0,d=0,e=0,f=0;d=K[a>>2];b=I[d|0]-48|0;if(b>>>0>9){return 0}while(1){e=-1;if(c>>>0<=214748364){c=Q(c,10);e=(c^2147483647)>>>0>>0?-1:c+b|0}b=d+1|0;K[a>>2]=b;f=I[d+1|0];c=e;d=b;b=f-48|0;if(b>>>0<10){continue}break}return c}function Fc(a,b){var c=0,d=0,e=0;A(+a);d=v(1)|0;e=v(0)|0;c=d>>>20&2047;if((c|0)!=2047){if(!c){if(a==0){c=0}else{a=Fc(a*0x10000000000000000,b);c=K[b>>2]+-64|0}K[b>>2]=c;return a}K[b>>2]=c-1022;x(0,e|0);x(1,d&-2146435073|1071644672);a=+z()}return a}function he(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=R(0),f=0,g=0;d=ra-16|0;ra=d;if(c){while(1){ad(a,d+12|0);e=O[d+12>>2];if(R(S(e))>2]=f;b=b+4|0;a=a+4|0;g=g+1|0;if((g|0)!=(c|0)){continue}break}}ra=d+16|0}function Ya(a){var b=0,c=0,d=0;if(a){b=K[a+24>>2];if(b){c=K[a+16>>2];if(c){b=0;while(1){d=K[(K[a+24>>2]+Q(b,52)|0)+44>>2];if(d){Ga(d);c=K[a+16>>2]}b=b+1|0;if(c>>>0>b>>>0){continue}break}b=K[a+24>>2]}Ga(b)}b=K[a+28>>2];if(b){Ga(b)}Ga(a)}}function ge(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0;d=ra-16|0;ra=d;if(c){while(1){Zb(a,d+8|0);e=P[d+8>>3];if(S(e)<2147483647){f=~~e}else{f=-2147483648}K[b>>2]=f;b=b+4|0;a=a+8|0;g=g+1|0;if((g|0)!=(c|0)){continue}break}}ra=d+16|0}function Fd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;d=K[c+4>>2];e=K[c>>2]+K[c+8>>2]|0;if((d|0)==(e|0)){ua=-1;return-1}K[c+4>>2]=a+d;f=a;c=e-d|0;d=c;e=a>>>0>>0;a=c>>31;c=e&(a|0)>=(b|0)|(a|0)>(b|0);d=c?f:d;ua=c?b:a;return d|0}function Me(a,b,c,d){var e=0,f=0,g=0,h=0;f=b^d;g=f>>31;e=b>>31;a=a^e;h=a-e|0;e=(b^e)-((a>>>0>>0)+e|0)|0;a=d>>31;b=c^a;f=f>>31;a=Ne(h,e,b-a|0,(a^d)-((a>>>0>b>>>0)+a|0)|0)^f;b=a-f|0;ua=(g^ua)-((a>>>0>>0)+g|0)|0;return b}function _a(a){var b=0,c=0,d=0,e=0;if(a){b=K[a+20>>2];c=K[a+16>>2];if(Q(b,c)){while(1){e=K[K[a+24>>2]+(d<<2)>>2];if(e){Ga(e);c=K[a+16>>2];b=K[a+20>>2]}d=d+1|0;if(d>>>0>>0){continue}break}}Ga(K[a+24>>2]);Ga(a)}}function sc(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0;if(d){while(1){e=f<<2;g=e+a|0;h=c+e|0;i=K[h>>2];j=b+e|0;k=K[j>>2];e=K[g>>2]-(i+k>>2)|0;K[g>>2]=e+i;K[j>>2]=e;K[h>>2]=e+k;f=f+1|0;if((f|0)!=(d|0)){continue}break}}}function ib(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;K[a+48>>2]=0;K[a+36>>2]=K[a+32>>2];e=va[K[a+28>>2]](b,c,K[a>>2])|0;d=K[a+68>>2];if(!e){K[a+68>>2]=d|4;return 0}K[a+56>>2]=b;K[a+60>>2]=c;K[a+68>>2]=d&-5;return 1}function Ra(a,b,c,d,e){var f=0;f=ra-256|0;ra=f;if(!(e&73728|(c|0)<=(d|0))){d=c-d|0;c=d>>>0<256;Sc(f,b,c?d:256);if(!c){while(1){Pa(a,f,256);d=d-256|0;if(d>>>0>255){continue}break}}Pa(a,f,d)}ra=f+256|0}function Le(a,b,c){var d=0,e=0,f=0,g=0,h=0;e=c>>>16|0;d=a>>>16|0;h=Q(e,d);f=c&65535;a=a&65535;g=Q(f,a);d=(g>>>16|0)+Q(d,f)|0;a=(d&65535)+Q(a,e)|0;ua=h+Q(b,c)+(d>>>16)+(a>>>16)|0;return g&65535|a<<16}function Nb(a){var b=0;b=K[a+72>>2];K[a+72>>2]=b-1|b;b=K[a>>2];if(b&8){K[a>>2]=b|32;return-1}K[a+4>>2]=0;K[a+8>>2]=0;b=K[a+44>>2];K[a+28>>2]=b;K[a+20>>2]=b;K[a+16>>2]=b+K[a+48>>2];return 0}function xc(a){var b=0,c=0;a:{if(L[a+12|0]==255){K[a+12>>2]=65280;K[a+16>>2]=7;b=K[a+8>>2];c=0;if(b>>>0>=N[a+4>>2]){break a}K[a+8>>2]=b+1;K[a+12>>2]=L[b|0]|65280}K[a+16>>2]=0;c=1}return c}function Hd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;e=K[c+4>>2];d=K[c>>2]+K[c+8>>2]|0;if((e|0)==(d|0)){return-1}d=d-e|0;b=b>>>0>d>>>0?d:b;if(b){E(a,e,b)}K[c+4>>2]=b+K[c+4>>2];return b|0}function le(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=ra-16|0;ra=d;if(c){while(1){ad(a,d+12|0);O[b>>2]=O[d+12>>2];b=b+4|0;a=a+4|0;e=e+1|0;if((e|0)!=(c|0)){continue}break}}ra=d+16|0}function ke(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=ra-16|0;ra=d;if(c){while(1){Zb(a,d+8|0);O[b>>2]=P[d+8>>3];b=b+4|0;a=a+8|0;e=e+1|0;if((e|0)!=(c|0)){continue}break}}ra=d+16|0}function nd(a,b){a=a|0;b=b|0;b=K[a+28>>2];if(b>>>0>2]){while(1){pc(a,K[a+24>>2]+(Q(K[a+20>>2],b)<<2)|0);b=b+1|0;if(b>>>0>2]){continue}break}}Ga(K[a>>2]);Ga(a)}function rd(a,b){a=a|0;b=+b;var c=0;ma(a|0,0)|0;a=(a|0)==2?27:(a|0)==1?26:14;a:{if(K[7158]>>>a-1&1){K[7190]=K[7190]|1<>2];if(c){va[c|0](a)}}}function Xc(a,b){a=a|0;b=b|0;var c=0,d=0;c=K[a>>2];d=K[b>>2];a=K[a+4>>2];b=K[b+4>>2];return(c>>>0>d>>>0&(a|0)>=(b|0)|(a|0)>(b|0))-(c>>>0>>0&(a|0)<=(b|0)|(a|0)<(b|0))|0}function zd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0;e=ra-16|0;ra=e;a=Kb(ia(K[a+60>>2],b|0,c|0,d&255,e+8|0)|0);ra=e+16|0;ua=a?-1:K[e+12>>2];return(a?-1:K[e+8>>2])|0}function Cc(a,b,c,d){var e=0,f=0;e=ra-16|0;ra=e;if(c){while(1){Ha(a,e+12|0,d);O[b>>2]=N[e+12>>2];b=b+4|0;a=a+d|0;f=f+1|0;if((f|0)!=(c|0)){continue}break}}ra=e+16|0}function Bc(a,b,c,d){var e=0,f=0;e=ra-16|0;ra=e;if(c){while(1){Ha(a,e+12|0,d);K[b>>2]=K[e+12>>2];b=b+4|0;a=a+d|0;f=f+1|0;if((f|0)!=(c|0)){continue}break}}ra=e+16|0}function Zb(a,b){I[b+7|0]=L[a|0];I[b+6|0]=L[a+1|0];I[b+5|0]=L[a+2|0];I[b+4|0]=L[a+3|0];I[b+3|0]=L[a+4|0];I[b+2|0]=L[a+5|0];I[b+1|0]=L[a+6|0];I[b|0]=L[a+7|0]}function Xd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;if(c){Fa(d,2,10187,0);if(!Rb(K[a>>2],b,c,d,e)){Fa(d,1,6173,0);return 0}a=Uc(a,c,d)}else{a=0}return a|0}function Va(a){var b=0,c=0,d=0,e=0;b=K[a+12>>2];e=b;c=K[a+8>>2];if(!(b|c)){ua=0;return 0}d=K[a+56>>2];b=c-d|0;ua=e-(K[a+60>>2]+(c>>>0>>0)|0)|0;return b}function $d(a,b,c,d,e,f,g,h,i,j,k){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;k=k|0;return ab(K[a>>2],b,c,d,e,f,g,h,i,j,k)|0}function Ac(a,b){var c=0;c=ra-16|0;ra=c;if(a){if(b&3){a=28}else{a=mb(b,a);K[c+12>>2]=a;a=a?0:48}a=a?0:K[c+12>>2]}else{a=0}ra=c+16|0;return a}function id(a){a=a|0;var b=0;if(a){b=K[a+116>>2];if(b){Ga(b);K[a+116>>2]=0}b=K[a+120>>2];if(b){Ga(b);K[a+120>>2]=0}Ga(K[a+148>>2]);Ga(a)}} +function wb(a,b){var c=0,d=0;a:{if(b>>>0<=31){d=K[a>>2];c=a+4|0;break a}b=b-32|0;c=a}c=K[c>>2];K[a>>2]=d<>2]=c<>>32-b}function yb(a,b){var c=0,d=0;c=K[a+4>>2];a:{if(b>>>0<=31){d=K[a>>2];break a}b=b-32|0;d=c;c=0}K[a+4>>2]=c>>>b;K[a>>2]=c<<32-b|d>>>b}function fe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(!c){return 0}if(!Tb(K[a>>2],b,c,d)){Fa(d,1,6173,0);return 0}return Uc(a,c,d)|0}function te(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(K[K[a+96>>2]+16>>2]<<2!=(c|0)){Fa(d,1,4427,0);a=0}else{a=1}return a|0}function zc(){var a=0,b=0;a=Ia(1,44);a:{if(a){K[a+16>>2]=0;b=Ia(1,8);K[a+36>>2]=b;if(b){break a}Ga(a)}a=0}return a}function dc(a,b){a=a|0;b=b|0;if(!(!a|!b)){K[a+188>>2]=K[b+4>>2];K[a+184>>2]=K[b>>2];K[a+248>>2]=K[b+8248>>2]&2}}function ub(){var a=0,b=0;a=Ia(1,12);if(a){K[a+4>>2]=10;b=Ia(10,4);K[a+8>>2]=b;if(b){return a}Ga(a)}return 0}function Yd(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;return _b(K[a>>2],b,c,d,e,f,g)|0}function zb(a){var b=0;if(a){b=K[a+4>>2];if(b){va[b|0](K[a>>2])}Ga(K[a+32>>2]);K[a+32>>2]=0;Ga(a)}}function cc(a,b){a=a|0;b=b|0;a:{if(!a){break a}K[a+208>>2]=b;if(!b){break a}I[a+92|0]=L[a+92|0]|8}}function Ed(a,b,c){a=a|0;b=b|0;c=c|0;b=K[c+8>>2];K[c+4>>2]=K[c>>2]+(a>>>0>b>>>0?b:a);return 1}function _d(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return jb(K[a>>2],b,c,d,e,f)|0}function xe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(c){a=1}else{Fa(d,1,4338,0);a=0}return a|0}function ob(a){K[a>>2]=0;K[a+4>>2]=0;K[a+16>>2]=0;K[a+20>>2]=0;K[a+8>>2]=0;K[a+12>>2]=0}function ed(a,b,c){a=a|0;b=b|0;c=c|0;return!K[a+8>>2]&(K[a+216>>2]!=0&K[a+220>>2]!=0)}function Xa(a){if(K[a+12>>2]){K[a+40>>2]=0;while(1){if(K[a+24>>2]>0){continue}break}}}function ad(a,b){I[b+3|0]=L[a|0];I[b+2|0]=L[a+1|0];I[b+1|0]=L[a+2|0];I[b|0]=L[a+3|0]}function Cb(a){if(a){va[K[(K[a+76>>2]?20:16)+a>>2]](K[a+48>>2]);K[a+48>>2]=0;Ga(a)}}function ee(a,b){a=a|0;b=b|0;dc(K[a>>2],b);I[a+124|0]=0;K[a+128>>2]=K[b+8248>>2]&1}function Ia(a,b){if(!a|!b){a=0}else{b=Q(a,b);a=mb(8,b);if(a){Sc(a,0,b)}}return a}function Ka(a,b,c){var d=0;d=ra-16|0;ra=d;K[d+12>>2]=c;Kc(a,b,c,0,0);ra=d+16|0}function Pe(a){var b=0;while(1){if(a){a=a-1&a;b=b+1|0;continue}break}return b}function eb(a){var b=0;if(a){b=K[a+12>>2];if(b){Ga(b);K[a+12>>2]=0}Ga(a)}}function Zd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return $b(K[a>>2],b,c,d)|0}function Sa(a,b,c){a:{if(K[c+76>>2]<0){a=Mb(a,b,c);break a}a=Mb(a,b,c)}}function Nc(a,b){a=Lc(a-1|0);if(!a){a=Lc(b);a=a?a|32:0}return a}function ec(a){return K[a+12>>2]==K[a+4>>2]|K[a+8>>2]==K[a>>2]}function Sd(a,b,c){a=a|0;b=b|0;c=c|0;return $c(K[a>>2],b,c)|0}function tb(a){var b=0;if(a){b=K[a+8>>2];if(b){Ga(b)}Ga(a)}}function Lc(a){var b=0,c=0,d=0;return b=Ke(a),c=0,d=a,d?b:c}function vd(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;ua=0;return 0}function db(a,b,c,d,e,f,g,h){return qc(a,b,c,d,e,f,g,h,0)}function bb(a,b,c){K[((b<<2)+a|0)+28>>2]=(c<<5)+20784}function Pb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return 1}function Dc(a,b,c,d){return va[K[a+44>>2]](a,b,c,d)|0}function Wd(a,b,c){a=a|0;b=b|0;c=c|0;Xb(K[a>>2],b,c)}function vb(a,b,c){return va[K[a+40>>2]](a,b,0,c)|0}function re(a,b,c){a=a|0;b=b|0;c=c|0;ua=-1;return-1}function Ke(a){if(a){return 31-T(a-1^a)|0}return 32}function xd(a){a=a|0;return Kb(aa(K[a+60>>2])|0)|0}function Ua(a,b,c,d,e,f,g,h){qc(a,b,c,d,e,f,g,h,1)}function Kb(a){if(!a){return 0}K[6585]=a;return-1}function ne(a,b,c){a=a|0;b=b|0;c=c|0;Cc(a,b,c,2)}function me(a,b,c){a=a|0;b=b|0;c=c|0;Cc(a,b,c,4)}function je(a,b,c){a=a|0;b=b|0;c=c|0;Bc(a,b,c,2)}function ie(a,b,c){a=a|0;b=b|0;c=c|0;Bc(a,b,c,4)}function Pa(a,b,c){if(!(L[a|0]&32)){Mb(b,c,a)}}function Oe(a,b,c){Je(a,0,b,c);ua=ta;return sa}function bc(a,b,c){a=a|0;b=b|0;c=c|0;return 1}function Yc(a,b,c){a=a|0;b=b|0;c=c|0;return-1}function Be(a,b,c){a=a|0;b=b|0;c=c|0;return 0}function Ne(a,b,c,d){a=Je(a,b,c,d);return a}function Ja(a){if(!a){return 0}return Ab(a)}function de(a,b){a=a|0;b=b|0;cc(K[a>>2],b)}function Sc(a,b,c){if(c){B(a,b<<24>>24,c)}}function yc(a){return K[a+8>>2]-K[a>>2]|0}function pd(a){a=a|0;ka();ja(a+128|0);G()}function Vd(a){a=a|0;return Ub(K[a>>2])|0}function Td(a){a=a|0;return Vb(K[a>>2])|0}function fd(a,b){a=a|0;b=b|0;return 0}function Ab(a){a=a|0;return mb(8,a)|0}function Bd(a,b){a=a|0;b=b|0;ca(a|0)}function Ib(a){return K[a+28>>2]!=2}function Ad(a,b){a=a|0;b=b|0;$(a|0)}function hb(a,b,c){if(c){E(a,b,c)}}function gb(a,b){return Xc(a,b)}function sb(a){return Ac(a,32)}function Ma(a){return Ac(a,16)}function wd(a){a=a|0;return 0}function qd(a){a=a|0;Ec();G()}function Bb(){return Ia(1,36)}function gc(a,b){a=a|0;b=b|0}function kb(a){if(a){Ga(a)}}function Ta(a){K[a>>2]=0}function od(){Ec();G()}function Ec(){la();G()} +// EMSCRIPTEN_END_FUNCS +e=L;p(q);var va=c([null,gc,Be,re,Yc,Yc,ib,Ud,Jd,Dd,nd,md,ld,kd,jd,id,hd,gd,bc,ed,dd,cd,bd,Xc,Ie,He,Ge,Fe,Ee,De,Ce,Ae,ze,ye,xe,we,ve,ue,te,Pb,se,qe,Pb,Pb,pe,oe,ne,me,le,ke,je,ie,he,ge,be,Rd,Qd,Pd,Od,Nd,Md,Ld,Kd,Id,Hd,Gd,Fd,Ed,Ub,Vb,Xb,bc,Tb,cc,dc,Eb,ac,fd,$b,$c,Rb,_b,jb,ab,Vd,Td,Wd,ce,fe,fd,Zd,Sd,Xd,Yd,de,ee,Tc,_d,$d,ae,gc,Bd,Ad,sd,ud,td,od,xd,yd,zd,wd,vd,pd,qd]);function wa(){return H.byteLength/65536|0}function Ba(Ca){Ca=Ca|0;var xa=wa()|0;var ya=xa+Ca|0;if(xa{Module["instantiateWasm"](info,(mod,inst)=>{receiveInstance(mod,inst);resolve(mod.exports)})})}wasmBinaryFile??=findWasmBinary();var result=instantiateSync(wasmBinaryFile,info);return receiveInstance(result[0])}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.unshift(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.unshift(cb);var noExitRuntime=Module["noExitRuntime"]||true;var __abort_js=()=>abort("");var runtimeKeepaliveCounter=0;var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var _emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};function _copy_pixels_1(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);imageData.set(compG)}function _copy_pixels_3(compR_ptr,compG_ptr,compB_ptr,nb_pixels){compR_ptr>>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*3);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var _fd_close=fd=>52;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};function _gray_to_rgba(compG_ptr,nb_pixels){compG_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);for(let i=0;i>=2;compA_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compA=HEAP32.subarray(compA_ptr,compA_ptr+nb_pixels);for(let i=0;i>=2;compG_ptr>>=2;compB_ptr>>=2;const imageData=Module.imageData=new Uint8ClampedArray(nb_pixels*4);const compR=HEAP32.subarray(compR_ptr,compR_ptr+nb_pixels);const compG=HEAP32.subarray(compG_ptr,compG_ptr+nb_pixels);const compB=HEAP32.subarray(compB_ptr,compB_ptr+nb_pixels);for(let i=0;i0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=Module; + + + return moduleRtn; +} +); +})(); +export default OpenJPEG; diff --git a/gulpfile.mjs b/gulpfile.mjs index 8c97bf4fe5df3..008b5b99d61f0 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* eslint-env node */ import { babelPluginPDFJSPreprocessor, @@ -81,10 +80,10 @@ const config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString()); const ENV_TARGETS = [ "last 2 versions", - "Chrome >= 103", + "Chrome >= 110", "Firefox ESR", "Safari >= 16.4", - "Node >= 18", + "Node >= 20", "> 1%", "not IE > 0", "not dead", @@ -98,7 +97,7 @@ const AUTOPREFIXER_CONFIG = { const BABEL_TARGETS = ENV_TARGETS.join(", "); const BABEL_PRESET_ENV_OPTS = Object.freeze({ - corejs: "3.38.1", + corejs: "3.40.0", exclude: ["web.structured-clone"], shippedProposals: true, useBuiltIns: "usage", @@ -191,6 +190,9 @@ function createWebpackAlias(defines) { "fluent-dom": "node_modules/@fluent/dom/esm/index.js", }; const libraryAlias = { + "display-cmap_reader_factory": "src/display/stubs.js", + "display-standard_fontdata_factory": "src/display/stubs.js", + "display-wasm_factory": "src/display/stubs.js", "display-fetch_stream": "src/display/stubs.js", "display-network": "src/display/stubs.js", "display-node_stream": "src/display/stubs.js", @@ -215,10 +217,16 @@ function createWebpackAlias(defines) { "web-preferences": "", "web-print_service": "", "web-secondary_toolbar": "web/secondary_toolbar.js", + "web-signature_manager": "web/signature_manager.js", "web-toolbar": "web/toolbar.js", }; if (defines.CHROME) { + libraryAlias["display-cmap_reader_factory"] = + "src/display/cmap_reader_factory.js"; + libraryAlias["display-standard_fontdata_factory"] = + "src/display/standard_fontdata_factory.js"; + libraryAlias["display-wasm_factory"] = "src/display/wasm_factory.js"; libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js"; libraryAlias["display-network"] = "src/display/network.js"; @@ -231,6 +239,11 @@ function createWebpackAlias(defines) { // Aliases defined here must also be replicated in the paths section of // the tsconfig.json file for the type generation to work. // In the tsconfig.json files, the .js extension must be omitted. + libraryAlias["display-cmap_reader_factory"] = + "src/display/cmap_reader_factory.js"; + libraryAlias["display-standard_fontdata_factory"] = + "src/display/standard_fontdata_factory.js"; + libraryAlias["display-wasm_factory"] = "src/display/wasm_factory.js"; libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js"; libraryAlias["display-network"] = "src/display/network.js"; libraryAlias["display-node_stream"] = "src/display/node_stream.js"; @@ -371,8 +384,14 @@ function createWebpackConfig( }, devtool: enableSourceMaps ? "source-map" : undefined, module: { + parser: { + javascript: { + importMeta: false, + }, + }, rules: [ { + test: /\.[mc]?js$/, loader: "babel-loader", exclude: babelExcludeRegExp, options: { @@ -621,8 +640,7 @@ function createStandardFontBundle() { [ "external/standard_fonts/*.pfb", "external/standard_fonts/*.ttf", - "external/standard_fonts/LICENSE_FOXIT", - "external/standard_fonts/LICENSE_LIBERATION", + "external/standard_fonts/LICENSE_*", ], { base: "external/standard_fonts", @@ -631,6 +649,22 @@ function createStandardFontBundle() { ); } +function createWasmBundle() { + return ordered([ + gulp.src( + [ + "external/openjpeg/*.wasm", + "external/openjpeg/openjpeg_nowasm_fallback.js", + "external/openjpeg/LICENSE_*", + ], + { + base: "external/openjpeg", + encoding: false, + } + ), + ]); +} + function checkFile(filePath) { try { const stat = fs.lstatSync(filePath); @@ -676,12 +710,9 @@ function runTests(testsName, { bot = false, xfaOnly = false } = {}) { if (!bot) { args.push("--reftest"); } else { - const os = process.env.OS; - if (/windows/i.test(os)) { - // The browser-tests are too slow in Google Chrome on the Windows - // bot, causing a timeout, hence disabling them for now. - forceNoChrome = true; - } + // The browser-tests are too slow in Google Chrome on the bots, + // causing a timeout, hence disabling them for now. + forceNoChrome = true; } if (xfaOnly) { args.push("--xfaOnly"); @@ -760,12 +791,10 @@ function makeRef(done, bot) { let forceNoChrome = false; const args = ["test.mjs", "--masterMode"]; if (bot) { - const os = process.env.OS; - if (/windows/i.test(os)) { - // The browser-tests are too slow in Google Chrome on the Windows - // bot, causing a timeout, hence disabling them for now. - forceNoChrome = true; - } + // The browser-tests are too slow in Google Chrome on the bots, + // causing a timeout, hence disabling them for now. + forceNoChrome = true; + args.push("--noPrompts", "--strictVerify"); } if (process.argv.includes("--noChrome") || forceNoChrome) { @@ -1058,6 +1087,7 @@ function buildGeneric(defines, dir) { .pipe(gulp.dest(dir + "web")), createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")), createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")), + createWasmBundle().pipe(gulp.dest(dir + "web/wasm")), preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")), preprocessCSS("web/viewer.css", defines) @@ -1143,6 +1173,7 @@ function buildComponents(defines, dir) { "web/images/messageBar_*.svg", "web/images/toolbarButton-{editorHighlight,menuArrow}.svg", "web/images/cursor-*.svg", + "web/images/secondaryToolbarButton-documentProperties.svg", ]; return ordered([ @@ -1387,6 +1418,7 @@ gulp.task( createStandardFontBundle().pipe( gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts") ), + createWasmBundle().pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/wasm")), preprocessHTML("web/viewer.html", defines).pipe( gulp.dest(MOZCENTRAL_CONTENT_DIR + "web") @@ -1399,6 +1431,7 @@ gulp.task( .pipe( postcss([ discardCommentsCSS(), + postcssDarkThemeClass(), autoprefixer(MOZCENTRAL_AUTOPREFIXER_CONFIG), ]) ) @@ -1409,6 +1442,7 @@ gulp.task( .pipe( postcss([ discardCommentsCSS(), + postcssDarkThemeClass(), autoprefixer(MOZCENTRAL_AUTOPREFIXER_CONFIG), ]) ) @@ -1489,6 +1523,9 @@ gulp.task( createStandardFontBundle().pipe( gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts") ), + createWasmBundle().pipe( + gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/wasm") + ), preprocessHTML("web/viewer.html", defines).pipe( gulp.dest(CHROME_BUILD_CONTENT_DIR + "web") @@ -1573,6 +1610,9 @@ function buildLibHelper(bundleDefines, inputStream, outputDir) { defines: bundleDefines, map: { "pdfjs-lib": "../pdf.js", + "display-cmap_reader_factory": "./cmap_reader_factory.js", + "display-standard_fontdata_factory": "./standard_fontdata_factory.js", + "display-wasm_factory": "./wasm_factory.js", "display-fetch_stream": "./fetch_stream.js", "display-network": "./network.js", "display-node_stream": "./node_stream.js", @@ -1921,20 +1961,25 @@ function createBaseline(done) { gulp.task( "unittestcli", - gulp.series(setTestEnv, "lib-legacy", function runUnitTestCli(done) { - const options = [ - "node_modules/jasmine/bin/jasmine", - "JASMINE_CONFIG_PATH=test/unit/clitests.json", - ]; - const jasmineProcess = startNode(options, { stdio: "inherit" }); - jasmineProcess.on("close", function (code) { - if (code !== 0) { - done(new Error("Unit tests failed.")); - return; - } - done(); - }); - }) + gulp.series( + setTestEnv, + "generic-legacy", + "lib-legacy", + function runUnitTestCli(done) { + const options = [ + "node_modules/jasmine/bin/jasmine", + "JASMINE_CONFIG_PATH=test/unit/clitests.json", + ]; + const jasmineProcess = startNode(options, { stdio: "inherit" }); + jasmineProcess.on("close", function (code) { + if (code !== 0) { + done(new Error("Unit tests failed.")); + return; + } + done(); + }); + } + ) ); gulp.task("lint", function (done) { @@ -1944,8 +1989,6 @@ gulp.task("lint", function (done) { // Ensure that we lint the Firefox specific *.jsm files too. const esLintOptions = [ "node_modules/eslint/bin/eslint", - "--ext", - ".js,.jsm,.mjs,.json", ".", "--report-unused-disable-directives", ]; @@ -1974,8 +2017,9 @@ gulp.task("lint", function (done) { const svgLintOptions = [ "node_modules/svglint/bin/cli.js", - "web/**/*.svg", + "**/*.svg", "--ci", + "--no-summary", ]; const esLintProcess = startNode(esLintOptions, { stdio: "inherit" }); @@ -2000,12 +2044,7 @@ gulp.task("lint", function (done) { } const svgLintProcess = startNode(svgLintOptions, { - stdio: "pipe", - }); - svgLintProcess.stdout.setEncoding("utf8"); - svgLintProcess.stdout.on("data", m => { - m = m.toString().replace(/-+ Summary -+.*/ms, ""); - console.log(m); + stdio: "inherit", }); svgLintProcess.on("close", function (svgLintCode) { if (svgLintCode !== 0) { @@ -2054,6 +2093,15 @@ gulp.task( ) ); +gulp.task("dev-wasm", function () { + const VIEWER_WASM_OUTPUT = "web/wasm/"; + + fs.rmSync(VIEWER_WASM_OUTPUT, { recursive: true, force: true }); + fs.mkdirSync(VIEWER_WASM_OUTPUT, { recursive: true }); + + return createWasmBundle().pipe(gulp.dest(VIEWER_WASM_OUTPUT)); +}); + gulp.task( "dev-sandbox", gulp.series( @@ -2089,6 +2137,13 @@ gulp.task( gulp.series("locale") ); }, + function watchWasm() { + gulp.watch( + "external/openjpeg/*", + { ignoreInitial: false }, + gulp.series("dev-wasm") + ); + }, function watchDevSandbox() { gulp.watch( [ @@ -2244,8 +2299,7 @@ function packageJson() { bugs: DIST_BUGS_URL, license: DIST_LICENSE, optionalDependencies: { - canvas: "^3.0.0-rc2", - path2d: "^0.2.1", + "@napi-rs/canvas": "^0.1.67", }, browser: { canvas: false, @@ -2259,7 +2313,7 @@ function packageJson() { url: `git+${DIST_GIT_URL}`, }, engines: { - node: ">=18", + node: ">=20", }, scripts: {}, }; @@ -2310,6 +2364,12 @@ gulp.task( encoding: false, }) .pipe(gulp.dest(DIST_DIR)), + gulp + .src(GENERIC_DIR + "web/wasm/**/*", { + base: GENERIC_DIR + "web", + encoding: false, + }) + .pipe(gulp.dest(DIST_DIR)), gulp .src( [ diff --git a/l10n/ach/viewer.ftl b/l10n/ach/viewer.ftl index 36769b7084c2c..1b33bdfccf0ce 100644 --- a/l10n/ach/viewer.ftl +++ b/l10n/ach/viewer.ftl @@ -217,9 +217,56 @@ pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/af/viewer.ftl b/l10n/af/viewer.ftl index 7c4346fe018b0..d1f1a2bdf7dbe 100644 --- a/l10n/af/viewer.ftl +++ b/l10n/af/viewer.ftl @@ -204,9 +204,56 @@ pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingeb ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/an/viewer.ftl b/l10n/an/viewer.ftl index 673314778ad8b..5b8592d30c2c0 100644 --- a/l10n/an/viewer.ftl +++ b/l10n/an/viewer.ftl @@ -249,9 +249,56 @@ pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ar/viewer.ftl b/l10n/ar/viewer.ftl index 8d14767063a2d..478824cfd20f6 100644 --- a/l10n/ar/viewer.ftl +++ b/l10n/ar/viewer.ftl @@ -105,6 +105,14 @@ pdfjs-document-properties-button-label = خصائص المستند… pdfjs-document-properties-file-name = اسم الملف: pdfjs-document-properties-file-size = حجم الملف: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } ك.بايت ({ $b } بايتات) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } م.بايت ({ $b } بايتات) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت) @@ -119,6 +127,9 @@ pdfjs-document-properties-keywords = الكلمات الأساسية: pdfjs-document-properties-creation-date = تاريخ الإنشاء: pdfjs-document-properties-modification-date = تاريخ التعديل: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }، { $time } @@ -216,7 +227,7 @@ pdfjs-find-next-button = pdfjs-find-next-button-label = التالي pdfjs-find-highlight-checkbox = أبرِز الكل pdfjs-find-match-case-checkbox-label = طابق حالة الأحرف -pdfjs-find-match-diacritics-checkbox-label = طابِق الحركات +pdfjs-find-match-diacritics-checkbox-label = طابِق التشكيل pdfjs-find-entire-word-checkbox-label = كلمات كاملة pdfjs-find-reached-top = تابعت من الأسفل بعدما وصلت إلى بداية المستند pdfjs-find-reached-bottom = تابعت من الأعلى بعدما وصلت إلى نهاية المستند @@ -283,6 +294,9 @@ pdfjs-annotation-date-string = { $date }، { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [تعليق { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -321,6 +335,8 @@ pdfjs-editor-remove-stamp-button = .title = أزِل الصورة pdfjs-editor-remove-highlight-button = .title = أزِل الإبراز +pdfjs-editor-remove-signature-button = + .title = أزِل التوقيع ## @@ -337,6 +353,10 @@ pdfjs-editor-stamp-add-image-button-label = أضِف صورة pdfjs-editor-free-highlight-thickness-input = السماكة pdfjs-editor-free-highlight-thickness-title = .title = غيّر السُمك عند إبراز عناصر أُخرى غير النص +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = محرِّر النص + .default-content = ابدأ في كتابة… pdfjs-free-text = .aria-label = محرِّر النص pdfjs-free-text-default-content = ابدأ الكتابة… @@ -347,8 +367,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = نص بديل +pdfjs-editor-alt-text-edit-button = + .aria-label = حرّر النص البديل pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل pdfjs-editor-alt-text-dialog-label = اختر خيار pdfjs-editor-alt-text-dialog-description = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها. @@ -362,6 +383,9 @@ pdfjs-editor-alt-text-decorative-tooltip = عُلّمت على أنها زخرف # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = على سبيل المثال، "يجلس شاب على الطاولة لتناول وجبة" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = نص بديل ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -420,6 +444,150 @@ pdfjs-editor-highlight-show-all-button = ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = حرّر النص البديل (وصف الصورة) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = أضِف النص البديل (وصف الصورة) +pdfjs-editor-new-alt-text-textarea = + .placeholder = اكتب وصفك هنا… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = وصف مختصر للأشخاص الذين لا يستطيعون رؤية الصورة أو عندما لا يتم تحميل الصورة. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = أُنشئ هذا النص البديل تلقائيًا وقد يكون غير دقيق. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = اطّلع على المزيد +pdfjs-editor-new-alt-text-create-automatically-button-label = أنشئ نص بديل تلقائيًا +pdfjs-editor-new-alt-text-not-now-button = ليس الآن +pdfjs-editor-new-alt-text-error-title = لم يتمكن من إنشاء نص بديل تلقائيًا +pdfjs-editor-new-alt-text-error-description = يُرجى كتابة نص بديلك أو المحاولة مرة أخرى لاحقًا. +pdfjs-editor-new-alt-text-error-close-button = أغلق +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = يُنزّل نموذج الذكاء الاصطناعي للنص البديل ({ $downloadedSize } من { $totalSize } م.بايت) + .aria-valuetext = يُنزّل نموذج الذكاء الاصطناعي للنص البديل ({ $downloadedSize } من { $totalSize } م.بايت) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = أُضِيف نص بديل +pdfjs-editor-new-alt-text-added-button-label = أُضِيف نص بديل +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = نص بديل مفقود +pdfjs-editor-new-alt-text-missing-button-label = نص بديل مفقود +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = راجع النص البديل +pdfjs-editor-new-alt-text-to-review-button-label = راجع النص البديل +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = أُنشئ تلقائيًا: { $generatedAltText } ## Image alt-text settings +pdfjs-image-alt-text-settings-button = + .title = إعدادات النص البديل للصورة +pdfjs-image-alt-text-settings-button-label = إعدادات النص البديل للصورة +pdfjs-editor-alt-text-settings-dialog-label = إعدادات النص البديل للصورة +pdfjs-editor-alt-text-settings-automatic-title = نص بديل تلقائي +pdfjs-editor-alt-text-settings-create-model-button-label = أنشئ نص بديل تلقائيًا +pdfjs-editor-alt-text-settings-create-model-description = يقترح أوصافًا لمساعدة الأشخاص الذين لا يستطيعون رؤية الصورة أو عندما لا يتم تحميل الصورة. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = نموذج الذكاء الاصطناعي للنص البديل ({ $totalSize } م.بايت) +pdfjs-editor-alt-text-settings-ai-model-description = يتم تشغيله محليًا على جهازك حتى تظل بياناتك خاصة. مطلوب للنص البديل التلقائي. +pdfjs-editor-alt-text-settings-delete-model-button = احذف +pdfjs-editor-alt-text-settings-download-model-button = نزّل +pdfjs-editor-alt-text-settings-downloading-model-button = يُنزل… +pdfjs-editor-alt-text-settings-editor-title = مُحرِّر النص البديل +pdfjs-editor-alt-text-settings-show-dialog-button-label = أظهِر مُحرِّر النص البديل على الفور عند إضافة صورة +pdfjs-editor-alt-text-settings-show-dialog-description = يساعدك على التأكد من أن جميع صورك تحتوي على نص بديل. +pdfjs-editor-alt-text-settings-close-button = أغلق + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = أُزِيل البرز +pdfjs-editor-undo-bar-message-freetext = أُزيل النص +pdfjs-editor-undo-bar-message-ink = أُزِيلت الرسمة +pdfjs-editor-undo-bar-message-stamp = أُزيلت الصورة +pdfjs-editor-undo-bar-message-signature = أُزيل التوقيع +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [zero] أُزيل لا تعليق + [one] أُزيل تعليق + [two] أُزيل تعليقين + [few] أُزيلت { $count } تعليقات + [many] أُزيل { $count } تعليق + *[other] أُزيل { $count } تعليق + } +pdfjs-editor-undo-bar-undo-button = + .title = تراجع +pdfjs-editor-undo-bar-undo-button-label = تراجع +pdfjs-editor-undo-bar-close-button = + .title = أغلق +pdfjs-editor-undo-bar-close-button-label = أغلق + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = يتيح هذا النموذج للمستخدم إنشاء توقيع لإضافته إلى مستند PDF. ويمكن للمستخدم تحرير الاسم (الذي يعمل أيضًا كنص بديل)، وحفظ التوقيع بشكل اختياري للاستخدام المتكرر. +pdfjs-editor-add-signature-dialog-title = أضِف توقيعا + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = اكتب + .title = اكتب +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = ارسم + .title = ارسم +pdfjs-editor-add-signature-image-button = صورة + .title = صورة + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = اكتب توقيعك + .placeholder = اكتب توقيعك +pdfjs-editor-add-signature-draw-placeholder = ارسم توقيعك +pdfjs-editor-add-signature-draw-thickness-range-label = السماكة +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = سمك الرسم: { $thickness } +pdfjs-editor-add-signature-image-placeholder = اسحب الملف هنا لرفعه +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] أو اختر ملفات الصور + *[other] أو تصفح ملفات الصور + } + +## Controls + +pdfjs-editor-add-signature-description-label = الوصف (نص بديل) +pdfjs-editor-add-signature-description-input = + .title = الوصف (نص بديل) +pdfjs-editor-add-signature-description-default-when-drawing = توقيع +pdfjs-editor-add-signature-clear-button-label = امحُ التوقيع +pdfjs-editor-add-signature-clear-button = + .title = امحُ التوقيع +pdfjs-editor-add-signature-save-checkbox = احفظ التوقيع +pdfjs-editor-add-signature-save-warning-message = لقد وصلت إلى الحد الأقصى وهو 5 توقيعات محفوظة. أزِل توقيع واحد لحفظ المزيد. +pdfjs-editor-add-signature-image-upload-error-title = تعذر رفع الصورة. +pdfjs-editor-add-signature-image-upload-error-description = تحقق من اتصال الشبكة لديك أو جرّب صورة أخرى. +pdfjs-editor-add-signature-error-close-button = أغلق + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = ألغِ +pdfjs-editor-add-signature-add-button = أضِف + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ast/viewer.ftl b/l10n/ast/viewer.ftl index 2503cafceeab4..f5c8e610f62d7 100644 --- a/l10n/ast/viewer.ftl +++ b/l10n/ast/viewer.ftl @@ -193,9 +193,56 @@ pdfjs-password-cancel-button = Encaboxar ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/az/viewer.ftl b/l10n/az/viewer.ftl index 773aae4d62743..e74ef6bacd4c9 100644 --- a/l10n/az/viewer.ftl +++ b/l10n/az/viewer.ftl @@ -249,9 +249,56 @@ pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/be/viewer.ftl b/l10n/be/viewer.ftl index 2fcbace18c26c..5f8e06d421422 100644 --- a/l10n/be/viewer.ftl +++ b/l10n/be/viewer.ftl @@ -318,6 +318,9 @@ pdfjs-highlight-floating-button1 = .title = Падфарбаваць .aria-label = Падфарбаваць pdfjs-highlight-floating-button-label = Падфарбаваць +pdfjs-editor-signature-button = + .title = Дадаць подпіс +pdfjs-editor-signature-button-label = Дадаць подпіс ## Remove button for the various kind of editor. @@ -329,6 +332,8 @@ pdfjs-editor-remove-stamp-button = .title = Выдаліць выяву pdfjs-editor-remove-highlight-button = .title = Выдаліць падфарбоўку +pdfjs-editor-remove-signature-button = + .title = Выдаліць подпіс ## @@ -345,6 +350,13 @@ pdfjs-editor-stamp-add-image-button-label = Дадаць выяву pdfjs-editor-free-highlight-thickness-input = Таўшчыня pdfjs-editor-free-highlight-thickness-title = .title = Змяняць таўшчыню пры вылучэнні іншых элементаў, акрамя тэксту +pdfjs-editor-signature-add-signature-button = + .title = Дадаць новы подпіс +pdfjs-editor-signature-add-signature-button-label = Дадаць новы подпіс +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Тэкставы рэдактар + .default-content = Пачніце ўводзіць… pdfjs-free-text = .aria-label = Тэкставы рэдактар pdfjs-free-text-default-content = Пачніце набор тэксту… @@ -355,8 +367,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Альтэрнатыўны тэкст +pdfjs-editor-alt-text-edit-button = + .aria-label = Змяніць альтэрнатыўны тэкст pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст pdfjs-editor-alt-text-dialog-label = Выберыце варыянт pdfjs-editor-alt-text-dialog-description = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца. @@ -370,6 +383,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Пазначаны як дэкара # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Напрыклад, «Малады чалавек садзіцца за стол есці» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтэрнатыўны тэкст ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -451,10 +467,16 @@ pdfjs-editor-new-alt-text-error-close-button = Закрыць pdfjs-editor-new-alt-text-ai-model-downloading-progress = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ) .aria-valuetext = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Тэкст для атрыбута alt дададзены pdfjs-editor-new-alt-text-added-button-label = Тэкст для атрыбута alt дададзены # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Адсутнічае тэкст для атрыбута alt pdfjs-editor-new-alt-text-missing-button-label = Адсутнічае тэкст для атрыбута alt # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Водгук на тэкст для атрыбута alt pdfjs-editor-new-alt-text-to-review-button-label = Водгук на тэкст для атрыбута alt # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -481,3 +503,94 @@ pdfjs-editor-alt-text-settings-editor-title = Рэдактар тэксту дл pdfjs-editor-alt-text-settings-show-dialog-button-label = Адразу паказваць рэдактар тэксту для атрыбута alt пры даданні выявы pdfjs-editor-alt-text-settings-show-dialog-description = Дапамагае пераканацца, што ўсе вашы выявы маюць альтэрнатыўны тэкст. pdfjs-editor-alt-text-settings-close-button = Закрыць + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Падсвятленне выдалена +pdfjs-editor-undo-bar-message-freetext = Тэкст выдалены +pdfjs-editor-undo-bar-message-ink = Малюнак выдалены +pdfjs-editor-undo-bar-message-stamp = Відарыс выдалены +pdfjs-editor-undo-bar-message-signature = Подпіс выдалены +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анатацыя выдалена + [few] { $count } анатацыі выдалена + *[many] { $count } анатацый выдалена + } +pdfjs-editor-undo-bar-undo-button = + .title = Адмяніць +pdfjs-editor-undo-bar-undo-button-label = Адмяніць +pdfjs-editor-undo-bar-close-button = + .title = Закрыць +pdfjs-editor-undo-bar-close-button-label = Закрыць + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Гэты рэжым дазваляе карыстальніку ствараць подпіс для дадання ў дакумент PDF. Карыстальнік можа рэдагаваць імя (якое таксама служыць альтэрнатыўным тэкстам) і пры жаданні захаваць подпіс для паўторнага выкарыстання. +pdfjs-editor-add-signature-dialog-title = Дадаць подпіс + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Увод + .title = Увод +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Маляваць + .title = Маляваць +pdfjs-editor-add-signature-image-button = Выява + .title = Выява + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Увядзіце свой подпіс + .placeholder = Увядзіце свой подпіс +pdfjs-editor-add-signature-draw-placeholder = Намалюйце свой подпіс +pdfjs-editor-add-signature-draw-thickness-range-label = Таўшчыня +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Таўшчыня малюнка: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Перацягнуць файл сюды, каб загрузіць +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Або праглядайце файлы малюнкаў + *[other] Або праглядайце файлы малюнкаў + } + +## Controls + +pdfjs-editor-add-signature-description-label = Апісанне (альтэрнатыўны тэкст) +pdfjs-editor-add-signature-description-input = + .title = Апісанне (альтэрнатыўны тэкст) +pdfjs-editor-add-signature-description-default-when-drawing = Подпіс +pdfjs-editor-add-signature-clear-button-label = Выдаліць подпіс +pdfjs-editor-add-signature-clear-button = + .title = Выдаліць подпіс +pdfjs-editor-add-signature-save-checkbox = Захаваць подпіс +pdfjs-editor-add-signature-save-warning-message = Вы дасягнулі ліміту ў 5 захаваных подпісаў. Выдаліце адзін, каб захаваць іншы. +pdfjs-editor-add-signature-image-upload-error-title = Не ўдалося загрузіць выяву +pdfjs-editor-add-signature-image-upload-error-description = Праверце падключэнне да сеткі ці паспрабуйце іншую выяву. +pdfjs-editor-add-signature-error-close-button = Закрыць + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Скасаваць +pdfjs-editor-add-signature-add-button = Дадаць +pdfjs-editor-edit-signature-update-button = Абнавіць + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Выдаліць подпіс +pdfjs-editor-delete-signature-button-label = Выдаліць подпіс + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Рэдагаваць апісанне + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Рэдагаваць апісанне diff --git a/l10n/bg/viewer.ftl b/l10n/bg/viewer.ftl index c53950b11a49b..bfc13e3502cef 100644 --- a/l10n/bg/viewer.ftl +++ b/l10n/bg/viewer.ftl @@ -342,7 +342,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Алтернативен текст pdfjs-editor-alt-text-edit-button-label = Промяна на алтернативния текст pdfjs-editor-alt-text-dialog-label = Изберете от възможностите @@ -412,6 +411,34 @@ pdfjs-editor-colorpicker-red = ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +pdfjs-editor-new-alt-text-not-now-button = Не сега ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/bn/viewer.ftl b/l10n/bn/viewer.ftl index 1e20ecb835e0b..4f9320c606673 100644 --- a/l10n/bn/viewer.ftl +++ b/l10n/bn/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয় ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/bo/viewer.ftl b/l10n/bo/viewer.ftl index 824eab4ffbaa3..fec5da03ce90f 100644 --- a/l10n/bo/viewer.ftl +++ b/l10n/bo/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fo ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/br/viewer.ftl b/l10n/br/viewer.ftl index 60a3df0cb8a7e..e8a6c3a642aaa 100644 --- a/l10n/br/viewer.ftl +++ b/l10n/br/viewer.ftl @@ -338,3 +338,30 @@ pdfjs-editor-alt-text-settings-delete-model-button = Dilemel pdfjs-editor-alt-text-settings-download-model-button = Pellgargañ pdfjs-editor-alt-text-settings-downloading-model-button = O pellgargañ… pdfjs-editor-alt-text-settings-close-button = Serriñ + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/brx/viewer.ftl b/l10n/brx/viewer.ftl index 53ff72c5601db..62ee75f1f704a 100644 --- a/l10n/brx/viewer.ftl +++ b/l10n/brx/viewer.ftl @@ -210,9 +210,56 @@ pdfjs-web-fonts-disabled = वेब फन्टखौ लोरबां ख ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/bs/viewer.ftl b/l10n/bs/viewer.ftl index 39440424fc296..d57185efb2e65 100644 --- a/l10n/bs/viewer.ftl +++ b/l10n/bs/viewer.ftl @@ -215,9 +215,56 @@ pdfjs-web-fonts-disabled = Web fontovi su onemogućeni: nemoguće koristiti uba ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ca/viewer.ftl b/l10n/ca/viewer.ftl index 7417741df41d0..7a2746dc64227 100644 --- a/l10n/ca/viewer.ftl +++ b/l10n/ca/viewer.ftl @@ -311,3 +311,30 @@ pdfjs-ink-canvas = ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/cak/viewer.ftl b/l10n/cak/viewer.ftl index f40c1e92a4e42..e92b591d226e7 100644 --- a/l10n/cak/viewer.ftl +++ b/l10n/cak/viewer.ftl @@ -269,6 +269,12 @@ pdfjs-editor-free-text-button-label = Rucholajem tz'ib' pdfjs-editor-ink-button = .title = Tiwachib'ëx pdfjs-editor-ink-button-label = Tiwachib'ëx + +## Remove button for the various kind of editor. + + +## + # Editor Parameters pdfjs-editor-free-text-color-input = B'onil pdfjs-editor-free-text-size-input = Nimilem @@ -289,3 +295,44 @@ pdfjs-ink-canvas = ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ckb/viewer.ftl b/l10n/ckb/viewer.ftl index ae87335b8ba1a..b72eec213a625 100644 --- a/l10n/ckb/viewer.ftl +++ b/l10n/ckb/viewer.ftl @@ -234,9 +234,56 @@ pdfjs-web-fonts-disabled = جۆرەپیتی وێب ناچالاکە: نەتوا ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/cs/viewer.ftl b/l10n/cs/viewer.ftl index 0f90f86146246..e01f099d2377d 100644 --- a/l10n/cs/viewer.ftl +++ b/l10n/cs/viewer.ftl @@ -320,6 +320,9 @@ pdfjs-highlight-floating-button1 = .title = Zvýraznit .aria-label = Zvýraznit pdfjs-highlight-floating-button-label = Zvýraznit +pdfjs-editor-signature-button = + .title = Přidat podpis +pdfjs-editor-signature-button-label = Přidat podpis ## Remove button for the various kind of editor. @@ -331,6 +334,8 @@ pdfjs-editor-remove-stamp-button = .title = Odebrat obrázek pdfjs-editor-remove-highlight-button = .title = Odebrat zvýraznění +pdfjs-editor-remove-signature-button = + .title = Odebrat podpis ## @@ -347,6 +352,13 @@ pdfjs-editor-stamp-add-image-button-label = Přidat obrázek pdfjs-editor-free-highlight-thickness-input = Tloušťka pdfjs-editor-free-highlight-thickness-title = .title = Změna tloušťky při zvýrazňování jiných položek než textu +pdfjs-editor-signature-add-signature-button = + .title = Přidat nový podpis +pdfjs-editor-signature-add-signature-button-label = Přidat nový podpis +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textový editor + .default-content = Začněte psát... pdfjs-free-text = .aria-label = Textový editor pdfjs-free-text-default-content = Začněte psát… @@ -357,8 +369,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Náhradní popis +pdfjs-editor-alt-text-edit-button = + .aria-label = Upravit alternativní text pdfjs-editor-alt-text-edit-button-label = Upravit náhradní popis pdfjs-editor-alt-text-dialog-label = Vyberte možnost pdfjs-editor-alt-text-dialog-description = Náhradní popis pomáhá, když lidé obrázek nevidí nebo když se nenačítá. @@ -372,6 +385,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Označen jako dekorativní # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Například: “Mladý muž si sedá ke stolu, aby se najedl.” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativní text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -453,10 +469,16 @@ pdfjs-editor-new-alt-text-error-close-button = Zavřít pdfjs-editor-new-alt-text-ai-model-downloading-progress = Stahuje se model AI pro alternativní texty ({ $downloadedSize } z { $totalSize } MB) .aria-valuetext = Stahuje se model AI pro alternativní texty ({ $downloadedSize } z { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativní text byl přidán pdfjs-editor-new-alt-text-added-button-label = Alternativní text byl přidán # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Chybí alternativní text pdfjs-editor-new-alt-text-missing-button-label = Chybí alternativní text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Zkontrolovat alternativní text pdfjs-editor-new-alt-text-to-review-button-label = Zkontrolovat alternativní text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -483,3 +505,95 @@ pdfjs-editor-alt-text-settings-editor-title = Editor alternativního textu pdfjs-editor-alt-text-settings-show-dialog-button-label = Při přidávání obrázku hned zobrazit editor alternativního textu pdfjs-editor-alt-text-settings-show-dialog-description = Pomůže vám zajistit, aby všechny vaše obrázky obsahovaly alternativní text. pdfjs-editor-alt-text-settings-close-button = Zavřít + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Zvýraznění odebráno +pdfjs-editor-undo-bar-message-freetext = Text odstraněn +pdfjs-editor-undo-bar-message-ink = Kresba odstraněna +pdfjs-editor-undo-bar-message-stamp = Obrázek odebrán +pdfjs-editor-undo-bar-message-signature = Podpis odebrán +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotace odebrána + [few] { $count } anotace odebrány + [many] { $count } anotací odebráno + *[other] { $count } anotací odebráno + } +pdfjs-editor-undo-bar-undo-button = + .title = Zpět +pdfjs-editor-undo-bar-undo-button-label = Zpět +pdfjs-editor-undo-bar-close-button = + .title = Zavřít +pdfjs-editor-undo-bar-close-button-label = Zavřít + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Tento způsob umožňuje uživateli vytvořit podpis, který se přidá do dokumentu PDF. Uživatel může upravit jméno (které slouží zároveň jako alternativní text) a podpis uložit pro pozdější použití. +pdfjs-editor-add-signature-dialog-title = Přidat podpis + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typ + .title = Typ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Kreslit + .title = Kreslit +pdfjs-editor-add-signature-image-button = Obrázek + .title = Obrázek + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Zadejte svůj podpis + .placeholder = Zadejte svůj podpis +pdfjs-editor-add-signature-draw-placeholder = Nakreslete svůj podpis +pdfjs-editor-add-signature-draw-thickness-range-label = Tloušťka +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Tloušťka kresby: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Pro nahrání přetáhněte soubor sem +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Nebo vyberte soubory s obrázky + *[other] Nebo vyberte soubory s obrázky + } + +## Controls + +pdfjs-editor-add-signature-description-label = Popis (alternativní text) +pdfjs-editor-add-signature-description-input = + .title = Popis (alternativní text) +pdfjs-editor-add-signature-description-default-when-drawing = Podpis +pdfjs-editor-add-signature-clear-button-label = Vymazání podpisu +pdfjs-editor-add-signature-clear-button = + .title = Vymazání podpisu +pdfjs-editor-add-signature-save-checkbox = Uložit podpis +pdfjs-editor-add-signature-save-warning-message = Dosáhli jste limitu 5 uložených podpisů. Odstraňte jeden a uložte další. +pdfjs-editor-add-signature-image-upload-error-title = Obrázek se nepodařilo nahrát +pdfjs-editor-add-signature-image-upload-error-description = Zkontrolujte připojení k síti nebo zkuste jiný obrázek. +pdfjs-editor-add-signature-error-close-button = Zavřít + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Zrušit +pdfjs-editor-add-signature-add-button = Přidat +pdfjs-editor-edit-signature-update-button = Aktualizovat + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Odebrat podpis +pdfjs-editor-delete-signature-button-label = Odebrat podpis + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Upravit popis + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Upravit popis diff --git a/l10n/cy/viewer.ftl b/l10n/cy/viewer.ftl index 1c5415c8b3c05..e855c4bbd067a 100644 --- a/l10n/cy/viewer.ftl +++ b/l10n/cy/viewer.ftl @@ -324,6 +324,9 @@ pdfjs-highlight-floating-button1 = .title = Amlygu .aria-label = Amlygu pdfjs-highlight-floating-button-label = Amlygu +pdfjs-editor-signature-button = + .title = Ychwanegu llofnod +pdfjs-editor-signature-button-label = Ychwanegu llofnod ## Remove button for the various kind of editor. @@ -335,6 +338,8 @@ pdfjs-editor-remove-stamp-button = .title = Dileu delwedd pdfjs-editor-remove-highlight-button = .title = Tynnu amlygiad +pdfjs-editor-remove-signature-button = + .title = Dileu llofnod ## @@ -351,6 +356,13 @@ pdfjs-editor-stamp-add-image-button-label = Ychwanegu delwedd pdfjs-editor-free-highlight-thickness-input = Trwch pdfjs-editor-free-highlight-thickness-title = .title = Newid trwch wrth amlygu eitemau heblaw testun +pdfjs-editor-signature-add-signature-button = + .title = Ychwanegu llofnod newydd +pdfjs-editor-signature-add-signature-button-label = Ychwanegu llofnod newydd +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Golygydd Testun + .default-content = Cychwyn teipio… pdfjs-free-text = .aria-label = Golygydd Testun pdfjs-free-text-default-content = Cychwyn teipio… @@ -361,8 +373,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Testun amgen (alt) +pdfjs-editor-alt-text-edit-button = + .aria-label = Golygu testun amgen pdfjs-editor-alt-text-edit-button-label = Golygu testun amgen pdfjs-editor-alt-text-dialog-label = Dewisiadau pdfjs-editor-alt-text-dialog-description = Mae testun amgen (testun alt) yn helpu pan na all pobl weld y ddelwedd neu pan nad yw'n llwytho. @@ -376,6 +389,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcio fel addurniadol # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Er enghraifft, “Mae dyn ifanc yn eistedd wrth fwrdd i fwyta pryd bwyd” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testun amgen (alt) ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -444,7 +460,7 @@ pdfjs-editor-new-alt-text-textarea = pdfjs-editor-new-alt-text-description = Disgrifiad byr ar gyfer pobl sydd ddim yn gallu gweld y ddelwedd neu pan nad yw'r ddelwedd yn llwytho. # This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. pdfjs-editor-new-alt-text-disclaimer1 = Cafodd y testun amgen hwn ei greu'n awtomatig a gall fod yn anghywir. -pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Dysgu rhagor +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Rhagor pdfjs-editor-new-alt-text-create-automatically-button-label = Creu testun amgen yn awtomatig pdfjs-editor-new-alt-text-not-now-button = Nid nawr pdfjs-editor-new-alt-text-error-title = Methu â chreu testun amgen yn awtomatig @@ -457,10 +473,16 @@ pdfjs-editor-new-alt-text-error-close-button = Cau pdfjs-editor-new-alt-text-ai-model-downloading-progress = Wrthi'n llwytho i lawr model AI testun amgen ( { $downloadedSize } o { $totalSize } MB) .aria-valuetext = Wrthi'n llwytho i lawr model AI testun amgen ( { $downloadedSize } o { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Ychwanegwyd testun amgen pdfjs-editor-new-alt-text-added-button-label = Ychwanegwyd testun amgen # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testun amgen coll pdfjs-editor-new-alt-text-missing-button-label = Testun amgen coll # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Adolygu'r testun amgen pdfjs-editor-new-alt-text-to-review-button-label = Adolygu'r testun amgen # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -487,3 +509,97 @@ pdfjs-editor-alt-text-settings-editor-title = Golygydd testun amgen pdfjs-editor-alt-text-settings-show-dialog-button-label = Dangoswch y golygydd testun amgen yn syth wrth ychwanegu delwedd pdfjs-editor-alt-text-settings-show-dialog-description = Yn eich helpu i wneud yn siŵr bod gan eich holl ddelweddau destun amgen. pdfjs-editor-alt-text-settings-close-button = Cau + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Tynnwyd yr amlygu +pdfjs-editor-undo-bar-message-freetext = Tynnwyd y testun +pdfjs-editor-undo-bar-message-ink = Tynnwyd y lluniad +pdfjs-editor-undo-bar-message-stamp = Tynnwyd y ddelwedd +pdfjs-editor-undo-bar-message-signature = Llofnod wedi'i dynnu +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [zero] { $count } anodiad wedi'u tynnu + [one] { $count } anodiad wedi'i dynnu + [two] { $count } anodiad wedi'u tynnu + [few] { $count } anodiad wedi'u tynnu + [many] { $count } anodiad wedi'u tynnu + *[other] { $count } anodiad wedi'u tynnu + } +pdfjs-editor-undo-bar-undo-button = + .title = Dadwneud +pdfjs-editor-undo-bar-undo-button-label = Dadwneud +pdfjs-editor-undo-bar-close-button = + .title = Cau +pdfjs-editor-undo-bar-close-button-label = Cau + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Mae'r modd hwn yn caniatáu i'r defnyddiwr greu llofnod i'w ychwanegu at ddogfen PDF. Gall y defnyddiwr olygu'r enw (sydd hefyd yn gweithredu fel y testun amgen), ac yn ddewisol cadw'r llofnod i'w ddefnyddio dro ar ôl tro. +pdfjs-editor-add-signature-dialog-title = Ychwanegu llofnod + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Math + .title = Math +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Lluniadu + .title = Lluniadu +pdfjs-editor-add-signature-image-button = Delwedd + .title = Delwedd + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Teipiwch eich llofnod + .placeholder = Teipiwch eich llofnod +pdfjs-editor-add-signature-draw-placeholder = Lluniwch eich llofnod +pdfjs-editor-add-signature-draw-thickness-range-label = Trwch +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Trwch y llinell: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Llusgwch ffeil yma i'w llwytho +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Neu ddewis ffeiliau delwedd + *[other] Neu bori ffeiliau delwedd + } + +## Controls + +pdfjs-editor-add-signature-description-label = Disgrifiad (testun amgen) +pdfjs-editor-add-signature-description-input = + .title = Disgrifiad (testun amgen) +pdfjs-editor-add-signature-description-default-when-drawing = Llofnod +pdfjs-editor-add-signature-clear-button-label = Diddymu llofnod +pdfjs-editor-add-signature-clear-button = + .title = Diddymu llofnod +pdfjs-editor-add-signature-save-checkbox = Cadw llofnod +pdfjs-editor-add-signature-save-warning-message = Rydych chi wedi cyrraedd y terfyn o 5 llofnod sydd wedi'u cadw. Tynnwch un i gadw rhagor +pdfjs-editor-add-signature-image-upload-error-title = Methu llwytho'r ddelwedd. +pdfjs-editor-add-signature-image-upload-error-description = Gwiriwch eich cysylltiad rhwydwaith neu rhowch gynnig ar ddelwedd arall. +pdfjs-editor-add-signature-error-close-button = Cau + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Diddymu +pdfjs-editor-add-signature-add-button = Ychwanegu +pdfjs-editor-edit-signature-update-button = Diweddaru + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Dileu llofnod +pdfjs-editor-delete-signature-button-label = Dileu llofnod + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Golygu disgrifiad + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Golygu disgrifiad diff --git a/l10n/da/viewer.ftl b/l10n/da/viewer.ftl index a3ba621a94721..9b71b4131b084 100644 --- a/l10n/da/viewer.ftl +++ b/l10n/da/viewer.ftl @@ -184,7 +184,7 @@ pdfjs-toggle-sidebar-notification-button = .title = Slå sidepanel til eller fra (dokumentet indeholder disposition/vedhæftede filer/lag) pdfjs-toggle-sidebar-button-label = Slå sidepanel til eller fra pdfjs-document-outline-button = - .title = Vis dokumentets disposition (dobbeltklik for at vise/skjule alle elementer) + .title = Vis dokumentets disposition (dobbeltklik for at udvide/sammenfolde alle elementer) pdfjs-document-outline-button-label = Dokument-disposition pdfjs-attachments-button = .title = Vis vedhæftede filer @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Fremhæv .aria-label = Fremhæv pdfjs-highlight-floating-button-label = Fremhæv +pdfjs-editor-signature-button = + .title = Tilføj underskrift +pdfjs-editor-signature-button-label = Tilføj underskrift ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Fjern billede pdfjs-editor-remove-highlight-button = .title = Fjern fremhævning +pdfjs-editor-remove-signature-button = + .title = Fjern underskrift ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Tilføj billede pdfjs-editor-free-highlight-thickness-input = Tykkelse pdfjs-editor-free-highlight-thickness-title = .title = Ændr tykkelse, når andre elementer end tekst fremhæves +pdfjs-editor-signature-add-signature-button = + .title = Tilføj ny underskrift +pdfjs-editor-signature-add-signature-button-label = Tilføj ny underskrift +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Teksteditor + .default-content = Begynd at skrive… pdfjs-free-text = .aria-label = Teksteditor pdfjs-free-text-default-content = Begynd at skrive… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternativ tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alternativ tekst pdfjs-editor-alt-text-edit-button-label = Rediger alternativ tekst pdfjs-editor-alt-text-dialog-label = Vælg en indstilling pdfjs-editor-alt-text-dialog-description = Alternativ tekst hjælper folk, som ikke kan se billedet eller når det ikke indlæses. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Markeret som dekorativ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = For eksempel: "En ung mand sætter sig ved et bord for at spise et måltid mad" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Luk pdfjs-editor-new-alt-text-ai-model-downloading-progress = Henter alternativ tekst AI-model ({ $downloadedSize } af { $totalSize } MB) .aria-valuetext = Henter alternativ tekst AI-model ({ $downloadedSize } af { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ tekst tilføjet pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst tilføjet # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mangler alternativ tekst pdfjs-editor-new-alt-text-missing-button-label = Mangler alternativ tekst # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Gennemgå alternativ tekst pdfjs-editor-new-alt-text-to-review-button-label = Gennemgå alternativ tekst # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Redigering af alternativ tekst pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis redigering af alternativ tekst med det samme, når et billede tilføjes pdfjs-editor-alt-text-settings-show-dialog-description = Hjælper dig med at sikre, at alle dine billeder har alternativ tekst. pdfjs-editor-alt-text-settings-close-button = Luk + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Fremhævning fjernet +pdfjs-editor-undo-bar-message-freetext = Tekst fjernet +pdfjs-editor-undo-bar-message-ink = Tegning fjernet +pdfjs-editor-undo-bar-message-stamp = Billede fjernet +pdfjs-editor-undo-bar-message-signature = Underskrift fjernet +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } kommentar fjernet + *[other] { $count } kommentarer fjernet + } +pdfjs-editor-undo-bar-undo-button = + .title = Fortryd +pdfjs-editor-undo-bar-undo-button-label = Fortryd +pdfjs-editor-undo-bar-close-button = + .title = Luk +pdfjs-editor-undo-bar-close-button-label = Luk + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Modal-vinduet gør det muligt for brugeren at oprette en underskrift, som kan føjes til PDF-dokumenter. Brugeren kan redigere navnet (der også fungerer som alternativ tekst) og eventuelt gemme signaturen, så den kan bruges igen. +pdfjs-editor-add-signature-dialog-title = Tilføj en underskrift + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Indtast + .title = Indtast +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Tegn + .title = Tegn +pdfjs-editor-add-signature-image-button = Billede + .title = Billede + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Indtast din underskrift + .placeholder = Indtast din underskrift +pdfjs-editor-add-signature-draw-placeholder = Tegn din underskrift +pdfjs-editor-add-signature-draw-thickness-range-label = Tykkelse +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Linjetykkelse: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Træk en fil herhen for at uploade den +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Eller vælg billedfiler + *[other] Eller vælg billedfiler + } + +## Controls + +pdfjs-editor-add-signature-description-label = Beskrivelse (alternativ tekst) +pdfjs-editor-add-signature-description-input = + .title = Beskrivelse (alternativ tekst) +pdfjs-editor-add-signature-description-default-when-drawing = Underskrift +pdfjs-editor-add-signature-clear-button-label = Ryd underskrift +pdfjs-editor-add-signature-clear-button = + .title = Ryd underskrift +pdfjs-editor-add-signature-save-checkbox = Gem underskrift +pdfjs-editor-add-signature-save-warning-message = Du har nået grænsen på 5 gemte underskrifter. Fjern en for at tilføje en ny. +pdfjs-editor-add-signature-image-upload-error-title = Kunne ikke uploade billede +pdfjs-editor-add-signature-image-upload-error-description = Kontroller din netværksforbindelse eller prøv med et andet billede. +pdfjs-editor-add-signature-error-close-button = Luk + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Annuller +pdfjs-editor-add-signature-add-button = Tilføj +pdfjs-editor-edit-signature-update-button = Opdater + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Fjern underskrift +pdfjs-editor-delete-signature-button-label = Fjern underskrift + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Rediger beskrivelse + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Rediger beskrivelse diff --git a/l10n/de/viewer.ftl b/l10n/de/viewer.ftl index b8d7ab9abf7a6..f64e95dc730fb 100644 --- a/l10n/de/viewer.ftl +++ b/l10n/de/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Hervorheben .aria-label = Hervorheben pdfjs-highlight-floating-button-label = Hervorheben +pdfjs-editor-signature-button = + .title = Unterschrift hinzufügen +pdfjs-editor-signature-button-label = Unterschrift hinzufügen ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Grafik entfernen pdfjs-editor-remove-highlight-button = .title = Hervorhebung entfernen +pdfjs-editor-remove-signature-button = + .title = Unterschrift entfernen ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Grafik hinzufügen pdfjs-editor-free-highlight-thickness-input = Linienstärke pdfjs-editor-free-highlight-thickness-title = .title = Linienstärke beim Hervorheben anderer Elemente als Text ändern +pdfjs-editor-signature-add-signature-button = + .title = Neue Unterschrift hinzufügen +pdfjs-editor-signature-add-signature-button-label = Neue Unterschrift hinzufügen +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Texteditor + .default-content = Schreiben beginnen… pdfjs-free-text = .aria-label = Texteditor pdfjs-free-text-default-content = Schreiben beginnen… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternativ-Text +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternativ-Text bearbeiten pdfjs-editor-alt-text-edit-button-label = Alternativ-Text bearbeiten pdfjs-editor-alt-text-dialog-label = Option wählen pdfjs-editor-alt-text-dialog-description = Alt-Text (Alternativtext) hilft, wenn Personen die Grafik nicht sehen können oder wenn sie nicht geladen wird. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Als dekorativ markiert # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Zum Beispiel: "Ein junger Mann setzt sich an einen Tisch, um zu essen." +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ-Text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Schließen pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alternativ-Text-KI-Modell wird heruntergeladen ({ $downloadedSize } von { $totalSize } MB) .aria-valuetext = Alternativ-Text-KI-Modell wird heruntergeladen ({ $downloadedSize } von { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ-Text hinzugefügt pdfjs-editor-new-alt-text-added-button-label = Alternativ-Text hinzugefügt # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Fehlender Alternativ-Text pdfjs-editor-new-alt-text-missing-button-label = Fehlender Alternativ-Text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternativ-Text überprüfen pdfjs-editor-new-alt-text-to-review-button-label = Alternativ-Text überprüfen # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Alternativ-Texteditor pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternativ-Texteditor beim Hinzufügen einer Grafik anzeigen pdfjs-editor-alt-text-settings-show-dialog-description = Hilft Ihnen, sicherzustellen, dass alle Ihre Grafiken Alternativ-Text haben. pdfjs-editor-alt-text-settings-close-button = Schließen + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Hervorhebung entfernt +pdfjs-editor-undo-bar-message-freetext = Text entfernt +pdfjs-editor-undo-bar-message-ink = Zeichnung entfernt +pdfjs-editor-undo-bar-message-stamp = Grafik entfernt +pdfjs-editor-undo-bar-message-signature = Unterschrift entfernt +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } Anmerkung entfernt + *[other] { $count } Anmerkungen entfernt + } +pdfjs-editor-undo-bar-undo-button = + .title = Rückgängig +pdfjs-editor-undo-bar-undo-button-label = Rückgängig +pdfjs-editor-undo-bar-close-button = + .title = Schließen +pdfjs-editor-undo-bar-close-button-label = Schließen + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Dieses Modal ermöglicht es dem Benutzer, eine Unterschrift zu erstellen, um sie zu einem PDF-Dokument hinzuzufügen. Der Benutzer kann den Namen bearbeiten (der auch als Alt-Text dient) und optional die Unterschrift zur wiederholten Verwendung speichern. +pdfjs-editor-add-signature-dialog-title = Unterschrift hinzufügen + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Eintippen + .title = Eintippen +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Zeichnen + .title = Zeichnen +pdfjs-editor-add-signature-image-button = Grafik + .title = Grafik + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Tippen Sie Ihre Unterschrift ein + .placeholder = Tippen Sie Ihre Unterschrift ein +pdfjs-editor-add-signature-draw-placeholder = Ihre Unterschrift zeichnen +pdfjs-editor-add-signature-draw-thickness-range-label = Linienstärke +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Zeichnungsstärke: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Datei zum Hochladen hierher ziehen +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Oder Grafikdateien wählen + *[other] Oder Bilddateien durchsuchen + } + +## Controls + +pdfjs-editor-add-signature-description-label = Beschreibung (alternativer Text) +pdfjs-editor-add-signature-description-input = + .title = Beschreibung (alternativer Text) +pdfjs-editor-add-signature-description-default-when-drawing = Unterschrift +pdfjs-editor-add-signature-clear-button-label = Unterschrift löschen +pdfjs-editor-add-signature-clear-button = + .title = Unterschrift löschen +pdfjs-editor-add-signature-save-checkbox = Unterschrift speichern +pdfjs-editor-add-signature-save-warning-message = Sie haben die Grenze von 5 gespeicherten Unterschriften erreicht. Entfernen Sie eine, um weitere zu speichern. +pdfjs-editor-add-signature-image-upload-error-title = Grafik konnte nicht hochgeladen werden +pdfjs-editor-add-signature-image-upload-error-description = Überprüfen Sie Ihre Netzwerkverbindung, oder versuchen Sie es mit einer anderen Grafik. +pdfjs-editor-add-signature-error-close-button = Schließen + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Abbrechen +pdfjs-editor-add-signature-add-button = Hinzufügen +pdfjs-editor-edit-signature-update-button = Aktualisieren + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Unterschrift entfernen +pdfjs-editor-delete-signature-button-label = Unterschrift entfernen + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Beschreibung bearbeiten + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Beschreibung bearbeiten diff --git a/l10n/dsb/viewer.ftl b/l10n/dsb/viewer.ftl index eeb41da9da9f0..4c0b72ed0cbc2 100644 --- a/l10n/dsb/viewer.ftl +++ b/l10n/dsb/viewer.ftl @@ -320,6 +320,9 @@ pdfjs-highlight-floating-button1 = .title = Wuzwignuś .aria-label = Wuzwignuś pdfjs-highlight-floating-button-label = Wuzwignuś +pdfjs-editor-signature-button = + .title = Signaturu pśidaś +pdfjs-editor-signature-button-label = Signaturu pśidaś ## Remove button for the various kind of editor. @@ -331,6 +334,8 @@ pdfjs-editor-remove-stamp-button = .title = Wobraz wótwónoźeś pdfjs-editor-remove-highlight-button = .title = Wuzwignjenje wótpóraś +pdfjs-editor-remove-signature-button = + .title = Signaturu wótwónoźeś ## @@ -347,6 +352,13 @@ pdfjs-editor-stamp-add-image-button-label = Wobraz pśidaś pdfjs-editor-free-highlight-thickness-input = Tłustosć pdfjs-editor-free-highlight-thickness-title = .title = Tłustosć změniś, gaž se zapiski wuzwiguju, kótarež tekst njejsu +pdfjs-editor-signature-add-signature-button = + .title = Nowu signaturu pśidaś +pdfjs-editor-signature-add-signature-button-label = Nowu signaturu pśidaś +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstowy editor + .default-content = Zachopśo pisaś … pdfjs-free-text = .aria-label = Tekstowy editor pdfjs-free-text-default-content = Zachopśo pisaś… @@ -357,8 +369,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatiwny tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatiwny tekst wobźěłaś pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobźěłaś pdfjs-editor-alt-text-dialog-label = Nastajenje wubraś pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomaga, gaž luźe njamógu wobraz wiźeś abo gaž se wobraz njezacytajo. @@ -372,6 +385,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Ako dekoratiwny markěrowany # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Na pśikład, „Młody muski za blidom sejźi, aby jěź jědł“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatiwny tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -453,10 +469,16 @@ pdfjs-editor-new-alt-text-error-close-button = Zacyniś pdfjs-editor-new-alt-text-ai-model-downloading-progress = Model KI za alternatiwny tekst se ześěgujo ({ $downloadedSize } z { $totalSize } MB) .aria-valuetext = Model KI za alternatiwny tekst se ześěgujo ({ $downloadedSize } z { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatiwny tekst jo se pśidał pdfjs-editor-new-alt-text-added-button-label = Alternatiwny tekst jo se pśidał # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatiwny tekst felujo pdfjs-editor-new-alt-text-missing-button-label = Alternatiwny tekst felujo # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatiwny tekst pśeglědowaś pdfjs-editor-new-alt-text-to-review-button-label = Alternatiwny tekst pśeglědowaś # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -483,3 +505,95 @@ pdfjs-editor-alt-text-settings-editor-title = Editor za alternatiwny tekst pdfjs-editor-alt-text-settings-show-dialog-button-label = Editor alternatiwnego teksta ned pokazaś, gaž se wobraz pśidawa pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga, wam wšym swójim wobrazam alternatiwny tekst pśidaś. pdfjs-editor-alt-text-settings-close-button = Zacyniś + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Wótwónoźone wuzwignuś +pdfjs-editor-undo-bar-message-freetext = Tekst jo se wótwónoźeł +pdfjs-editor-undo-bar-message-ink = Kreslanka jo se wótwónoźeła +pdfjs-editor-undo-bar-message-stamp = Wobraz jo se wótwónoźeł +pdfjs-editor-undo-bar-message-signature = Signatura jo se wótwónoźeła +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } pśipisk jo se wótwónoźeł + [two] { $count } pśipiska stej se wótwónoźełej + [few] { $count } pśipiski su se wótwónoźeli + *[other] { $count } pśipiskow jo se wótwónoźeło + } +pdfjs-editor-undo-bar-undo-button = + .title = Anulěrowaś +pdfjs-editor-undo-bar-undo-button-label = Anulěrowaś +pdfjs-editor-undo-bar-close-button = + .title = Zacyniś +pdfjs-editor-undo-bar-close-button-label = Zacyniś + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Toś ten modalny dialog wužywarjeju zmóžnja, signaturu napóraś, aby PDF-dokument pśidał. Wužywaŕ móžo mě wobźěłaś (kótarež teke ako alternatiwny tekst słužy) a pó žycenju signaturu za wóspjetne wužywanje składowaś. +pdfjs-editor-add-signature-dialog-title = Signaturu pśidaś + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typ + .title = Typ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Kresliś + .title = Kresliś +pdfjs-editor-add-signature-image-button = Wobraz + .title = Wobraz + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Zapódajśo swóju signaturu + .placeholder = Zapódajśo swóju signaturu +pdfjs-editor-add-signature-draw-placeholder = Kresliśo swóju signaturu +pdfjs-editor-add-signature-draw-thickness-range-label = Tłustosć +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Tłustosć kreslanki: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Śěgniśo dataju sem, aby ju nagrał +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Abo wubjeŕśo wobrazowe dataje + *[other] Abo pśepytajśo wobrazowe dataje + } + +## Controls + +pdfjs-editor-add-signature-description-label = Wopisanje (alternatiwny tekst) +pdfjs-editor-add-signature-description-input = + .title = Wopisanje (alternatiwny tekst) +pdfjs-editor-add-signature-description-default-when-drawing = Signatura +pdfjs-editor-add-signature-clear-button-label = Signaturu lašowaś +pdfjs-editor-add-signature-clear-button = + .title = Signaturu lašowaś +pdfjs-editor-add-signature-save-checkbox = Signaturu składowaś +pdfjs-editor-add-signature-save-warning-message = Sćo dojśpił limit 5 skłaźonych signaturow. Wótwónoźćo jadnu, aby wěcej składował. +pdfjs-editor-add-signature-image-upload-error-title = Wobraz njedajo se nagraś +pdfjs-editor-add-signature-image-upload-error-description = Pśeglědajśo swój seśowy zwisk abo wopytajśo drugi wobraz. +pdfjs-editor-add-signature-error-close-button = Zacyniś + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Pśetergnuś +pdfjs-editor-add-signature-add-button = Pśidaś +pdfjs-editor-edit-signature-update-button = Aktualizěrowaś + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Signaturu wótwónoźeś +pdfjs-editor-delete-signature-button-label = Signaturu wótwónoźeś + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Wopisanje wobźěłaś + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Wopisanje wobźěłaś diff --git a/l10n/el/viewer.ftl b/l10n/el/viewer.ftl index a95b6e71cd42d..d68da090bb9f5 100644 --- a/l10n/el/viewer.ftl +++ b/l10n/el/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Επισήμανση .aria-label = Επισήμανση pdfjs-highlight-floating-button-label = Επισήμανση +pdfjs-editor-signature-button = + .title = Προσθήκη υπογραφής +pdfjs-editor-signature-button-label = Προσθήκη υπογραφής ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Αφαίρεση εικόνας pdfjs-editor-remove-highlight-button = .title = Αφαίρεση επισήμανσης +pdfjs-editor-remove-signature-button = + .title = Αφαίρεση υπογραφής ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Προσθήκη εικόνας pdfjs-editor-free-highlight-thickness-input = Πάχος pdfjs-editor-free-highlight-thickness-title = .title = Αλλαγή πάχους κατά την επισήμανση στοιχείων εκτός κειμένου +pdfjs-editor-signature-add-signature-button = + .title = Προσθήκη νέας υπογραφής +pdfjs-editor-signature-add-signature-button-label = Προσθήκη νέας υπογραφής +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Επεξεργασία κειμένου + .default-content = Ξεκινήστε να πληκτρολογείτε… pdfjs-free-text = .aria-label = Επεξεργασία κειμένου pdfjs-free-text-default-content = Ξεκινήστε να πληκτρολογείτε… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Εναλλακτικό κείμενο +pdfjs-editor-alt-text-edit-button = + .aria-label = Επεξεργασία εναλλακτικού κειμένου pdfjs-editor-alt-text-edit-button-label = Επεξεργασία εναλλακτικού κειμένου pdfjs-editor-alt-text-dialog-label = Διαλέξτε μια επιλογή pdfjs-editor-alt-text-dialog-description = Το εναλλακτικό κείμενο είναι χρήσιμο όταν οι άνθρωποι δεν μπορούν να δουν την εικόνα ή όταν αυτή δεν φορτώνεται. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Επισημασμένο ως δια # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Για παράδειγμα, «Ένας νεαρός άνδρας κάθεται σε ένα τραπέζι για να φάει ένα γεύμα» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Εναλλακτικό κείμενο ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -446,13 +462,19 @@ pdfjs-editor-new-alt-text-error-close-button = Κλείσιμο # $totalSize (Number) - the total size (in MB) of the AI model. # $downloadedSize (Number) - the downloaded size (in MB) of the AI model. # $percent (Number) - the percentage of the downloaded size. -pdfjs-editor-new-alt-text-ai-model-downloading-progress = Λήψη μοντέλου AI εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) - .aria-valuetext = Λήψη μοντέλου AI εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Λήψη μοντέλου ΤΝ εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) + .aria-valuetext = Λήψη μοντέλου ΤΝ εναλλακτικού κειμένου ({ $downloadedSize } από { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Προστέθηκε εναλλακτικό κείμενο pdfjs-editor-new-alt-text-added-button-label = Προστέθηκε εναλλακτικό κείμενο # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Απουσία εναλλακτικού κειμένου pdfjs-editor-new-alt-text-missing-button-label = Απουσία εναλλακτικού κειμένου # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Έλεγχος εναλλακτικού κειμένου pdfjs-editor-new-alt-text-to-review-button-label = Έλεγχος εναλλακτικού κειμένου # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -470,7 +492,7 @@ pdfjs-editor-alt-text-settings-create-model-button-label = Αυτόματη δη pdfjs-editor-alt-text-settings-create-model-description = Προτείνει περιγραφές για άτομα που δεν μπορούν να δουν την εικόνα ή όταν η εικόνα δεν φορτώνεται. # Variables: # $totalSize (Number) - the total size (in MB) of the AI model. -pdfjs-editor-alt-text-settings-download-model-label = Μοντέλο AI εναλλακτικού κειμένου ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-download-model-label = Μοντέλο ΤΝ εναλλακτικού κειμένου ({ $totalSize } MB) pdfjs-editor-alt-text-settings-ai-model-description = Εκτελείται τοπικά στη συσκευή σας, ώστε τα δεδομένα σας να παραμένουν ιδιωτικά. Απαιτείται για τη δημιουργία του αυτόματου εναλλακτικού κειμένου. pdfjs-editor-alt-text-settings-delete-model-button = Διαγραφή pdfjs-editor-alt-text-settings-download-model-button = Λήψη @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Επεξεργασία εναλλ pdfjs-editor-alt-text-settings-show-dialog-button-label = Άμεση εμφάνιση της επεξεργασίας εναλλακτικού κειμένου κατά την προσθήκη εικόνας pdfjs-editor-alt-text-settings-show-dialog-description = Σας βοηθά να βεβαιωθείτε ότι όλες οι εικόνες σας έχουν εναλλακτικό κείμενο. pdfjs-editor-alt-text-settings-close-button = Κλείσιμο + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Η επισήμανση αφαιρέθηκε +pdfjs-editor-undo-bar-message-freetext = Το κείμενο αφαιρέθηκε +pdfjs-editor-undo-bar-message-ink = Το σχέδιο αφαιρέθηκε +pdfjs-editor-undo-bar-message-stamp = Η εικόνα αφαιρέθηκε +pdfjs-editor-undo-bar-message-signature = Η υπογραφή αφαιρέθηκε +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Αφαιρέθηκε { $count } σχολιασμός + *[other] Αφαιρέθηκαν { $count } σχολιασμοί + } +pdfjs-editor-undo-bar-undo-button = + .title = Αναίρεση +pdfjs-editor-undo-bar-undo-button-label = Αναίρεση +pdfjs-editor-undo-bar-close-button = + .title = Κλείσιμο +pdfjs-editor-undo-bar-close-button-label = Κλείσιμο + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Αυτό το παράθυρο διαλόγου επιτρέπει στον χρήστη να δημιουργήσει μια υπογραφή για να την προσθέσει σε ένα έγγραφο PDF. Ο χρήστης μπορεί να επεξεργαστεί το όνομα (το οποίο χρησιμεύει και ως εναλλακτικό κείμενο) και, προαιρετικά, να αποθηκεύσει την υπογραφή για επαναλαμβανόμενη χρήση. +pdfjs-editor-add-signature-dialog-title = Προσθήκη υπογραφής + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Πληκτρολόγηση + .title = Πληκτρολόγηση +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Σχέδιο + .title = Σχέδιο +pdfjs-editor-add-signature-image-button = Εικόνα + .title = Εικόνα + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Πληκτρολογήστε την υπογραφή σας + .placeholder = Πληκτρολογήστε την υπογραφή σας +pdfjs-editor-add-signature-draw-placeholder = Σχεδιάστε την υπογραφή σας +pdfjs-editor-add-signature-draw-thickness-range-label = Πάχος +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Πάχος σχεδίου: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Σύρετε ένα αρχείο εδώ για μεταφόρτωση +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ή επιλέξτε αρχεία εικόνας + *[other] Ή περιηγηθείτε σε αρχεία εικόνας + } + +## Controls + +pdfjs-editor-add-signature-description-label = Περιγραφή (εναλλακτικό κείμενο) +pdfjs-editor-add-signature-description-input = + .title = Περιγραφή (εναλλακτικό κείμενο) +pdfjs-editor-add-signature-description-default-when-drawing = Υπογραφή +pdfjs-editor-add-signature-clear-button-label = Απαλοιφή υπογραφής +pdfjs-editor-add-signature-clear-button = + .title = Απαλοιφή υπογραφής +pdfjs-editor-add-signature-save-checkbox = Αποθήκευση υπογραφής +pdfjs-editor-add-signature-save-warning-message = Έχετε φτάσει το όριο των 5 αποθηκευμένων υπογραφών. Αφαιρέστε μία για να αποθηκεύσετε περισσότερες. +pdfjs-editor-add-signature-image-upload-error-title = Δεν ήταν δυνατή η μεταφόρτωση της εικόνας +pdfjs-editor-add-signature-image-upload-error-description = Ελέγξτε τη σύνδεση δικτύου σας ή δοκιμάστε μια άλλη εικόνα. +pdfjs-editor-add-signature-error-close-button = Κλείσιμο + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Ακύρωση +pdfjs-editor-add-signature-add-button = Προσθήκη +pdfjs-editor-edit-signature-update-button = Ενημέρωση + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Αφαίρεση υπογραφής +pdfjs-editor-delete-signature-button-label = Αφαίρεση υπογραφής + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Επεξεργασία περιγραφής + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Επεξεργασία περιγραφής diff --git a/l10n/en-CA/viewer.ftl b/l10n/en-CA/viewer.ftl index 729b61527dd47..b2fc2a1f7699e 100644 --- a/l10n/en-CA/viewer.ftl +++ b/l10n/en-CA/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Highlight .aria-label = Highlight pdfjs-highlight-floating-button-label = Highlight +pdfjs-editor-signature-button = + .title = Add signature +pdfjs-editor-signature-button-label = Add signature ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Remove image pdfjs-editor-remove-highlight-button = .title = Remove highlight +pdfjs-editor-remove-signature-button = + .title = Remove signature ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Add image pdfjs-editor-free-highlight-thickness-input = Thickness pdfjs-editor-free-highlight-thickness-title = .title = Change thickness when highlighting items other than text +pdfjs-editor-signature-add-signature-button = + .title = Add new signature +pdfjs-editor-signature-add-signature-button-label = Add new signature +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Text Editor + .default-content = Start typing… pdfjs-free-text = .aria-label = Text Editor pdfjs-free-text-default-content = Start typing… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt text +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text pdfjs-editor-alt-text-edit-button-label = Edit alt text pdfjs-editor-alt-text-dialog-label = Choose an option pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = For example, “A young man sits down at a table to eat a meal” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Close pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added pdfjs-editor-new-alt-text-added-button-label = Alt text added # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text pdfjs-editor-new-alt-text-missing-button-label = Missing alt text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text pdfjs-editor-new-alt-text-to-review-button-label = Review alt text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Alt text editor pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +pdfjs-editor-undo-bar-message-signature = Signature removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = This modal allows the user to create a signature to add to a PDF document. The user can edit the name (which also serves as the alt text), and optionally save the signature for repeated use. +pdfjs-editor-add-signature-dialog-title = Add a signature + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Type + .title = Type +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Draw + .title = Draw +pdfjs-editor-add-signature-image-button = Image + .title = Image + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Type your signature + .placeholder = Type your signature +pdfjs-editor-add-signature-draw-placeholder = Draw your signature +pdfjs-editor-add-signature-draw-thickness-range-label = Thickness +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Drawing thickness: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Drag a file here to upload +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Or choose image files + *[other] Or browse image files + } + +## Controls + +pdfjs-editor-add-signature-description-label = Description (alt text) +pdfjs-editor-add-signature-description-input = + .title = Description (alt text) +pdfjs-editor-add-signature-description-default-when-drawing = Signature +pdfjs-editor-add-signature-clear-button-label = Clear signature +pdfjs-editor-add-signature-clear-button = + .title = Clear signature +pdfjs-editor-add-signature-save-checkbox = Save signature +pdfjs-editor-add-signature-save-warning-message = You’ve reached the limit of 5 saved signatures. Remove one to save more. +pdfjs-editor-add-signature-image-upload-error-title = Couldn’t upload image +pdfjs-editor-add-signature-image-upload-error-description = Check your network connection or try another image. +pdfjs-editor-add-signature-error-close-button = Close + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancel +pdfjs-editor-add-signature-add-button = Add +pdfjs-editor-edit-signature-update-button = Update + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Remove signature +pdfjs-editor-delete-signature-button-label = Remove signature + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Edit description + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Edit description diff --git a/l10n/en-GB/viewer.ftl b/l10n/en-GB/viewer.ftl index 52e4a12bda36f..08e16ca688bec 100644 --- a/l10n/en-GB/viewer.ftl +++ b/l10n/en-GB/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Highlight .aria-label = Highlight pdfjs-highlight-floating-button-label = Highlight +pdfjs-editor-signature-button = + .title = Add signature +pdfjs-editor-signature-button-label = Add signature ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Remove image pdfjs-editor-remove-highlight-button = .title = Remove highlight +pdfjs-editor-remove-signature-button = + .title = Remove signature ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Add image pdfjs-editor-free-highlight-thickness-input = Thickness pdfjs-editor-free-highlight-thickness-title = .title = Change thickness when highlighting items other than text +pdfjs-editor-signature-add-signature-button = + .title = Add new signature +pdfjs-editor-signature-add-signature-button-label = Add new signature +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Text Editor + .default-content = Start typing… pdfjs-free-text = .aria-label = Text Editor pdfjs-free-text-default-content = Start typing… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt text +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text pdfjs-editor-alt-text-edit-button-label = Edit alt text pdfjs-editor-alt-text-dialog-label = Choose an option pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = For example, “A young man sits down at a table to eat a meal” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Close pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added pdfjs-editor-new-alt-text-added-button-label = Alt text added # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text pdfjs-editor-new-alt-text-missing-button-label = Missing alt text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text pdfjs-editor-new-alt-text-to-review-button-label = Review alt text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Alt text editor pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +pdfjs-editor-undo-bar-message-signature = Signature removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = This modal allows the user to create a signature to add to a PDF document. The user can edit the name (which also serves as the alt text), and optionally save the signature for repeated use. +pdfjs-editor-add-signature-dialog-title = Add a signature + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Type + .title = Type +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Draw + .title = Draw +pdfjs-editor-add-signature-image-button = Image + .title = Image + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Type your signature + .placeholder = Type your signature +pdfjs-editor-add-signature-draw-placeholder = Draw your signature +pdfjs-editor-add-signature-draw-thickness-range-label = Thickness +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Drawing thickness: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Drag a file here to upload +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Or choose image files + *[other] Or browse image files + } + +## Controls + +pdfjs-editor-add-signature-description-label = Description (alt text) +pdfjs-editor-add-signature-description-input = + .title = Description (alt text) +pdfjs-editor-add-signature-description-default-when-drawing = Signature +pdfjs-editor-add-signature-clear-button-label = Clear signature +pdfjs-editor-add-signature-clear-button = + .title = Clear signature +pdfjs-editor-add-signature-save-checkbox = Save signature +pdfjs-editor-add-signature-save-warning-message = You’ve reached the limit of 5 saved signatures. Remove one to save more. +pdfjs-editor-add-signature-image-upload-error-title = Couldn’t upload image +pdfjs-editor-add-signature-image-upload-error-description = Check your network connection or try another image. +pdfjs-editor-add-signature-error-close-button = Close + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancel +pdfjs-editor-add-signature-add-button = Add +pdfjs-editor-edit-signature-update-button = Update + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Remove signature +pdfjs-editor-delete-signature-button-label = Remove signature + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Edit description + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Edit description diff --git a/l10n/en-US/viewer.ftl b/l10n/en-US/viewer.ftl index 29848cd992fce..e8c32ae45f2df 100644 --- a/l10n/en-US/viewer.ftl +++ b/l10n/en-US/viewer.ftl @@ -320,6 +320,9 @@ pdfjs-highlight-floating-button1 = .title = Highlight .aria-label = Highlight pdfjs-highlight-floating-button-label = Highlight +pdfjs-editor-signature-button = + .title = Add signature +pdfjs-editor-signature-button-label = Add signature ## Remove button for the various kind of editor. @@ -331,6 +334,8 @@ pdfjs-editor-remove-stamp-button = .title = Remove image pdfjs-editor-remove-highlight-button = .title = Remove highlight +pdfjs-editor-remove-signature-button = + .title = Remove signature ## @@ -347,6 +352,9 @@ pdfjs-editor-stamp-add-image-button-label = Add image pdfjs-editor-free-highlight-thickness-input = Thickness pdfjs-editor-free-highlight-thickness-title = .title = Change thickness when highlighting items other than text +pdfjs-editor-signature-add-signature-button = + .title = Add new signature +pdfjs-editor-signature-add-signature-button-label = Add new signature # .default-content is used as a placeholder in an empty text editor. pdfjs-free-text2 = @@ -360,9 +368,12 @@ pdfjs-ink-canvas = ## Alt-text dialog # Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt text pdfjs-editor-alt-text-button-label = Alt text -pdfjs-editor-alt-text-edit-button-label = Edit alt text +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit alt text pdfjs-editor-alt-text-dialog-label = Choose an option pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. pdfjs-editor-alt-text-add-description-label = Add a description @@ -457,12 +468,18 @@ pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text A .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt text added pdfjs-editor-new-alt-text-added-button-label = Alt text added # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Missing alt text pdfjs-editor-new-alt-text-missing-button-label = Missing alt text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Review alt text pdfjs-editor-new-alt-text-to-review-button-label = Review alt text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. @@ -494,3 +511,101 @@ pdfjs-editor-alt-text-settings-editor-title = Alt text editor pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text. pdfjs-editor-alt-text-settings-close-button = Close + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Highlight removed +pdfjs-editor-undo-bar-message-freetext = Text removed +pdfjs-editor-undo-bar-message-ink = Drawing removed +pdfjs-editor-undo-bar-message-stamp = Image removed +pdfjs-editor-undo-bar-message-signature = Signature removed +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removed + *[other] { $count } annotations removed + } + +pdfjs-editor-undo-bar-undo-button = + .title = Undo +pdfjs-editor-undo-bar-undo-button-label = Undo +pdfjs-editor-undo-bar-close-button = + .title = Close +pdfjs-editor-undo-bar-close-button-label = Close + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = This modal allows the user to create a signature to add to a PDF document. The user can edit the name (which also serves as the alt text), and optionally save the signature for repeated use. +pdfjs-editor-add-signature-dialog-title = Add a signature + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Type + .title = Type +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Draw + .title = Draw +pdfjs-editor-add-signature-image-button = Image + .title = Image + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Type your signature + .placeholder = Type your signature +pdfjs-editor-add-signature-draw-placeholder = Draw your signature +pdfjs-editor-add-signature-draw-thickness-range-label = Thickness + +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Drawing thickness: { $thickness } + +pdfjs-editor-add-signature-image-placeholder = Drag a file here to upload +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Or choose image files + *[other] Or browse image files + } + +## Controls + +pdfjs-editor-add-signature-description-label = Description (alt text) +pdfjs-editor-add-signature-description-input = + .title = Description (alt text) +pdfjs-editor-add-signature-description-default-when-drawing = Signature + + +pdfjs-editor-add-signature-clear-button-label = Clear signature +pdfjs-editor-add-signature-clear-button = + .title = Clear signature +pdfjs-editor-add-signature-save-checkbox = Save signature +pdfjs-editor-add-signature-save-warning-message = You’ve reached the limit of 5 saved signatures. Remove one to save more. +pdfjs-editor-add-signature-image-upload-error-title = Couldn’t upload image +pdfjs-editor-add-signature-image-upload-error-description = Check your network connection or try another image. +pdfjs-editor-add-signature-error-close-button = Close + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancel +pdfjs-editor-add-signature-add-button = Add + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Remove signature +pdfjs-editor-delete-signature-button-label = Remove signature + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Edit description + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Edit description + +## Dialog buttons + +pdfjs-editor-edit-signature-update-button = Update diff --git a/l10n/eo/viewer.ftl b/l10n/eo/viewer.ftl index bb68201cab1c3..b43027a8b59f6 100644 --- a/l10n/eo/viewer.ftl +++ b/l10n/eo/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Elstarigi .aria-label = Elstarigi pdfjs-highlight-floating-button-label = Elstarigi +pdfjs-editor-signature-button = + .title = Aldoni subskribon +pdfjs-editor-signature-button-label = Aldoni subskribon ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Forigi bildon pdfjs-editor-remove-highlight-button = .title = Forigi elstaraĵon +pdfjs-editor-remove-signature-button = + .title = Forigi subskribon ## @@ -343,8 +348,15 @@ pdfjs-editor-stamp-add-image-button-label = Aldoni bildon pdfjs-editor-free-highlight-thickness-input = Dikeco pdfjs-editor-free-highlight-thickness-title = .title = Ŝanĝi dikecon dum elstarigo de netekstaj elementoj +pdfjs-editor-signature-add-signature-button = + .title = Aldoni novan subskribon +pdfjs-editor-signature-add-signature-button-label = Aldoni novan subskribon +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Teksta redaktilo + .default-content = Komencu tajpi… pdfjs-free-text = - .aria-label = Tekstan redaktilon + .aria-label = Teksta redaktilo pdfjs-free-text-default-content = Ektajpi… pdfjs-ink = .aria-label = Desegnan redaktilon @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternativa teksto +pdfjs-editor-alt-text-edit-button = + .aria-label = Redakti alternativan tekston pdfjs-editor-alt-text-edit-button-label = Redakti alternativan tekston pdfjs-editor-alt-text-dialog-label = Elektu eblon pdfjs-editor-alt-text-dialog-description = Alternativa teksto helpas personojn, en la okazoj kiam ili ne povas vidi aŭ ŝargi la bildon. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Markita kiel ornama # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Ekzemple: “Juna persono sidiĝas ĉetable por ekmanĝi” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativa teksto ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Fermi pdfjs-editor-new-alt-text-ai-model-downloading-progress = Elŝuto de modelo de artefarita intelekto por alternativa teksto ({ $downloadedSize } el { $totalSize } MO) .aria-valuetext = Elŝuto de modelo de artefarita intelekto por alternativa teksto ({ $downloadedSize } el { $totalSize } MO) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativa teksto aldonita pdfjs-editor-new-alt-text-added-button-label = Alternativa teksto aldonita # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mankas alternativa teksto pdfjs-editor-new-alt-text-missing-button-label = Mankas alternativa teksto # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Kontroli alternativan tekston pdfjs-editor-new-alt-text-to-review-button-label = Kontroli alternativan tekston # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Redaktilo de alternativa teksto pdfjs-editor-alt-text-settings-show-dialog-button-label = Montri redaktilon de alternativa teksto tuj post aldono de bildo pdfjs-editor-alt-text-settings-show-dialog-description = Tio ĉi helpas vin kontroli ĉu ĉiuj bildoj havas alternativan tekston. pdfjs-editor-alt-text-settings-close-button = Fermi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Elstaraĵo forigita +pdfjs-editor-undo-bar-message-freetext = Teksto forigita +pdfjs-editor-undo-bar-message-ink = Desegno forigita +pdfjs-editor-undo-bar-message-stamp = Bildo forigita +pdfjs-editor-undo-bar-message-signature = Subskribo forigita +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] unu prinoto forigita + *[other] { $count } prinotoj forigitaj + } +pdfjs-editor-undo-bar-undo-button = + .title = Malfari +pdfjs-editor-undo-bar-undo-button-label = Malfari +pdfjs-editor-undo-bar-close-button = + .title = Fermi +pdfjs-editor-undo-bar-close-button-label = Fermi + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Tiu ĉi fenestro permesas al la uzanto krei subskribon por aldoni al dokumento PDF. La uzanto povas modifi la nomon (kiu estas cetere la alternativa teksto) kaj havas la eblon konservi la subskribon por posta uzo. +pdfjs-editor-add-signature-dialog-title = Aldoni subskribon + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Tajpi + .title = Tajpi +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Desegni + .title = Desegni +pdfjs-editor-add-signature-image-button = Bildo + .title = Bildo + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Tajpu vian subskribon + .placeholder = Tajpu vian subskribon +pdfjs-editor-add-signature-draw-placeholder = Desegni vian subskribon +pdfjs-editor-add-signature-draw-thickness-range-label = Dikeco +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Dikeco de desegno: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Trenu dosieron ĉi tien por alŝuti ĝin +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Aŭ elektu bildan dosieron + *[other] Aŭ elektu bildan dosieron + } + +## Controls + +pdfjs-editor-add-signature-description-label = Priskribo (alternativa teksto) +pdfjs-editor-add-signature-description-input = + .title = Priskribo (alternativa teksto) +pdfjs-editor-add-signature-description-default-when-drawing = Subskribo +pdfjs-editor-add-signature-clear-button-label = Viŝi subskribon +pdfjs-editor-add-signature-clear-button = + .title = Viŝi subskribon +pdfjs-editor-add-signature-save-checkbox = Konservi subskribon +pdfjs-editor-add-signature-save-warning-message = Vi atingis la limon de kvin konservitaj subskriboj. Forigi unu por povi konservi pli da. +pdfjs-editor-add-signature-image-upload-error-title = Ne eblis alŝuti bildon +pdfjs-editor-add-signature-image-upload-error-description = Kontrolu vian retaliron aŭ provu alŝuti alian bildon. +pdfjs-editor-add-signature-error-close-button = Fermi + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Nuligi +pdfjs-editor-add-signature-add-button = Aldoni +pdfjs-editor-edit-signature-update-button = Ĝisdatigi + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Forigi subskribon +pdfjs-editor-delete-signature-button-label = Forigi subskribon + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Modifi priskribon + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Modifi priskribon diff --git a/l10n/es-AR/viewer.ftl b/l10n/es-AR/viewer.ftl index 33f88f6b838c1..9e7bf9452385f 100644 --- a/l10n/es-AR/viewer.ftl +++ b/l10n/es-AR/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Resaltar .aria-label = Resaltar pdfjs-highlight-floating-button-label = Resaltar +pdfjs-editor-signature-button = + .title = Agregar firma +pdfjs-editor-signature-button-label = Agregar firma ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Eliminar imagen pdfjs-editor-remove-highlight-button = .title = Eliminar resaltado +pdfjs-editor-remove-signature-button = + .title = Eliminar firma ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Agregar una imagen pdfjs-editor-free-highlight-thickness-input = Grosor pdfjs-editor-free-highlight-thickness-title = .title = Cambiar el grosor al resaltar elementos que no sean texto +pdfjs-editor-signature-add-signature-button = + .title = Agregar nueva firma +pdfjs-editor-signature-add-signature-button-label = Agregar nueva firma +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comenzar a tipear… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Empezar a tipear… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo pdfjs-editor-alt-text-dialog-label = Eligir una opción pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Cerrar pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = Descargando modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo agregado pdfjs-editor-new-alt-text-added-button-label = Texto alternativo agregado # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Calificar el texto alternativo pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al agregar una imagen pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarse de que todas las imágenes tengan texto alternativo. pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminado +pdfjs-editor-undo-bar-message-signature = Firma eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Este modal permite al usuario crear una firma para agregar a un documento PDF. El usuario puede editar el nombre (que también sirve como texto alternativo) y opcionalmente guardar la firma para un uso repetido. +pdfjs-editor-add-signature-dialog-title = Agregar una firma + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Tipear + .title = Tipear +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Dibujar + .title = Dibujar +pdfjs-editor-add-signature-image-button = Imagen + .title = Imagen + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Escribir la firma + .placeholder = Escribir la firma +pdfjs-editor-add-signature-draw-placeholder = Dibujar la firma +pdfjs-editor-add-signature-draw-thickness-range-label = Grosor +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Grosor del dibujo: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Arrastrar un archivo acá para subirlo +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] O seleccionar archivos de imágenes + *[other] O seleccionar archivos de imágenes + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descripción (texto alternativo) +pdfjs-editor-add-signature-description-input = + .title = Descripción (texto alternativo) +pdfjs-editor-add-signature-description-default-when-drawing = Firma +pdfjs-editor-add-signature-clear-button-label = Borrar firma +pdfjs-editor-add-signature-clear-button = + .title = Borrar firma +pdfjs-editor-add-signature-save-checkbox = Guardar firma +pdfjs-editor-add-signature-save-warning-message = Se alcanzó el límite de 5 firmas guardadas. Elimine una para guardar más. +pdfjs-editor-add-signature-image-upload-error-title = No se pudo subir la imagen +pdfjs-editor-add-signature-image-upload-error-description = Verifique la conexión de red o pruebe con otra imagen. +pdfjs-editor-add-signature-error-close-button = Cerrar + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancelar +pdfjs-editor-add-signature-add-button = Agregar +pdfjs-editor-edit-signature-update-button = Actualizar + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Eliminar firma +pdfjs-editor-delete-signature-button-label = Eliminar firma + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Editar descripción + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Editar descripción diff --git a/l10n/es-CL/viewer.ftl b/l10n/es-CL/viewer.ftl index 51dbb2241ed3f..c7fb5f220050b 100644 --- a/l10n/es-CL/viewer.ftl +++ b/l10n/es-CL/viewer.ftl @@ -292,7 +292,7 @@ pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", tim ## Password -pdfjs-password-label = Ingrese la contraseña para abrir este archivo PDF. +pdfjs-password-label = Ingresa la contraseña para abrir este archivo PDF. pdfjs-password-invalid = Contraseña inválida. Por favor, vuelve a intentarlo. pdfjs-password-ok-button = Aceptar pdfjs-password-cancel-button = Cancelar @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Destacar .aria-label = Destacar pdfjs-highlight-floating-button-label = Destacar +pdfjs-editor-signature-button = + .title = Añadir firma +pdfjs-editor-signature-button-label = Añadir firma ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Eliminar imagen pdfjs-editor-remove-highlight-button = .title = Quitar resaltado +pdfjs-editor-remove-signature-button = + .title = Eliminar firma ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Añadir imagen pdfjs-editor-free-highlight-thickness-input = Grosor pdfjs-editor-free-highlight-thickness-title = .title = Cambia el grosor al resaltar elementos que no sean texto +pdfjs-editor-signature-add-signature-button = + .title = Añadir nueva firma +pdfjs-editor-signature-add-signature-button-label = Añadir nueva firma +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Empieza a escribir… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Empieza a escribir… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo pdfjs-editor-alt-text-dialog-label = Elige una opción pdfjs-editor-alt-text-dialog-description = El texto alternativo (alt text) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Cerrar pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se añadió el texto alternativo pdfjs-editor-new-alt-text-added-button-label = Se añadió el texto alternativo # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarte de que todas tus imágenes tengan texto alternativo. pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +pdfjs-editor-undo-bar-message-signature = Firma eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Este modal permite al usuario crear una firma para agregarla a un documento PDF. El usuario puede editar el nombre (que también sirve como texto alternativo) y, opcionalmente, guardar la firma para usarla nuevamente. +pdfjs-editor-add-signature-dialog-title = Añadir una firma + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Escribir + .title = Escribir +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Dibujar + .title = Dibujar +pdfjs-editor-add-signature-image-button = Imagen + .title = Imagen + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Escribe tu firma + .placeholder = Escribe tu firma +pdfjs-editor-add-signature-draw-placeholder = Dibuja tu firma +pdfjs-editor-add-signature-draw-thickness-range-label = Grosor +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Grosor del dibujo: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Arrastre un archivo aquí para cargarlo +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] O elige archivos de imagen + *[other] O busca archivos de imagen + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descripción (texto alternativo) +pdfjs-editor-add-signature-description-input = + .title = Descripción (texto alternativo) +pdfjs-editor-add-signature-description-default-when-drawing = Firma +pdfjs-editor-add-signature-clear-button-label = Limpiar firma +pdfjs-editor-add-signature-clear-button = + .title = Limpiar firma +pdfjs-editor-add-signature-save-checkbox = Guardar firma +pdfjs-editor-add-signature-save-warning-message = Has alcanzado el límite de 5 firmas guardadas. Elimina una para guardar más. +pdfjs-editor-add-signature-image-upload-error-title = No se pudo subir la imagen +pdfjs-editor-add-signature-image-upload-error-description = Verifica tu conexión de red o prueba con otra imagen. +pdfjs-editor-add-signature-error-close-button = Cerrar + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancelar +pdfjs-editor-add-signature-add-button = Añadir +pdfjs-editor-edit-signature-update-button = Actualizar + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Eliminar firma +pdfjs-editor-delete-signature-button-label = Eliminar firma + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Editar descripción + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Editar descripción diff --git a/l10n/es-ES/viewer.ftl b/l10n/es-ES/viewer.ftl index 43df2afc18c88..b378246f11c85 100644 --- a/l10n/es-ES/viewer.ftl +++ b/l10n/es-ES/viewer.ftl @@ -343,6 +343,10 @@ pdfjs-editor-stamp-add-image-button-label = Añadir imagen pdfjs-editor-free-highlight-thickness-input = Grosor pdfjs-editor-free-highlight-thickness-title = .title = Cambiar el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Empiece a escribir… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Empezar a escribir… @@ -353,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar el texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo pdfjs-editor-alt-text-dialog-label = Eligir una opción pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. @@ -368,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = Cerrar pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se añadió el texto alternativo pdfjs-editor-new-alt-text-added-button-label = Se añadió el texto alternativo # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo pdfjs-editor-new-alt-text-missing-button-label = Falta el texto alternativo # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +493,47 @@ pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen pdfjs-editor-alt-text-settings-show-dialog-description = Le ayuda a asegurarse de que todas sus imágenes tengan texto alternativo. pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/es-MX/viewer.ftl b/l10n/es-MX/viewer.ftl index 6487dcc32e00f..45574d50c50cc 100644 --- a/l10n/es-MX/viewer.ftl +++ b/l10n/es-MX/viewer.ftl @@ -105,6 +105,14 @@ pdfjs-document-properties-button-label = Propiedades del documento… pdfjs-document-properties-file-name = Nombre del archivo: pdfjs-document-properties-file-size = Tamaño del archivo: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes) @@ -119,6 +127,9 @@ pdfjs-document-properties-keywords = Palabras claves: pdfjs-document-properties-creation-date = Fecha de creación: pdfjs-document-properties-modification-date = Fecha de modificación: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } @@ -275,6 +286,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [{ $type } anotación] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -329,6 +343,10 @@ pdfjs-editor-stamp-add-image-button-label = Agregar imagen pdfjs-editor-free-highlight-thickness-input = Espesor pdfjs-editor-free-highlight-thickness-title = .title = Cambiar el grosor al resaltar elementos que no sean texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comenzar a escribir… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Empieza a escribir… @@ -339,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo pdfjs-editor-alt-text-dialog-label = Elige una opción pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga. @@ -354,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -366,6 +388,22 @@ pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha: cambiar el t pdfjs-editor-resizer-label-bottom-middle = Abajo en el medio: cambiar el tamaño pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda: cambiar el tamaño pdfjs-editor-resizer-label-middle-left = Centro izquierda: cambiar el tamaño +pdfjs-editor-resizer-top-left = + .aria-label = Esquina superior izquierda — redimensionar +pdfjs-editor-resizer-top-middle = + .aria-label = Borde superior en el medio — redimensionar +pdfjs-editor-resizer-top-right = + .aria-label = Esquina superior derecha — redimensionar +pdfjs-editor-resizer-middle-right = + .aria-label = Borde derecho en el medio — redimensionar +pdfjs-editor-resizer-bottom-right = + .aria-label = Esquina inferior derecha — redimensionar +pdfjs-editor-resizer-bottom-middle = + .aria-label = Borde inferior en el medio — redimensionar +pdfjs-editor-resizer-bottom-left = + .aria-label = Esquina inferior izquierda — redimensionar +pdfjs-editor-resizer-middle-left = + .aria-label = Borde izquierdo en el medio — redimensionar ## Color picker @@ -396,6 +434,106 @@ pdfjs-editor-highlight-show-all-button = ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descripción de la imagen) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Agregar texto alternativo (descripción de la imagen) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Escribe tu descripción aquí… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Breve descripción para las personas que no pueden ver la imagen o cuando la imagen no se carga. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Este texto alternativo fue creado automáticamente y puede ser inexacto. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saber más +pdfjs-editor-new-alt-text-create-automatically-button-label = Crear texto alternativo automáticamente +pdfjs-editor-new-alt-text-not-now-button = Ahora no +pdfjs-editor-new-alt-text-error-title = No se pudo crear el texto alternativo automáticamente +pdfjs-editor-new-alt-text-error-description = Escribe tu propio texto alternativo o inténtalo de nuevo más tarde. +pdfjs-editor-new-alt-text-error-close-button = Cerrar +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) + .aria-valuetext = Descargando el modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Se agregó el texto alternativo +pdfjs-editor-new-alt-text-added-button-label = Se agregó el texto alternativo +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Falta el texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Falta texto alternativo +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar el texto alternativo +pdfjs-editor-new-alt-text-to-review-button-label = Revisar el texto alternativo +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Creado automáticamente: { $generatedAltText } ## Image alt-text settings +pdfjs-image-alt-text-settings-button = + .title = Ajustes del texto alternativo de la imagen +pdfjs-image-alt-text-settings-button-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-dialog-label = Ajustes del texto alternativo de la imagen +pdfjs-editor-alt-text-settings-automatic-title = Texto alternativo automático +pdfjs-editor-alt-text-settings-create-model-button-label = Crear texto alternativo automáticamente +pdfjs-editor-alt-text-settings-create-model-description = Sugiere descripciones para ayudar a las personas que no pueden ver la imagen o cuando la imagen no se carga. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Modelo de IA de texto alternativo ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Se ejecuta localmente en el dispositivo para que los datos se mantengan privados. Requerido para texto alternativo automático. +pdfjs-editor-alt-text-settings-delete-model-button = Eliminar +pdfjs-editor-alt-text-settings-download-model-button = Descargar +pdfjs-editor-alt-text-settings-downloading-model-button = Descargando… +pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo +pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar el editor de texto alternativo inmediatamente al añadir una imagen +pdfjs-editor-alt-text-settings-show-dialog-description = Te ayuda a asegurarte de que todas tus imágenes tengan texto alternativo. +pdfjs-editor-alt-text-settings-close-button = Cerrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Resaltado eliminado +pdfjs-editor-undo-bar-message-freetext = Texto eliminado +pdfjs-editor-undo-bar-message-ink = Dibujo eliminado +pdfjs-editor-undo-bar-message-stamp = Imagen eliminada +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotación eliminada + *[other] { $count } anotaciones eliminadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Deshacer +pdfjs-editor-undo-bar-undo-button-label = Deshacer +pdfjs-editor-undo-bar-close-button = + .title = Cerrar +pdfjs-editor-undo-bar-close-button-label = Cerrar + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/et/viewer.ftl b/l10n/et/viewer.ftl index b28c6d5041226..d08906b5e6e87 100644 --- a/l10n/et/viewer.ftl +++ b/l10n/et/viewer.ftl @@ -260,9 +260,56 @@ pdfjs-web-fonts-disabled = Veebifondid on keelatud: PDFiga kaasatud fonte pole v ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/eu/viewer.ftl b/l10n/eu/viewer.ftl index 7675ffea6a6a8..03f55debed183 100644 --- a/l10n/eu/viewer.ftl +++ b/l10n/eu/viewer.ftl @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Kendu irudia pdfjs-editor-remove-highlight-button = .title = Kendu nabarmentzea +pdfjs-editor-remove-signature-button = + .title = Kendu sinadura ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Gehitu irudia pdfjs-editor-free-highlight-thickness-input = Loditasuna pdfjs-editor-free-highlight-thickness-title = .title = Aldatu loditasuna testua ez beste elementuak nabarmentzean +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Testu-editorea + .default-content = Hasi idazten… pdfjs-free-text = .aria-label = Testu-editorea pdfjs-free-text-default-content = Hasi idazten… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Testu alternatiboa +pdfjs-editor-alt-text-edit-button = + .aria-label = Editatu testu alternatiboa pdfjs-editor-alt-text-edit-button-label = Editatu testu alternatiboa pdfjs-editor-alt-text-dialog-label = Aukeratu aukera pdfjs-editor-alt-text-dialog-description = Testu alternatiboak laguntzen du jendeak ezin duenean irudia ikusi edo ez denean kargatzen. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Apaingarri gisa markatuta # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Adibidez, "gizon gaztea mahaian eserita dago bazkaltzeko" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testu alternatiboa ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Itxi pdfjs-editor-new-alt-text-ai-model-downloading-progress = Testu alternatiboaren AA modeloa deskargatzen ({ $totalSize }/{ $downloadedSize } MB) .aria-valuetext = Testu alternatiboaren AA modeloa deskargatzen ({ $totalSize }/{ $downloadedSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Testu alternatiboa gehituta pdfjs-editor-new-alt-text-added-button-label = Testu alternatiboa gehituta # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testu alternatiboa falta da pdfjs-editor-new-alt-text-missing-button-label = Testu alternatiboa falta da # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Berrikusi testu alternatiboa pdfjs-editor-new-alt-text-to-review-button-label = Berrikusi testu alternatiboa # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,91 @@ pdfjs-editor-alt-text-settings-editor-title = Testu alternatiboaren editorea pdfjs-editor-alt-text-settings-show-dialog-button-label = Erakutsi testu alternatiboa irudi bat gehitzean berehala pdfjs-editor-alt-text-settings-show-dialog-description = Zure irudiek testu alternatiboa duela ziurtatzen laguntzen dizu. pdfjs-editor-alt-text-settings-close-button = Itxi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Nabarmentzea kenduta +pdfjs-editor-undo-bar-message-freetext = Testua kenduta +pdfjs-editor-undo-bar-message-ink = Marrazkia kenduta +pdfjs-editor-undo-bar-message-stamp = Irudia kenduta +pdfjs-editor-undo-bar-message-signature = Sinadura kenduta +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Esku-ohar bat kenduta + *[other] { $count } esku-ohar kenduta + } +pdfjs-editor-undo-bar-undo-button = + .title = Desegin +pdfjs-editor-undo-bar-undo-button-label = Desegin +pdfjs-editor-undo-bar-close-button = + .title = Itxi +pdfjs-editor-undo-bar-close-button-label = Itxi + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = + Leiho modal honek PDF dokumentu batera gehitzeko sinadurak + sortzea ahalbidetzen dio erabiltzaileari. Erabiltzaileak izena edita + dezake (testu alternatibo modura ere erabiltzen dena) eta sinadura + gordetzeko aukera du gehiagotan erabili ahal izateko. +pdfjs-editor-add-signature-dialog-title = Gehitu sinadura + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Mota + .title = Mota +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Marraztu + .title = Marraztu +pdfjs-editor-add-signature-image-button = Irudia + .title = Irudia + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Idatzi zure sinadura + .placeholder = Idatzi zure sinadura +pdfjs-editor-add-signature-draw-placeholder = Marraztu zure sinadura +pdfjs-editor-add-signature-draw-thickness-range-label = Loditasuna +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Marrazteko loditasuna: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Igotzeko, jaregin fitxategia hemen +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Edo aukeratu irudi-fitxategiak + *[other] Edo arakatu irudi-fitxategiak + } + +## Controls + +pdfjs-editor-add-signature-description-label = Azalpena (testu alternatiboa) +pdfjs-editor-add-signature-description-input = + .title = Azalpena (testu alternatiboa) +pdfjs-editor-add-signature-description-default-when-drawing = Sinadura +pdfjs-editor-add-signature-clear-button-label = Garbitu sinadura +pdfjs-editor-add-signature-clear-button = + .title = Garbitu sinadura +pdfjs-editor-add-signature-save-checkbox = Gorde sinadura +pdfjs-editor-add-signature-save-warning-message = Gordetako sinadura kopuruaren mugara heldu zara (5). Gehiago gorde ahal izateko, ken ezazu bat. +pdfjs-editor-add-signature-image-upload-error-title = Ezin da irudia igo +pdfjs-editor-add-signature-image-upload-error-description = Egiaztatu zure sareko konexioa edo saiatu beste irudi batekin. +pdfjs-editor-add-signature-error-close-button = Itxi + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Utzi +pdfjs-editor-add-signature-add-button = Gehitu + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/fa/viewer.ftl b/l10n/fa/viewer.ftl index f367e3c6479b9..0ac2a1c760257 100644 --- a/l10n/fa/viewer.ftl +++ b/l10n/fa/viewer.ftl @@ -39,7 +39,18 @@ pdfjs-open-file-button-label = باز کردن pdfjs-print-button = .title = چاپ pdfjs-print-button-label = چاپ +pdfjs-save-button = + .title = ذخیره pdfjs-save-button-label = ذخیره +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = دریافت +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = دریافت +pdfjs-bookmark-button = + .title = صفحه فعلی (مشاهده نشانی اینترنتی از صفحه فعلی) +pdfjs-bookmark-button-label = صفحه فعلی ## Secondary toolbar and context menu @@ -64,21 +75,35 @@ pdfjs-cursor-text-select-tool-button-label = ابزارِ انتخابِ متن pdfjs-cursor-hand-tool-button = .title = فعال کردن ابزارِ دست pdfjs-cursor-hand-tool-button-label = ابزار دست +pdfjs-scroll-page-button = + .title = استفاده از پیمایش صفحه +pdfjs-scroll-page-button-label = پیمایش صفحه pdfjs-scroll-vertical-button = .title = استفاده از پیمایش عمودی pdfjs-scroll-vertical-button-label = پیمایش عمودی pdfjs-scroll-horizontal-button = .title = استفاده از پیمایش افقی pdfjs-scroll-horizontal-button-label = پیمایش افقی +pdfjs-spread-none-button = + .title = صفحات پیوسته را یکی نکنید +pdfjs-spread-none-button-label = بدون صفحات پیوسته ## Document properties dialog pdfjs-document-properties-button = .title = خصوصیات سند... pdfjs-document-properties-button-label = خصوصیات سند... -pdfjs-document-properties-file-name = نام فایل: +pdfjs-document-properties-file-name = نام پرونده: pdfjs-document-properties-file-size = حجم پرونده: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } کیلوبایت ({ $b } بایت) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } مگابایت ({ $b } بایت) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } کیلوبایت ({ $size_b } بایت) @@ -93,6 +118,9 @@ pdfjs-document-properties-keywords = کلیدواژه‌ها: pdfjs-document-properties-creation-date = تاریخ ایجاد: pdfjs-document-properties-modification-date = تاریخ ویرایش: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }، { $time } @@ -209,6 +237,10 @@ pdfjs-rendering-error = هنگام بارگیری صفحه خطایی رخ دا ## Annotations +# Variables: +# $date (Date) - the modification date of the annotation +# $time (Time) - the modification time of the annotation +pdfjs-annotation-date-string = { $date }، { $time } # .alt: This is used as a tooltip. # Variables: # $type (String) - an annotation type from a list defined in the PDF spec @@ -216,6 +248,9 @@ pdfjs-rendering-error = هنگام بارگیری صفحه خطایی رخ دا # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [{ $type } Annotation] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -233,14 +268,108 @@ pdfjs-editor-free-text-button-label = متن pdfjs-editor-ink-button = .title = کشیدن pdfjs-editor-ink-button-label = کشیدن +pdfjs-editor-stamp-button = + .title = افزودن یا ویرایش تصاویر +pdfjs-editor-stamp-button-label = افزودن یا ویرایش تصاویر +pdfjs-editor-highlight-button = + .title = برجسته کردن +pdfjs-editor-highlight-button-label = برجسته کردن +pdfjs-highlight-floating-button1 = + .title = برجسته کردن + .aria-label = برجسته کردن +pdfjs-highlight-floating-button-label = برجسته کردن + +## Remove button for the various kind of editor. + + +## + # Editor Parameters pdfjs-editor-free-text-color-input = رنگ pdfjs-editor-free-text-size-input = اندازه pdfjs-editor-ink-color-input = رنگ +pdfjs-editor-stamp-add-image-button = + .title = افزودن تصویر +pdfjs-editor-stamp-add-image-button-label = افزودن تصویر +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ویرایشگر متن + .default-content = شروع به نوشتن کنید… +pdfjs-free-text = + .aria-label = ویرایشگر متن +pdfjs-free-text-default-content = شروع به نوشتن کنید… ## Alt-text dialog +pdfjs-editor-alt-text-add-description-label = افزودن توضیحات +pdfjs-editor-alt-text-cancel-button = انصراف +pdfjs-editor-alt-text-save-button = ذخیره ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + +pdfjs-editor-colorpicker-button = + .title = تغییر رنگ +pdfjs-editor-colorpicker-dropdown = + .aria-label = انتخاب رنگ +pdfjs-editor-colorpicker-yellow = + .title = زرد +pdfjs-editor-colorpicker-green = + .title = سبز +pdfjs-editor-colorpicker-blue = + .title = آبی +pdfjs-editor-colorpicker-pink = + .title = صورتی +pdfjs-editor-colorpicker-red = + .title = قرمز + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = نمایش همه +pdfjs-editor-highlight-show-all-button = + .title = نمایش همه + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = بیشتر بدانید +pdfjs-editor-new-alt-text-not-now-button = اکنون نه +pdfjs-editor-new-alt-text-error-close-button = بستن + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-delete-model-button = حذف +pdfjs-editor-alt-text-settings-download-model-button = دریافت +pdfjs-editor-alt-text-settings-downloading-model-button = در حال دریافت… +pdfjs-editor-alt-text-settings-close-button = بستن + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ff/viewer.ftl b/l10n/ff/viewer.ftl index d1419f54340a1..9ae6ef1cbcfbe 100644 --- a/l10n/ff/viewer.ftl +++ b/l10n/ff/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = Ponte geese ko daaƴaaɗe: horiima huutoraade ponte P ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/fi/viewer.ftl b/l10n/fi/viewer.ftl index 88c96de8f3b64..b18bc33bb4c3a 100644 --- a/l10n/fi/viewer.ftl +++ b/l10n/fi/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Korostus .aria-label = Korostus pdfjs-highlight-floating-button-label = Korostus +pdfjs-editor-signature-button = + .title = Lisää allekirjoitus +pdfjs-editor-signature-button-label = Lisää allekirjoitus ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Poista kuva pdfjs-editor-remove-highlight-button = .title = Poista korostus +pdfjs-editor-remove-signature-button = + .title = Poista allekirjoitus ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Lisää kuva pdfjs-editor-free-highlight-thickness-input = Paksuus pdfjs-editor-free-highlight-thickness-title = .title = Muuta paksuutta korostaessasi muita kohteita kuin tekstiä +pdfjs-editor-signature-add-signature-button = + .title = Lisää uusi allekirjoitus +pdfjs-editor-signature-add-signature-button-label = Lisää uusi allekirjoitus +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstimuokkain + .default-content = Aloita kirjoittaminen… pdfjs-free-text = .aria-label = Tekstimuokkain pdfjs-free-text-default-content = Aloita kirjoittaminen… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Vaihtoehtoinen teksti +pdfjs-editor-alt-text-edit-button = + .aria-label = Muokkaa vaihtoehtoista tekstiä pdfjs-editor-alt-text-edit-button-label = Muokkaa vaihtoehtoista tekstiä pdfjs-editor-alt-text-dialog-label = Valitse vaihtoehto pdfjs-editor-alt-text-dialog-description = Vaihtoehtoinen teksti ("alt-teksti") auttaa ihmisiä, jotka eivät näe kuvaa tai kun kuva ei lataudu. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Merkitty koristeelliseksi # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Esimerkiksi "Nuori mies istuu pöytään syömään aterian" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Vaihtoehtoinen teksti ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Sulje pdfjs-editor-new-alt-text-ai-model-downloading-progress = Ladataan vaihtoehtoisen tekstin tekoälymallia ({ $downloadedSize } / { $totalSize } Mt) .aria-valuetext = Ladataan vaihtoehtoisen tekstin tekoälymallia ({ $downloadedSize } / { $totalSize } Mt) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Vaihtoehtoinen teksti lisätty pdfjs-editor-new-alt-text-added-button-label = Vaihtoehtoinen teksti lisätty # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Vaihtoehtoinen teksti puuttuu pdfjs-editor-new-alt-text-missing-button-label = Vaihtoehtoinen teksti puuttuu # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Tarkista vaihtoehtoinen teksti pdfjs-editor-new-alt-text-to-review-button-label = Tarkista vaihtoehtoinen teksti # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Vaihtoehtoisen tekstin muokkain pdfjs-editor-alt-text-settings-show-dialog-button-label = Näytä vaihtoehtoisen tekstin muokkain heti, kun lisäät kuvan pdfjs-editor-alt-text-settings-show-dialog-description = Auttaa varmistamaan, että kaikissa kuvissasi on vaihtoehtoinen teksti. pdfjs-editor-alt-text-settings-close-button = Sulje + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Korostus poistettu +pdfjs-editor-undo-bar-message-freetext = Teksti poistettu +pdfjs-editor-undo-bar-message-ink = Piirustus poistettu +pdfjs-editor-undo-bar-message-stamp = Kuva poistettu +pdfjs-editor-undo-bar-message-signature = Allekirjoitus poistettu +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } merkintä poistettu + *[other] { $count } merkintää poistettu + } +pdfjs-editor-undo-bar-undo-button = + .title = Kumoa +pdfjs-editor-undo-bar-undo-button-label = Kumoa +pdfjs-editor-undo-bar-close-button = + .title = Sulje +pdfjs-editor-undo-bar-close-button-label = Sulje + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Tämän ikkunan avulla käyttäjä voi luoda allekirjoituksen PDF-asiakirjaan lisättäväksi. Käyttäjä voi muokata nimeä (joka toimii myös vaihtoehtoisena tekstinä) ja valinnaisesti tallentaa allekirjoituksen toistuvaa käyttöä varten. +pdfjs-editor-add-signature-dialog-title = Lisää allekirjoitus + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Kirjoita + .title = Kirjoita +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Piirrä + .title = Piirrä +pdfjs-editor-add-signature-image-button = Kuva + .title = Kuva + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Kirjoita allekirjoituksesi + .placeholder = Kirjoita allekirjoituksesi +pdfjs-editor-add-signature-draw-placeholder = Piirrä allekirjoituksesi +pdfjs-editor-add-signature-draw-thickness-range-label = Paksuus +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Piirustuksen paksuus: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Lähetä tiedosto vetämällä se tähän +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Tai selaa kuvatiedostoja + *[other] Tai selaa kuvatiedostoja + } + +## Controls + +pdfjs-editor-add-signature-description-label = Kuvaus (vaihtoehtoinen teksti) +pdfjs-editor-add-signature-description-input = + .title = Kuvaus (vaihtoehtoinen teksti) +pdfjs-editor-add-signature-description-default-when-drawing = Allekirjoitus +pdfjs-editor-add-signature-clear-button-label = Tyhjennä allekirjoitus +pdfjs-editor-add-signature-clear-button = + .title = Tyhjennä allekirjoitus +pdfjs-editor-add-signature-save-checkbox = Tallenna allekirjoitus +pdfjs-editor-add-signature-save-warning-message = Olet saavuttanut viiden tallennetun allekirjoituksen rajan. Poista yksi säästääksesi lisää. +pdfjs-editor-add-signature-image-upload-error-title = Kuvaa ei voitu lähettää +pdfjs-editor-add-signature-image-upload-error-description = Tarkista verkkoyhteyden tila tai kokeile toista kuvaa. +pdfjs-editor-add-signature-error-close-button = Sulje + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Peruuta +pdfjs-editor-add-signature-add-button = Lisää +pdfjs-editor-edit-signature-update-button = Päivitä + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Poista allekirjoitus +pdfjs-editor-delete-signature-button-label = Poista allekirjoitus + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Muokkaa kuvausta + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Muokkaa kuvausta diff --git a/l10n/fr/viewer.ftl b/l10n/fr/viewer.ftl index 23d82501d4ef3..3a1187c37995c 100644 --- a/l10n/fr/viewer.ftl +++ b/l10n/fr/viewer.ftl @@ -312,6 +312,9 @@ pdfjs-highlight-floating-button1 = .title = Surligner .aria-label = Surligner pdfjs-highlight-floating-button-label = Surligner +pdfjs-editor-signature-button = + .title = Ajouter une signature +pdfjs-editor-signature-button-label = Ajouter une signature ## Remove button for the various kind of editor. @@ -323,6 +326,8 @@ pdfjs-editor-remove-stamp-button = .title = Supprimer l’image pdfjs-editor-remove-highlight-button = .title = Supprimer le surlignage +pdfjs-editor-remove-signature-button = + .title = Retirer la signature ## @@ -339,6 +344,13 @@ pdfjs-editor-stamp-add-image-button-label = Ajouter une image pdfjs-editor-free-highlight-thickness-input = Épaisseur pdfjs-editor-free-highlight-thickness-title = .title = Modifier l’épaisseur pour le surlignage d’éléments non textuels +pdfjs-editor-signature-add-signature-button = + .title = Ajouter une nouvelle signature +pdfjs-editor-signature-add-signature-button-label = Ajouter une nouvelle signature +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Éditeur de texte + .default-content = Commencez à écrire… pdfjs-free-text = .aria-label = Éditeur de texte pdfjs-free-text-default-content = Commencer à écrire… @@ -349,8 +361,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texte alternatif +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifier le texte alternatif pdfjs-editor-alt-text-edit-button-label = Modifier le texte alternatif pdfjs-editor-alt-text-dialog-label = Sélectionnez une option pdfjs-editor-alt-text-dialog-description = Le texte alternatif est utile lorsque des personnes ne peuvent pas voir l’image ou que l’image ne se charge pas. @@ -364,6 +377,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marquée comme décorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Par exemple, « Un jeune homme est assis à une table pour prendre un repas » +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texte alternatif ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -445,10 +461,16 @@ pdfjs-editor-new-alt-text-error-close-button = Fermer pdfjs-editor-new-alt-text-ai-model-downloading-progress = Téléchargement du modèle d’IA de texte alternatif ({ $downloadedSize } sur { $totalSize } Mo) .aria-valuetext = Téléchargement du modèle d’IA de texte alternatif ({ $downloadedSize } sur { $totalSize } Mo) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texte alternatif ajouté pdfjs-editor-new-alt-text-added-button-label = Texte alternatif ajouté # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texte alternatif manquant pdfjs-editor-new-alt-text-missing-button-label = Texte alternatif manquant # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Réviser le texte alternatif pdfjs-editor-new-alt-text-to-review-button-label = Réviser le texte alternatif # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -475,3 +497,93 @@ pdfjs-editor-alt-text-settings-editor-title = Éditeur de texte alternatif pdfjs-editor-alt-text-settings-show-dialog-button-label = Afficher l’éditeur de texte alternatif immédiatement lors de l’ajout d’une image pdfjs-editor-alt-text-settings-show-dialog-description = Vous aide à vous assurer que toutes vos images ont du texte alternatif. pdfjs-editor-alt-text-settings-close-button = Fermer + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Surlignage supprimé +pdfjs-editor-undo-bar-message-freetext = Texte supprimé +pdfjs-editor-undo-bar-message-ink = Dessin supprimé +pdfjs-editor-undo-bar-message-stamp = Image supprimée +pdfjs-editor-undo-bar-message-signature = Signature retirée +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation supprimée + *[other] { $count } annotations supprimées + } +pdfjs-editor-undo-bar-undo-button = + .title = Annuler +pdfjs-editor-undo-bar-undo-button-label = Annuler +pdfjs-editor-undo-bar-close-button = + .title = Fermer +pdfjs-editor-undo-bar-close-button-label = Fermer + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Cette fenêtre permet de créer une signature à ajouter à un document au format PDF. Il est possible d’en modifier le nom (qui sert également de texte alternatif) et, éventuellement, de l’enregistrer pour une utilisation répétée. +pdfjs-editor-add-signature-dialog-title = Ajout d’une signature + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Saisir + .title = Saisir au clavier +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Dessiner + .title = Dessiner +pdfjs-editor-add-signature-image-button = Image + .title = Image + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Saisissez votre signature + .placeholder = Saisissez votre signature +pdfjs-editor-add-signature-draw-placeholder = Tracez votre signature +pdfjs-editor-add-signature-draw-thickness-range-label = Épaisseur +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Épaisseur du trait : { $thickness } +pdfjs-editor-add-signature-image-placeholder = Déposez un fichier ici pour l’envoyer +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ou choisissez parmi les fichiers image + *[other] Ou parcourez les fichiers image + } + +## Controls + +pdfjs-editor-add-signature-description-label = Description (texte alternatif) +pdfjs-editor-add-signature-description-input = + .title = Description (texte alternatif) +pdfjs-editor-add-signature-description-default-when-drawing = Signature +pdfjs-editor-add-signature-clear-button-label = Effacer la signature +pdfjs-editor-add-signature-clear-button = + .title = Effacer la signature +pdfjs-editor-add-signature-save-checkbox = Enregistrer la signature +pdfjs-editor-add-signature-save-warning-message = Vous avez atteint la limite de 5 signatures enregistrées. Supprimez-en une pour en enregistrer une autre. +pdfjs-editor-add-signature-image-upload-error-title = Impossible d’envoyer l’image +pdfjs-editor-add-signature-image-upload-error-description = Vérifiez votre connexion réseau ou essayez avec une autre image. +pdfjs-editor-add-signature-error-close-button = Fermer + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Annuler +pdfjs-editor-add-signature-add-button = Ajouter +pdfjs-editor-edit-signature-update-button = Mettre à jour + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Supprimer la signature +pdfjs-editor-delete-signature-button-label = Supprimer la signature + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Modifier la description + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Modifier la description diff --git a/l10n/fur/viewer.ftl b/l10n/fur/viewer.ftl index c587f556d89c2..1bca7d5811edd 100644 --- a/l10n/fur/viewer.ftl +++ b/l10n/fur/viewer.ftl @@ -6,7 +6,7 @@ ## Main toolbar buttons (tooltips and alt text for images) pdfjs-previous-button = - .title = Pagjine precedente + .title = Pagjine di prime pdfjs-previous-button-label = Indaûr pdfjs-next-button = .title = Prossime pagjine @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Gjave imagjin pdfjs-editor-remove-highlight-button = .title = Gjave evidenziazion +pdfjs-editor-remove-signature-button = + .title = Gjave firme ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Zonte imagjin pdfjs-editor-free-highlight-thickness-input = Spessôr pdfjs-editor-free-highlight-thickness-title = .title = Modifiche il spessôr de selezion pai elements che no son testuâi +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editôr di test + .default-content = Scomence a scrivi… pdfjs-free-text = .aria-label = Editôr di test pdfjs-free-text-default-content = Scomence a scrivi… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Test alternatîf +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifiche test alternatîf pdfjs-editor-alt-text-edit-button-label = Modifiche test alternatîf pdfjs-editor-alt-text-dialog-label = Sielç une opzion pdfjs-editor-alt-text-dialog-description = Il test alternatîf (“alt text”) al jude cuant che lis personis no puedin viodi la imagjin o cuant che la imagjine no ven cjariade. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Segnade come decorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Par esempli, “Un zovin si sente a taule par mangjâ” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Test alternatîf ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Siere pdfjs-editor-new-alt-text-ai-model-downloading-progress = Daûr a discjariâil model IA pal test alternatîf ({ $downloadedSize } di { $totalSize } MB) .aria-valuetext = Daûr a discjariâ il model IA pal test alternatîf ({ $downloadedSize } di { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Test alternatîf zontât pdfjs-editor-new-alt-text-added-button-label = Test alternatîf zontât # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Al mancje il test alternatîf pdfjs-editor-new-alt-text-missing-button-label = Al mancje il test alternatîf # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Verifiche test alternatîf pdfjs-editor-new-alt-text-to-review-button-label = Verifiche test alternatîf # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,87 @@ pdfjs-editor-alt-text-settings-editor-title = Modifiche test alternatîf pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostre l'editôr dal test alternatîf a pene che e ven zontade une imagjin pdfjs-editor-alt-text-settings-show-dialog-description = Ti jude a sigurâti che dutis lis tôs imagjins a vedin il test alternatîf. pdfjs-editor-alt-text-settings-close-button = Siere + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Evidenziazion gjavade +pdfjs-editor-undo-bar-message-freetext = Test gjavât +pdfjs-editor-undo-bar-message-ink = Dissen gjavât +pdfjs-editor-undo-bar-message-stamp = Imagjin gjavade +pdfjs-editor-undo-bar-message-signature = Firme gjavade +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } note gjavade + *[other] { $count } notis gjavadis + } +pdfjs-editor-undo-bar-undo-button = + .title = Anule +pdfjs-editor-undo-bar-undo-button-label = Anule +pdfjs-editor-undo-bar-close-button = + .title = Siere +pdfjs-editor-undo-bar-close-button-label = Siere + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Chest barcon al permet al utent di creâ une firme di zontâ a un document PDF. L’utent al pues modificâ il non (che al vignarà doprât ancje come test alternatîf) e, se lu desidere, salvâ la firme par tornâ a doprâle un doman. +pdfjs-editor-add-signature-dialog-title = Zonte une firme + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Scrîf + .title = Scrîf +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Dissegne + .title = Dissegne +pdfjs-editor-add-signature-image-button = Imagjin + .title = Imagjin + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Scrîf la tô firme + .placeholder = Scrîf la tô firme +pdfjs-editor-add-signature-draw-placeholder = Dissegne la tô firme +pdfjs-editor-add-signature-draw-thickness-range-label = Spessôr +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Spessôr de tresse: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Strissine un file achì par cjariâlu +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Opûr sielç i files imagjin + *[other] Opûr sgarfe pai files imagjin + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descrizion (test alternatîf) +pdfjs-editor-add-signature-description-input = + .title = Descrizion (test alternatîf) +pdfjs-editor-add-signature-description-default-when-drawing = Firme +pdfjs-editor-add-signature-clear-button-label = Nete firme +pdfjs-editor-add-signature-clear-button = + .title = Nete firme +pdfjs-editor-add-signature-save-checkbox = Salve firme +pdfjs-editor-add-signature-save-warning-message = Tu sês rivât/rivade al limit di 5 firmis salvadis. Gjave une par salvânt une altre. +pdfjs-editor-add-signature-image-upload-error-title = Impussibil cjariâ la imagjin +pdfjs-editor-add-signature-image-upload-error-description = Controle la conession di rêt o prove cuntune altre imagjin. +pdfjs-editor-add-signature-error-close-button = Siere + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Anule +pdfjs-editor-add-signature-add-button = Zonte + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/fy-NL/viewer.ftl b/l10n/fy-NL/viewer.ftl index d336034d99301..60679f590ab06 100644 --- a/l10n/fy-NL/viewer.ftl +++ b/l10n/fy-NL/viewer.ftl @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Ofbylding fuortsmite pdfjs-editor-remove-highlight-button = .title = Markearring fuortsmite +pdfjs-editor-remove-signature-button = + .title = Hantekening fuortsmite ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Ofbylding tafoegje pdfjs-editor-free-highlight-thickness-input = Tsjokte pdfjs-editor-free-highlight-thickness-title = .title = Tsjokte wizigje by aksintuearring fan oare items as tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstbewurker + .default-content = Start mei typen… pdfjs-free-text = .aria-label = Tekstbewurker pdfjs-free-text-default-content = Begjin mei typen… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternative tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternative tekst bewurkje pdfjs-editor-alt-text-edit-button-label = Alternative tekst bewurkje pdfjs-editor-alt-text-dialog-label = Kies in opsje pdfjs-editor-alt-text-dialog-description = Alternative tekst helpt wannear’t minsken de ôfbylding net sjen kinne of wannear’t dizze net laden wurdt. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = As dekoratyf markearre # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Bygelyks, ‘In jonge man sit oan in tafel om te iten’ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternative tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -431,7 +441,7 @@ pdfjs-editor-new-alt-text-dialog-edit-label = Alternative tekst (ôfbyldingsbesk # Modal header positioned above a text box where users can add the alt text. pdfjs-editor-new-alt-text-dialog-add-label = Alternative tekst (ôfbyldingsbeskriuwing) tafoegje pdfjs-editor-new-alt-text-textarea = - .placeholder = Skriuw hjir jo beskriuwing... + .placeholder = Skriuw hjir jo beskriuwing… # This text refers to the alt text box above this description. It offers a definition of alt text. pdfjs-editor-new-alt-text-description = Koarte beskriuwing foar minsken dy’t de ôfbylding net sjen kinne of wannear’t de ôfbylding net laden wurdt. # This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Slute pdfjs-editor-new-alt-text-ai-model-downloading-progress = AI-model foar alternative tekst downloade ({ $downloadedSize } fan { $totalSize } MB) .aria-valuetext = AI-model foar alternative tekst downloade ({ $downloadedSize } fan { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternative tekst tafoege pdfjs-editor-new-alt-text-added-button-label = Alternative tekst tafoege # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternative tekst ûntbrekt pdfjs-editor-new-alt-text-missing-button-label = Alternative tekst ûntbrekt # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternative tekst beoardiele pdfjs-editor-new-alt-text-to-review-button-label = Alternative tekst beoardiele # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,87 @@ pdfjs-editor-alt-text-settings-editor-title = Alternative-tekstbewurker pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternative-tekstbewurker daliks toane by tafoegjen fan in ôfbylding pdfjs-editor-alt-text-settings-show-dialog-description = Helpt jo derfoar te soargjen dat al jo ôfbyldingen alternative tekst hawwe. pdfjs-editor-alt-text-settings-close-button = Slute + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markearring fuortsmiten +pdfjs-editor-undo-bar-message-freetext = Tekst fuortsmiten +pdfjs-editor-undo-bar-message-ink = Tekening fuortsmiten +pdfjs-editor-undo-bar-message-stamp = Ofbylding fuortsmiten +pdfjs-editor-undo-bar-message-signature = Hantekening fuortsmiten +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotaasje fuortsmiten + *[other] { $count } annotaasjes fuortsmiten + } +pdfjs-editor-undo-bar-undo-button = + .title = Ungedien meitsje +pdfjs-editor-undo-bar-undo-button-label = Ungedien meitsje +pdfjs-editor-undo-bar-close-button = + .title = Slute +pdfjs-editor-undo-bar-close-button-label = Slute + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Mei dizze modal kin de brûker in hantekening meitsje om oan in PDF-dokumint ta te foegjen. De brûker kin de namme bewurkje (dy't ek tsjinnet as alternative tekst), en opsjoneel de ûndertekening bewarje foar werhelle gebrûk. +pdfjs-editor-add-signature-dialog-title = In hantekening tafoegje + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Type + .title = Type +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Tekenje + .title = Tekenje +pdfjs-editor-add-signature-image-button = Ofbylding + .title = Ofbylding + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Jo hantekening type + .placeholder = Jo hantekening type +pdfjs-editor-add-signature-draw-placeholder = Jo hantekening tekenje +pdfjs-editor-add-signature-draw-thickness-range-label = Tsjokte +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Tekentsjokte: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Sleep bestân hjirhinne om op te laden +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Of kies ôfbyldingsbestannen + *[other] Of kies ôfbyldingsbestannen + } + +## Controls + +pdfjs-editor-add-signature-description-label = Beskriuwing (alternative tekst) +pdfjs-editor-add-signature-description-input = + .title = Beskriuwing (alternative tekst) +pdfjs-editor-add-signature-description-default-when-drawing = Hantekening +pdfjs-editor-add-signature-clear-button-label = Hantekening wiskje +pdfjs-editor-add-signature-clear-button = + .title = Hantekening wiskje +pdfjs-editor-add-signature-save-checkbox = Hantekening bewarje +pdfjs-editor-add-signature-save-warning-message = Jo hawwe de limyt fan 5 bewarre hantekeningen berikt. Ferwiderje ien om in oar te bewarjen. +pdfjs-editor-add-signature-image-upload-error-title = Kin de ôfbylding net oplade +pdfjs-editor-add-signature-image-upload-error-description = Kontrolearje jo netwurkferbining of probearje in oare ôfbylding. +pdfjs-editor-add-signature-error-close-button = Slute + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Annulearje +pdfjs-editor-add-signature-add-button = Tafoegje + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ga-IE/viewer.ftl b/l10n/ga-IE/viewer.ftl index cb5930890c078..e81e149cad1fd 100644 --- a/l10n/ga-IE/viewer.ftl +++ b/l10n/ga-IE/viewer.ftl @@ -205,9 +205,56 @@ pdfjs-web-fonts-disabled = Tá clófhoirne Gréasáin díchumasaithe: ní féidi ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/gd/viewer.ftl b/l10n/gd/viewer.ftl index a3d62a0fd3ad9..71d39c255cc82 100644 --- a/l10n/gd/viewer.ftl +++ b/l10n/gd/viewer.ftl @@ -311,3 +311,30 @@ pdfjs-ink-canvas = ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/gl/viewer.ftl b/l10n/gl/viewer.ftl index 641a6075bce99..dced173f39973 100644 --- a/l10n/gl/viewer.ftl +++ b/l10n/gl/viewer.ftl @@ -328,7 +328,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar o texto alternativo pdfjs-editor-alt-text-dialog-label = Escoller unha opción @@ -383,3 +382,30 @@ pdfjs-editor-resizer-middle-left = ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/gn/viewer.ftl b/l10n/gn/viewer.ftl index a7a68e74b228d..19e14f3fbd82d 100644 --- a/l10n/gn/viewer.ftl +++ b/l10n/gn/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Mbosa’y .aria-label = Mbosa’y pdfjs-highlight-floating-button-label = Mbosa’y +pdfjs-editor-signature-button = + .title = Embojuaju teraguapy +pdfjs-editor-signature-button-label = Embojuaju teraguapy ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Emboguete ta’ãnga pdfjs-editor-remove-highlight-button = .title = Eipe’a jehechaveha +pdfjs-editor-remove-signature-button = + .title = Embogue teraguapy ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Embojuaju ta’ãnga pdfjs-editor-free-highlight-thickness-input = Anambusu pdfjs-editor-free-highlight-thickness-title = .title = Emoambue anambusukue embosa’ývo mba’eporu ha’e’ỹva moñe’ẽrã +pdfjs-editor-signature-add-signature-button = + .title = Embojuaju teraguapy pyahu +pdfjs-editor-signature-add-signature-button-label = Embojuaju teraguapy pyahu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Moñe’ẽrã moheñoiha + .default-content = Eñepyrũ ehai… pdfjs-free-text = .aria-label = Moñe’ẽrã moheñoiha pdfjs-free-text-default-content = Ehai ñepyrũ… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Moñe’ẽrã mokõiháva +pdfjs-editor-alt-text-edit-button = + .aria-label = Embojuruja moñe’ẽrã mokõiháva pdfjs-editor-alt-text-edit-button-label = Embojuruja moñe’ẽrã mokõiháva pdfjs-editor-alt-text-dialog-label = Eiporavo poravorã pdfjs-editor-alt-text-dialog-description = Moñe’ẽrã ykepegua (moñe’ẽrã ykepegua) nepytyvõ nderehecháiramo ta’ãnga térã nahenyhẽiramo. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Jeguakárõ mongurusupyre # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Techapyrã: “Peteĩ mitãrusu oguapy mesápe okaru hag̃ua” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Moñe’ẽrã mokõiháva ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -432,6 +448,10 @@ pdfjs-editor-new-alt-text-dialog-edit-label = Embosako’i moñe’ẽrã mokõi pdfjs-editor-new-alt-text-dialog-add-label = Embojuaju moñe’ẽrã mokõiha (ta’ãngáre ñeñe’ẽ) pdfjs-editor-new-alt-text-textarea = .placeholder = Edescribi ko’ápe… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Ñemyesakã mbykymi opavave ohecha’ỹva upe ta’ãnga térã pe ta’ãnga nahenyhẽiramo. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ko moñe’ẽrã mokõiha oñemoheñói ijehegui ha ikatu ndoikoporãi. pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Eikuaave pdfjs-editor-new-alt-text-create-automatically-button-label = Emoheñói moñe’ẽrã mokõiha ijeheguíva pdfjs-editor-new-alt-text-not-now-button = Ani ko’ág̃a @@ -445,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Mboty pdfjs-editor-new-alt-text-ai-model-downloading-progress = Emboguejyhína IA moñe’ẽrã mokõiháva ({ $downloadedSize } { $totalSize } MB) mba’e .aria-valuetext = Emboguejyhína IA moñe’ẽrã mokõiháva ({ $downloadedSize } { $totalSize } MB) mba’e # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Moñe’ẽrã mokõiha mbojuajupyre pdfjs-editor-new-alt-text-added-button-label = Oñembojuaju moñe’ẽrã mokõiha # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Ndaipóri moñe’ẽrã mokõiha pdfjs-editor-new-alt-text-missing-button-label = Ndaipóri moñe’ẽrã mokõiha # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Ehechajey moñe’ẽrã mokõiha pdfjs-editor-new-alt-text-to-review-button-label = Ehechajey moñe’ẽrã mokõiha # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -463,9 +489,11 @@ pdfjs-image-alt-text-settings-button-label = Ta’ãnga moñe’ẽrã mokõiha pdfjs-editor-alt-text-settings-dialog-label = Ta’ãnga moñe’ẽrã mokõiha ñemboheko pdfjs-editor-alt-text-settings-automatic-title = Moñe’ẽrã mokõiha ijeheguíva pdfjs-editor-alt-text-settings-create-model-button-label = Emoheñói moñe’ẽrã mokõiha ijeheguíva +pdfjs-editor-alt-text-settings-create-model-description = Ñemyesakã mbykymi opavave tapicha ohecha’ỹva upe ta’ãnga térã pe ta’ãnga nahenyhẽiramo. # Variables: # $totalSize (Number) - the total size (in MB) of the AI model. pdfjs-editor-alt-text-settings-download-model-label = Peteĩva IA moñe’ẽrã mokõiha ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Oku’e mba’e’okaitépe umi mba’ekuaarã hekoñemi hag̃ua. Tekotevẽva moñe’ẽrã ykegua ijeheguívape. pdfjs-editor-alt-text-settings-delete-model-button = Mboguete pdfjs-editor-alt-text-settings-download-model-button = Mboguejy pdfjs-editor-alt-text-settings-downloading-model-button = Emboguejyhína… @@ -473,3 +501,89 @@ pdfjs-editor-alt-text-settings-editor-title = Moñe’ẽrã mokõiha mbosako’ pdfjs-editor-alt-text-settings-show-dialog-button-label = Ehechauka moñe’ẽrã mokõiha mbosako’iha embojuajúvo ta’ãnga pdfjs-editor-alt-text-settings-show-dialog-description = Nepytyvõta ta’ãngakuéra orekotaha moñe’ẽrã mokõiha. pdfjs-editor-alt-text-settings-close-button = Mboty + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Mbosa’ýva mboguete +pdfjs-editor-undo-bar-message-freetext = Moñe’ẽrã mboguepyre +pdfjs-editor-undo-bar-message-ink = Ta’ãnga mboguepyre +pdfjs-editor-undo-bar-message-stamp = Ta’ãnga mboguepyre +pdfjs-editor-undo-bar-message-signature = Teraguapy mboguepyre +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } jehaikue mboguepyre + *[other] { $count } jehaikue mboguepyre + } +pdfjs-editor-undo-bar-undo-button = + .title = Mboguevi +pdfjs-editor-undo-bar-undo-button-label = Mboguevi +pdfjs-editor-undo-bar-close-button = + .title = Mboty +pdfjs-editor-undo-bar-close-button-label = Mboty + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-title = Embojuaju teraguapy + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Jehai + .title = Jehai +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Moha’ãnga + .title = Moha’ãnga +pdfjs-editor-add-signature-image-button = Ta’ãnga + .title = Ta’ãnga + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Ehai nde reraguapy + .placeholder = Ehai nde reraguapy +pdfjs-editor-add-signature-draw-placeholder = Emoha’ãnga nde reraguapy +pdfjs-editor-add-signature-draw-thickness-range-label = Anambusu +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Ta’ãnga anambusukue: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Egueru marandurenda ápe ehupi hag̃ua +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Eiporavo ta’ãnga marandurenda + *[other] Eiporavo ta’ãnga marandurenda + } + +## Controls + +pdfjs-editor-add-signature-description-label = Moha’ãnga (moñe’ẽrã ykepegua) +pdfjs-editor-add-signature-description-input = + .title = Moha’ãnga (moñe’ẽrã ykepegua) +pdfjs-editor-add-signature-description-default-when-drawing = Teraguapy +pdfjs-editor-add-signature-clear-button-label = Emboguete teraguapy +pdfjs-editor-add-signature-clear-button = + .title = Emboguete teraguapy +pdfjs-editor-add-signature-save-checkbox = Eñongatu teraguapy +pdfjs-editor-add-signature-error-close-button = Mboty + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Heja +pdfjs-editor-add-signature-add-button = Mbojuaju +pdfjs-editor-edit-signature-update-button = Mbohekopyahu + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Embogue teraguapy +pdfjs-editor-delete-signature-button-label = Embogue teraguapy + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Embosako’i moha’ãnga + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Embosako’i moha’ãnga diff --git a/l10n/gu-IN/viewer.ftl b/l10n/gu-IN/viewer.ftl index 5d8bb549f3125..b3beb1556e21a 100644 --- a/l10n/gu-IN/viewer.ftl +++ b/l10n/gu-IN/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = વેબ ફોન્ટ નિષ્ક્રિય ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/he/viewer.ftl b/l10n/he/viewer.ftl index 11302c4cceb59..28d6d7f426771 100644 --- a/l10n/he/viewer.ftl +++ b/l10n/he/viewer.ftl @@ -293,7 +293,7 @@ pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", tim ## Password pdfjs-password-label = נא להכניס את הססמה לפתיחת קובץ PDF זה. -pdfjs-password-invalid = ססמה שגויה. נא לנסות שנית. +pdfjs-password-invalid = ססמה שגויה. נא לנסות שוב. pdfjs-password-ok-button = אישור pdfjs-password-cancel-button = ביטול pdfjs-web-fonts-disabled = גופני רשת מנוטרלים: לא ניתן להשתמש בגופני PDF מוטבעים. @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = סימון .aria-label = סימון pdfjs-highlight-floating-button-label = סימון +pdfjs-editor-signature-button = + .title = הוספת חתימה +pdfjs-editor-signature-button-label = הוספת חתימה ## Remove button for the various kind of editor. @@ -326,7 +329,9 @@ pdfjs-editor-remove-freetext-button = pdfjs-editor-remove-stamp-button = .title = הסרת תמונה pdfjs-editor-remove-highlight-button = - .title = הסרת הדגשה + .title = הסרת סימון +pdfjs-editor-remove-signature-button = + .title = הסרת חתימה ## @@ -342,7 +347,14 @@ pdfjs-editor-stamp-add-image-button-label = הוספת תמונה # This refers to the thickness of the line used for free highlighting (not bound to text) pdfjs-editor-free-highlight-thickness-input = עובי pdfjs-editor-free-highlight-thickness-title = - .title = שינוי עובי בעת הדגשת פריטים שאינם טקסט + .title = שינוי עובי בעת סימון פריטים שאינם טקסט +pdfjs-editor-signature-add-signature-button = + .title = הוספת חתימה חדשה +pdfjs-editor-signature-add-signature-button-label = הוספת חתימה חדשה +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = עורך טקסט + .default-content = נא להתחיל להקליד… pdfjs-free-text = .aria-label = עורך טקסט pdfjs-free-text-default-content = להתחיל להקליד… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = טקסט חלופי +pdfjs-editor-alt-text-edit-button = + .aria-label = עריכת טקסט חלופי pdfjs-editor-alt-text-edit-button-label = עריכת טקסט חלופי pdfjs-editor-alt-text-dialog-label = בחירת אפשרות pdfjs-editor-alt-text-dialog-description = טקסט חלופי עוזר כשאנשים לא יכולים לראות את התמונה או כשהיא לא נטענת. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = מסומן כדקורטיבי # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = לדוגמה, ״גבר צעיר מתיישב ליד שולחן לאכול ארוחה״ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = טקסט חלופי ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -400,7 +416,7 @@ pdfjs-editor-resizer-middle-left = ## Color picker # This means "Color used to highlight text" -pdfjs-editor-highlight-colorpicker-label = צבע הדגשה +pdfjs-editor-highlight-colorpicker-label = צבע סימון pdfjs-editor-colorpicker-button = .title = שינוי צבע pdfjs-editor-colorpicker-dropdown = @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = סגירה pdfjs-editor-new-alt-text-ai-model-downloading-progress = בתהליך הורדת מודל AI של טקסט חלופי ({ $downloadedSize } מתוך { $totalSize } מ״ב) .aria-valuetext = בתהליך הורדת מודל AI של טקסט חלופי ({ $downloadedSize } מתוך { $totalSize } מ״ב) # This is a button that users can click to edit the alt text they have already added. -pdfjs-editor-new-alt-text-added-button-label = טקסט חלופי נוסף +pdfjs-editor-new-alt-text-added-button = + .aria-label = נוסף טקסט חלופי +pdfjs-editor-new-alt-text-added-button-label = נוסף טקסט חלופי # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = חסר טקסט חלופי pdfjs-editor-new-alt-text-missing-button-label = חסר טקסט חלופי # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = סקירת טקסט חלופי pdfjs-editor-new-alt-text-to-review-button-label = סקירת טקסט חלופי # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = עורך טקסט חלופי pdfjs-editor-alt-text-settings-show-dialog-button-label = הצגת עורך טקסט חלופי מיד בעת הוספת תמונה pdfjs-editor-alt-text-settings-show-dialog-description = מסייע לך לוודא שלכל התמונות שלך יש טקסט חלופי. pdfjs-editor-alt-text-settings-close-button = סגירה + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = הסימון הוסר +pdfjs-editor-undo-bar-message-freetext = הטקסט הוסר +pdfjs-editor-undo-bar-message-ink = הציור הוסר +pdfjs-editor-undo-bar-message-stamp = התמונה הוסרה +pdfjs-editor-undo-bar-message-signature = החתימה הוסרה +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] הערה אחת הוסרה + *[other] { $count } הערות הוסרו + } +pdfjs-editor-undo-bar-undo-button = + .title = ביטול פעולה +pdfjs-editor-undo-bar-undo-button-label = ביטול פעלה +pdfjs-editor-undo-bar-close-button = + .title = סגירה +pdfjs-editor-undo-bar-close-button-label = סגירה + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = מודל זה מאפשר למשתמש ליצור חתימה להוספה למסמך PDF. המשתמש יכול לערוך את השם (שמשמש גם כטקסט האלטרנטיבי), ובאופן אופציונלי לשמור את החתימה לשימוש חוזר. +pdfjs-editor-add-signature-dialog-title = הוספת חתימה + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = הקלדה + .title = הקלדה +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = ציור + .title = ציור +pdfjs-editor-add-signature-image-button = תמונה + .title = תמונה + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = נא להקליד את החתימה שלך + .placeholder = נא להקליד את החתימה שלך +pdfjs-editor-add-signature-draw-placeholder = נא לצייר את החתימה שלך +pdfjs-editor-add-signature-draw-thickness-range-label = עובי +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = עובי הציור: { $thickness } +pdfjs-editor-add-signature-image-placeholder = יש לגרור לכאן קובץ להעלאה +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] או לבחור בקובצי תמונה + *[other] או לעיין בקובצי תמונה + } + +## Controls + +pdfjs-editor-add-signature-description-label = תיאור (טקסט חלופי) +pdfjs-editor-add-signature-description-input = + .title = תיאור (טקסט חלופי) +pdfjs-editor-add-signature-description-default-when-drawing = חתימה +pdfjs-editor-add-signature-clear-button-label = ניקוי חתימה +pdfjs-editor-add-signature-clear-button = + .title = ניקוי חתימה +pdfjs-editor-add-signature-save-checkbox = שמירת החתימה +pdfjs-editor-add-signature-save-warning-message = הגעת למגבלה של 5 חתימות שמורות. יש להסיר אחד כדי לשמור עוד. +pdfjs-editor-add-signature-image-upload-error-title = לא ניתן להעלות את התמונה +pdfjs-editor-add-signature-image-upload-error-description = נא לבדוק את החיבור שלך לרשת או לנסות תמונה אחרת. +pdfjs-editor-add-signature-error-close-button = סגירה + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = ביטול +pdfjs-editor-add-signature-add-button = הוספה +pdfjs-editor-edit-signature-update-button = עדכון + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = הסרת חתימה +pdfjs-editor-delete-signature-button-label = הסרת חתימה + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = עריכת תיאור + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = עריכת תיאור diff --git a/l10n/hi-IN/viewer.ftl b/l10n/hi-IN/viewer.ftl index b6f378f6919ab..e0822aef407fd 100644 --- a/l10n/hi-IN/viewer.ftl +++ b/l10n/hi-IN/viewer.ftl @@ -265,3 +265,30 @@ pdfjs-editor-free-text-color-input = रंग ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/hr/viewer.ftl b/l10n/hr/viewer.ftl index 0c66341f762d7..d2a97eff21b8c 100644 --- a/l10n/hr/viewer.ftl +++ b/l10n/hr/viewer.ftl @@ -105,6 +105,14 @@ pdfjs-document-properties-button-label = Svojstva dokumenta … pdfjs-document-properties-file-name = Ime datoteke: pdfjs-document-properties-file-size = Veličina datoteke: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bajtova) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bajtova) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtova) @@ -119,12 +127,15 @@ pdfjs-document-properties-keywords = Ključne riječi: pdfjs-document-properties-creation-date = Datum stvaranja: pdfjs-document-properties-modification-date = Datum promjene: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } pdfjs-document-properties-creator = Stvaratelj: pdfjs-document-properties-producer = PDF stvaratelj: -pdfjs-document-properties-version = PDF inačica: +pdfjs-document-properties-version = PDF verzija: pdfjs-document-properties-page-count = Broj stranica: pdfjs-document-properties-page-size = Dimenzije stranice: pdfjs-document-properties-page-size-unit-inches = in @@ -224,7 +235,7 @@ pdfjs-find-reached-bottom = Dosegnut kraj dokumenta, nastavak s početka # $current (Number) - the index of the currently active find result # $total (Number) - the total number of matches in the document pdfjs-find-match-count = - { NUMBER($total) -> + { $total -> [one] { $current } od { $total } rezultata [few] { $current } od { $total } rezultata *[other] { $current } od { $total } rezultata @@ -232,7 +243,7 @@ pdfjs-find-match-count = # Variables: # $limit (Number) - the maximum number of matches pdfjs-find-match-count-limit = - { NUMBER($limit) -> + { $limit -> [one] Više od { $limit } rezultat [few] Više od { $limit } rezultata *[other] Više od { $limit } rezultata @@ -277,6 +288,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [{ $type } Bilješka] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -331,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Dodaj sliku pdfjs-editor-free-highlight-thickness-input = Debljina pdfjs-editor-free-highlight-thickness-title = .title = Promjeni debljinu pri isticanju drugih stavki osim teksta +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Uređivač teksta + .default-content = Počni tipkati … pdfjs-free-text = .aria-label = Uređivač teksta pdfjs-free-text-default-content = Počni tipkati … @@ -341,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternativni tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Uredi alternativni tekst pdfjs-editor-alt-text-edit-button-label = Uredi alternativni tekst pdfjs-editor-alt-text-dialog-label = Odaberi jednu opciju pdfjs-editor-alt-text-dialog-description = Alternativni tekst pomaže slijepim osobama ili kada se slika ne učita. @@ -356,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Označeno kao ukrasno # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Na primjer, „Mladić sjeda za stol kako bi jeo” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativni tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -414,13 +436,40 @@ pdfjs-editor-highlight-show-all-button = ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Uredi alternativni tekst (opis slike) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Dodaj alternativni tekst (opis slike) pdfjs-editor-new-alt-text-textarea = .placeholder = Ovdje upiši tvoj opis … +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Kratki opis koji pomažu osobama koji ne mogu vidjeti sliku ili kada se slika ne učita. # This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. pdfjs-editor-new-alt-text-disclaimer1 = Ovaj je alternativni tekst stvoren automatski i može biti netočan. pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Saznaj više pdfjs-editor-new-alt-text-create-automatically-button-label = Automatski stvori alternativni tekst +pdfjs-editor-new-alt-text-not-now-button = Ne sada pdfjs-editor-new-alt-text-error-title = Nije bilo moguće automatski izraditi alternativni tekst +pdfjs-editor-new-alt-text-error-description = Napiši vlastiti alternativni tekst ili pokušaj kasnije ponovo. +pdfjs-editor-new-alt-text-error-close-button = Zatvori +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Preuzimanje alternativnog teksta UI modela ({ $downloadedSize } od { $totalSize } MB) + .aria-valuetext = Preuzimanje alternativnog teksta UI modela ({ $downloadedSize } od { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativni tekst je dodan +pdfjs-editor-new-alt-text-added-button-label = Alternativni tekst je dodan +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Nedostaje alternativni tekst +pdfjs-editor-new-alt-text-missing-button-label = Nedostaje alternativni tekst +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Pregledaj alternativni tekst +pdfjs-editor-new-alt-text-to-review-button-label = Pregledaj alternativni tekst # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: # $generatedAltText (String) - the generated alt-text. @@ -434,6 +483,10 @@ pdfjs-image-alt-text-settings-button-label = Postavke alternativnog teksta slike pdfjs-editor-alt-text-settings-dialog-label = Postavke alternativnog teksta slike pdfjs-editor-alt-text-settings-automatic-title = Automatski alternativni tekst pdfjs-editor-alt-text-settings-create-model-button-label = Stvori alternativni tekst automatski +pdfjs-editor-alt-text-settings-create-model-description = Predlaže opise koji pomažu osobama koji ne mogu vidjeti sliku ili kada se slika ne učita. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Alternativni tekst UI modela ({ $totalSize } MB) pdfjs-editor-alt-text-settings-ai-model-description = Radi lokalno na tvom uređaju kako bi tvoji podaci ostali privatni. Potrebno za automatski alternativni tekst. pdfjs-editor-alt-text-settings-delete-model-button = Izbriši pdfjs-editor-alt-text-settings-download-model-button = Preuzmi @@ -442,3 +495,48 @@ pdfjs-editor-alt-text-settings-editor-title = Uređivač alternativnog teksta pdfjs-editor-alt-text-settings-show-dialog-button-label = Prikaži uređivač alternativnog teksta odmah pri dodavanju slike pdfjs-editor-alt-text-settings-show-dialog-description = Pomaže osigurati da sve tvoje slike imaju alternativni tekst. pdfjs-editor-alt-text-settings-close-button = Zatvori + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Isticanje uklonjeno +pdfjs-editor-undo-bar-message-freetext = Tekst uklonjen +pdfjs-editor-undo-bar-message-ink = Crtež uklonjen +pdfjs-editor-undo-bar-message-stamp = Slika uklonjena +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } pribilješka uklonjena + [few] { $count } pribilješke uklonjene + *[other] { $count } pribilješki uklonjeno + } +pdfjs-editor-undo-bar-undo-button = + .title = Poništi +pdfjs-editor-undo-bar-undo-button-label = Poništi +pdfjs-editor-undo-bar-close-button = + .title = Zatvori +pdfjs-editor-undo-bar-close-button-label = Zatvori + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/hsb/viewer.ftl b/l10n/hsb/viewer.ftl index d5fe5d5daa02e..9c9abb84ed379 100644 --- a/l10n/hsb/viewer.ftl +++ b/l10n/hsb/viewer.ftl @@ -320,6 +320,9 @@ pdfjs-highlight-floating-button1 = .title = Wuzběhnjenje .aria-label = Wuzběhnjenje pdfjs-highlight-floating-button-label = Wuzběhnjenje +pdfjs-editor-signature-button = + .title = Signaturu přidać +pdfjs-editor-signature-button-label = Signaturu přidać ## Remove button for the various kind of editor. @@ -331,6 +334,8 @@ pdfjs-editor-remove-stamp-button = .title = Wobraz wotstronić pdfjs-editor-remove-highlight-button = .title = Wuzběhnjenje wotstronić +pdfjs-editor-remove-signature-button = + .title = Signaturu wotstronić ## @@ -347,6 +352,13 @@ pdfjs-editor-stamp-add-image-button-label = Wobraz přidać pdfjs-editor-free-highlight-thickness-input = Tołstosć pdfjs-editor-free-highlight-thickness-title = .title = Tołstosć změnić, hdyž so zapiski wuzběhuja, kotrež tekst njejsu +pdfjs-editor-signature-add-signature-button = + .title = Nowu signaturu přidać +pdfjs-editor-signature-add-signature-button-label = Nowu signaturu přidać +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstowy editor + .default-content = Započńće pisać … pdfjs-free-text = .aria-label = Tekstowy editor pdfjs-free-text-default-content = Započńće pisać… @@ -357,8 +369,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatiwny tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatiwny tekst wobdźěłać pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobdźěłać pdfjs-editor-alt-text-dialog-label = Nastajenje wubrać pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomha, hdyž ludźo njemóža wobraz widźeć abo hdyž so wobraz njezačita. @@ -372,6 +385,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Jako dekoratiwny markěrowany # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Na přikład, „Młody muž za blidom sedźi, zo by jědź jědł“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatiwny tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -453,10 +469,16 @@ pdfjs-editor-new-alt-text-error-close-button = Začinić pdfjs-editor-new-alt-text-ai-model-downloading-progress = Model KI za alternatiwny tekst so sćahuje ({ $downloadedSize } z { $totalSize } MB) .aria-valuetext = Model KI za alternatiwny tekst so sćahuje ({ $downloadedSize } z { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatiwny tekst je so přidał pdfjs-editor-new-alt-text-added-button-label = Alternatiwny tekst je so přidał # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatiwny tekst faluje pdfjs-editor-new-alt-text-missing-button-label = Alternatiwny tekst faluje # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatiwny tekst přepruwować pdfjs-editor-new-alt-text-to-review-button-label = Alternatiwny tekst přepruwować # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -483,3 +505,95 @@ pdfjs-editor-alt-text-settings-editor-title = Editor za alternatiwny tekst pdfjs-editor-alt-text-settings-show-dialog-button-label = Editor alternatiwneho teksta hnydom pokazać, hdyž so wobraz přidawa pdfjs-editor-alt-text-settings-show-dialog-description = Pomha, wam wšěm swojim wobrazam alternatiwny tekst přidać. pdfjs-editor-alt-text-settings-close-button = Začinić + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Wotstronjene wuzběhnyć +pdfjs-editor-undo-bar-message-freetext = Tekst je so wotstronił +pdfjs-editor-undo-bar-message-ink = Rysowanka je so wotstroniła +pdfjs-editor-undo-bar-message-stamp = Wobraz je so wotstronił +pdfjs-editor-undo-bar-message-signature = Signatura je so wotstroniła +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } přispomnjenka je so wotstroniła + [two] { $count } přispomnjence stej so wotstroniłoj + [few] { $count } přispomnjenki su so wotstronili + *[other] { $count } přispomnjenkow je so wotstroniło + } +pdfjs-editor-undo-bar-undo-button = + .title = Cofnyć +pdfjs-editor-undo-bar-undo-button-label = Cofnyć +pdfjs-editor-undo-bar-close-button = + .title = Začinić +pdfjs-editor-undo-bar-close-button-label = Začinić + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Tutón modalny dialog wužiwarjej zmóžnja, signaturu wutworić, zo by PDF-dokument přidał. Wužiwar móže mjeno wobdźěłać (kotrež tež jako alternatiwny tekst słuži) a po přeću signaturu za wospjetne wužiwanje składować. +pdfjs-editor-add-signature-dialog-title = Signaturu přidać + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typ + .title = Typ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Rysować + .title = Rysować +pdfjs-editor-add-signature-image-button = Wobraz + .title = Wobraz + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Zapodajće swoju signaturu + .placeholder = Zapodajće swoju signaturu +pdfjs-editor-add-signature-draw-placeholder = Rysujće swoju signaturu +pdfjs-editor-add-signature-draw-thickness-range-label = Tołstosć +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Tołstosć rysowanki: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Ćehńće dataju sem, zo byšće ju nahrał +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Abo přepytajće wobrazowe dataje + *[other] Abo přepytajće wobrazowe dataje + } + +## Controls + +pdfjs-editor-add-signature-description-label = Wopisanje (alternatiwny tekst) +pdfjs-editor-add-signature-description-input = + .title = Wopisanje (alternatiwny tekst) +pdfjs-editor-add-signature-description-default-when-drawing = Signatura +pdfjs-editor-add-signature-clear-button-label = Signaturu zhašeć +pdfjs-editor-add-signature-clear-button = + .title = Signaturu zhašeć +pdfjs-editor-add-signature-save-checkbox = Signaturu składować +pdfjs-editor-add-signature-save-warning-message = Sće limit 5 składowanych signaturow docpěł. Wotstrońće jednu, zo byšće wjace składował. +pdfjs-editor-add-signature-image-upload-error-title = Wobraz njeda so nahrać +pdfjs-editor-add-signature-image-upload-error-description = Přepruwujće swój syćowy zwisk abo spytajće druhi wobraz. +pdfjs-editor-add-signature-error-close-button = Začinić + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Přetorhnyć +pdfjs-editor-add-signature-add-button = Přidać +pdfjs-editor-edit-signature-update-button = Aktualizować + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Signaturu wotstronić +pdfjs-editor-delete-signature-button-label = Signaturu wotstronić + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Wopisanje wobdźěłać + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Wopisanje wobdźěłać diff --git a/l10n/hu/viewer.ftl b/l10n/hu/viewer.ftl index d8f5c5eaae0b5..57b5fd7a8d891 100644 --- a/l10n/hu/viewer.ftl +++ b/l10n/hu/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Kiemelés .aria-label = Kiemelés pdfjs-highlight-floating-button-label = Kiemelés +pdfjs-editor-signature-button = + .title = Aláírás hozzáadása +pdfjs-editor-signature-button-label = Aláírás hozzáadása ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Kép eltávolítása pdfjs-editor-remove-highlight-button = .title = Kiemelés eltávolítása +pdfjs-editor-remove-signature-button = + .title = Aláírás eltávolítása ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Kép hozzáadása pdfjs-editor-free-highlight-thickness-input = Vastagság pdfjs-editor-free-highlight-thickness-title = .title = Vastagság módosítása, ha nem szöveges elemeket emel ki +pdfjs-editor-signature-add-signature-button = + .title = Új aláírás hozzáadása +pdfjs-editor-signature-add-signature-button-label = Új aláírás hozzáadása +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Szövegszerkesztő + .default-content = Kezdjen gépelni… pdfjs-free-text = .aria-label = Szövegszerkesztő pdfjs-free-text-default-content = Kezdjen el gépelni… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatív szöveg +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatív szöveg szerkesztése pdfjs-editor-alt-text-edit-button-label = Alternatív szöveg szerkesztése pdfjs-editor-alt-text-dialog-label = Válasszon egy lehetőséget pdfjs-editor-alt-text-dialog-description = Az alternatív szöveg segít, ha az emberek nem látják a képet, vagy ha az nem töltődik be. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Megjelölve dekoratívként # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Például: „Egy fiatal férfi leül enni egy asztalhoz” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatív szöveg ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Bezárás pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alternatív szöveg MI modell letöltése ({ $downloadedSize } / { $totalSize } MB) .aria-valuetext = Alternatív szöveg MI modell letöltése ({ $downloadedSize } / { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatív szöveg hozzáadva pdfjs-editor-new-alt-text-added-button-label = Alternatív szöveg hozzáadva # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Hiányzó alternatív szöveg pdfjs-editor-new-alt-text-missing-button-label = Hiányzó alternatív szöveg # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatív szöveg áttekintése pdfjs-editor-new-alt-text-to-review-button-label = Alternatív szöveg szerkesztése # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Alternatív szöveg szerkesztője pdfjs-editor-alt-text-settings-show-dialog-button-label = Az alternatív szöveg szerkesztőjének azonnali megjelenítése egy kép hozzáadásakor pdfjs-editor-alt-text-settings-show-dialog-description = Segít elérni, hogy az összes képén legyen alternatív szöveg. pdfjs-editor-alt-text-settings-close-button = Bezárás + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Kiemelés eltávolítva +pdfjs-editor-undo-bar-message-freetext = Szöveg eltávolítva +pdfjs-editor-undo-bar-message-ink = Rajz eltávolítva +pdfjs-editor-undo-bar-message-stamp = Kép eltávolítva +pdfjs-editor-undo-bar-message-signature = Aláírás eltávolítva +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } kommentár eltávolítva + *[other] { $count } kommentár eltávolítva + } +pdfjs-editor-undo-bar-undo-button = + .title = Visszavonás +pdfjs-editor-undo-bar-undo-button-label = Visszavonás +pdfjs-editor-undo-bar-close-button = + .title = Bezárás +pdfjs-editor-undo-bar-close-button-label = Bezárás + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Ez a mód lehetővé teszi a felhasználónak, hogy aláírást hozzon létre, és ezt egy PDF dokumentumhoz adja. A felhasználó szerkesztheti a nevet (ez egyben alternatív szövegként is szolgál), és ismételt felhasználás céljából tetszés szerint mentheti az aláírást. +pdfjs-editor-add-signature-dialog-title = Aláírás hozzáadása + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Beírás + .title = Beírás +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Rajzolás + .title = Rajzolás +pdfjs-editor-add-signature-image-button = Kép + .title = Kép + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Írja be az aláírását + .placeholder = Írja be az aláírását +pdfjs-editor-add-signature-draw-placeholder = Rajzolja le az aláírását +pdfjs-editor-add-signature-draw-thickness-range-label = Vastagság +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Rajzolási vastagság: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Húzzon ide egy fájlt a feltöltéshez +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Vagy tallózzon a képfájlok között + *[other] Vagy tallózzon a képfájlok között + } + +## Controls + +pdfjs-editor-add-signature-description-label = Leírás (alternatív szöveg) +pdfjs-editor-add-signature-description-input = + .title = Leírás (alternatív szöveg) +pdfjs-editor-add-signature-description-default-when-drawing = Aláírás +pdfjs-editor-add-signature-clear-button-label = Aláírás törlése +pdfjs-editor-add-signature-clear-button = + .title = Aláírás törlése +pdfjs-editor-add-signature-save-checkbox = Aláírás mentése +pdfjs-editor-add-signature-save-warning-message = Elérte a mentett aláírások 5 darabos korlátját. A mentéshez távolítson el egyet. +pdfjs-editor-add-signature-image-upload-error-title = A kép nem tölthető fel +pdfjs-editor-add-signature-image-upload-error-description = Ellenőrizze a hálózati kapcsolatot, vagy próbálkozzon egy másik képpel. +pdfjs-editor-add-signature-error-close-button = Bezárás + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Mégse +pdfjs-editor-add-signature-add-button = Hozzáadás +pdfjs-editor-edit-signature-update-button = Frissítés + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Aláírás eltávolítása +pdfjs-editor-delete-signature-button-label = Aláírás eltávolítása + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Leírás szerkesztése + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Leírás szerkesztése diff --git a/l10n/hy-AM/viewer.ftl b/l10n/hy-AM/viewer.ftl index 5c9dd27b180a6..605825b7abbe1 100644 --- a/l10n/hy-AM/viewer.ftl +++ b/l10n/hy-AM/viewer.ftl @@ -270,3 +270,37 @@ pdfjs-free-text-default-content = Սկսել մուտքագրումը… pdfjs-editor-highlight-show-all-button-label = Ցուցադրել բոլորը pdfjs-editor-highlight-show-all-button = .title = Ցուցադրել բոլորը + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/hye/viewer.ftl b/l10n/hye/viewer.ftl index 75cdc06431cbd..007225c63df28 100644 --- a/l10n/hye/viewer.ftl +++ b/l10n/hye/viewer.ftl @@ -260,9 +260,56 @@ pdfjs-web-fonts-disabled = Վեբ-տառատեսակները անջատուած ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ia/viewer.ftl b/l10n/ia/viewer.ftl index b69be14bc7af9..e34be65b448d8 100644 --- a/l10n/ia/viewer.ftl +++ b/l10n/ia/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Evidentiar .aria-label = Evidentiar pdfjs-highlight-floating-button-label = Evidentiar +pdfjs-editor-signature-button = + .title = Adder signatura +pdfjs-editor-signature-button-label = Adder signatura ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Remover imagine pdfjs-editor-remove-highlight-button = .title = Remover evidentia +pdfjs-editor-remove-signature-button = + .title = Remover signatura ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Adder imagine pdfjs-editor-free-highlight-thickness-input = Spissor pdfjs-editor-free-highlight-thickness-title = .title = Cambiar spissor evidentiante elementos differente de texto +pdfjs-editor-signature-add-signature-button = + .title = Adder nove signatura +pdfjs-editor-signature-add-signature-button-label = Adder nove signatura +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Initiar a inserer… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Comenciar a scriber… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternative +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger texto alternative pdfjs-editor-alt-text-edit-button-label = Rediger texto alternative pdfjs-editor-alt-text-dialog-label = Elige un option pdfjs-editor-alt-text-dialog-description = Le texto alternative (alt text) adjuta quando le personas non pote vider le imagine o quando illo non carga. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcate como decorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Per exemplo, “Un juvene sede a un tabula pro mangiar un repasto” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternative ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Clauder pdfjs-editor-new-alt-text-ai-model-downloading-progress = Discargante modello de intelligentia artificial del texto alternative ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = Discargante modello de intelligentia artificial del texto alternative ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternative addite pdfjs-editor-new-alt-text-added-button-label = Texto alternative addite # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texto alternative mancante pdfjs-editor-new-alt-text-missing-button-label = Texto alternative mancante # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revider texto alternative pdfjs-editor-new-alt-text-to-review-button-label = Revider texto alternative # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Rediger texto alternative pdfjs-editor-alt-text-settings-show-dialog-button-label = Monstrar le redactor de texto alternative a pena on adde un imagine pdfjs-editor-alt-text-settings-show-dialog-description = Te adjuta a verifica que tote tu imagines ha un texto alternative. pdfjs-editor-alt-text-settings-close-button = Clauder + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Evidentiation removite +pdfjs-editor-undo-bar-message-freetext = Texto removite +pdfjs-editor-undo-bar-message-ink = Designo removite +pdfjs-editor-undo-bar-message-stamp = Imagine removite +pdfjs-editor-undo-bar-message-signature = Signatura removite +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotation removite + *[other] { $count } annotationes removite + } +pdfjs-editor-undo-bar-undo-button = + .title = Disfacer +pdfjs-editor-undo-bar-undo-button-label = Disfacer +pdfjs-editor-undo-bar-close-button = + .title = Clauder +pdfjs-editor-undo-bar-close-button-label = Clauder + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Iste formulario permitte al usator crear un firma a adder a un documento PDF. Le usator pote modificar le nomine (le qual tamben servi de texto alternative) e, si desirate, salvar le firma pro uso repetite. +pdfjs-editor-add-signature-dialog-title = Adder un signatura + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typar + .title = Typar +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Designar + .title = Designar +pdfjs-editor-add-signature-image-button = Imagine + .title = Imagine + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Insere tu firma + .placeholder = Insere tu firma +pdfjs-editor-add-signature-draw-placeholder = Designa tu firma +pdfjs-editor-add-signature-draw-thickness-range-label = Spissor +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Spissor de designo: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Trahe un file hic pro incargar lo +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] O elige files de imagine + *[other] O folietta files de imagine + } + +## Controls + +pdfjs-editor-add-signature-description-label = Description (texto alternative) +pdfjs-editor-add-signature-description-input = + .title = Description (texto alternative) +pdfjs-editor-add-signature-description-default-when-drawing = Signatura +pdfjs-editor-add-signature-clear-button-label = Rader signatura +pdfjs-editor-add-signature-clear-button = + .title = Rader signatura +pdfjs-editor-add-signature-save-checkbox = Salvar signatura +pdfjs-editor-add-signature-save-warning-message = Tu ha attingite le limite de 5 firmas salvate. Remove un pro salvar un altere. +pdfjs-editor-add-signature-image-upload-error-title = Non poteva incargar le imagine +pdfjs-editor-add-signature-image-upload-error-description = Verifica tu connexion al rete o tenta un altere imagine. +pdfjs-editor-add-signature-error-close-button = Clauder + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancellar +pdfjs-editor-add-signature-add-button = Adder +pdfjs-editor-edit-signature-update-button = Actualisar + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Remover signatura +pdfjs-editor-delete-signature-button-label = Remover signatura + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Rediger description + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Rediger description diff --git a/l10n/id/viewer.ftl b/l10n/id/viewer.ftl index fee8d18bfcaf9..9784ab179f534 100644 --- a/l10n/id/viewer.ftl +++ b/l10n/id/viewer.ftl @@ -42,6 +42,12 @@ pdfjs-print-button-label = Cetak pdfjs-save-button = .title = Simpan pdfjs-save-button-label = Simpan +# Used in Firefox for Android as a tooltip for the download button (“download” is a verb). +pdfjs-download-button = + .title = Unduh +# Used in Firefox for Android as a label for the download button (“download” is a verb). +# Length of the translation matters since we are in a mobile context, with limited screen estate. +pdfjs-download-button-label = Unduh pdfjs-bookmark-button = .title = Laman Saat Ini (Lihat URL dari Laman Sekarang) pdfjs-bookmark-button-label = Laman Saat Ini @@ -99,6 +105,14 @@ pdfjs-document-properties-button-label = Properti Dokumen… pdfjs-document-properties-file-name = Nama berkas: pdfjs-document-properties-file-size = Ukuran berkas: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte) @@ -113,6 +127,9 @@ pdfjs-document-properties-keywords = Kata Kunci: pdfjs-document-properties-creation-date = Tanggal Dibuat: pdfjs-document-properties-modification-date = Tanggal Dimodifikasi: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } @@ -214,6 +231,13 @@ pdfjs-find-match-diacritics-checkbox-label = Pencocokan Diakritik pdfjs-find-entire-word-checkbox-label = Seluruh teks pdfjs-find-reached-top = Sampai di awal dokumen, dilanjutkan dari bawah pdfjs-find-reached-bottom = Sampai di akhir dokumen, dilanjutkan dari atas +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = { $current } dari { $total } yang cocok +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = Lebih dari { $limit } kecocokan pdfjs-find-not-found = Frasa tidak ditemukan ## Predefined zoom values @@ -254,6 +278,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [Anotasi { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -271,12 +298,47 @@ pdfjs-editor-free-text-button-label = Teks pdfjs-editor-ink-button = .title = Gambar pdfjs-editor-ink-button-label = Gambar +pdfjs-editor-stamp-button = + .title = Tambah atau edit gambar +pdfjs-editor-stamp-button-label = Tambah atau edit gambar +pdfjs-editor-highlight-button = + .title = Sorot +pdfjs-editor-highlight-button-label = Sorot +pdfjs-highlight-floating-button1 = + .title = Sorot + .aria-label = Sorot +pdfjs-highlight-floating-button-label = Sorot + +## Remove button for the various kind of editor. + +pdfjs-editor-remove-ink-button = + .title = Hapus gambar +pdfjs-editor-remove-freetext-button = + .title = Hapus teks +pdfjs-editor-remove-stamp-button = + .title = Hapus gambar +pdfjs-editor-remove-highlight-button = + .title = Hapus sorotan + +## + # Editor Parameters pdfjs-editor-free-text-color-input = Warna pdfjs-editor-free-text-size-input = Ukuran pdfjs-editor-ink-color-input = Warna pdfjs-editor-ink-thickness-input = Ketebalan pdfjs-editor-ink-opacity-input = Opasitas +pdfjs-editor-stamp-add-image-button = + .title = Tambahkan gambar +pdfjs-editor-stamp-add-image-button-label = Tambahkan gambar +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = Ketebalan +pdfjs-editor-free-highlight-thickness-title = + .title = Ubah ketebalan saat menyorot item selain teks +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor Teks + .default-content = Mulai mengetik… pdfjs-free-text = .aria-label = Editor Teks pdfjs-free-text-default-content = Mulai mengetik… @@ -287,7 +349,179 @@ pdfjs-ink-canvas = ## Alt-text dialog +pdfjs-editor-alt-text-button-label = Teks alternatif +pdfjs-editor-alt-text-edit-button = + .aria-label = Edit teks alternatif +pdfjs-editor-alt-text-edit-button-label = Edit teks alternatif +pdfjs-editor-alt-text-dialog-label = Pilih opsi +pdfjs-editor-alt-text-dialog-description = Teks alternatif membantu ketika orang tidak dapat melihat gambar atau ketika tidak termuat. +pdfjs-editor-alt-text-add-description-label = Tambahkan deskripsi +pdfjs-editor-alt-text-add-description-description = Upayakan 1-2 kalimat yang menggambarkan subjek, latar, atau tindakan. +pdfjs-editor-alt-text-mark-decorative-label = Tandai sebagai dekoratif +pdfjs-editor-alt-text-mark-decorative-description = Ini digunakan untuk gambar hias, seperti batas atau tanda air. +pdfjs-editor-alt-text-cancel-button = Batal +pdfjs-editor-alt-text-save-button = Simpan +pdfjs-editor-alt-text-decorative-tooltip = Ditandai sebagai dekoratif +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = Misalnya, “Seorang pemuda duduk di meja untuk makan” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Teks alternatif ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. +pdfjs-editor-resizer-label-top-left = Pojok kiri atas — ubah ukuran +pdfjs-editor-resizer-label-top-middle = Tengah atas — ubah ukuran +pdfjs-editor-resizer-label-top-right = Pojok kanan atas — ubah ukuran +pdfjs-editor-resizer-label-middle-right = Kanan tengah — ubah ukuran +pdfjs-editor-resizer-label-bottom-right = Pojok kanan bawah — ubah ukuran +pdfjs-editor-resizer-label-bottom-middle = Tengah bawah — ubah ukuran +pdfjs-editor-resizer-label-bottom-left = Pojok kiri bawah — ubah ukuran +pdfjs-editor-resizer-label-middle-left = Kiri tengah — ubah ukuran +pdfjs-editor-resizer-top-left = + .aria-label = Pojok kiri atas — ubah ukuran +pdfjs-editor-resizer-top-middle = + .aria-label = Tengah atas — ubah ukuran +pdfjs-editor-resizer-top-right = + .aria-label = Pojok kanan atas — ubah ukuran +pdfjs-editor-resizer-middle-right = + .aria-label = Kanan tengah — ubah ukuran +pdfjs-editor-resizer-bottom-right = + .aria-label = Pojok kanan bawah — ubah ukuran +pdfjs-editor-resizer-bottom-middle = + .aria-label = Tengah bawah — ubah ukuran +pdfjs-editor-resizer-bottom-left = + .aria-label = Pojok kiri bawah — ubah ukuran +pdfjs-editor-resizer-middle-left = + .aria-label = Kiri tengah — ubah ukuran + +## Color picker + +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Warna sorot +pdfjs-editor-colorpicker-button = + .title = Ubah warna +pdfjs-editor-colorpicker-dropdown = + .aria-label = Pilihan warna +pdfjs-editor-colorpicker-yellow = + .title = Kuning +pdfjs-editor-colorpicker-green = + .title = Hijau +pdfjs-editor-colorpicker-blue = + .title = Biru +pdfjs-editor-colorpicker-pink = + .title = Merah Jambu +pdfjs-editor-colorpicker-red = + .title = Merah + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + +pdfjs-editor-highlight-show-all-button-label = Tampilkan semua +pdfjs-editor-highlight-show-all-button = + .title = Tampilkan semua + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Edit teks alternatif (deskripsi gambar) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Tambahkan teks alternatif (deskripsi gambar) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Tulis deskripsi Anda di sini… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Deskripsi singkat untuk orang yang tidak dapat melihat gambar atau saat gambar tidak termuat. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Teks alternatif ini dibuat secara otomatis dan mungkin tidak akurat. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Pelajari lebih lanjut +pdfjs-editor-new-alt-text-create-automatically-button-label = Buat teks alternatif secara otomatis +pdfjs-editor-new-alt-text-not-now-button = Jangan sekarang +pdfjs-editor-new-alt-text-error-title = Tidak bisa membuat teks alternatif secara otomatis +pdfjs-editor-new-alt-text-error-description = Silakan tulis teks alternatif Anda sendiri atau coba lagi nanti. +pdfjs-editor-new-alt-text-error-close-button = Tutup +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Mengunduh model AI teks alternatif ({ $downloadedSize } dari { $totalSize } MB) + .aria-valuetext = Mengunduh model AI teks alternatif ({ $downloadedSize } dari { $totalSize } MB) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Teks alternatif ditambahkan +pdfjs-editor-new-alt-text-added-button-label = Teks alternatif ditambahkan +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Teks alternatif hilang +pdfjs-editor-new-alt-text-missing-button-label = Teks alternatif hilang +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Tinjau teks alternatif +pdfjs-editor-new-alt-text-to-review-button-label = Tinjau teks alternatif +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Dibuat secara otomatis: { $generatedAltText } + +## Image alt-text settings + +pdfjs-image-alt-text-settings-button = + .title = Pengaturan teks alternatif gambar +pdfjs-image-alt-text-settings-button-label = Pengaturan teks alternatif gambar +pdfjs-editor-alt-text-settings-dialog-label = Pengaturan teks alternatif gambar +pdfjs-editor-alt-text-settings-automatic-title = Teks alternatif otomatis +pdfjs-editor-alt-text-settings-create-model-button-label = Buat teks alternatif secara otomatis +pdfjs-editor-alt-text-settings-create-model-description = Menyarankan deskripsi untuk membantu orang yang tidak dapat melihat gambar atau ketika gambar tidak termuat. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Model AI teks alternatif ({ $totalSize } MB) +pdfjs-editor-alt-text-settings-ai-model-description = Berjalan secara lokal di perangkat Anda sehingga data Anda tetap pribadi. Diperlukan untuk teks alternatif otomatis. +pdfjs-editor-alt-text-settings-delete-model-button = Hapus +pdfjs-editor-alt-text-settings-download-model-button = Unduh +pdfjs-editor-alt-text-settings-downloading-model-button = Mengunduh… +pdfjs-editor-alt-text-settings-editor-title = Editor teks alternatif +pdfjs-editor-alt-text-settings-show-dialog-button-label = Tampilkan editor teks alternatif segera saat menambahkan gambar +pdfjs-editor-alt-text-settings-show-dialog-description = Membantu Anda memastikan semua gambar Anda memiliki teks alternatif. +pdfjs-editor-alt-text-settings-close-button = Tutup + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Sorotan dihapus +pdfjs-editor-undo-bar-message-freetext = Teks dihapus +pdfjs-editor-undo-bar-message-ink = Gambar dihapus +pdfjs-editor-undo-bar-message-stamp = Gambar dihapus +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = { $count } anotasi dihapus +pdfjs-editor-undo-bar-undo-button = + .title = Urungkan +pdfjs-editor-undo-bar-undo-button-label = Urungkan +pdfjs-editor-undo-bar-close-button = + .title = Tutup +pdfjs-editor-undo-bar-close-button-label = Tutup + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/is/viewer.ftl b/l10n/is/viewer.ftl index 802426207262c..56ee2976343df 100644 --- a/l10n/is/viewer.ftl +++ b/l10n/is/viewer.ftl @@ -179,10 +179,10 @@ pdfjs-printing-not-ready = Aðvörun: Ekki er búið að hlaða inn allri PDF sk ## Tooltips and alt text for side panel toolbar buttons pdfjs-toggle-sidebar-button = - .title = Víxla hliðarspjaldi af/á + .title = Víxla hliðarstiku af/á pdfjs-toggle-sidebar-notification-button = - .title = Víxla hliðarslá (skjal inniheldur yfirlit/viðhengi/lög) -pdfjs-toggle-sidebar-button-label = Víxla hliðarspjaldi af/á + .title = Víxla hliðarstiku af/á (skjal inniheldur yfirlit/viðhengi/lög) +pdfjs-toggle-sidebar-button-label = Víxla hliðarstiku af/á pdfjs-document-outline-button = .title = Sýna yfirlit skjals (tvísmelltu til að opna/loka öllum hlutum) pdfjs-document-outline-button-label = Efnisskipan skjals @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Áherslulita .aria-label = Áherslulita pdfjs-highlight-floating-button-label = Áherslulita +pdfjs-editor-signature-button = + .title = Bæta við undirritun +pdfjs-editor-signature-button-label = Bæta við undirritun ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Fjarlægja mynd pdfjs-editor-remove-highlight-button = .title = Fjarlægja áherslulit +pdfjs-editor-remove-signature-button = + .title = Fjarlægja undirskrift ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Bæta við mynd pdfjs-editor-free-highlight-thickness-input = Þykkt pdfjs-editor-free-highlight-thickness-title = .title = Breyta þykkt við áherslulitun annarra atriða en texta +pdfjs-editor-signature-add-signature-button = + .title = Bæta við nýrri undirritun +pdfjs-editor-signature-add-signature-button-label = Bæta við nýrri undirritun +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textaritill + .default-content = Byrjaðu að skrifa… pdfjs-free-text = .aria-label = Textaritill pdfjs-free-text-default-content = Byrjaðu að skrifa… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt-varatexti +pdfjs-editor-alt-text-edit-button = + .aria-label = Breyta alt-myndatexta pdfjs-editor-alt-text-edit-button-label = Breyta alt-varatexta pdfjs-editor-alt-text-dialog-label = Veldu valkost pdfjs-editor-alt-text-dialog-description = Alt-varatexti (auka-myndatexti) hjálpar þegar fólk getur ekki séð myndina eða þegar hún hleðst ekki inn. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Merkt sem skraut # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Til dæmis: „Ungur maður sest við borð til að snæða máltíð“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-myndatexti ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Loka pdfjs-editor-new-alt-text-ai-model-downloading-progress = Sækir gervigreindarlíkan með alt-myndatextum ({ $downloadedSize } af { $totalSize } MB) .aria-valuetext = Sækir gervigreindarlíkan með alt-myndatextum ({ $downloadedSize } af { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt-myndatexta bætt við pdfjs-editor-new-alt-text-added-button-label = Alt-myndatexta bætt við # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Vantar alt-myndatexta pdfjs-editor-new-alt-text-missing-button-label = Vantar alt-myndatexta # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Yfirfara alt-myndatexta pdfjs-editor-new-alt-text-to-review-button-label = Yfirfara myndatexta # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Ritill fyrir alt-myndatexta pdfjs-editor-alt-text-settings-show-dialog-button-label = Sýna alt-myndatextaritil strax þegar mynd er bætt við pdfjs-editor-alt-text-settings-show-dialog-description = Hjálpar þér að tryggja að allar myndirnar þínar séu með alt-myndatexta. pdfjs-editor-alt-text-settings-close-button = Loka + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Áherslulitun fjarlægð +pdfjs-editor-undo-bar-message-freetext = Texti fjarlægður +pdfjs-editor-undo-bar-message-ink = Teikning fjarlægð +pdfjs-editor-undo-bar-message-stamp = Mynd fjarlægð +pdfjs-editor-undo-bar-message-signature = Undirskrift fjarlægð +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } glósa fjarlægð + *[other] { $count } glósur fjarlægðar + } +pdfjs-editor-undo-bar-undo-button = + .title = Afturkalla +pdfjs-editor-undo-bar-undo-button-label = Afturkalla +pdfjs-editor-undo-bar-close-button = + .title = Loka +pdfjs-editor-undo-bar-close-button-label = Loka + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Þessi gluggi gerir notandanum kleift að búa til undirskrift til að bæta við PDF-skjal. Notandinn getur breytt nafninu (sem einnig þjónar sem alt-texti), og valið að vista undirskriftina til endurtekinnar notkunar. +pdfjs-editor-add-signature-dialog-title = Bæta við undirskrift + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Tegund + .title = Tegund +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Teikna + .title = Teikna +pdfjs-editor-add-signature-image-button = Mynd + .title = Mynd + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Skrifaðu inn undirskriftina þína + .placeholder = Skrifaðu inn undirskriftina þína +pdfjs-editor-add-signature-draw-placeholder = Teiknaðu undirskriftina þína +pdfjs-editor-add-signature-draw-thickness-range-label = Þykkt +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Sverleiki teikningar: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Dragðu skrá hingað til að senda inn +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Eða skoðaðu myndskrár + *[other] Eða skoðaðu myndskrár + } + +## Controls + +pdfjs-editor-add-signature-description-label = Lýsing (alt-hjálpartexti) +pdfjs-editor-add-signature-description-input = + .title = Lýsing (alt-hjálpartexti) +pdfjs-editor-add-signature-description-default-when-drawing = Undirskrift +pdfjs-editor-add-signature-clear-button-label = Hreinsa undirskrift +pdfjs-editor-add-signature-clear-button = + .title = Hreinsa undirskrift +pdfjs-editor-add-signature-save-checkbox = Vista undirskrift +pdfjs-editor-add-signature-save-warning-message = Þú hefur náð hámarki 5 vistaðra undirskrifta. Fjarlægðu eina til að geta vistað fleiri. +pdfjs-editor-add-signature-image-upload-error-title = Ekki tókst að senda inn mynd +pdfjs-editor-add-signature-image-upload-error-description = Athugaðu nettenginguna þína eða prófaðu aðra mynd. +pdfjs-editor-add-signature-error-close-button = Loka + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Hætta við +pdfjs-editor-add-signature-add-button = Bæta við +pdfjs-editor-edit-signature-update-button = Uppfæra + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Fjarlægja undirritun +pdfjs-editor-delete-signature-button-label = Fjarlægja undirritun + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Breyta lýsingu + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Breyta lýsingu diff --git a/l10n/it/viewer.ftl b/l10n/it/viewer.ftl index bdbed4a3569de..b573a573215ef 100644 --- a/l10n/it/viewer.ftl +++ b/l10n/it/viewer.ftl @@ -104,9 +104,11 @@ pdfjs-document-properties-button = pdfjs-document-properties-button-label = Proprietà del documento… pdfjs-document-properties-file-name = Nome file: pdfjs-document-properties-file-size = Dimensione file: +# Variables: # $kb (Number) - the PDF file size in kilobytes # $b (Number) - the PDF file size in bytes pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } byte) +# Variables: # $mb (Number) - the PDF file size in megabytes # $b (Number) - the PDF file size in bytes pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } byte) @@ -124,6 +126,8 @@ pdfjs-document-properties-subject = Oggetto: pdfjs-document-properties-keywords = Parole chiave: pdfjs-document-properties-creation-date = Data creazione: pdfjs-document-properties-modification-date = Data modifica: +# Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } # Variables: # $date (Date) - the creation/modification date of the PDF file @@ -282,12 +286,14 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [Annotazione: { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password pdfjs-password-label = Inserire la password per aprire questo file PDF. -pdfjs-password-invalid = Password non corretta. Riprovare. +pdfjs-password-invalid = Password non corretta. Riprova. pdfjs-password-ok-button = OK pdfjs-password-cancel-button = Annulla pdfjs-web-fonts-disabled = I web font risultano disattivati: impossibile utilizzare i caratteri incorporati nel PDF. @@ -310,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Evidenzia .aria-label = Evidenzia pdfjs-highlight-floating-button-label = Evidenzia +pdfjs-editor-signature-button = + .title = Aggiungi firma +pdfjs-editor-signature-button-label = Aggiungi firma ## Remove button for the various kind of editor. @@ -321,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Rimuovi immagine pdfjs-editor-remove-highlight-button = .title = Rimuovi evidenziazione +pdfjs-editor-remove-signature-button = + .title = Rimuovi firma ## @@ -337,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Aggiungi immagine pdfjs-editor-free-highlight-thickness-input = Spessore pdfjs-editor-free-highlight-thickness-title = .title = Modifica lo spessore della selezione per elementi non testuali +pdfjs-editor-signature-add-signature-button = + .title = Aggiungi nuova firma +pdfjs-editor-signature-add-signature-button-label = Aggiungi nuova firma +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor di testo + .default-content = Inizia a digitare… pdfjs-free-text = .aria-label = Editor di testo pdfjs-free-text-default-content = Inizia a digitare… @@ -347,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Testo alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifica testo alternativo pdfjs-editor-alt-text-edit-button-label = Modifica testo alternativo pdfjs-editor-alt-text-dialog-label = Scegli un’opzione pdfjs-editor-alt-text-dialog-description = Il testo alternativo (“alt text”) aiuta quando le persone non possono vedere l’immagine o quando l’immagine non viene caricata. @@ -362,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Contrassegnata come decorativa # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Ad esempio, “Un giovane si siede a tavola per mangiare” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Testo alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -443,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Chiudi pdfjs-editor-new-alt-text-ai-model-downloading-progress = Download in corso del modello IA per il testo alternativo ({ $downloadedSize } di { $totalSize } MB) .aria-valuetext = Download in corso del modello IA per il testo alternativo ({ $downloadedSize } di { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Aggiunto testo alternativo pdfjs-editor-new-alt-text-added-button-label = Aggiunto testo alternativo # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Testo alternativo mancante pdfjs-editor-new-alt-text-missing-button-label = Testo alternativo mancante # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Verifica testo alternativo pdfjs-editor-new-alt-text-to-review-button-label = Verifica testo alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -473,3 +501,94 @@ pdfjs-editor-alt-text-settings-editor-title = Modifica testo alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostra l’editor del testo alternativo non appena si aggiunge un’immagine pdfjs-editor-alt-text-settings-show-dialog-description = Ti aiuta ad assicurarti che tutte le tue immagini abbiano il testo alternativo. pdfjs-editor-alt-text-settings-close-button = Chiudi + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Evidenziazione rimossa +pdfjs-editor-undo-bar-message-freetext = Testo rimosso +pdfjs-editor-undo-bar-message-ink = Disegno rimosso +pdfjs-editor-undo-bar-message-stamp = Immagine rimossa +pdfjs-editor-undo-bar-message-signature = Firma rimossa +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotazione rimossa + *[other] { $count } annotazioni rimosse + } +pdfjs-editor-undo-bar-undo-button = + .title = Annulla +pdfjs-editor-undo-bar-undo-button-label = Annulla +pdfjs-editor-undo-bar-close-button = + .title = Chiudi +pdfjs-editor-undo-bar-close-button-label = Chiudi + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Questa finestra consente all’utente di creare una firma da aggiungere a un documento PDF. L’utente può modificare il nome (che verrà utilizzato anche come testo alternativo) e, se lo desidera, salvare la firma per riutilizzarla in futuro. +pdfjs-editor-add-signature-dialog-title = Aggiungi una firma + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Scrivi + .title = Scrivi +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Disegna + .title = Disegna +pdfjs-editor-add-signature-image-button = Immagine + .title = Immagine + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Digita la tua firma + .placeholder = Digita la tua firma +pdfjs-editor-add-signature-draw-placeholder = Disegna la tua firma +pdfjs-editor-add-signature-draw-thickness-range-label = Spessore +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Spessore del tratto: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Trascina un file qui per caricarlo +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Oppure scegli un file immagine + *[other] Oppure sfoglia i file immagine + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descrizione (testo alternativo) +pdfjs-editor-add-signature-description-input = + .title = Descrizione (testo alternativo) +pdfjs-editor-add-signature-description-default-when-drawing = Firma +pdfjs-editor-add-signature-clear-button-label = Cancella firma +pdfjs-editor-add-signature-clear-button = + .title = Cancella firma +pdfjs-editor-add-signature-save-checkbox = Salva firma +pdfjs-editor-add-signature-save-warning-message = Hai raggiunto il limite di 5 firme salvate. Rimuovine una per salvarne altre. +pdfjs-editor-add-signature-image-upload-error-title = Impossibile caricare l’immagine +pdfjs-editor-add-signature-image-upload-error-description = Controlla la connessione di rete o prova con un’altra immagine. +pdfjs-editor-add-signature-error-close-button = Chiudi + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Annulla +pdfjs-editor-add-signature-add-button = Aggiungi +pdfjs-editor-edit-signature-update-button = Aggiorna + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Rimuovi firma +pdfjs-editor-delete-signature-button-label = Rimuovi firma + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Modifica descrizione + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Modifica descrizione + diff --git a/l10n/ja/viewer.ftl b/l10n/ja/viewer.ftl index 97a73987e279d..60dd0758d9b3f 100644 --- a/l10n/ja/viewer.ftl +++ b/l10n/ja/viewer.ftl @@ -319,6 +319,8 @@ pdfjs-editor-remove-stamp-button = .title = 画像を削除します pdfjs-editor-remove-highlight-button = .title = 強調を削除します +pdfjs-editor-remove-signature-button = + .title = 署名を削除します ## @@ -335,6 +337,10 @@ pdfjs-editor-stamp-add-image-button-label = 画像を追加 pdfjs-editor-free-highlight-thickness-input = 太さ pdfjs-editor-free-highlight-thickness-title = .title = テキスト以外のアイテムを強調する時の太さを変更します +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = フリーテキスト注釈エディター + .default-content = テキストを入力してください... pdfjs-free-text = .aria-label = フリーテキスト注釈エディター pdfjs-free-text-default-content = テキストを入力してください... @@ -345,8 +351,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = 代替テキスト +pdfjs-editor-alt-text-edit-button = + .aria-label = 代替テキストを編集 pdfjs-editor-alt-text-edit-button-label = 代替テキストを編集 pdfjs-editor-alt-text-dialog-label = オプションの選択 pdfjs-editor-alt-text-dialog-description = 代替テキストは画像が表示されない場合や読み込まれない場合にユーザーの助けになります。 @@ -360,6 +367,9 @@ pdfjs-editor-alt-text-decorative-tooltip = 装飾マークが付いています # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = 例:「若い人がテーブルの席について食事をしています」 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 代替テキスト ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +451,16 @@ pdfjs-editor-new-alt-text-error-close-button = 閉じる pdfjs-editor-new-alt-text-ai-model-downloading-progress = 代替テキスト AI モデルをダウンロードしています ({ $downloadedSize } / { $totalSize } MB) .aria-valuetext = 代替テキスト AI モデルをダウンロードしています ({ $downloadedSize } / { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 代替テキストを追加しました pdfjs-editor-new-alt-text-added-button-label = 代替テキストを追加しました # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 代替テキストがありません pdfjs-editor-new-alt-text-missing-button-label = 代替テキストがありません # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 代替テキストをレビュー pdfjs-editor-new-alt-text-to-review-button-label = 代替テキストをレビュー # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +487,74 @@ pdfjs-editor-alt-text-settings-editor-title = 代替テキストエディター pdfjs-editor-alt-text-settings-show-dialog-button-label = 画像の追加時に代替テキストエディターを表示する pdfjs-editor-alt-text-settings-show-dialog-description = すべての画像に代替テキストを追加する助けになります。 pdfjs-editor-alt-text-settings-close-button = 閉じる + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 強調表示が削除されました +pdfjs-editor-undo-bar-message-freetext = フリーテキスト注釈が削除されました +pdfjs-editor-undo-bar-message-ink = インク注釈が削除されました +pdfjs-editor-undo-bar-message-stamp = 画像が削除されました +pdfjs-editor-undo-bar-message-signature = 署名が削除されました +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = { $count } 個の注釈が削除されました +pdfjs-editor-undo-bar-undo-button = + .title = 元に戻す +pdfjs-editor-undo-bar-undo-button-label = 元に戻す +pdfjs-editor-undo-bar-close-button = + .title = 閉じる +pdfjs-editor-undo-bar-close-button-label = 閉じる + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = このダイアログではユーザーが署名を作成して PDF 文書に追加できます。 +pdfjs-editor-add-signature-dialog-title = 署名を追加 + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = タイプ + .title = キーボード入力します +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = 手書き + .title = 手書き入力します +pdfjs-editor-add-signature-image-button = 画像 + .title = 画像を指定します + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = 署名をキーボード入力 + .placeholder = 署名をキーボード入力 +pdfjs-editor-add-signature-draw-placeholder = 署名を手書き入力 +pdfjs-editor-add-signature-draw-thickness-range-label = 線の太さ +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = 線の太さ: { $thickness } +pdfjs-editor-add-signature-image-placeholder = ファイルをここにドラッグしてアップロード +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] または画像ファイルを選択 + *[other] または画像ファイルを参照 + } + +## Controls + +pdfjs-editor-add-signature-description-label = 説明 (代替テキスト) +pdfjs-editor-add-signature-description-input = + .title = 説明 (代替テキスト) を追加します +pdfjs-editor-add-signature-description-default-when-drawing = 署名 +pdfjs-editor-add-signature-clear-button-label = 署名を消去 +pdfjs-editor-add-signature-clear-button = + .title = 署名を消去します +pdfjs-editor-add-signature-save-checkbox = 署名を保存 +pdfjs-editor-add-signature-save-warning-message = 保存された署名が上限の 5 個に達しました。さらに保存するにはいずれかを削除してください。 +pdfjs-editor-add-signature-image-upload-error-title = 画像をアップロードできません +pdfjs-editor-add-signature-image-upload-error-description = ネットワーク接続を確認するか別の画像を試してください。 +pdfjs-editor-add-signature-error-close-button = 閉じる + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = キャンセル +pdfjs-editor-add-signature-add-button = 追加 diff --git a/l10n/ka/viewer.ftl b/l10n/ka/viewer.ftl index 1a1fdb448b064..c63d44d09ba0a 100644 --- a/l10n/ka/viewer.ftl +++ b/l10n/ka/viewer.ftl @@ -31,8 +31,8 @@ pdfjs-zoom-in-button-label = მოახლოება pdfjs-zoom-select = .title = ზომა pdfjs-presentation-mode-button = - .title = ჩვენების რეჟიმზე გადართვა -pdfjs-presentation-mode-button-label = ჩვენების რეჟიმი + .title = წარდგენის რეჟიმზე გადართვა +pdfjs-presentation-mode-button-label = წარდგენის რეჟიმი pdfjs-open-file-button = .title = ფაილის გახსნა pdfjs-open-file-button-label = გახსნა @@ -105,6 +105,14 @@ pdfjs-document-properties-button-label = დოკუმენტის შე pdfjs-document-properties-file-name = ფაილის სახელი: pdfjs-document-properties-file-size = ფაილის მოცულობა: # Variables: +# $kb (Number) - the PDF file size in kilobytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } კბაიტი ({ $b } ბაიტი) +# Variables: +# $mb (Number) - the PDF file size in megabytes +# $b (Number) - the PDF file size in bytes +pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } მბაიტი ({ $b } ბაიტი) +# Variables: # $size_kb (Number) - the PDF file size in kilobytes # $size_b (Number) - the PDF file size in bytes pdfjs-document-properties-kb = { $size_kb } კბ ({ $size_b } ბაიტი) @@ -119,6 +127,9 @@ pdfjs-document-properties-keywords = საკვანძო სიტყვე pdfjs-document-properties-creation-date = შექმნის დრო: pdfjs-document-properties-modification-date = ჩასწორების დრო: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } @@ -170,7 +181,7 @@ pdfjs-printing-not-ready = გაფრთხილება: PDF სრულ pdfjs-toggle-sidebar-button = .title = გვერდითა ზოლის გამოჩენა/დამალვა pdfjs-toggle-sidebar-notification-button = - .title = გვერდითი ზოლის გამოჩენა (შეიცავს სარჩევს/დანართს/ფენებს) + .title = გვერდითი ზოლის გამოჩენა (შეიცავს სარჩევს/დანართს/შრეებს) pdfjs-toggle-sidebar-button-label = გვერდითა ზოლის გამოჩენა/დამალვა pdfjs-document-outline-button = .title = დოკუმენტის სარჩევის ჩვენება (ორმაგი წკაპით თითოეულის ჩამოშლა/აკეცვა) @@ -179,8 +190,8 @@ pdfjs-attachments-button = .title = დანართების ჩვენება pdfjs-attachments-button-label = დანართები pdfjs-layers-button = - .title = ფენების გამოჩენა (ორმაგი წკაპით ყველა ფენის ნაგულისხმევზე დაბრუნება) -pdfjs-layers-button-label = ფენები + .title = შრეების გამოჩენა (ორმაგი წკაპით ყველა შრის ნაგულისხმევზე დაბრუნება) +pdfjs-layers-button-label = შრეები pdfjs-thumbs-button = .title = შეთვალიერება pdfjs-thumbs-button-label = ესკიზები @@ -190,7 +201,7 @@ pdfjs-current-outline-item-button-label = მიმდინარე გვე pdfjs-findbar-button = .title = პოვნა დოკუმენტში pdfjs-findbar-button-label = ძიება -pdfjs-additional-layers = დამატებითი ფენები +pdfjs-additional-layers = დამატებითი შრეები ## Thumbnails panel item (tooltip and alt text for images) @@ -209,10 +220,10 @@ pdfjs-find-input = .title = ძიება .placeholder = პოვნა დოკუმენტში… pdfjs-find-previous-button = - .title = ფრაზის წინა კონტექსტის პოვნა + .title = წინა დამთხვევის პოვნა pdfjs-find-previous-button-label = წინა pdfjs-find-next-button = - .title = ფრაზის შემდეგი კონტექსტის პოვნა + .title = მომდევნო დამთხვევის პოვნა pdfjs-find-next-button-label = შემდეგი pdfjs-find-highlight-checkbox = ყველაფრის მონიშვნა pdfjs-find-match-case-checkbox-label = მთავრულით @@ -275,6 +286,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [{ $type } შენიშვნა] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -288,7 +302,7 @@ pdfjs-web-fonts-disabled = ვებშრიფტები გამორთ pdfjs-editor-free-text-button = .title = წარწერა -pdfjs-editor-free-text-button-label = ტექსტი +pdfjs-editor-free-text-button-label = წარწერა pdfjs-editor-ink-button = .title = ხაზვა pdfjs-editor-ink-button-label = ხაზვა @@ -298,6 +312,10 @@ pdfjs-editor-stamp-button-label = სურათების დართვა pdfjs-editor-highlight-button = .title = მონიშვნა pdfjs-editor-highlight-button-label = მონიშვნა +pdfjs-highlight-floating-button1 = + .title = მონიშვნა + .aria-label = მონიშვნა +pdfjs-highlight-floating-button-label = მონიშვნა ## Remove button for the various kind of editor. @@ -309,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = სურათის მოცილება pdfjs-editor-remove-highlight-button = .title = მონიშვნის მოცილება +pdfjs-editor-remove-signature-button = + .title = ხელმოწერის მოცილება ## @@ -321,6 +341,14 @@ pdfjs-editor-ink-opacity-input = გაუმჭვირვალობა pdfjs-editor-stamp-add-image-button = .title = სურათის დამატება pdfjs-editor-stamp-add-image-button-label = სურათის დამატება +# This refers to the thickness of the line used for free highlighting (not bound to text) +pdfjs-editor-free-highlight-thickness-input = სისქე +pdfjs-editor-free-highlight-thickness-title = + .title = სისქის შეცვლა წარწერის გარდა სხვა ნაწილების მონიშვნისას +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ნაწერის ჩასწორება + .default-content = დაიწყეთ აკრეფა… pdfjs-free-text = .aria-label = ნაწერის ჩასწორება pdfjs-free-text-default-content = აკრიფეთ… @@ -331,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = თანდართული წარწერა +pdfjs-editor-alt-text-edit-button = + .aria-label = დართული წარწერის ჩასწორება pdfjs-editor-alt-text-edit-button-label = თანდართული წარწერის ჩასწორება pdfjs-editor-alt-text-dialog-label = არჩევა pdfjs-editor-alt-text-dialog-description = თანდართული (შემნაცვლებელი) წარწერა გამოსადეგია მათთვის, ვინც ვერ ხედავს სურათებს ან გამოისახება მაშინ, როცა სურათი ვერ ჩაიტვირთება. @@ -346,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = მოინიშნოს მორ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = მაგალითად, „ახალგაზრდა მამაკაცი მაგიდასთან ზის და სადილობს“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = დართული წარწერა ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -397,10 +429,153 @@ pdfjs-editor-colorpicker-red = ## Show all highlights ## This is a toggle button to show/hide all the highlights. +pdfjs-editor-highlight-show-all-button-label = ყველას ჩვენება +pdfjs-editor-highlight-show-all-button = + .title = ყველას ჩვენება ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = დართული წარწერის ჩასწორება (სურათის აღწერის) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = დართული წარწერის დამატება (სურათის აღწერის) +pdfjs-editor-new-alt-text-textarea = + .placeholder = დაწერეთ თქვენი აღწერა აქ… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = მოკლე აღწერა მათთვის, ვინც ვერ ხედავს სურათს ან ვისთანაც ვერ ჩაიტვირთება სურათი. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = ეს დართული წარწერა ავტომატურადაა შედგენილი და შესაძლოა, უმართებულო იყოს. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = ვრცლად +pdfjs-editor-new-alt-text-create-automatically-button-label = დართული წარწერის ავტომატური შედგენა +pdfjs-editor-new-alt-text-not-now-button = ახლა არა +pdfjs-editor-new-alt-text-error-title = დართული წარწერის შედგენა ვერ მოხერხდა +pdfjs-editor-new-alt-text-error-description = გთხოვთ დაწეროთ საკუთარი დანართი და კვლავ სცადოთ მოგვიანებით. +pdfjs-editor-new-alt-text-error-close-button = დახურვა +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = ჩამოიტვირთება დართული წარწერის შესადეგი AI-მოდელი ({ $downloadedSize } ზომით { $totalSize } მბაიტი) + .aria-valuetext = ჩამოიტვირთება დართული წარწერის შესადეგი AI-მოდელი ({ $downloadedSize } ზომით { $totalSize } მბაიტი) +# This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = დართული წარწერა დამატებულია +pdfjs-editor-new-alt-text-added-button-label = დართული წარწერა დამატებულია +# This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = აკლია დართული წარწერა +pdfjs-editor-new-alt-text-missing-button-label = აკლია დართული წარწერა +# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = დართული წარწერის გადახედვა +pdfjs-editor-new-alt-text-to-review-button-label = დართული წარწერის გადახედვა +# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. +# Variables: +# $generatedAltText (String) - the generated alt-text. +pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = შედგენილია ავტომატურად: { $generatedAltText } ## Image alt-text settings +pdfjs-image-alt-text-settings-button = + .title = სურათის დართული წარწერის პარამეტრები +pdfjs-image-alt-text-settings-button-label = სურათის დართული წარწერის პარამეტრები +pdfjs-editor-alt-text-settings-dialog-label = სურათის დართული წარწერის პარამეტრები +pdfjs-editor-alt-text-settings-automatic-title = ავტომატურად დართული წარწერა +pdfjs-editor-alt-text-settings-create-model-button-label = დართული წარწერის ავტომატური შედგენა +pdfjs-editor-alt-text-settings-create-model-description = აღწერს სურათს მათთვის, ვინც ვერ ხედავს ან ვისთანაც ვერ ჩაიტვირთება. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = დართული წარწერის შესადგენი AI-მოდელი ({ $totalSize } მბაიტი) +pdfjs-editor-alt-text-settings-ai-model-description = ეშვება ადგილობრივად თქვენს მოწყობილობასა, ასე რომ მონაცემები დარჩება პირადი. საჭიროა წარწერის ავტომატურად დართვისთვის. +pdfjs-editor-alt-text-settings-delete-model-button = წაშლა +pdfjs-editor-alt-text-settings-download-model-button = ჩამოტვირთვა +pdfjs-editor-alt-text-settings-downloading-model-button = ჩამოიტვრითება... +pdfjs-editor-alt-text-settings-editor-title = დართული წარწერის ჩამსწორებელი +pdfjs-editor-alt-text-settings-show-dialog-button-label = გამოჩნდეს დართული წარწერის ჩამსწორებელი სურათის დამატებისთანავე +pdfjs-editor-alt-text-settings-show-dialog-description = უზრუნველყოფს, რომ თქვენს ყველა სურათს ახლდეს დართული წარწერა. +pdfjs-editor-alt-text-settings-close-button = დახურვა + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = მონიშვნა მოცილებულია +pdfjs-editor-undo-bar-message-freetext = წარწერა მოცილებულია +pdfjs-editor-undo-bar-message-ink = ნახატი მოცილებულია +pdfjs-editor-undo-bar-message-stamp = სურათი მოცილებულია +pdfjs-editor-undo-bar-message-signature = ხელმოწერა მოცილებულია +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } შენიშვნა მოცილებულია + *[other] { $count } შენიშვნა მოცილებულია + } +pdfjs-editor-undo-bar-undo-button = + .title = დაბრუნება +pdfjs-editor-undo-bar-undo-button-label = დაბრუნება +pdfjs-editor-undo-bar-close-button = + .title = დახურვა +pdfjs-editor-undo-bar-close-button-label = დახურვა + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = ეს არე საშუალებას აძლევს მომხმარებელს, შექმნას საკუთარი ხელმოწერა PDF-დოკუმენტისთვის. მომხმარებელს შეეძლება ჩაასწოროს სახელი (რომელიც დართული ტექსტის მოვალეობასაც ასრულებს) და სურვილისამებრ შეინახოს ხელმოწერა განმეორებით გამოსაყენებლად. +pdfjs-editor-add-signature-dialog-title = ხელმოწერის დამატება + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = აკრეფა + .title = აკრეფა +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = მოხაზვა + .title = მოხაზვა +pdfjs-editor-add-signature-image-button = სურათი + .title = სურათი + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = აკრიფეთ ხელმოწერა + .placeholder = აკრიფეთ ხელმოწერა +pdfjs-editor-add-signature-draw-placeholder = მოხაზეთ ხელმოწერა +pdfjs-editor-add-signature-draw-thickness-range-label = სისქე +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = მოხაზულის სისქე: { $thickness } +pdfjs-editor-add-signature-image-placeholder = ჩავლებით გადმოიტანეთ ასატვირთად +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] ან ამოარჩიეთ სურათებიდან + *[other] ან ამოარჩიეთ სურათებიდან + } + +## Controls + +pdfjs-editor-add-signature-description-label = აღწერილობა (დართული ტექსტი) +pdfjs-editor-add-signature-description-input = + .title = აღწერილობა (დართული ტექსტი) +pdfjs-editor-add-signature-description-default-when-drawing = ხელმოწერა +pdfjs-editor-add-signature-clear-button-label = ხელმოწერის წაშლა +pdfjs-editor-add-signature-clear-button = + .title = ხელმოწერის წაშლა +pdfjs-editor-add-signature-save-checkbox = ხელმოწერის შენახვა +pdfjs-editor-add-signature-save-warning-message = მიღწეულია 5 ხელმოწერის შენახვის ზღვარი. მოაცილეთ რომელიმე ახლის შესანახად. +pdfjs-editor-add-signature-image-upload-error-title = ვერ აიტვირთა სურათი +pdfjs-editor-add-signature-image-upload-error-description = შეამოწმეთ ქსელთან კავშირი ან მოსინჯეთ სხვა სურათი. +pdfjs-editor-add-signature-error-close-button = დახურვა + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = გაუქმება +pdfjs-editor-add-signature-add-button = დამატება + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/kab/viewer.ftl b/l10n/kab/viewer.ftl index dda88c1e315ac..2da4edd874f9d 100644 --- a/l10n/kab/viewer.ftl +++ b/l10n/kab/viewer.ftl @@ -353,7 +353,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Aḍris amaskal pdfjs-editor-alt-text-edit-button-label = Ẓreg aḍris amaskal pdfjs-editor-alt-text-dialog-label = Fren taxtirt @@ -436,3 +435,30 @@ pdfjs-editor-alt-text-settings-delete-model-button = Kkes pdfjs-editor-alt-text-settings-download-model-button = Sader pdfjs-editor-alt-text-settings-downloading-model-button = Asader… pdfjs-editor-alt-text-settings-close-button = Mdel + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/kk/viewer.ftl b/l10n/kk/viewer.ftl index 28a89962baf37..109a65d2de339 100644 --- a/l10n/kk/viewer.ftl +++ b/l10n/kk/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Ерекшелеу .aria-label = Ерекшелеу pdfjs-highlight-floating-button-label = Ерекшелеу +pdfjs-editor-signature-button = + .title = Қолтаңбаны қосу +pdfjs-editor-signature-button-label = Қолтаңбаны қосу ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Суретті өшіру pdfjs-editor-remove-highlight-button = .title = Түспен ерекшелеуді өшіру +pdfjs-editor-remove-signature-button = + .title = Қолтаңбаны өшіру ## @@ -343,6 +348,10 @@ pdfjs-editor-stamp-add-image-button-label = Суретті қосу pdfjs-editor-free-highlight-thickness-input = Қалыңдығы pdfjs-editor-free-highlight-thickness-title = .title = Мәтіннен басқа элементтерді ерекшелеу кезінде қалыңдықты өзгерту +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Мәтін түзеткіші + .default-content = Теріп бастаңыз… pdfjs-free-text = .aria-label = Мәтін түзеткіші pdfjs-free-text-default-content = Теруді бастау… @@ -353,8 +362,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Балама мәтін +pdfjs-editor-alt-text-edit-button = + .aria-label = Балама мәтінді өңдеу pdfjs-editor-alt-text-edit-button-label = Балама мәтінді өңдеу pdfjs-editor-alt-text-dialog-label = Опцияны таңдау pdfjs-editor-alt-text-dialog-description = Балама мәтін адамдар суретті көре алмағанда немесе ол жүктелмегенде көмектеседі. @@ -368,6 +378,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Декоративті деп бел # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Мысалы, "Жас жігіт тамақ ішу үшін үстел басына отырады" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Балама мәтін ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +462,16 @@ pdfjs-editor-new-alt-text-error-close-button = Жабу pdfjs-editor-new-alt-text-ai-model-downloading-progress = Балама мәтін үшін ЖИ моделі жүктеп алынуда ({ $downloadedSize }/{ $totalSize } МБ) .aria-valuetext = Балама мәтін үшін ЖИ моделі жүктеп алынуда ({ $downloadedSize }/{ $totalSize } МБ) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Балама мәтін қосылды pdfjs-editor-new-alt-text-added-button-label = Балама мәтін қосылды # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Балама мәтін жоқ pdfjs-editor-new-alt-text-missing-button-label = Балама мәтін жоқ # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Балама мәтінге пікір қалдыру pdfjs-editor-new-alt-text-to-review-button-label = Балама мәтінге пікір қалдыру # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +498,86 @@ pdfjs-editor-alt-text-settings-editor-title = Баламалы мәтін ред pdfjs-editor-alt-text-settings-show-dialog-button-label = Суретті қосқанда балама мәтін редакторын бірден көрсету pdfjs-editor-alt-text-settings-show-dialog-description = Барлық суреттерде балама мәтін бар екеніне көз жеткізуге көмектеседі. pdfjs-editor-alt-text-settings-close-button = Жабу + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Ерекшелеу өшірілді +pdfjs-editor-undo-bar-message-freetext = Мәтін өшірілді +pdfjs-editor-undo-bar-message-ink = Сызба өшірілді +pdfjs-editor-undo-bar-message-stamp = Сурет өшірілді +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анимация өшірілді + *[other] { $count } анимация өшірілді + } +pdfjs-editor-undo-bar-undo-button = + .title = Болдырмау +pdfjs-editor-undo-bar-undo-button-label = Болдырмау +pdfjs-editor-undo-bar-close-button = + .title = Жабу +pdfjs-editor-undo-bar-close-button-label = Жабу + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-title = Қолтаңба қосу + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Енгізу + .title = Енгізу +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Сурет салу + .title = Сурет салу +pdfjs-editor-add-signature-image-button = Сурет + .title = Сурет + +## Tab panels + +pdfjs-editor-add-signature-draw-thickness-range-label = Қалыңдығы +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Сызба қалыңздығы: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Жүктеп жіберу үшін файлды осы жерге сүйреңіз +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Немесе сурет файлдарын таңдаңыз + *[other] Немесе сурет файлдарын шолыңыз + } + +## Controls + +pdfjs-editor-add-signature-description-label = Сипаттама (балама мәтін) +pdfjs-editor-add-signature-description-input = + .title = Сипаттама (балама мәтін) +pdfjs-editor-add-signature-description-default-when-drawing = Қолтаңба +pdfjs-editor-add-signature-clear-button-label = Қолтаңбаны өшіру +pdfjs-editor-add-signature-clear-button = + .title = Қолтаңбаны өшіру +pdfjs-editor-add-signature-save-checkbox = Қолтаңбаны сақтау +pdfjs-editor-add-signature-save-warning-message = Сақталған 5 қолтаңбаның шегіне жеттіңіз. Көбірек сақтау үшін біреуін алып тастаңыз. +pdfjs-editor-add-signature-image-upload-error-title = Суретті жүктеп жіберу мүмкін емес. +pdfjs-editor-add-signature-error-close-button = Жабу + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Бас тарту +pdfjs-editor-add-signature-add-button = Қосу +pdfjs-editor-edit-signature-update-button = Жаңарту + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Қолтаңбаны өшіру +pdfjs-editor-delete-signature-button-label = Қолтаңбаны өшіру + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Сипаттаманы түзету + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Сипаттаманы түзету diff --git a/l10n/km/viewer.ftl b/l10n/km/viewer.ftl index 6efd105460f5f..af8cf52ef8d01 100644 --- a/l10n/km/viewer.ftl +++ b/l10n/km/viewer.ftl @@ -215,9 +215,56 @@ pdfjs-web-fonts-disabled = បាន​បិទ​ពុម្ពអក្ស ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/kn/viewer.ftl b/l10n/kn/viewer.ftl index 03322555e1941..61dc8e962ffe7 100644 --- a/l10n/kn/viewer.ftl +++ b/l10n/kn/viewer.ftl @@ -205,9 +205,56 @@ pdfjs-web-fonts-disabled = ಜಾಲ ಅಕ್ಷರಶೈಲಿಯನ್ನು ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ko/viewer.ftl b/l10n/ko/viewer.ftl index ee20d78d4a8a2..e07e68f868262 100644 --- a/l10n/ko/viewer.ftl +++ b/l10n/ko/viewer.ftl @@ -308,6 +308,9 @@ pdfjs-highlight-floating-button1 = .title = 강조 표시 .aria-label = 강조 표시 pdfjs-highlight-floating-button-label = 강조 표시 +pdfjs-editor-signature-button = + .title = 서명 추가 +pdfjs-editor-signature-button-label = 서명 추가 ## Remove button for the various kind of editor. @@ -319,6 +322,8 @@ pdfjs-editor-remove-stamp-button = .title = 이미지 제거 pdfjs-editor-remove-highlight-button = .title = 강조 표시 제거 +pdfjs-editor-remove-signature-button = + .title = 서명 제거 ## @@ -335,6 +340,13 @@ pdfjs-editor-stamp-add-image-button-label = 이미지 추가 pdfjs-editor-free-highlight-thickness-input = 두께 pdfjs-editor-free-highlight-thickness-title = .title = 텍스트 이외의 항목을 강조 표시할 때 두께 변경 +pdfjs-editor-signature-add-signature-button = + .title = 새 서명 추가 +pdfjs-editor-signature-add-signature-button-label = 새 서명 추가 +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 텍스트 편집기 + .default-content = 입력을 시작하세요… pdfjs-free-text = .aria-label = 텍스트 편집기 pdfjs-free-text-default-content = 입력하세요… @@ -345,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = 대체 텍스트 +pdfjs-editor-alt-text-edit-button = + .aria-label = 대체 텍스트 편집 pdfjs-editor-alt-text-edit-button-label = 대체 텍스트 편집 pdfjs-editor-alt-text-dialog-label = 옵션을 선택하세요 pdfjs-editor-alt-text-dialog-description = 대체 텍스트는 사람들이 이미지를 볼 수 없거나 이미지가 로드되지 않을 때 도움이 됩니다. @@ -360,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = 장식용으로 표시됨 # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = 예를 들어, “한 청년이 식탁에 앉아 식사를 하고 있습니다.” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 대체 텍스트 ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = 닫기 pdfjs-editor-new-alt-text-ai-model-downloading-progress = 대체 텍스트 AI 모델 다운로드 중 ({ $downloadedSize } / { $totalSize } MB) .aria-valuetext = 대체 텍스트 AI 모델 다운로드 중 ({ $downloadedSize } / { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 대체 텍스트 추가됨 pdfjs-editor-new-alt-text-added-button-label = 대체 텍스트 추가됨 # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 대체 텍스트 누락 pdfjs-editor-new-alt-text-missing-button-label = 대체 텍스트 누락 # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 대체 텍스트 검토 pdfjs-editor-new-alt-text-to-review-button-label = 대체 텍스트 검토 # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +493,89 @@ pdfjs-editor-alt-text-settings-editor-title = 대체 텍스트 편집기 pdfjs-editor-alt-text-settings-show-dialog-button-label = 이미지 추가 시 바로 대체 텍스트 편집기 표시 pdfjs-editor-alt-text-settings-show-dialog-description = 모든 이미지에 대체 텍스트가 있는지 확인하는 데 도움이 됩니다. pdfjs-editor-alt-text-settings-close-button = 닫기 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 강조 표시 제거됨 +pdfjs-editor-undo-bar-message-freetext = 텍스트 제거됨 +pdfjs-editor-undo-bar-message-ink = 그리기 제거됨 +pdfjs-editor-undo-bar-message-stamp = 이미지 제거됨 +pdfjs-editor-undo-bar-message-signature = 서명 제거됨 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 주석 { $count }개 제거됨 +pdfjs-editor-undo-bar-undo-button = + .title = 실행 취소 +pdfjs-editor-undo-bar-undo-button-label = 실행 취소 +pdfjs-editor-undo-bar-close-button = + .title = 닫기 +pdfjs-editor-undo-bar-close-button-label = 닫기 + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = 이 모달로 PDF 문서에 추가 할 서명을 만들 수 있습니다. 사용자는 이름(대체 텍스트 역할도 함)을 편집하고, 반복해 사용할 수 있도록 서명을 저장할 수도 있습니다. +pdfjs-editor-add-signature-dialog-title = 서명 추가 + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = 입력 + .title = 입력 +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = 그리기 + .title = 그리기 +pdfjs-editor-add-signature-image-button = 이미지 + .title = 이미지 + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = 서명 입력 + .placeholder = 서명 입력 +pdfjs-editor-add-signature-draw-placeholder = 서명 그리기 +pdfjs-editor-add-signature-draw-thickness-range-label = 두께 +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = 그리기 두께: { $thickness } +pdfjs-editor-add-signature-image-placeholder = 업로드할 파일을 여기로 끌어서 놓기 +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] 또는 이미지 파일 찾아보기 + *[other] 또는 이미지 파일 찾아보기 + } + +## Controls + +pdfjs-editor-add-signature-description-label = 설명 (대체 텍스트) +pdfjs-editor-add-signature-description-input = + .title = 설명 (대체 텍스트) +pdfjs-editor-add-signature-description-default-when-drawing = 서명 +pdfjs-editor-add-signature-clear-button-label = 서명 지우기 +pdfjs-editor-add-signature-clear-button = + .title = 서명 지우기 +pdfjs-editor-add-signature-save-checkbox = 서명 저장 +pdfjs-editor-add-signature-save-warning-message = 저장된 서명의 한계에 도달했습니다. 더 저장하려면 하나를 제거하세요. +pdfjs-editor-add-signature-image-upload-error-title = 이미지를 업로드할 수 없음 +pdfjs-editor-add-signature-image-upload-error-description = 네트워크 연결을 확인하거나 다른 이미지로 시도하세요. +pdfjs-editor-add-signature-error-close-button = 닫기 + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = 취소 +pdfjs-editor-add-signature-add-button = 추가 +pdfjs-editor-edit-signature-update-button = 업데이트 + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = 서명 제거 +pdfjs-editor-delete-signature-button-label = 서명 제거 + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = 설명 편집 + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = 설명 편집 diff --git a/l10n/lij/viewer.ftl b/l10n/lij/viewer.ftl index b2941f9f659b5..1fbc3259d5553 100644 --- a/l10n/lij/viewer.ftl +++ b/l10n/lij/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = I font do web en dizativæ: inposcibile adeuviâ i ca ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/lo/viewer.ftl b/l10n/lo/viewer.ftl index 557e201527a03..3a6c1f52cc747 100644 --- a/l10n/lo/viewer.ftl +++ b/l10n/lo/viewer.ftl @@ -311,3 +311,30 @@ pdfjs-ink-canvas = ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/lt/viewer.ftl b/l10n/lt/viewer.ftl index a8ee7a08f3745..daf56c1093fb3 100644 --- a/l10n/lt/viewer.ftl +++ b/l10n/lt/viewer.ftl @@ -260,9 +260,56 @@ pdfjs-web-fonts-disabled = Saityno šriftai išjungti – PDF faile esančių š ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ltg/viewer.ftl b/l10n/ltg/viewer.ftl index d262165450683..9c716b7334c09 100644 --- a/l10n/ltg/viewer.ftl +++ b/l10n/ltg/viewer.ftl @@ -238,9 +238,56 @@ pdfjs-web-fonts-disabled = Šķārsteikla fonti nav aktivizāti: Navar īgult PD ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/lv/viewer.ftl b/l10n/lv/viewer.ftl index 067dc105e659c..2da76c12e2d34 100644 --- a/l10n/lv/viewer.ftl +++ b/l10n/lv/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = Tīmekļa fonti nav aktivizēti: Nevar iegult PDF fon ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/meh/viewer.ftl b/l10n/meh/viewer.ftl index d8bddc9d543f3..28c883c32d41c 100644 --- a/l10n/meh/viewer.ftl +++ b/l10n/meh/viewer.ftl @@ -79,9 +79,56 @@ pdfjs-password-cancel-button = Nkuvi-ka ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/mk/viewer.ftl b/l10n/mk/viewer.ftl index 47d24b24091fc..b990bdaaa42a1 100644 --- a/l10n/mk/viewer.ftl +++ b/l10n/mk/viewer.ftl @@ -207,9 +207,56 @@ pdfjs-web-fonts-disabled = Интернет фонтовите се оневоз ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/mr/viewer.ftl b/l10n/mr/viewer.ftl index 49948b1938a14..4b57de7c19561 100644 --- a/l10n/mr/viewer.ftl +++ b/l10n/mr/viewer.ftl @@ -231,9 +231,56 @@ pdfjs-web-fonts-disabled = वेब टंक असमर्थीत आह ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ms/viewer.ftl b/l10n/ms/viewer.ftl index 11b866514144a..5e389394512c4 100644 --- a/l10n/ms/viewer.ftl +++ b/l10n/ms/viewer.ftl @@ -239,9 +239,56 @@ pdfjs-web-fonts-disabled = Fon web dinyahdayakan: tidak dapat menggunakan fon te ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/my/viewer.ftl b/l10n/my/viewer.ftl index d3b973d87f18a..50cf34861d4c6 100644 --- a/l10n/my/viewer.ftl +++ b/l10n/my/viewer.ftl @@ -198,9 +198,56 @@ pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fo ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/nb-NO/viewer.ftl b/l10n/nb-NO/viewer.ftl index d802ebcc4f81e..f2324859a36b9 100644 --- a/l10n/nb-NO/viewer.ftl +++ b/l10n/nb-NO/viewer.ftl @@ -343,6 +343,10 @@ pdfjs-editor-stamp-add-image-button-label = Legg til bilde pdfjs-editor-free-highlight-thickness-input = Tykkelse pdfjs-editor-free-highlight-thickness-title = .title = Endre tykkelse når du markerer andre elementer enn tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstredigering + .default-content = Begynn å skrive… pdfjs-free-text = .aria-label = Tekstredigering pdfjs-free-text-default-content = Begynn å skrive… @@ -353,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt-tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alt-tekst pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst pdfjs-editor-alt-text-dialog-label = Velg et alternativ pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikke kan se bildet eller når det ikke lastes inn. @@ -368,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Merket som dekorativ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = For eksempel, «En ung mann setter seg ved et bord for å spise et måltid» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = Lukk pdfjs-editor-new-alt-text-ai-model-downloading-progress = Laster ned alternativ tekst AI-modell ({ $downloadedSize } av { $totalSize } MB) .aria-valuetext = Laster ned alternativ tekst AI-modell ({ $downloadedSize } av { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alt-tekst lagt til pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst lagt til # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mangler alt-tekst pdfjs-editor-new-alt-text-missing-button-label = Mangler alternativ tekst # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Gjennomgå alt-tekst pdfjs-editor-new-alt-text-to-review-button-label = Gjennomgå alternativ tekst # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +493,36 @@ pdfjs-editor-alt-text-settings-editor-title = Alternativ tekst-redigerer pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis alternativ tekst-redigerer direkte når du legger til et bilde pdfjs-editor-alt-text-settings-show-dialog-description = Hjelper deg å sørge for at alle bildene dine har alternativ tekst. pdfjs-editor-alt-text-settings-close-button = Lukk + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-undo-button = + .title = Angre +pdfjs-editor-undo-bar-undo-button-label = Angre +pdfjs-editor-undo-bar-close-button = + .title = Lukk +pdfjs-editor-undo-bar-close-button-label = Lukk + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ne-NP/viewer.ftl b/l10n/ne-NP/viewer.ftl index 65193b6e8cd3a..8d29315de6843 100644 --- a/l10n/ne-NP/viewer.ftl +++ b/l10n/ne-NP/viewer.ftl @@ -226,9 +226,56 @@ pdfjs-web-fonts-disabled = वेब फन्ट असक्षम छन्: ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/nl/viewer.ftl b/l10n/nl/viewer.ftl index 7b162e42ccd64..b4b5013275321 100644 --- a/l10n/nl/viewer.ftl +++ b/l10n/nl/viewer.ftl @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Afbeelding verwijderen pdfjs-editor-remove-highlight-button = .title = Markering verwijderen +pdfjs-editor-remove-signature-button = + .title = Handtekening verwijderen ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Afbeelding toevoegen pdfjs-editor-free-highlight-thickness-input = Dikte pdfjs-editor-free-highlight-thickness-title = .title = Dikte wijzigen bij accentuering van andere items dan tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstbewerker + .default-content = Start met typen… pdfjs-free-text = .aria-label = Tekstbewerker pdfjs-free-text-default-content = Begin met typen… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatieve tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatieve tekst bewerken pdfjs-editor-alt-text-edit-button-label = Alternatieve tekst bewerken pdfjs-editor-alt-text-dialog-label = Kies een optie pdfjs-editor-alt-text-dialog-description = Alternatieve tekst helpt wanneer mensen de afbeelding niet kunnen zien of wanneer deze niet wordt geladen. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Als decoratief gemarkeerd # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Bijvoorbeeld: ‘Een jonge man gaat aan een tafel zitten om te eten’ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatieve tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Sluiten pdfjs-editor-new-alt-text-ai-model-downloading-progress = AI-model voor alternatieve tekst downloaden ({ $downloadedSize } van { $totalSize } MB) .aria-valuetext = AI-model voor alternatieve tekst downloaden ({ $downloadedSize } van { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatieve tekst toegevoegd pdfjs-editor-new-alt-text-added-button-label = Alternatieve tekst toegevoegd # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatieve tekst ontbreekt pdfjs-editor-new-alt-text-missing-button-label = Alternatieve tekst ontbreekt # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatieve tekst beoordelen pdfjs-editor-new-alt-text-to-review-button-label = Alternatieve tekst beoordelen # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,87 @@ pdfjs-editor-alt-text-settings-editor-title = Alternatieve-tekstbewerker pdfjs-editor-alt-text-settings-show-dialog-button-label = Alternatieve-tekstbewerker meteen tonen bij toevoegen van een afbeelding pdfjs-editor-alt-text-settings-show-dialog-description = Helpt u ervoor te zorgen dat al uw afbeeldingen alternatieve tekst hebben. pdfjs-editor-alt-text-settings-close-button = Sluiten + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markering verwijderd +pdfjs-editor-undo-bar-message-freetext = Tekst verwijderd +pdfjs-editor-undo-bar-message-ink = Tekening verwijderd +pdfjs-editor-undo-bar-message-stamp = Afbeelding verwijderd +pdfjs-editor-undo-bar-message-signature = Handtekening verwijderd +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotatie verwijderd + *[other] { $count } annotaties verwijderd + } +pdfjs-editor-undo-bar-undo-button = + .title = Ongedaan maken +pdfjs-editor-undo-bar-undo-button-label = Ongedaan maken +pdfjs-editor-undo-bar-close-button = + .title = Sluiten +pdfjs-editor-undo-bar-close-button-label = Sluiten + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Met deze modal kan de gebruiker een handtekening maken om aan een PDF-document toe te voegen. De gebruiker kan de naam (die ook als alternatieve tekst dient) bewerken en optioneel de ondertekening opslaan voor herhaald gebruik. +pdfjs-editor-add-signature-dialog-title = Een handtekening toevoegen + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typen + .title = Typen +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Tekenen + .title = Tekenen +pdfjs-editor-add-signature-image-button = Afbeelding + .title = Afbeelding + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Uw handtekening typen + .placeholder = Uw handtekening typen +pdfjs-editor-add-signature-draw-placeholder = Uw handtekening tekenen +pdfjs-editor-add-signature-draw-thickness-range-label = Dikte +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Tekendikte: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Sleep bestand hierheen om te uploaden +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Of kies afbeeldingsbestanden + *[other] Of kies afbeeldingsbestanden + } + +## Controls + +pdfjs-editor-add-signature-description-label = Beschrijving (alternatieve tekst) +pdfjs-editor-add-signature-description-input = + .title = Beschrijving (alternatieve tekst) +pdfjs-editor-add-signature-description-default-when-drawing = Handtekening +pdfjs-editor-add-signature-clear-button-label = Handtekening wissen +pdfjs-editor-add-signature-clear-button = + .title = Handtekening wissen +pdfjs-editor-add-signature-save-checkbox = Handtekening opslaan +pdfjs-editor-add-signature-save-warning-message = U hebt de limiet van 5 opgeslagen handtekeningen bereikt. Verwijder er een om een andere op te slaan. +pdfjs-editor-add-signature-image-upload-error-title = Kan afbeelding niet uploaden +pdfjs-editor-add-signature-image-upload-error-description = Controleer uw netwerkverbinding of probeer een andere afbeelding. +pdfjs-editor-add-signature-error-close-button = Sluiten + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Annuleren +pdfjs-editor-add-signature-add-button = Toevoegen + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/nn-NO/viewer.ftl b/l10n/nn-NO/viewer.ftl index 3044c57b97e72..e14f432866ea3 100644 --- a/l10n/nn-NO/viewer.ftl +++ b/l10n/nn-NO/viewer.ftl @@ -334,15 +334,19 @@ pdfjs-editor-remove-highlight-button = pdfjs-editor-free-text-color-input = Farge pdfjs-editor-free-text-size-input = Storleik pdfjs-editor-ink-color-input = Farge -pdfjs-editor-ink-thickness-input = Tjukkleik +pdfjs-editor-ink-thickness-input = Tjukn pdfjs-editor-ink-opacity-input = Ugjennomskinleg pdfjs-editor-stamp-add-image-button = .title = Legg til bilde pdfjs-editor-stamp-add-image-button-label = Legg til bilde # This refers to the thickness of the line used for free highlighting (not bound to text) -pdfjs-editor-free-highlight-thickness-input = Tjukkleik +pdfjs-editor-free-highlight-thickness-input = Tjukn pdfjs-editor-free-highlight-thickness-title = .title = Endre tjukn når du markerer andre element enn tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Tekstredigering + .default-content = Begynn å skrive… pdfjs-free-text = .aria-label = Tekstredigering pdfjs-free-text-default-content = Byrje å skrive… @@ -353,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt-tekst +pdfjs-editor-alt-text-edit-button = + .aria-label = Rediger alt-tekst tekst pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst pdfjs-editor-alt-text-dialog-label = Vel eit alternativ pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikkje kan sjå bildet eller når det ikkje vert lasta inn. @@ -368,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Merkt som dekorativ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Til dømes, «Ein ung mann set seg ved eit bord for å ete eit måltid» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt-tekst ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = Lat att pdfjs-editor-new-alt-text-ai-model-downloading-progress = Lastar ned AI-modell med alternativ tekst ({ $downloadedSize } av { $totalSize } MB) .aria-valuetext = Lastar ned AI-modell med alternativ tekst ({ $downloadedSize } av { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ tekst lagt til pdfjs-editor-new-alt-text-added-button-label = Alternativ tekst lagt til # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Manglande alternativ tekst pdfjs-editor-new-alt-text-missing-button-label = Manglande alternativ tekst # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Vurder alternativ tekst pdfjs-editor-new-alt-text-to-review-button-label = Vurder alternativ tekst # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +493,45 @@ pdfjs-editor-alt-text-settings-editor-title = Alternativ tekst-redigerar pdfjs-editor-alt-text-settings-show-dialog-button-label = Vis alternativ tekst-redigerar direkte når du legg til eit bilde pdfjs-editor-alt-text-settings-show-dialog-description = Hjelper deg med å sørgje for at alle bilda dine har alternativ tekst. pdfjs-editor-alt-text-settings-close-button = Lat att + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-freetext = Tekst fjerna +pdfjs-editor-undo-bar-message-ink = Teikning fjerna +pdfjs-editor-undo-bar-message-stamp = Bilde fjerna +pdfjs-editor-undo-bar-undo-button = + .title = Angre +pdfjs-editor-undo-bar-undo-button-label = Angre +pdfjs-editor-undo-bar-close-button = + .title = Lat att +pdfjs-editor-undo-bar-close-button-label = Lat att + +## Add a signature dialog + + +## Tab names + + +## Tab panels + +pdfjs-editor-add-signature-draw-thickness-range-label = Tjukn + +## Controls + +pdfjs-editor-add-signature-image-upload-error-title = Klarte ikkje å oppdatere bilde +pdfjs-editor-add-signature-error-close-button = Lat att + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Avbryt +pdfjs-editor-add-signature-add-button = Legg til +pdfjs-editor-edit-signature-update-button = Oppdater + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/oc/viewer.ftl b/l10n/oc/viewer.ftl index 76bef4fedb5b5..d022f5da1cdc3 100644 --- a/l10n/oc/viewer.ftl +++ b/l10n/oc/viewer.ftl @@ -220,6 +220,21 @@ pdfjs-find-match-diacritics-checkbox-label = Respectar los diacritics pdfjs-find-entire-word-checkbox-label = Mots entièrs pdfjs-find-reached-top = Naut de la pagina atenh, perseguida del bas pdfjs-find-reached-bottom = Bas de la pagina atench, perseguida al començament +# Variables: +# $current (Number) - the index of the currently active find result +# $total (Number) - the total number of matches in the document +pdfjs-find-match-count = + { $total -> + [one] Ocurréncia { $current } de { $total } + *[other] Ocurréncia { $current } de { $total } + } +# Variables: +# $limit (Number) - the maximum number of matches +pdfjs-find-match-count-limit = + { $limit -> + [one] Mai de { $limit } ocurréncia + *[other] Mai de { $limit } ocurréncias + } pdfjs-find-not-found = Frasa pas trobada ## Predefined zoom values @@ -260,6 +275,9 @@ pdfjs-annotation-date-string = { $date } a { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [Anotacion { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -312,6 +330,10 @@ pdfjs-editor-stamp-add-image-button = pdfjs-editor-stamp-add-image-button-label = Apondre imatge # This refers to the thickness of the line used for free highlighting (not bound to text) pdfjs-editor-free-highlight-thickness-input = Espessor +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de tèxte + .default-content = Començatz de picar… pdfjs-free-text = .aria-label = Editor de tèxte pdfjs-free-text-default-content = Començatz d’escriure… @@ -322,7 +344,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Tèxt alternatiu pdfjs-editor-alt-text-edit-button-label = Modificar lo tèxt alternatiu pdfjs-editor-alt-text-dialog-label = Causir una opcion @@ -359,3 +380,54 @@ pdfjs-editor-colorpicker-red = pdfjs-editor-highlight-show-all-button-label = O afichar tot pdfjs-editor-highlight-show-all-button = .title = O afichar tot + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + +pdfjs-editor-new-alt-text-error-close-button = Tampar + +## Image alt-text settings + +pdfjs-editor-alt-text-settings-automatic-title = Tèxte alternatiu automatic +pdfjs-editor-alt-text-settings-create-model-button-label = Crear un tèxte alternatiu automaticament +pdfjs-editor-alt-text-settings-delete-model-button = Suprimir +pdfjs-editor-alt-text-settings-download-model-button = Telecargar +pdfjs-editor-alt-text-settings-downloading-model-button = Telecargament… +pdfjs-editor-alt-text-settings-editor-title = Editor de tèxte alternatiu +pdfjs-editor-alt-text-settings-close-button = Tampar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-freetext = Tèxte suprimit +pdfjs-editor-undo-bar-message-ink = Dessenh suprimit +pdfjs-editor-undo-bar-message-stamp = Imatge suprimit +pdfjs-editor-undo-bar-undo-button = + .title = Anullar +pdfjs-editor-undo-bar-undo-button-label = Anullar +pdfjs-editor-undo-bar-close-button = + .title = Tampar +pdfjs-editor-undo-bar-close-button-label = Tampar + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/pa-IN/viewer.ftl b/l10n/pa-IN/viewer.ftl index f977edf297f01..83c0f7ea93031 100644 --- a/l10n/pa-IN/viewer.ftl +++ b/l10n/pa-IN/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = ਹਾਈਲਾਈਟ .aria-label = ਹਾਈਲਾਈਟ pdfjs-highlight-floating-button-label = ਹਾਈਲਾਈਟ +pdfjs-editor-signature-button = + .title = ਦਸਤਖ਼ਤ ਜੋੜੋ +pdfjs-editor-signature-button-label = ਦਸਤਖ਼ਤ ਜੋੜੋ ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = ਚਿੱਤਰ ਨੂੰ ਹਟਾਓ pdfjs-editor-remove-highlight-button = .title = ਹਾਈਲਾਈਟ ਨੂੰ ਹਟਾਓ +pdfjs-editor-remove-signature-button = + .title = ਦਸਤਖ਼ਤ ਨੂੰ ਹਟਾਓ ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = ਚਿੱਤਰ ਜੋੜੋ pdfjs-editor-free-highlight-thickness-input = ਮੋਟਾਈ pdfjs-editor-free-highlight-thickness-title = .title = ਚੀਜ਼ਾਂ ਨੂੰ ਹੋਰ ਲਿਖਤਾਂ ਤੋਂ ਉਘਾੜਨ ਸਮੇਂ ਮੋਟਾਈ ਨੂੰ ਬਦਲੋ +pdfjs-editor-signature-add-signature-button = + .title = ਨਵੇਂ ਦਸਤਖ਼ਤ ਨੂੰ ਜੋੜੋ +pdfjs-editor-signature-add-signature-button-label = ਨਵੇਂ ਦਸਤਖ਼ਤ ਨੂੰ ਜੋੜੋ +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ਲਿਖਤ ਐਡੀਟਰ + .default-content = …ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ pdfjs-free-text = .aria-label = ਲਿਖਤ ਐਡੀਟਰ pdfjs-free-text-default-content = …ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = ਬਦਲਵੀਂ ਲਿਖਤ +pdfjs-editor-alt-text-edit-button = + .aria-label = ਬਦਲਵੀ ਲਿਖਤ ਨੂੰ ਸੋਧੋ pdfjs-editor-alt-text-edit-button-label = ਬਦਲਵੀ ਲਿਖਤ ਨੂੰ ਸੋਧੋ pdfjs-editor-alt-text-dialog-label = ਚੋਣ ਕਰੋ pdfjs-editor-alt-text-dialog-description = ਚਿੱਤਰ ਨਾ ਦਿੱਸਣ ਜਾਂ ਲੋਡ ਨਾ ਹੋਣ ਦੀ ਹਾਲਤ ਵਿੱਚ Alt ਲਿਖਤ (ਬਦਲਵੀਂ ਲਿਖਤ) ਲੋਕਾਂ ਲਈ ਮਦਦਗਾਰ ਹੁੰਦੀ ਹੈ। @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = ਸਜਾਵਟ ਵਜੋਂ ਨਿ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = ਮਿਸਾਲ ਵਜੋਂ, “ਗੱਭਰੂ ਭੋਜਨ ਲੈ ਕੇ ਮੇਜ਼ ਉੱਤੇ ਬੈਠਾ ਹੈ” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = ਬੰਦ ਕਰੋ pdfjs-editor-new-alt-text-ai-model-downloading-progress = ਬਦਲਵਾਂ ਲਿਖਤ AI ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ({ $totalSize } MB ਵਿੱਚੋਂ { $downloadedSize }) .aria-valuetext = ਬਦਲਵਾਂ ਲਿਖਤ AI ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ({ $totalSize } MB ਵਿੱਚੋਂ { $downloadedSize }) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ ਜੋੜੀ pdfjs-editor-new-alt-text-added-button-label = ਬਦਲਵੀਂ ਲਿਖਤ ਜੋੜੀ # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = ਬਦਲਵਾਂ ਲਿਖਤ ਗੁੰਮ ਹੈ pdfjs-editor-new-alt-text-missing-button-label = ਬਦਲਵਾਂ ਲਿਖਤ ਗੁੰਮ ਹੈ # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = ਬਦਲਵੀਂ ਲਿਖਤ ਦਾ ਰੀਵਿਊ ਕਰੋ pdfjs-editor-new-alt-text-to-review-button-label = ਬਦਲਵੀਂ ਲਿਖਤ ਦਾ ਰੀਵਿਊ ਕਰੋ # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = ਬਦਲਵੀਂ ਲਿਖਤ pdfjs-editor-alt-text-settings-show-dialog-button-label = ਜਦੋਂ ਵਿੱਚ ਚਿੱਤਰ ਜੋੜਿਆ ਜਾਵੇ ਤਾਂ ਫ਼ੌਰਨ ਬਦਲਵੀ ਲਿਖਤ ਸੰਪਾਦਕ ਵੇਖਾਓ pdfjs-editor-alt-text-settings-show-dialog-description = ਤੁਹਾਡੀ ਮਦਦ ਕਰਦਾ ਹੈ ਕਿ ਤੁਹਾਡੇ ਸਾਰੇ ਚਿੱਤਰਾਂ ਲਈ ਬਦਲਵੀਂ ਲਿਖਤ ਮੌਜੂਦ ਹੋਵੇ। pdfjs-editor-alt-text-settings-close-button = ਬੰਦ ਕਰੋ + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = ਹਾਈਲਾਈਟ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-freetext = ਲਿਖਤ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-ink = ਡਰਾਇੰਗ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-stamp = ਚਿੱਤਰ ਨੂੰ ਹਟਾਇਆ ਗਿਆ +pdfjs-editor-undo-bar-message-signature = ਦਸਤਖ਼ਤ ਨੂੰ ਹਟਾਇਆ +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ਵਿਆਖਿਆ ਨੂੰ ਹਟਾਇਆ + *[other] { $count } ਵਿਆਖਿਆਵਾਂ ਨੂੰ ਹਟਾਇਆ + } +pdfjs-editor-undo-bar-undo-button = + .title = ਵਾਪਸ +pdfjs-editor-undo-bar-undo-button-label = ਵਾਪਸ +pdfjs-editor-undo-bar-close-button = + .title = ਬੰਦ ਕਰੋ +pdfjs-editor-undo-bar-close-button-label = ਬੰਦ ਕਰੋ + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = ਇਹ ਮਾਡਲ ਵਰਤੋਂਕਾਰ ਨੂੰ PDF ਦਸਤਾਵੇਜ਼ ਵਿੱਚ ਜੋੜਨ ਲਈ ਦਸਤਖ਼ਤ ਬਣਾਉਣ ਦਿੰਦਾ ਹੈ। ਵਰਤੋਂਕਾਰ ਨਾਂ ਨੂੰ ਸੋਧ ਸਕਦਾ ਹੈ (ਜੋ ਕਿ ਬਦਲਵੀਂ ਲਿਖਤ ਵਜੋਂ ਕੰਮ ਕਰੇਗਾ) ਅਤੇ ਦੁਬਾਰਾ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦਸਤਖ਼ਤਾਂ ਨੂੰ ਸੰਭਾਲ ਵੀ ਸਕਦਾ ਹੈ। +pdfjs-editor-add-signature-dialog-title = ਦਸਤਖ਼ਤ ਨੂੰ ਜੋੜੋ + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = ਕਿਸਮ + .title = ਕਿਸਮ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = ਵਾਹੋ + .title = ਵਾਹੋ +pdfjs-editor-add-signature-image-button = ਚਿੱਤਰ + .title = ਚਿੱਤਰ + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = ਆਪਣੇ ਦਸਤਖ਼ਤ ਨੂੰ ਟਾਈਪ ਕਰੋ + .placeholder = ਆਪਣੇ ਦਸਤਖ਼ਤ ਨੂੰ ਟਾਈਪ ਕਰੋ +pdfjs-editor-add-signature-draw-placeholder = ਆਪਣੇ ਦਸਤਖ਼ਤ ਨੂੰ ਵਾਹੋ +pdfjs-editor-add-signature-draw-thickness-range-label = ਮੋਟਾਈ +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = ਵਹਾਉਣ ਲਈ ਚੌੜਾਈ: { $thickness } +pdfjs-editor-add-signature-image-placeholder = ਅੱਪਲੋਡ ਕਰਨ ਲਈ ਫ਼ਾਇਲ ਨੂੰ ਇੱਥੇ ਖਿੱਚੋ +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] ਜਾਂ ਚਿੱਤਰ ਫ਼ਾਇਲਾਂ ਨੂੰ ਚੁਣੋ + *[other] ਜਾਂ ਚਿੱਤਰ ਫ਼ਾਇਲਾਂ ਦੀ ਝਲਕ ਵੇਖੋ + } + +## Controls + +pdfjs-editor-add-signature-description-label = ਵਰਣਨ (ਬਦਲਵੀਂ ਲਿਖਤ) +pdfjs-editor-add-signature-description-input = + .title = ਵਰਣਨ (ਬਦਲਵੀਂ ਲਿਖਤ) +pdfjs-editor-add-signature-description-default-when-drawing = ਦਸਤਖ਼ਤ +pdfjs-editor-add-signature-clear-button-label = ਦਸਤਖ਼ਤ ਨੂੰ ਮਿਟਾਓ +pdfjs-editor-add-signature-clear-button = + .title = ਦਸਤਖ਼ਤ ਨੂੰ ਮਿਟਾਓ +pdfjs-editor-add-signature-save-checkbox = ਦਸਤਖ਼ਤ ਨੂੰ ਸੰਭਾਲੋ +pdfjs-editor-add-signature-save-warning-message = ਤੁਸੀਂ ਵੱਧ ਤੋਂ ਵੱਧ 5 ਸੰਭਾਲੇ ਦਸਤਖ਼ਤਾਂ ਦੀ ਹੱਦ ਤੱਕ ਅੱਪੜੇ। ਹੋਰ ਸੰਭਾਲਣ ਲਈ ਇੱਕ ਨੂੰ ਹਟਾਓ। +pdfjs-editor-add-signature-image-upload-error-title = ਚਿੱਤਰ ਨੂੰ ਅੱਪਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ +pdfjs-editor-add-signature-image-upload-error-description = ਆਪਣੇ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਜਾਂ ਹੋਰ ਚਿੱਤਰ ਨੂੰ ਅਜ਼ਮਾਓ। +pdfjs-editor-add-signature-error-close-button = ਬੰਦ ਕਰੋ + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = ਰੱਦ ਕਰੋ +pdfjs-editor-add-signature-add-button = ਜੋੜੋ +pdfjs-editor-edit-signature-update-button = ਅੱਪਡੇਟ + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = ਦਸਤਖ਼ਤ ਨੂੰ ਹਟਾਓ +pdfjs-editor-delete-signature-button-label = ਦਸਤਖ਼ਤ ਨੂੰ ਹਟਾਓ + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = ਵਰਣਨ ਨੂੰ ਸੋਧੋ + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = ਵਰਣਨ ਨੂੰ ਸੋਧੋ diff --git a/l10n/pl/viewer.ftl b/l10n/pl/viewer.ftl index 5ed53a29c3ff9..8fe75bbd71b45 100644 --- a/l10n/pl/viewer.ftl +++ b/l10n/pl/viewer.ftl @@ -345,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Dodaj obraz pdfjs-editor-free-highlight-thickness-input = Grubość pdfjs-editor-free-highlight-thickness-title = .title = Zmień grubość podczas wyróżniania elementów innych niż tekst +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Edytor tekstu + .default-content = Zacznij pisać… pdfjs-free-text = .aria-label = Edytor tekstu pdfjs-free-text-default-content = Zacznij pisać… @@ -355,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Tekst alternatywny +pdfjs-editor-alt-text-edit-button = + .aria-label = Edytuj tekst alternatywny pdfjs-editor-alt-text-edit-button-label = Edytuj tekst alternatywny pdfjs-editor-alt-text-dialog-label = Wybierz opcję pdfjs-editor-alt-text-dialog-description = Tekst alternatywny pomaga, kiedy ktoś nie może zobaczyć obrazu lub gdy się nie wczytuje. @@ -370,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Oznaczone jako dekoracyjne # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Na przykład: „Młody człowiek siada przy stole, aby zjeść posiłek” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Tekst alternatywny ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -451,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Zamknij pdfjs-editor-new-alt-text-ai-model-downloading-progress = Pobieranie modelu SI tekstu alternatywnego ({ $downloadedSize } z { $totalSize } MB) .aria-valuetext = Pobieranie modelu SI tekstu alternatywnego ({ $downloadedSize } z { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Dodano tekst alternatywny pdfjs-editor-new-alt-text-added-button-label = Dodano tekst alternatywny # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Brak tekstu alternatywnego pdfjs-editor-new-alt-text-missing-button-label = Brak tekstu alternatywnego # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Przejrzyj tekst alternatywny pdfjs-editor-new-alt-text-to-review-button-label = Przejrzyj tekst alternatywny # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -481,3 +495,48 @@ pdfjs-editor-alt-text-settings-editor-title = Edytor tekstu alternatywnego pdfjs-editor-alt-text-settings-show-dialog-button-label = Wyświetlanie edytora tekstu alternatywnego od razu po dodaniu obrazu pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga upewnić się, że wszystkie obrazy mają tekst alternatywny. pdfjs-editor-alt-text-settings-close-button = Zamknij + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Usunięto wyróżnienie +pdfjs-editor-undo-bar-message-freetext = Usunięto tekst +pdfjs-editor-undo-bar-message-ink = Usunięto rysunek +pdfjs-editor-undo-bar-message-stamp = Usunięto obraz +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] Usunięto przypis + [few] Usunięto { $count } przypisy + *[many] Usunięto { $count } przypisów + } +pdfjs-editor-undo-bar-undo-button = + .title = Cofnij +pdfjs-editor-undo-bar-undo-button-label = Cofnij +pdfjs-editor-undo-bar-close-button = + .title = Zamknij +pdfjs-editor-undo-bar-close-button-label = Zamknij + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/pt-BR/viewer.ftl b/l10n/pt-BR/viewer.ftl index 3205411ace4f0..12a662ed85647 100644 --- a/l10n/pt-BR/viewer.ftl +++ b/l10n/pt-BR/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Destaque .aria-label = Destaque pdfjs-highlight-floating-button-label = Destaque +pdfjs-editor-signature-button = + .title = Adicionar assinatura +pdfjs-editor-signature-button-label = Adicionar assinatura ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Remover imagem pdfjs-editor-remove-highlight-button = .title = Remover destaque +pdfjs-editor-remove-signature-button = + .title = Remover assinatura ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Adicionar imagem pdfjs-editor-free-highlight-thickness-input = Espessura pdfjs-editor-free-highlight-thickness-title = .title = Mudar espessura ao destacar itens que não são texto +pdfjs-editor-signature-add-signature-button = + .title = Adicionar nova assinatura +pdfjs-editor-signature-add-signature-button-label = Adicionar nova assinatura +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comece a digitar… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Comece digitando… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo pdfjs-editor-alt-text-dialog-label = Escolha uma opção pdfjs-editor-alt-text-dialog-description = O texto alternativo ajuda quando uma imagem não aparece ou não é carregada. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativa # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -431,7 +447,7 @@ pdfjs-editor-new-alt-text-dialog-edit-label = Editar texto alternativo (descriç # Modal header positioned above a text box where users can add the alt text. pdfjs-editor-new-alt-text-dialog-add-label = Adicionar texto alternativo (descrição da imagem) pdfjs-editor-new-alt-text-textarea = - .placeholder = Escreva sua descrição aqui… + .placeholder = Você pode escrever uma descrição aqui… # This text refers to the alt text box above this description. It offers a definition of alt text. pdfjs-editor-new-alt-text-description = Descrição curta para pessoas que não conseguem ver a imagem ou quando a imagem não é carregada. # This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Fechar pdfjs-editor-new-alt-text-ai-model-downloading-progress = Baixando modelo de inteligência artificial de texto alternativo ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = Baixando modelo de inteligência artificial de texto alternativo ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo adicionado pdfjs-editor-new-alt-text-added-button-label = Texto alternativo adicionado # This is a button that users can click to open the alt text editor and add alt text when it is not present. -pdfjs-editor-new-alt-text-missing-button-label = Falta texto alternativo +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Sem texto alternativo +pdfjs-editor-new-alt-text-missing-button-label = Sem texto alternativo # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Revisar texto alternativo pdfjs-editor-new-alt-text-to-review-button-label = Revisar texto alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar o editor de texto alternativo imediatamente ao adicionar uma imagem pdfjs-editor-alt-text-settings-show-dialog-description = Ajuda a assegurar que todas as suas imagens tenham texto alternativo. pdfjs-editor-alt-text-settings-close-button = Fechar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Destaque removido +pdfjs-editor-undo-bar-message-freetext = Texto removido +pdfjs-editor-undo-bar-message-ink = Desenho removido +pdfjs-editor-undo-bar-message-stamp = Imagem removida +pdfjs-editor-undo-bar-message-signature = Assinatura removida +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotação removida + *[other] { $count } anotações removidas + } +pdfjs-editor-undo-bar-undo-button = + .title = Desfazer +pdfjs-editor-undo-bar-undo-button-label = Desfazer +pdfjs-editor-undo-bar-close-button = + .title = Fechar +pdfjs-editor-undo-bar-close-button-label = Fechar + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Esta janela permite ao usuário criar uma assinatura para adicionar a um documento PDF. O usuário pode editar o nome (que também serve como texto alternativo) e, opcionalmente, salvar a assinatura usar novamente. +pdfjs-editor-add-signature-dialog-title = Adicionar uma assinatura + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Digitar + .title = Digitar +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Desenhar + .title = Desenhar +pdfjs-editor-add-signature-image-button = Imagem + .title = Imagem + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Digite sua assinatura + .placeholder = Digite sua assinatura +pdfjs-editor-add-signature-draw-placeholder = Desenhe sua assinatura +pdfjs-editor-add-signature-draw-thickness-range-label = Espessura +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Espessura do desenho: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Arraste um arquivo aqui para enviar +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ou escolha arquivos de imagem + *[other] Ou escolha arquivos de imagem + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descrição (texto alternativo) +pdfjs-editor-add-signature-description-input = + .title = Descrição (texto alternativo) +pdfjs-editor-add-signature-description-default-when-drawing = Assinatura +pdfjs-editor-add-signature-clear-button-label = Limpar assinatura +pdfjs-editor-add-signature-clear-button = + .title = Limpar assinatura +pdfjs-editor-add-signature-save-checkbox = Salvar assinatura +pdfjs-editor-add-signature-save-warning-message = Você atingiu o limite de 5 assinaturas salvas. Remova uma para salvar mais. +pdfjs-editor-add-signature-image-upload-error-title = Não foi possível enviar a imagem +pdfjs-editor-add-signature-image-upload-error-description = Verifique sua conexão de rede ou tente outra imagem. +pdfjs-editor-add-signature-error-close-button = Fechar + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancelar +pdfjs-editor-add-signature-add-button = Adicionar +pdfjs-editor-edit-signature-update-button = Atualizar + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Remover assinatura +pdfjs-editor-delete-signature-button-label = Remover assinatura + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Mudar descrição + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Mudar descrição diff --git a/l10n/pt-PT/viewer.ftl b/l10n/pt-PT/viewer.ftl index 4184c0aa95b8f..5256a11e9e3dc 100644 --- a/l10n/pt-PT/viewer.ftl +++ b/l10n/pt-PT/viewer.ftl @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Remover imagem pdfjs-editor-remove-highlight-button = .title = Remover destaque +pdfjs-editor-remove-signature-button = + .title = Remover assinatura ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Adicionar imagem pdfjs-editor-free-highlight-thickness-input = Espessura pdfjs-editor-free-highlight-thickness-title = .title = Alterar espessura quando destacar itens que não sejam texto +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editor de texto + .default-content = Comece a escrever… pdfjs-free-text = .aria-label = Editor de texto pdfjs-free-text-default-content = Começar a digitar… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Texto alternativo +pdfjs-editor-alt-text-edit-button = + .aria-label = Editar texto alternativo pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo pdfjs-editor-alt-text-dialog-label = Escolher uma opção pdfjs-editor-alt-text-dialog-description = O texto alternativo (texto alternativo) ajuda quando as pessoas não conseguem ver a imagem ou quando a mesma não é carregada. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Texto alternativo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Fechar pdfjs-editor-new-alt-text-ai-model-downloading-progress = A transferir o modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) .aria-valuetext = A transferir o modelo de IA de texto alternativo ({ $downloadedSize } de { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Texto alternativo adicionado pdfjs-editor-new-alt-text-added-button-label = Texto alternativo adicionado # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Texto alternativo em falta pdfjs-editor-new-alt-text-missing-button-label = Texto alternativo em falta # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Rever texto alternativo pdfjs-editor-new-alt-text-to-review-button-label = Rever texto alternativo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,87 @@ pdfjs-editor-alt-text-settings-editor-title = Editor de texto alternativo pdfjs-editor-alt-text-settings-show-dialog-button-label = Mostrar editor de texto alternativo imediatamente ao adicionar uma imagem pdfjs-editor-alt-text-settings-show-dialog-description = Ajuda a garantir que todas as suas imagens tenham um texto alternativo. pdfjs-editor-alt-text-settings-close-button = Fechar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Destaque removido +pdfjs-editor-undo-bar-message-freetext = Texto removido +pdfjs-editor-undo-bar-message-ink = Desenho removido +pdfjs-editor-undo-bar-message-stamp = Imagem removida +pdfjs-editor-undo-bar-message-signature = Assinatura removida +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotação removida + *[other] { $count } anotações removidas + } +pdfjs-editor-undo-bar-undo-button = + .title = Anular +pdfjs-editor-undo-bar-undo-button-label = Anular +pdfjs-editor-undo-bar-close-button = + .title = Fechar +pdfjs-editor-undo-bar-close-button-label = Fechar + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Este modo permite ao utilizador criar uma assinatura para adicionar a um documento PDF. O utilizador pode editar o nome (que também funciona como texto alternativo) e, opcionalmente, guardar a assinatura para utilizações frequentes. +pdfjs-editor-add-signature-dialog-title = Adicionar uma assinatura + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Digitar + .title = Digitar +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Desenhar + .title = Desenhar +pdfjs-editor-add-signature-image-button = Imagem + .title = Imagem + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Digite a sua assinatura + .placeholder = Digite a sua assinatura +pdfjs-editor-add-signature-draw-placeholder = Desenhe a sua assinatura +pdfjs-editor-add-signature-draw-thickness-range-label = Espessura +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Espessura do desenho: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Arraste um ficheiro aqui para carregar +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ou escolha ficheiros de imagem + *[other] Ou explore ficheiros de imagem + } + +## Controls + +pdfjs-editor-add-signature-description-label = Descrição (texto alternativo) +pdfjs-editor-add-signature-description-input = + .title = Descrição (texto alternativo) +pdfjs-editor-add-signature-description-default-when-drawing = Assinatura +pdfjs-editor-add-signature-clear-button-label = Limpar assinatura +pdfjs-editor-add-signature-clear-button = + .title = Limpar assinatura +pdfjs-editor-add-signature-save-checkbox = Guardar assinatura +pdfjs-editor-add-signature-save-warning-message = Atingiu o limite de 5 assinaturas guardadas. Remova uma para guardar mais. +pdfjs-editor-add-signature-image-upload-error-title = Não foi possível carregar a imagem +pdfjs-editor-add-signature-image-upload-error-description = Verifique a sua ligação à rede ou tente outra imagem. +pdfjs-editor-add-signature-error-close-button = Fechar + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Cancelar +pdfjs-editor-add-signature-add-button = Adicionar + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/rm/viewer.ftl b/l10n/rm/viewer.ftl index 2c28eedd73641..5a151abefa5f6 100644 --- a/l10n/rm/viewer.ftl +++ b/l10n/rm/viewer.ftl @@ -327,6 +327,8 @@ pdfjs-editor-remove-stamp-button = .title = Allontanar la grafica pdfjs-editor-remove-highlight-button = .title = Allontanar l'emfasa +pdfjs-editor-remove-signature-button = + .title = Allontanar la signatura ## @@ -343,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Agiuntar in maletg pdfjs-editor-free-highlight-thickness-input = Grossezza pdfjs-editor-free-highlight-thickness-title = .title = Midar la grossezza cun relevar elements betg textuals +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Editur da text + .default-content = Cumenza a tippar… pdfjs-free-text = .aria-label = Editur da text pdfjs-free-text-default-content = Cumenzar a tippar… @@ -353,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Text alternativ +pdfjs-editor-alt-text-edit-button = + .aria-label = Modifitgar il text alternativ pdfjs-editor-alt-text-edit-button-label = Modifitgar il text alternativ pdfjs-editor-alt-text-dialog-label = Tscherner ina opziun pdfjs-editor-alt-text-dialog-description = Il text alternativ (alt text) gida en cas che persunas na vesan betg il maletg u sch'i na reussescha betg d'al chargiar. @@ -368,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Marcà sco decorativ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Per exempel: «In um giuven sesa a maisa per mangiar in past» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Text alternativ ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Serrar pdfjs-editor-new-alt-text-ai-model-downloading-progress = Telechargiar il model IA da text alternativ ({ $downloadedSize } da { $totalSize } MB) .aria-valuetext = Telechargiar il model IA da text alternativ ({ $downloadedSize } da { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Agiuntà text alternativ pdfjs-editor-new-alt-text-added-button-label = Text alternativ agiuntà # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Text alternativ manca pdfjs-editor-new-alt-text-missing-button-label = Text alternativ manca # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Repassar il text alternativ pdfjs-editor-new-alt-text-to-review-button-label = Repassar il text alternativ # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +495,63 @@ pdfjs-editor-alt-text-settings-editor-title = Editur per text alternativ pdfjs-editor-alt-text-settings-show-dialog-button-label = Mussar l’editur per text alternativ directamain cun agiuntar in maletg pdfjs-editor-alt-text-settings-show-dialog-description = Ta gida a garantir che tut tes maletgs hajan in text alternativ. pdfjs-editor-alt-text-settings-close-button = Serrar + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Allontanà la marcaziun +pdfjs-editor-undo-bar-message-freetext = Allontanà il text +pdfjs-editor-undo-bar-message-ink = Allontanà il dissegn +pdfjs-editor-undo-bar-message-stamp = Allontanà il maletg +pdfjs-editor-undo-bar-message-signature = Allontanà la signatura +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } annotaziun allontanada + *[other] { $count } annotaziuns allontanadas + } +pdfjs-editor-undo-bar-undo-button = + .title = Revocar +pdfjs-editor-undo-bar-undo-button-label = Revocar +pdfjs-editor-undo-bar-close-button = + .title = Serrar +pdfjs-editor-undo-bar-close-button-label = Serrar + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Questa fanestra permetta a l’utilisader da crear ina signatura per l’agiuntar ad in document PDF. L’utilisader po modifitgar il num (che serva era sco text alternativ) e memorisar opziunalmain la signatura per l’utilisar anc ina giada en l’avegnir. +pdfjs-editor-add-signature-dialog-title = Agiuntar ina signatura + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Tippar + .title = Tippar +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Dissegnar + .title = Dissegnar +pdfjs-editor-add-signature-image-button = Maletg + .title = Maletg + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Tippa tia signatura + .placeholder = Tippa tia signatura +pdfjs-editor-add-signature-draw-placeholder = Dissegna tia signatura +pdfjs-editor-add-signature-draw-thickness-range-label = Grossezza + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ro/viewer.ftl b/l10n/ro/viewer.ftl index 7c6f0b6a33d71..b0c3e8431e3ea 100644 --- a/l10n/ro/viewer.ftl +++ b/l10n/ro/viewer.ftl @@ -243,9 +243,56 @@ pdfjs-web-fonts-disabled = Fonturile web sunt dezactivate: nu se pot folosi font ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ru/viewer.ftl b/l10n/ru/viewer.ftl index fa31170268283..3011e8cb7c3f7 100644 --- a/l10n/ru/viewer.ftl +++ b/l10n/ru/viewer.ftl @@ -318,6 +318,9 @@ pdfjs-highlight-floating-button1 = .title = Выделение .aria-label = Выделение pdfjs-highlight-floating-button-label = Выделение +pdfjs-editor-signature-button = + .title = Добавить подпись +pdfjs-editor-signature-button-label = Добавить подпись ## Remove button for the various kind of editor. @@ -329,6 +332,8 @@ pdfjs-editor-remove-stamp-button = .title = Удалить изображение pdfjs-editor-remove-highlight-button = .title = Удалить выделение +pdfjs-editor-remove-signature-button = + .title = Удалить подпись ## @@ -345,6 +350,13 @@ pdfjs-editor-stamp-add-image-button-label = Добавить изображен pdfjs-editor-free-highlight-thickness-input = Толщина pdfjs-editor-free-highlight-thickness-title = .title = Изменить толщину при выделении элементов, кроме текста +pdfjs-editor-signature-add-signature-button = + .title = Добавить новую подпись +pdfjs-editor-signature-add-signature-button-label = Добавить новую подпись +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Текстовый редактор + .default-content = Начните ввод... pdfjs-free-text = .aria-label = Текстовый редактор pdfjs-free-text-default-content = Начните вводить… @@ -355,8 +367,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Альтернативный текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Изменить альтернативный текст pdfjs-editor-alt-text-edit-button-label = Изменить альтернативный текст pdfjs-editor-alt-text-dialog-label = Выберите вариант pdfjs-editor-alt-text-dialog-description = Альтернативный текст помогает, когда люди не видят изображение или оно не загружается. @@ -370,6 +383,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Помечен как декорат # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Например: «Молодой человек садится за стол, чтобы поесть» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтернативный текст ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -451,10 +467,16 @@ pdfjs-editor-new-alt-text-error-close-button = Закрыть pdfjs-editor-new-alt-text-ai-model-downloading-progress = Загрузка модели ИИ для альтернативного текста ({ $downloadedSize } из { $totalSize } МБ) .aria-valuetext = Загрузка модели ИИ для альтернативного текста ({ $downloadedSize } из { $totalSize } МБ) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Альтернативный текст добавлен pdfjs-editor-new-alt-text-added-button-label = Альтернативный текст добавлен # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Отсутствует альтернативный текст pdfjs-editor-new-alt-text-missing-button-label = Отсутствует альтернативный текст # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Оценить альтернативный текст pdfjs-editor-new-alt-text-to-review-button-label = Оценить альтернативный текст # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -481,3 +503,94 @@ pdfjs-editor-alt-text-settings-editor-title = Редактор альтерна pdfjs-editor-alt-text-settings-show-dialog-button-label = Сразу показывать редактор альтернативного текста при добавлении изображения pdfjs-editor-alt-text-settings-show-dialog-description = Помогает вам убедиться, что все ваши изображения имеют альтернативный текст. pdfjs-editor-alt-text-settings-close-button = Закрыть + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Выделение удалено +pdfjs-editor-undo-bar-message-freetext = Текст удалён +pdfjs-editor-undo-bar-message-ink = Рисунок удалён +pdfjs-editor-undo-bar-message-stamp = Изображение удалено +pdfjs-editor-undo-bar-message-signature = Подпись удалена +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } аннотация удалена + [few] { $count } аннотации удалены + *[many] { $count } аннотаций удалены + } +pdfjs-editor-undo-bar-undo-button = + .title = Отменить +pdfjs-editor-undo-bar-undo-button-label = Отменить +pdfjs-editor-undo-bar-close-button = + .title = Закрыть +pdfjs-editor-undo-bar-close-button-label = Закрыть + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Это окно позволяет пользователю создать подпись для добавления в PDF-документ. Пользователь может отредактировать имя (которое также используется в качестве альтернативного текста) и, по желанию, сохранить подпись для повторного использования. +pdfjs-editor-add-signature-dialog-title = Добавить подпись + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Ввод + .title = Ввод +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Рисовать + .title = Рисовать +pdfjs-editor-add-signature-image-button = Изображение + .title = Изображение + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Введите свою подпись + .placeholder = Введите свою подпись +pdfjs-editor-add-signature-draw-placeholder = Нарисуйте свою подпись +pdfjs-editor-add-signature-draw-thickness-range-label = Толщина +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Толщина рисунка: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Перетащите сюда файл для загрузки +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Или просмотрите файлы изображений + *[other] Или просмотрите файлы изображений + } + +## Controls + +pdfjs-editor-add-signature-description-label = Описание (альтернативный текст) +pdfjs-editor-add-signature-description-input = + .title = Описание (альтернативный текст) +pdfjs-editor-add-signature-description-default-when-drawing = Подпись +pdfjs-editor-add-signature-clear-button-label = Удалить подпись +pdfjs-editor-add-signature-clear-button = + .title = Удалить подпись +pdfjs-editor-add-signature-save-checkbox = Сохранить подпись +pdfjs-editor-add-signature-save-warning-message = Вы достигли лимита в 5 сохранённых подписей. Удалите одну, чтобы сохранить другие. +pdfjs-editor-add-signature-image-upload-error-title = Не удалось загрузить изображение +pdfjs-editor-add-signature-image-upload-error-description = Проверьте подключение к сети или попробуйте другое изображение. +pdfjs-editor-add-signature-error-close-button = Закрыть + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Отмена +pdfjs-editor-add-signature-add-button = Добавить +pdfjs-editor-edit-signature-update-button = Обновить + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Удалить подпись +pdfjs-editor-delete-signature-button-label = Удалить подпись + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Изменить описание + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Изменить описание diff --git a/l10n/sat/viewer.ftl b/l10n/sat/viewer.ftl index 2fbbc123cee50..569bf72b8e4c5 100644 --- a/l10n/sat/viewer.ftl +++ b/l10n/sat/viewer.ftl @@ -323,3 +323,30 @@ pdfjs-ink-canvas = ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sc/viewer.ftl b/l10n/sc/viewer.ftl index 1137c2bb083f8..d48b8dd0b50f6 100644 --- a/l10n/sc/viewer.ftl +++ b/l10n/sc/viewer.ftl @@ -301,7 +301,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Testu alternativu pdfjs-editor-alt-text-edit-button-label = Modifica su testu alternativu pdfjs-editor-alt-text-dialog-label = Sèbera un’optzione @@ -336,9 +335,7 @@ pdfjs-editor-colorpicker-pink = ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. -# This is a button that users can click to open the alt text editor and add alt text when it is not present. pdfjs-editor-new-alt-text-missing-button-label = Mancat su testu alternativu -# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. pdfjs-editor-new-alt-text-to-review-button-label = Revisiona su testu alternativu # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -365,3 +362,30 @@ pdfjs-editor-alt-text-settings-editor-title = Editore de testu alternativu pdfjs-editor-alt-text-settings-show-dialog-button-label = Mustra deretu s’editore de testu alternativu cando siat agiunta un’immàgine pdfjs-editor-alt-text-settings-show-dialog-description = T’agiudat a assegurare chi totu is immàgines tuas tèngiant unu testu alternativu. pdfjs-editor-alt-text-settings-close-button = Serra + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/scn/viewer.ftl b/l10n/scn/viewer.ftl index a3c7c038c0927..510bcbe41508f 100644 --- a/l10n/scn/viewer.ftl +++ b/l10n/scn/viewer.ftl @@ -66,9 +66,56 @@ pdfjs-password-cancel-button = Sfai ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sco/viewer.ftl b/l10n/sco/viewer.ftl index 6f71c47a3ff91..8b74e2545830a 100644 --- a/l10n/sco/viewer.ftl +++ b/l10n/sco/viewer.ftl @@ -256,9 +256,56 @@ pdfjs-web-fonts-disabled = Wab fonts are disabled: cannae yaise embeddit PDF fon ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/si/viewer.ftl b/l10n/si/viewer.ftl index 2b3a7027d36fc..8018182323a42 100644 --- a/l10n/si/viewer.ftl +++ b/l10n/si/viewer.ftl @@ -182,7 +182,7 @@ pdfjs-find-highlight-checkbox = සියල්ල උද්දීපනය pdfjs-find-entire-word-checkbox-label = සමස්ත වචන pdfjs-find-reached-top = ලේඛනයේ මුදුනට ළඟා විය, පහළ සිට ඉහළට pdfjs-find-reached-bottom = ලේඛනයේ අවසානයට ළඟා විය, ඉහළ සිට පහළට -pdfjs-find-not-found = වැකිකඩ හමු නොවිණි +pdfjs-find-not-found = වැකිකඩ හමු නොවුණි ## Predefined zoom values @@ -230,6 +230,9 @@ pdfjs-editor-free-text-button-label = පෙළ pdfjs-editor-ink-button = .title = අඳින්න pdfjs-editor-ink-button-label = අඳින්න +pdfjs-editor-stamp-button = + .title = රූප සංස්කරණය හෝ එක් කරන්න +pdfjs-editor-stamp-button-label = රූප සංස්කරණය හෝ එක් කරන්න ## Remove button for the various kind of editor. @@ -247,6 +250,7 @@ pdfjs-free-text-default-content = ලිවීීම අරඹන්න… ## Alt-text dialog +pdfjs-editor-alt-text-mark-decorative-description = මෙය දාර හෝ දිය සලකුණු වැනි අලංකාර රූප සඳහා භාවිතා වේ. ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -265,3 +269,30 @@ pdfjs-free-text-default-content = ලිවීීම අරඹන්න… ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sk/viewer.ftl b/l10n/sk/viewer.ftl index 6477017835e90..833291342b7f0 100644 --- a/l10n/sk/viewer.ftl +++ b/l10n/sk/viewer.ftl @@ -331,6 +331,8 @@ pdfjs-editor-remove-stamp-button = .title = Odstrániť obrázok pdfjs-editor-remove-highlight-button = .title = Odstrániť zvýraznenie +pdfjs-editor-remove-signature-button = + .title = Odstrániť podpis ## @@ -347,6 +349,10 @@ pdfjs-editor-stamp-add-image-button-label = Pridať obrázok pdfjs-editor-free-highlight-thickness-input = Hrúbka pdfjs-editor-free-highlight-thickness-title = .title = Zmeňte hrúbku pre zvýrazňovanie iných položiek ako textu +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textový editor + .default-content = Začnite písať… pdfjs-free-text = .aria-label = Textový editor pdfjs-free-text-default-content = Začnite písať… @@ -357,8 +363,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatívny text +pdfjs-editor-alt-text-edit-button = + .aria-label = Upraviť alternatívny text pdfjs-editor-alt-text-edit-button-label = Upraviť alternatívny text pdfjs-editor-alt-text-dialog-label = Vyberte možnosť pdfjs-editor-alt-text-dialog-description = Alternatívny text (alt text) pomáha, keď ľudia obrázok nevidia alebo sa nenačítava. @@ -372,6 +379,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Označený ako dekoratívny # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Napríklad: „Mladý muž si sadá za stôl, aby sa najedol“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatívny text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -453,10 +463,16 @@ pdfjs-editor-new-alt-text-error-close-button = Zavrieť pdfjs-editor-new-alt-text-ai-model-downloading-progress = Sťahuje sa model AI pre alternatívne texty ({ $downloadedSize } z { $totalSize } MB) .aria-valuetext = Sťahuje sa model AI pre alternatívne texty ({ $downloadedSize } z { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatívny text bol pridaný pdfjs-editor-new-alt-text-added-button-label = Alternatívny text bol pridaný # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Chýbajúci alternatívny text pdfjs-editor-new-alt-text-missing-button-label = Chýbajúci alternatívny text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Skontrolovať alternatívny text pdfjs-editor-new-alt-text-to-review-button-label = Skontrolovať alternatívny text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -483,3 +499,89 @@ pdfjs-editor-alt-text-settings-editor-title = Editor alternatívneho textu pdfjs-editor-alt-text-settings-show-dialog-button-label = Pri pridávaní obrázka ihneď zobraziť editor alternatívneho textu pdfjs-editor-alt-text-settings-show-dialog-description = Pomáha vám zabezpečiť, aby všetky vaše obrázky mali alternatívny text. pdfjs-editor-alt-text-settings-close-button = Zavrieť + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Zvýraznenie bolo odstránené +pdfjs-editor-undo-bar-message-freetext = Text bol odstránený +pdfjs-editor-undo-bar-message-ink = Kreslenie bolo odstránené +pdfjs-editor-undo-bar-message-stamp = Obrázok bol odstránený +pdfjs-editor-undo-bar-message-signature = Podpis bol odstránený +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anotácia odstránená + [few] { $count } anotácie odstránené + [many] { $count } anotácií odstránených + *[other] { $count } anotácií odstránených + } +pdfjs-editor-undo-bar-undo-button = + .title = Späť +pdfjs-editor-undo-bar-undo-button-label = Späť +pdfjs-editor-undo-bar-close-button = + .title = Zavrieť +pdfjs-editor-undo-bar-close-button-label = Zavrieť + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Toto okno umožňuje používateľovi vytvoriť podpis, ktorý sa pridá do dokumentu PDF. Používateľ môže upraviť meno (ktoré zároveň slúži ako alternatívny text) a voliteľne uložiť podpis, ak ho plánuje v budúcnosti znova použiť. +pdfjs-editor-add-signature-dialog-title = Pridať podpis + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typ + .title = Typ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Kresliť + .title = Kresliť +pdfjs-editor-add-signature-image-button = Obrázok + .title = Obrázok + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Zadajte svoj podpis + .placeholder = Zadajte svoj podpis +pdfjs-editor-add-signature-draw-placeholder = Nakreslite svoj podpis +pdfjs-editor-add-signature-draw-thickness-range-label = Hrúbka +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Hrúbka ceruzky: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Sem presuňte súbor, ktorý chcete nahrať +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Alebo vyberte súbor s obrázkom + *[other] Alebo vyberte súbor s obrázkom + } + +## Controls + +pdfjs-editor-add-signature-description-label = Popis (alternatívny text) +pdfjs-editor-add-signature-description-input = + .title = Popis (alternatívny text) +pdfjs-editor-add-signature-description-default-when-drawing = Podpis +pdfjs-editor-add-signature-clear-button-label = Vymazať podpis +pdfjs-editor-add-signature-clear-button = + .title = Vymazať podpis +pdfjs-editor-add-signature-save-checkbox = Uložiť podpis +pdfjs-editor-add-signature-save-warning-message = Dosiahli ste limit 5 uložených podpisov. Ak chcete uložiť ďalší, jeden odstráňte. +pdfjs-editor-add-signature-image-upload-error-title = Obrázok sa nepodarilo nahrať +pdfjs-editor-add-signature-image-upload-error-description = Skontrolujte sieťové pripojenie alebo skúste iný obrázok. +pdfjs-editor-add-signature-error-close-button = Zavrieť + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Zrušiť +pdfjs-editor-add-signature-add-button = Pridať + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/skr/viewer.ftl b/l10n/skr/viewer.ftl index 186f61474399c..7f10336ab6451 100644 --- a/l10n/skr/viewer.ftl +++ b/l10n/skr/viewer.ftl @@ -353,7 +353,6 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alt متن pdfjs-editor-alt-text-edit-button-label = alt متن وِچ ترمیم کرو pdfjs-editor-alt-text-dialog-label = ہِک اختیار چُݨو @@ -368,6 +367,9 @@ pdfjs-editor-alt-text-decorative-tooltip = آرائشی دے طور تے نشا # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = مثال دے طور تے، "ہِک جؤان کھاݨاں کھاوݨ کِیتے میز اُتّے ٻیٹھا ہِے" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alt متن ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +451,16 @@ pdfjs-editor-new-alt-text-error-close-button = بند کرو pdfjs-editor-new-alt-text-ai-model-downloading-progress = آلٹ عبارت اے آئی ماڈل({ $totalSize }ایم بی دے { $downloadedSize }) ڈاؤن لوڈ تھیندا پئے .aria-valuetext = آلٹ عبارت اے آئی ماڈل({ $totalSize }ایم بی دے { $downloadedSize }) ڈاؤن لوڈ تھیندا پئے # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = آلٹ عبارت شامل تھی ڳئی pdfjs-editor-new-alt-text-added-button-label = آلٹ عبارت شامل تھی ڳئی # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = متبادل عبارت غائب ہے pdfjs-editor-new-alt-text-missing-button-label = متبادل عبارت غائب ہے # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = alt متن تے نظرثانی کرو pdfjs-editor-new-alt-text-to-review-button-label = alt متن تے نظرثانی کرو # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +487,36 @@ pdfjs-editor-alt-text-settings-editor-title = متبادل ٹیکسٹ ایڈیٹ pdfjs-editor-alt-text-settings-show-dialog-button-label = تصویر شامل کرݨ ویلے فوری طور تے آلٹ ٹیکسٹ ایڈیٹر ݙکھاؤ pdfjs-editor-alt-text-settings-show-dialog-description = ایہ تہاکوں یقینی بݨاوݨ وچ مدد کریندے جو تہاݙیاں ساریاں تصویراں وچ آلٹ عبارت ہے۔ pdfjs-editor-alt-text-settings-close-button = بند کرو + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-undo-button = + .title = کیتا اݨ کیتا +pdfjs-editor-undo-bar-undo-button-label = کیتا اݨ کیتا +pdfjs-editor-undo-bar-close-button = + .title = بند کرو +pdfjs-editor-undo-bar-close-button-label = بند کرو + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sl/viewer.ftl b/l10n/sl/viewer.ftl index d30bf3ea38ef5..c427c96153e28 100644 --- a/l10n/sl/viewer.ftl +++ b/l10n/sl/viewer.ftl @@ -320,6 +320,9 @@ pdfjs-highlight-floating-button1 = .title = Označi .aria-label = Označi pdfjs-highlight-floating-button-label = Označi +pdfjs-editor-signature-button = + .title = Dodaj podpis +pdfjs-editor-signature-button-label = Dodaj podpis ## Remove button for the various kind of editor. @@ -331,6 +334,8 @@ pdfjs-editor-remove-stamp-button = .title = Odstrani sliko pdfjs-editor-remove-highlight-button = .title = Odstrani označbo +pdfjs-editor-remove-signature-button = + .title = Odstrani podpis ## @@ -347,6 +352,13 @@ pdfjs-editor-stamp-add-image-button-label = Dodaj sliko pdfjs-editor-free-highlight-thickness-input = Debelina pdfjs-editor-free-highlight-thickness-title = .title = Spremeni debelino pri označevanju nebesedilnih elementov +pdfjs-editor-signature-add-signature-button = + .title = Dodaj nov podpis +pdfjs-editor-signature-add-signature-button-label = Dodaj nov podpis +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Urejevalnik besedila + .default-content = Začnite tipkati … pdfjs-free-text = .aria-label = Urejevalnik besedila pdfjs-free-text-default-content = Začnite tipkati … @@ -357,8 +369,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Nadomestno besedilo +pdfjs-editor-alt-text-edit-button = + .aria-label = Uredi nadomestno besedilo pdfjs-editor-alt-text-edit-button-label = Uredi nadomestno besedilo pdfjs-editor-alt-text-dialog-label = Izberite možnost pdfjs-editor-alt-text-dialog-description = Nadomestno besedilo se prikaže tistim, ki ne vidijo slike, ali če se ta ne naloži. @@ -372,6 +385,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Označeno kot okrasno # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Na primer: "Mladenič sedi za mizo pri jedi" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Nadomestno besedilo ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -453,10 +469,16 @@ pdfjs-editor-new-alt-text-error-close-button = Zapri pdfjs-editor-new-alt-text-ai-model-downloading-progress = Prenašanje modela UI za nadomestno besedilo ({ $downloadedSize } od { $totalSize } MB) .aria-valuetext = Prenašanje modela UI za nadomestno besedilo ({ $downloadedSize } od { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Nadomestno besedilo dodano pdfjs-editor-new-alt-text-added-button-label = Nadomestno besedilo dodano # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Nadomestno besedilo manjka pdfjs-editor-new-alt-text-missing-button-label = Nadomestno besedilo manjka # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Oceni nadomestno besedilo pdfjs-editor-new-alt-text-to-review-button-label = Oceni nadomestno besedilo # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -483,3 +505,95 @@ pdfjs-editor-alt-text-settings-editor-title = Urejevalnik nadomestnega besedila pdfjs-editor-alt-text-settings-show-dialog-button-label = Ob dodajanju slike takoj prikaži urejevalnik nadomestnega besedila pdfjs-editor-alt-text-settings-show-dialog-description = Pomaga vam zagotoviti, da imajo vse vaše slike nadomestno besedilo. pdfjs-editor-alt-text-settings-close-button = Zapri + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Označba odstranjena +pdfjs-editor-undo-bar-message-freetext = Besedilo odstranjeno +pdfjs-editor-undo-bar-message-ink = Risba odstranjena +pdfjs-editor-undo-bar-message-stamp = Slika odstranjena +pdfjs-editor-undo-bar-message-signature = Podpis odstranjen +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } označba odstranjena + [two] { $count } označbi odstranjeni + [few] { $count } označbe odstranjene + *[other] { $count } označb odstranjenih + } +pdfjs-editor-undo-bar-undo-button = + .title = Razveljavi +pdfjs-editor-undo-bar-undo-button-label = Razveljavi +pdfjs-editor-undo-bar-close-button = + .title = Zapri +pdfjs-editor-undo-bar-close-button-label = Zapri + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Ta način omogoča uporabniku ustvariti podpis, ki ga želi dodati dokumentu PDF. Uporabnik lahko uredi ime (ki se uporablja tudi kot nadomestno besedilo) in podpis po želji shrani za ponovno uporabo. +pdfjs-editor-add-signature-dialog-title = Dodaj podpis + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Natipkaj + .title = Natipkaj +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Nariši + .title = Nariši +pdfjs-editor-add-signature-image-button = Slika + .title = Slika + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Natipkajte svoj podpis + .placeholder = Natipkajte svoj podpis +pdfjs-editor-add-signature-draw-placeholder = Narišite svoj podpis +pdfjs-editor-add-signature-draw-thickness-range-label = Debelina +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Debelina peresa: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Povlecite datoteko sem za nalaganje +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ali prebrskajte slikovne datoteke + *[other] Ali prebrskajte slikovne datoteke + } + +## Controls + +pdfjs-editor-add-signature-description-label = Opis (nadomestno besedilo) +pdfjs-editor-add-signature-description-input = + .title = Opis (nadomestno besedilo) +pdfjs-editor-add-signature-description-default-when-drawing = Podpis +pdfjs-editor-add-signature-clear-button-label = Pobriši podpis +pdfjs-editor-add-signature-clear-button = + .title = Pobriši podpis +pdfjs-editor-add-signature-save-checkbox = Shrani podpis +pdfjs-editor-add-signature-save-warning-message = Dosegli ste omejitev 5 shranjenih podpisov. Če želite shraniti novega, enega odstranite. +pdfjs-editor-add-signature-image-upload-error-title = Slike ni bilo mogoče naložiti +pdfjs-editor-add-signature-image-upload-error-description = Preverite svojo povezavo z omrežjem ali poskusite z drugo sliko. +pdfjs-editor-add-signature-error-close-button = Zapri + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Prekliči +pdfjs-editor-add-signature-add-button = Dodaj +pdfjs-editor-edit-signature-update-button = Spremeni + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Odstrani podpis +pdfjs-editor-delete-signature-button-label = Odstrani podpis + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Uredi opis + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Uredi opis diff --git a/l10n/son/viewer.ftl b/l10n/son/viewer.ftl index fa4f6b1fc1403..011870c4ae049 100644 --- a/l10n/son/viewer.ftl +++ b/l10n/son/viewer.ftl @@ -198,9 +198,56 @@ pdfjs-web-fonts-disabled = Interneti šigirawey kay: ši hin ka goy nda PDF šig ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sq/viewer.ftl b/l10n/sq/viewer.ftl index 9edbe328fccd8..f98d8da7a808b 100644 --- a/l10n/sq/viewer.ftl +++ b/l10n/sq/viewer.ftl @@ -118,6 +118,9 @@ pdfjs-document-properties-keywords = Fjalëkyçe: pdfjs-document-properties-creation-date = Datë Krijimi: pdfjs-document-properties-modification-date = Datë Ndryshimi: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } @@ -274,6 +277,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [Nënvizim { $type }] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -301,6 +307,9 @@ pdfjs-highlight-floating-button1 = .title = Theksim .aria-label = Theksim pdfjs-highlight-floating-button-label = Theksim +pdfjs-editor-signature-button = + .title = Shtoni nënshkrim +pdfjs-editor-signature-button-label = Shtoni nënshkrim ## Remove button for the various kind of editor. @@ -312,6 +321,8 @@ pdfjs-editor-remove-stamp-button = .title = Hiq figurë pdfjs-editor-remove-highlight-button = .title = Hiqe theksimin +pdfjs-editor-remove-signature-button = + .title = Hiqe nënshkrimin ## @@ -328,6 +339,13 @@ pdfjs-editor-stamp-add-image-button-label = Shtoni figurë pdfjs-editor-free-highlight-thickness-input = Trashësi pdfjs-editor-free-highlight-thickness-title = .title = Ndryshoni trashësinë kur theksoni objekte tjetër nga tekst +pdfjs-editor-signature-add-signature-button = + .title = Shtoni nënshkrim të ri +pdfjs-editor-signature-add-signature-button-label = Shtoni nënshkrim të ri +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Përpunues Tekstesh + .default-content = Filloni të shtypni… pdfjs-free-text = .aria-label = Përpunues Tekstesh pdfjs-free-text-default-content = Filloni të shtypni… @@ -338,8 +356,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Tekst alternativ +pdfjs-editor-alt-text-edit-button = + .aria-label = Përpunoni tekst alternativ pdfjs-editor-alt-text-edit-button-label = Përpunoni tekst alternativ pdfjs-editor-alt-text-dialog-label = Zgjidhni një mundësi pdfjs-editor-alt-text-dialog-description = Teksti alt (tekst alternativ) vjen në ndihmë kur njerëzit s’mund të shohin figurën, ose kur ajo nuk ngarkohet. @@ -353,6 +372,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Iu vu shenjë si dekorative # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Për shembull, “Një djalosh ulet në një tryezë të hajë” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Tekst alternativ ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -434,10 +456,16 @@ pdfjs-editor-new-alt-text-error-close-button = Mbylle pdfjs-editor-new-alt-text-ai-model-downloading-progress = Po shkarkohet model IA teksti alternativ ({ $downloadedSize } nga { $totalSize } MB) .aria-valuetext = Po shkarkohet model IA teksti alternativ ({ $downloadedSize } nga { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = U shtua tekst alternativ pdfjs-editor-new-alt-text-added-button-label = U shtua tekst alternativ # This is a button that users can click to open the alt text editor and add alt text when it is not present. -pdfjs-editor-new-alt-text-missing-button-label = Mungon teskt alternativ +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Mungon tekst alternativ +pdfjs-editor-new-alt-text-missing-button-label = Mungon tekst alternativ # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Shqyrtoni tekst alternativ pdfjs-editor-new-alt-text-to-review-button-label = Shqyrtoni tekst alternativ # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -464,3 +492,93 @@ pdfjs-editor-alt-text-settings-editor-title = Përpunues teksti alternativ pdfjs-editor-alt-text-settings-show-dialog-button-label = Shfaq menjëherë përpunues teksti alternativ, kur shtohet një figurë pdfjs-editor-alt-text-settings-show-dialog-description = Ju ndihmon të siguroheni se krejt figurat tuaja kanë tekst alternativ. pdfjs-editor-alt-text-settings-close-button = Mbylle + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = U hoq theksimi +pdfjs-editor-undo-bar-message-freetext = U hoq tekst +pdfjs-editor-undo-bar-message-ink = U hoq vizatim +pdfjs-editor-undo-bar-message-stamp = U hoq figurë +pdfjs-editor-undo-bar-message-signature = Nënshkrimi u hoq +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] U hoq { $count } shënim + *[other] U hoqën { $count } shënime + } +pdfjs-editor-undo-bar-undo-button = + .title = Zhbëje +pdfjs-editor-undo-bar-undo-button-label = Zhbëje +pdfjs-editor-undo-bar-close-button = + .title = Mbylle +pdfjs-editor-undo-bar-close-button-label = Mbylle + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Kjo dritare modale i lejon përdoruesit të krijojë një nënshkrim për ta shtuar te një dokument PDF. Përdoruesi mund të përpunojë emrin (i cili shërben edhe si tekst alternativ) dhe, nëse do, ta ruajë nënshkrimin, për ta përdorur prapë. +pdfjs-editor-add-signature-dialog-title = Shtoni një nënshkrim + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Lloj + .title = Lloj +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Vizatoni + .title = Vizatoni +pdfjs-editor-add-signature-image-button = Figurë + .title = Figurë + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Shtypni nënshkrimin tuaj + .placeholder = Shtypni nënshkrimin tuaj +pdfjs-editor-add-signature-draw-placeholder = Vizatoni nënshkrimin tuaj +pdfjs-editor-add-signature-draw-thickness-range-label = Trashësi +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Trashësi vizatimi: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Tërhiqni këtu një kartelë për ngarkim +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ose zgjidhni kartelë figure + *[other] Ose zgjidhni kartelë figure + } + +## Controls + +pdfjs-editor-add-signature-description-label = Përshkrim (tekst alternativ) +pdfjs-editor-add-signature-description-input = + .title = Përshkrim (tekst alternativ) +pdfjs-editor-add-signature-description-default-when-drawing = Nënshkrim +pdfjs-editor-add-signature-clear-button-label = Spastroje nënshkrimin +pdfjs-editor-add-signature-clear-button = + .title = Spastroje nënshkrimin +pdfjs-editor-add-signature-save-checkbox = Ruaje nënshkrimin +pdfjs-editor-add-signature-save-warning-message = Keni mbërritur në kufirin e 5 nënshkrimeve të ruajtura. Që të ruani tjetër, hiqni një. +pdfjs-editor-add-signature-image-upload-error-title = S’u ngarkua dot figurë +pdfjs-editor-add-signature-image-upload-error-description = Kontrolloni lidhjen tuaj në rrjet, ose provoni figurë tjetër. +pdfjs-editor-add-signature-error-close-button = Mbylle + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Anuloje +pdfjs-editor-add-signature-add-button = Shtoje +pdfjs-editor-edit-signature-update-button = Përditësoje + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Hiqe nënshkrimin +pdfjs-editor-delete-signature-button-label = Hiqe nënshkrimin + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Përpunoni përshkrimin + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Përpunoni përshkrimin diff --git a/l10n/sr/viewer.ftl b/l10n/sr/viewer.ftl index dbdb67e488836..a30caaeecaef0 100644 --- a/l10n/sr/viewer.ftl +++ b/l10n/sr/viewer.ftl @@ -113,6 +113,9 @@ pdfjs-document-properties-keywords = Кључне речи: pdfjs-document-properties-creation-date = Датум креирања: pdfjs-document-properties-modification-date = Датум модификације: # Variables: +# $dateObj (Date) - the creation/modification date and time of the PDF file +pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } +# Variables: # $date (Date) - the creation/modification date of the PDF file # $time (Time) - the creation/modification time of the PDF file pdfjs-document-properties-date-string = { $date }, { $time } @@ -162,10 +165,10 @@ pdfjs-printing-not-ready = Упозорење: PDF није у потпунос ## Tooltips and alt text for side panel toolbar buttons pdfjs-toggle-sidebar-button = - .title = Прикажи додатну палету + .title = Прикажи/сакриј бочни панел pdfjs-toggle-sidebar-notification-button = - .title = Прикажи/сакриј бочну траку (документ садржи контуру/прилоге/слојеве) -pdfjs-toggle-sidebar-button-label = Прикажи додатну палету + .title = Прикажи/сакриј бочни панел (документ садржи контуру/прилоге/слојеве) +pdfjs-toggle-sidebar-button-label = Прикажи/сакриј бочни панел pdfjs-document-outline-button = .title = Прикажи структуру документа (двоструким кликом проширујете/скупљате све ставке) pdfjs-document-outline-button-label = Контура документа @@ -254,6 +257,9 @@ pdfjs-annotation-date-string = { $date }, { $time } # Some common types are e.g.: "Check", "Text", "Comment", "Note" pdfjs-text-annotation-type = .alt = [{ $type } коментар] +# Variables: +# $dateObj (Date) - the modification date and time of the annotation +pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } ## Password @@ -271,9 +277,27 @@ pdfjs-editor-free-text-button-label = Текст pdfjs-editor-ink-button = .title = Цртај pdfjs-editor-ink-button-label = Цртај +pdfjs-editor-stamp-button = + .title = Додај или уреди слике +pdfjs-editor-stamp-button-label = Додај или уреди слике +pdfjs-editor-highlight-button = + .title = Означи +pdfjs-editor-highlight-button-label = Означи +pdfjs-highlight-floating-button1 = + .title = Означи + .aria-label = Означи +pdfjs-highlight-floating-button-label = Означи ## Remove button for the various kind of editor. +pdfjs-editor-remove-ink-button = + .title = Уклони цртеж +pdfjs-editor-remove-freetext-button = + .title = Уклони текст +pdfjs-editor-remove-stamp-button = + .title = Уклони слику +pdfjs-editor-remove-highlight-button = + .title = Уклони ознаку ## @@ -283,6 +307,15 @@ pdfjs-editor-free-text-size-input = Величина pdfjs-editor-ink-color-input = Боја pdfjs-editor-ink-thickness-input = Дебљина pdfjs-editor-ink-opacity-input = Опацитет +pdfjs-editor-stamp-add-image-button = + .title = Додај слику +pdfjs-editor-stamp-add-image-button-label = Додај слику +pdfjs-editor-free-highlight-thickness-title = + .title = Промени дебљину при означавању других ставки сем текста +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Уређивач текста + .default-content = Почни куцати… pdfjs-free-text = .aria-label = Уређивач текста pdfjs-free-text-default-content = Почни куцање… @@ -293,21 +326,123 @@ pdfjs-ink-canvas = ## Alt-text dialog +pdfjs-editor-alt-text-button-label = Алтернативни текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Уреди алтернативни текст +pdfjs-editor-alt-text-edit-button-label = Уреди алтернативни текст +pdfjs-editor-alt-text-dialog-label = Одабери опцију +pdfjs-editor-alt-text-dialog-description = Алтернативни текст помаже слепим и слабовидим особама или када се слика не учита. +pdfjs-editor-alt-text-add-description-label = Додај опис +pdfjs-editor-alt-text-add-description-description = Сажмите у 1-2 реченице које описују предмет, окружење или радње. +pdfjs-editor-alt-text-mark-decorative-label = Означи као украсно +pdfjs-editor-alt-text-mark-decorative-description = Ово је за украсне слике, као што су ивице или водени печати. +pdfjs-editor-alt-text-cancel-button = Откажи +pdfjs-editor-alt-text-save-button = Сачувај +pdfjs-editor-alt-text-decorative-tooltip = Означено као украсно +# .placeholder: This is a placeholder for the alt text input area +pdfjs-editor-alt-text-textarea = + .placeholder = На пример: „Младић седа за сто да једе“ +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Алтернативни текст ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. +pdfjs-editor-resizer-label-top-left = Горњи леви угао — промени величину +pdfjs-editor-resizer-label-top-middle = Средина горе — промени величину +pdfjs-editor-resizer-label-top-right = Горњи десни угао — промени величину +pdfjs-editor-resizer-label-middle-right = Средина десно — промени величину +pdfjs-editor-resizer-label-bottom-right = Доњи десни угао — промени величину +pdfjs-editor-resizer-label-bottom-middle = Средина доле — промени величину +pdfjs-editor-resizer-label-bottom-left = Доњи леви угао — промени величину +pdfjs-editor-resizer-label-middle-left = Средина лево — промени величину +pdfjs-editor-resizer-top-left = + .aria-label = Горњи леви угао — промени величину +pdfjs-editor-resizer-top-middle = + .aria-label = Средина горе — промени величину +pdfjs-editor-resizer-top-right = + .aria-label = Горњи десни угао — промени величину +pdfjs-editor-resizer-middle-right = + .aria-label = Средина десно — промени величину +pdfjs-editor-resizer-bottom-right = + .aria-label = Доњи десни угао — промени величину +pdfjs-editor-resizer-bottom-middle = + .aria-label = Средина доле — промени величину +pdfjs-editor-resizer-bottom-left = + .aria-label = Доњи леви угао — промени величину +pdfjs-editor-resizer-middle-left = + .aria-label = Средина лево — промени величину ## Color picker +# This means "Color used to highlight text" +pdfjs-editor-highlight-colorpicker-label = Боја означавања +pdfjs-editor-colorpicker-button = + .title = Промени боју +pdfjs-editor-colorpicker-dropdown = + .aria-label = Избор боја +pdfjs-editor-colorpicker-yellow = + .title = Жута +pdfjs-editor-colorpicker-green = + .title = Зелена +pdfjs-editor-colorpicker-blue = + .title = Плава +pdfjs-editor-colorpicker-pink = + .title = Розе +pdfjs-editor-colorpicker-red = + .title = Црвена ## Show all highlights ## This is a toggle button to show/hide all the highlights. +pdfjs-editor-highlight-show-all-button-label = Прикажи све +pdfjs-editor-highlight-show-all-button = + .title = Прикажи све ## New alt-text dialog ## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. +# Modal header positioned above a text box where users can edit the alt text. +pdfjs-editor-new-alt-text-dialog-edit-label = Уреди алтернативни текст (опис слике) +# Modal header positioned above a text box where users can add the alt text. +pdfjs-editor-new-alt-text-dialog-add-label = Додај алтернативни текст (опис слике) +pdfjs-editor-new-alt-text-textarea = + .placeholder = Напиши опис овде… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Кратак опис за слепе и слабовиде људе или када се слика не успе учитати. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Овај алтернативни текст је направљен аутоматски и може бити нетачан. +pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Сазнајте више +pdfjs-editor-new-alt-text-create-automatically-button-label = Прави алтернативни текст аутоматски +pdfjs-editor-new-alt-text-not-now-button = Не сада ## Image alt-text settings + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/sv-SE/viewer.ftl b/l10n/sv-SE/viewer.ftl index 87c7da09209ef..e2a3de11b8266 100644 --- a/l10n/sv-SE/viewer.ftl +++ b/l10n/sv-SE/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Markera .aria-label = Markera pdfjs-highlight-floating-button-label = Markera +pdfjs-editor-signature-button = + .title = Lägg till signatur +pdfjs-editor-signature-button-label = Lägg till signatur ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Ta bort bild pdfjs-editor-remove-highlight-button = .title = Ta bort markering +pdfjs-editor-remove-signature-button = + .title = Ta bort signatur ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Lägg till bild pdfjs-editor-free-highlight-thickness-input = Tjocklek pdfjs-editor-free-highlight-thickness-title = .title = Ändra tjocklek när du markerar andra objekt än text +pdfjs-editor-signature-add-signature-button = + .title = Lägg till ny signatur +pdfjs-editor-signature-add-signature-button-label = Lägg till ny signatur +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Textredigerare + .default-content = Börja skriva… pdfjs-free-text = .aria-label = Textredigerare pdfjs-free-text-default-content = Börja skriva… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternativ text +pdfjs-editor-alt-text-edit-button = + .aria-label = Redigera alternativ text pdfjs-editor-alt-text-edit-button-label = Redigera alternativ text pdfjs-editor-alt-text-dialog-label = Välj ett alternativ pdfjs-editor-alt-text-dialog-description = Alt text (alternativ text) hjälper till när människor inte kan se bilden eller när den inte laddas. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Märkt som dekorativ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Till exempel, "En ung man sätter sig vid ett bord för att äta en måltid" +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternativ text ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Stäng pdfjs-editor-new-alt-text-ai-model-downloading-progress = Hämtar AI-modell med alternativ text ({ $downloadedSize } av { $totalSize } MB) .aria-valuetext = Hämtar AI-modell med alternativ text ({ $downloadedSize } av { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternativ text tillagd pdfjs-editor-new-alt-text-added-button-label = Alternativ text tillagd # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Saknar alternativ text pdfjs-editor-new-alt-text-missing-button-label = Saknar alternativ text # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Granska alternativ text pdfjs-editor-new-alt-text-to-review-button-label = Granska alternativ text # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,93 @@ pdfjs-editor-alt-text-settings-editor-title = Alternativ textredigerare pdfjs-editor-alt-text-settings-show-dialog-button-label = Visa alternativ textredigerare direkt när du lägger till en bild pdfjs-editor-alt-text-settings-show-dialog-description = Hjälper dig att se till att alla dina bilder har alternativ text. pdfjs-editor-alt-text-settings-close-button = Stäng + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Markering borttagen +pdfjs-editor-undo-bar-message-freetext = Text borttagen +pdfjs-editor-undo-bar-message-ink = Ritning borttagen +pdfjs-editor-undo-bar-message-stamp = Bild borttagen +pdfjs-editor-undo-bar-message-signature = Signatur borttagen +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } anteckning har tagits bort + *[other] { $count } anteckningar har tagits bort + } +pdfjs-editor-undo-bar-undo-button = + .title = Ångra +pdfjs-editor-undo-bar-undo-button-label = Ångra +pdfjs-editor-undo-bar-close-button = + .title = Stäng +pdfjs-editor-undo-bar-close-button-label = Stäng + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Denna modal tillåter användaren att skapa en signatur för att lägga till i ett PDF-dokument. Användaren kan redigera namnet (som också fungerar som alternativ text) och eventuellt spara signaturen för upprepad användning. +pdfjs-editor-add-signature-dialog-title = Lägg till en signatur + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Typ + .title = Typ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Rita + .title = Rita +pdfjs-editor-add-signature-image-button = Bild + .title = Bild + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Skriv din signatur + .placeholder = Skriv din signatur +pdfjs-editor-add-signature-draw-placeholder = Rita din signatur +pdfjs-editor-add-signature-draw-thickness-range-label = Tjocklek +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Ritningstjocklek: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Dra en fil hit för att ladda upp +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Eller välj bildfiler + *[other] Eller bläddra bland bildfiler + } + +## Controls + +pdfjs-editor-add-signature-description-label = Beskrivning (alternativ text) +pdfjs-editor-add-signature-description-input = + .title = Beskrivning (alternativ text) +pdfjs-editor-add-signature-description-default-when-drawing = Signatur +pdfjs-editor-add-signature-clear-button-label = Rensa signatur +pdfjs-editor-add-signature-clear-button = + .title = Rensa signatur +pdfjs-editor-add-signature-save-checkbox = Spara signatur +pdfjs-editor-add-signature-save-warning-message = Du har nått gränsen på 5 sparade signaturer. Ta bort en för att spara fler. +pdfjs-editor-add-signature-image-upload-error-title = Det gick inte att ladda upp bilden +pdfjs-editor-add-signature-image-upload-error-description = Kontrollera din nätverksanslutning eller försök med en annan bild. +pdfjs-editor-add-signature-error-close-button = Stäng + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Avbryt +pdfjs-editor-add-signature-add-button = Lägg till +pdfjs-editor-edit-signature-update-button = Uppdatera + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Ta bort signatur +pdfjs-editor-delete-signature-button-label = Ta bort signatur + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Redigera beskrivning + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Redigera beskrivning diff --git a/l10n/szl/viewer.ftl b/l10n/szl/viewer.ftl index cbf166e452569..6f9274fb2c57d 100644 --- a/l10n/szl/viewer.ftl +++ b/l10n/szl/viewer.ftl @@ -249,9 +249,56 @@ pdfjs-web-fonts-disabled = Necowe fōnty sōm zastawiōne: niy idzie użyć wklu ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ta/viewer.ftl b/l10n/ta/viewer.ftl index 82cf19703753c..9edf9e8b63ce5 100644 --- a/l10n/ta/viewer.ftl +++ b/l10n/ta/viewer.ftl @@ -215,9 +215,56 @@ pdfjs-web-fonts-disabled = வலை எழுத்துருக்கள் ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/te/viewer.ftl b/l10n/te/viewer.ftl index 94dc2b8eef22d..173f97f820ef6 100644 --- a/l10n/te/viewer.ftl +++ b/l10n/te/viewer.ftl @@ -224,6 +224,12 @@ pdfjs-web-fonts-disabled = వెబ్ ఫాంట్లు అచేతని ## Editing + +## Remove button for the various kind of editor. + + +## + # Editor Parameters pdfjs-editor-free-text-color-input = రంగు pdfjs-editor-free-text-size-input = పరిమాణం @@ -237,3 +243,44 @@ pdfjs-editor-ink-opacity-input = అకిరణ్యత ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/tg/viewer.ftl b/l10n/tg/viewer.ftl index 8b25bad6f7eb5..4d9669c098d03 100644 --- a/l10n/tg/viewer.ftl +++ b/l10n/tg/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Ҷудокунӣ .aria-label = Ҷудокунӣ pdfjs-highlight-floating-button-label = Ҷудокунӣ +pdfjs-editor-signature-button = + .title = Илова кардани имзо +pdfjs-editor-signature-button-label = Илова кардани имзо ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Тоза кардани тасвир pdfjs-editor-remove-highlight-button = .title = Тоза кардани ҷудокунӣ +pdfjs-editor-remove-signature-button = + .title = Тоза кардани имзо ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Илова кардани тасви pdfjs-editor-free-highlight-thickness-input = Ғафсӣ pdfjs-editor-free-highlight-thickness-title = .title = Иваз кардани ғафсӣ ҳангоми ҷудокунии унсурҳо ба ғайр аз матн +pdfjs-editor-signature-add-signature-button = + .title = Илова кардани имзои нав +pdfjs-editor-signature-add-signature-button-label = Илова кардани имзои нав +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Муҳаррири матн + .default-content = Матнро ворид кунед… pdfjs-free-text = .aria-label = Муҳаррири матн pdfjs-free-text-default-content = Нависед… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Матни иловагӣ +pdfjs-editor-alt-text-edit-button = + .aria-label = Таҳрир кардани матни ивазкунанда pdfjs-editor-alt-text-edit-button-label = Таҳрир кардани матни иловагӣ pdfjs-editor-alt-text-dialog-label = Имконеро интихоб намоед pdfjs-editor-alt-text-dialog-description = Вақте ки одамон тасвирро дида наметавонанд ё вақте ки тасвир бор карда намешавад, матни иловагӣ (Alt text) кумак мерасонад. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Ҳамчун матни ороишӣ # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Барои мисол, «Ман забони тоҷикиро дӯст медорам» +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Матни ивазкунанда ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -432,17 +448,33 @@ pdfjs-editor-new-alt-text-dialog-edit-label = Таҳрир кардани мат pdfjs-editor-new-alt-text-dialog-add-label = Илова кардани матни иловагӣ (тафсири тасвир) pdfjs-editor-new-alt-text-textarea = .placeholder = Тафсири худро дар ин ҷо нависед… +# This text refers to the alt text box above this description. It offers a definition of alt text. +pdfjs-editor-new-alt-text-description = Тавсифи мухтасар барои одамоне, ки аксҳоро дида наметавонанд ё вақте ки аксҳо кушода намешаванд. +# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human. +pdfjs-editor-new-alt-text-disclaimer1 = Ин матни ивазкунанда ба таври худкор сохта шудааст ва шояд нодуруст бошад. pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Маълумоти бештар pdfjs-editor-new-alt-text-create-automatically-button-label = Ба таври худкор эҷод кардани матни иловагӣ pdfjs-editor-new-alt-text-not-now-button = Ҳоло не pdfjs-editor-new-alt-text-error-title = Матни иловагӣ ба таври худкор эҷод карда нашуд pdfjs-editor-new-alt-text-error-description = Лутфан, матни иловагии худро ворид кунед ё баъдтар аз нав кӯшиш кунед. pdfjs-editor-new-alt-text-error-close-button = Пӯшидан +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +# $downloadedSize (Number) - the downloaded size (in MB) of the AI model. +# $percent (Number) - the percentage of the downloaded size. +pdfjs-editor-new-alt-text-ai-model-downloading-progress = Боргирии модели зеҳни сунъӣ (AI) барои матни ивазкунанда ({ $downloadedSize } аз { $totalSize } МБ) + .aria-valuetext = Боргирии модели зеҳни сунъӣ (AI) барои матни ивазкунанда ({ $downloadedSize } аз { $totalSize } МБ) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Матни иловагӣ илова карда шуд pdfjs-editor-new-alt-text-added-button-label = Матни иловагӣ илова карда шуд # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Матни иловагӣ вуҷуд надорад pdfjs-editor-new-alt-text-missing-button-label = Матни иловагӣ вуҷуд надорад # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Бознигарӣ кардани матни иловагӣ pdfjs-editor-new-alt-text-to-review-button-label = Бознигарӣ кардани матни иловагӣ # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -457,8 +489,105 @@ pdfjs-image-alt-text-settings-button-label = Танзимоти матни ил pdfjs-editor-alt-text-settings-dialog-label = Танзимоти матни иловагии тасвир pdfjs-editor-alt-text-settings-automatic-title = Матни иловагии худкор pdfjs-editor-alt-text-settings-create-model-button-label = Ба таври худкор эҷод кардани матни иловагӣ +pdfjs-editor-alt-text-settings-create-model-description = Ин имкон барои расонидани кумак ба одамоне, ки аксҳоро дида наметавонанд ё вақте ки аксҳо кушода намешаванд, тавсифи аксҳоро пешниҳод мекунад. +# Variables: +# $totalSize (Number) - the total size (in MB) of the AI model. +pdfjs-editor-alt-text-settings-download-model-label = Модели зеҳни сунъӣ «AI» барои матни ивазкунанда ({ $totalSize } МБ) +pdfjs-editor-alt-text-settings-ai-model-description = Дар дастгоҳи шумо ба таври маҳаллӣ кор мекунад, бинобар ин махфияти маълумоти шахсии шумо нигоҳ дошта мешавад. Барои матни ивазкунандаи худкор лозим аст. pdfjs-editor-alt-text-settings-delete-model-button = Нест кардан pdfjs-editor-alt-text-settings-download-model-button = Боргирӣ кардан pdfjs-editor-alt-text-settings-downloading-model-button = Дар ҳоли боргирӣ… pdfjs-editor-alt-text-settings-editor-title = Муҳаррири матни иловагӣ +pdfjs-editor-alt-text-settings-show-dialog-button-label = Дарҳол нишон додани муҳаррири матни ивазкунанда ҳангоми иловакунии тасвир +pdfjs-editor-alt-text-settings-show-dialog-description = Ба шумо кумак мекунад, ки боварӣ ҳосил кунед, ки ҳамаи тасвирҳои шумо дорои матни ивазкунанда мебошанд. pdfjs-editor-alt-text-settings-close-button = Пӯшидан + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Ҷудосозӣ тоза карда шуд +pdfjs-editor-undo-bar-message-freetext = Матн тоза карда шуд +pdfjs-editor-undo-bar-message-ink = Расм тоза карда шуд +pdfjs-editor-undo-bar-message-stamp = Тасвир тоза карда шуд +pdfjs-editor-undo-bar-message-signature = Имзо тоза карда шуд +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ҳошиянависӣ тоза карда шуд + *[other] { $count } ҳошиянависӣ тоза карда шуданд + } +pdfjs-editor-undo-bar-undo-button = + .title = Бекор кардан +pdfjs-editor-undo-bar-undo-button-label = Бекор кардан +pdfjs-editor-undo-bar-close-button = + .title = Пӯшидан +pdfjs-editor-undo-bar-close-button-label = Пӯшидан + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Ин равзанаи зоҳирӣ ба корбар имкон медиҳад, ки тавонад имзоеро эҷод карда, ба ҳуҷҷати «PDF» илова намояд. Корбар метавонад номро таҳрир кунад (ном, инчунин, ҳамчун матни иловагӣ хизмат мекунад), ва ихтиёран имзоро барои истифодаи такрорӣ нигоҳ медорад. +pdfjs-editor-add-signature-dialog-title = Илова кардани имзо + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Нависед + .title = Нависед +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Имзо гузоред + .title = Имзо гузоред +pdfjs-editor-add-signature-image-button = Тасвир + .title = Тасвир + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Имзои худро бо ном нависед + .placeholder = Имзои худро бо ном нависед +pdfjs-editor-add-signature-draw-placeholder = Имзои худро кашида, гузоред +pdfjs-editor-add-signature-draw-thickness-range-label = Ғафсӣ +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Ғафсии имзо: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Барои бор кардани файл, онро дар ин ҷой кашида, гузоред +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Ё файлҳои тасвириро интихоб кунед + *[other] Ё файлҳои тасвириро интихоб кунед + } + +## Controls + +pdfjs-editor-add-signature-description-label = Тавсиф (матни иловагӣ) +pdfjs-editor-add-signature-description-input = + .title = Тавсиф (матни иловагӣ) +pdfjs-editor-add-signature-description-default-when-drawing = Имзо +pdfjs-editor-add-signature-clear-button-label = Пок кардани имзо +pdfjs-editor-add-signature-clear-button = + .title = Пок кардани имзо +pdfjs-editor-add-signature-save-checkbox = Нигоҳ доштани имзо +pdfjs-editor-add-signature-save-warning-message = Шумо ба ҳадди 5 имзои нигоҳдошташуда расидед. Барои нигоҳ доштани имзои нав, яке аз имзоҳои нигоҳдошташударо тоза намоед. +pdfjs-editor-add-signature-image-upload-error-title = Тасвир бор карда нашуд +pdfjs-editor-add-signature-image-upload-error-description = Пайвастшавии шабакаи худро санҷед ё тасвири дигареро кӯшиш кунед. +pdfjs-editor-add-signature-error-close-button = Пӯшидан + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Бекор кардан +pdfjs-editor-add-signature-add-button = Илова кардан +pdfjs-editor-edit-signature-update-button = Навсозӣ кардан + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Тоза кардани имзо +pdfjs-editor-delete-signature-button-label = Тоза кардани имзо + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Таҳрир кардани тавсиф + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Таҳрир кардани тавсиф diff --git a/l10n/th/viewer.ftl b/l10n/th/viewer.ftl index 3aefcef0a7f03..d7d24c641e2e1 100644 --- a/l10n/th/viewer.ftl +++ b/l10n/th/viewer.ftl @@ -319,6 +319,8 @@ pdfjs-editor-remove-stamp-button = .title = เอาภาพออก pdfjs-editor-remove-highlight-button = .title = เอาการเน้นสีออก +pdfjs-editor-remove-signature-button = + .title = ลบลายเซ็น ## @@ -335,6 +337,10 @@ pdfjs-editor-stamp-add-image-button-label = เพิ่มภาพ pdfjs-editor-free-highlight-thickness-input = ความหนา pdfjs-editor-free-highlight-thickness-title = .title = เปลี่ยนความหนาเมื่อเน้นรายการอื่นๆ ที่ไม่ใช่ข้อความ +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = ตัวแก้ไขข้อความ + .default-content = เริ่มพิมพ์ได้เลย… pdfjs-free-text = .aria-label = ตัวแก้ไขข้อความ pdfjs-free-text-default-content = เริ่มพิมพ์… @@ -345,8 +351,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = ข้อความทดแทน +pdfjs-editor-alt-text-edit-button = + .aria-label = แก้ไขข้อความทดแทน pdfjs-editor-alt-text-edit-button-label = แก้ไขข้อความทดแทน pdfjs-editor-alt-text-dialog-label = เลือกตัวเลือก pdfjs-editor-alt-text-dialog-description = ข้อความทดแทนสามารถช่วยเหลือได้เมื่อผู้ใช้มองไม่เห็นภาพ หรือภาพไม่โหลด @@ -360,6 +367,9 @@ pdfjs-editor-alt-text-decorative-tooltip = ทำเครื่องหมา # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = ตัวอย่างเช่น “ชายหนุ่มคนหนึ่งนั่งลงที่โต๊ะเพื่อรับประทานอาหารมื้อหนึ่ง” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = ข้อความทดแทน ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +451,16 @@ pdfjs-editor-new-alt-text-error-close-button = ปิด pdfjs-editor-new-alt-text-ai-model-downloading-progress = กำลังดาวน์โหลดโมเดล AI สำหรับข้อความทดแทน ({ $downloadedSize } จาก { $totalSize } MB) .aria-valuetext = กำลังดาวน์โหลดโมเดล AI สำหรับข้อความทดแทน ({ $downloadedSize } จาก { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = เพิ่มข้อความทดแทนแล้ว pdfjs-editor-new-alt-text-added-button-label = เพิ่มข้อความทดแทนแล้ว # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = ขาดข้อความทดแทน pdfjs-editor-new-alt-text-missing-button-label = ขาดข้อความทดแทน # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = ตรวจสอบข้อความทดแทน pdfjs-editor-new-alt-text-to-review-button-label = ตรวจสอบข้อความทดแทน # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +487,83 @@ pdfjs-editor-alt-text-settings-editor-title = ตัวแก้ไขข้อ pdfjs-editor-alt-text-settings-show-dialog-button-label = แสดงตัวแก้ไขข้อความทดแทนทันทีเมื่อเพิ่มภาพ pdfjs-editor-alt-text-settings-show-dialog-description = ช่วยให้คุณแน่ใจว่าภาพทั้งหมดของคุณมีข้อความทดแทน pdfjs-editor-alt-text-settings-close-button = ปิด + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = เอาการเน้นสีออกแล้ว +pdfjs-editor-undo-bar-message-freetext = เอาข้อความออกแล้ว +pdfjs-editor-undo-bar-message-ink = เอาภาพวาดออกแล้ว +pdfjs-editor-undo-bar-message-stamp = เอาภาพออกแล้ว +pdfjs-editor-undo-bar-message-signature = ลบลายเซ็นแล้ว +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = เอาคำอธิบายประกอบ { $count } รายการออกแล้ว +pdfjs-editor-undo-bar-undo-button = + .title = เลิกทำ +pdfjs-editor-undo-bar-undo-button-label = เลิกทำ +pdfjs-editor-undo-bar-close-button = + .title = ปิด +pdfjs-editor-undo-bar-close-button-label = ปิด + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = โมดัลนี้ช่วยให้ผู้ใช้สามารถสร้างลายเซ็นเพื่อใช้เพิ่มลงในเอกสาร PDF ได้ ผู้ใช้สามารถแก้ไขชื่อ (ซึ่งใช้เป็นข้อความทดแทนได้ด้วย) และสามารถเลือกบันทึกลายเซ็นเพื่อใช้งานซ้ำได้ +pdfjs-editor-add-signature-dialog-title = เพิ่มลายเซ็น + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = พิมพ์ + .title = พิมพ์ +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = วาด + .title = วาด +pdfjs-editor-add-signature-image-button = ภาพ + .title = ภาพ + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = พิมพ์ลายเซ็นของคุณ + .placeholder = พิมพ์ลายเซ็นของคุณ +pdfjs-editor-add-signature-draw-placeholder = วาดลายเซ็นของคุณ +pdfjs-editor-add-signature-draw-thickness-range-label = ความหนา +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = ความหนาของการวาด: { $thickness } +pdfjs-editor-add-signature-image-placeholder = ลากไฟล์มาที่นี่เพื่ออัปโหลด +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] หรือเลือกไฟล์ภาพ + *[other] หรือเรียกดูไฟล์ภาพ + } + +## Controls + +pdfjs-editor-add-signature-description-label = คำอธิบาย (ข้อความทดแทน) +pdfjs-editor-add-signature-description-input = + .title = คำอธิบาย (ข้อความทดแทน) +pdfjs-editor-add-signature-description-default-when-drawing = ลายเซ็น +pdfjs-editor-add-signature-clear-button-label = ล้างลายเซ็น +pdfjs-editor-add-signature-clear-button = + .title = ล้างลายเซ็น +pdfjs-editor-add-signature-save-checkbox = บันทึกลายเซ็น +pdfjs-editor-add-signature-save-warning-message = คุณมีลายเซ็นที่บันทึกถึงจำนวนสูงสุด 5 รายการแล้ว โปรดลบรายการหนึ่งออกเมื่อจะบันทึกเพิ่ม +pdfjs-editor-add-signature-image-upload-error-title = ไม่สามารถอัปโหลดภาพได้ +pdfjs-editor-add-signature-image-upload-error-description = ตรวจสอบการเชื่อมต่อเครือข่ายของคุณหรือลองใช้ภาพอื่น +pdfjs-editor-add-signature-error-close-button = ปิด + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = ยกเลิก +pdfjs-editor-add-signature-add-button = เพิ่ม + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/tl/viewer.ftl b/l10n/tl/viewer.ftl index faa0009bd4db3..f70000148e72e 100644 --- a/l10n/tl/viewer.ftl +++ b/l10n/tl/viewer.ftl @@ -249,9 +249,56 @@ pdfjs-web-fonts-disabled = Naka-disable ang mga Web font: hindi kayang gamitin a ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/tr/viewer.ftl b/l10n/tr/viewer.ftl index 0d70b3f94a206..baef8fee40611 100644 --- a/l10n/tr/viewer.ftl +++ b/l10n/tr/viewer.ftl @@ -316,6 +316,9 @@ pdfjs-highlight-floating-button1 = .title = Vurgula .aria-label = Vurgula pdfjs-highlight-floating-button-label = Vurgula +pdfjs-editor-signature-button = + .title = İmza ekle +pdfjs-editor-signature-button-label = İmza ekle ## Remove button for the various kind of editor. @@ -327,6 +330,8 @@ pdfjs-editor-remove-stamp-button = .title = Resmi kaldır pdfjs-editor-remove-highlight-button = .title = Vurgulamayı kaldır +pdfjs-editor-remove-signature-button = + .title = İmzayı kaldır ## @@ -343,6 +348,13 @@ pdfjs-editor-stamp-add-image-button-label = Resim ekle pdfjs-editor-free-highlight-thickness-input = Kalınlık pdfjs-editor-free-highlight-thickness-title = .title = Metin dışındaki öğeleri vurgularken kalınlığı değiştir +pdfjs-editor-signature-add-signature-button = + .title = Yeni imza ekle +pdfjs-editor-signature-add-signature-button-label = Yeni imza ekle +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Metin düzenleyicisi + .default-content = Yazmaya başlayın… pdfjs-free-text = .aria-label = Metin düzenleyicisi pdfjs-free-text-default-content = Yazmaya başlayın… @@ -353,8 +365,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Alternatif metin +pdfjs-editor-alt-text-edit-button = + .aria-label = Alternatif metni düzenle pdfjs-editor-alt-text-edit-button-label = Alternatif metni düzenle pdfjs-editor-alt-text-dialog-label = Bir seçenek seçin pdfjs-editor-alt-text-dialog-description = Alternatif metin, insanlar resmi göremediğinde veya resim yüklenmediğinde işe yarar. @@ -368,6 +381,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Dekoratif olarak işaretlendi # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Örneğin, “Genç bir adam yemek yemek için masaya oturuyor” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Alternatif metin ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -449,10 +465,16 @@ pdfjs-editor-new-alt-text-error-close-button = Kapat pdfjs-editor-new-alt-text-ai-model-downloading-progress = Alt metin yapay zekâ modeli indiriliyor ({ $downloadedSize } / { $totalSize } MB) .aria-valuetext = Alt metin yapay zekâ modeli indiriliyor ({ $downloadedSize } / { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Alternatif metin eklendi pdfjs-editor-new-alt-text-added-button-label = Alt metin eklendi # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Alternatif metin eksik pdfjs-editor-new-alt-text-missing-button-label = Alt metin eksik # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Alternatif metni incele pdfjs-editor-new-alt-text-to-review-button-label = Alt metni incele # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -479,3 +501,92 @@ pdfjs-editor-alt-text-settings-editor-title = Alt metin düzenleyicisi pdfjs-editor-alt-text-settings-show-dialog-button-label = Resim eklerken alt metin düzenleyicisini hemen göster pdfjs-editor-alt-text-settings-show-dialog-description = Tüm resimlerinizin alt metne sahip olduğundan emin olmanızı sağlar. pdfjs-editor-alt-text-settings-close-button = Kapat + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Vurgulama silindi +pdfjs-editor-undo-bar-message-freetext = Metin silindi +pdfjs-editor-undo-bar-message-ink = Çizim silindi +pdfjs-editor-undo-bar-message-stamp = Görsel silindi +pdfjs-editor-undo-bar-message-signature = İmza kaldırıldı +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } ek açıklama silindi + *[other] { $count } ek açıklama silindi + } +pdfjs-editor-undo-bar-undo-button = + .title = Geri al +pdfjs-editor-undo-bar-undo-button-label = Geri al +pdfjs-editor-undo-bar-close-button = + .title = Kapat +pdfjs-editor-undo-bar-close-button-label = Kapat + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-title = İmza ekle + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Yaz + .title = Yaz +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Çiz + .title = Çiz +pdfjs-editor-add-signature-image-button = Resim + .title = Resim + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = İmzanızı yazın + .placeholder = İmzanızı yazın +pdfjs-editor-add-signature-draw-placeholder = İmzanızı çizin +pdfjs-editor-add-signature-draw-thickness-range-label = Kalınlık +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Çizgi kalınlığı: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Yüklenecek dosyayı buraya sürükleyin +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Veya resim dosyalarına göz atın + *[other] Veya resim dosyalarına göz atın + } + +## Controls + +pdfjs-editor-add-signature-description-label = Açıklama (alt metin) +pdfjs-editor-add-signature-description-input = + .title = Açıklama (alt metin) +pdfjs-editor-add-signature-description-default-when-drawing = İmza +pdfjs-editor-add-signature-clear-button-label = İmzayı temizle +pdfjs-editor-add-signature-clear-button = + .title = İmzayı temizle +pdfjs-editor-add-signature-save-checkbox = İmzayı kaydet +pdfjs-editor-add-signature-save-warning-message = Kayıtlı 5 imza sınırına ulaştınız. Daha fazla imza kaydetmek için imzalardan birini kaldırın. +pdfjs-editor-add-signature-image-upload-error-title = Resim yüklenemedi +pdfjs-editor-add-signature-image-upload-error-description = Ağ bağlantınızı kontrol edin veya başka bir resim deneyin. +pdfjs-editor-add-signature-error-close-button = Kapat + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Vazgeç +pdfjs-editor-add-signature-add-button = Ekle +pdfjs-editor-edit-signature-update-button = Güncelle + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = İmzayı kaldır +pdfjs-editor-delete-signature-button-label = İmzayı kaldır + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Açıklamayı düzenle + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Açıklamayı düzenle diff --git a/l10n/trs/viewer.ftl b/l10n/trs/viewer.ftl index aba3c72a93f25..2b4c44b24d74e 100644 --- a/l10n/trs/viewer.ftl +++ b/l10n/trs/viewer.ftl @@ -189,9 +189,56 @@ pdfjs-password-cancel-button = Duyichin' ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/uk/viewer.ftl b/l10n/uk/viewer.ftl index d11cb440d5a79..1c4d302d053cf 100644 --- a/l10n/uk/viewer.ftl +++ b/l10n/uk/viewer.ftl @@ -298,7 +298,7 @@ pdfjs-password-label = Введіть пароль для відкриття ц pdfjs-password-invalid = Неправильний пароль. Спробуйте ще раз. pdfjs-password-ok-button = OK pdfjs-password-cancel-button = Скасувати -pdfjs-web-fonts-disabled = Веб-шрифти вимкнено: неможливо використати вбудовані у PDF шрифти. +pdfjs-web-fonts-disabled = Вебшрифти вимкнено: неможливо використати вбудовані у PDF шрифти. ## Editing @@ -345,6 +345,10 @@ pdfjs-editor-stamp-add-image-button-label = Додати зображення pdfjs-editor-free-highlight-thickness-input = Товщина pdfjs-editor-free-highlight-thickness-title = .title = Змінюйте товщину під час підсвічування елементів, крім тексту +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Текстовий редактор + .default-content = Напишіть щось… pdfjs-free-text = .aria-label = Текстовий редактор pdfjs-free-text-default-content = Почніть вводити… @@ -355,8 +359,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Альтернативний текст +pdfjs-editor-alt-text-edit-button = + .aria-label = Редагувати альтернативний текст pdfjs-editor-alt-text-edit-button-label = Змінити альтернативний текст pdfjs-editor-alt-text-dialog-label = Вибрати варіант pdfjs-editor-alt-text-dialog-description = Альтернативний текст допомагає, коли зображення не видно або коли воно не завантажується. @@ -370,6 +375,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Позначено декоратив # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Наприклад, “Молодий чоловік сідає за стіл їсти” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Альтернативний текст ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -451,10 +459,16 @@ pdfjs-editor-new-alt-text-error-close-button = Закрити pdfjs-editor-new-alt-text-ai-model-downloading-progress = Завантаження моделі ШІ для альтернативного тексту ({ $downloadedSize } з { $totalSize } МБ) .aria-valuetext = Завантаження моделі ШІ для альтернативного тексту ({ $downloadedSize } з { $totalSize } МБ) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Альтернативний текст додано pdfjs-editor-new-alt-text-added-button-label = Альтернативний текст додано # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Відсутній альтернативний текст pdfjs-editor-new-alt-text-missing-button-label = Відсутній альтернативний текст # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Переглянути альтернативний текст pdfjs-editor-new-alt-text-to-review-button-label = Переглянути альтернативний текст # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -481,3 +495,48 @@ pdfjs-editor-alt-text-settings-editor-title = Редактор альтерна pdfjs-editor-alt-text-settings-show-dialog-button-label = Показувати редактор альтернативного тексту під час додавання зображення pdfjs-editor-alt-text-settings-show-dialog-description = Допомагає переконатися, що всі ваші зображення мають альтернативний текст. pdfjs-editor-alt-text-settings-close-button = Закрити + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Підсвічення вилучено +pdfjs-editor-undo-bar-message-freetext = Текст вилучено +pdfjs-editor-undo-bar-message-ink = Малюнок вилучено +pdfjs-editor-undo-bar-message-stamp = Зображення вилучено +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = + { $count -> + [one] { $count } анотацію вилучено + [few] { $count } анотації вилучено + *[many] { $count } анотацій вилучено + } +pdfjs-editor-undo-bar-undo-button = + .title = Повернути +pdfjs-editor-undo-bar-undo-button-label = Повернути +pdfjs-editor-undo-bar-close-button = + .title = Закрити +pdfjs-editor-undo-bar-close-button-label = Закрити + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/ur/viewer.ftl b/l10n/ur/viewer.ftl index c15f157e78b73..9d657d4162994 100644 --- a/l10n/ur/viewer.ftl +++ b/l10n/ur/viewer.ftl @@ -240,9 +240,56 @@ pdfjs-web-fonts-disabled = ویب فانٹ نا اہل ہیں: شامل PDF فا ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/uz/viewer.ftl b/l10n/uz/viewer.ftl index fb82f22d41c5e..3a9448d3ddbca 100644 --- a/l10n/uz/viewer.ftl +++ b/l10n/uz/viewer.ftl @@ -179,9 +179,56 @@ pdfjs-web-fonts-disabled = Veb shriftlar oʻchirilgan: ichki PDF shriftlardan fo ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/vi/viewer.ftl b/l10n/vi/viewer.ftl index 14d990addd5a2..4f4334b498c03 100644 --- a/l10n/vi/viewer.ftl +++ b/l10n/vi/viewer.ftl @@ -308,6 +308,9 @@ pdfjs-highlight-floating-button1 = .title = Đánh dấu .aria-label = Đánh dấu pdfjs-highlight-floating-button-label = Đánh dấu +pdfjs-editor-signature-button = + .title = Thêm chữ ký +pdfjs-editor-signature-button-label = Thêm chữ ký ## Remove button for the various kind of editor. @@ -319,6 +322,8 @@ pdfjs-editor-remove-stamp-button = .title = Xóa ảnh pdfjs-editor-remove-highlight-button = .title = Xóa phần đánh dấu +pdfjs-editor-remove-signature-button = + .title = Xoá chữ ký ## @@ -335,6 +340,13 @@ pdfjs-editor-stamp-add-image-button-label = Thêm hình ảnh pdfjs-editor-free-highlight-thickness-input = Độ dày pdfjs-editor-free-highlight-thickness-title = .title = Thay đổi độ dày khi đánh dấu các mục không phải là văn bản +pdfjs-editor-signature-add-signature-button = + .title = Thêm chữ ký mới +pdfjs-editor-signature-add-signature-button-label = Thêm chữ ký mới +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = Trình chỉnh sửa văn bản + .default-content = Bắt đầu nhập… pdfjs-free-text = .aria-label = Trình sửa văn bản pdfjs-free-text-default-content = Bắt đầu nhập… @@ -345,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = Văn bản thay thế +pdfjs-editor-alt-text-edit-button = + .aria-label = Chỉnh sửa văn bản thay thế pdfjs-editor-alt-text-edit-button-label = Chỉnh sửa văn bản thay thế pdfjs-editor-alt-text-dialog-label = Chọn một lựa chọn pdfjs-editor-alt-text-dialog-description = Văn bản thay thế sẽ hữu ích khi mọi người không thể thấy hình ảnh hoặc khi hình ảnh không tải. @@ -360,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = Đã đánh dấu là trang trí # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = Ví dụ: “Một thanh niên ngồi xuống bàn để thưởng thức một bữa ăn” +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = Văn bản thay thế ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = Đóng pdfjs-editor-new-alt-text-ai-model-downloading-progress = Đang tải xuống mô hình AI văn bản thay thế ({ $downloadedSize } trong số { $totalSize } MB) .aria-valuetext = Đang tải xuống mô hình AI văn bản thay thế ({ $downloadedSize } trong số { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = Đã thêm văn bản thay thế pdfjs-editor-new-alt-text-added-button-label = Đã thêm văn bản thay thế # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = Thiếu văn bản thay thế pdfjs-editor-new-alt-text-missing-button-label = Thiếu văn bản thay thế # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = Xem lại văn bản thay thế pdfjs-editor-new-alt-text-to-review-button-label = Xem lại văn bản thay thế # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +493,89 @@ pdfjs-editor-alt-text-settings-editor-title = Trình soạn thảo văn bản th pdfjs-editor-alt-text-settings-show-dialog-button-label = Hiển thị ngay trình soạn thảo văn bản thay thế khi thêm hình ảnh pdfjs-editor-alt-text-settings-show-dialog-description = Giúp bạn đảm bảo tất cả hình ảnh của bạn đều có văn bản thay thế. pdfjs-editor-alt-text-settings-close-button = Đóng + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = Đã xóa đánh dấu +pdfjs-editor-undo-bar-message-freetext = Đã xóa văn bản +pdfjs-editor-undo-bar-message-ink = Đã xóa bản vẽ +pdfjs-editor-undo-bar-message-stamp = Đã xóa hình ảnh +pdfjs-editor-undo-bar-message-signature = Chữ ký đã bị xoá +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = { $count } chú thích đã bị xóa +pdfjs-editor-undo-bar-undo-button = + .title = Hoàn tác +pdfjs-editor-undo-bar-undo-button-label = Hoàn tác +pdfjs-editor-undo-bar-close-button = + .title = Đóng +pdfjs-editor-undo-bar-close-button-label = Đóng + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = Phương thức này cho phép người dùng tạo một chữ ký để thêm vào tài liệu PDF. Người dùng có thể chỉnh sửa tên (cũng đóng vai trò là văn bản thay thế) và tùy chọn lưu chữ ký để sử dụng nhiều lần. +pdfjs-editor-add-signature-dialog-title = Thêm chữ ký + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = Loại + .title = Loại +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = Vẽ + .title = Vẽ +pdfjs-editor-add-signature-image-button = Hình ảnh + .title = Hình ảnh + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = Nhập chữ ký của bạn + .placeholder = Nhập chữ ký của bạn +pdfjs-editor-add-signature-draw-placeholder = Vẽ chữ ký của bạn +pdfjs-editor-add-signature-draw-thickness-range-label = Độ dày +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = Độ dày bút vẽ: { $thickness } +pdfjs-editor-add-signature-image-placeholder = Kéo một tập tin tại đây để tải lên +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] Hoặc chọn hình ảnh + *[other] Hoặc chọn hình ảnh + } + +## Controls + +pdfjs-editor-add-signature-description-label = Mô tả (văn bản thay thế) +pdfjs-editor-add-signature-description-input = + .title = Mô tả (văn bản thay thế) +pdfjs-editor-add-signature-description-default-when-drawing = Chữ ký +pdfjs-editor-add-signature-clear-button-label = Xoá chữ ký +pdfjs-editor-add-signature-clear-button = + .title = Xoá chữ ký +pdfjs-editor-add-signature-save-checkbox = Lưu chữ ký +pdfjs-editor-add-signature-save-warning-message = Bạn đã đạt đến giới hạn 5 chữ ký đã lưu. Hãy xóa một cái để lưu thêm. +pdfjs-editor-add-signature-image-upload-error-title = Không thể tải lên hình ảnh +pdfjs-editor-add-signature-image-upload-error-description = Kiểm tra kết nối mạng của bạn hoặc thử hình ảnh khác. +pdfjs-editor-add-signature-error-close-button = Đóng + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = Hủy bỏ +pdfjs-editor-add-signature-add-button = Thêm +pdfjs-editor-edit-signature-update-button = Cập nhật + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = Xoá chữ ký +pdfjs-editor-delete-signature-button-label = Xoá chữ ký + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = Chỉnh sửa mô tả + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = Chỉnh sửa mô tả diff --git a/l10n/wo/viewer.ftl b/l10n/wo/viewer.ftl index d66c4591fcd01..b926e914282a2 100644 --- a/l10n/wo/viewer.ftl +++ b/l10n/wo/viewer.ftl @@ -119,9 +119,56 @@ pdfjs-password-cancel-button = Neenal ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/xh/viewer.ftl b/l10n/xh/viewer.ftl index 079888733d062..02702259f4047 100644 --- a/l10n/xh/viewer.ftl +++ b/l10n/xh/viewer.ftl @@ -204,9 +204,56 @@ pdfjs-web-fonts-disabled = Iifonti zewebhu ziqhwalelisiwe: ayikwazi ukusebenzisa ## Editing +## Remove button for the various kind of editor. + + +## + + ## Alt-text dialog ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. + +## Color picker + + +## Show all highlights +## This is a toggle button to show/hide all the highlights. + + +## New alt-text dialog +## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy. + + +## Image alt-text settings + + +## "Annotations removed" bar + + +## Add a signature dialog + + +## Tab names + + +## Tab panels + + +## Controls + + +## Dialog buttons + + +## Main menu for adding/removing signatures + + +## Editor toolbar + + +## Edit signature description dialog + diff --git a/l10n/zh-CN/viewer.ftl b/l10n/zh-CN/viewer.ftl index 9cebb3cd2c2cf..e983ad17c137f 100644 --- a/l10n/zh-CN/viewer.ftl +++ b/l10n/zh-CN/viewer.ftl @@ -308,6 +308,9 @@ pdfjs-highlight-floating-button1 = .title = 高亮 .aria-label = 高亮 pdfjs-highlight-floating-button-label = 高亮 +pdfjs-editor-signature-button = + .title = 添加签名 +pdfjs-editor-signature-button-label = 添加签名 ## Remove button for the various kind of editor. @@ -319,6 +322,8 @@ pdfjs-editor-remove-stamp-button = .title = 移除图像 pdfjs-editor-remove-highlight-button = .title = 移除高亮 +pdfjs-editor-remove-signature-button = + .title = 移除签名 ## @@ -335,6 +340,13 @@ pdfjs-editor-stamp-add-image-button-label = 添加图像 pdfjs-editor-free-highlight-thickness-input = 粗细 pdfjs-editor-free-highlight-thickness-title = .title = 更改高亮粗细(用于文本以外项目) +pdfjs-editor-signature-add-signature-button = + .title = 添加新签名 +pdfjs-editor-signature-add-signature-button-label = 添加新签名 +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 文本编辑器 + .default-content = 在此键入… pdfjs-free-text = .aria-label = 文本编辑器 pdfjs-free-text-default-content = 开始输入… @@ -345,13 +357,14 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = 替换文字 +pdfjs-editor-alt-text-edit-button = + .aria-label = 编辑替换文字 pdfjs-editor-alt-text-edit-button-label = 编辑替换文字 pdfjs-editor-alt-text-dialog-label = 选择一项 pdfjs-editor-alt-text-dialog-description = 替换文字可在用户无法看到或加载图像时,描述其内容。 pdfjs-editor-alt-text-add-description-label = 添加描述 -pdfjs-editor-alt-text-add-description-description = 描述主题、背景或动作,长度尽量控制在两句话内。 +pdfjs-editor-alt-text-add-description-description = 用一两个句子,描述主题、背景或动作。 pdfjs-editor-alt-text-mark-decorative-label = 标记为装饰 pdfjs-editor-alt-text-mark-decorative-description = 用于装饰的图像,例如边框和水印。 pdfjs-editor-alt-text-cancel-button = 取消 @@ -360,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = 已标记为装饰 # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = 例如:一个少年坐到桌前,准备吃饭 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 替换文字 ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = 关闭 pdfjs-editor-new-alt-text-ai-model-downloading-progress = 正在下载提供替换文字的 AI 模型({ $downloadedSize }/{ $totalSize } MB) .aria-valuetext = 正在下载提供替换文字的 AI 模型({ $downloadedSize }/{ $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 已添加替换文字 pdfjs-editor-new-alt-text-added-button-label = 已添加替换文字 # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 缺少替换文字 pdfjs-editor-new-alt-text-missing-button-label = 缺少替换文字 # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 检查替换文字 pdfjs-editor-new-alt-text-to-review-button-label = 检查替换文字 # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +493,89 @@ pdfjs-editor-alt-text-settings-editor-title = 替换文字编辑器 pdfjs-editor-alt-text-settings-show-dialog-button-label = 添加图像后立即显示替换文字编辑器 pdfjs-editor-alt-text-settings-show-dialog-description = 帮助确保所有图像均拥有替换文字。 pdfjs-editor-alt-text-settings-close-button = 关闭 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 已移除高亮 +pdfjs-editor-undo-bar-message-freetext = 已移除文本 +pdfjs-editor-undo-bar-message-ink = 已移除绘图 +pdfjs-editor-undo-bar-message-stamp = 已移除图像 +pdfjs-editor-undo-bar-message-signature = 签名已移除 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 已移除 { $count } 条注释 +pdfjs-editor-undo-bar-undo-button = + .title = 撤销 +pdfjs-editor-undo-bar-undo-button-label = 撤销 +pdfjs-editor-undo-bar-close-button = + .title = 关闭 +pdfjs-editor-undo-bar-close-button-label = 关闭 + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = 用户可通过此模态对话框创建要添加到 PDF 文档中的签名、编辑其名称(同时用作替换文字),并可保存签名以便重复使用。 +pdfjs-editor-add-signature-dialog-title = 添加签名 + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = 键入 + .title = 键入 +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = 绘制 + .title = 绘制 +pdfjs-editor-add-signature-image-button = 图像 + .title = 图像 + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = 键入签名 + .placeholder = 键入签名 +pdfjs-editor-add-signature-draw-placeholder = 绘制签名 +pdfjs-editor-add-signature-draw-thickness-range-label = 粗细 +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = 笔画粗细:{ $thickness } +pdfjs-editor-add-signature-image-placeholder = 拖放文件到此处以上传 +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] 或选取图像文件 + *[other] 或浏览图像文件 + } + +## Controls + +pdfjs-editor-add-signature-description-label = 描述(替换文字) +pdfjs-editor-add-signature-description-input = + .title = 描述(替换文字) +pdfjs-editor-add-signature-description-default-when-drawing = 签名 +pdfjs-editor-add-signature-clear-button-label = 清除签名 +pdfjs-editor-add-signature-clear-button = + .title = 清除签名 +pdfjs-editor-add-signature-save-checkbox = 保存签名 +pdfjs-editor-add-signature-save-warning-message = 最多可保存 5 个签名,请移除一个以继续保存。 +pdfjs-editor-add-signature-image-upload-error-title = 无法上传图像 +pdfjs-editor-add-signature-image-upload-error-description = 请检查网络连接,或尝试上传其他图像。 +pdfjs-editor-add-signature-error-close-button = 关闭 + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = 取消 +pdfjs-editor-add-signature-add-button = 添加 +pdfjs-editor-edit-signature-update-button = 更新 + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = 移除签名 +pdfjs-editor-delete-signature-button-label = 移除签名 + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = 编辑说明 + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = 编辑说明 diff --git a/l10n/zh-TW/viewer.ftl b/l10n/zh-TW/viewer.ftl index e96d6375fbea9..84f898311c74d 100644 --- a/l10n/zh-TW/viewer.ftl +++ b/l10n/zh-TW/viewer.ftl @@ -308,6 +308,9 @@ pdfjs-highlight-floating-button1 = .title = 強調 .aria-label = 強調 pdfjs-highlight-floating-button-label = 強調 +pdfjs-editor-signature-button = + .title = 加入簽名 +pdfjs-editor-signature-button-label = 加入簽名 ## Remove button for the various kind of editor. @@ -319,6 +322,8 @@ pdfjs-editor-remove-stamp-button = .title = 移除圖片 pdfjs-editor-remove-highlight-button = .title = 移除強調範圍 +pdfjs-editor-remove-signature-button = + .title = 移除簽章 ## @@ -335,6 +340,13 @@ pdfjs-editor-stamp-add-image-button-label = 新增圖片 pdfjs-editor-free-highlight-thickness-input = 線條粗細 pdfjs-editor-free-highlight-thickness-title = .title = 更改強調文字以外的項目時的線條粗細 +pdfjs-editor-signature-add-signature-button = + .title = 新增簽名 +pdfjs-editor-signature-add-signature-button-label = 新增簽章 +# .default-content is used as a placeholder in an empty text editor. +pdfjs-free-text2 = + .aria-label = 文字編輯器 + .default-content = 請打字… pdfjs-free-text = .aria-label = 文本編輯器 pdfjs-free-text-default-content = 在此打字… @@ -345,8 +357,9 @@ pdfjs-ink-canvas = ## Alt-text dialog -# Alternative text (alt text) helps when people can't see the image. pdfjs-editor-alt-text-button-label = 替代文字 +pdfjs-editor-alt-text-edit-button = + .aria-label = 編輯替代文字 pdfjs-editor-alt-text-edit-button-label = 編輯替代文字 pdfjs-editor-alt-text-dialog-label = 挑選一種 pdfjs-editor-alt-text-dialog-description = 替代文字可協助盲人,或於圖片無法載入時提供說明。 @@ -360,6 +373,9 @@ pdfjs-editor-alt-text-decorative-tooltip = 已標示為裝飾性內容 # .placeholder: This is a placeholder for the alt text input area pdfjs-editor-alt-text-textarea = .placeholder = 例如:「有一位年輕男人坐在桌子前面吃飯」 +# Alternative text (alt text) helps when people can't see the image. +pdfjs-editor-alt-text-button = + .aria-label = 替代文字 ## Editor resizers ## This is used in an aria label to help to understand the role of the resizer. @@ -441,10 +457,16 @@ pdfjs-editor-new-alt-text-error-close-button = 關閉 pdfjs-editor-new-alt-text-ai-model-downloading-progress = 正在下載替代文字 AI 模型({ $downloadedSize } / { $totalSize } MB) .aria-valuetext = 正在下載替代文字 AI 模型({ $downloadedSize } / { $totalSize } MB) # This is a button that users can click to edit the alt text they have already added. +pdfjs-editor-new-alt-text-added-button = + .aria-label = 已新增替代文字 pdfjs-editor-new-alt-text-added-button-label = 已新增替代文字 # This is a button that users can click to open the alt text editor and add alt text when it is not present. +pdfjs-editor-new-alt-text-missing-button = + .aria-label = 缺少替代文字 pdfjs-editor-new-alt-text-missing-button-label = 缺少替代文字 # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. +pdfjs-editor-new-alt-text-to-review-button = + .aria-label = 確認替代文字 pdfjs-editor-new-alt-text-to-review-button-label = 確認替代文字 # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # Variables: @@ -471,3 +493,89 @@ pdfjs-editor-alt-text-settings-editor-title = 替代文字編輯器 pdfjs-editor-alt-text-settings-show-dialog-button-label = 新增圖片後立即顯示替代文字編輯器 pdfjs-editor-alt-text-settings-show-dialog-description = 幫助您確保所有圖片都有替代文字。 pdfjs-editor-alt-text-settings-close-button = 關閉 + +## "Annotations removed" bar + +pdfjs-editor-undo-bar-message-highlight = 已移除強調 +pdfjs-editor-undo-bar-message-freetext = 已移除文字 +pdfjs-editor-undo-bar-message-ink = 已移除繪圖 +pdfjs-editor-undo-bar-message-stamp = 已移除圖片 +pdfjs-editor-undo-bar-message-signature = 已移除簽章 +# Variables: +# $count (Number) - the number of removed annotations. +pdfjs-editor-undo-bar-message-multiple = 已移除 { $count } 筆註解 +pdfjs-editor-undo-bar-undo-button = + .title = 還原 +pdfjs-editor-undo-bar-undo-button-label = 還原 +pdfjs-editor-undo-bar-close-button = + .title = 關閉 +pdfjs-editor-undo-bar-close-button-label = 關閉 + +## Add a signature dialog + +pdfjs-editor-add-signature-dialog-label = 此對話框讓使用者能夠建立簽章以加入 PDF 文件。使用者可以編輯他們的姓名(同時也是替代文字),並選擇性儲存簽章,以供未來重複使用。 +pdfjs-editor-add-signature-dialog-title = 加入簽章 + +## Tab names + +# Type is a verb (you can type your name as signature) +pdfjs-editor-add-signature-type-button = 打字 + .title = 打字 +# Draw is a verb (you can draw your signature) +pdfjs-editor-add-signature-draw-button = 手繪 + .title = 手繪 +pdfjs-editor-add-signature-image-button = 圖片 + .title = 圖片 + +## Tab panels + +pdfjs-editor-add-signature-type-input = + .aria-label = 輸入簽章 + .placeholder = 輸入簽章 +pdfjs-editor-add-signature-draw-placeholder = 手繪簽章 +pdfjs-editor-add-signature-draw-thickness-range-label = 線條粗細 +# Variables: +# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature. +pdfjs-editor-add-signature-draw-thickness-range = + .title = 繪製時的線條粗細:{ $thickness } +pdfjs-editor-add-signature-image-placeholder = 將檔案拖曳到此處即可上傳 +pdfjs-editor-add-signature-image-browse-link = + { PLATFORM() -> + [macos] 或選擇圖片檔案 + *[other] 或瀏覽圖片檔案 + } + +## Controls + +pdfjs-editor-add-signature-description-label = 描述(替代文字) +pdfjs-editor-add-signature-description-input = + .title = 描述(替代文字) +pdfjs-editor-add-signature-description-default-when-drawing = 簽章 +pdfjs-editor-add-signature-clear-button-label = 清除簽章 +pdfjs-editor-add-signature-clear-button = + .title = 清除簽章 +pdfjs-editor-add-signature-save-checkbox = 儲存簽章 +pdfjs-editor-add-signature-save-warning-message = 您已經儲存 5 式簽章,請移除任一式才能再新增。 +pdfjs-editor-add-signature-image-upload-error-title = 無法上傳圖片 +pdfjs-editor-add-signature-image-upload-error-description = 請檢查您的網路連線,或改用其他圖片。 +pdfjs-editor-add-signature-error-close-button = 關閉 + +## Dialog buttons + +pdfjs-editor-add-signature-cancel-button = 取消 +pdfjs-editor-add-signature-add-button = 新增 +pdfjs-editor-edit-signature-update-button = 更新 + +## Main menu for adding/removing signatures + +pdfjs-editor-delete-signature-button = + .title = 移除簽章 +pdfjs-editor-delete-signature-button-label = 移除簽章 + +## Editor toolbar + +pdfjs-editor-add-signature-edit-button-label = 編輯描述 + +## Edit signature description dialog + +pdfjs-editor-edit-signature-dialog-title = 編輯描述 diff --git a/package-lock.json b/package-lock.json index eb62e79b964b8..a3ee44fab00c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,63 +7,63 @@ "name": "pdf.js", "license": "Apache-2.0", "devDependencies": { - "@babel/core": "^7.25.8", - "@babel/preset-env": "^7.25.8", - "@babel/runtime": "^7.25.7", + "@babel/core": "^7.26.9", + "@babel/preset-env": "^7.26.9", + "@babel/runtime": "^7.26.9", "@fluent/bundle": "^0.18.0", "@fluent/dom": "^0.10.0", - "@jazzer.js/core": "^2.1.0", "@metalsmith/layouts": "^2.7.0", "@metalsmith/markdown": "^1.10.0", + "@napi-rs/canvas": "^0.1.67", + "@types/node": "^22.13.5", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", - "caniuse-lite": "^1.0.30001669", - "canvas": "^3.0.0-rc2", - "core-js": "^3.38.1", - "eslint": "^8.57.1", + "caniuse-lite": "^1.0.30001700", + "core-js": "^3.40.0", + "eslint": "^9.21.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jasmine": "^4.2.2", - "eslint-plugin-json": "^3.1.0", + "eslint-plugin-json": "^4.0.1", "eslint-plugin-no-unsanitized": "^4.1.2", - "eslint-plugin-perfectionist": "^3.9.1", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-unicorn": "^56.0.0", + "eslint-plugin-perfectionist": "^4.9.0", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-unicorn": "^57.0.0", + "globals": "^16.0.0", "gulp": "^5.0.0", "gulp-cli": "^3.0.0", "gulp-postcss": "^10.0.0", "gulp-rename": "^2.0.0", "gulp-replace": "^1.1.4", - "gulp-zip": "^6.0.0", - "highlight.js": "^11.10.0", - "jasmine": "^5.4.0", + "gulp-zip": "^6.1.0", + "highlight.js": "^11.11.1", + "jasmine": "^5.6.0", "jsdoc": "^4.0.4", "jstransformer-nunjucks": "^1.2.0", "metalsmith": "^2.6.3", - "metalsmith-html-relative": "^2.0.5", + "metalsmith-html-relative": "^2.0.6", "ordered-read-streams": "^2.0.0", - "path2d": "^0.2.1", "pngjs": "^7.0.0", - "postcss": "^8.4.47", + "postcss": "^8.5.3", "postcss-dark-theme-class": "^1.3.0", - "postcss-dir-pseudo-class": "^9.0.0", + "postcss-dir-pseudo-class": "^9.0.1", "postcss-discard-comments": "^7.0.3", - "postcss-nesting": "^13.0.0", - "prettier": "^3.3.3", - "puppeteer": "23.3.1", - "stylelint": "^16.10.0", - "stylelint-prettier": "^5.0.2", - "svglint": "^3.0.0", - "terser-webpack-plugin": "^5.3.10", + "postcss-nesting": "^13.0.1", + "prettier": "^3.5.2", + "puppeteer": "^24.2.1", + "stylelint": "^16.14.1", + "stylelint-prettier": "^5.0.3", + "svglint": "^3.1.0", + "terser-webpack-plugin": "^5.3.11", "tsc-alias": "^1.8.10", "ttest": "^4.0.0", - "typescript": "^5.6.3", + "typescript": "^5.7.3", "vinyl": "^3.0.0", - "webpack": "^5.95.0", + "webpack": "^5.98.0", "webpack-stream": "^7.0.0", "yargs": "^17.7.2" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -102,13 +102,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -116,9 +117,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -126,22 +127,22 @@ } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -166,13 +167,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -182,41 +184,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", - "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -244,18 +232,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", - "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/traverse": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -276,13 +264,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", - "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, @@ -303,9 +291,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, "license": "MIT", "dependencies": { @@ -320,44 +308,43 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", - "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -367,22 +354,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", - "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -390,15 +377,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", - "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-wrap-function": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -408,15 +395,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", - "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -425,38 +412,24 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", - "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -464,9 +437,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -474,9 +447,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -484,58 +457,42 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", - "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -545,14 +502,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", - "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -562,13 +519,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", - "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -578,13 +535,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", - "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -594,15 +551,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", - "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -612,14 +569,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", - "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -641,13 +598,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", - "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -657,13 +614,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -689,13 +646,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", - "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -705,15 +662,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", - "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -723,15 +680,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", - "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -741,13 +698,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", - "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -757,13 +714,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", - "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -773,14 +730,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", - "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -790,14 +747,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", - "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -807,17 +764,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", - "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/traverse": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -838,14 +795,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", - "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/template": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -855,13 +812,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", - "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -871,14 +828,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", - "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -888,13 +845,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", - "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -904,14 +861,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -921,13 +878,13 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", - "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -937,14 +894,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", - "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -954,13 +910,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", - "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -970,14 +926,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", - "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -987,15 +943,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", - "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1005,13 +961,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", - "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1021,13 +977,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", - "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1037,13 +993,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", - "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1053,13 +1009,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", - "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1069,14 +1025,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", - "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1086,15 +1042,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", - "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1104,16 +1059,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", - "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1123,14 +1078,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", - "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1140,14 +1095,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1157,13 +1112,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", - "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1173,13 +1128,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", - "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1189,13 +1144,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", - "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1205,15 +1160,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", - "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-transform-parameters": "^7.25.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1223,14 +1178,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", - "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1240,13 +1195,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", - "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1256,14 +1211,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", - "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1273,13 +1228,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", - "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1289,14 +1244,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", - "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1306,15 +1261,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", - "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1324,13 +1279,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", - "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1340,13 +1295,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", - "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1356,14 +1311,31 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", - "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1373,13 +1345,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", - "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1389,14 +1361,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", - "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1406,13 +1378,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", - "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1422,13 +1394,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", - "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1438,13 +1410,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", - "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1454,13 +1426,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", - "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1470,14 +1442,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", - "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1487,14 +1459,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", - "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1504,14 +1476,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", - "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1521,79 +1493,80 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", - "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.8", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.25.7", - "@babel/plugin-syntax-import-attributes": "^7.25.7", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.8", - "@babel/plugin-transform-async-to-generator": "^7.25.7", - "@babel/plugin-transform-block-scoped-functions": "^7.25.7", - "@babel/plugin-transform-block-scoping": "^7.25.7", - "@babel/plugin-transform-class-properties": "^7.25.7", - "@babel/plugin-transform-class-static-block": "^7.25.8", - "@babel/plugin-transform-classes": "^7.25.7", - "@babel/plugin-transform-computed-properties": "^7.25.7", - "@babel/plugin-transform-destructuring": "^7.25.7", - "@babel/plugin-transform-dotall-regex": "^7.25.7", - "@babel/plugin-transform-duplicate-keys": "^7.25.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-dynamic-import": "^7.25.8", - "@babel/plugin-transform-exponentiation-operator": "^7.25.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.8", - "@babel/plugin-transform-for-of": "^7.25.7", - "@babel/plugin-transform-function-name": "^7.25.7", - "@babel/plugin-transform-json-strings": "^7.25.8", - "@babel/plugin-transform-literals": "^7.25.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", - "@babel/plugin-transform-member-expression-literals": "^7.25.7", - "@babel/plugin-transform-modules-amd": "^7.25.7", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-modules-systemjs": "^7.25.7", - "@babel/plugin-transform-modules-umd": "^7.25.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-new-target": "^7.25.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", - "@babel/plugin-transform-numeric-separator": "^7.25.8", - "@babel/plugin-transform-object-rest-spread": "^7.25.8", - "@babel/plugin-transform-object-super": "^7.25.7", - "@babel/plugin-transform-optional-catch-binding": "^7.25.8", - "@babel/plugin-transform-optional-chaining": "^7.25.8", - "@babel/plugin-transform-parameters": "^7.25.7", - "@babel/plugin-transform-private-methods": "^7.25.7", - "@babel/plugin-transform-private-property-in-object": "^7.25.8", - "@babel/plugin-transform-property-literals": "^7.25.7", - "@babel/plugin-transform-regenerator": "^7.25.7", - "@babel/plugin-transform-reserved-words": "^7.25.7", - "@babel/plugin-transform-shorthand-properties": "^7.25.7", - "@babel/plugin-transform-spread": "^7.25.7", - "@babel/plugin-transform-sticky-regex": "^7.25.7", - "@babel/plugin-transform-template-literals": "^7.25.7", - "@babel/plugin-transform-typeof-symbol": "^7.25.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.7", - "@babel/plugin-transform-unicode-property-regex": "^7.25.7", - "@babel/plugin-transform-unicode-regex": "^7.25.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -1627,9 +1600,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "dev": true, "license": "MIT", "dependencies": { @@ -1640,32 +1613,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1684,33 +1657,23 @@ } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/types/node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", - "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", "dev": true, "funding": [ { @@ -1727,13 +1690,13 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.1" + "@csstools/css-tokenizer": "^3.0.3" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", - "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", "dev": true, "funding": [ { @@ -1751,9 +1714,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", - "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", + "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", "dev": true, "funding": [ { @@ -1770,54 +1733,8 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1" - } - }, - "node_modules/@csstools/selector-resolve-nested": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-2.0.0.tgz", - "integrity": "sha512-oklSrRvOxNeeOW1yARd4WNCs/D09cQjunGZUgSq6vM8GpzFswN+8rBZyJA29YFZhOTQ6GFzxgLDNtVbt9wPZMA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.1.0" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", - "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.1.0" + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" } }, "node_modules/@dual-bundle/import-meta-resolve": { @@ -1832,39 +1749,73 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1872,7 +1823,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1882,18 +1833,17 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1904,6 +1854,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1911,26 +1862,38 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@eslint/js": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", + "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@fluent/bundle": { @@ -1979,20 +1942,42 @@ "node": ">=10.13.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2008,13 +1993,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -2112,103 +2103,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/bug-detectors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/bug-detectors/-/bug-detectors-2.1.0.tgz", - "integrity": "sha512-rFERKoSkLNZq52rRCXQKRqLX9Bfkb2+tzMrI/tDQ27//HwfdaJUej4MPKXg3YxFSGAEtshNNSXAn2liJmyLWpQ==", - "dev": true, - "dependencies": { - "@jazzer.js/core": "2.1.0", - "@jazzer.js/hooking": "2.1.0" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-2.1.0.tgz", - "integrity": "sha512-9uTuKlZ0WyC8evq++46TYg0SFszGx7X4/Y8nVsFmIg9sc0ceO9fmE1qg8KOafkzM9DvSdv+JL2UYWxnmgE1CJQ==", - "dev": true, - "dependencies": { - "@jazzer.js/bug-detectors": "2.1.0", - "@jazzer.js/fuzzer": "2.1.0", - "@jazzer.js/hooking": "2.1.0", - "@jazzer.js/instrumentor": "2.1.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "tmp": "^0.2.1", - "yargs": "^17.7.2" - }, - "bin": { - "jazzer": "dist/cli.js" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/fuzzer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-2.1.0.tgz", - "integrity": "sha512-WroUAGH1+AFoI+5kRGOjvh9+1GsYEmCinvEt9yF0E0pBTeadssCG6Y8adeYh6W0rum2LjPsjVfLEWMunRLt/Ew==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.5.0", - "cmake-js": "^7.2.1", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/hooking": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-2.1.0.tgz", - "integrity": "sha512-caNQslD27mxkYfi7woVEN98rbPL41Ezn/pGkMB/ghcBWcG0jCJDqacRJX4Gv8ypfIhemDJEocL26Ld2XSx07Pw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/instrumentor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-2.1.0.tgz", - "integrity": "sha512-ewvx2DKczcwusO5XlRYaMO757cFDKY+mEbS2g5k81Razq3AA/uRVU1/Ei/1FZeg9tKCuaTq9Gwl+9bdA9ER8Og==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/generator": "^7.23.0", - "@jazzer.js/fuzzer": "2.1.0", - "@jazzer.js/hooking": "2.1.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^6.0.1", - "proper-lockfile": "^4.1.2", - "source-map-support": "^0.5.21" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2242,13 +2136,14 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -2279,6 +2174,41 @@ "node": ">=v12.0.0" } }, + "node_modules/@keyv/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + } + }, + "node_modules/@keyv/serialize/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/@metalsmith/layouts": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@metalsmith/layouts/-/layouts-2.7.0.tgz", @@ -2315,10 +2245,202 @@ "metalsmith": "^2.5.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@napi-rs/canvas": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.67.tgz", + "integrity": "sha512-VA4Khm/5Kg2bQGx3jXotTC4MloOG8b1Ung80exafUK0k5u6yJmIz3Q2iXeeWZs5weV+LQOEB+CPKsYwEYaGAjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.67", + "@napi-rs/canvas-darwin-arm64": "0.1.67", + "@napi-rs/canvas-darwin-x64": "0.1.67", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.67", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.67", + "@napi-rs/canvas-linux-arm64-musl": "0.1.67", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.67", + "@napi-rs/canvas-linux-x64-gnu": "0.1.67", + "@napi-rs/canvas-linux-x64-musl": "0.1.67", + "@napi-rs/canvas-win32-x64-msvc": "0.1.67" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.67.tgz", + "integrity": "sha512-W+3DFG5h0WU8Vqqb3W5fNmm5/TPH5ECZRinQDK4CAKFSUkc4iZcDwrmyFG9sB4KdHazf1mFVHCpEeVMO6Mk6Zg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.67.tgz", + "integrity": "sha512-xzrv7QboI47yhIHR5P5u/9KGswokuOKLiKSukr1Ku03RRJxP6lGuVtrAZAgdRg7F9FsuF2REf2yK53YVb6pMlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.67.tgz", + "integrity": "sha512-SNk9lYBr84N0gW8MZ2IrjygFtbFBILr3SEqMdHzHHuph20SQmssFvJGPZwSSCMEyKAvyqhogbmlew0te5Z4w9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.67.tgz", + "integrity": "sha512-qmBlSvUpl567bzH8tNXi82u5FrL4d0qINqd6K9O7GWGGGFmKMJdrgi2/SW3wwCTxqHBasIDdVWc4KSJfwyaoDQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.67.tgz", + "integrity": "sha512-k3nAPQefkMeFuJ65Rqdnx92KX1JXQhEKjjWeKsCJB+7sIBgQUWtHo9c3etfVLv5pkWJJDFi/Zc2soNkH3E8dRA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.67.tgz", + "integrity": "sha512-lZwHWR1cCP408l86n3Qbs3X1oFeAYMjJIQvQl1VMZh6wo5PfI+jaZSKBUOd8x44TnVllX9yhLY9unNRztk/sUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.67.tgz", + "integrity": "sha512-PdBC9p6bLHA1W3OdA0vTHj701SB/kioGQ1uCFBRMs5KBCaMLb/H4aNi8uaIUIEvBWnxeAjoNcLU7//q0FxEosw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.67.tgz", + "integrity": "sha512-kJJX6eWzjipL/LdKOWCJctc88e5yzuXri8+s0V/lN06OwuLGW62TWS3lvi8qlUrGMOfRGabSWWlB4omhASSB8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.67.tgz", + "integrity": "sha512-jLKiPWGeN6ZzhnaLG7ex7eexsiHJ1mdtPK1qKvETIcu45dApMXyUIHvdL6XWB5gFFtj5ScHzLUxv1vkfPZsoxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.67", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.67.tgz", + "integrity": "sha512-K/JmkOFbc4iRZYUqJhj0jwqfHA/wNQEmTiGNsgZ6d59yF/IBNp5T0D5eg3B8ghjI8GxDYCiSJ6DNX8mC3Oh2EQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2374,19 +2496,18 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", - "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.1.tgz", + "integrity": "sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "debug": "^4.3.6", + "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.0", + "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { @@ -2396,19 +2517,6 @@ "node": ">=18" } }, - "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2416,6 +2524,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -2428,18 +2543,28 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/expect": { "version": "1.20.4", @@ -2448,10 +2573,11 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -2488,22 +2614,28 @@ "dev": true }, "node_modules/@types/node": { - "version": "14.14.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", - "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==", - "dev": true + "version": "22.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", + "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/vinyl": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.9.tgz", - "integrity": "sha512-KCr4aTEUkzSF89qw09e2oxsC/RXXT3K5ZPv4gvj3XTiWVrxNoi7WrqNTahNE/Hul5C9z3B8w+yWNTQgua12oag==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", "dev": true, + "license": "MIT", "dependencies": { "@types/expect": "^1.20.4", "@types/node": "*" @@ -2521,14 +2653,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", - "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0" + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2539,9 +2671,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", - "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -2553,20 +2685,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", - "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2575,10 +2707,8 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -2607,30 +2737,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", - "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0" + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2640,18 +2757,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", - "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.10.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.24.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2661,155 +2779,177 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -2817,13 +2957,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/a-sync-waterfall": { "version": "1.0.1", @@ -2833,10 +2975,11 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2844,25 +2987,26 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2918,15 +3062,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -2979,25 +3114,29 @@ } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/ansi-wrap": { @@ -3009,24 +3148,6 @@ "node": ">=0.10.0" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3241,6 +3362,7 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3273,12 +3395,6 @@ "node": ">= 10.13.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -3333,18 +3449,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/b4a": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", @@ -3394,14 +3498,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -3448,47 +3552,64 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", - "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", + "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", - "bare-path": "^2.0.0", + "bare-path": "^3.0.0", "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.7.0" } }, "node_modules/bare-os": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", - "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", + "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", "dev": true, "license": "Apache-2.0", - "optional": true + "optional": true, + "engines": { + "bare": ">=1.6.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", - "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "b4a": "^1.6.6", - "streamx": "^2.20.0" + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, "node_modules/base64-js": { @@ -3533,40 +3654,6 @@ "url": "https://bevry.me/fund" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3604,9 +3691,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -3624,10 +3711,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -3636,35 +3723,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -3675,6 +3739,40 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "node_modules/builtin-modules": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz", + "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.7.tgz", + "integrity": "sha512-AbfG7dAuYNjYxFUtL1lAqmlWdxczCJ47w7cFjhGcnGnUdwSo6VgmSojfoW3tUI12HUkgTJ5kqj78yyq6TsFtlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.6.0", + "keyv": "^5.2.3" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.2.3.tgz", + "integrity": "sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.0.2" + } + }, "node_modules/cached-iterable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/cached-iterable/-/cached-iterable-0.3.0.tgz", @@ -3756,9 +3854,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "dev": true, "funding": [ { @@ -3776,22 +3874,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/canvas": { - "version": "3.0.0-rc2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.0.0-rc2.tgz", - "integrity": "sha512-esx4bYDznnqgRX4G8kaEaf0W3q8xIc51WpmrIitDzmcoEgwnv9wSKdzT6UxWZ4wkVu5+ileofppX0TpyviJRdQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "simple-get": "^3.0.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -3811,18 +3893,16 @@ "dev": true }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, "engines": { - "node": ">=4" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cheerio": { @@ -3869,12 +3949,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -3885,24 +3959,23 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", - "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.3.0.tgz", + "integrity": "sha512-G3x1bkST13kmbL7+dT/oRkNH/7C4UqG+0YQpmySrzXspyOhYgDNc6lhSGpj3cuexvH25WTENhTYq2Tt9JRXtbw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "dev": true, "funding": [ { @@ -3910,6 +3983,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -3931,225 +4005,49 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, - "node_modules/cmake-js": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.2.1.tgz", - "integrity": "sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==", - "dev": true, - "dependencies": { - "axios": "^1.3.2", - "debug": "^4", - "fs-extra": "^10.1.0", - "lodash.isplainobject": "^4.0.6", - "memory-stream": "^1.0.0", - "node-api-headers": "^0.0.2", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.3.8", - "tar": "^6.1.11", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.6.0" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/cmake-js/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cmake-js/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cmake-js/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cmake-js/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cmake-js/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cmake-js/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cmake-js/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/cmake-js/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/cmake-js/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" } }, "node_modules/co": { @@ -4159,37 +4057,25 @@ "dev": true, "license": "MIT" }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/color-support": { "version": "1.1.3", @@ -4206,17 +4092,12 @@ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } + "license": "MIT" }, "node_modules/common-path-prefix": { "version": "3.0.0", @@ -4230,12 +4111,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4266,9 +4141,9 @@ } }, "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4278,13 +4153,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -4342,10 +4217,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4428,13 +4304,13 @@ } }, "node_modules/css-tree": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.0.tgz", - "integrity": "sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, "license": "MIT", "dependencies": { - "mdn-data": "2.10.0", + "mdn-data": "2.12.2", "source-map-js": "^1.0.1" }, "engines": { @@ -4531,9 +4407,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -4594,27 +4470,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4631,30 +4486,6 @@ "node": ">=0.10.0" } }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-require-extensions/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4706,21 +4537,6 @@ "node": ">= 14" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -4731,9 +4547,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1330662", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", - "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", + "version": "0.0.1402036", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1402036.tgz", + "integrity": "sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==", "dev": true, "license": "BSD-3-Clause" }, @@ -4765,18 +4581,6 @@ "dev": true, "license": "MIT" }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -4886,6 +4690,7 @@ "resolved": "https://registry.npmjs.org/easy-transform-stream/-/easy-transform-stream-1.0.1.tgz", "integrity": "sha512-ktkaa6XR7COAR3oj02CF3IOgz2m1hCaY3SfzvKT4Svt2MhHw9XCt+ncJNWfe2TGz31iqzNGZ8spdKQflj+Rlog==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -4894,9 +4699,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.32", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", - "integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==", + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", "dev": true, "license": "ISC" }, @@ -5133,9 +4938,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -5184,60 +4989,63 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", + "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.21.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -5378,16 +5186,17 @@ } }, "node_modules/eslint-plugin-json": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz", - "integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-4.0.1.tgz", + "integrity": "sha512-3An5ISV5dq/kHfXdNyY5TUe2ONC3yXFSkLX2gu+W8xAhKhfvrRvkSAeKXCxZqZ0KJLX15ojBuLPyj+UikQMkOA==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.21", "vscode-json-languageservice": "^4.1.6" }, "engines": { - "node": ">=12.0" + "node": ">=18.0" } }, "node_modules/eslint-plugin-no-unsanitized": { @@ -5401,72 +5210,27 @@ } }, "node_modules/eslint-plugin-perfectionist": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-3.9.1.tgz", - "integrity": "sha512-9WRzf6XaAxF4Oi5t/3TqKP5zUjERhasHmLFHin2Yw6ZAp/EP/EVA2dr3BhQrrHWCm5SzTMZf0FcjDnBkO2xFkA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.9.0.tgz", + "integrity": "sha512-76lDfJnonOcXGW3bEXuqhEGId0LrOlvIE1yLHvK/eKMMPOc0b43KchAIR2Bdbqlg+LPXU5/Q+UzuzkO+cWHT6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "^8.9.0", - "@typescript-eslint/utils": "^8.9.0", - "minimatch": "^9.0.5", - "natural-compare-lite": "^1.4.0" + "@typescript-eslint/types": "^8.24.0", + "@typescript-eslint/utils": "^8.24.0", + "natural-orderby": "^5.0.0" }, "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "astro-eslint-parser": "^1.0.2", - "eslint": ">=8.0.0", - "svelte": ">=3.0.0", - "svelte-eslint-parser": "^0.41.1", - "vue-eslint-parser": ">=9.0.0" - }, - "peerDependenciesMeta": { - "astro-eslint-parser": { - "optional": true - }, - "svelte": { - "optional": true - }, - "svelte-eslint-parser": { - "optional": true - }, - "vue-eslint-parser": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "eslint": ">=8.0.0" } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5495,28 +5259,28 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "56.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.0.tgz", - "integrity": "sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw==", + "version": "57.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz", + "integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^4.0.0", + "@babel/helper-validator-identifier": "^7.25.9", + "@eslint-community/eslint-utils": "^4.4.1", + "ci-info": "^4.1.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "esquery": "^1.6.0", - "globals": "^15.9.0", - "indent-string": "^4.0.0", - "is-builtin-module": "^3.2.1", - "jsesc": "^3.0.2", + "globals": "^15.15.0", + "indent-string": "^5.0.0", + "is-builtin-module": "^4.0.0", + "jsesc": "^3.1.0", "pluralize": "^8.0.0", - "read-pkg-up": "^7.0.1", + "read-package-up": "^11.0.0", "regexp-tree": "^0.1.27", - "regjsparser": "^0.10.0", - "semver": "^7.6.3", - "strip-indent": "^3.0.0" + "regjsparser": "^0.12.0", + "semver": "^7.7.1", + "strip-indent": "^4.0.0" }, "engines": { "node": ">=18.18" @@ -5525,68 +5289,59 @@ "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=8.56.0" + "eslint": ">=9.20.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-unicorn/node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint-plugin-unicorn/node_modules/regjsparser": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", - "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "node_modules/eslint-plugin-unicorn/node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" }, "engines": { - "node": ">=10" + "node": ">=6" } }, "node_modules/eslint-scope": { @@ -5611,38 +5366,8 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "url": "https://opencollective.com/eslint" + } }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", @@ -5660,18 +5385,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5685,16 +5398,30 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5705,6 +5432,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -5737,21 +5465,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5761,31 +5474,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5857,27 +5545,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5890,42 +5557,32 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "prelude-ls": "^1.2.1" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -6014,15 +5671,6 @@ "node": ">=0.8.x" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -6138,16 +5786,17 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -6177,6 +5826,13 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/fast-xml-parser": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", @@ -6228,23 +5884,18 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6290,6 +5941,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/findup-sync": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", @@ -6340,46 +6004,26 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -6439,20 +6083,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -6466,39 +6096,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/fs-mkdirp-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz", @@ -6610,12 +6207,17 @@ } }, "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6640,27 +6242,20 @@ } }, "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "dev": true, "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" + "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6891,9 +6486,9 @@ } }, "node_modules/globals": { - "version": "15.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", - "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", "dev": true, "license": "MIT", "engines": { @@ -6975,12 +6570,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -7042,30 +6631,6 @@ "node": ">=10.13.0" } }, - "node_modules/gulp-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/gulp-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7093,58 +6658,11 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/gulp-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/gulp-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gulp-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/gulp-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } @@ -7189,12 +6707,13 @@ } }, "node_modules/gulp-plugin-extras": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/gulp-plugin-extras/-/gulp-plugin-extras-0.3.0.tgz", - "integrity": "sha512-I/kOBSpo61QsGQZcqozZYEnDseKvpudUafVVWDLYgBFAUJ37kW5R8Sjw9cMYzpGyPUfEYOeoY4p+dkfLqgyJUQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-plugin-extras/-/gulp-plugin-extras-1.1.0.tgz", + "integrity": "sha512-T0AXOEVoKYzLIBlwEZ7LtAx2w4ExIozIoxVeYEVLFbdxI7i0sWvFDq0F8mm47djixDF3vAqDPoyGwh3Sg/PWtQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/vinyl": "^2.0.9", + "@types/vinyl": "^2.0.12", "chalk": "^5.3.0", "easy-transform-stream": "^1.0.1" }, @@ -7205,18 +6724,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gulp-plugin-extras/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/gulp-postcss": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-10.0.0.tgz", @@ -7285,15 +6792,16 @@ } }, "node_modules/gulp-zip": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-6.0.0.tgz", - "integrity": "sha512-fPGvNve2dBoZxGKcviTU7mOa77eQibyhwgGLTxnF+ZCKX8RFaTZKkPbdPnmw0r4TNPRjPCkQB/0VuP+MzgkEYg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-6.1.0.tgz", + "integrity": "sha512-couiqfO4CSM4q3oKnihLhYq5mVmwyXfgLP/0eeM7oVlN+psn45vfvJHcCL3AkPgTi4NojnUFV2IozYqZClIujQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-stream": "^8.0.1", - "gulp-plugin-extras": "^0.3.0", + "get-stream": "^9.0.1", + "gulp-plugin-extras": "^1.1.0", "vinyl": "^3.0.0", - "yazl": "^2.5.1" + "yazl": "^3.3.1" }, "engines": { "node": ">=18" @@ -7340,16 +6848,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -7404,12 +6902,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7424,9 +6916,9 @@ } }, "node_modules/highlight.js": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", - "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -7445,17 +6937,32 @@ "node": ">=0.10.0" } }, + "node_modules/hookified": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.7.0.tgz", + "integrity": "sha512-XQdMjqC1AyeOzfs+17cnIk7Wdfu1hh2JtcyNfBf5u9jHrT3iZUlGHxLTntFBuk5lwkqJ6l3+daeQdHK5yByHVA==", + "dev": true, + "license": "MIT" + }, "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" }, "node_modules/html-tags": { "version": "3.3.1", @@ -7514,14 +7021,15 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { "node": ">= 14" @@ -7596,12 +7104,29 @@ } }, "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/inflight": { @@ -7748,6 +7273,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-builtin-module": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz", + "integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7826,15 +7367,13 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-glob": { @@ -7898,15 +7437,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -7979,6 +7509,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -8096,136 +7639,6 @@ "node": ">=0.10.0" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istextorbinary": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", @@ -8261,23 +7674,23 @@ } }, "node_modules/jasmine": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.4.0.tgz", - "integrity": "sha512-E2u4ylX5tgGYvbynImU6EUBKKrSVB1L72FEPjGh4M55ov1VsxR26RA2JU91L9YSPFgcjo4mCLyKn/QXvEYGBkA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.6.0.tgz", + "integrity": "sha512-6frlW22jhgRjtlp68QY/DDVCUfrYqmSxDBWM13mrBzYQGx1XITfVcJltnY15bk8B5cRfN5IpKvemkDiDTSRCsA==", "dev": true, "license": "MIT", "dependencies": { "glob": "^10.2.2", - "jasmine-core": "~5.4.0" + "jasmine-core": "~5.6.0" }, "bin": { "jasmine": "bin/jasmine.js" } }, "node_modules/jasmine-core": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.4.0.tgz", - "integrity": "sha512-T4fio3W++llLd7LGSGsioriDHgWyhoL6YTu4k37uwJLF7DzOzspz7mNxRoM3cQdLWtL/ebazQpIf/yZGJx/gzg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.6.0.tgz", + "integrity": "sha512-niVlkeYVRwKFpmfWg6suo6H9CrNnydfBLEqefM5UjibYS+UoTjZdmvPJSiuyrRLGnFj1eYRhFd/ch+5hSlsFVA==", "dev": true, "license": "MIT" }, @@ -8572,18 +7985,6 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -8786,9 +8187,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", - "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", + "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", "dev": true, "license": "MIT" }, @@ -8812,6 +8213,20 @@ "node": ">=10.13.0" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/liftoff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.0.tgz", @@ -8913,12 +8328,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -8935,7 +8344,8 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-update": { "version": "5.0.1", @@ -9160,9 +8570,9 @@ } }, "node_modules/mdn-data": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.10.0.tgz", - "integrity": "sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", "dev": true, "license": "CC0-1.0" }, @@ -9186,29 +8596,6 @@ "node": ">=4.3.0 <5.0.0 || >=5.10" } }, - "node_modules/memory-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", - "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", - "dev": true, - "dependencies": { - "readable-stream": "^3.4.0" - } - }, - "node_modules/memory-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -9262,9 +8649,9 @@ } }, "node_modules/metalsmith-html-relative": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/metalsmith-html-relative/-/metalsmith-html-relative-2.0.5.tgz", - "integrity": "sha512-u5oZ19xP898DnIYW2LshjIvgBbCHtcxl3wcf4cG/Na83kkF1Ki+g4eoj7QEIQCddBh18aA6CqO3aYn3CNzgLFg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/metalsmith-html-relative/-/metalsmith-html-relative-2.0.6.tgz", + "integrity": "sha512-TxO9i71pudoNloCXXRglNMgGJAK0Iiyv5rFkLZLfwjX3LCI943VWppo+zEGnQTQTiHF5q4UA1QmlYzItiSi/cA==", "dev": true, "license": "GPL-3.0-or-later", "dependencies": { @@ -9457,19 +8844,7 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/min-indent": { @@ -9516,43 +8891,6 @@ "node": ">= 6" } }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -9560,12 +8898,6 @@ "dev": true, "license": "MIT" }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9596,9 +8928,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -9606,6 +8938,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -9613,24 +8946,21 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "node_modules/natural-orderby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/neo-async": { "version": "2.6.2", @@ -9648,62 +8978,26 @@ "node": ">= 0.4.0" } }, - "node_modules/node-abi": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", - "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", - "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==", - "dev": true - }, - "node_modules/node-api-headers": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-0.0.2.tgz", - "integrity": "sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==", - "dev": true - }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-range": { @@ -9741,15 +9035,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9959,47 +9244,20 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -10222,16 +9480,6 @@ "node": ">=8" } }, - "node_modules/path2d": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz", - "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -10240,9 +9488,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -10326,9 +9574,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -10346,8 +9594,8 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -10380,9 +9628,9 @@ } }, "node_modules/postcss-dir-pseudo-class": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.0.tgz", - "integrity": "sha512-T59BG9lURiXmhcJMyKbyjNAK3KCyEQYEhaz9GAETHXfIy9XbGQeyz+H0zIwRJlrP4KKRPJolNYe3QjQPemMjBA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", + "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", "dev": true, "funding": [ { @@ -10396,7 +9644,7 @@ ], "license": "MIT-0", "dependencies": { - "postcss-selector-parser": "^6.1.0" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": ">=18" @@ -10405,6 +9653,20 @@ "postcss": "^8.4" } }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-discard-comments": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz", @@ -10457,9 +9719,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.0.tgz", - "integrity": "sha512-TCGQOizyqvEkdeTPM+t6NYwJ3EJszYE/8t8ILxw/YoeUvz2rz7aM8XTAmBWh9/DJjfaaabL88fWrsVHSPF2zgA==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", + "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", "dev": true, "funding": [ { @@ -10473,9 +9735,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^2.0.0", - "@csstools/selector-specificity": "^4.0.0", - "postcss-selector-parser": "^6.1.0" + "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": ">=18" @@ -10484,6 +9746,66 @@ "postcss": "^8.4" } }, + "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", + "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", @@ -10538,139 +9860,20 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "dev": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/prebuild-install/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prebuild-install/node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/prebuild-install/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", "dev": true, "license": "MIT", "bin": { @@ -10720,59 +9923,21 @@ "asap": "~2.0.3" } }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -10792,7 +9957,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/prr": { "version": "1.0.1", @@ -10801,9 +9967,9 @@ "dev": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "license": "MIT", "dependencies": { @@ -10830,18 +9996,18 @@ } }, "node_modules/puppeteer": { - "version": "23.3.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.3.1.tgz", - "integrity": "sha512-BxkuJyCv46ZKW8KEHiVMHgHEC89jKK9FffReWjbw1IfBUmNx+6JIZyqOtaJeSwyolTdVqqb5fiPiXflKeH3dKQ==", + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.2.1.tgz", + "integrity": "sha512-Euno62ou0cd0dTkOYTNioSOsFF4VpSnz4ldD38hi9ov9xCNtr8DbhmoJRUx+V9OuPgecueZbKOohRrnrhkbg3Q==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.4.0", - "chromium-bidi": "0.6.5", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.3.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1330662", - "puppeteer-core": "23.3.1", + "devtools-protocol": "0.0.1402036", + "puppeteer-core": "24.2.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -10852,16 +10018,16 @@ } }, "node_modules/puppeteer-core": { - "version": "23.3.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.3.1.tgz", - "integrity": "sha512-m5gTpITEqqpSgAvPUI/Ch9igh5sNJV+BVVbqQMzqirRDVHDCkLGHaydEQZx2NZvSXdwCFrIV///cpSlX/uD0Sg==", + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.2.1.tgz", + "integrity": "sha512-bCypUh3WXzETafv1TCFAjIUnI8BiQ/d+XvEfEXDLcIMm9CAvROqnBmbt79yBjwasoDZsgfXnUmIJU7Y27AalVQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.4.0", - "chromium-bidi": "0.6.5", - "debug": "^4.3.7", - "devtools-protocol": "0.0.1330662", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.3.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1402036", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -10922,141 +10088,62 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, + "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" + "node": ">=18" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -11100,33 +10187,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/redent/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/redent/node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -11367,15 +10427,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -11386,21 +10437,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11482,18 +10518,19 @@ "license": "MIT" }, "node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -11562,12 +10599,16 @@ } }, "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, + "license": "ISC", "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-greatest-satisfied-range": { @@ -11591,12 +10632,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -11656,37 +10691,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -11701,6 +10705,7 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -11713,42 +10718,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -11761,9 +10730,9 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11776,13 +10745,13 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -11790,19 +10759,6 @@ "node": ">= 14" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11907,9 +10863,9 @@ "license": "MIT" }, "node_modules/streamx": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", - "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "dev": true, "license": "MIT", "dependencies": { @@ -11931,59 +10887,30 @@ } }, "node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" @@ -12042,15 +10969,16 @@ } }, "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/strip-ansi-cjs": { @@ -12066,15 +10994,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -12086,15 +11005,19 @@ } }, "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, + "license": "MIT", "dependencies": { - "min-indent": "^1.0.0" + "min-indent": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { @@ -12116,9 +11039,9 @@ "dev": true }, "node_modules/stylelint": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.10.0.tgz", - "integrity": "sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.1.tgz", + "integrity": "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw==", "dev": true, "funding": [ { @@ -12132,43 +11055,43 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1", - "@csstools/media-query-list-parser": "^3.0.1", - "@csstools/selector-specificity": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", - "css-tree": "^3.0.0", + "css-tree": "^3.1.0", "debug": "^4.3.7", - "fast-glob": "^3.3.2", + "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.1.0", + "file-entry-cache": "^10.0.5", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^6.0.2", + "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.34.0", + "known-css-properties": "^0.35.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", - "picocolors": "^1.0.1", - "postcss": "^8.4.47", + "picocolors": "^1.1.1", + "postcss": "^8.5.1", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", - "postcss-selector-parser": "^6.1.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "supports-hyperlinks": "^3.1.0", "svg-tags": "^1.0.0", - "table": "^6.8.2", + "table": "^6.9.0", "write-file-atomic": "^5.0.1" }, "bin": { @@ -12179,9 +11102,9 @@ } }, "node_modules/stylelint-prettier": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-5.0.2.tgz", - "integrity": "sha512-qJ+BN+1T2ZcKz9WIrv0x+eFGHzSUnXfXd5gL///T6XoJvr3D8/ztzz2fhtmXef7Vb8P33zBXmLTTveByr0nwBw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-5.0.3.tgz", + "integrity": "sha512-B6V0oa35ekRrKZlf+6+jA+i50C4GXJ7X1PPmoCqSUoXN6BrNF6NhqqhanvkLjqw2qgvrS0wjdpeC+Tn06KN3jw==", "dev": true, "license": "MIT", "dependencies": { @@ -12195,6 +11118,29 @@ "stylelint": ">=16.0.0" } }, + "node_modules/stylelint/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, "node_modules/stylelint/node_modules/balanced-match": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", @@ -12202,30 +11148,25 @@ "dev": true }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", - "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.5.tgz", + "integrity": "sha512-umpQsJrBNsdMDgreSryMEXvJh66XeLtZUwA8Gj7rHGearGufUFv6rB/bcXRFsiGWw/VeSUgUofF4Rf2UKEOrTA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" + "flat-cache": "^6.1.5" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.5.tgz", + "integrity": "sha512-QR+2kN38f8nMfiIQ1LHYjuDEmZNZVjxuxY+HufbS3BW0EX01Q5OnH7iduOYRutmgiXb797HAKcXUeXrvRjjgSQ==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" + "cacheable": "^1.8.7", + "flatted": "^3.3.2", + "hookified": "^1.6.0" } }, "node_modules/stylelint/node_modules/global-modules": { @@ -12255,24 +11196,15 @@ } }, "node_modules/stylelint/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/stylelint/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/stylelint/node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -12291,46 +11223,25 @@ "node": ">=0.10.0" } }, - "node_modules/stylelint/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/stylelint/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/stylelint/node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } @@ -12341,19 +11252,6 @@ "integrity": "sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==", "dev": true }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-hyperlinks": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", @@ -12432,10 +11330,11 @@ "dev": true }, "node_modules/svglint": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/svglint/-/svglint-3.0.0.tgz", - "integrity": "sha512-WX1gta0wz0dH35J5Dq3CP4Mz2jrrxrSqmEyehtraZaw4L4KRKAcTADaHxmj4Z+qYB6DP3IbDnaKNaQDFrpvVvA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/svglint/-/svglint-3.1.0.tgz", + "integrity": "sha512-5poEBAiB1VpswI2YLS2tx50/KfbcYc3AWWjsXBQJxcl6nkx632+LvN3biopFJbPGU1qrSnPQ22Iq42pFzbvBZw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1", "chalk": "^5.0.0", @@ -12466,18 +11365,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/svglint/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/svglint/node_modules/dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -12726,18 +11613,6 @@ "node": ">= 6" } }, - "node_modules/svglint/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/svglint/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -12792,10 +11667,11 @@ } }, "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -12808,70 +11684,28 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/table/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/table/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/tapable": { "version": "2.2.1", @@ -12883,27 +11717,10 @@ "node": ">=6" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "dev": true, "license": "MIT", "dependencies": { @@ -12911,8 +11728,8 @@ "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/tar-stream": { @@ -12927,42 +11744,6 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -12973,10 +11754,11 @@ } }, "node_modules/terser": { - "version": "5.27.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.1.tgz", - "integrity": "sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -12991,16 +11773,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -13020,34 +11803,10 @@ "optional": true }, "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "optional": true + } } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/text-decoder": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", @@ -13058,12 +11817,6 @@ "b4a": "^1.6.4" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "node_modules/textextensions": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", @@ -13091,18 +11844,6 @@ "node": ">=0.10.0" } }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -13142,16 +11883,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tsc-alias": { @@ -13323,25 +12064,30 @@ "summary": "^2.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "^5.0.1" + "prelude-ls": "^1.2.1" }, "engines": { - "node": "*" + "node": ">= 0.8.0" } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", + "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typed-array-buffer": { @@ -13429,9 +12175,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -13464,17 +12210,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -13527,15 +12262,22 @@ } }, "node_modules/undici": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", - "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18.17" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -13580,19 +12322,23 @@ "node": ">=4" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -13610,8 +12356,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -13629,19 +12375,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true, - "license": "MIT" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13658,10 +12391,11 @@ } }, "node_modules/validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -13895,19 +12629,19 @@ } }, "node_modules/webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -13919,9 +12653,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -14022,24 +12756,6 @@ "node": ">= 0.10" } }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -14111,15 +12827,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -14155,148 +12862,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-fn": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", @@ -14339,9 +12904,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { @@ -14417,15 +12982,6 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -14440,41 +12996,6 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -14487,12 +13008,23 @@ } }, "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3" + "buffer-crc32": "^1.0.0" + } + }, + "node_modules/yazl/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" } }, "node_modules/yocto-queue": { @@ -14508,9 +13040,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index c0e2ebdcef65b..90f999ce15b91 100644 --- a/package.json +++ b/package.json @@ -2,58 +2,58 @@ "name": "pdf.js", "type": "module", "devDependencies": { - "@babel/core": "^7.25.8", - "@babel/preset-env": "^7.25.8", - "@babel/runtime": "^7.25.7", + "@babel/core": "^7.26.9", + "@babel/preset-env": "^7.26.9", + "@babel/runtime": "^7.26.9", "@fluent/bundle": "^0.18.0", "@fluent/dom": "^0.10.0", - "@jazzer.js/core": "^2.1.0", "@metalsmith/layouts": "^2.7.0", "@metalsmith/markdown": "^1.10.0", + "@napi-rs/canvas": "^0.1.67", + "@types/node": "^22.13.5", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", - "caniuse-lite": "^1.0.30001669", - "canvas": "^3.0.0-rc2", - "core-js": "^3.38.1", - "eslint": "^8.57.1", + "caniuse-lite": "^1.0.30001700", + "core-js": "^3.40.0", + "eslint": "^9.21.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jasmine": "^4.2.2", - "eslint-plugin-json": "^3.1.0", + "eslint-plugin-json": "^4.0.1", "eslint-plugin-no-unsanitized": "^4.1.2", - "eslint-plugin-perfectionist": "^3.9.1", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-unicorn": "^56.0.0", + "eslint-plugin-perfectionist": "^4.9.0", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-unicorn": "^57.0.0", + "globals": "^16.0.0", "gulp": "^5.0.0", "gulp-cli": "^3.0.0", "gulp-postcss": "^10.0.0", "gulp-rename": "^2.0.0", "gulp-replace": "^1.1.4", - "gulp-zip": "^6.0.0", - "highlight.js": "^11.10.0", - "jasmine": "^5.4.0", + "gulp-zip": "^6.1.0", + "highlight.js": "^11.11.1", + "jasmine": "^5.6.0", "jsdoc": "^4.0.4", "jstransformer-nunjucks": "^1.2.0", "metalsmith": "^2.6.3", - "metalsmith-html-relative": "^2.0.5", + "metalsmith-html-relative": "^2.0.6", "ordered-read-streams": "^2.0.0", - "path2d": "^0.2.1", "pngjs": "^7.0.0", - "postcss": "^8.4.47", + "postcss": "^8.5.3", "postcss-dark-theme-class": "^1.3.0", - "postcss-dir-pseudo-class": "^9.0.0", + "postcss-dir-pseudo-class": "^9.0.1", "postcss-discard-comments": "^7.0.3", - "postcss-nesting": "^13.0.0", - "prettier": "^3.3.3", - "puppeteer": "23.3.1", - "stylelint": "^16.10.0", - "stylelint-prettier": "^5.0.2", - "svglint": "^3.0.0", - "terser-webpack-plugin": "^5.3.10", + "postcss-nesting": "^13.0.1", + "prettier": "^3.5.2", + "puppeteer": "^24.2.1", + "stylelint": "^16.14.1", + "stylelint-prettier": "^5.0.3", + "svglint": "^3.1.0", + "terser-webpack-plugin": "^5.3.11", "tsc-alias": "^1.8.10", "ttest": "^4.0.0", - "typescript": "^5.6.3", + "typescript": "^5.7.3", "vinyl": "^3.0.0", - "webpack": "^5.95.0", + "webpack": "^5.98.0", "webpack-stream": "^7.0.0", "yargs": "^17.7.2" }, @@ -62,7 +62,7 @@ "url": "git://github.com/mozilla/pdf.js.git" }, "engines": { - "node": ">=18" + "node": ">=20" }, "license": "Apache-2.0" } diff --git a/pdfjs.config b/pdfjs.config index bbb92ef22dcfe..9a093503d4c54 100644 --- a/pdfjs.config +++ b/pdfjs.config @@ -1,5 +1,5 @@ { - "stableVersion": "4.7.76", - "baseVersion": "2a4630f89a2ff7b8e2197ff532124555639a156b", - "versionPrefix": "4.8." + "stableVersion": "4.10.38", + "baseVersion": "7a57af12e13a47927c460e6b739a6ca132e7603d", + "versionPrefix": "5.0." } diff --git a/src/core/annotation.js b/src/core/annotation.js index fe48f13872d52..9ef394a31850d 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -42,6 +42,7 @@ import { collectActions, escapeString, getInheritableProperty, + getParentToUpdate, getRotationMatrix, isNumberArray, lookupMatrix, @@ -68,7 +69,6 @@ import { FileSpec } from "./file_spec.js"; import { JpegStream } from "./jpeg_stream.js"; import { ObjectLoader } from "./object_loader.js"; import { OperatorList } from "./operator_list.js"; -import { writeObject } from "./writer.js"; import { XFAFactory } from "./xfa/factory.js"; class AnnotationFactory { @@ -83,18 +83,24 @@ class AnnotationFactory { // Only necessary to prevent the `Catalog.attachments`-getter, used // with "GoToE" actions, from throwing and thus breaking parsing: pdfManager.ensureCatalog("attachments"), + pdfManager.ensureCatalog("globalColorSpaceCache"), ]).then( - // eslint-disable-next-line arrow-body-style - ([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => { - return { - pdfManager, - acroForm: acroForm instanceof Dict ? acroForm : Dict.empty, - xfaDatasets, - structTreeRoot, - baseUrl, - attachments, - }; - }, + ([ + acroForm, + xfaDatasets, + structTreeRoot, + baseUrl, + attachments, + globalColorSpaceCache, + ]) => ({ + pdfManager, + acroForm: acroForm instanceof Dict ? acroForm : Dict.empty, + xfaDatasets, + structTreeRoot, + baseUrl, + attachments, + globalColorSpaceCache, + }), reason => { warn(`createGlobals: "${reason}".`); return null; @@ -332,10 +338,15 @@ class AnnotationFactory { return imagePromises; } - static async saveNewAnnotations(evaluator, task, annotations, imagePromises) { + static async saveNewAnnotations( + evaluator, + task, + annotations, + imagePromises, + changes + ) { const xref = evaluator.xref; let baseFontRef; - const dependencies = []; const promises = []; const { isOffscreenCanvasSupported } = evaluator.options; @@ -351,38 +362,33 @@ class AnnotationFactory { baseFont.set("Type", Name.get("Font")); baseFont.set("Subtype", Name.get("Type1")); baseFont.set("Encoding", Name.get("WinAnsiEncoding")); - const buffer = []; baseFontRef = xref.getNewTemporaryRef(); - await writeObject(baseFontRef, baseFont, buffer, xref); - dependencies.push({ ref: baseFontRef, data: buffer.join("") }); + changes.put(baseFontRef, { + data: baseFont, + }); } promises.push( - FreeTextAnnotation.createNewAnnotation( - xref, - annotation, - dependencies, - { evaluator, task, baseFontRef } - ) + FreeTextAnnotation.createNewAnnotation(xref, annotation, changes, { + evaluator, + task, + baseFontRef, + }) ); break; case AnnotationEditorType.HIGHLIGHT: if (annotation.quadPoints) { promises.push( - HighlightAnnotation.createNewAnnotation( - xref, - annotation, - dependencies - ) + HighlightAnnotation.createNewAnnotation(xref, annotation, changes) ); } else { promises.push( - InkAnnotation.createNewAnnotation(xref, annotation, dependencies) + InkAnnotation.createNewAnnotation(xref, annotation, changes) ); } break; case AnnotationEditorType.INK: promises.push( - InkAnnotation.createNewAnnotation(xref, annotation, dependencies) + InkAnnotation.createNewAnnotation(xref, annotation, changes) ); break; case AnnotationEditorType.STAMP: @@ -391,26 +397,28 @@ class AnnotationFactory { : null; if (image?.imageStream) { const { imageStream, smaskStream } = image; - const buffer = []; if (smaskStream) { const smaskRef = xref.getNewTemporaryRef(); - await writeObject(smaskRef, smaskStream, buffer, xref); - dependencies.push({ ref: smaskRef, data: buffer.join("") }); + changes.put(smaskRef, { + data: smaskStream, + }); imageStream.dict.set("SMask", smaskRef); - buffer.length = 0; } const imageRef = (image.imageRef = xref.getNewTemporaryRef()); - await writeObject(imageRef, imageStream, buffer, xref); - dependencies.push({ ref: imageRef, data: buffer.join("") }); + changes.put(imageRef, { + data: imageStream, + }); image.imageStream = image.smaskStream = null; } promises.push( - StampAnnotation.createNewAnnotation( - xref, - annotation, - dependencies, - { image } - ) + StampAnnotation.createNewAnnotation(xref, annotation, changes, { + image, + }) + ); + break; + case AnnotationEditorType.SIGNATURE: + promises.push( + StampAnnotation.createNewAnnotation(xref, annotation, changes, {}) ); break; } @@ -418,7 +426,6 @@ class AnnotationFactory { return { annotations: await Promise.all(promises), - dependencies, }; } @@ -515,6 +522,18 @@ class AnnotationFactory { ) ); break; + case AnnotationEditorType.SIGNATURE: + promises.push( + StampAnnotation.createNewPrintAnnotation( + annotationGlobals, + xref, + annotation, + { + evaluatorOptions: options, + } + ) + ); + break; } } @@ -1145,9 +1164,7 @@ class Annotation { } const objectLoader = new ObjectLoader(resources, keys, resources.xref); - return objectLoader.load().then(function () { - return resources; - }); + return objectLoader.load().then(() => resources); }); } @@ -1157,7 +1174,7 @@ class Annotation { const isUsingOwnCanvas = !!( hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY ); - if (isUsingOwnCanvas && (rect[0] === rect[2] || rect[1] === rect[3])) { + if (isUsingOwnCanvas && (this.width === 0 || this.height === 0)) { // Empty annotation, don't draw anything. this.data.hasOwnCanvas = false; return { @@ -1227,7 +1244,7 @@ class Annotation { return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas }; } - async save(evaluator, task, annotationStorage) { + async save(evaluator, task, annotationStorage, changes) { return null; } @@ -1415,6 +1432,14 @@ class Annotation { } return fieldName.join("."); } + + get width() { + return this.data.rect[2] - this.data.rect[0]; + } + + get height() { + return this.data.rect[3] - this.data.rect[1]; + } } /** @@ -1690,12 +1715,12 @@ class MarkupAnnotation extends Annotation { buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`); } - let pointsArray = this.data.quadPoints; - if (!pointsArray) { - // If there are no quadpoints, the rectangle should be used instead. - // Convert the rectangle definition to a points array similar to how the - // quadpoints are defined. - pointsArray = Float32Array.from([ + // If there are no quadpoints, the rectangle should be used instead. + // Convert the rectangle definition to a points array similar to how the + // quadpoints are defined. + const pointsArray = + this.data.quadPoints || + Float32Array.from([ this.rectangle[0], this.rectangle[3], this.rectangle[2], @@ -1705,7 +1730,6 @@ class MarkupAnnotation extends Annotation { this.rectangle[2], this.rectangle[1], ]); - } for (let i = 0, ii = pointsArray.length; i < ii; i += 8) { const [mX, MX, mY, MY] = pointsCallback( @@ -1758,14 +1782,10 @@ class MarkupAnnotation extends Annotation { this._streams.push(this.appearance, appearanceStream); } - static async createNewAnnotation(xref, annotation, dependencies, params) { - if (!annotation.ref) { - annotation.ref = xref.getNewTemporaryRef(); - } + static async createNewAnnotation(xref, annotation, changes, params) { + const annotationRef = (annotation.ref ||= xref.getNewTemporaryRef()); - const annotationRef = annotation.ref; const ap = await this.createNewAppearanceStream(annotation, xref, params); - const buffer = []; let annotationDict; if (ap) { @@ -1773,8 +1793,9 @@ class MarkupAnnotation extends Annotation { annotationDict = this.createNewDict(annotation, xref, { apRef, }); - await writeObject(apRef, ap, buffer, xref); - dependencies.push({ ref: apRef, data: buffer.join("") }); + changes.put(apRef, { + data: ap, + }); } else { annotationDict = this.createNewDict(annotation, xref, {}); } @@ -1782,10 +1803,11 @@ class MarkupAnnotation extends Annotation { annotationDict.set("StructParent", annotation.parentTreeId); } - buffer.length = 0; - await writeObject(annotationRef, annotationDict, buffer, xref); + changes.put(annotationRef, { + data: annotationDict, + }); - return { ref: annotationRef, data: buffer.join("") }; + return { ref: annotationRef }; } static async createNewPrintAnnotation( @@ -1895,6 +1917,7 @@ class WidgetAnnotation extends Annotation { data.fieldFlags = 0; } + data.password = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD); data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY); data.required = this.hasFieldFlag(AnnotationFieldFlag.REQUIRED); data.hidden = @@ -1966,14 +1989,9 @@ class WidgetAnnotation extends Annotation { rotation = this.rotation; } - if (rotation === 0) { - return IDENTITY_MATRIX; - } - - const width = this.data.rect[2] - this.data.rect[0]; - const height = this.data.rect[3] - this.data.rect[1]; - - return getRotationMatrix(rotation, width, height); + return rotation === 0 + ? IDENTITY_MATRIX + : getRotationMatrix(rotation, this.width, this.height); } getBorderAndBackgroundAppearances(annotationStorage) { @@ -1985,12 +2003,10 @@ class WidgetAnnotation extends Annotation { if (!this.backgroundColor && !this.borderColor) { return ""; } - const width = this.data.rect[2] - this.data.rect[0]; - const height = this.data.rect[3] - this.data.rect[1]; const rect = rotation === 0 || rotation === 180 - ? `0 0 ${width} ${height} re` - : `0 0 ${height} ${width} re`; + ? `0 0 ${this.width} ${this.height} re` + : `0 0 ${this.height} ${this.width} re`; let str = ""; if (this.backgroundColor) { @@ -2054,12 +2070,7 @@ class WidgetAnnotation extends Annotation { ); const matrix = [1, 0, 0, 1, 0, 0]; - const bbox = [ - 0, - 0, - this.data.rect[2] - this.data.rect[0], - this.data.rect[3] - this.data.rect[1], - ]; + const bbox = [0, 0, this.width, this.height]; const transform = getTransformMatrix(this.data.rect, bbox, matrix); let optionalContent; @@ -2112,7 +2123,25 @@ class WidgetAnnotation extends Annotation { amendSavedDict(annotationStorage, dict) {} - async save(evaluator, task, annotationStorage) { + setValue(dict, value, xref, changes) { + const { dict: parentDict, ref: parentRef } = getParentToUpdate( + dict, + this.ref, + xref + ); + if (!parentDict) { + dict.set("V", value); + } else if (!changes.has(parentRef)) { + const newParentDict = parentDict.clone(); + newParentDict.set("V", value); + changes.put(parentRef, { data: newParentDict }); + return newParentDict; + } + + return null; + } + + async save(evaluator, task, annotationStorage, changes) { const storageEntry = annotationStorage?.get(this.data.id); const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); let value = storageEntry?.value, @@ -2123,7 +2152,7 @@ class WidgetAnnotation extends Annotation { rotation === undefined && flags === undefined ) { - return null; + return; } value ||= this.data.fieldValue; } @@ -2137,7 +2166,7 @@ class WidgetAnnotation extends Annotation { isArrayEqual(value, this.data.fieldValue) && flags === undefined ) { - return null; + return; } if (rotation === undefined) { @@ -2154,7 +2183,7 @@ class WidgetAnnotation extends Annotation { ); if (appearance === null && flags === undefined) { // Appearance didn't change. - return null; + return; } } else { // No need to create an appearance: the pdf has the flag /NeedAppearances @@ -2171,7 +2200,7 @@ class WidgetAnnotation extends Annotation { const originalDict = xref.fetchIfRef(this.ref); if (!(originalDict instanceof Dict)) { - return null; + return; } const dict = new Dict(xref); @@ -2195,25 +2224,26 @@ class WidgetAnnotation extends Annotation { value, }; - dict.set( - "V", + const newParentDict = this.setValue( + dict, Array.isArray(value) ? value.map(stringToAsciiOrUTF16BE) - : stringToAsciiOrUTF16BE(value) + : stringToAsciiOrUTF16BE(value), + xref, + changes ); - this.amendSavedDict(annotationStorage, dict); + this.amendSavedDict(annotationStorage, newParentDict || dict); const maybeMK = this._getMKDict(rotation); if (maybeMK) { dict.set("MK", maybeMK); } - const buffer = []; - const changes = [ - // data for the original object - // V field changed + reference for new AP - { ref: this.ref, data: "", xfa, needAppearances }, - ]; + changes.put(this.ref, { + data: dict, + xfa, + needAppearances, + }); if (appearance !== null) { const newRef = xref.getNewTemporaryRef(); const AP = new Dict(xref); @@ -2225,12 +2255,7 @@ class WidgetAnnotation extends Annotation { const appearanceDict = (appearanceStream.dict = new Dict(xref)); appearanceDict.set("Subtype", Name.get("Form")); appearanceDict.set("Resources", resources); - appearanceDict.set("BBox", [ - 0, - 0, - this.data.rect[2] - this.data.rect[0], - this.data.rect[3] - this.data.rect[1], - ]); + appearanceDict.set("BBox", [0, 0, this.width, this.height]); const rotationMatrix = this.getRotationMatrix(annotationStorage); if (rotationMatrix !== IDENTITY_MATRIX) { @@ -2238,31 +2263,18 @@ class WidgetAnnotation extends Annotation { appearanceDict.set("Matrix", rotationMatrix); } - await writeObject(newRef, appearanceStream, buffer, xref); - - changes.push( - // data for the new AP - { - ref: newRef, - data: buffer.join(""), - xfa: null, - needAppearances: false, - } - ); - buffer.length = 0; + changes.put(newRef, { + data: appearanceStream, + xfa: null, + needAppearances: false, + }); } dict.set("M", `D:${getModificationDate()}`); - await writeObject(this.ref, dict, buffer, xref); - - changes[0].data = buffer.join(""); - - return changes; } async _getAppearance(evaluator, task, intent, annotationStorage) { - const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD); - if (isPassword) { + if (this.data.password) { return null; } const storageEntry = annotationStorage?.get(this.data.id); @@ -2343,8 +2355,7 @@ class WidgetAnnotation extends Annotation { const defaultPadding = 1; const defaultHPadding = 2; - let totalHeight = this.data.rect[3] - this.data.rect[1]; - let totalWidth = this.data.rect[2] - this.data.rect[0]; + let { width: totalWidth, height: totalHeight } = this; if (rotation === 90 || rotation === 270) { [totalWidth, totalHeight] = [totalHeight, totalWidth]; @@ -2774,8 +2785,8 @@ class TextWidgetAnnotation extends WidgetAnnotation { this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE); this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) && - !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) && - !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) && + !this.data.multiLine && + !this.data.password && !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) && this.data.maxLen !== 0; this.data.doNotScroll = this.hasFieldFlag(AnnotationFieldFlag.DONOTSCROLL); @@ -2962,7 +2973,7 @@ class TextWidgetAnnotation extends WidgetAnnotation { value: this.data.fieldValue, defaultValue: this.data.defaultFieldValue || "", multiline: this.data.multiLine, - password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD), + password: this.data.password, charLimit: this.data.maxLen, comb: this.data.comb, editable: !this.data.readOnly, @@ -2986,13 +2997,12 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { this.checkedAppearance = null; this.uncheckedAppearance = null; - this.data.checkBox = - !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && - !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); - this.data.radioButton = - this.hasFieldFlag(AnnotationFieldFlag.RADIO) && - !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); - this.data.pushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + const isRadio = this.hasFieldFlag(AnnotationFieldFlag.RADIO), + isPushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); + + this.data.checkBox = !isRadio && !isPushButton; + this.data.radioButton = isRadio && !isPushButton; + this.data.pushButton = isPushButton; this.data.isTooltipOnly = false; if (this.data.checkBox) { @@ -3078,22 +3088,20 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { }; } - async save(evaluator, task, annotationStorage) { + async save(evaluator, task, annotationStorage, changes) { if (this.data.checkBox) { - return this._saveCheckbox(evaluator, task, annotationStorage); + this._saveCheckbox(evaluator, task, annotationStorage, changes); + return; } if (this.data.radioButton) { - return this._saveRadioButton(evaluator, task, annotationStorage); + this._saveRadioButton(evaluator, task, annotationStorage, changes); } - - // Nothing to save - return null; } - async _saveCheckbox(evaluator, task, annotationStorage) { + async _saveCheckbox(evaluator, task, annotationStorage, changes) { if (!annotationStorage) { - return null; + return; } const storageEntry = annotationStorage.get(this.data.id); const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); @@ -3102,18 +3110,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { if (rotation === undefined && flags === undefined) { if (value === undefined) { - return null; + return; } const defaultValue = this.data.fieldValue === this.data.exportValue; if (defaultValue === value) { - return null; + return; } } let dict = evaluator.xref.fetchIfRef(this.ref); if (!(dict instanceof Dict)) { - return null; + return; } dict = dict.clone(); @@ -3130,7 +3138,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { }; const name = Name.get(value ? this.data.exportValue : "Off"); - dict.set("V", name); + this.setValue(dict, name, evaluator.xref, changes); + dict.set("AS", name); dict.set("M", `D:${getModificationDate()}`); if (flags !== undefined) { @@ -3142,15 +3151,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { dict.set("MK", maybeMK); } - const buffer = []; - await writeObject(this.ref, dict, buffer, evaluator.xref); - - return [{ ref: this.ref, data: buffer.join(""), xfa }]; + changes.put(this.ref, { + data: dict, + xfa, + needAppearances: false, + }); } - async _saveRadioButton(evaluator, task, annotationStorage) { + async _saveRadioButton(evaluator, task, annotationStorage, changes) { if (!annotationStorage) { - return null; + return; } const storageEntry = annotationStorage.get(this.data.id); const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint); @@ -3159,18 +3169,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { if (rotation === undefined && flags === undefined) { if (value === undefined) { - return null; + return; } const defaultValue = this.data.fieldValue === this.data.buttonValue; if (defaultValue === value) { - return null; + return; } } let dict = evaluator.xref.fetchIfRef(this.ref); if (!(dict instanceof Dict)) { - return null; + return; } dict = dict.clone(); @@ -3188,24 +3198,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { }; const name = Name.get(value ? this.data.buttonValue : "Off"); - const buffer = []; - let parentData = null; - if (value) { - if (this.parent instanceof Ref) { - const parent = evaluator.xref.fetch(this.parent); - parent.set("V", name); - await writeObject(this.parent, parent, buffer, evaluator.xref); - parentData = buffer.join(""); - buffer.length = 0; - } else if (this.parent instanceof Dict) { - this.parent.set("V", name); - } - } - - if (!this.parent) { - // If there is no parent then we must set the value in the field. - dict.set("V", name); + this.setValue(dict, name, evaluator.xref, changes); } dict.set("AS", name); @@ -3219,18 +3213,15 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { dict.set("MK", maybeMK); } - await writeObject(this.ref, dict, buffer, evaluator.xref); - const newRefs = [{ ref: this.ref, data: buffer.join(""), xfa }]; - if (parentData) { - newRefs.push({ ref: this.parent, data: parentData, xfa: null }); - } - - return newRefs; + changes.put(this.ref, { + data: dict, + xfa, + needAppearances: false, + }); } _getDefaultCheckedAppearance(params, type) { - const width = this.data.rect[2] - this.data.rect[0]; - const height = this.data.rect[3] - this.data.rect[1]; + const { width, height } = this; const bbox = [0, 0, width, height]; // Ratio used to have a mark slightly smaller than the bbox. @@ -3525,6 +3516,18 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation { } } + // It's a workaround for the issue #19083. + // Normally a choice widget is a mix of a text field and a listbox, + // So in the case where the V entry isn't an option we should just set it + // as the text field value. + if (this.data.options.length === 0 && this.data.fieldValue.length > 0) { + // If there are no options, then the field value is the only option. + this.data.options = this.data.fieldValue.map(value => ({ + exportValue: value, + displayValue: value, + })); + } + // Process field flags for the display layer. this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO); this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT); @@ -3603,8 +3606,7 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation { const defaultPadding = 1; const defaultHPadding = 2; - let totalHeight = this.data.rect[3] - this.data.rect[1]; - let totalWidth = this.data.rect[2] - this.data.rect[0]; + let { width: totalWidth, height: totalHeight } = this; if (rotation === 90 || rotation === 270) { [totalWidth, totalHeight] = [totalHeight, totalWidth]; @@ -3816,10 +3818,7 @@ class PopupAnnotation extends Annotation { // version. this.data.noHTML = false; - if ( - this.data.rect[0] === this.data.rect[2] || - this.data.rect[1] === this.data.rect[3] - ) { + if (this.width === 0 || this.height === 0) { this.data.rect = null; } @@ -3888,7 +3887,7 @@ class FreeTextAnnotation extends MarkupAnnotation { // We want to be able to add mouse listeners to the annotation. this.data.noHTML = false; - const { evaluatorOptions, xref } = params; + const { annotationGlobals, evaluatorOptions, xref } = params; this.data.annotationType = AnnotationType.FREETEXT; this.setDefaultAppearance(params); this._hasAppearance = !!this.appearance; @@ -3897,7 +3896,8 @@ class FreeTextAnnotation extends MarkupAnnotation { const { fontColor, fontSize } = parseAppearanceStream( this.appearance, evaluatorOptions, - xref + xref, + annotationGlobals.globalColorSpaceCache ); this.data.defaultAppearanceData.fontColor = fontColor; this.data.defaultAppearanceData.fontSize = fontSize || 10; @@ -4384,7 +4384,7 @@ class InkAnnotation extends MarkupAnnotation { const { dict, xref } = params; this.data.annotationType = AnnotationType.INK; this.data.inkLists = []; - this.data.isEditable = !this.data.noHTML && this.data.it === "InkHighlight"; + this.data.isEditable = !this.data.noHTML; // We want to be able to add mouse listeners to the annotation. this.data.noHTML = false; this.data.opacity = dict.get("CA") || 1; @@ -4461,17 +4461,30 @@ class InkAnnotation extends MarkupAnnotation { } static createNewDict(annotation, xref, { apRef, ap }) { - const { color, opacity, paths, outlines, rect, rotation, thickness } = - annotation; - const ink = new Dict(xref); + const { + oldAnnotation, + color, + opacity, + paths, + outlines, + rect, + rotation, + thickness, + user, + } = annotation; + const ink = oldAnnotation || new Dict(xref); ink.set("Type", Name.get("Annot")); ink.set("Subtype", Name.get("Ink")); - ink.set("CreationDate", `D:${getModificationDate()}`); + ink.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`); ink.set("Rect", rect); - ink.set("InkList", outlines?.points || paths.map(p => p.points)); + ink.set("InkList", outlines?.points || paths.points); ink.set("F", 4); ink.set("Rotate", rotation); + if (user) { + ink.set("T", stringToAsciiOrUTF16BE(user)); + } + if (outlines) { // Free highlight. // There's nothing about this in the spec, but it's used when highlighting @@ -4486,10 +4499,7 @@ class InkAnnotation extends MarkupAnnotation { bs.set("W", thickness); // Color. - ink.set( - "C", - Array.from(color, c => c / 255) - ); + ink.set("C", getPdfColorArray(color)); // Opacity. ink.set("CA", opacity); @@ -4525,28 +4535,32 @@ class InkAnnotation extends MarkupAnnotation { appearanceBuffer.push("/R0 gs"); } - const buffer = []; - for (const { bezier } of paths) { - buffer.length = 0; - buffer.push( - `${numberToString(bezier[0])} ${numberToString(bezier[1])} m` + for (const outline of paths.lines) { + appearanceBuffer.push( + `${numberToString(outline[4])} ${numberToString(outline[5])} m` ); - if (bezier.length === 2) { - buffer.push( - `${numberToString(bezier[0])} ${numberToString(bezier[1])} l S` - ); - } else { - for (let i = 2, ii = bezier.length; i < ii; i += 6) { - const curve = bezier - .slice(i, i + 6) - .map(numberToString) - .join(" "); - buffer.push(`${curve} c`); + for (let i = 6, ii = outline.length; i < ii; i += 6) { + if (isNaN(outline[i])) { + appearanceBuffer.push( + `${numberToString(outline[i + 4])} ${numberToString( + outline[i + 5] + )} l` + ); + } else { + const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6); + appearanceBuffer.push( + [c1x, c1y, c2x, c2y, x, y].map(numberToString).join(" ") + " c" + ); } - buffer.push("S"); } - appearanceBuffer.push(buffer.join("\n")); + if (outline.length === 6) { + appearanceBuffer.push( + `${numberToString(outline[4])} ${numberToString(outline[5])} l` + ); + } } + appearanceBuffer.push("S"); + const appearance = appearanceBuffer.join("\n"); const appearanceStreamDict = new Dict(xref); @@ -4589,18 +4603,17 @@ class InkAnnotation extends MarkupAnnotation { `${numberToString(outline[4])} ${numberToString(outline[5])} m` ); for (let i = 6, ii = outline.length; i < ii; i += 6) { - if (isNaN(outline[i]) || outline[i] === null) { + if (isNaN(outline[i])) { appearanceBuffer.push( `${numberToString(outline[i + 4])} ${numberToString( outline[i + 5] )} l` ); } else { - const curve = outline - .slice(i, i + 6) - .map(numberToString) - .join(" "); - appearanceBuffer.push(`${curve} c`); + const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6); + appearanceBuffer.push( + [c1x, c1y, c2x, c2y, x, y].map(numberToString).join(" ") + " c" + ); } } appearanceBuffer.push("h f"); @@ -4700,10 +4713,7 @@ class HighlightAnnotation extends MarkupAnnotation { highlight.set("QuadPoints", quadPoints); // Color. - highlight.set( - "C", - Array.from(color, c => c / 255) - ); + highlight.set("C", getPdfColorArray(color)); // Opacity. highlight.set("CA", opacity); @@ -5008,7 +5018,6 @@ class StampAnnotation extends MarkupAnnotation { oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}` ); - stamp.set("CreationDate", `D:${getModificationDate()}`); stamp.set("Rect", rect); stamp.set("F", 4); stamp.set("Border", [0, 0, 0]); @@ -5032,11 +5041,61 @@ class StampAnnotation extends MarkupAnnotation { return stamp; } + static async #createNewAppearanceStreamForDrawing(annotation, xref) { + const { areContours, color, rect, lines, thickness } = annotation; + + const appearanceBuffer = [ + `${thickness} w 1 J 1 j`, + `${getPdfColor(color, /* isFill */ areContours)}`, + ]; + + for (const line of lines) { + appearanceBuffer.push( + `${numberToString(line[4])} ${numberToString(line[5])} m` + ); + for (let i = 6, ii = line.length; i < ii; i += 6) { + if (isNaN(line[i])) { + appearanceBuffer.push( + `${numberToString(line[i + 4])} ${numberToString(line[i + 5])} l` + ); + } else { + const [c1x, c1y, c2x, c2y, x, y] = line.slice(i, i + 6); + appearanceBuffer.push( + [c1x, c1y, c2x, c2y, x, y].map(numberToString).join(" ") + " c" + ); + } + } + if (line.length === 6) { + appearanceBuffer.push( + `${numberToString(line[4])} ${numberToString(line[5])} l` + ); + } + } + appearanceBuffer.push(areContours ? "F" : "S"); + + const appearance = appearanceBuffer.join("\n"); + + const appearanceStreamDict = new Dict(xref); + appearanceStreamDict.set("FormType", 1); + appearanceStreamDict.set("Subtype", Name.get("Form")); + appearanceStreamDict.set("Type", Name.get("XObject")); + appearanceStreamDict.set("BBox", rect); + appearanceStreamDict.set("Length", appearance.length); + + const ap = new StringStream(appearance); + ap.dict = appearanceStreamDict; + + return ap; + } + static async createNewAppearanceStream(annotation, xref, params) { if (annotation.oldAnnotation) { // We'll use the AP we already have. return null; } + if (annotation.isSignature) { + return this.#createNewAppearanceStreamForDrawing(annotation, xref); + } const { rotation } = annotation; const { imageRef, width, height } = params.image; diff --git a/src/core/base_stream.js b/src/core/base_stream.js index febb4ed109c15..0266449a1bc4e 100644 --- a/src/core/base_stream.js +++ b/src/core/base_stream.js @@ -64,6 +64,10 @@ class BaseStream { return false; } + get isAsyncDecoder() { + return false; + } + get canAsyncDecodeImageFromBuffer() { return false; } diff --git a/src/core/catalog.js b/src/core/catalog.js index 8f09309b95ef2..5e800cb89d98c 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -14,15 +14,7 @@ */ import { - collectActions, - isNumberArray, - MissingDataException, - PDF_VERSION_REGEXP, - recoverJsURL, - toRomanNumerals, - XRefEntryException, -} from "./core_utils.js"; -import { + _isValidExplicitDest, createValidAbsoluteUrl, DocumentActionEventType, FormatError, @@ -34,6 +26,15 @@ import { stringToUTF8String, warn, } from "../shared/util.js"; +import { + collectActions, + isNumberArray, + MissingDataException, + PDF_VERSION_REGEXP, + recoverJsURL, + toRomanNumerals, + XRefEntryException, +} from "./core_utils.js"; import { Dict, isDict, @@ -44,61 +45,22 @@ import { RefSet, RefSetCache, } from "./primitives.js"; +import { GlobalColorSpaceCache, GlobalImageCache } from "./image_utils.js"; import { NameTree, NumberTree } from "./name_number_tree.js"; import { BaseStream } from "./base_stream.js"; import { clearGlobalCaches } from "./cleanup_helper.js"; import { ColorSpace } from "./colorspace.js"; import { FileSpec } from "./file_spec.js"; -import { GlobalImageCache } from "./image_utils.js"; import { MetadataParser } from "./metadata_parser.js"; import { StructTreeRoot } from "./struct_tree.js"; -function isValidExplicitDest(dest) { - if (!Array.isArray(dest) || dest.length < 2) { - return false; - } - const [page, zoom, ...args] = dest; - if (!(page instanceof Ref) && !Number.isInteger(page)) { - return false; - } - if (!(zoom instanceof Name)) { - return false; - } - const argsLen = args.length; - let allowNull = true; - switch (zoom.name) { - case "XYZ": - if (argsLen < 2 || argsLen > 3) { - return false; - } - break; - case "Fit": - case "FitB": - return argsLen === 0; - case "FitH": - case "FitBH": - case "FitV": - case "FitBV": - if (argsLen > 1) { - return false; - } - break; - case "FitR": - if (argsLen !== 4) { - return false; - } - allowNull = false; - break; - default: - return false; - } - for (const arg of args) { - if (!(typeof arg === "number" || (allowNull && arg === null))) { - return false; - } - } - return true; -} +const isRef = v => v instanceof Ref; + +const isValidExplicitDest = _isValidExplicitDest.bind( + null, + /* validRef = */ isRef, + /* validName = */ isName +); function fetchDest(dest) { if (dest instanceof Dict) { @@ -140,6 +102,7 @@ class Catalog { this.fontCache = new RefSetCache(); this.builtInCMapCache = new Map(); this.standardFontDataCache = new Map(); + this.globalColorSpaceCache = new GlobalColorSpaceCache(); this.globalImageCache = new GlobalImageCache(); this.pageKidsCountCache = new RefSetCache(); this.pageIndexCache = new RefSetCache(); @@ -567,10 +530,7 @@ class Catalog { const onParsed = []; if (Array.isArray(refs)) { for (const value of refs) { - if (!(value instanceof Ref)) { - continue; - } - if (groupRefCache.has(value)) { + if (value instanceof Ref && groupRefCache.has(value)) { onParsed.push(value.toString()); } } @@ -629,7 +589,7 @@ class Catalog { return null; } const nestedOrder = parseOrder(value.slice(1), nestedLevels); - if (!nestedOrder || !nestedOrder.length) { + if (!nestedOrder?.length) { return null; } return { name: stringToPDFString(nestedName), order: nestedOrder }; @@ -710,61 +670,63 @@ class Catalog { } get destinations() { - const obj = this._readDests(), + const rawDests = this.#readDests(), dests = Object.create(null); - if (obj instanceof NameTree) { - for (const [key, value] of obj.getAll()) { - const dest = fetchDest(value); - if (dest) { - dests[stringToPDFString(key)] = dest; + for (const obj of rawDests) { + if (obj instanceof NameTree) { + for (const [key, value] of obj.getAll()) { + const dest = fetchDest(value); + if (dest) { + dests[stringToPDFString(key)] = dest; + } } - } - } else if (obj instanceof Dict) { - obj.forEach(function (key, value) { - const dest = fetchDest(value); - if (dest) { - dests[key] = dest; + } else if (obj instanceof Dict) { + for (const [key, value] of obj) { + const dest = fetchDest(value); + if (dest) { + // Always let the NameTree take precedence. + dests[key] ||= dest; + } } - }); + } } return shadow(this, "destinations", dests); } getDestination(id) { - const obj = this._readDests(); - if (obj instanceof NameTree) { - const dest = fetchDest(obj.get(id)); - if (dest) { - return dest; + const rawDests = this.#readDests(); + for (const obj of rawDests) { + if (obj instanceof NameTree || obj instanceof Dict) { + const dest = fetchDest(obj.get(id)); + if (dest) { + return dest; + } } + } + + if (rawDests[0] instanceof NameTree) { // Fallback to checking the *entire* NameTree, in an attempt to handle // corrupt PDF documents with out-of-order NameTrees (fixes issue 10272). - const allDest = this.destinations[id]; - if (allDest) { - warn(`Found "${id}" at an incorrect position in the NameTree.`); - return allDest; - } - } else if (obj instanceof Dict) { - const dest = fetchDest(obj.get(id)); + const dest = this.destinations[id]; if (dest) { + warn(`Found "${id}" at an incorrect position in the NameTree.`); return dest; } } return null; } - /** - * @private - */ - _readDests() { + #readDests() { const obj = this._catDict.get("Names"); + const rawDests = []; if (obj?.has("Dests")) { - return new NameTree(obj.getRaw("Dests"), this.xref); - } else if (this._catDict.has("Dests")) { + rawDests.push(new NameTree(obj.getRaw("Dests"), this.xref)); + } + if (this._catDict.has("Dests")) { // Simple destination dictionary. - return this._catDict.get("Dests"); + rawDests.push(this._catDict.get("Dests")); } - return undefined; + return rawDests; } get pageLabels() { @@ -926,8 +888,7 @@ class Catalog { } let prefs = null; - for (const key of obj.getKeys()) { - const value = obj.get(key); + for (const [key, value] of obj) { let prefValue; switch (key) { @@ -1171,28 +1132,16 @@ class Catalog { return shadow(this, "jsActions", actions); } - async fontFallback(id, handler) { - const translatedFonts = await Promise.all(this.fontCache); - - for (const translatedFont of translatedFonts) { - if (translatedFont.loadedName === id) { - translatedFont.fallback(handler); - return; - } - } - } - async cleanup(manuallyTriggered = false) { clearGlobalCaches(); + this.globalColorSpaceCache.clear(); this.globalImageCache.clear(/* onlyData = */ manuallyTriggered); this.pageKidsCountCache.clear(); this.pageIndexCache.clear(); this.pageDictCache.clear(); this.nonBlendModesSet.clear(); - const translatedFonts = await Promise.all(this.fontCache); - - for (const { dict } of translatedFonts) { + for (const { dict } of await Promise.all(this.fontCache)) { delete dict.cacheKey; } this.fontCache.clear(); @@ -1526,9 +1475,7 @@ class Catalog { if (!found) { throw new FormatError("Kid reference not found in parent's kids."); } - return Promise.all(kidPromises).then(function () { - return [total, parentRef]; - }); + return Promise.all(kidPromises).then(() => [total, parentRef]); }); } diff --git a/src/core/ccitt.js b/src/core/ccitt.js index ac204bd8098bc..8a57a5dbe46ed 100644 --- a/src/core/ccitt.js +++ b/src/core/ccitt.js @@ -466,7 +466,7 @@ const blackTable3 = [ */ class CCITTFaxDecoder { constructor(source, options = {}) { - if (!source || typeof source.next !== "function") { + if (typeof source?.next !== "function") { throw new Error('CCITTFaxDecoder - invalid "source" parameter.'); } this.source = source; diff --git a/src/core/cff_parser.js b/src/core/cff_parser.js index 446764f0d6515..cdb66224098fd 100644 --- a/src/core/cff_parser.js +++ b/src/core/cff_parser.js @@ -28,6 +28,7 @@ import { ISOAdobeCharset, } from "./charsets.js"; import { ExpertEncoding, StandardEncoding } from "./encodings.js"; +import { readInt16 } from "./core_utils.js"; // Maximum subroutine call depth of type 2 charstrings. Matches OTS. const MAX_SUBR_NESTING = 10; @@ -359,8 +360,8 @@ class CFFParser { if (value === 30) { return parseFloatOperand(); } else if (value === 28) { - value = dict[pos++]; - value = ((value << 24) | (dict[pos++] << 16)) >> 16; + value = readInt16(dict, pos); + pos += 2; return value; } else if (value === 29) { value = dict[pos++]; @@ -510,7 +511,7 @@ class CFFParser { } } else if (value === 28) { // number (16 bit) - stack[stackSize] = ((data[j] << 24) | (data[j + 1] << 16)) >> 16; + stack[stackSize] = readInt16(data, j); j += 2; stackSize++; } else if (value === 14) { diff --git a/src/core/chunked_stream.js b/src/core/chunked_stream.js index b4ebe5b64c53d..c77f62531ed51 100644 --- a/src/core/chunked_stream.js +++ b/src/core/chunked_stream.js @@ -414,9 +414,7 @@ class ChunkedStreamManager { } } - chunksToRequest.sort(function (a, b) { - return a - b; - }); + chunksToRequest.sort((a, b) => a - b); return this._requestChunks(chunksToRequest); } diff --git a/src/core/cmap.js b/src/core/cmap.js index 5e7040c6d2a4e..6c0cab60cc6ae 100644 --- a/src/core/cmap.js +++ b/src/core/cmap.js @@ -662,7 +662,7 @@ async function extendCMap(cMap, fetchBuiltInCMap, useCMap) { // any previously defined entries. cMap.useCMap.forEach(function (key, value) { if (!cMap.contains(key)) { - cMap.mapOne(key, cMap.useCMap.lookup(key)); + cMap.mapOne(key, value); } }); diff --git a/src/core/colorspace.js b/src/core/colorspace.js index 9f5ce00b76db5..6bd879b619ce4 100644 --- a/src/core/colorspace.js +++ b/src/core/colorspace.js @@ -306,19 +306,20 @@ class ColorSpace { return shadow(this, "usesZeroToOneRange", true); } - /** - * @private - */ - static _cache(cacheKey, xref, localColorSpaceCache, parsedColorSpace) { - if (!localColorSpaceCache) { + static #cache( + cacheKey, + xref, + globalColorSpaceCache, + localColorSpaceCache, + parsedCS + ) { + if (!globalColorSpaceCache || !localColorSpaceCache) { throw new Error( - 'ColorSpace._cache - expected "localColorSpaceCache" argument.' + 'ColorSpace.#cache - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.' ); } - if (!parsedColorSpace) { - throw new Error( - 'ColorSpace._cache - expected "parsedColorSpace" argument.' - ); + if (!parsedCS) { + throw new Error('ColorSpace.#cache - expected "parsedCS" argument.'); } let csName, csRef; if (cacheKey instanceof Ref) { @@ -331,20 +332,31 @@ class ColorSpace { csName = cacheKey.name; } if (csName || csRef) { - localColorSpaceCache.set(csName, csRef, parsedColorSpace); + localColorSpaceCache.set(csName, csRef, parsedCS); + + if (csRef) { + globalColorSpaceCache.set(/* name = */ null, csRef, parsedCS); + } } } - static getCached(cacheKey, xref, localColorSpaceCache) { - if (!localColorSpaceCache) { + static getCached( + cacheKey, + xref, + globalColorSpaceCache, + localColorSpaceCache + ) { + if (!globalColorSpaceCache || !localColorSpaceCache) { throw new Error( - 'ColorSpace.getCached - expected "localColorSpaceCache" argument.' + 'ColorSpace.getCached - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.' ); } if (cacheKey instanceof Ref) { - const localColorSpace = localColorSpaceCache.getByRef(cacheKey); - if (localColorSpace) { - return localColorSpace; + const cachedCS = + globalColorSpaceCache.getByRef(cacheKey) || + localColorSpaceCache.getByRef(cacheKey); + if (cachedCS) { + return cachedCS; } try { @@ -357,10 +369,7 @@ class ColorSpace { } } if (cacheKey instanceof Name) { - const localColorSpace = localColorSpaceCache.getByName(cacheKey.name); - if (localColorSpace) { - return localColorSpace; - } + return localColorSpaceCache.getByName(cacheKey.name) || null; } return null; } @@ -370,26 +379,28 @@ class ColorSpace { xref, resources = null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert( - !this.getCached(cs, xref, localColorSpaceCache), + !this.getCached(cs, xref, globalColorSpaceCache, localColorSpaceCache), "Expected `ColorSpace.getCached` to have been manually checked " + "before calling `ColorSpace.parseAsync`." ); } - const parsedColorSpace = this._parse( + const parsedCS = this.#parse(cs, xref, resources, pdfFunctionFactory); + + // Attempt to cache the parsed ColorSpace, by name and/or reference. + this.#cache( cs, xref, - resources, - pdfFunctionFactory + globalColorSpaceCache, + localColorSpaceCache, + parsedCS ); - // Attempt to cache the parsed ColorSpace, by name and/or reference. - this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); - - return parsedColorSpace; + return parsedCS; } static parse({ @@ -397,29 +408,33 @@ class ColorSpace { xref, resources = null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { - const cachedColorSpace = this.getCached(cs, xref, localColorSpaceCache); - if (cachedColorSpace) { - return cachedColorSpace; - } - const parsedColorSpace = this._parse( + const cachedCS = this.getCached( cs, xref, - resources, - pdfFunctionFactory + globalColorSpaceCache, + localColorSpaceCache ); + if (cachedCS) { + return cachedCS; + } + const parsedCS = this.#parse(cs, xref, resources, pdfFunctionFactory); // Attempt to cache the parsed ColorSpace, by name and/or reference. - this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); + this.#cache( + cs, + xref, + globalColorSpaceCache, + localColorSpaceCache, + parsedCS + ); - return parsedColorSpace; + return parsedCS; } - /** - * @private - */ - static _parse(cs, xref, resources = null, pdfFunctionFactory) { + static #parse(cs, xref, resources = null, pdfFunctionFactory) { cs = xref.fetchIfRef(cs); if (cs instanceof Name) { switch (cs.name) { @@ -443,7 +458,7 @@ class ColorSpace { const resourcesCS = colorSpaces.get(cs.name); if (resourcesCS) { if (resourcesCS instanceof Name) { - return this._parse( + return this.#parse( resourcesCS, xref, resources, @@ -493,7 +508,7 @@ class ColorSpace { numComps = dict.get("N"); const alt = dict.get("Alternate"); if (alt) { - const altCS = this._parse(alt, xref, resources, pdfFunctionFactory); + const altCS = this.#parse(alt, xref, resources, pdfFunctionFactory); // Ensure that the number of components are correct, // and also (indirectly) that it is not a PatternCS. if (altCS.numComps === numComps) { @@ -512,12 +527,12 @@ class ColorSpace { case "Pattern": baseCS = cs[1] || null; if (baseCS) { - baseCS = this._parse(baseCS, xref, resources, pdfFunctionFactory); + baseCS = this.#parse(baseCS, xref, resources, pdfFunctionFactory); } return new PatternCS(baseCS); case "I": case "Indexed": - baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory); + baseCS = this.#parse(cs[1], xref, resources, pdfFunctionFactory); const hiVal = Math.max(0, Math.min(xref.fetchIfRef(cs[2]), 255)); const lookup = xref.fetchIfRef(cs[3]); return new IndexedCS(baseCS, hiVal, lookup); @@ -525,7 +540,7 @@ class ColorSpace { case "DeviceN": const name = xref.fetchIfRef(cs[1]); numComps = Array.isArray(name) ? name.length : 1; - baseCS = this._parse(cs[2], xref, resources, pdfFunctionFactory); + baseCS = this.#parse(cs[2], xref, resources, pdfFunctionFactory); const tintFn = pdfFunctionFactory.create(cs[3]); return new AlternateCS(numComps, baseCS, tintFn); case "Lab": diff --git a/src/core/core_utils.js b/src/core/core_utils.js index 0f81422e8fecb..f099f219effd7 100644 --- a/src/core/core_utils.js +++ b/src/core/core_utils.js @@ -17,6 +17,7 @@ import { AnnotationEditorPrefix, assert, BaseException, + hexNumbers, objectSize, stringToPDFString, Util, @@ -26,6 +27,8 @@ import { Dict, isName, Ref, RefSet } from "./primitives.js"; import { BaseStream } from "./base_stream.js"; const PDF_VERSION_REGEXP = /^[1-9]\.\d$/; +const MAX_INT_32 = 2 ** 31 - 1; +const MIN_INT_32 = -(2 ** 31); function getLookupTableFactory(initializer) { let lookup; @@ -100,6 +103,16 @@ function arrayBuffersToBytes(arr) { return data; } +async function fetchBinaryData(url) { + const response = await fetch(url); + if (!response.ok) { + throw new Error( + `Failed to fetch file "${url}" with "${response.statusText}".` + ); + } + return new Uint8Array(await response.arrayBuffer()); +} + /** * Get the value of an inheritable property. * @@ -145,6 +158,36 @@ function getInheritableProperty({ return values; } +/** + * Get the parent dictionary to update when a property is set. + * + * @param {Dict} dict - Dictionary from where to start the traversal. + * @param {Ref} ref - The reference to the dictionary. + * @param {XRef} xref - The `XRef` instance. + */ +function getParentToUpdate(dict, ref, xref) { + const visited = new RefSet(); + const firstDict = dict; + const result = { dict: null, ref: null }; + + while (dict instanceof Dict && !visited.has(ref)) { + visited.put(ref); + if (dict.has("T")) { + break; + } + ref = dict.getRaw("Parent"); + if (!(ref instanceof Ref)) { + return result; + } + dict = xref.fetch(ref); + } + if (dict instanceof Dict && dict !== firstDict) { + result.dict = dict; + result.ref = ref; + } + return result; +} + // prettier-ignore const ROMAN_NUMBER_MAP = [ "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", @@ -164,42 +207,30 @@ function toRomanNumerals(number, lowerCase = false) { Number.isInteger(number) && number > 0, "The number should be a positive integer." ); - const romanBuf = []; - let pos; - // Thousands - while (number >= 1000) { - number -= 1000; - romanBuf.push("M"); - } - // Hundreds - pos = (number / 100) | 0; - number %= 100; - romanBuf.push(ROMAN_NUMBER_MAP[pos]); - // Tens - pos = (number / 10) | 0; - number %= 10; - romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]); - // Ones - romanBuf.push(ROMAN_NUMBER_MAP[20 + number]); // eslint-disable-line unicorn/no-array-push-push - - const romanStr = romanBuf.join(""); - return lowerCase ? romanStr.toLowerCase() : romanStr; + + const roman = + "M".repeat((number / 1000) | 0) + + ROMAN_NUMBER_MAP[((number % 1000) / 100) | 0] + + ROMAN_NUMBER_MAP[10 + (((number % 100) / 10) | 0)] + + ROMAN_NUMBER_MAP[20 + (number % 10)]; + return lowerCase ? roman.toLowerCase() : roman; } // Calculate the base 2 logarithm of the number `x`. This differs from the // native function in the sense that it returns the ceiling value and that it // returns 0 instead of `Infinity`/`NaN` for `x` values smaller than/equal to 0. function log2(x) { - if (x <= 0) { - return 0; - } - return Math.ceil(Math.log2(x)); + return x > 0 ? Math.ceil(Math.log2(x)) : 0; } function readInt8(data, offset) { return (data[offset] << 24) >> 24; } +function readInt16(data, offset) { + return ((data[offset] << 24) | (data[offset + 1] << 16)) >> 16; +} + function readUint16(data, offset) { return (data[offset] << 8) | data[offset + 1]; } @@ -253,7 +284,7 @@ function isNumberArray(arr, len) { // BigInt64Array/BigUint64Array types (their elements aren't "number"). return ( ArrayBuffer.isView(arr) && - (arr.length === 0 || typeof arr[0] === "number") && + !(arr instanceof BigInt64Array || arr instanceof BigUint64Array) && (len === null || arr.length === len) ); } @@ -572,13 +603,10 @@ function recoverJsURL(str) { const jsUrl = regex.exec(str); if (jsUrl?.[2]) { - const url = jsUrl[2]; - let newWindow = false; - - if (jsUrl[3] === "true" && jsUrl[1] === "app.launchURL") { - newWindow = true; - } - return { url, newWindow }; + return { + url: jsUrl[2], + newWindow: jsUrl[1] === "app.launchURL" && jsUrl[3] === "true", + }; } return null; @@ -634,10 +662,7 @@ function stringToUTF16HexString(str) { const buf = []; for (let i = 0, ii = str.length; i < ii; i++) { const char = str.charCodeAt(i); - buf.push( - ((char >> 8) & 0xff).toString(16).padStart(2, "0"), - (char & 0xff).toString(16).padStart(2, "0") - ); + buf.push(hexNumbers[(char >> 8) & 0xff], hexNumbers[char & 0xff]); } return buf.join(""); } @@ -690,9 +715,11 @@ export { encodeToXmlString, escapePDFName, escapeString, + fetchBinaryData, getInheritableProperty, getLookupTableFactory, getNewAnnotationsMap, + getParentToUpdate, getRotationMatrix, getSizeInBytes, isAscii, @@ -703,11 +730,14 @@ export { lookupMatrix, lookupNormalRect, lookupRect, + MAX_INT_32, + MIN_INT_32, MissingDataException, numberToString, ParserEOFException, parseXFAPath, PDF_VERSION_REGEXP, + readInt16, readInt8, readUint16, readUint32, diff --git a/src/core/crypto.js b/src/core/crypto.js index 1eee1e6fc8a49..89daf30b2cc3c 100644 --- a/src/core/crypto.js +++ b/src/core/crypto.js @@ -1397,13 +1397,7 @@ class CipherTransform { // Generate an initialization vector const iv = new Uint8Array(16); - if (typeof crypto !== "undefined") { - crypto.getRandomValues(iv); - } else { - for (let i = 0; i < 16; i++) { - iv[i] = Math.floor(256 * Math.random()); - } - } + crypto.getRandomValues(iv); let data = stringToBytes(s); data = cipher.encrypt(data, iv); @@ -1601,12 +1595,10 @@ class CipherTransformFactory { } #buildObjectKey(num, gen, encryptionKey, isAes = false) { - const key = new Uint8Array(encryptionKey.length + 9); const n = encryptionKey.length; - let i; - for (i = 0; i < n; ++i) { - key[i] = encryptionKey[i]; - } + const key = new Uint8Array(n + 9); + key.set(encryptionKey); + let i = n; key[i++] = num & 0xff; key[i++] = (num >> 8) & 0xff; key[i++] = (num >> 16) & 0xff; @@ -1619,7 +1611,7 @@ class CipherTransformFactory { key[i++] = 0x54; } const hash = calculateMD5(key, 0, i); - return hash.subarray(0, Math.min(encryptionKey.length + 5, 16)); + return hash.subarray(0, Math.min(n + 5, 16)); } #buildCipherConstructor(cf, name, num, gen, key) { @@ -1795,7 +1787,14 @@ class CipherTransformFactory { ); } - this.encryptionKey = encryptionKey; + if (algorithm === 4 && encryptionKey.length < 16) { + // Extend key to 16 byte minimum (undocumented), + // fixes issue19484_1.pdf and issue19484_2.pdf. + this.encryptionKey = new Uint8Array(16); + this.encryptionKey.set(encryptionKey); + } else { + this.encryptionKey = encryptionKey; + } if (algorithm >= 4) { const cf = dict.get("CF"); diff --git a/src/core/decode_stream.js b/src/core/decode_stream.js index 1d963b4e4502e..ef2bab40bb375 100644 --- a/src/core/decode_stream.js +++ b/src/core/decode_stream.js @@ -99,8 +99,11 @@ class DecodeStream extends BaseStream { return this.buffer.subarray(pos, end); } - async getImageData(length, decoderOptions = null) { + async getImageData(length, decoderOptions) { if (!this.canAsyncDecodeImageFromBuffer) { + if (this.isAsyncDecoder) { + return this.decodeImage(null, decoderOptions); + } return this.getBytes(length, decoderOptions); } const data = await this.stream.asyncGetBytes(); @@ -132,6 +135,8 @@ class DecodeStream extends BaseStream { class StreamsSequenceStream extends DecodeStream { constructor(streams, onError = null) { + streams = streams.filter(s => s instanceof BaseStream); + let maybeLength = 0; for (const stream of streams) { maybeLength += diff --git a/src/core/decrypt_stream.js b/src/core/decrypt_stream.js index 99dfd4ffa51a6..ee2fff21f0503 100644 --- a/src/core/decrypt_stream.js +++ b/src/core/decrypt_stream.js @@ -36,7 +36,7 @@ class DecryptStream extends DecodeStream { chunk = this.str.getBytes(chunkSize); this.initialized = true; } - if (!chunk || chunk.length === 0) { + if (!chunk?.length) { this.eof = true; return; } diff --git a/src/core/default_appearance.js b/src/core/default_appearance.js index 740a037c645db..51d179daff2b0 100644 --- a/src/core/default_appearance.js +++ b/src/core/default_appearance.js @@ -97,11 +97,12 @@ function parseDefaultAppearance(str) { } class AppearanceStreamEvaluator extends EvaluatorPreprocessor { - constructor(stream, evaluatorOptions, xref) { + constructor(stream, evaluatorOptions, xref, globalColorSpaceCache) { super(stream); this.stream = stream; this.evaluatorOptions = evaluatorOptions; this.xref = xref; + this.globalColorSpaceCache = globalColorSpaceCache; this.resources = stream.dict?.get("Resources"); } @@ -161,6 +162,7 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { xref: this.xref, resources: this.resources, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache: this._localColorSpaceCache, }); break; @@ -210,8 +212,18 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { // Parse appearance stream to extract font and color information. // It returns the font properties used to render the first text object. -function parseAppearanceStream(stream, evaluatorOptions, xref) { - return new AppearanceStreamEvaluator(stream, evaluatorOptions, xref).parse(); +function parseAppearanceStream( + stream, + evaluatorOptions, + xref, + globalColorSpaceCache +) { + return new AppearanceStreamEvaluator( + stream, + evaluatorOptions, + xref, + globalColorSpaceCache + ).parse(); } function getPdfColor(color, isFill) { diff --git a/src/core/document.js b/src/core/document.js index 2dab34d062000..fad6aad0af6d4 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -20,12 +20,14 @@ import { info, InvalidPDFException, isArrayEqual, + objectSize, PageActionEventType, RenderingIntentFlag, shadow, stringToBytes, stringToPDFString, stringToUTF8String, + toHexUtil, unreachable, Util, warn, @@ -69,11 +71,9 @@ import { OperatorList } from "./operator_list.js"; import { PartialEvaluator } from "./evaluator.js"; import { StreamsSequenceStream } from "./decode_stream.js"; import { StructTreePage } from "./struct_tree.js"; -import { writeObject } from "./writer.js"; import { XFAFactory } from "./xfa/factory.js"; import { XRef } from "./xref.js"; -const DEFAULT_USER_UNIT = 1.0; const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792]; class Page { @@ -87,6 +87,7 @@ class Page { fontCache, builtInCMapCache, standardFontDataCache, + globalColorSpaceCache, globalImageCache, systemFontCache, nonBlendModesSet, @@ -100,6 +101,7 @@ class Page { this.fontCache = fontCache; this.builtInCMapCache = builtInCMapCache; this.standardFontDataCache = standardFontDataCache; + this.globalColorSpaceCache = globalColorSpaceCache; this.globalImageCache = globalImageCache; this.systemFontCache = systemFontCache; this.nonBlendModesSet = nonBlendModesSet; @@ -194,11 +196,12 @@ class Page { } get userUnit() { - let obj = this.pageDict.get("UserUnit"); - if (typeof obj !== "number" || obj <= 0) { - obj = DEFAULT_USER_UNIT; - } - return shadow(this, "userUnit", obj); + const obj = this.pageDict.get("UserUnit"); + return shadow( + this, + "userUnit", + typeof obj === "number" && obj > 0 ? obj : 1.0 + ); } get view() { @@ -313,7 +316,7 @@ class Page { await Promise.all(promises); } - async saveNewAnnotations(handler, task, annotations, imagePromises) { + async saveNewAnnotations(handler, task, annotations, imagePromises, changes) { if (this.xfaFactory) { throw new Error("XFA: Cannot save new annotations."); } @@ -326,6 +329,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -347,7 +351,8 @@ class Page { partialEvaluator, task, annotations, - imagePromises + imagePromises, + changes ); for (const { ref } of newData.annotations) { @@ -357,27 +362,20 @@ class Page { } } - const savedDict = pageDict.get("Annots"); - pageDict.set("Annots", annotationsArray); - const buffer = []; - await writeObject(this.ref, pageDict, buffer, this.xref); - if (savedDict) { - pageDict.set("Annots", savedDict); - } + const dict = pageDict.clone(); + dict.set("Annots", annotationsArray); + changes.put(this.ref, { + data: dict, + }); - const objects = newData.dependencies; - objects.push( - { ref: this.ref, data: buffer.join("") }, - ...newData.annotations - ); for (const deletedRef of deletedAnnotations) { - objects.push({ ref: deletedRef, data: null }); + changes.put(deletedRef, { + data: null, + }); } - - return objects; } - save(handler, task, annotationStorage) { + save(handler, task, annotationStorage, changes) { const partialEvaluator = new PartialEvaluator({ xref: this.xref, handler, @@ -386,6 +384,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -394,11 +393,11 @@ class Page { // Fetch the page's annotations and save the content // in case of interactive form fields. return this._parsedAnnotations.then(function (annotations) { - const newRefsPromises = []; + const promises = []; for (const annotation of annotations) { - newRefsPromises.push( + promises.push( annotation - .save(partialEvaluator, task, annotationStorage) + .save(partialEvaluator, task, annotationStorage, changes) .catch(function (reason) { warn( "save - ignoring annotation data during " + @@ -409,9 +408,7 @@ class Page { ); } - return Promise.all(newRefsPromises).then(function (newRefs) { - return newRefs.filter(newRef => !!newRef); - }); + return Promise.all(promises); }); } @@ -453,6 +450,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -548,9 +546,7 @@ class Page { resources: this.resources, operatorList: opList, }) - .then(function () { - return opList; - }); + .then(() => opList); }); // Fetch the page's annotations and add their operator lists to the @@ -677,6 +673,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -706,7 +703,7 @@ class Page { const structTree = await this.pdfManager.ensure(this, "_parseStructTree", [ structTreeRoot, ]); - return structTree.serializable; + return this.pdfManager.ensure(structTree, "serializable"); } /** @@ -749,6 +746,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -862,10 +860,6 @@ const STARTXREF_SIGNATURE = new Uint8Array([ ]); const ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]); -const FINGERPRINT_FIRST_BYTES = 1024; -const EMPTY_FINGERPRINT = - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - function find(stream, signature, limit = 1024, backwards = false) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert(limit > 0, 'The "limit" must be a positive integer.'); @@ -1294,13 +1288,8 @@ class PDFDocument { }, }; - const fonts = new Map(); - fontRes.forEach((fontName, font) => { - fonts.set(fontName, font); - }); const promises = []; - - for (const [fontName, font] of fonts) { + for (const [fontName, font] of fontRes) { const descriptor = font.get("FontDescriptor"); if (!(descriptor instanceof Dict)) { continue; @@ -1489,9 +1478,7 @@ class PDFDocument { return shadow(this, "documentInfo", docInfo); } - for (const key of infoDict.getKeys()) { - const value = infoDict.get(key); - + for (const [key, value] of infoDict) { switch (key) { case "Title": case "Author": @@ -1548,30 +1535,24 @@ class PDFDocument { } get fingerprints() { + const FINGERPRINT_FIRST_BYTES = 1024; + const EMPTY_FINGERPRINT = "\x00".repeat(16); + function validate(data) { return ( typeof data === "string" && - data.length > 0 && + data.length === 16 && data !== EMPTY_FINGERPRINT ); } - function hexString(hash) { - const buf = []; - for (const num of hash) { - const hex = num.toString(16); - buf.push(hex.padStart(2, "0")); - } - return buf.join(""); - } - - const idArray = this.xref.trailer.get("ID"); + const id = this.xref.trailer.get("ID"); let hashOriginal, hashModified; - if (Array.isArray(idArray) && validate(idArray[0])) { - hashOriginal = stringToBytes(idArray[0]); + if (Array.isArray(id) && validate(id[0])) { + hashOriginal = stringToBytes(id[0]); - if (idArray[1] !== idArray[0] && validate(idArray[1])) { - hashModified = stringToBytes(idArray[1]); + if (id[1] !== id[0] && validate(id[1])) { + hashModified = stringToBytes(id[1]); } } else { hashOriginal = calculateMD5( @@ -1582,8 +1563,8 @@ class PDFDocument { } return shadow(this, "fingerprints", [ - hexString(hashOriginal), - hashModified ? hexString(hashModified) : null, + toHexUtil(hashOriginal), + hashModified ? toHexUtil(hashModified) : null, ]); } @@ -1644,24 +1625,25 @@ class PDFDocument { } else { promise = catalog.getPageDict(pageIndex); } - // eslint-disable-next-line arrow-body-style - promise = promise.then(([pageDict, ref]) => { - return new Page({ - pdfManager: this.pdfManager, - xref: this.xref, - pageIndex, - pageDict, - ref, - globalIdFactory: this._globalIdFactory, - fontCache: catalog.fontCache, - builtInCMapCache: catalog.builtInCMapCache, - standardFontDataCache: catalog.standardFontDataCache, - globalImageCache: catalog.globalImageCache, - systemFontCache: catalog.systemFontCache, - nonBlendModesSet: catalog.nonBlendModesSet, - xfaFactory, - }); - }); + promise = promise.then( + ([pageDict, ref]) => + new Page({ + pdfManager: this.pdfManager, + xref: this.xref, + pageIndex, + pageDict, + ref, + globalIdFactory: this._globalIdFactory, + fontCache: catalog.fontCache, + builtInCMapCache: catalog.builtInCMapCache, + standardFontDataCache: catalog.standardFontDataCache, + globalColorSpaceCache: catalog.globalColorSpaceCache, + globalImageCache: catalog.globalImageCache, + systemFontCache: catalog.systemFontCache, + nonBlendModesSet: catalog.nonBlendModesSet, + xfaFactory, + }) + ); this._pagePromises.set(pageIndex, promise); return promise; @@ -1755,6 +1737,7 @@ class PDFDocument { fontCache: catalog.fontCache, builtInCMapCache: catalog.builtInCMapCache, standardFontDataCache: catalog.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: catalog.globalImageCache, systemFontCache: catalog.systemFontCache, nonBlendModesSet: catalog.nonBlendModesSet, @@ -1769,8 +1752,15 @@ class PDFDocument { } } - fontFallback(id, handler) { - return this.catalog.fontFallback(id, handler); + async fontFallback(id, handler) { + const { catalog, pdfManager } = this; + + for (const translatedFont of await Promise.all(catalog.fontCache)) { + if (translatedFont.loadedName === id) { + translatedFont.fallback(handler, pdfManager.evaluatorOptions); + return; + } + } } async cleanup(manuallyTriggered = false) { @@ -1798,6 +1788,13 @@ class PDFDocument { if (!(field instanceof Dict)) { return; } + let subtype = await field.getAsync("Subtype"); + subtype = subtype instanceof Name ? subtype.name : null; + // Skip unrelated annotation types (see issue 19281). + switch (subtype) { + case "Link": + return; + } if (field.has("T")) { const partName = stringToPDFString(await field.getAsync("T")); name = name === "" ? partName : `${name}.${partName}`; @@ -1913,9 +1910,12 @@ class PDFDocument { }) ); } - await Promise.all(allPromises); - return { allFields, orphanFields }; + + return { + allFields: objectSize(allFields) > 0 ? allFields : null, + orphanFields, + }; }); return shadow(this, "fieldObjects", promise); @@ -1938,7 +1938,7 @@ class PDFDocument { if (catalogJsActions) { return true; } - if (fieldObjects) { + if (fieldObjects?.allFields) { return Object.values(fieldObjects.allFields).some(fieldObject => fieldObject.some(object => object.actions !== null) ); diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 27ba453fd441c..91ac7c28dd27b 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* eslint-disable no-var */ import { AbortException, @@ -33,6 +32,12 @@ import { import { CMapFactory, IdentityCMap } from "./cmap.js"; import { Cmd, Dict, EOF, isName, Name, Ref, RefSet } from "./primitives.js"; import { ErrorFont, Font } from "./fonts.js"; +import { + fetchBinaryData, + isNumberArray, + lookupMatrix, + lookupNormalRect, +} from "./core_utils.js"; import { getEncoding, MacRomanEncoding, @@ -52,7 +57,6 @@ import { import { getTilingPatternIR, Pattern } from "./pattern.js"; import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js"; import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js"; -import { isNumberArray, lookupMatrix, lookupNormalRect } from "./core_utils.js"; import { isPDFFunction, PDFFunctionFactory } from "./function.js"; import { Lexer, Parser } from "./parser.js"; import { @@ -71,7 +75,6 @@ import { getFontSubstitution } from "./font_substitutions.js"; import { getGlyphsUnicode } from "./glyphlist.js"; import { getMetrics } from "./metrics.js"; import { getUnicodeForGlyph } from "./unicode.js"; -import { ImageResizer } from "./image_resizer.js"; import { MurmurHash3_64 } from "../shared/murmurhash3.js"; import { OperatorList } from "./operator_list.js"; import { PDFImage } from "./image.js"; @@ -83,11 +86,15 @@ const DefaultPartialEvaluatorOptions = Object.freeze({ ignoreErrors: false, isEvalSupported: true, isOffscreenCanvasSupported: false, + isImageDecoderSupported: false, canvasMaxAreaInBytes: -1, fontExtraProperties: false, useSystemFonts: true, + useWasm: true, + useWorkerFetch: true, cMapUrl: null, standardFontDataUrl: null, + wasmUrl: null, }); const PatternType = { @@ -174,7 +181,7 @@ function addLocallyCachedImageOps(opList, data) { if (data.objId) { opList.addDependency(data.objId); } - opList.addImageOps(data.fn, data.args, data.optionalContent); + opList.addImageOps(data.fn, data.args, data.optionalContent, data.hasMask); if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) { data.args[0].count++; @@ -214,6 +221,7 @@ class PartialEvaluator { fontCache, builtInCMapCache, standardFontDataCache, + globalColorSpaceCache, globalImageCache, systemFontCache, options = null, @@ -225,6 +233,7 @@ class PartialEvaluator { this.fontCache = fontCache; this.builtInCMapCache = builtInCMapCache; this.standardFontDataCache = standardFontDataCache; + this.globalColorSpaceCache = globalColorSpaceCache; this.globalImageCache = globalImageCache; this.systemFontCache = systemFontCache; this.options = options || DefaultPartialEvaluatorOptions; @@ -232,7 +241,6 @@ class PartialEvaluator { this._regionalImageCache = new RegionalImageCache(); this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this); - ImageResizer.setMaxArea(this.options.canvasMaxAreaInBytes); } /** @@ -380,22 +388,18 @@ class PartialEvaluator { } let data; - if (this.options.cMapUrl !== null) { + if (this.options.useWorkerFetch) { // Only compressed CMaps are (currently) supported here. - const url = `${this.options.cMapUrl}${name}.bcmap`; - const response = await fetch(url); - if (!response.ok) { - throw new Error( - `fetchBuiltInCMap: failed to fetch file "${url}" with "${response.statusText}".` - ); - } data = { - cMapData: new Uint8Array(await response.arrayBuffer()), + cMapData: await fetchBinaryData(`${this.options.cMapUrl}${name}.bcmap`), isCompressed: true, }; } else { // Get the data on the main-thread instead. - data = await this.handler.sendWithPromise("FetchBuiltInCMap", { name }); + data = await this.handler.sendWithPromise("FetchBinaryData", { + type: "cMapReaderFactory", + name, + }); } // Cache the CMap data, to avoid fetching it repeatedly. this.builtInCMapCache.set(name, data); @@ -423,30 +427,20 @@ class PartialEvaluator { filename = standardFontNameToFileName[name]; let data; - if (this.options.standardFontDataUrl !== null) { - const url = `${this.options.standardFontDataUrl}${filename}`; - const response = await fetch(url); - if (!response.ok) { - warn( - `fetchStandardFontData: failed to fetch file "${url}" with "${response.statusText}".` + try { + if (this.options.useWorkerFetch) { + data = await fetchBinaryData( + `${this.options.standardFontDataUrl}${filename}` ); } else { - data = new Uint8Array(await response.arrayBuffer()); - } - } else { - // Get the data on the main-thread instead. - try { - data = await this.handler.sendWithPromise("FetchStandardFontData", { + // Get the data on the main-thread instead. + data = await this.handler.sendWithPromise("FetchBinaryData", { + type: "standardFontDataFactory", filename, }); - } catch (e) { - warn( - `fetchStandardFontData: failed to fetch file "${filename}" with "${e}".` - ); } - } - - if (!data) { + } catch (ex) { + warn(ex); return null; } // Cache the "raw" standard font data, to avoid fetching it repeatedly @@ -500,6 +494,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( cs, this.xref, + this.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { @@ -735,13 +730,9 @@ class PartialEvaluator { } const SMALL_IMAGE_DIMENSIONS = 200; + const hasMask = dict.has("SMask") || dict.has("Mask"); // Inlining small images into the queue as RGB data - if ( - isInline && - w + h < SMALL_IMAGE_DIMENSIONS && - !dict.has("SMask") && - !dict.has("Mask") - ) { + if (isInline && w + h < SMALL_IMAGE_DIMENSIONS && !hasMask) { try { const imageObj = new PDFImage({ xref: this.xref, @@ -749,6 +740,7 @@ class PartialEvaluator { image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }); // We force the use of RGBA_32BPP images here, because we can't handle @@ -798,7 +790,12 @@ class PartialEvaluator { // Ensure that the dependency is added before the image is decoded. operatorList.addDependency(objId); args = [objId, w, h]; - operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent); + operatorList.addImageOps( + OPS.paintImageXObject, + args, + optionalContent, + hasMask + ); if (cacheGlobally) { if (this.globalImageCache.hasDecodeFailed(imageRef)) { @@ -807,6 +804,7 @@ class PartialEvaluator { fn: OPS.paintImageXObject, args, optionalContent, + hasMask, byteSize: 0, // Data is `null`, since decoding failed previously. }); @@ -817,7 +815,7 @@ class PartialEvaluator { // For large (at least 500x500) or more complex images that we'll cache // globally, check if the image is still cached locally on the main-thread // to avoid having to re-parse the image (since that can be slow). - if (w * h > 250000 || dict.has("SMask") || dict.has("Mask")) { + if (w * h > 250000 || hasMask) { const localLength = await this.handler.sendWithPromise("commonobj", [ objId, "CopyLocalImage", @@ -830,6 +828,7 @@ class PartialEvaluator { fn: OPS.paintImageXObject, args, optionalContent, + hasMask, byteSize: 0, // Temporary entry, to avoid `setData` returning early. }); this.globalImageCache.addByteSize(imageRef, localLength); @@ -844,6 +843,7 @@ class PartialEvaluator { image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }) .then(async imageObj => { @@ -877,6 +877,7 @@ class PartialEvaluator { fn: OPS.paintImageXObject, args, optionalContent, + hasMask, }; localImageCache.set(cacheKey, imageRef, cacheData); @@ -889,6 +890,7 @@ class PartialEvaluator { fn: OPS.paintImageXObject, args, optionalContent, + hasMask, byteSize: 0, // Temporary entry, note `addByteSize` above. }); } @@ -1049,28 +1051,19 @@ class PartialEvaluator { ) { const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null; - let translated = await this.loadFont( + const translated = await this.loadFont( fontName, fontRef, resources, + task, fallbackFontDict, cssFontInfo ); if (translated.font.isType3Font) { - try { - await translated.loadType3Data(this, resources, task); - // Add the dependencies to the parent operatorList so they are - // resolved before Type3 operatorLists are executed synchronously. - operatorList.addDependencies(translated.type3Dependencies); - } catch (reason) { - translated = new TranslatedFont({ - loadedName: "g_font_error", - font: new ErrorFont(`Type3 font load error: ${reason}`), - dict: translated.font, - evaluatorOptions: this.options, - }); - } + // Add the dependencies to the parent operatorList so they are + // resolved before Type3 operatorLists are executed synchronously. + operatorList.addDependencies(translated.type3Dependencies); } state.font = translated.font; @@ -1089,8 +1082,7 @@ class PartialEvaluator { if ( isAddToPathSet || state.fillColorSpace.name === "Pattern" || - font.disableFontFace || - this.options.disableFontFace + font.disableFontFace ) { PartialEvaluator.buildFontPaths( font, @@ -1133,8 +1125,7 @@ class PartialEvaluator { // This array holds the converted/processed state data. const gStateObj = []; let promise = Promise.resolve(); - for (const key of gState.getKeys()) { - const value = gState.get(key); + for (const [key, value] of gState) { switch (key) { case "Type": break; @@ -1234,18 +1225,16 @@ class PartialEvaluator { fontName, font, resources, + task, fallbackFontDict = null, cssFontInfo = null ) { - // eslint-disable-next-line arrow-body-style - const errorFont = async () => { - return new TranslatedFont({ + const errorFont = async () => + new TranslatedFont({ loadedName: "g_font_error", font: new ErrorFont(`Font "${fontName}" is not available.`), dict: font, - evaluatorOptions: this.options, }); - }; let fontRef; if (font) { @@ -1365,15 +1354,21 @@ class PartialEvaluator { font.loadedName = `${this.idFactory.getDocId()}_${fontID}`; this.translateFont(preEvaluatedFont) - .then(translatedFont => { - resolve( - new TranslatedFont({ - loadedName: font.loadedName, - font: translatedFont, - dict: font, - evaluatorOptions: this.options, - }) - ); + .then(async translatedFont => { + const translated = new TranslatedFont({ + loadedName: font.loadedName, + font: translatedFont, + dict: font, + }); + + if (translatedFont.isType3Font) { + try { + await translated.loadType3Data(this, resources, task); + } catch (reason) { + throw new Error(`Type3 font load error: ${reason}`); + } + } + resolve(translated); }) .catch(reason => { // TODO reject? @@ -1382,11 +1377,8 @@ class PartialEvaluator { resolve( new TranslatedFont({ loadedName: font.loadedName, - font: new ErrorFont( - reason instanceof Error ? reason.message : reason - ), + font: new ErrorFont(reason?.message), dict: font, - evaluatorOptions: this.options, }) ); }); @@ -1476,6 +1468,7 @@ class PartialEvaluator { xref: this.xref, resources, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }).catch(reason => { if (reason instanceof AbortException) { @@ -1509,6 +1502,7 @@ class PartialEvaluator { this.xref, resources, this._pdfFunctionFactory, + this.globalColorSpaceCache, localColorSpaceCache ); patternIR = shadingFill.getIR(); @@ -1819,7 +1813,8 @@ class PartialEvaluator { operatorList.addImageOps( globalImage.fn, globalImage.args, - globalImage.optionalContent + globalImage.optionalContent, + globalImage.hasMask ); resolveXObject(); @@ -1890,7 +1885,7 @@ class PartialEvaluator { ); return; case OPS.setFont: - var fontSize = args[1]; + const fontSize = args[1]; // eagerly collect all fonts next( self @@ -1916,7 +1911,7 @@ class PartialEvaluator { parsingText = false; break; case OPS.endInlineImage: - var cacheKey = args[0].cacheKey; + const cacheKey = args[0].cacheKey; if (cacheKey) { const localImage = localImageCache.getByName(cacheKey); if (localImage) { @@ -1949,8 +1944,8 @@ class PartialEvaluator { self.ensureStateFont(stateManager.state); continue; } - var combinedGlyphs = []; - var state = stateManager.state; + const combinedGlyphs = [], + state = stateManager.state; for (const arrItem of args[0]) { if (typeof arrItem === "string") { combinedGlyphs.push(...self.handleText(arrItem, state)); @@ -1989,6 +1984,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( args[0], xref, + self.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { @@ -2014,6 +2010,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( args[0], xref, + self.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { @@ -2626,16 +2623,12 @@ class PartialEvaluator { } async function handleSetFont(fontName, fontRef) { - const translated = await self.loadFont(fontName, fontRef, resources); - - if (translated.font.isType3Font) { - try { - await translated.loadType3Data(self, resources, task); - } catch { - // Ignore Type3-parsing errors, since we only use `loadType3Data` - // here to ensure that we'll always obtain a useful /FontBBox. - } - } + const translated = await self.loadFont( + fontName, + fontRef, + resources, + task + ); textState.loadedName = translated.loadedName; textState.font = translated.font; @@ -3082,6 +3075,8 @@ class PartialEvaluator { const operation = {}; let stop, + name, + isValidName, args = []; while (!(stop = timeSlotManager.check())) { // The arguments parsed by read() are not used beyond this loop, so @@ -3101,7 +3096,7 @@ class PartialEvaluator { switch (fn | 0) { case OPS.setFont: // Optimization to ignore multiple identical Tf commands. - var fontNameArg = args[0].name, + const fontNameArg = args[0].name, fontSizeArg = args[1]; if ( textState.font && @@ -3242,12 +3237,10 @@ class PartialEvaluator { break; case OPS.paintXObject: flushTextContentItem(); - if (!xobjs) { - xobjs = resources.get("XObject") || Dict.empty; - } + xobjs ??= resources.get("XObject") || Dict.empty; - var isValidName = args[0] instanceof Name; - var name = args[0].name; + isValidName = args[0] instanceof Name; + name = args[0].name; if (isValidName && emptyXObjectCache.getByName(name)) { break; @@ -4372,7 +4365,7 @@ class PartialEvaluator { newProperties ); } - return new Font(baseFontName, file, newProperties); + return new Font(baseFontName, file, newProperties, this.options); } } @@ -4564,7 +4557,7 @@ class PartialEvaluator { const newProperties = await this.extractDataStructures(dict, properties); this.extractWidths(dict, descriptor, newProperties); - return new Font(fontName.name, fontFile, newProperties); + return new Font(fontName.name, fontFile, newProperties, this.options); } static buildFontPaths(font, glyphs, handler, evaluatorOptions) { @@ -4612,30 +4605,31 @@ class PartialEvaluator { } class TranslatedFont { - constructor({ loadedName, font, dict, evaluatorOptions }) { + #sent = false; + + #type3Loaded = null; + + constructor({ loadedName, font, dict }) { this.loadedName = loadedName; this.font = font; this.dict = dict; - this._evaluatorOptions = evaluatorOptions || DefaultPartialEvaluatorOptions; - this.type3Loaded = null; this.type3Dependencies = font.isType3Font ? new Set() : null; - this.sent = false; } send(handler) { - if (this.sent) { + if (this.#sent) { return; } - this.sent = true; + this.#sent = true; handler.send("commonobj", [ this.loadedName, "Font", - this.font.exportData(this._evaluatorOptions.fontExtraProperties), + this.font.exportData(), ]); } - fallback(handler) { + fallback(handler, evaluatorOptions) { if (!this.font.data) { return; } @@ -4651,17 +4645,17 @@ class TranslatedFont { this.font, /* glyphs = */ this.font.glyphCacheValues, handler, - this._evaluatorOptions + evaluatorOptions ); } loadType3Data(evaluator, resources, task) { - if (this.type3Loaded) { - return this.type3Loaded; - } - if (!this.font.isType3Font) { - throw new Error("Must be a Type3 font."); + if (this.#type3Loaded) { + return this.#type3Loaded; } + const { font, type3Dependencies } = this; + assert(font.isType3Font, "Must be a Type3 font."); + // When parsing Type3 glyphs, always ignore them if there are errors. // Compared to the parsing of e.g. an entire page, it doesn't really // make sense to only be able to render a Type3 glyph partially. @@ -4673,14 +4667,12 @@ class TranslatedFont { } type3Evaluator.type3FontRefs = type3FontRefs; - const translatedFont = this.font, - type3Dependencies = this.type3Dependencies; let loadCharProcsPromise = Promise.resolve(); const charProcs = this.dict.get("CharProcs"); const fontResources = this.dict.get("Resources") || resources; const charProcOperatorList = Object.create(null); - const fontBBox = Util.normalizeRect(translatedFont.bbox || [0, 0, 0, 0]), + const fontBBox = Util.normalizeRect(font.bbox || [0, 0, 0, 0]), width = fontBBox[2] - fontBBox[0], height = fontBBox[3] - fontBBox[1]; const fontBBoxSize = Math.hypot(width, height); @@ -4704,7 +4696,7 @@ class TranslatedFont { // colour-related parameters) in the graphics state; // any use of such operators shall be ignored." if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) { - this._removeType3ColorOperators(operatorList, fontBBoxSize); + this.#removeType3ColorOperators(operatorList, fontBBoxSize); } charProcOperatorList[key] = operatorList.getIR(); @@ -4719,20 +4711,17 @@ class TranslatedFont { }); }); } - this.type3Loaded = loadCharProcsPromise.then(() => { - translatedFont.charProcOperatorList = charProcOperatorList; + this.#type3Loaded = loadCharProcsPromise.then(() => { + font.charProcOperatorList = charProcOperatorList; if (this._bbox) { - translatedFont.isCharBBox = true; - translatedFont.bbox = this._bbox; + font.isCharBBox = true; + font.bbox = this._bbox; } }); - return this.type3Loaded; + return this.#type3Loaded; } - /** - * @private - */ - _removeType3ColorOperators(operatorList, fontBBoxSize = NaN) { + #removeType3ColorOperators(operatorList, fontBBoxSize = NaN) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert( operatorList.fnArray[0] === OPS.setCharWidthAndBounds, diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 572cda50995bd..0cd2798d4dc97 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -14,45 +14,30 @@ */ import { + assert, bytesToString, FONT_IDENTITY_MATRIX, - FontRenderOps, FormatError, unreachable, + Util, warn, } from "../shared/util.js"; +import { + isNumberArray, + readInt16, + readInt8, + readUint16, + readUint32, +} from "./core_utils.js"; import { CFFParser } from "./cff_parser.js"; import { getGlyphsUnicode } from "./glyphlist.js"; -import { isNumberArray } from "./core_utils.js"; import { StandardEncoding } from "./encodings.js"; import { Stream } from "./stream.js"; // TODO: use DataView and its methods. -function getUint32(data, offset) { - return ( - ((data[offset] << 24) | - (data[offset + 1] << 16) | - (data[offset + 2] << 8) | - data[offset + 3]) >>> - 0 - ); -} - -function getUint16(data, offset) { - return (data[offset] << 8) | data[offset + 1]; -} - -function getInt16(data, offset) { - return ((data[offset] << 24) | (data[offset + 1] << 16)) >> 16; -} - -function getInt8(data, offset) { - return (data[offset] << 24) >> 24; -} - function getFloat214(data, offset) { - return getInt16(data, offset) / 16384; + return readInt16(data, offset) / 16384; } function getSubroutineBias(subrs) { @@ -68,48 +53,48 @@ function getSubroutineBias(subrs) { function parseCmap(data, start, end) { const offset = - getUint16(data, start + 2) === 1 - ? getUint32(data, start + 8) - : getUint32(data, start + 16); - const format = getUint16(data, start + offset); + readUint16(data, start + 2) === 1 + ? readUint32(data, start + 8) + : readUint32(data, start + 16); + const format = readUint16(data, start + offset); let ranges, p, i; if (format === 4) { - getUint16(data, start + offset + 2); // length - const segCount = getUint16(data, start + offset + 6) >> 1; + readUint16(data, start + offset + 2); // length + const segCount = readUint16(data, start + offset + 6) >> 1; p = start + offset + 14; ranges = []; for (i = 0; i < segCount; i++, p += 2) { - ranges[i] = { end: getUint16(data, p) }; + ranges[i] = { end: readUint16(data, p) }; } p += 2; for (i = 0; i < segCount; i++, p += 2) { - ranges[i].start = getUint16(data, p); + ranges[i].start = readUint16(data, p); } for (i = 0; i < segCount; i++, p += 2) { - ranges[i].idDelta = getUint16(data, p); + ranges[i].idDelta = readUint16(data, p); } for (i = 0; i < segCount; i++, p += 2) { - let idOffset = getUint16(data, p); + let idOffset = readUint16(data, p); if (idOffset === 0) { continue; } ranges[i].ids = []; for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) { - ranges[i].ids[j] = getUint16(data, p + idOffset); + ranges[i].ids[j] = readUint16(data, p + idOffset); idOffset += 2; } } return ranges; } else if (format === 12) { - const groups = getUint32(data, start + offset + 12); + const groups = readUint32(data, start + offset + 12); p = start + offset + 16; ranges = []; for (i = 0; i < groups; i++) { - start = getUint32(data, p); + start = readUint32(data, p); ranges.push({ start, - end: getUint32(data, p + 4), - idDelta: getUint32(data, p + 8) - start, + end: readUint32(data, p + 4), + idDelta: readUint32(data, p + 8) - start, }); p += 12; } @@ -140,10 +125,10 @@ function parseGlyfTable(glyf, loca, isGlyphLocationsLong) { let itemSize, itemDecode; if (isGlyphLocationsLong) { itemSize = 4; - itemDecode = getUint32; + itemDecode = readUint32; } else { itemSize = 2; - itemDecode = (data, offset) => 2 * getUint16(data, offset); + itemDecode = (data, offset) => 2 * readUint16(data, offset); } const glyphs = []; let startOffset = itemDecode(loca, 0); @@ -182,40 +167,46 @@ function lookupCmap(ranges, unicode) { function compileGlyf(code, cmds, font) { function moveTo(x, y) { - cmds.add(FontRenderOps.MOVE_TO, [x, y]); + if (firstPoint) { + // Close the current subpath in adding a straight line to the first point. + cmds.add("L", firstPoint); + } + firstPoint = [x, y]; + cmds.add("M", [x, y]); } function lineTo(x, y) { - cmds.add(FontRenderOps.LINE_TO, [x, y]); + cmds.add("L", [x, y]); } function quadraticCurveTo(xa, ya, x, y) { - cmds.add(FontRenderOps.QUADRATIC_CURVE_TO, [xa, ya, x, y]); + cmds.add("Q", [xa, ya, x, y]); } let i = 0; - const numberOfContours = getInt16(code, i); + const numberOfContours = readInt16(code, i); let flags; + let firstPoint = null; let x = 0, y = 0; i += 10; if (numberOfContours < 0) { // composite glyph do { - flags = getUint16(code, i); - const glyphIndex = getUint16(code, i + 2); + flags = readUint16(code, i); + const glyphIndex = readUint16(code, i + 2); i += 4; let arg1, arg2; if (flags & 0x01) { if (flags & 0x02) { - arg1 = getInt16(code, i); - arg2 = getInt16(code, i + 2); + arg1 = readInt16(code, i); + arg2 = readInt16(code, i + 2); } else { - arg1 = getUint16(code, i); - arg2 = getUint16(code, i + 2); + arg1 = readUint16(code, i); + arg2 = readUint16(code, i + 2); } i += 4; } else if (flags & 0x02) { - arg1 = getInt8(code, i++); - arg2 = getInt8(code, i++); + arg1 = readInt8(code, i++); + arg2 = readInt8(code, i++); } else { arg1 = code[i++]; arg2 = code[i++]; @@ -249,22 +240,15 @@ function compileGlyf(code, cmds, font) { if (subglyph) { // TODO: the transform should be applied only if there is a scale: // https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1205 - cmds.add(FontRenderOps.SAVE); - cmds.add(FontRenderOps.TRANSFORM, [ - scaleX, - scale01, - scale10, - scaleY, - x, - y, - ]); + cmds.save(); + cmds.transform([scaleX, scale01, scale10, scaleY, x, y]); if (!(flags & 0x02)) { // TODO: we must use arg1 and arg2 to make something similar to: // https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1209 } compileGlyf(subglyph, cmds, font); - cmds.add(FontRenderOps.RESTORE); + cmds.restore(); } } while (flags & 0x20); } else { @@ -272,10 +256,10 @@ function compileGlyf(code, cmds, font) { const endPtsOfContours = []; let j, jj; for (j = 0; j < numberOfContours; j++) { - endPtsOfContours.push(getUint16(code, i)); + endPtsOfContours.push(readUint16(code, i)); i += 2; } - const instructionLength = getUint16(code, i); + const instructionLength = readUint16(code, i); i += 2 + instructionLength; // skipping the instructions const numberOfPoints = endPtsOfContours.at(-1) + 1; const points = []; @@ -292,7 +276,7 @@ function compileGlyf(code, cmds, font) { for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x12) { case 0x00: - x += getInt16(code, i); + x += readInt16(code, i); i += 2; break; case 0x02: @@ -307,7 +291,7 @@ function compileGlyf(code, cmds, font) { for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x24) { case 0x00: - y += getInt16(code, i); + y += readInt16(code, i); i += 2; break; case 0x04: @@ -369,19 +353,25 @@ function compileGlyf(code, cmds, font) { function compileCharString(charStringCode, cmds, font, glyphId) { function moveTo(x, y) { - cmds.add(FontRenderOps.MOVE_TO, [x, y]); + if (firstPoint) { + // Close the current subpath in adding a straight line to the first point. + cmds.add("L", firstPoint); + } + firstPoint = [x, y]; + cmds.add("M", [x, y]); } function lineTo(x, y) { - cmds.add(FontRenderOps.LINE_TO, [x, y]); + cmds.add("L", [x, y]); } function bezierCurveTo(x1, y1, x2, y2, x, y) { - cmds.add(FontRenderOps.BEZIER_CURVE_TO, [x1, y1, x2, y2, x, y]); + cmds.add("C", [x1, y1, x2, y2, x, y]); } const stack = []; let x = 0, y = 0; let stems = 0; + let firstPoint = null; function parse(code) { let i = 0; @@ -548,8 +538,8 @@ function compileCharString(charStringCode, cmds, font, glyphId) { const bchar = stack.pop(); y = stack.pop(); x = stack.pop(); - cmds.add(FontRenderOps.SAVE); - cmds.add(FontRenderOps.TRANSLATE, [x, y]); + cmds.save(); + cmds.translate(x, y); let cmap = lookupCmap( font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]]) @@ -560,7 +550,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) { font, cmap.glyphId ); - cmds.add(FontRenderOps.RESTORE); + cmds.restore(); cmap = lookupCmap( font.cmap, @@ -660,7 +650,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) { } break; case 28: - stack.push(((code[i] << 24) | (code[i + 1] << 16)) >> 16); + stack.push(readInt16(code, i)); i += 2; break; case 29: // callgsubr @@ -744,27 +734,49 @@ function compileCharString(charStringCode, cmds, font, glyphId) { parse(charStringCode); } -const NOOP = []; +const NOOP = ""; class Commands { cmds = []; + transformStack = []; + + currentTransform = [1, 0, 0, 1, 0, 0]; + add(cmd, args) { if (args) { - if (!isNumberArray(args, null)) { - warn( - `Commands.add - "${cmd}" has at least one non-number arg: "${args}".` - ); - // "Fix" the wrong args by replacing them with 0. - const newArgs = args.map(arg => (typeof arg === "number" ? arg : 0)); - this.cmds.push(cmd, ...newArgs); - } else { - this.cmds.push(cmd, ...args); + const [a, b, c, d, e, f] = this.currentTransform; + for (let i = 0, ii = args.length; i < ii; i += 2) { + const x = args[i]; + const y = args[i + 1]; + args[i] = a * x + c * y + e; + args[i + 1] = b * x + d * y + f; } + this.cmds.push(`${cmd}${args.join(" ")}`); } else { this.cmds.push(cmd); } } + + transform(transf) { + this.currentTransform = Util.transform(this.currentTransform, transf); + } + + translate(x, y) { + this.transform([1, 0, 0, 1, x, y]); + } + + save() { + this.transformStack.push(this.currentTransform.slice()); + } + + restore() { + this.currentTransform = this.transformStack.pop() || [1, 0, 0, 1, 0, 0]; + } + + getSVG() { + return this.cmds.join(""); + } } class CompiledFont { @@ -785,7 +797,7 @@ class CompiledFont { const { charCode, glyphId } = lookupCmap(this.cmap, unicode); let fn = this.compiledGlyphs[glyphId], compileEx; - if (!fn) { + if (fn === undefined) { try { fn = this.compileGlyph(this.glyphs[glyphId], glyphId); } catch (ex) { @@ -804,7 +816,7 @@ class CompiledFont { } compileGlyph(code, glyphId) { - if (!code || code.length === 0 || code[0] === 14) { + if (!code?.length || code[0] === 14) { return NOOP; } @@ -820,15 +832,14 @@ class CompiledFont { warn("Invalid fd index for glyph index."); } } + assert(isNumberArray(fontMatrix, 6), "Expected a valid fontMatrix."); const cmds = new Commands(); - cmds.add(FontRenderOps.SAVE); - cmds.add(FontRenderOps.TRANSFORM, fontMatrix.slice()); - cmds.add(FontRenderOps.SCALE); + cmds.transform(fontMatrix.slice()); this.compileGlyphImpl(code, cmds, glyphId); - cmds.add(FontRenderOps.RESTORE); + cmds.add("Z"); - return cmds.cmds; + return cmds.getSVG(); } compileGlyphImpl() { @@ -858,14 +869,14 @@ class TrueTypeCompiled extends CompiledFont { } class Type2Compiled extends CompiledFont { - constructor(cffInfo, cmap, fontMatrix, glyphNameMap) { + constructor(cffInfo, cmap, fontMatrix) { super(fontMatrix || [0.001, 0, 0, 0.001, 0, 0]); this.glyphs = cffInfo.glyphs; this.gsubrs = cffInfo.gsubrs || []; this.subrs = cffInfo.subrs || []; this.cmap = cmap; - this.glyphNameMap = glyphNameMap || getGlyphsUnicode(); + this.glyphNameMap = getGlyphsUnicode(); this.gsubrsBias = getSubroutineBias(this.gsubrs); this.subrsBias = getSubroutineBias(this.subrs); @@ -884,11 +895,11 @@ class FontRendererFactory { static create(font, seacAnalysisEnabled) { const data = new Uint8Array(font.data); let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm; - const numTables = getUint16(data, 4); + const numTables = readUint16(data, 4); for (let i = 0, p = 12; i < numTables; i++, p += 16) { const tag = bytesToString(data.subarray(p, p + 4)); - const offset = getUint32(data, p + 8); - const length = getUint32(data, p + 12); + const offset = readUint32(data, p + 8); + const length = readUint32(data, p + 12); switch (tag) { case "cmap": cmap = parseCmap(data, offset, offset + length); @@ -900,8 +911,8 @@ class FontRendererFactory { loca = data.subarray(offset, offset + length); break; case "head": - unitsPerEm = getUint16(data, offset + 18); - indexToLocFormat = getUint16(data, offset + 50); + unitsPerEm = readUint16(data, offset + 18); + indexToLocFormat = readUint16(data, offset + 50); break; case "CFF ": cff = parseCff(data, offset, offset + length, seacAnalysisEnabled); @@ -919,7 +930,7 @@ class FontRendererFactory { fontMatrix ); } - return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap); + return new Type2Compiled(cff, cmap, font.fontMatrix); } } diff --git a/src/core/fonts.js b/src/core/fonts.js index 8dcda2a20ad51..a2e090d61f500 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -82,13 +82,14 @@ const EXPORT_DATA_PROPERTIES = [ "black", "bold", "charProcOperatorList", - "composite", "cssFontInfo", "data", "defaultVMetrics", "defaultWidth", "descent", + "disableFontFace", "fallbackName", + "fontExtraProperties", "fontMatrix", "isInvalidPDFjsFont", "isType3Font", @@ -98,22 +99,23 @@ const EXPORT_DATA_PROPERTIES = [ "missingFile", "name", "remeasure", - "subtype", "systemFontInfo", - "type", "vertical", ]; const EXPORT_DATA_EXTRA_PROPERTIES = [ "cMap", + "composite", "defaultEncoding", "differences", "isMonospace", "isSerifFont", "isSymbolicFont", "seacMap", + "subtype", "toFontChar", "toUnicode", + "type", "vmetrics", "widths", ]; @@ -485,6 +487,8 @@ function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId, toUnicode) { const isInPrivateArea = code => (PRIVATE_USE_AREAS[0][0] <= code && code <= PRIVATE_USE_AREAS[0][1]) || (PRIVATE_USE_AREAS[1][0] <= code && code <= PRIVATE_USE_AREAS[1][1]); + let LIGATURE_TO_UNICODE = null; + for (const originalCharCode in charCodeToGlyphId) { let glyphId = charCodeToGlyphId[originalCharCode]; // For missing glyphs don't create the mappings so the glyph isn't @@ -514,7 +518,23 @@ function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId, toUnicode) { // glyph ids to the correct unicode. let unicode = toUnicode.get(originalCharCode); if (typeof unicode === "string") { - unicode = unicode.codePointAt(0); + if (unicode.length === 1) { + unicode = unicode.codePointAt(0); + } else { + if (!LIGATURE_TO_UNICODE) { + LIGATURE_TO_UNICODE = new Map(); + // The code range [0xfb00, 0xfb4f] contains some ligature characters + // but not all. + // See https://www.compart.com/en/unicode/block/U+FB00. + for (let i = 0xfb00; i <= 0xfb4f; i++) { + const normalized = String.fromCharCode(i).normalize("NFKD"); + if (normalized.length > 1) { + LIGATURE_TO_UNICODE.set(normalized, i); + } + } + } + unicode = LIGATURE_TO_UNICODE.get(unicode) || unicode.codePointAt(0); + } } if (unicode && !isInPrivateArea(unicode) && !usedGlyphIds.has(glyphId)) { toUnicodeExtraMap.set(unicode, glyphId); @@ -556,9 +576,7 @@ function getRanges(glyphs, toUnicodeExtraMap, numGlyphs) { if (codes.length === 0) { codes.push({ fontCharCode: 0, glyphId: 0 }); } - codes.sort(function fontGetRangesSort(a, b) { - return a.fontCharCode - b.fontCharCode; - }); + codes.sort((a, b) => a.fontCharCode - b.fontCharCode); // Split the sorted codes into ranges. const ranges = []; @@ -952,11 +970,12 @@ function createNameTable(name, proto) { * decoding logics whatever type it is (assuming the font type is supported). */ class Font { - constructor(name, file, properties) { + constructor(name, file, properties, evaluatorOptions) { this.name = name; this.psName = null; this.mimetype = null; - this.disableFontFace = false; + this.disableFontFace = evaluatorOptions.disableFontFace; + this.fontExtraProperties = evaluatorOptions.fontExtraProperties; this.loadedName = properties.loadedName; this.isType3Font = properties.isType3Font; @@ -1123,18 +1142,17 @@ class Font { return shadow(this, "renderer", renderer); } - exportData(extraProperties = false) { - const exportDataProperties = extraProperties + exportData() { + const exportDataProps = this.fontExtraProperties ? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES] : EXPORT_DATA_PROPERTIES; const data = Object.create(null); - let property, value; - for (property of exportDataProperties) { - value = this[property]; + for (const prop of exportDataProps) { + const value = this[prop]; // Ignore properties that haven't been explicitly set. if (value !== undefined) { - data[property] = value; + data[prop] = value; } } return data; @@ -1757,20 +1775,23 @@ class Font { } // removing duplicate entries - mappings.sort(function (a, b) { - return a.charCode - b.charCode; - }); - for (let i = 1; i < mappings.length; i++) { - if (mappings[i - 1].charCode === mappings[i].charCode) { - mappings.splice(i, 1); - i--; + mappings.sort((a, b) => a.charCode - b.charCode); + const finalMappings = [], + seenCharCodes = new Set(); + for (const map of mappings) { + const { charCode } = map; + + if (seenCharCodes.has(charCode)) { + continue; } + seenCharCodes.add(charCode); + finalMappings.push(map); } return { platformId: potentialTable.platformId, encodingId: potentialTable.encodingId, - mappings, + mappings: finalMappings, hasShortCmap, }; } @@ -3579,7 +3600,7 @@ class ErrorFont { return [chars]; } - exportData(extraProperties = false) { + exportData() { return { error: this.error }; } } diff --git a/src/core/function.js b/src/core/function.js index 618eeee233572..6864b13d4980b 100644 --- a/src/core/function.js +++ b/src/core/function.js @@ -252,12 +252,9 @@ class PDFFunction { // Building the cube vertices: its part and sample index // http://rjwagner49.com/Mathematics/Interpolation.pdf const cubeVertices = 1 << inputSize; - const cubeN = new Float64Array(cubeVertices); + const cubeN = new Float64Array(cubeVertices).fill(1); const cubeVertex = new Uint32Array(cubeVertices); let i, j; - for (j = 0; j < cubeVertices; j++) { - cubeN[j] = 1; - } let k = outputSize, pos = 1; diff --git a/src/core/image.js b/src/core/image.js index d384935d75361..cfdf43ade8170 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -100,6 +100,7 @@ class PDFImage { mask = null, isMask = false, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { this.image = image; @@ -214,12 +215,15 @@ class PDFImage { xref, resources: isInline ? res : null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.numComps = this.colorSpace.numComps; if (this.jpxDecoderOptions) { - this.jpxDecoderOptions.numComponents = hasColorSpace ? this.numComp : 0; + this.jpxDecoderOptions.numComponents = hasColorSpace + ? this.numComps + : 0; // If the jpx image has a color space then it musn't be used in order to // be able to use the color space that comes from the pdf. this.jpxDecoderOptions.isIndexedColormap = @@ -259,6 +263,7 @@ class PDFImage { image: smask, isInline, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } else if (mask) { @@ -275,6 +280,7 @@ class PDFImage { isInline, isMask: true, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } @@ -295,6 +301,7 @@ class PDFImage { image, isInline = false, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { const imageData = image; @@ -326,6 +333,7 @@ class PDFImage { smask: smaskData, mask: maskData, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } diff --git a/src/core/image_resizer.js b/src/core/image_resizer.js index 946b03a1cc1c6..3268a742825ff 100644 --- a/src/core/image_resizer.js +++ b/src/core/image_resizer.js @@ -13,7 +13,9 @@ * limitations under the License. */ -import { FeatureTest, ImageKind, shadow } from "../shared/util.js"; +import { FeatureTest, ImageKind, shadow, warn } from "../shared/util.js"; +import { convertToRGBA } from "../shared/image_utils.js"; +import { MAX_INT_32 } from "./core_utils.js"; const MIN_IMAGE_DIM = 2048; @@ -34,11 +36,23 @@ const MAX_ERROR = 128; class ImageResizer { static #goodSquareLength = MIN_IMAGE_DIM; + static #isImageDecoderSupported = FeatureTest.isImageDecoderSupported; + constructor(imgData, isMask) { this._imgData = imgData; this._isMask = isMask; } + static get canUseImageDecoder() { + return shadow( + this, + "canUseImageDecoder", + this.#isImageDecoderSupported + ? ImageDecoder.isTypeSupported("image/bmp") + : Promise.resolve(false) + ); + } + static needsToBeResized(width, height) { if (width <= this.#goodSquareLength && height <= this.#goodSquareLength) { return false; @@ -106,11 +120,15 @@ class ImageResizer { } } - static setMaxArea(area) { + static setOptions({ + canvasMaxAreaInBytes = -1, + isImageDecoderSupported = false, + }) { if (!this._hasMaxArea) { // Divide by 4 to have the value in pixels. - this.MAX_AREA = area >> 2; + this.MAX_AREA = canvasMaxAreaInBytes >> 2; } + this.#isImageDecoderSupported = isImageDecoderSupported; } static _areGoodDims(width, height) { @@ -156,15 +174,52 @@ class ImageResizer { } async _createImage() { + const { _imgData: imgData } = this; + const { width, height } = imgData; + + if (width * height * 4 > MAX_INT_32) { + // The resulting RGBA image is too large. + // We just rescale the data. + const result = this.#rescaleImageData(); + if (result) { + return result; + } + } + const data = this._encodeBMP(); - const blob = new Blob([data.buffer], { - type: "image/bmp", - }); - const bitmapPromise = createImageBitmap(blob); + let decoder, imagePromise; + + if (await ImageResizer.canUseImageDecoder) { + decoder = new ImageDecoder({ + data, + type: "image/bmp", + preferAnimation: false, + transfer: [data.buffer], + }); + imagePromise = decoder + .decode() + .catch(reason => { + warn(`BMP image decoding failed: ${reason}`); + // It's a bit unfortunate to create the BMP twice but we shouldn't be + // here in the first place. + return createImageBitmap( + new Blob([this._encodeBMP().buffer], { + type: "image/bmp", + }) + ); + }) + .finally(() => { + decoder.close(); + }); + } else { + imagePromise = createImageBitmap( + new Blob([data.buffer], { + type: "image/bmp", + }) + ); + } const { MAX_AREA, MAX_DIM } = ImageResizer; - const { _imgData: imgData } = this; - const { width, height } = imgData; const minFactor = Math.max( width / MAX_DIM, height / MAX_DIM, @@ -185,7 +240,8 @@ class ImageResizer { let newWidth = width; let newHeight = height; - let bitmap = await bitmapPromise; + const result = await imagePromise; + let bitmap = result.image || result; for (const step of steps) { const prevWidth = newWidth; @@ -210,6 +266,9 @@ class ImageResizer { newWidth, newHeight ); + + // Release the resources associated with the bitmap. + bitmap.close(); bitmap = canvas.transferToImageBitmap(); } @@ -221,6 +280,91 @@ class ImageResizer { return imgData; } + #rescaleImageData() { + const { _imgData: imgData } = this; + const { data, width, height, kind } = imgData; + const rgbaSize = width * height * 4; + // K is such as width * height * 4 / 2 ** K <= 2 ** 31 - 1 + const K = Math.ceil(Math.log2(rgbaSize / MAX_INT_32)); + const newWidth = width >> K; + const newHeight = height >> K; + let rgbaData; + let maxHeight = height; + + // We try to allocate the buffer with the maximum size but it can fail. + try { + rgbaData = new Uint8Array(rgbaSize); + } catch { + // n is such as 2 ** n - 1 > width * height * 4 + let n = Math.floor(Math.log2(rgbaSize + 1)); + + while (true) { + try { + rgbaData = new Uint8Array(2 ** n - 1); + break; + } catch { + n -= 1; + } + } + + maxHeight = Math.floor((2 ** n - 1) / (width * 4)); + const newSize = width * maxHeight * 4; + if (newSize < rgbaData.length) { + rgbaData = new Uint8Array(newSize); + } + } + + const src32 = new Uint32Array(rgbaData.buffer); + const dest32 = new Uint32Array(newWidth * newHeight); + + let srcPos = 0; + let newIndex = 0; + const step = Math.ceil(height / maxHeight); + const remainder = height % maxHeight === 0 ? height : height % maxHeight; + for (let k = 0; k < step; k++) { + const h = k < step - 1 ? maxHeight : remainder; + ({ srcPos } = convertToRGBA({ + kind, + src: data, + dest: src32, + width, + height: h, + inverseDecode: this._isMask, + srcPos, + })); + + for (let i = 0, ii = h >> K; i < ii; i++) { + const buf = src32.subarray((i << K) * width); + for (let j = 0; j < newWidth; j++) { + dest32[newIndex++] = buf[j << K]; + } + } + } + + if (ImageResizer.needsToBeResized(newWidth, newHeight)) { + imgData.data = dest32; + imgData.width = newWidth; + imgData.height = newHeight; + imgData.kind = ImageKind.RGBA_32BPP; + + return null; + } + + const canvas = new OffscreenCanvas(newWidth, newHeight); + const ctx = canvas.getContext("2d", { willReadFrequently: true }); + ctx.putImageData( + new ImageData(new Uint8ClampedArray(dest32.buffer), newWidth, newHeight), + 0, + 0 + ); + imgData.data = null; + imgData.bitmap = canvas.transferToImageBitmap(); + imgData.width = newWidth; + imgData.height = newHeight; + + return imgData; + } + _encodeBMP() { const { width, height, kind } = this._imgData; let data = this._imgData.data; diff --git a/src/core/image_utils.js b/src/core/image_utils.js index 9638d37efa3ee..a907d5ceb0394 100644 --- a/src/core/image_utils.js +++ b/src/core/image_utils.js @@ -13,12 +13,7 @@ * limitations under the License. */ -import { - assert, - MAX_IMAGE_SIZE_TO_CACHE, - unreachable, - warn, -} from "../shared/util.js"; +import { assert, unreachable, warn } from "../shared/util.js"; import { RefSet, RefSetCache } from "./primitives.js"; class BaseLocalCache { @@ -174,12 +169,32 @@ class RegionalImageCache extends BaseLocalCache { } } +class GlobalColorSpaceCache extends BaseLocalCache { + constructor(options) { + super({ onlyRefs: true }); + } + + set(name = null, ref, data) { + if (!ref) { + throw new Error('GlobalColorSpaceCache.set - expected "ref" argument.'); + } + if (this._imageCache.has(ref)) { + return; + } + this._imageCache.put(ref, data); + } + + clear() { + this._imageCache.clear(); + } +} + class GlobalImageCache { static NUM_PAGES_THRESHOLD = 2; static MIN_IMAGES_TO_CACHE = 10; - static MAX_BYTE_SIZE = 5 * MAX_IMAGE_SIZE_TO_CACHE; + static MAX_BYTE_SIZE = 5e7; // Fifty megabytes. #decodeFailedSet = new RefSet(); @@ -295,6 +310,7 @@ class GlobalImageCache { } export { + GlobalColorSpaceCache, GlobalImageCache, LocalColorSpaceCache, LocalFunctionCache, diff --git a/src/core/jbig2.js b/src/core/jbig2.js index cb50f0833a238..df689d9638de5 100644 --- a/src/core/jbig2.js +++ b/src/core/jbig2.js @@ -14,7 +14,14 @@ */ import { BaseException, shadow } from "../shared/util.js"; -import { log2, readInt8, readUint16, readUint32 } from "./core_utils.js"; +import { + log2, + MAX_INT_32, + MIN_INT_32, + readInt8, + readUint16, + readUint32, +} from "./core_utils.js"; import { ArithmeticDecoder } from "./arithmetic_decoder.js"; import { CCITTFaxDecoder } from "./ccitt.js"; @@ -52,9 +59,6 @@ class DecodingContext { } } -const MAX_INT_32 = 2 ** 31 - 1; -const MIN_INT_32 = -(2 ** 31); - // Annex A. Arithmetic Integer Decoding Procedure // A.2 Procedure for decoding values function decodeInteger(contextCache, procedure, decoder) { @@ -370,9 +374,7 @@ function decodeBitmap( // Sorting is non-standard, and it is not required. But sorting increases // the number of template bits that can be reused from the previous // contextLabel in the main loop. - template.sort(function (a, b) { - return a.y - b.y || a.x - b.x; - }); + template.sort((a, b) => a.y - b.y || a.x - b.x); const templateLength = template.length; const templateX = new Int8Array(templateLength); @@ -801,9 +803,7 @@ function decodeTextRegion( for (i = 0; i < height; i++) { row = new Uint8Array(width); if (defaultPixelValue) { - for (let j = 0; j < width; j++) { - row[j] = defaultPixelValue; - } + row.fill(defaultPixelValue); } bitmap.push(row); } @@ -1037,9 +1037,7 @@ function decodeHalftoneRegion( for (i = 0; i < regionHeight; i++) { row = new Uint8Array(regionWidth); if (defaultPixelValue) { - for (j = 0; j < regionWidth; j++) { - row[j] = defaultPixelValue; - } + row.fill(defaultPixelValue); } regionBitmap.push(row); } diff --git a/src/core/jpeg_stream.js b/src/core/jpeg_stream.js index 6cf2de2cba764..4af24eeb8e733 100644 --- a/src/core/jpeg_stream.js +++ b/src/core/jpeg_stream.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { shadow, warn } from "../shared/util.js"; +import { FeatureTest, shadow, warn } from "../shared/util.js"; import { DecodeStream } from "./decode_stream.js"; import { Dict } from "./primitives.js"; import { JpegImage } from "./jpg.js"; @@ -23,6 +23,8 @@ import { JpegImage } from "./jpg.js"; * like all the other DecodeStreams. */ class JpegStream extends DecodeStream { + static #isImageDecoderSupported = FeatureTest.isImageDecoderSupported; + constructor(stream, maybeLength, params) { super(maybeLength); @@ -36,14 +38,16 @@ class JpegStream extends DecodeStream { return shadow( this, "canUseImageDecoder", - // eslint-disable-next-line no-undef - typeof ImageDecoder === "undefined" - ? Promise.resolve(false) - : // eslint-disable-next-line no-undef - ImageDecoder.isTypeSupported("image/jpeg") + this.#isImageDecoderSupported + ? ImageDecoder.isTypeSupported("image/jpeg") + : Promise.resolve(false) ); } + static setOptions({ isImageDecoderSupported = false }) { + this.#isImageDecoderSupported = isImageDecoderSupported; + } + get bytes() { // If `this.maybeLength` is null, we'll get the entire stream. return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); @@ -159,11 +163,23 @@ class JpegStream extends DecodeStream { if (!bytes) { return null; } - const data = this.#skipUselessBytes(bytes); - if (!JpegImage.canUseImageDecoder(data, jpegOptions.colorTransform)) { + let data = this.#skipUselessBytes(bytes); + const useImageDecoder = JpegImage.canUseImageDecoder( + data, + jpegOptions.colorTransform + ); + if (!useImageDecoder) { return null; } - // eslint-disable-next-line no-undef + if (useImageDecoder.exifStart) { + // Replace the entire EXIF-block with dummy data, to ensure that a + // non-default EXIF orientation won't cause the image to be rotated + // when using `ImageDecoder` (fixes bug1942064.pdf). + // + // Copy the data first, to avoid modifying the original PDF document. + data = data.slice(); + data.fill(0x00, useImageDecoder.exifStart, useImageDecoder.exifEnd); + } decoder = new ImageDecoder({ data, type: "image/jpeg", diff --git a/src/core/jpg.js b/src/core/jpg.js index a7640a6d46dc1..26ff546e0ca49 100644 --- a/src/core/jpg.js +++ b/src/core/jpg.js @@ -782,8 +782,11 @@ function readDataBlock(data, offset) { } const array = data.subarray(offset, endOffset); - offset += array.length; - return { appData: array, newOffset: offset }; + return { + appData: array, + oldOffset: offset, + newOffset: offset + array.length, + }; } function skipData(data, offset) { @@ -805,6 +808,7 @@ class JpegImage { } static canUseImageDecoder(data, colorTransform = -1) { + let exifOffsets = null; let offset = 0; let numComponents = null; let fileMarker = readUint16(data, offset); @@ -817,6 +821,31 @@ class JpegImage { markerLoop: while (fileMarker !== /* EOI (End of Image) = */ 0xffd9) { switch (fileMarker) { + case 0xffe1: // APP1 - Exif + // TODO: Remove this once https://github.com/w3c/webcodecs/issues/870 + // is fixed. + const { appData, oldOffset, newOffset } = readDataBlock(data, offset); + offset = newOffset; + + // 'Exif\x00\x00' + if ( + appData[0] === 0x45 && + appData[1] === 0x78 && + appData[2] === 0x69 && + appData[3] === 0x66 && + appData[4] === 0 && + appData[5] === 0 + ) { + if (exifOffsets) { + throw new JpegError("Duplicate EXIF-blocks found."); + } + // Don't do the EXIF-block replacement here, see `JpegStream`, + // since that can modify the original PDF document. + exifOffsets = { exifStart: oldOffset + 6, exifEnd: newOffset }; + } + fileMarker = readUint16(data, offset); + offset += 2; + continue; case 0xffc0: // SOF0 (Start of Frame, Baseline DCT) case 0xffc1: // SOF1 (Start of Frame, Extended DCT) case 0xffc2: // SOF2 (Start of Frame, Progressive DCT) @@ -838,12 +867,12 @@ class JpegImage { offset += 2; } if (numComponents === 4) { - return false; + return null; } if (numComponents === 3 && colorTransform === 0) { - return false; + return null; } - return true; + return exifOffsets || {}; } parse(data, { dnlScanLines = null } = {}) { diff --git a/src/core/jpx.js b/src/core/jpx.js index 5cb4fec6f8f3b..8fd7c8b7f44a8 100644 --- a/src/core/jpx.js +++ b/src/core/jpx.js @@ -14,6 +14,7 @@ */ import { BaseException, warn } from "../shared/util.js"; +import { fetchBinaryData } from "./core_utils.js"; import OpenJPEG from "../../external/openjpeg/openjpeg.js"; import { Stream } from "./stream.js"; @@ -24,20 +25,129 @@ class JpxError extends BaseException { } class JpxImage { - static #module = null; - - static decode(data, decoderOptions) { - decoderOptions ||= {}; - this.#module ||= OpenJPEG({ warn }); - const imageData = this.#module.decode(data, decoderOptions); - if (typeof imageData === "string") { - throw new JpxError(imageData); + static #buffer = null; + + static #handler = null; + + static #modulePromise = null; + + static #useWasm = true; + + static #useWorkerFetch = true; + + static #wasmUrl = null; + + static setOptions({ handler, useWasm, useWorkerFetch, wasmUrl }) { + this.#useWasm = useWasm; + this.#useWorkerFetch = useWorkerFetch; + this.#wasmUrl = wasmUrl; + + if (!useWorkerFetch) { + this.#handler = handler; + } + } + + static async #getJsModule(fallbackCallback) { + const path = + typeof PDFJSDev === "undefined" + ? `../${this.#wasmUrl}openjpeg_nowasm_fallback.js` + : `${this.#wasmUrl}openjpeg_nowasm_fallback.js`; + + let instance = null; + try { + const mod = await (typeof PDFJSDev === "undefined" + ? import(path) // eslint-disable-line no-unsanitized/method + : __non_webpack_import__(path)); + instance = mod.default(); + } catch (e) { + warn(`JpxImage#getJsModule: ${e}`); + } + fallbackCallback(instance); + } + + static async #instantiateWasm(fallbackCallback, imports, successCallback) { + const filename = "openjpeg.wasm"; + try { + if (!this.#buffer) { + if (this.#useWorkerFetch) { + this.#buffer = await fetchBinaryData(`${this.#wasmUrl}${filename}`); + } else { + this.#buffer = await this.#handler.sendWithPromise( + "FetchBinaryData", + { type: "wasmFactory", filename } + ); + } + } + const results = await WebAssembly.instantiate(this.#buffer, imports); + return successCallback(results.instance); + } catch (reason) { + warn(`JpxImage#instantiateWasm: ${reason}`); + + this.#getJsModule(fallbackCallback); + return null; + } finally { + this.#handler = null; + } + } + + static async decode( + bytes, + { numComponents = 4, isIndexedColormap = false, smaskInData = false } = {} + ) { + if (!this.#modulePromise) { + const { promise, resolve } = Promise.withResolvers(); + const promises = [promise]; + if (!this.#useWasm) { + this.#getJsModule(resolve); + } else { + promises.push( + OpenJPEG({ + warn, + instantiateWasm: this.#instantiateWasm.bind(this, resolve), + }) + ); + } + this.#modulePromise = Promise.race(promises); + } + const module = await this.#modulePromise; + + if (!module) { + throw new JpxError("OpenJPEG failed to initialize"); + } + let ptr; + + try { + const size = bytes.length; + ptr = module._malloc(size); + module.HEAPU8.set(bytes, ptr); + const ret = module._jp2_decode( + ptr, + size, + numComponents > 0 ? numComponents : 0, + !!isIndexedColormap, + !!smaskInData + ); + if (ret) { + const { errorMessages } = module; + if (errorMessages) { + delete module.errorMessages; + throw new JpxError(errorMessages); + } + throw new JpxError("Unknown error"); + } + const { imageData } = module; + module.imageData = null; + + return imageData; + } finally { + if (ptr) { + module._free(ptr); + } } - return imageData; } static cleanup() { - this.#module = null; + this.#modulePromise = null; } static parseImageProperties(stream) { diff --git a/src/core/jpx_stream.js b/src/core/jpx_stream.js index 09a79a44579e2..e454d58bd9754 100644 --- a/src/core/jpx_stream.js +++ b/src/core/jpx_stream.js @@ -13,9 +13,9 @@ * limitations under the License. */ +import { shadow, unreachable } from "../shared/util.js"; import { DecodeStream } from "./decode_stream.js"; import { JpxImage } from "./jpx.js"; -import { shadow } from "../shared/util.js"; /** * For JPEG 2000's we use a library to decode these images and @@ -42,15 +42,19 @@ class JpxStream extends DecodeStream { } readBlock(decoderOptions) { - this.decodeImage(null, decoderOptions); + unreachable("JpxStream.readBlock"); } - decodeImage(bytes, decoderOptions) { + get isAsyncDecoder() { + return true; + } + + async decodeImage(bytes, decoderOptions) { if (this.eof) { return this.buffer; } bytes ||= this.bytes; - this.buffer = JpxImage.decode(bytes, decoderOptions); + this.buffer = await JpxImage.decode(bytes, decoderOptions); this.bufferLength = this.buffer.length; this.eof = true; diff --git a/src/core/operator_list.js b/src/core/operator_list.js index 2fdc1bb9179e3..1212e6e939461 100644 --- a/src/core/operator_list.js +++ b/src/core/operator_list.js @@ -636,7 +636,11 @@ class OperatorList { } } - addImageOps(fn, args, optionalContent) { + addImageOps(fn, args, optionalContent, hasMask = false) { + if (hasMask) { + this.addOp(OPS.save); + this.addOp(OPS.setGState, [[["SMask", false]]]); + } if (optionalContent !== undefined) { this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]); } @@ -646,6 +650,9 @@ class OperatorList { if (optionalContent !== undefined) { this.addOp(OPS.endMarkedContent, []); } + if (hasMask) { + this.addOp(OPS.restore); + } } addDependency(dependency) { diff --git a/src/core/parser.js b/src/core/parser.js index 94b2e7894cbcd..50ea563dbad31 100644 --- a/src/core/parser.js +++ b/src/core/parser.js @@ -248,8 +248,12 @@ class Parser { } // Check that the "EI" sequence isn't part of the image data, since // that would cause the image to be truncated (fixes issue11124.pdf). + // + // Check more than the `followingBytes` to be able to find operators + // with multiple arguments, e.g. transform (cm) with decimal arguments + // (fixes issue19494.pdf). const tmpLexer = new Lexer( - new Stream(followingBytes.slice()), + new Stream(stream.peekBytes(5 * n)), knownCommands ); // Reduce the number of (potential) warning messages. diff --git a/src/core/pattern.js b/src/core/pattern.js index 38fb16c20370a..7257a67f88a0b 100644 --- a/src/core/pattern.js +++ b/src/core/pattern.js @@ -52,6 +52,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ) { const dict = shading instanceof BaseStream ? shading.dict : shading; @@ -66,6 +67,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ); case ShadingType.FREE_FORM_MESH: @@ -77,6 +79,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ); default: @@ -114,7 +117,14 @@ class BaseShading { // Radial and axial shading have very similar implementations // If needed, the implementations can be broken into two classes. class RadialAxialShading extends BaseShading { - constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) { + constructor( + dict, + xref, + resources, + pdfFunctionFactory, + globalColorSpaceCache, + localColorSpaceCache + ) { super(); this.shadingType = dict.get("ShadingType"); let coordsLen = 0; @@ -132,6 +142,7 @@ class RadialAxialShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.bbox = lookupNormalRect(dict.getArray("BBox"), null); @@ -340,24 +351,19 @@ class MeshStreamReader { } readBits(n) { - let buffer = this.buffer; - let bufferLength = this.bufferLength; + const { stream } = this; + let { buffer, bufferLength } = this; + if (n === 32) { if (bufferLength === 0) { - return ( - ((this.stream.getByte() << 24) | - (this.stream.getByte() << 16) | - (this.stream.getByte() << 8) | - this.stream.getByte()) >>> - 0 - ); + return stream.getInt32() >>> 0; } buffer = (buffer << 24) | - (this.stream.getByte() << 16) | - (this.stream.getByte() << 8) | - this.stream.getByte(); - const nextByte = this.stream.getByte(); + (stream.getByte() << 16) | + (stream.getByte() << 8) | + stream.getByte(); + const nextByte = stream.getByte(); this.buffer = nextByte & ((1 << bufferLength) - 1); return ( ((buffer << (8 - bufferLength)) | @@ -366,10 +372,10 @@ class MeshStreamReader { ); } if (n === 8 && bufferLength === 0) { - return this.stream.getByte(); + return stream.getByte(); } while (bufferLength < n) { - buffer = (buffer << 8) | this.stream.getByte(); + buffer = (buffer << 8) | stream.getByte(); bufferLength += 8; } bufferLength -= n; @@ -457,6 +463,7 @@ class MeshShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ) { super(); @@ -471,6 +478,7 @@ class MeshShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.background = dict.has("Background") diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index 35d34cb537095..02c4e58b40f99 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -20,6 +20,9 @@ import { warn, } from "../shared/util.js"; import { ChunkedStreamManager } from "./chunked_stream.js"; +import { ImageResizer } from "./image_resizer.js"; +import { JpegStream } from "./jpeg_stream.js"; +import { JpxImage } from "./jpx.js"; import { MissingDataException } from "./core_utils.js"; import { PDFDocument } from "./document.js"; import { Stream } from "./stream.js"; @@ -36,23 +39,41 @@ function parseDocBaseUrl(url) { } class BasePdfManager { - constructor(args) { + constructor({ + // source, + // disableAutoFetch, + docBaseUrl, + docId, + enableXfa, + evaluatorOptions, + handler, + // length, + password, + // rangeChunkSize, + }) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && this.constructor === BasePdfManager ) { unreachable("Cannot initialize BasePdfManager."); } - this._docBaseUrl = parseDocBaseUrl(args.docBaseUrl); - this._docId = args.docId; - this._password = args.password; - this.enableXfa = args.enableXfa; - - // Check `OffscreenCanvas` support once, rather than repeatedly throughout - // the worker-thread code. - args.evaluatorOptions.isOffscreenCanvasSupported &&= + this._docBaseUrl = parseDocBaseUrl(docBaseUrl); + this._docId = docId; + this._password = password; + this.enableXfa = enableXfa; + + // Check `OffscreenCanvas` and `ImageDecoder` support once, + // rather than repeatedly throughout the worker-thread code. + evaluatorOptions.isOffscreenCanvasSupported &&= FeatureTest.isOffscreenCanvasSupported; - this.evaluatorOptions = Object.freeze(args.evaluatorOptions); + evaluatorOptions.isImageDecoderSupported &&= + FeatureTest.isImageDecoderSupported; + this.evaluatorOptions = Object.freeze(evaluatorOptions); + + // Initialize image-options once per document. + ImageResizer.setOptions(evaluatorOptions); + JpegStream.setOptions(evaluatorOptions); + JpxImage.setOptions({ ...evaluatorOptions, handler }); } get docId() { diff --git a/src/core/primitives.js b/src/core/primitives.js index a6801935de2fe..c48a34eb71c20 100644 --- a/src/core/primitives.js +++ b/src/core/primitives.js @@ -69,7 +69,7 @@ const nonSerializable = function nonSerializableClosure() { class Dict { constructor(xref = null) { // Map should only be used internally, use functions below to access. - this._map = Object.create(null); + this._map = new Map(); this.xref = xref; this.objId = null; this.suppressEncryption = false; @@ -81,12 +81,12 @@ class Dict { } get size() { - return Object.keys(this._map).length; + return this._map.size; } // Automatically dereferences Ref objects. get(key1, key2, key3) { - let value = this._map[key1]; + let value = this._map.get(key1); if (value === undefined && key2 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -94,7 +94,7 @@ class Dict { ) { unreachable("Dict.get: Expected keys to be ordered by length."); } - value = this._map[key2]; + value = this._map.get(key2); if (value === undefined && key3 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -102,7 +102,7 @@ class Dict { ) { unreachable("Dict.get: Expected keys to be ordered by length."); } - value = this._map[key3]; + value = this._map.get(key3); } } if (value instanceof Ref && this.xref) { @@ -113,7 +113,7 @@ class Dict { // Same as get(), but returns a promise and uses fetchIfRefAsync(). async getAsync(key1, key2, key3) { - let value = this._map[key1]; + let value = this._map.get(key1); if (value === undefined && key2 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -121,7 +121,7 @@ class Dict { ) { unreachable("Dict.getAsync: Expected keys to be ordered by length."); } - value = this._map[key2]; + value = this._map.get(key2); if (value === undefined && key3 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -129,7 +129,7 @@ class Dict { ) { unreachable("Dict.getAsync: Expected keys to be ordered by length."); } - value = this._map[key3]; + value = this._map.get(key3); } } if (value instanceof Ref && this.xref) { @@ -140,7 +140,7 @@ class Dict { // Same as get(), but dereferences all elements if the result is an Array. getArray(key1, key2, key3) { - let value = this._map[key1]; + let value = this._map.get(key1); if (value === undefined && key2 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -148,7 +148,7 @@ class Dict { ) { unreachable("Dict.getArray: Expected keys to be ordered by length."); } - value = this._map[key2]; + value = this._map.get(key2); if (value === undefined && key3 !== undefined) { if ( (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && @@ -156,7 +156,7 @@ class Dict { ) { unreachable("Dict.getArray: Expected keys to be ordered by length."); } - value = this._map[key3]; + value = this._map.get(key3); } } if (value instanceof Ref && this.xref) { @@ -176,16 +176,16 @@ class Dict { // No dereferencing. getRaw(key) { - return this._map[key]; + return this._map.get(key); } getKeys() { - return Object.keys(this._map); + return [...this._map.keys()]; } // No dereferencing. getRawValues() { - return Object.values(this._map); + return [...this._map.values()]; } set(key, value) { @@ -196,16 +196,21 @@ class Dict { unreachable('Dict.set: The "value" cannot be undefined.'); } } - this._map[key] = value; + this._map.set(key, value); } has(key) { - return this._map[key] !== undefined; + return this._map.has(key); } - forEach(callback) { - for (const key in this._map) { - callback(key, this.get(key)); + *[Symbol.iterator]() { + for (const [key, value] of this._map) { + yield [ + key, + value instanceof Ref && this.xref + ? this.xref.fetch(value, this.suppressEncryption) + : value, + ]; } } @@ -226,7 +231,7 @@ class Dict { if (!(dict instanceof Dict)) { continue; } - for (const [key, value] of Object.entries(dict._map)) { + for (const [key, value] of dict._map) { let property = properties.get(key); if (property === undefined) { property = []; @@ -242,20 +247,20 @@ class Dict { } for (const [name, values] of properties) { if (values.length === 1 || !(values[0] instanceof Dict)) { - mergedDict._map[name] = values[0]; + mergedDict._map.set(name, values[0]); continue; } const subDict = new Dict(xref); for (const dict of values) { - for (const [key, value] of Object.entries(dict._map)) { - if (subDict._map[key] === undefined) { - subDict._map[key] = value; + for (const [key, value] of dict._map) { + if (!subDict._map.has(key)) { + subDict._map.set(key, value); } } } if (subDict.size > 0) { - mergedDict._map[name] = subDict; + mergedDict._map.set(name, subDict); } } properties.clear(); @@ -383,6 +388,10 @@ class RefSetCache { this._map.clear(); } + *values() { + yield* this._map.values(); + } + *items() { for (const [ref, value] of this._map) { yield [Ref.fromString(ref), value]; diff --git a/src/core/run_length_stream.js b/src/core/run_length_stream.js index 8df345fa7ea4b..72e66daa23d5a 100644 --- a/src/core/run_length_stream.js +++ b/src/core/run_length_stream.js @@ -48,11 +48,9 @@ class RunLengthStream extends DecodeStream { } } else { n = 257 - n; - const b = repeatHeader[1]; buffer = this.ensureBuffer(bufferLength + n + 1); - for (let i = 0; i < n; i++) { - buffer[bufferLength++] = b; - } + buffer.fill(repeatHeader[1], bufferLength, bufferLength + n); + bufferLength += n; } this.bufferLength = bufferLength; } diff --git a/src/core/standard_fonts.js b/src/core/standard_fonts.js index 56212b9019716..59cc5865df1b6 100644 --- a/src/core/standard_fonts.js +++ b/src/core/standard_fonts.js @@ -77,6 +77,7 @@ const getStdFontMap = getLookupTableFactory(function (t) { t.CourierNewPSMT = "Courier"; t["Helvetica-BoldItalic"] = "Helvetica-BoldOblique"; t["Helvetica-Italic"] = "Helvetica-Oblique"; + t["HelveticaLTStd-Bold"] = "Helvetica-Bold"; t["Symbol-Bold"] = "Symbol"; t["Symbol-BoldItalic"] = "Symbol"; t["Symbol-Italic"] = "Symbol"; @@ -542,6 +543,37 @@ const getGlyphMapForStandardFonts = getLookupTableFactory(function (t) { t[337] = 9552; t[493] = 1039; t[494] = 1040; + t[570] = 1040; + t[571] = 1041; + t[572] = 1042; + t[573] = 1043; + t[574] = 1044; + t[575] = 1045; + t[576] = 1046; + t[577] = 1047; + t[578] = 1048; + t[579] = 1049; + t[580] = 1050; + t[581] = 1051; + t[582] = 1052; + t[583] = 1053; + t[584] = 1054; + t[585] = 1055; + t[586] = 1056; + t[587] = 1057; + t[588] = 1058; + t[589] = 1059; + t[590] = 1060; + t[591] = 1061; + t[592] = 1062; + t[593] = 1063; + t[594] = 1064; + t[595] = 1065; + t[596] = 1066; + t[597] = 1067; + t[598] = 1068; + t[599] = 1069; + t[600] = 1070; t[672] = 1488; t[673] = 1489; t[674] = 1490; diff --git a/src/core/struct_tree.js b/src/core/struct_tree.js index 44e7e5b9ca5b4..fa27a14c00ef3 100644 --- a/src/core/struct_tree.js +++ b/src/core/struct_tree.js @@ -17,7 +17,6 @@ import { AnnotationPrefix, stringToPDFString, warn } from "../shared/util.js"; import { Dict, isName, Name, Ref, RefSetCache } from "./primitives.js"; import { lookupNormalRect, stringToAsciiOrUTF16BE } from "./core_utils.js"; import { NumberTree } from "./name_number_tree.js"; -import { writeObject } from "./writer.js"; const MAX_DEPTH = 40; @@ -63,12 +62,11 @@ class StructTreeRoot { if (!(roleMapDict instanceof Dict)) { return; } - roleMapDict.forEach((key, value) => { - if (!(value instanceof Name)) { - return; + for (const [key, value] of roleMapDict) { + if (value instanceof Name) { + this.roleMap.set(key, value.name); } - this.roleMap.set(key, value.name); - }); + } } static async canCreateStructureTree({ @@ -117,7 +115,7 @@ class StructTreeRoot { xref, catalogRef, pdfManager, - newRefs, + changes, }) { const root = pdfManager.catalog.cloneDict(); const cache = new RefSetCache(); @@ -146,18 +144,17 @@ class StructTreeRoot { nums, xref, pdfManager, - newRefs, + changes, cache, }); structTreeRoot.set("ParentTreeNextKey", nextKey); cache.put(parentTreeRef, parentTree); - const buffer = []; for (const [ref, obj] of cache.items()) { - buffer.length = 0; - await writeObject(ref, obj, buffer, xref); - newRefs.push({ ref, data: buffer.join("") }); + changes.put(ref, { + data: obj, + }); } } @@ -235,7 +232,7 @@ class StructTreeRoot { return true; } - async updateStructureTree({ newAnnotationsByPage, pdfManager, newRefs }) { + async updateStructureTree({ newAnnotationsByPage, pdfManager, changes }) { const xref = this.dict.xref; const structTreeRoot = this.dict.clone(); const structTreeRootRef = this.ref; @@ -273,7 +270,7 @@ class StructTreeRoot { nums, xref, pdfManager, - newRefs, + changes, cache, }); @@ -288,11 +285,10 @@ class StructTreeRoot { cache.put(numsRef, nums); } - const buffer = []; for (const [ref, obj] of cache.items()) { - buffer.length = 0; - await writeObject(ref, obj, buffer, xref); - newRefs.push({ ref, data: buffer.join("") }); + changes.put(ref, { + data: obj, + }); } } @@ -304,13 +300,12 @@ class StructTreeRoot { nums, xref, pdfManager, - newRefs, + changes, cache, }) { const objr = Name.get("OBJR"); let nextKey = -1; let structTreePageObjs; - const buffer = []; for (const [pageIndex, elements] of newAnnotationsByPage) { const page = await pdfManager.getPage(pageIndex); @@ -350,9 +345,9 @@ class StructTreeRoot { // We update the existing tag. const tagDict = xref.fetch(objRef).clone(); StructTreeRoot.#writeProperties(tagDict, accessibilityData); - buffer.length = 0; - await writeObject(objRef, tagDict, buffer, xref); - newRefs.push({ ref: objRef, data: buffer.join("") }); + changes.put(objRef, { + data: tagDict, + }); continue; } } diff --git a/src/core/to_unicode_map.js b/src/core/to_unicode_map.js index 24bd8fe7d977c..e1a0e5c9b34a4 100644 --- a/src/core/to_unicode_map.js +++ b/src/core/to_unicode_map.js @@ -28,7 +28,7 @@ class ToUnicodeMap { forEach(callback) { for (const charCode in this._map) { - callback(charCode, this._map[charCode].charCodeAt(0)); + callback(charCode, this._map[charCode].codePointAt(0)); } } diff --git a/src/core/worker.js b/src/core/worker.js index a8e0c42306233..3015e4d4de8ef 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -18,14 +18,10 @@ import { assert, getVerbosityLevel, info, - InvalidPDFException, isNodeJS, - MissingPDFException, PasswordException, setVerbosityLevel, stringToPDFString, - UnexpectedResponseException, - UnknownErrorException, VerbosityLevel, warn, } from "../shared/util.js"; @@ -34,12 +30,12 @@ import { getNewAnnotationsMap, XRefParseException, } from "./core_utils.js"; -import { Dict, isDict, Ref } from "./primitives.js"; +import { Dict, isDict, Ref, RefSetCache } from "./primitives.js"; import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js"; +import { MessageHandler, wrapReason } from "../shared/message_handler.js"; import { AnnotationFactory } from "./annotation.js"; import { clearGlobalCaches } from "./cleanup_helper.js"; import { incrementalUpdate } from "./writer.js"; -import { MessageHandler } from "../shared/message_handler.js"; import { PDFWorkerStream } from "./worker_stream.js"; import { StructTreeRoot } from "./struct_tree.js"; @@ -70,9 +66,23 @@ class WorkerTask { } class WorkerMessageHandler { + static { + // Worker thread (and not Node.js)? + if ( + typeof window === "undefined" && + !isNodeJS && + typeof self !== "undefined" && + /* isMessagePort = */ + typeof self.postMessage === "function" && + "onmessage" in self + ) { + this.initializeFromPort(self); + } + } + static setup(handler, port) { let testMessageProcessed = false; - handler.on("test", function (data) { + handler.on("test", data => { if (testMessageProcessed) { return; // we already processed 'test' message once } @@ -82,13 +92,11 @@ class WorkerMessageHandler { handler.send("test", data instanceof Uint8Array); }); - handler.on("configure", function (data) { + handler.on("configure", data => { setVerbosityLevel(data.verbosity); }); - handler.on("GetDocRequest", function (data) { - return WorkerMessageHandler.createDocumentHandler(data, port); - }); + handler.on("GetDocRequest", data => this.createDocumentHandler(data, port)); } static createDocumentHandler(docParams, port) { @@ -165,6 +173,7 @@ class WorkerMessageHandler { if (isPureXfa) { const task = new WorkerTask("loadXfaFonts"); startWorkerTask(task); + await Promise.all([ pdfManager .loadXfaFonts(handler, task) @@ -189,7 +198,7 @@ class WorkerMessageHandler { return { numPages, fingerprints, htmlForXfa }; } - function getPdfManager({ + async function getPdfManager({ data, password, disableAutoFetch, @@ -211,31 +220,20 @@ class WorkerMessageHandler { password, rangeChunkSize, }; - const pdfManagerCapability = Promise.withResolvers(); - let newPdfManager; if (data) { - try { - pdfManagerArgs.source = data; + pdfManagerArgs.source = data; - newPdfManager = new LocalPdfManager(pdfManagerArgs); - pdfManagerCapability.resolve(newPdfManager); - } catch (ex) { - pdfManagerCapability.reject(ex); - } - return pdfManagerCapability.promise; + return new LocalPdfManager(pdfManagerArgs); } + const pdfStream = new PDFWorkerStream(handler), + fullRequest = pdfStream.getFullReader(); - let pdfStream, - cachedChunks = []; - try { - pdfStream = new PDFWorkerStream(handler); - } catch (ex) { - pdfManagerCapability.reject(ex); - return pdfManagerCapability.promise; - } + const pdfManagerCapability = Promise.withResolvers(); + let newPdfManager, + cachedChunks = [], + loaded = 0; - const fullRequest = pdfStream.getFullReader(); fullRequest.headersReady .then(function () { if (!fullRequest.isRangeSupported) { @@ -263,30 +261,22 @@ class WorkerMessageHandler { cancelXHRs = null; }); - let loaded = 0; - const flushChunks = function () { - const pdfFile = arrayBuffersToBytes(cachedChunks); - if (length && pdfFile.length !== length) { - warn("reported HTTP length is different from actual"); - } - // the data is array, instantiating directly from it - try { - pdfManagerArgs.source = pdfFile; - - newPdfManager = new LocalPdfManager(pdfManagerArgs); - pdfManagerCapability.resolve(newPdfManager); - } catch (ex) { - pdfManagerCapability.reject(ex); - } - cachedChunks = []; - }; new Promise(function (resolve, reject) { const readChunk = function ({ value, done }) { try { ensureNotTerminated(); if (done) { if (!newPdfManager) { - flushChunks(); + const pdfFile = arrayBuffersToBytes(cachedChunks); + cachedChunks = []; + + if (length && pdfFile.length !== length) { + warn("reported HTTP length is different from actual"); + } + pdfManagerArgs.source = pdfFile; + + newPdfManager = new LocalPdfManager(pdfManagerArgs); + pdfManagerCapability.resolve(newPdfManager); } cancelXHRs = null; return; @@ -322,7 +312,7 @@ class WorkerMessageHandler { cancelXHRs = null; }); - cancelXHRs = function (reason) { + cancelXHRs = reason => { pdfStream.cancelAllRequests(reason); }; @@ -353,18 +343,9 @@ class WorkerMessageHandler { finishWorkerTask(task); handler.send("DocException", ex); }); - } else if ( - ex instanceof InvalidPDFException || - ex instanceof MissingPDFException || - ex instanceof UnexpectedResponseException || - ex instanceof UnknownErrorException - ) { - handler.send("DocException", ex); } else { - handler.send( - "DocException", - new UnknownErrorException(ex.message, ex.toString()) - ); + // Ensure that we always fallback to `UnknownErrorException`. + handler.send("DocException", wrapReason(ex)); } } @@ -469,9 +450,9 @@ class WorkerMessageHandler { }); handler.on("GetPageJSActions", function ({ pageIndex }) { - return pdfManager.getPage(pageIndex).then(function (page) { - return pdfManager.ensure(page, "jsActions"); - }); + return pdfManager + .getPage(pageIndex) + .then(page => pdfManager.ensure(page, "jsActions")); }); handler.on("GetOutline", function (data) { @@ -498,9 +479,7 @@ class WorkerMessageHandler { }); handler.on("GetData", function (data) { - return pdfManager.requestLoadedStream().then(function (stream) { - return stream.bytes; - }); + return pdfManager.requestLoadedStream().then(stream => stream.bytes); }); handler.on("GetAnnotations", function ({ pageIndex, intent }) { @@ -547,6 +526,7 @@ class WorkerMessageHandler { pdfManager.ensureDoc("linearization"), pdfManager.ensureCatalog("structTreeRoot"), ]; + const changes = new RefSetCache(); const promises = []; const newAnnotationsByPage = !isPureXfa @@ -596,8 +576,16 @@ class WorkerMessageHandler { newAnnotationPromises.push( pdfManager.getPage(pageIndex).then(page => { const task = new WorkerTask(`Save (editor): page ${pageIndex}`); + startWorkerTask(task); + return page - .saveNewAnnotations(handler, task, annotations, imagePromises) + .saveNewAnnotations( + handler, + task, + annotations, + imagePromises, + changes + ) .finally(function () { finishWorkerTask(task); }); @@ -607,26 +595,24 @@ class WorkerMessageHandler { if (structTreeRoot === null) { // No structTreeRoot exists, so we need to create one. promises.push( - Promise.all(newAnnotationPromises).then(async newRefs => { + Promise.all(newAnnotationPromises).then(async () => { await StructTreeRoot.createStructureTree({ newAnnotationsByPage, xref, catalogRef, pdfManager, - newRefs, + changes, }); - return newRefs; }) ); } else if (structTreeRoot) { promises.push( - Promise.all(newAnnotationPromises).then(async newRefs => { + Promise.all(newAnnotationPromises).then(async () => { await structTreeRoot.updateStructureTree({ newAnnotationsByPage, pdfManager, - newRefs, + changes, }); - return newRefs; }) ); } @@ -639,8 +625,10 @@ class WorkerMessageHandler { promises.push( pdfManager.getPage(pageIndex).then(function (page) { const task = new WorkerTask(`Save: page ${pageIndex}`); + startWorkerTask(task); + return page - .save(handler, task, annotationStorage) + .save(handler, task, annotationStorage, changes) .finally(function () { finishWorkerTask(task); }); @@ -650,26 +638,21 @@ class WorkerMessageHandler { } const refs = await Promise.all(promises); - let newRefs = []; let xfaData = null; if (isPureXfa) { xfaData = refs[0]; if (!xfaData) { return stream.bytes; } - } else { - newRefs = refs.flat(2); - - if (newRefs.length === 0) { - // No new refs so just return the initial bytes - return stream.bytes; - } + } else if (changes.size === 0) { + // No new refs so just return the initial bytes + return stream.bytes; } const needAppearances = acroFormRef && acroForm instanceof Dict && - newRefs.some(ref => ref.needAppearances); + changes.values().some(ref => ref.needAppearances); const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null; let xfaDatasetsRef = null; @@ -695,11 +678,11 @@ class WorkerMessageHandler { const infoObj = Object.create(null); const xrefInfo = xref.trailer.get("Info") || null; if (xrefInfo instanceof Dict) { - xrefInfo.forEach((key, value) => { + for (const [key, value] of xrefInfo) { if (typeof value === "string") { infoObj[key] = stringToPDFString(value); } - }); + } } newXrefInfo = { @@ -719,7 +702,7 @@ class WorkerMessageHandler { return incrementalUpdate({ originalData: stream.bytes, xrefInfo: newXrefInfo, - newRefs, + changes, xref, hasXfa: !!xfa, xfaDatasetsRef, @@ -827,9 +810,9 @@ class WorkerMessageHandler { }); handler.on("GetStructTree", function (data) { - return pdfManager.getPage(data.pageIndex).then(function (page) { - return pdfManager.ensure(page, "getStructTree"); - }); + return pdfManager + .getPage(data.pageIndex) + .then(page => pdfManager.ensure(page, "getStructTree")); }); handler.on("FontFallback", function (data) { @@ -854,9 +837,7 @@ class WorkerMessageHandler { } else { clearGlobalCaches(); } - if (cancelXHRs) { - cancelXHRs(new AbortException("Worker was terminated.")); - } + cancelXHRs?.(new AbortException("Worker was terminated.")); for (const task of WorkerTasks) { waitOn.push(task.finished); @@ -889,9 +870,9 @@ class WorkerMessageHandler { return pdfManager.ensureDoc("startXRef"); }); handler.on("GetAnnotArray", function (data) { - return pdfManager.getPage(data.pageIndex).then(function (page) { - return page.annotations.map(a => a.toString()); - }); + return pdfManager + .getPage(data.pageIndex) + .then(page => page.annotations.map(a => a.toString())); }); } @@ -900,25 +881,9 @@ class WorkerMessageHandler { static initializeFromPort(port) { const handler = new MessageHandler("worker", "main", port); - WorkerMessageHandler.setup(handler, port); + this.setup(handler, port); handler.send("ready", null); } } -function isMessagePort(maybePort) { - return ( - typeof maybePort.postMessage === "function" && "onmessage" in maybePort - ); -} - -// Worker thread (and not Node.js)? -if ( - typeof window === "undefined" && - !isNodeJS && - typeof self !== "undefined" && - isMessagePort(self) -) { - WorkerMessageHandler.initializeFromPort(self); -} - export { WorkerMessageHandler, WorkerTask }; diff --git a/src/core/writer.js b/src/core/writer.js index 54af13b4920aa..a9095d97b4b82 100644 --- a/src/core/writer.js +++ b/src/core/writer.js @@ -23,9 +23,9 @@ import { parseXFAPath, } from "./core_utils.js"; import { SimpleDOMNode, SimpleXMLParser } from "./xml_parser.js"; +import { Stream, StringStream } from "./stream.js"; import { BaseStream } from "./base_stream.js"; import { calculateMD5 } from "./crypto.js"; -import { Stream } from "./stream.js"; async function writeObject(ref, obj, buffer, { encrypt = null }) { const transform = encrypt?.createCipherTransform(ref.num, ref.gen); @@ -192,10 +192,10 @@ function computeMD5(filesize, xrefInfo) { return bytesToString(calculateMD5(array)); } -function writeXFADataForAcroform(str, newRefs) { +function writeXFADataForAcroform(str, changes) { const xml = new SimpleXMLParser({ hasAttributes: true }).parseFromString(str); - for (const { xfa } of newRefs) { + for (const { xfa } of changes) { if (!xfa) { continue; } @@ -230,7 +230,7 @@ async function updateAcroform({ hasXfaDatasetsEntry, xfaDatasetsRef, needAppearances, - newRefs, + changes, }) { if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) { warn("XFA - Cannot save it"); @@ -257,33 +257,23 @@ async function updateAcroform({ dict.set("NeedAppearances", true); } - const buffer = []; - await writeObject(acroFormRef, dict, buffer, xref); - - newRefs.push({ ref: acroFormRef, data: buffer.join("") }); + changes.put(acroFormRef, { + data: dict, + }); } -function updateXFA({ xfaData, xfaDatasetsRef, newRefs, xref }) { +function updateXFA({ xfaData, xfaDatasetsRef, changes, xref }) { if (xfaData === null) { const datasets = xref.fetchIfRef(xfaDatasetsRef); - xfaData = writeXFADataForAcroform(datasets.getString(), newRefs); + xfaData = writeXFADataForAcroform(datasets.getString(), changes); } + const xfaDataStream = new StringStream(xfaData); + xfaDataStream.dict = new Dict(xref); + xfaDataStream.dict.set("Type", Name.get("EmbeddedFile")); - const encrypt = xref.encrypt; - if (encrypt) { - const transform = encrypt.createCipherTransform( - xfaDatasetsRef.num, - xfaDatasetsRef.gen - ); - xfaData = transform.encryptString(xfaData); - } - const data = - `${xfaDatasetsRef.num} ${xfaDatasetsRef.gen} obj\n` + - `<< /Type /EmbeddedFile /Length ${xfaData.length}>>\nstream\n` + - xfaData + - "\nendstream\nendobj\n"; - - newRefs.push({ ref: xfaDatasetsRef, data }); + changes.put(xfaDatasetsRef, { + data: xfaDataStream, + }); } async function getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer) { @@ -383,12 +373,12 @@ function computeIDs(baseOffset, xrefInfo, newXref) { } } -function getTrailerDict(xrefInfo, newRefs, useXrefStream) { +function getTrailerDict(xrefInfo, changes, useXrefStream) { const newXref = new Dict(null); newXref.set("Prev", xrefInfo.startXRef); const refForXrefTable = xrefInfo.newRef; if (useXrefStream) { - newRefs.push({ ref: refForXrefTable, data: "" }); + changes.put(refForXrefTable, { data: "" }); newXref.set("Size", refForXrefTable.num + 1); newXref.set("Type", Name.get("XRef")); } else { @@ -406,10 +396,24 @@ function getTrailerDict(xrefInfo, newRefs, useXrefStream) { return newXref; } +async function writeChanges(changes, xref, buffer = []) { + const newRefs = []; + for (const [ref, { data }] of changes.items()) { + if (data === null || typeof data === "string") { + newRefs.push({ ref, data }); + continue; + } + await writeObject(ref, data, buffer, xref); + newRefs.push({ ref, data: buffer.join("") }); + buffer.length = 0; + } + return newRefs.sort((a, b) => /* compare the refs */ a.ref.num - b.ref.num); +} + async function incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, xref = null, hasXfa = false, xfaDatasetsRef = null, @@ -428,19 +432,21 @@ async function incrementalUpdate({ hasXfaDatasetsEntry, xfaDatasetsRef, needAppearances, - newRefs, + changes, }); if (hasXfa) { updateXFA({ xfaData, xfaDatasetsRef, - newRefs, + changes, xref, }); } + const newXref = getTrailerDict(xrefInfo, changes, useXrefStream); const buffer = []; + const newRefs = await writeChanges(changes, xref, buffer); let baseOffset = originalData.length; const lastByte = originalData.at(-1); if (lastByte !== /* \n */ 0x0a && lastByte !== /* \r */ 0x0d) { @@ -449,10 +455,6 @@ async function incrementalUpdate({ baseOffset += 1; } - const newXref = getTrailerDict(xrefInfo, newRefs, useXrefStream); - newRefs = newRefs.sort( - (a, b) => /* compare the refs */ a.ref.num - b.ref.num - ); for (const { data } of newRefs) { if (data !== null) { buffer.push(data); @@ -482,4 +484,4 @@ async function incrementalUpdate({ return array; } -export { incrementalUpdate, writeDict, writeObject }; +export { incrementalUpdate, writeChanges, writeDict, writeObject }; diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 8ec158ff888fe..4fd5031d668dd 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -90,6 +90,7 @@ import { XFAObject, XFAObjectArray, } from "./xfa_object.js"; +import { fromBase64Util, Util, warn } from "../../shared/util.js"; import { getBBox, getColor, @@ -102,7 +103,6 @@ import { getStringOption, HTMLResult, } from "./utils.js"; -import { stringToBytes, Util, warn } from "../../shared/util.js"; import { getMetrics } from "./fonts.js"; import { recoverJsURL } from "../core_utils.js"; import { searchNode } from "./som.js"; @@ -1421,7 +1421,7 @@ class ChoiceList extends XFAObject { const field = ui[$getParent](); const fontSize = field.font?.size || 10; const optionStyle = { - fontSize: `calc(${fontSize}px * var(--scale-factor))`, + fontSize: `calc(${fontSize}px * var(--total-scale-factor))`, }; const children = []; @@ -3427,7 +3427,7 @@ class Image extends StringObject { } if (!buffer && this.transferEncoding === "base64") { - buffer = stringToBytes(atob(this[$content])); + buffer = fromBase64Util(this[$content]); } if (!buffer) { @@ -4317,7 +4317,7 @@ class Para extends XFAObject { style.paddingLeft = measureToString(this.marginLeft); } if (this.marginRight !== "") { - style.paddingight = measureToString(this.marginRight); + style.paddingRight = measureToString(this.marginRight); } if (this.spaceAbove !== "") { style.paddingTop = measureToString(this.spaceAbove); diff --git a/src/core/xfa/xhtml.js b/src/core/xfa/xhtml.js index 3e9847c7beb88..c7db14c5d2f13 100644 --- a/src/core/xfa/xhtml.js +++ b/src/core/xfa/xhtml.js @@ -94,7 +94,8 @@ const StyleMapping = new Map([ [ "font-size", (value, original) => { - value = original.fontSize = getMeasurement(value); + // The font size must be positive. + value = original.fontSize = Math.abs(getMeasurement(value)); return measureToString(0.99 * value); }, ], @@ -178,7 +179,7 @@ function mapStyle(styleStr, node, richText) { } if (richText && style.fontSize) { - style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`; + style.fontSize = `calc(${style.fontSize} * var(--total-scale-factor))`; } fixTextIndent(style); diff --git a/src/core/xml_parser.js b/src/core/xml_parser.js index a1a60bf063f2a..665c46828b41f 100644 --- a/src/core/xml_parser.js +++ b/src/core/xml_parser.js @@ -321,11 +321,7 @@ class SimpleDOMNode { if (!this.childNodes) { return this.nodeValue || ""; } - return this.childNodes - .map(function (child) { - return child.textContent; - }) - .join(""); + return this.childNodes.map(child => child.textContent).join(""); } get children() { diff --git a/src/core/xref.js b/src/core/xref.js index 313f18a71d917..4a02f5ed8fedd 100644 --- a/src/core/xref.js +++ b/src/core/xref.js @@ -310,18 +310,15 @@ class XRef { if (!("streamState" in this)) { // Stores state of the stream as we process it so we can resume // from middle of stream in case of missing data error - const streamParameters = stream.dict; - const byteWidths = streamParameters.get("W"); - let range = streamParameters.get("Index"); - if (!range) { - range = [0, streamParameters.get("Size")]; - } + const { dict, pos } = stream; + const byteWidths = dict.get("W"); + const range = dict.get("Index") || [0, dict.get("Size")]; this.streamState = { entryRanges: range, byteWidths, entryNum: 0, - streamPos: stream.pos, + streamPos: pos, }; } this.readXRefStream(stream); @@ -680,6 +677,31 @@ class XRef { if (this.topDict) { return this.topDict; } + + // When no trailer dictionary candidate exists, try picking the first + // dictionary that contains a /Root entry (fixes issue18986.pdf). + if (!trailerDicts.length) { + for (const [num, entry] of this.entries.entries()) { + if (!entry) { + continue; + } + const ref = Ref.get(num, entry.gen); + let obj; + + try { + obj = this.fetch(ref); + } catch { + continue; + } + if (obj instanceof BaseStream) { + obj = obj.dict; + } + if (obj instanceof Dict && obj.has("Root")) { + return obj; + } + } + } + // nothing helps throw new InvalidPDFException("Invalid PDF structure."); } @@ -820,7 +842,6 @@ class XRef { if (xrefEntry === null) { // The referenced entry can be free. - this._cacheMap.set(num, xrefEntry); return xrefEntry; } // Prevent circular references, in corrupt PDF documents, from hanging the diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 955b16f363303..8afd218293720 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -37,26 +37,16 @@ import { Util, warn, } from "../shared/util.js"; -import { - DOMSVGFactory, - PDFDateString, - setLayerDimensions, -} from "./display_utils.js"; +import { PDFDateString, setLayerDimensions } from "./display_utils.js"; import { AnnotationStorage } from "./annotation_storage.js"; import { ColorConverters } from "../shared/scripting_utils.js"; +import { DOMSVGFactory } from "./svg_factory.js"; import { XfaLayer } from "./xfa_layer.js"; const DEFAULT_TAB_INDEX = 1000; const DEFAULT_FONT_SIZE = 9; const GetElementsByNameSet = new WeakSet(); -function getRectDims(rect) { - return { - width: rect[2] - rect[0], - height: rect[3] - rect[1], - }; -} - /** * @typedef {Object} AnnotationElementParameters * @property {Object} data @@ -246,12 +236,11 @@ class AnnotationElement { }, } = this; currentRect?.splice(0, 4, ...rect); - const { width, height } = getRectDims(rect); style.left = `${(100 * (rect[0] - pageX)) / pageWidth}%`; style.top = `${(100 * (pageHeight - rect[3] + pageY)) / pageHeight}%`; if (rotation === 0) { - style.width = `${(100 * width) / pageWidth}%`; - style.height = `${(100 * height) / pageHeight}%`; + style.width = `${(100 * /* width = */ (rect[2] - rect[0])) / pageWidth}%`; + style.height = `${(100 * /* height = */ (rect[3] - rect[1])) / pageHeight}%`; } else { this.setRotation(rotation); } @@ -300,8 +289,7 @@ class AnnotationElement { } return container; } - - const { width, height } = getRectDims(data.rect); + const { width, height } = this; if (!ignoreBorder && data.borderStyle.width > 0) { style.borderWidth = `${data.borderStyle.width}px`; @@ -309,10 +297,10 @@ class AnnotationElement { const horizontalRadius = data.borderStyle.horizontalCornerRadius; const verticalRadius = data.borderStyle.verticalCornerRadius; if (horizontalRadius > 0 || verticalRadius > 0) { - const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; + const radius = `calc(${horizontalRadius}px * var(--total-scale-factor)) / calc(${verticalRadius}px * var(--total-scale-factor))`; style.borderRadius = radius; } else if (this instanceof RadioButtonWidgetAnnotationElement) { - const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`; + const radius = `calc(${width}px * var(--total-scale-factor)) / calc(${height}px * var(--total-scale-factor))`; style.borderRadius = radius; } @@ -384,19 +372,13 @@ class AnnotationElement { return; } const { pageWidth, pageHeight } = this.parent.viewport.rawDims; - const { width, height } = getRectDims(this.data.rect); + let { width, height } = this; - let elementWidth, elementHeight; - if (angle % 180 === 0) { - elementWidth = (100 * width) / pageWidth; - elementHeight = (100 * height) / pageHeight; - } else { - elementWidth = (100 * height) / pageWidth; - elementHeight = (100 * width) / pageHeight; + if (angle % 180 !== 0) { + [width, height] = [height, width]; } - - container.style.width = `${elementWidth}%`; - container.style.height = `${elementHeight}%`; + container.style.width = `${(100 * width) / pageWidth}%`; + container.style.height = `${(100 * height) / pageHeight}%`; container.setAttribute("data-main-rotation", (360 - angle) % 360); } @@ -751,6 +733,14 @@ class AnnotationElement { }); }); } + + get width() { + return this.data.rect[2] - this.data.rect[0]; + } + + get height() { + return this.data.rect[3] - this.data.rect[1]; + } } class LinkAnnotationElement extends AnnotationElement { @@ -1204,7 +1194,7 @@ class WidgetAnnotationElement extends AnnotationElement { roundToOneDecimal(height / LINE_FACTOR) ); } - style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${computedFontSize}px * var(--total-scale-factor))`; style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); @@ -1289,7 +1279,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { } } else { element = document.createElement("input"); - element.type = "text"; + element.type = this.data.password ? "password" : "text"; element.setAttribute("value", fieldFormattedValues ?? textContent); if (this.data.doNotScroll) { element.style.overflowX = "hidden"; @@ -1563,7 +1553,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { const combWidth = fieldWidth / maxLen; element.classList.add("comb"); - element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`; + element.style.letterSpacing = `calc(${combWidth}px * var(--total-scale-factor) - 1ch)`; } } else { element = document.createElement("div"); @@ -2209,23 +2199,7 @@ class PopupElement { const baseColor = (popup.style.outlineColor = Util.makeHexColor( ...this.#color )); - if ( - (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || - CSS.supports("background-color", "color-mix(in srgb, red 30%, white)") - ) { - popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; - } else { - // color-mix isn't supported in some browsers hence this version. - // See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix#browser_compatibility - // TODO: Use color-mix when it's supported everywhere. - // Enlighten the color. - const BACKGROUND_ENLIGHT = 0.7; - popup.style.backgroundColor = Util.makeHexColor( - ...this.#color.map(c => - Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c) - ) - ); - } + popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; } const header = document.createElement("span"); @@ -2305,7 +2279,7 @@ class PopupElement { style: { color: this.#fontColor, fontSize: this.#fontSize - ? `calc(${this.#fontSize}px * var(--scale-factor))` + ? `calc(${this.#fontSize}px * var(--total-scale-factor))` : "", }, }; @@ -2540,8 +2514,7 @@ class LineAnnotationElement extends AnnotationElement { // Create an invisible line with the same starting and ending coordinates // that acts as the trigger for the popup. Only the line itself should // trigger the popup, not the entire container. - const data = this.data; - const { width, height } = getRectDims(data.rect); + const { data, width, height } = this; const svg = this.svgFactory.create( width, height, @@ -2595,8 +2568,7 @@ class SquareAnnotationElement extends AnnotationElement { // Create an invisible square with the same rectangle that acts as the // trigger for the popup. Only the square itself should trigger the // popup, not the entire container. - const data = this.data; - const { width, height } = getRectDims(data.rect); + const { data, width, height } = this; const svg = this.svgFactory.create( width, height, @@ -2652,8 +2624,7 @@ class CircleAnnotationElement extends AnnotationElement { // Create an invisible circle with the same ellipse that acts as the // trigger for the popup. Only the circle itself should trigger the // popup, not the entire container. - const data = this.data; - const { width, height } = getRectDims(data.rect); + const { data, width, height } = this; const svg = this.svgFactory.create( width, height, @@ -2715,11 +2686,12 @@ class PolylineAnnotationElement extends AnnotationElement { // popup, not the entire container. const { data: { rect, vertices, borderStyle, popupRef }, + width, + height, } = this; if (!vertices) { return this.container; } - const { width, height } = getRectDims(rect); const svg = this.svgFactory.create( width, height, @@ -2795,6 +2767,8 @@ class CaretAnnotationElement extends AnnotationElement { } class InkAnnotationElement extends AnnotationElement { + #polylinesGroupElement = null; + #polylines = []; constructor(parameters) { @@ -2812,44 +2786,71 @@ class InkAnnotationElement extends AnnotationElement { : AnnotationEditorType.INK; } + #getTransform(rotation, rect) { + // PDF coordinates are calculated from a bottom left origin, so + // transform the polyline coordinates to a top left origin for the + // SVG element. + switch (rotation) { + case 90: + return { + transform: `rotate(90) translate(${-rect[0]},${rect[1]}) scale(1,-1)`, + width: rect[3] - rect[1], + height: rect[2] - rect[0], + }; + case 180: + return { + transform: `rotate(180) translate(${-rect[2]},${rect[1]}) scale(1,-1)`, + width: rect[2] - rect[0], + height: rect[3] - rect[1], + }; + case 270: + return { + transform: `rotate(270) translate(${-rect[2]},${rect[3]}) scale(1,-1)`, + width: rect[3] - rect[1], + height: rect[2] - rect[0], + }; + default: + return { + transform: `translate(${-rect[0]},${rect[3]}) scale(1,-1)`, + width: rect[2] - rect[0], + height: rect[3] - rect[1], + }; + } + } + render() { this.container.classList.add(this.containerClassName); // Create an invisible polyline with the same points that acts as the // trigger for the popup. const { - data: { rect, inkLists, borderStyle, popupRef }, + data: { rect, rotation, inkLists, borderStyle, popupRef }, } = this; - const { width, height } = getRectDims(rect); + const { transform, width, height } = this.#getTransform(rotation, rect); + const svg = this.svgFactory.create( width, height, /* skipDimensions = */ true ); - - for (const inkList of inkLists) { - // Convert the ink list to a single points string that the SVG - // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are - // calculated from a bottom left origin, so transform the polyline - // coordinates to a top left origin for the SVG element. - let points = []; - for (let i = 0, ii = inkList.length; i < ii; i += 2) { - const x = inkList[i] - rect[0]; - const y = rect[3] - inkList[i + 1]; - points.push(`${x},${y}`); - } - points = points.join(" "); - + const g = (this.#polylinesGroupElement = + this.svgFactory.createElement("svg:g")); + svg.append(g); + // Ensure that the 'stroke-width' is always non-zero, since otherwise it + // won't be possible to open/close the popup (note e.g. issue 11122). + g.setAttribute("stroke-width", borderStyle.width || 1); + g.setAttribute("stroke-linecap", "round"); + g.setAttribute("stroke-linejoin", "round"); + g.setAttribute("stroke-miterlimit", 10); + g.setAttribute("stroke", "transparent"); + g.setAttribute("fill", "transparent"); + g.setAttribute("transform", transform); + + for (let i = 0, ii = inkLists.length; i < ii; i++) { const polyline = this.svgFactory.createElement(this.svgElementName); this.#polylines.push(polyline); - polyline.setAttribute("points", points); - // Ensure that the 'stroke-width' is always non-zero, since otherwise it - // won't be possible to open/close the popup (note e.g. issue 11122). - polyline.setAttribute("stroke-width", borderStyle.width || 1); - polyline.setAttribute("stroke", "transparent"); - polyline.setAttribute("fill", "transparent"); - - svg.append(polyline); + polyline.setAttribute("points", inkLists[i].join(",")); + g.append(polyline); } if (!popupRef && this.hasPopupData) { @@ -2862,6 +2863,29 @@ class InkAnnotationElement extends AnnotationElement { return this.container; } + updateEdited(params) { + super.updateEdited(params); + const { thickness, points, rect } = params; + const g = this.#polylinesGroupElement; + if (thickness >= 0) { + g.setAttribute("stroke-width", thickness || 1); + } + if (points) { + for (let i = 0, ii = this.#polylines.length; i < ii; i++) { + this.#polylines[i].setAttribute("points", points[i].join(",")); + } + } + if (rect) { + const { transform, width, height } = this.#getTransform( + this.data.rotation, + rect + ); + const root = g.parentElement; + root.setAttribute("viewBox", `0 0 ${width} ${height}`); + g.setAttribute("transform", transform); + } + } + getElementsToTriggerPopup() { return this.#polylines; } @@ -3172,8 +3196,7 @@ class AnnotationLayer { } const isPopupAnnotation = data.annotationType === AnnotationType.POPUP; if (!isPopupAnnotation) { - const { width, height } = getRectDims(data.rect); - if (width <= 0 || height <= 0) { + if (data.rect[2] === data.rect[0] || data.rect[3] === data.rect[1]) { continue; // Ignore empty annotations. } } else { @@ -3215,6 +3238,35 @@ class AnnotationLayer { this.#setAnnotationCanvasMap(); } + /** + * Add link annotations to the annotation layer. + * + * @param {Array} annotations + * @param {IPDFLinkService} linkService + * @memberof AnnotationLayer + */ + async addLinkAnnotations(annotations, linkService) { + const elementParams = { + data: null, + layer: this.div, + linkService, + svgFactory: new DOMSVGFactory(), + parent: this, + }; + for (const data of annotations) { + data.borderStyle ||= AnnotationLayer._defaultBorderStyle; + + elementParams.data = data; + const element = AnnotationElementFactory.create(elementParams); + + if (!element.isRenderable) { + continue; + } + const rendered = element.render(); + await this.#appendElement(rendered, data.id); + } + } + /** * Update the annotation elements on existing annotation layer. * @@ -3252,6 +3304,22 @@ class AnnotationLayer { } else { firstChild.after(canvas); } + + const editableAnnotation = this.#editableAnnotations.get(id); + if (!editableAnnotation) { + continue; + } + if (editableAnnotation._hasNoCanvas) { + // The canvas wasn't available when the annotation was created. + this._annotationEditorUIManager?.setMissingCanvas( + id, + element.id, + canvas + ); + editableAnnotation._hasNoCanvas = false; + } else { + editableAnnotation.canvas = canvas; + } } this.#annotationCanvasMap.clear(); } @@ -3263,6 +3331,24 @@ class AnnotationLayer { getEditableAnnotation(id) { return this.#editableAnnotations.get(id); } + + /** + * @private + */ + static get _defaultBorderStyle() { + return shadow( + this, + "_defaultBorderStyle", + Object.freeze({ + width: 1, + rawWidth: 1, + style: AnnotationBorderStyleType.SOLID, + dashArray: [3], + horizontalCornerRadius: 0, + verticalCornerRadius: 0, + }) + ); + } } export { diff --git a/src/display/api.js b/src/display/api.js index 2488837622238..0ca74fe8e4aaa 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -18,22 +18,18 @@ */ import { + _isValidExplicitDest, AbortException, AnnotationMode, assert, + FeatureTest, getVerbosityLevel, info, - InvalidPDFException, isNodeJS, - MAX_IMAGE_SIZE_TO_CACHE, - MissingPDFException, - PasswordException, RenderingIntentFlag, setVerbosityLevel, shadow, stringToBytes, - UnexpectedResponseException, - UnknownErrorException, unreachable, warn, } from "../shared/util.js"; @@ -42,29 +38,29 @@ import { PrintAnnotationStorage, SerializableEmpty, } from "./annotation_storage.js"; +import { FontFaceObject, FontLoader } from "./font_loader.js"; import { - deprecated, - DOMCanvasFactory, - DOMCMapReaderFactory, - DOMFilterFactory, - DOMStandardFontDataFactory, isDataScheme, isValidFetchUrl, PageViewport, RenderingCancelledException, StatTimer, } from "./display_utils.js"; -import { FontFaceObject, FontLoader } from "./font_loader.js"; +import { MessageHandler, wrapReason } from "../shared/message_handler.js"; import { NodeCanvasFactory, NodeCMapReaderFactory, NodeFilterFactory, - NodePackages, NodeStandardFontDataFactory, + NodeWasmFactory, } from "display-node_utils"; import { CanvasGraphics } from "./canvas.js"; +import { DOMCanvasFactory } from "./canvas_factory.js"; +import { DOMCMapReaderFactory } from "display-cmap_reader_factory"; +import { DOMFilterFactory } from "./filter_factory.js"; +import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory"; +import { DOMWasmFactory } from "display-wasm_factory"; import { GlobalWorkerOptions } from "./worker_options.js"; -import { MessageHandler } from "../shared/message_handler.js"; import { Metadata } from "./metadata.js"; import { OptionalContentConfig } from "./optional_content_config.js"; import { PDFDataTransportStream } from "./transport_stream.js"; @@ -76,24 +72,6 @@ import { XfaText } from "./xfa_text.js"; const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536 const RENDERING_CANCELLED_TIMEOUT = 100; // ms -const DELAYED_CLEANUP_TIMEOUT = 5000; // ms - -const DefaultCanvasFactory = - typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS - ? NodeCanvasFactory - : DOMCanvasFactory; -const DefaultCMapReaderFactory = - typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS - ? NodeCMapReaderFactory - : DOMCMapReaderFactory; -const DefaultFilterFactory = - typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS - ? NodeFilterFactory - : DOMFilterFactory; -const DefaultStandardFontDataFactory = - typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS - ? NodeStandardFontDataFactory - : DOMStandardFontDataFactory; /** * @typedef { Int8Array | Uint8Array | Uint8ClampedArray | @@ -145,9 +123,8 @@ const DefaultStandardFontDataFactory = * @property {boolean} [cMapPacked] - Specifies if the Adobe CMaps are binary * packed or not. The default value is `true`. * @property {Object} [CMapReaderFactory] - The factory that will be used when - * reading built-in CMap files. Providing a custom factory is useful for - * environments without Fetch API or `XMLHttpRequest` support, such as - * Node.js. The default value is {DOMCMapReaderFactory}. + * reading built-in CMap files. + * The default value is {DOMCMapReaderFactory}. * @property {boolean} [useSystemFonts] - When `true`, fonts that aren't * embedded in the PDF document will fallback to a system font. * The default value is `true` in web environments and `false` in Node.js; @@ -156,13 +133,21 @@ const DefaultStandardFontDataFactory = * @property {string} [standardFontDataUrl] - The URL where the standard font * files are located. Include the trailing slash. * @property {Object} [StandardFontDataFactory] - The factory that will be used - * when reading the standard font files. Providing a custom factory is useful - * for environments without Fetch API or `XMLHttpRequest` support, such as - * Node.js. The default value is {DOMStandardFontDataFactory}. + * when reading the standard font files. + * The default value is {DOMStandardFontDataFactory}. + * @property {string} [wasmUrl] - The URL where the wasm files are located. + * Include the trailing slash. + * @property {Object} [WasmFactory] - The factory that will be used + * when reading the wasm files. + * The default value is {DOMWasmFactory}. * @property {boolean} [useWorkerFetch] - Enable using the Fetch API in the * worker-thread when reading CMap and standard font files. When `true`, - * the `CMapReaderFactory` and `StandardFontDataFactory` options are ignored. + * the `CMapReaderFactory`, `StandardFontDataFactory`, and `WasmFactory` + * options are ignored. * The default value is `true` in web environments and `false` in Node.js. + * @property {boolean} [useWasm] - Attempt to use WebAssembly in order to + * improve e.g. image decoding performance. + * The default value is `true`. * @property {boolean} [stopAtErrors] - Reject certain promises, e.g. * `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated * PDF data cannot be successfully parsed, instead of attempting to recover @@ -177,6 +162,21 @@ const DefaultStandardFontDataFactory = * `OffscreenCanvas` in the worker. Primarily used to improve performance of * image conversion/rendering. * The default value is `true` in web environments and `false` in Node.js. + * @property {boolean} [isImageDecoderSupported] - Determines if we can use + * `ImageDecoder` in the worker. Primarily used to improve performance of + * image conversion/rendering. + * The default value is `true` in web environments and `false` in Node.js. + * + * NOTE: Also temporarily disabled in Chromium browsers, until we no longer + * support the affected browser versions, because of various bugs: + * + * - Crashes when using the BMP decoder with huge images, e.g. issue6741.pdf; + * see https://issues.chromium.org/issues/374807001 + * + * - Broken images when using the JPEG decoder with images that have custom + * colour profiles, e.g. GitHub discussion 19030; + * see https://issues.chromium.org/issues/378869810 + * * @property {number} [canvasMaxAreaInBytes] - The integer value is used to * know when an image must be resized (uses `OffscreenCanvas` in the worker). * If it's -1 then a possibly slow algorithm is used to guess the max value. @@ -262,15 +262,25 @@ function getDocument(src = {}) { typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null; - const cMapUrl = typeof src.cMapUrl === "string" ? src.cMapUrl : null; + const cMapUrl = getFactoryUrlProp(src.cMapUrl); const cMapPacked = src.cMapPacked !== false; - const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory; - const standardFontDataUrl = - typeof src.standardFontDataUrl === "string" - ? src.standardFontDataUrl - : null; + const CMapReaderFactory = + src.CMapReaderFactory || + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeCMapReaderFactory + : DOMCMapReaderFactory); + const standardFontDataUrl = getFactoryUrlProp(src.standardFontDataUrl); const StandardFontDataFactory = - src.StandardFontDataFactory || DefaultStandardFontDataFactory; + src.StandardFontDataFactory || + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeStandardFontDataFactory + : DOMStandardFontDataFactory); + const wasmUrl = getFactoryUrlProp(src.wasmUrl); + const WasmFactory = + src.WasmFactory || + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeWasmFactory + : DOMWasmFactory); const ignoreErrors = src.stopAtErrors !== true; const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 @@ -281,6 +291,16 @@ function getDocument(src = {}) { typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS; + const isImageDecoderSupported = + // eslint-disable-next-line no-nested-ternary + typeof src.isImageDecoderSupported === "boolean" + ? src.isImageDecoderSupported + : // eslint-disable-next-line no-nested-ternary + typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL") + ? true + : typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME") + ? false + : !isNodeJS && (FeatureTest.platform.isFirefox || !globalThis.chrome); const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1; @@ -293,9 +313,18 @@ function getDocument(src = {}) { const disableStream = src.disableStream === true; const disableAutoFetch = src.disableAutoFetch === true; const pdfBug = src.pdfBug === true; - const CanvasFactory = src.CanvasFactory || DefaultCanvasFactory; - const FilterFactory = src.FilterFactory || DefaultFilterFactory; + const CanvasFactory = + src.CanvasFactory || + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeCanvasFactory + : DOMCanvasFactory); + const FilterFactory = + src.FilterFactory || + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeFilterFactory + : DOMFilterFactory); const enableHWA = src.enableHWA === true; + const useWasm = src.useWasm !== false; // Parameters whose default values depend on other parameters. const length = rangeTransport ? rangeTransport.length : (src.length ?? NaN); @@ -307,25 +336,17 @@ function getDocument(src = {}) { typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || - (CMapReaderFactory === DOMCMapReaderFactory && + !!( + CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && + WasmFactory === DOMWasmFactory && cMapUrl && standardFontDataUrl && + wasmUrl && isValidFetchUrl(cMapUrl, document.baseURI) && - isValidFetchUrl(standardFontDataUrl, document.baseURI)); - - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { - if (src.canvasFactory) { - deprecated( - "`canvasFactory`-instance option, please use `CanvasFactory` instead." - ); - } - if (src.filterFactory) { - deprecated( - "`filterFactory`-instance option, please use `FilterFactory` instead." - ); - } - } + isValidFetchUrl(standardFontDataUrl, document.baseURI) && + isValidFetchUrl(wasmUrl, document.baseURI) + ); // Parameters only intended for development/testing purposes. const styleElement = @@ -351,6 +372,11 @@ function getDocument(src = {}) { useWorkerFetch ? null : new StandardFontDataFactory({ baseUrl: standardFontDataUrl }), + wasmFactory: + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || + useWorkerFetch + ? null + : new WasmFactory({ baseUrl: wasmUrl }), }; if (!worker) { @@ -385,16 +411,18 @@ function getDocument(src = {}) { ignoreErrors, isEvalSupported, isOffscreenCanvasSupported, + isImageDecoderSupported, canvasMaxAreaInBytes, fontExtraProperties, useSystemFonts, - cMapUrl: useWorkerFetch ? cMapUrl : null, - standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null, + useWasm, + useWorkerFetch, + cMapUrl, + standardFontDataUrl, + wasmUrl, }, }; const transportParams = { - disableFontFace, - fontExtraProperties, ownerDocument, pdfBug, styleElement, @@ -439,15 +467,20 @@ function getDocument(src = {}) { PDFJSDev.test("GENERIC") && isNodeJS ) { - const isFetchSupported = - typeof fetch !== "undefined" && - typeof Response !== "undefined" && - "body" in Response.prototype; - - NetworkStream = - isFetchSupported && isValidFetchUrl(url) - ? PDFFetchStream - : PDFNodeStream; + if (isValidFetchUrl(url)) { + if ( + typeof fetch === "undefined" || + typeof Response === "undefined" || + !("body" in Response.prototype) + ) { + throw new Error( + "getDocument - the Fetch API was disabled in Node.js, see `--no-experimental-fetch`." + ); + } + NetworkStream = PDFFetchStream; + } else { + NetworkStream = PDFNodeStream; + } } else { NetworkStream = isValidFetchUrl(url) ? PDFFetchStream @@ -497,18 +530,20 @@ function getUrlProp(val) { if (val instanceof URL) { return val.href; } - try { - // The full path is required in the 'url' field. - return new URL(val, window.location).href; - } catch { + if (typeof val === "string") { if ( typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && - isNodeJS && - typeof val === "string" + isNodeJS ) { return val; // Use the url as-is in Node.js environments. } + + // The full path is required in the 'url' field. + const url = URL.parse(val, window.location); + if (url) { + return url.href; + } } throw new Error( "Invalid PDF url data: " + @@ -551,16 +586,31 @@ function getDataProp(val) { ); } -function isRefProxy(ref) { - return ( - typeof ref === "object" && - Number.isInteger(ref?.num) && - ref.num >= 0 && - Number.isInteger(ref?.gen) && - ref.gen >= 0 - ); +function getFactoryUrlProp(val) { + if (typeof val !== "string") { + return null; + } + if (val.endsWith("/")) { + return val; + } + throw new Error(`Invalid factory url: "${val}" must include trailing slash.`); } +const isRefProxy = v => + typeof v === "object" && + Number.isInteger(v?.num) && + v.num >= 0 && + Number.isInteger(v?.gen) && + v.gen >= 0; + +const isNameProxy = v => typeof v === "object" && typeof v?.name === "string"; + +const isValidExplicitDest = _isValidExplicitDest.bind( + null, + /* validRef = */ isRefProxy, + /* validName = */ isNameProxy +); + /** * @typedef {Object} OnProgressParameters * @property {number} loaded - Currently loaded number of bytes. @@ -624,23 +674,26 @@ class PDFDocumentLoadingTask { */ async destroy() { this.destroyed = true; - try { - if (this._worker?.port) { - this._worker._pendingDestroy = true; - } + + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { await this._transport?.destroy(); - } catch (ex) { - if (this._worker?.port) { - delete this._worker._pendingDestroy; + } else { + try { + if (this._worker?.port) { + this._worker._pendingDestroy = true; + } + await this._transport?.destroy(); + } catch (ex) { + if (this._worker?.port) { + delete this._worker._pendingDestroy; + } + throw ex; } - throw ex; } - this._transport = null; - if (this._worker) { - this._worker.destroy(); - this._worker = null; - } + + this._worker?.destroy(); + this._worker = null; } } @@ -817,8 +870,8 @@ class PDFDocumentProxy { } /** - * @type {Array} A (not guaranteed to be) unique ID to - * identify the PDF document. + * @type {Array} A (not guaranteed to be) unique ID to identify + * the PDF document. * NOTE: The first element will always be defined for all PDF documents, * whereas the second element is only defined for *modified* PDF documents. */ @@ -1305,8 +1358,6 @@ class PDFDocumentProxy { * Proxy to a `PDFPage` in the worker thread. */ class PDFPageProxy { - #delayedCleanupTimeout = null; - #pendingCleanup = false; constructor(pageIndex, pageInfo, transport, pdfBug = false) { @@ -1319,7 +1370,6 @@ class PDFPageProxy { this.commonObjs = transport.commonObjs; this.objs = new PDFObjects(); - this._maybeCleanupAfterRender = false; this._intentStates = new Map(); this.destroyed = false; } @@ -1374,6 +1424,7 @@ class PDFPageProxy { } = {}) { return new PageViewport({ viewBox: this.view, + userUnit: this.userUnit, scale, rotation, offsetX, @@ -1455,10 +1506,8 @@ class PDFPageProxy { ); const { renderingIntent, cacheKey } = intentArgs; // If there was a pending destroy, cancel it so no cleanup happens during - // this call to render... + // this call to render. this.#pendingCleanup = false; - // ... and ensure that a delayed cleanup is always aborted. - this.#abortDelayedCleanup(); optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent); @@ -1497,10 +1546,10 @@ class PDFPageProxy { // Attempt to reduce memory usage during *printing*, by always running // cleanup immediately once rendering has finished. - if (this._maybeCleanupAfterRender || intentPrint) { + if (intentPrint) { this.#pendingCleanup = true; } - this.#tryCleanup(/* delayed = */ !intentPrint); + this.#tryCleanup(); if (error) { internalRenderTask.capability.reject(error); @@ -1734,7 +1783,6 @@ class PDFPageProxy { } this.objs.clear(); this.#pendingCleanup = false; - this.#abortDelayedCleanup(); return Promise.all(waitOn); } @@ -1748,7 +1796,7 @@ class PDFPageProxy { */ cleanup(resetStats = false) { this.#pendingCleanup = true; - const success = this.#tryCleanup(/* delayed = */ false); + const success = this.#tryCleanup(); if (resetStats && success) { this._stats &&= new StatTimer(); @@ -1758,25 +1806,12 @@ class PDFPageProxy { /** * Attempts to clean up if rendering is in a state where that's possible. - * @param {boolean} [delayed] - Delay the cleanup, to e.g. improve zooming - * performance in documents with large images. - * The default value is `false`. * @returns {boolean} Indicates if clean-up was successfully run. */ - #tryCleanup(delayed = false) { - this.#abortDelayedCleanup(); - + #tryCleanup() { if (!this.#pendingCleanup || this.destroyed) { return false; } - if (delayed) { - this.#delayedCleanupTimeout = setTimeout(() => { - this.#delayedCleanupTimeout = null; - this.#tryCleanup(/* delayed = */ false); - }, DELAYED_CLEANUP_TIMEOUT); - - return false; - } for (const { renderTasks, operatorList } of this._intentStates.values()) { if (renderTasks.size > 0 || !operatorList.lastChunk) { return false; @@ -1788,13 +1823,6 @@ class PDFPageProxy { return true; } - #abortDelayedCleanup() { - if (this.#delayedCleanupTimeout) { - clearTimeout(this.#delayedCleanupTimeout); - this.#delayedCleanupTimeout = null; - } - } - /** * @private */ @@ -1828,7 +1856,7 @@ class PDFPageProxy { } if (operatorListChunk.lastChunk) { - this.#tryCleanup(/* delayed = */ true); + this.#tryCleanup(); } } @@ -1891,7 +1919,7 @@ class PDFPageProxy { for (const internalRenderTask of intentState.renderTasks) { internalRenderTask.operatorListChanged(); } - this.#tryCleanup(/* delayed = */ true); + this.#tryCleanup(); } if (intentState.displayReadyCapability) { @@ -2066,14 +2094,9 @@ class PDFWorker { // Check if URLs have the same origin. For non-HTTP based URLs, returns // false. this._isSameOrigin = (baseUrl, otherUrl) => { - let base; - try { - base = new URL(baseUrl); - if (!base.origin || base.origin === "null") { - return false; // non-HTTP url - } - } catch { - return false; + const base = URL.parse(baseUrl); + if (!base?.origin || base.origin === "null") { + return false; // non-HTTP url } const other = new URL(otherUrl, base); return base.origin === other.origin; @@ -2125,14 +2148,6 @@ class PDFWorker { * @type {Promise} */ get promise() { - if ( - typeof PDFJSDev !== "undefined" && - PDFJSDev.test("GENERIC") && - isNodeJS - ) { - // Ensure that all Node.js packages/polyfills have loaded. - return Promise.all([NodePackages.promise, this._readyCapability.promise]); - } return this._readyCapability.promise; } @@ -2194,7 +2209,7 @@ class PDFWorker { if ( typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && - !PDFWorker._isSameOrigin(window.location.href, workerSrc) + !PDFWorker._isSameOrigin(window.location, workerSrc) ) { workerSrc = PDFWorker._createCDNWrapper( new URL(workerSrc, window.location).href @@ -2313,21 +2328,21 @@ class PDFWorker { */ destroy() { this.destroyed = true; - if (this._webWorker) { - // We need to terminate only web worker created resource. - this._webWorker.terminate(); - this._webWorker = null; - } + + // We need to terminate only web worker created resource. + this._webWorker?.terminate(); + this._webWorker = null; + PDFWorker.#workerPorts?.delete(this._port); this._port = null; - if (this._messageHandler) { - this._messageHandler.destroy(); - this._messageHandler = null; - } + + this._messageHandler?.destroy(); + this._messageHandler = null; } /** * @param {PDFWorkerParameters} params - The worker initialization parameters. + * @returns {PDFWorker} */ static fromPort(params) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { @@ -2416,6 +2431,7 @@ class WorkerTransport { this.filterFactory = factory.filterFactory; this.cMapReaderFactory = factory.cMapReaderFactory; this.standardFontDataFactory = factory.standardFontDataFactory; + this.wasmFactory = factory.wasmFactory; this.destroyed = false; this.destroyCapability = null; @@ -2577,10 +2593,9 @@ class WorkerTransport { new AbortException("Worker was terminated.") ); - if (this.messageHandler) { - this.messageHandler.destroy(); - this.messageHandler = null; - } + this.messageHandler?.destroy(); + this.messageHandler = null; + this.destroyCapability.resolve(); }, this.destroyCapability.reject); return this.destroyCapability.promise; @@ -2634,32 +2649,27 @@ class WorkerTransport { }; }); - messageHandler.on("ReaderHeadersReady", data => { - const headersCapability = Promise.withResolvers(); - const fullReader = this._fullReader; - fullReader.headersReady.then(() => { - // If stream or range are disabled, it's our only way to report - // loading progress. - if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) { - if (this._lastProgress) { - loadingTask.onProgress?.(this._lastProgress); - } - fullReader.onProgress = evt => { - loadingTask.onProgress?.({ - loaded: evt.loaded, - total: evt.total, - }); - }; - } + messageHandler.on("ReaderHeadersReady", async data => { + await this._fullReader.headersReady; - headersCapability.resolve({ - isStreamingSupported: fullReader.isStreamingSupported, - isRangeSupported: fullReader.isRangeSupported, - contentLength: fullReader.contentLength, - }); - }, headersCapability.reject); + const { isStreamingSupported, isRangeSupported, contentLength } = + this._fullReader; + + // If stream or range are disabled, it's our only way to report + // loading progress. + if (!isStreamingSupported || !isRangeSupported) { + if (this._lastProgress) { + loadingTask.onProgress?.(this._lastProgress); + } + this._fullReader.onProgress = evt => { + loadingTask.onProgress?.({ + loaded: evt.loaded, + total: evt.total, + }); + }; + } - return headersCapability.promise; + return { isStreamingSupported, isRangeSupported, contentLength }; }); messageHandler.on("GetRangeReader", (data, sink) => { @@ -2725,34 +2735,18 @@ class WorkerTransport { loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this)); }); - messageHandler.on("DocException", function (ex) { - let reason; - switch (ex.name) { - case "PasswordException": - reason = new PasswordException(ex.message, ex.code); - break; - case "InvalidPDFException": - reason = new InvalidPDFException(ex.message); - break; - case "MissingPDFException": - reason = new MissingPDFException(ex.message); - break; - case "UnexpectedResponseException": - reason = new UnexpectedResponseException(ex.message, ex.status); - break; - case "UnknownErrorException": - reason = new UnknownErrorException(ex.message, ex.details); - break; - default: - unreachable("DocException - expected a valid Error."); - } - loadingTask._capability.reject(reason); + messageHandler.on("DocException", ex => { + loadingTask._capability.reject(wrapReason(ex)); }); - messageHandler.on("PasswordRequest", exception => { + messageHandler.on("PasswordRequest", ex => { this.#passwordCapability = Promise.withResolvers(); - if (loadingTask.onPassword) { + try { + if (!loadingTask.onPassword) { + throw wrapReason(ex); + } + const updatePassword = password => { if (password instanceof Error) { this.#passwordCapability.reject(password); @@ -2760,15 +2754,9 @@ class WorkerTransport { this.#passwordCapability.resolve({ password }); } }; - try { - loadingTask.onPassword(updatePassword, exception.code); - } catch (ex) { - this.#passwordCapability.reject(ex); - } - } else { - this.#passwordCapability.reject( - new PasswordException(exception.message, exception.code) - ); + loadingTask.onPassword(updatePassword, ex.code); + } catch (err) { + this.#passwordCapability.reject(err); } return this.#passwordCapability.promise; }); @@ -2804,8 +2792,6 @@ class WorkerTransport { switch (type) { case "Font": - const { disableFontFace, fontExtraProperties, pdfBug } = this._params; - if ("error" in exportedData) { const exportedError = exportedData.error; warn(`Error during font loading: ${exportedError}`); @@ -2814,19 +2800,16 @@ class WorkerTransport { } const inspectFont = - pdfBug && globalThis.FontInspector?.enabled + this._params.pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null; - const font = new FontFaceObject(exportedData, { - disableFontFace, - inspectFont, - }); + const font = new FontFaceObject(exportedData, inspectFont); this.fontLoader .bind(font) .catch(() => messageHandler.sendWithPromise("FontFallback", { id })) .finally(() => { - if (!fontExtraProperties && font.data) { + if (!font.fontExtraProperties && font.data) { // Immediately release the `font.data` property once the font // has been attached to the DOM, since it's no longer needed, // rather than waiting for a `PDFDocumentProxy.cleanup` call. @@ -2884,13 +2867,6 @@ class WorkerTransport { switch (type) { case "Image": - pageProxy.objs.resolve(id, imageData); - - // Heuristic that will allow us not to store large data. - if (imageData?.dataLen > MAX_IMAGE_SIZE_TO_CACHE) { - pageProxy._maybeCleanupAfterRender = true; - } - break; case "Pattern": pageProxy.objs.resolve(id, imageData); break; @@ -2909,32 +2885,21 @@ class WorkerTransport { }); }); - messageHandler.on("FetchBuiltInCMap", data => { - if (this.destroyed) { - return Promise.reject(new Error("Worker was destroyed.")); + messageHandler.on("FetchBinaryData", async data => { + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + throw new Error("Not implemented: FetchBinaryData"); } - if (!this.cMapReaderFactory) { - return Promise.reject( - new Error( - "CMapReaderFactory not initialized, see the `useWorkerFetch` parameter." - ) - ); - } - return this.cMapReaderFactory.fetch(data); - }); - - messageHandler.on("FetchStandardFontData", data => { if (this.destroyed) { - return Promise.reject(new Error("Worker was destroyed.")); + throw new Error("Worker was destroyed."); } - if (!this.standardFontDataFactory) { - return Promise.reject( - new Error( - "StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter." - ) + const factory = this[data.type]; + + if (!factory) { + throw new Error( + `${data.type} not initialized, see the \`useWorkerFetch\` parameter.` ); } - return this.standardFontDataFactory.fetch(data); + return factory.fetch(data); }); } @@ -3220,6 +3185,20 @@ class PDFObjects { return !!obj && obj.data !== INITIAL_DATA; } + /** + * @param {string} objId + * @returns {boolean} + */ + delete(objId) { + const obj = this.#objs[objId]; + if (!obj || obj.data === INITIAL_DATA) { + // Only allow removing the object *after* it's been resolved. + return false; + } + delete this.#objs[objId]; + return true; + } + /** * Resolves the object `objId` with optional `data`. * @@ -3258,17 +3237,27 @@ class PDFObjects { class RenderTask { #internalRenderTask = null; + /** + * Callback for incremental rendering -- a function that will be called + * each time the rendering is paused. To continue rendering call the + * function that is the first argument to the callback. + * @type {function} + */ + onContinue = null; + + /** + * A function that will be synchronously called when the rendering tasks + * finishes with an error (either because of an actual error, or because the + * rendering is cancelled). + * + * @type {function} + * @param {Error} error + */ + onError = null; + constructor(internalRenderTask) { this.#internalRenderTask = internalRenderTask; - /** - * Callback for incremental rendering -- a function that will be called - * each time the rendering is paused. To continue rendering call the - * function that is the first argument to the callback. - * @type {function} - */ - this.onContinue = null; - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { // For testing purposes. Object.defineProperty(this, "getOperatorList", { @@ -3425,13 +3414,13 @@ class InternalRenderTask { } InternalRenderTask.#canvasInUse.delete(this._canvas); - this.callback( - error || - new RenderingCancelledException( - `Rendering cancelled, page ${this._pageIndex + 1}`, - extraDelay - ) + error ||= new RenderingCancelledException( + `Rendering cancelled, page ${this._pageIndex + 1}`, + extraDelay ); + this.callback(error); + + this.task.onError?.(error); } operatorListChanged() { @@ -3501,11 +3490,8 @@ const build = export { build, - DefaultCanvasFactory, - DefaultCMapReaderFactory, - DefaultFilterFactory, - DefaultStandardFontDataFactory, getDocument, + isValidExplicitDest, LoopbackPort, PDFDataRangeTransport, PDFDocumentLoadingTask, diff --git a/src/display/base_factory.js b/src/display/base_factory.js deleted file mode 100644 index 7d72ac49ab267..0000000000000 --- a/src/display/base_factory.js +++ /dev/null @@ -1,234 +0,0 @@ -/* Copyright 2015 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { unreachable } from "../shared/util.js"; - -class BaseFilterFactory { - constructor() { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - this.constructor === BaseFilterFactory - ) { - unreachable("Cannot initialize BaseFilterFactory."); - } - } - - addFilter(maps) { - return "none"; - } - - addHCMFilter(fgColor, bgColor) { - return "none"; - } - - addAlphaFilter(map) { - return "none"; - } - - addLuminosityFilter(map) { - return "none"; - } - - addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { - return "none"; - } - - destroy(keepHCM = false) {} -} - -class BaseCanvasFactory { - #enableHWA = false; - - constructor({ enableHWA = false }) { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - this.constructor === BaseCanvasFactory - ) { - unreachable("Cannot initialize BaseCanvasFactory."); - } - this.#enableHWA = enableHWA; - } - - create(width, height) { - if (width <= 0 || height <= 0) { - throw new Error("Invalid canvas size"); - } - const canvas = this._createCanvas(width, height); - return { - canvas, - context: canvas.getContext("2d", { - willReadFrequently: !this.#enableHWA, - }), - }; - } - - reset(canvasAndContext, width, height) { - if (!canvasAndContext.canvas) { - throw new Error("Canvas is not specified"); - } - if (width <= 0 || height <= 0) { - throw new Error("Invalid canvas size"); - } - canvasAndContext.canvas.width = width; - canvasAndContext.canvas.height = height; - } - - destroy(canvasAndContext) { - if (!canvasAndContext.canvas) { - throw new Error("Canvas is not specified"); - } - // Zeroing the width and height cause Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - canvasAndContext.canvas.width = 0; - canvasAndContext.canvas.height = 0; - canvasAndContext.canvas = null; - canvasAndContext.context = null; - } - - /** - * @ignore - */ - _createCanvas(width, height) { - unreachable("Abstract method `_createCanvas` called."); - } -} - -class BaseCMapReaderFactory { - constructor({ baseUrl = null, isCompressed = true }) { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - this.constructor === BaseCMapReaderFactory - ) { - unreachable("Cannot initialize BaseCMapReaderFactory."); - } - this.baseUrl = baseUrl; - this.isCompressed = isCompressed; - } - - async fetch({ name }) { - if (!this.baseUrl) { - throw new Error( - "Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided." - ); - } - if (!name) { - throw new Error("CMap name must be specified."); - } - const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); - - return this._fetch(url) - .then(cMapData => ({ cMapData, isCompressed: this.isCompressed })) - .catch(reason => { - throw new Error( - `Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}` - ); - }); - } - - /** - * @ignore - * @returns {Promise} - */ - async _fetch(url) { - unreachable("Abstract method `_fetch` called."); - } -} - -class BaseStandardFontDataFactory { - constructor({ baseUrl = null }) { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - this.constructor === BaseStandardFontDataFactory - ) { - unreachable("Cannot initialize BaseStandardFontDataFactory."); - } - this.baseUrl = baseUrl; - } - - async fetch({ filename }) { - if (!this.baseUrl) { - throw new Error( - "Ensure that the `standardFontDataUrl` API parameter is provided." - ); - } - if (!filename) { - throw new Error("Font filename must be specified."); - } - const url = `${this.baseUrl}${filename}`; - - return this._fetch(url).catch(reason => { - throw new Error(`Unable to load font data at: ${url}`); - }); - } - - /** - * @ignore - * @returns {Promise} - */ - async _fetch(url) { - unreachable("Abstract method `_fetch` called."); - } -} - -class BaseSVGFactory { - constructor() { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - this.constructor === BaseSVGFactory - ) { - unreachable("Cannot initialize BaseSVGFactory."); - } - } - - create(width, height, skipDimensions = false) { - if (width <= 0 || height <= 0) { - throw new Error("Invalid SVG dimensions"); - } - const svg = this._createSVG("svg:svg"); - svg.setAttribute("version", "1.1"); - - if (!skipDimensions) { - svg.setAttribute("width", `${width}px`); - svg.setAttribute("height", `${height}px`); - } - - svg.setAttribute("preserveAspectRatio", "none"); - svg.setAttribute("viewBox", `0 0 ${width} ${height}`); - - return svg; - } - - createElement(type) { - if (typeof type !== "string") { - throw new Error("Invalid SVG element type"); - } - return this._createSVG(type); - } - - /** - * @ignore - */ - _createSVG(type) { - unreachable("Abstract method `_createSVG` called."); - } -} - -export { - BaseCanvasFactory, - BaseCMapReaderFactory, - BaseFilterFactory, - BaseStandardFontDataFactory, - BaseSVGFactory, -}; diff --git a/src/display/canvas.js b/src/display/canvas.js index 098741ce9d275..ff63e0da12a12 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -484,6 +484,7 @@ class CanvasExtraState { this.fillColor = "#000000"; this.strokeColor = "#000000"; this.patternFill = false; + this.patternStroke = false; // Note: fill alpha applies to all non-stroking operations this.fillAlpha = 1; this.strokeAlpha = 1; @@ -586,7 +587,7 @@ class CanvasExtraState { } function putBinaryImageData(ctx, imgData) { - if (typeof ImageData !== "undefined" && imgData instanceof ImageData) { + if (imgData instanceof ImageData) { ctx.putImageData(imgData, 0, 0); return; } @@ -1884,15 +1885,19 @@ class CanvasGraphics { return; } - ctx.save(); - ctx.beginPath(); - for (const path of paths) { - ctx.setTransform(...path.transform); - ctx.translate(path.x, path.y); - path.addToPath(ctx, path.fontSize); + const newPath = new Path2D(); + const invTransf = ctx.getTransform().invertSelf(); + for (const { transform, x, y, fontSize, path } of paths) { + newPath.addPath( + path, + new DOMMatrix(transform) + .preMultiplySelf(invTransf) + .translate(x, y) + .scale(fontSize, -fontSize) + ); } - ctx.restore(); - ctx.clip(); + + ctx.clip(newPath); ctx.beginPath(); delete this.pendingTextPaths; } @@ -2001,7 +2006,16 @@ class CanvasGraphics { this.moveText(0, this.current.leading); } - paintChar(character, x, y, patternTransform) { + #getScaledPath(path, currentTransform, transform) { + const newPath = new Path2D(); + newPath.addPath( + path, + new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform) + ); + return newPath; + } + + paintChar(character, x, y, patternFillTransform, patternStrokeTransform) { const ctx = this.ctx; const current = this.current; const font = current.font; @@ -2013,31 +2027,65 @@ class CanvasGraphics { textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG ); const patternFill = current.patternFill && !font.missingFile; + const patternStroke = current.patternStroke && !font.missingFile; - let addToPath; - if (font.disableFontFace || isAddToPathSet || patternFill) { - addToPath = font.getPathGenerator(this.commonObjs, character); + let path; + if ( + font.disableFontFace || + isAddToPathSet || + patternFill || + patternStroke + ) { + path = font.getPathGenerator(this.commonObjs, character); } - if (font.disableFontFace || patternFill) { + if (font.disableFontFace || patternFill || patternStroke) { ctx.save(); ctx.translate(x, y); - ctx.beginPath(); - addToPath(ctx, fontSize); - if (patternTransform) { - ctx.setTransform(...patternTransform); - } + ctx.scale(fontSize, -fontSize); + let currentTransform; if ( fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE ) { - ctx.fill(); + if (patternFillTransform) { + currentTransform = ctx.getTransform(); + ctx.setTransform(...patternFillTransform); + ctx.fill( + this.#getScaledPath(path, currentTransform, patternFillTransform) + ); + } else { + ctx.fill(path); + } } if ( fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE ) { - ctx.stroke(); + if (patternStrokeTransform) { + currentTransform ||= ctx.getTransform(); + ctx.setTransform(...patternStrokeTransform); + const { a, b, c, d } = currentTransform; + const invPatternTransform = Util.inverseTransform( + patternStrokeTransform + ); + const transf = Util.transform( + [a, b, c, d, 0, 0], + invPatternTransform + ); + const [sx, sy] = Util.singularValueDecompose2dScale(transf); + + // Cancel the pattern scaling of the line width. + // If sx and sy are different, unfortunately we can't do anything and + // we'll have a rendering bug. + ctx.lineWidth *= Math.max(sx, sy) / fontSize; + ctx.stroke( + this.#getScaledPath(path, currentTransform, patternStrokeTransform) + ); + } else { + ctx.lineWidth /= fontSize; + ctx.stroke(path); + } } ctx.restore(); } else { @@ -2062,7 +2110,7 @@ class CanvasGraphics { x, y, fontSize, - addToPath, + path, }); } } @@ -2127,7 +2175,7 @@ class CanvasGraphics { ctx.scale(textHScale, 1); } - let patternTransform; + let patternFillTransform, patternStrokeTransform; if (current.patternFill) { ctx.save(); const pattern = current.fillColor.getPattern( @@ -2136,11 +2184,24 @@ class CanvasGraphics { getCurrentTransformInverse(ctx), PathType.FILL ); - patternTransform = getCurrentTransform(ctx); + patternFillTransform = getCurrentTransform(ctx); ctx.restore(); ctx.fillStyle = pattern; } + if (current.patternStroke) { + ctx.save(); + const pattern = current.strokeColor.getPattern( + ctx, + this, + getCurrentTransformInverse(ctx), + PathType.STROKE + ); + patternStrokeTransform = getCurrentTransform(ctx); + ctx.restore(); + ctx.strokeStyle = pattern; + } + let lineWidth = current.lineWidth; const scale = current.textMatrixScale; if (scale === 0 || lineWidth === 0) { @@ -2233,7 +2294,13 @@ class CanvasGraphics { // common case ctx.fillText(character, scaledX, scaledY); } else { - this.paintChar(character, scaledX, scaledY, patternTransform); + this.paintChar( + character, + scaledX, + scaledY, + patternFillTransform, + patternStrokeTransform + ); if (accent) { const scaledAccentX = scaledX + (fontSize * accent.offset.x) / fontSizeScale; @@ -2243,7 +2310,8 @@ class CanvasGraphics { accent.fontChar, scaledAccentX, scaledAccentY, - patternTransform + patternFillTransform, + patternStrokeTransform ); } } @@ -2294,7 +2362,7 @@ class CanvasGraphics { ctx.save(); ctx.transform(...current.textMatrix); - ctx.translate(current.x, current.y); + ctx.translate(current.x, current.y + current.textRise); ctx.scale(textHScale, fontDirection); @@ -2379,6 +2447,7 @@ class CanvasGraphics { setStrokeColorN() { this.current.strokeColor = this.getColorN_Pattern(arguments); + this.current.patternStroke = true; } setFillColorN() { @@ -2392,10 +2461,12 @@ class CanvasGraphics { g, b ); + this.current.patternStroke = false; } setStrokeTransparent() { this.ctx.strokeStyle = this.current.strokeColor = "transparent"; + this.current.patternStroke = false; } setFillRGBColor(r, g, b) { diff --git a/src/display/canvas_factory.js b/src/display/canvas_factory.js new file mode 100644 index 0000000000000..988e764859828 --- /dev/null +++ b/src/display/canvas_factory.js @@ -0,0 +1,92 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { unreachable } from "../shared/util.js"; + +class BaseCanvasFactory { + #enableHWA = false; + + constructor({ enableHWA = false }) { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseCanvasFactory + ) { + unreachable("Cannot initialize BaseCanvasFactory."); + } + this.#enableHWA = enableHWA; + } + + create(width, height) { + if (width <= 0 || height <= 0) { + throw new Error("Invalid canvas size"); + } + const canvas = this._createCanvas(width, height); + return { + canvas, + context: canvas.getContext("2d", { + willReadFrequently: !this.#enableHWA, + }), + }; + } + + reset(canvasAndContext, width, height) { + if (!canvasAndContext.canvas) { + throw new Error("Canvas is not specified"); + } + if (width <= 0 || height <= 0) { + throw new Error("Invalid canvas size"); + } + canvasAndContext.canvas.width = width; + canvasAndContext.canvas.height = height; + } + + destroy(canvasAndContext) { + if (!canvasAndContext.canvas) { + throw new Error("Canvas is not specified"); + } + // Zeroing the width and height cause Firefox to release graphics + // resources immediately, which can greatly reduce memory consumption. + canvasAndContext.canvas.width = 0; + canvasAndContext.canvas.height = 0; + canvasAndContext.canvas = null; + canvasAndContext.context = null; + } + + /** + * @ignore + */ + _createCanvas(width, height) { + unreachable("Abstract method `_createCanvas` called."); + } +} + +class DOMCanvasFactory extends BaseCanvasFactory { + constructor({ ownerDocument = globalThis.document, enableHWA = false }) { + super({ enableHWA }); + this._document = ownerDocument; + } + + /** + * @ignore + */ + _createCanvas(width, height) { + const canvas = this._document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + return canvas; + } +} + +export { BaseCanvasFactory, DOMCanvasFactory }; diff --git a/src/display/cmap_reader_factory.js b/src/display/cmap_reader_factory.js new file mode 100644 index 0000000000000..5246fbb23dfa3 --- /dev/null +++ b/src/display/cmap_reader_factory.js @@ -0,0 +1,75 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { stringToBytes, unreachable } from "../shared/util.js"; +import { fetchData } from "./display_utils.js"; + +class BaseCMapReaderFactory { + constructor({ baseUrl = null, isCompressed = true }) { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseCMapReaderFactory + ) { + unreachable("Cannot initialize BaseCMapReaderFactory."); + } + this.baseUrl = baseUrl; + this.isCompressed = isCompressed; + } + + async fetch({ name }) { + if (!this.baseUrl) { + throw new Error( + "Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided." + ); + } + if (!name) { + throw new Error("CMap name must be specified."); + } + const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); + + return this._fetch(url) + .then(cMapData => ({ cMapData, isCompressed: this.isCompressed })) + .catch(reason => { + throw new Error( + `Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}` + ); + }); + } + + /** + * @ignore + * @returns {Promise} + */ + async _fetch(url) { + unreachable("Abstract method `_fetch` called."); + } +} + +class DOMCMapReaderFactory extends BaseCMapReaderFactory { + /** + * @ignore + */ + async _fetch(url) { + const data = await fetchData( + url, + /* type = */ this.isCompressed ? "arraybuffer" : "text" + ); + return data instanceof ArrayBuffer + ? new Uint8Array(data) + : stringToBytes(data); + } +} + +export { BaseCMapReaderFactory, DOMCMapReaderFactory }; diff --git a/src/display/display_utils.js b/src/display/display_utils.js index 60b4587ebacf1..fae0abce35a2d 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -13,18 +13,10 @@ * limitations under the License. */ -import { - BaseCanvasFactory, - BaseCMapReaderFactory, - BaseFilterFactory, - BaseStandardFontDataFactory, - BaseSVGFactory, -} from "./base_factory.js"; import { BaseException, FeatureTest, shadow, - stringToBytes, Util, warn, } from "../shared/util.js"; @@ -39,479 +31,6 @@ class PixelsPerInch { static PDF_TO_CSS_UNITS = this.CSS / this.PDF; } -/** - * FilterFactory aims to create some SVG filters we can use when drawing an - * image (or whatever) on a canvas. - * Filters aren't applied with ctx.putImageData because it just overwrites the - * underlying pixels. - * With these filters, it's possible for example to apply some transfer maps on - * an image without the need to apply them on the pixel arrays: the renderer - * does the magic for us. - */ -class DOMFilterFactory extends BaseFilterFactory { - #baseUrl; - - #_cache; - - #_defs; - - #docId; - - #document; - - #_hcmCache; - - #id = 0; - - constructor({ docId, ownerDocument = globalThis.document }) { - super(); - this.#docId = docId; - this.#document = ownerDocument; - } - - get #cache() { - return (this.#_cache ||= new Map()); - } - - get #hcmCache() { - return (this.#_hcmCache ||= new Map()); - } - - get #defs() { - if (!this.#_defs) { - const div = this.#document.createElement("div"); - const { style } = div; - style.visibility = "hidden"; - style.contain = "strict"; - style.width = style.height = 0; - style.position = "absolute"; - style.top = style.left = 0; - style.zIndex = -1; - - const svg = this.#document.createElementNS(SVG_NS, "svg"); - svg.setAttribute("width", 0); - svg.setAttribute("height", 0); - this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); - div.append(svg); - svg.append(this.#_defs); - this.#document.body.append(div); - } - return this.#_defs; - } - - #createTables(maps) { - if (maps.length === 1) { - const mapR = maps[0]; - const buffer = new Array(256); - for (let i = 0; i < 256; i++) { - buffer[i] = mapR[i] / 255; - } - - const table = buffer.join(","); - return [table, table, table]; - } - - const [mapR, mapG, mapB] = maps; - const bufferR = new Array(256); - const bufferG = new Array(256); - const bufferB = new Array(256); - for (let i = 0; i < 256; i++) { - bufferR[i] = mapR[i] / 255; - bufferG[i] = mapG[i] / 255; - bufferB[i] = mapB[i] / 255; - } - return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; - } - - #createUrl(id) { - if (this.#baseUrl === undefined) { - // Unless a ``-element is present a relative URL should work. - this.#baseUrl = ""; - - const url = this.#document.URL; - if (url !== this.#document.baseURI) { - if (isDataScheme(url)) { - warn('#createUrl: ignore "data:"-URL for performance reasons.'); - } else { - this.#baseUrl = url.split("#", 1)[0]; - } - } - } - return `url(${this.#baseUrl}#${id})`; - } - - addFilter(maps) { - if (!maps) { - return "none"; - } - - // When a page is zoomed the page is re-drawn but the maps are likely - // the same. - let value = this.#cache.get(maps); - if (value) { - return value; - } - - const [tableR, tableG, tableB] = this.#createTables(maps); - const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; - - value = this.#cache.get(key); - if (value) { - this.#cache.set(maps, value); - return value; - } - - // We create a SVG filter: feComponentTransferElement - // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement - - const id = `g_${this.#docId}_transfer_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(maps, url); - this.#cache.set(key, url); - - const filter = this.#createFilter(id); - this.#addTransferMapConversion(tableR, tableG, tableB, filter); - - return url; - } - - addHCMFilter(fgColor, bgColor) { - const key = `${fgColor}-${bgColor}`; - const filterName = "base"; - let info = this.#hcmCache.get(filterName); - if (info?.key === key) { - return info.url; - } - - if (info) { - info.filter?.remove(); - info.key = key; - info.url = "none"; - info.filter = null; - } else { - info = { - key, - url: "none", - filter: null, - }; - this.#hcmCache.set(filterName, info); - } - - if (!fgColor || !bgColor) { - return info.url; - } - - const fgRGB = this.#getRGB(fgColor); - fgColor = Util.makeHexColor(...fgRGB); - const bgRGB = this.#getRGB(bgColor); - bgColor = Util.makeHexColor(...bgRGB); - this.#defs.style.color = ""; - - if ( - (fgColor === "#000000" && bgColor === "#ffffff") || - fgColor === bgColor - ) { - return info.url; - } - - // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance - // - // Relative luminance: - // https://www.w3.org/TR/WCAG20/#relativeluminancedef - // - // We compute the rounded luminance of the default background color. - // Then for every color in the pdf, if its rounded luminance is the - // same as the background one then it's replaced by the new - // background color else by the foreground one. - const map = new Array(256); - for (let i = 0; i <= 255; i++) { - const x = i / 255; - map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; - } - const table = map.join(","); - - const id = `g_${this.#docId}_hcm_filter`; - const filter = (info.filter = this.#createFilter(id)); - this.#addTransferMapConversion(table, table, table, filter); - this.#addGrayConversion(filter); - - const getSteps = (c, n) => { - const start = fgRGB[c] / 255; - const end = bgRGB[c] / 255; - const arr = new Array(n + 1); - for (let i = 0; i <= n; i++) { - arr[i] = start + (i / n) * (end - start); - } - return arr.join(","); - }; - this.#addTransferMapConversion( - getSteps(0, 5), - getSteps(1, 5), - getSteps(2, 5), - filter - ); - - info.url = this.#createUrl(id); - return info.url; - } - - addAlphaFilter(map) { - // When a page is zoomed the page is re-drawn but the maps are likely - // the same. - let value = this.#cache.get(map); - if (value) { - return value; - } - - const [tableA] = this.#createTables([map]); - const key = `alpha_${tableA}`; - - value = this.#cache.get(key); - if (value) { - this.#cache.set(map, value); - return value; - } - - const id = `g_${this.#docId}_alpha_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(map, url); - this.#cache.set(key, url); - - const filter = this.#createFilter(id); - this.#addTransferMapAlphaConversion(tableA, filter); - - return url; - } - - addLuminosityFilter(map) { - // When a page is zoomed the page is re-drawn but the maps are likely - // the same. - let value = this.#cache.get(map || "luminosity"); - if (value) { - return value; - } - - let tableA, key; - if (map) { - [tableA] = this.#createTables([map]); - key = `luminosity_${tableA}`; - } else { - key = "luminosity"; - } - - value = this.#cache.get(key); - if (value) { - this.#cache.set(map, value); - return value; - } - - const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(map, url); - this.#cache.set(key, url); - - const filter = this.#createFilter(id); - this.#addLuminosityConversion(filter); - if (map) { - this.#addTransferMapAlphaConversion(tableA, filter); - } - - return url; - } - - addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { - const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; - let info = this.#hcmCache.get(filterName); - if (info?.key === key) { - return info.url; - } - - if (info) { - info.filter?.remove(); - info.key = key; - info.url = "none"; - info.filter = null; - } else { - info = { - key, - url: "none", - filter: null, - }; - this.#hcmCache.set(filterName, info); - } - - if (!fgColor || !bgColor) { - return info.url; - } - - const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); - let fgGray = Math.round( - 0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2] - ); - let bgGray = Math.round( - 0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2] - ); - let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map( - this.#getRGB.bind(this) - ); - if (bgGray < fgGray) { - [fgGray, bgGray, newFgRGB, newBgRGB] = [ - bgGray, - fgGray, - newBgRGB, - newFgRGB, - ]; - } - this.#defs.style.color = ""; - - // Now we can create the filters to highlight some canvas parts. - // The colors in the pdf will almost be Canvas and CanvasText, hence we - // want to filter them to finally get Highlight and HighlightText. - // Since we're in HCM the background color and the foreground color should - // be really different when converted to grayscale (if they're not then it - // means that we've a poor contrast). Once the canvas colors are converted - // to grayscale we can easily map them on their new colors. - // The grayscale step is important because if we've something like: - // fgColor = #FF.... - // bgColor = #FF.... - // then we are enable to map the red component on the new red components - // which can be different. - - const getSteps = (fg, bg, n) => { - const arr = new Array(256); - const step = (bgGray - fgGray) / n; - const newStart = fg / 255; - const newStep = (bg - fg) / (255 * n); - let prev = 0; - for (let i = 0; i <= n; i++) { - const k = Math.round(fgGray + i * step); - const value = newStart + i * newStep; - for (let j = prev; j <= k; j++) { - arr[j] = value; - } - prev = k + 1; - } - for (let i = prev; i < 256; i++) { - arr[i] = arr[prev - 1]; - } - return arr.join(","); - }; - - const id = `g_${this.#docId}_hcm_${filterName}_filter`; - const filter = (info.filter = this.#createFilter(id)); - - this.#addGrayConversion(filter); - this.#addTransferMapConversion( - getSteps(newFgRGB[0], newBgRGB[0], 5), - getSteps(newFgRGB[1], newBgRGB[1], 5), - getSteps(newFgRGB[2], newBgRGB[2], 5), - filter - ); - - info.url = this.#createUrl(id); - return info.url; - } - - destroy(keepHCM = false) { - if (keepHCM && this.#hcmCache.size !== 0) { - return; - } - if (this.#_defs) { - this.#_defs.parentNode.parentNode.remove(); - this.#_defs = null; - } - if (this.#_cache) { - this.#_cache.clear(); - this.#_cache = null; - } - this.#id = 0; - } - - #addLuminosityConversion(filter) { - const feColorMatrix = this.#document.createElementNS( - SVG_NS, - "feColorMatrix" - ); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute( - "values", - "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0" - ); - filter.append(feColorMatrix); - } - - #addGrayConversion(filter) { - const feColorMatrix = this.#document.createElementNS( - SVG_NS, - "feColorMatrix" - ); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute( - "values", - "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0" - ); - filter.append(feColorMatrix); - } - - #createFilter(id) { - const filter = this.#document.createElementNS(SVG_NS, "filter"); - filter.setAttribute("color-interpolation-filters", "sRGB"); - filter.setAttribute("id", id); - this.#defs.append(filter); - - return filter; - } - - #appendFeFunc(feComponentTransfer, func, table) { - const feFunc = this.#document.createElementNS(SVG_NS, func); - feFunc.setAttribute("type", "discrete"); - feFunc.setAttribute("tableValues", table); - feComponentTransfer.append(feFunc); - } - - #addTransferMapConversion(rTable, gTable, bTable, filter) { - const feComponentTransfer = this.#document.createElementNS( - SVG_NS, - "feComponentTransfer" - ); - filter.append(feComponentTransfer); - this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); - this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); - this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); - } - - #addTransferMapAlphaConversion(aTable, filter) { - const feComponentTransfer = this.#document.createElementNS( - SVG_NS, - "feComponentTransfer" - ); - filter.append(feComponentTransfer); - this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); - } - - #getRGB(color) { - this.#defs.style.color = color; - return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); - } -} - -class DOMCanvasFactory extends BaseCanvasFactory { - constructor({ ownerDocument = globalThis.document, enableHWA = false }) { - super({ enableHWA }); - this._document = ownerDocument; - } - - /** - * @ignore - */ - _createCanvas(width, height) { - const canvas = this._document.createElement("canvas"); - canvas.width = width; - canvas.height = height; - return canvas; - } -} - async function fetchData(url, type = "text") { if ( (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || @@ -560,44 +79,11 @@ async function fetchData(url, type = "text") { }); } -class DOMCMapReaderFactory extends BaseCMapReaderFactory { - /** - * @ignore - */ - async _fetch(url) { - const data = await fetchData( - url, - /* type = */ this.isCompressed ? "arraybuffer" : "text" - ); - return data instanceof ArrayBuffer - ? new Uint8Array(data) - : stringToBytes(data); - } -} - -class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { - /** - * @ignore - */ - async _fetch(url) { - const data = await fetchData(url, /* type = */ "arraybuffer"); - return new Uint8Array(data); - } -} - -class DOMSVGFactory extends BaseSVGFactory { - /** - * @ignore - */ - _createSVG(type) { - return document.createElementNS(SVG_NS, type); - } -} - /** * @typedef {Object} PageViewportParameters * @property {Array} viewBox - The xMin, yMin, xMax and * yMax coordinates. + * @property {number} userUnit - The size of units. * @property {number} scale - The scale of the viewport. * @property {number} rotation - The rotation, in degrees, of the viewport. * @property {number} [offsetX] - The horizontal, i.e. x-axis, offset. The @@ -631,6 +117,7 @@ class PageViewport { */ constructor({ viewBox, + userUnit, scale, rotation, offsetX = 0, @@ -638,11 +125,14 @@ class PageViewport { dontFlip = false, }) { this.viewBox = viewBox; + this.userUnit = userUnit; this.scale = scale; this.rotation = rotation; this.offsetX = offsetX; this.offsetY = offsetY; + scale *= userUnit; // Take the userUnit into account. + // creating transform to convert pdf coordinate system to the normal // canvas like coordinates taking in account scale and rotation const centerX = (viewBox[2] + viewBox[0]) / 2; @@ -723,12 +213,13 @@ class PageViewport { * @type {Object} */ get rawDims() { - const { viewBox } = this; + const dims = this.viewBox; + return shadow(this, "rawDims", { - pageWidth: viewBox[2] - viewBox[0], - pageHeight: viewBox[3] - viewBox[1], - pageX: viewBox[0], - pageY: viewBox[1], + pageWidth: dims[2] - dims[0], + pageHeight: dims[3] - dims[1], + pageX: dims[0], + pageY: dims[1], }); } @@ -746,6 +237,7 @@ class PageViewport { } = {}) { return new PageViewport({ viewBox: this.viewBox.slice(), + userUnit: this.userUnit, scale, rotation, offsetX, @@ -910,13 +402,9 @@ function isValidFetchUrl(url, baseUrl) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { throw new Error("Not implemented: isValidFetchUrl"); } - try { - const { protocol } = baseUrl ? new URL(url, baseUrl) : new URL(url); - // The Fetch API only supports the http/https protocols, and not file/ftp. - return protocol === "http:" || protocol === "https:"; - } catch { - return false; // `new URL()` will throw on incorrect data. - } + const res = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); + // The Fetch API only supports the http/https protocols, and not file/ftp. + return res?.protocol === "http:" || res?.protocol === "https:"; } /** @@ -926,8 +414,14 @@ function noContextMenu(e) { e.preventDefault(); } +function stopEvent(e) { + e.preventDefault(); + e.stopPropagation(); +} + // Deprecated API function -- display regardless of the `verbosity` setting. function deprecated(details) { + // eslint-disable-next-line no-console console.log("Deprecated API usage: " + details); } @@ -1023,6 +517,7 @@ function getXfaPageViewport(xfaPage, { scale = 1, rotation = 0 }) { return new PageViewport({ viewBox, + userUnit: 1, scale, rotation, }); @@ -1097,13 +592,13 @@ function setLayerDimensions( const { style } = div; const useRound = FeatureTest.isCSSRoundSupported; - const w = `var(--scale-factor) * ${pageWidth}px`, - h = `var(--scale-factor) * ${pageHeight}px`; + const w = `var(--total-scale-factor) * ${pageWidth}px`, + h = `var(--total-scale-factor) * ${pageHeight}px`; const widthStr = useRound - ? `round(down, ${w}, var(--scale-round-x, 1px))` + ? `round(down, ${w}, var(--scale-round-x))` : `calc(${w})`, heightStr = useRound - ? `round(down, ${h}, var(--scale-round-y, 1px))` + ? `round(down, ${h}, var(--scale-round-y))` : `calc(${h})`; if (!mustFlip || viewport.rotation % 180 === 0) { @@ -1150,13 +645,22 @@ class OutputScale { } } +// See https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types +// to know which types are supported by the browser. +const SupportedImageMimeTypes = [ + "image/apng", + "image/avif", + "image/bmp", + "image/gif", + "image/jpeg", + "image/png", + "image/svg+xml", + "image/webp", + "image/x-icon", +]; + export { deprecated, - DOMCanvasFactory, - DOMCMapReaderFactory, - DOMFilterFactory, - DOMStandardFontDataFactory, - DOMSVGFactory, fetchData, getColorValues, getCurrentTransform, @@ -1176,4 +680,7 @@ export { RenderingCancelledException, setLayerDimensions, StatTimer, + stopEvent, + SupportedImageMimeTypes, + SVG_NS, }; diff --git a/src/display/draw_layer.js b/src/display/draw_layer.js index 0b114bbe1d279..3c763f443f923 100644 --- a/src/display/draw_layer.js +++ b/src/display/draw_layer.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { DOMSVGFactory } from "./display_utils.js"; +import { DOMSVGFactory } from "./svg_factory.js"; import { shadow } from "../shared/util.js"; /** @@ -24,12 +24,12 @@ import { shadow } from "../shared/util.js"; class DrawLayer { #parent = null; - #id = 0; - #mapping = new Map(); #toUpdate = new Map(); + static #id = 0; + constructor({ pageIndex }) { this.pageIndex = pageIndex; } @@ -55,7 +55,7 @@ class DrawLayer { return shadow(this, "_svgFactory", new DOMSVGFactory()); } - static #setBox(element, { x = 0, y = 0, width = 1, height = 1 } = {}) { + static #setBox(element, [x, y, width, height]) { const { style } = element; style.top = `${100 * y}%`; style.left = `${100 * x}%`; @@ -63,11 +63,10 @@ class DrawLayer { style.height = `${100 * height}%`; } - #createSVG(box) { + #createSVG() { const svg = DrawLayer._svgFactory.create(1, 1, /* skipDimensions = */ true); this.#parent.append(svg); svg.setAttribute("aria-hidden", true); - DrawLayer.#setBox(svg, box); return svg; } @@ -86,59 +85,62 @@ class DrawLayer { return clipPathId; } - highlight(outlines, color, opacity, isPathUpdatable = false) { - const id = this.#id++; - const root = this.#createSVG(outlines.box); - root.classList.add("highlight"); - if (outlines.free) { - root.classList.add("free"); + #updateProperties(element, properties) { + for (const [key, value] of Object.entries(properties)) { + if (value === null) { + element.removeAttribute(key); + } else { + element.setAttribute(key, value); + } } + } + + draw(properties, isPathUpdatable = false, hasClip = false) { + const id = DrawLayer.#id++; + const root = this.#createSVG(); + const defs = DrawLayer._svgFactory.createElement("defs"); root.append(defs); const path = DrawLayer._svgFactory.createElement("path"); defs.append(path); const pathId = `path_p${this.pageIndex}_${id}`; path.setAttribute("id", pathId); - path.setAttribute("d", outlines.toSVGPath()); + path.setAttribute("vector-effect", "non-scaling-stroke"); if (isPathUpdatable) { this.#toUpdate.set(id, path); } // Create the clipping path for the editor div. - const clipPathId = this.#createClipPath(defs, pathId); + const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null; const use = DrawLayer._svgFactory.createElement("use"); root.append(use); - root.setAttribute("fill", color); - root.setAttribute("fill-opacity", opacity); use.setAttribute("href", `#${pathId}`); + this.updateProperties(root, properties); this.#mapping.set(id, root); return { id, clipPathId: `url(#${clipPathId})` }; } - highlightOutline(outlines) { + drawOutline(properties, mustRemoveSelfIntersections) { // We cannot draw the outline directly in the SVG for highlights because // it composes with its parent with mix-blend-mode: multiply. // But the outline has a different mix-blend-mode, so we need to draw it in // its own SVG. - const id = this.#id++; - const root = this.#createSVG(outlines.box); - root.classList.add("highlightOutline"); + const id = DrawLayer.#id++; + const root = this.#createSVG(); const defs = DrawLayer._svgFactory.createElement("defs"); root.append(defs); const path = DrawLayer._svgFactory.createElement("path"); defs.append(path); const pathId = `path_p${this.pageIndex}_${id}`; path.setAttribute("id", pathId); - path.setAttribute("d", outlines.toSVGPath()); path.setAttribute("vector-effect", "non-scaling-stroke"); let maskId; - if (outlines.free) { - root.classList.add("free"); + if (mustRemoveSelfIntersections) { const mask = DrawLayer._svgFactory.createElement("mask"); defs.append(mask); maskId = `mask_p${this.pageIndex}_${id}`; @@ -169,67 +171,64 @@ class DrawLayer { use1.classList.add("mainOutline"); use2.classList.add("secondaryOutline"); + this.updateProperties(root, properties); + this.#mapping.set(id, root); return id; } - finalizeLine(id, line) { - const path = this.#toUpdate.get(id); - this.#toUpdate.delete(id); - this.updateBox(id, line.box); - path.setAttribute("d", line.toSVGPath()); - } - - updateLine(id, line) { - const root = this.#mapping.get(id); - const defs = root.firstChild; - const path = defs.firstChild; - path.setAttribute("d", line.toSVGPath()); - } - - removeFreeHighlight(id) { - this.remove(id); + finalizeDraw(id, properties) { this.#toUpdate.delete(id); + this.updateProperties(id, properties); } - updatePath(id, line) { - this.#toUpdate.get(id).setAttribute("d", line.toSVGPath()); - } - - updateBox(id, box) { - DrawLayer.#setBox(this.#mapping.get(id), box); - } - - show(id, visible) { - this.#mapping.get(id).classList.toggle("hidden", !visible); - } - - rotate(id, angle) { - this.#mapping.get(id).setAttribute("data-main-rotation", angle); - } - - changeColor(id, color) { - this.#mapping.get(id).setAttribute("fill", color); - } - - changeOpacity(id, opacity) { - this.#mapping.get(id).setAttribute("fill-opacity", opacity); - } - - addClass(id, className) { - this.#mapping.get(id).classList.add(className); - } - - removeClass(id, className) { - this.#mapping.get(id).classList.remove(className); + updateProperties(elementOrId, properties) { + if (!properties) { + return; + } + const { root, bbox, rootClass, path } = properties; + const element = + typeof elementOrId === "number" + ? this.#mapping.get(elementOrId) + : elementOrId; + if (!element) { + return; + } + if (root) { + this.#updateProperties(element, root); + } + if (bbox) { + DrawLayer.#setBox(element, bbox); + } + if (rootClass) { + const { classList } = element; + for (const [className, value] of Object.entries(rootClass)) { + classList.toggle(className, value); + } + } + if (path) { + const defs = element.firstChild; + const pathElement = defs.firstChild; + this.#updateProperties(pathElement, path); + } } - getSVGRoot(id) { - return this.#mapping.get(id); + updateParent(id, layer) { + if (layer === this) { + return; + } + const root = this.#mapping.get(id); + if (!root) { + return; + } + layer.#parent.append(root); + this.#mapping.delete(id); + layer.#mapping.set(id, root); } remove(id) { + this.#toUpdate.delete(id); if (this.#parent === null) { return; } @@ -243,6 +242,7 @@ class DrawLayer { root.remove(); } this.#mapping.clear(); + this.#toUpdate.clear(); } } diff --git a/src/display/editor/alt_text.js b/src/display/editor/alt_text.js index cc7181d855a8c..6ccdea85f4b94 100644 --- a/src/display/editor/alt_text.js +++ b/src/display/editor/alt_text.js @@ -22,6 +22,8 @@ class AltText { #altTextButton = null; + #altTextButtonLabel = null; + #altTextTooltip = null; #altTextTooltipTimeout = null; @@ -40,38 +42,46 @@ class AltText { static #l10nNewButton = null; - static _l10nPromise = null; + static _l10n = null; constructor(editor) { this.#editor = editor; this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; AltText.#l10nNewButton ||= Object.freeze({ - added: "pdfjs-editor-new-alt-text-added-button-label", - missing: "pdfjs-editor-new-alt-text-missing-button-label", - review: "pdfjs-editor-new-alt-text-to-review-button-label", + added: "pdfjs-editor-new-alt-text-added-button", + "added-label": "pdfjs-editor-new-alt-text-added-button-label", + missing: "pdfjs-editor-new-alt-text-missing-button", + "missing-label": "pdfjs-editor-new-alt-text-missing-button-label", + review: "pdfjs-editor-new-alt-text-to-review-button", + "review-label": "pdfjs-editor-new-alt-text-to-review-button-label", }); } - static initialize(l10nPromise) { - AltText._l10nPromise ||= l10nPromise; + static initialize(l10n) { + AltText._l10n ??= l10n; } async render() { const altText = (this.#altTextButton = document.createElement("button")); altText.className = "altText"; - let msg; + altText.tabIndex = "0"; + + const label = (this.#altTextButtonLabel = document.createElement("span")); + altText.append(label); + if (this.#useNewAltTextFlow) { altText.classList.add("new"); - msg = await AltText._l10nPromise.get(AltText.#l10nNewButton.missing); - } else { - msg = await AltText._l10nPromise.get( - "pdfjs-editor-alt-text-button-label" + altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing); + label.setAttribute( + "data-l10n-id", + AltText.#l10nNewButton["missing-label"] ); + } else { + altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button"); + label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label"); } - altText.textContent = msg; - altText.setAttribute("aria-label", msg); - altText.tabIndex = "0"; + const signal = this.#editor._uiManager._signal; altText.addEventListener("contextmenu", noContextMenu, { signal }); altText.addEventListener("pointerdown", event => event.stopPropagation(), { @@ -144,9 +154,10 @@ class AltText { return; } this.#guessedText = guessedText; - this.#textWithDisclaimer = await AltText._l10nPromise.get( - "pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer" - )({ generatedAltText: guessedText }); + this.#textWithDisclaimer = await AltText._l10n.get( + "pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", + { generatedAltText: guessedText } + ); this.#setState(); } @@ -229,6 +240,7 @@ class AltText { destroy() { this.#altTextButton?.remove(); this.#altTextButton = null; + this.#altTextButtonLabel = null; this.#altTextTooltip = null; this.#badge?.remove(); this.#badge = null; @@ -242,19 +254,12 @@ class AltText { if (this.#useNewAltTextFlow) { button.classList.toggle("done", !!this.#altText); - AltText._l10nPromise - .get(AltText.#l10nNewButton[this.#label]) - .then(msg => { - button.setAttribute("aria-label", msg); - // We can't just use button.textContent here, because it would remove - // the existing tooltip element. - for (const child of button.childNodes) { - if (child.nodeType === Node.TEXT_NODE) { - child.textContent = msg; - break; - } - } - }); + button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]); + + this.#altTextButtonLabel?.setAttribute( + "data-l10n-id", + AltText.#l10nNewButton[`${this.#label}-label`] + ); if (!this.#altText) { this.#altTextTooltip?.remove(); return; @@ -266,11 +271,7 @@ class AltText { return; } button.classList.add("done"); - AltText._l10nPromise - .get("pdfjs-editor-alt-text-edit-button-label") - .then(msg => { - button.setAttribute("aria-label", msg); - }); + button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button"); } let tooltip = this.#altTextTooltip; @@ -315,11 +316,15 @@ class AltText { { signal } ); } - tooltip.innerText = this.#altTextDecorative - ? await AltText._l10nPromise.get( - "pdfjs-editor-alt-text-decorative-tooltip" - ) - : this.#altText; + if (this.#altTextDecorative) { + tooltip.setAttribute( + "data-l10n-id", + "pdfjs-editor-alt-text-decorative-tooltip" + ); + } else { + tooltip.removeAttribute("data-l10n-id"); + tooltip.textContent = this.#altText; + } if (!tooltip.parentNode) { button.append(tooltip); diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index 1cce512288524..b15d64cc13fdb 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -31,6 +31,7 @@ import { FreeTextEditor } from "./freetext.js"; import { HighlightEditor } from "./highlight.js"; import { InkEditor } from "./ink.js"; import { setLayerDimensions } from "../display_utils.js"; +import { SignatureEditor } from "./signature.js"; import { StampEditor } from "./stamp.js"; /** @@ -72,10 +73,14 @@ class AnnotationEditorLayer { #hadPointerDown = false; - #isCleaningUp = false; - #isDisabling = false; + #isEnabling = false; + + #drawingAC = null; + + #focusedElement = null; + #textLayer = null; #textSelectionAC = null; @@ -85,10 +90,13 @@ class AnnotationEditorLayer { static _initialized = false; static #editorTypes = new Map( - [FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [ - type._editorType, - type, - ]) + [ + FreeTextEditor, + InkEditor, + StampEditor, + HighlightEditor, + SignatureEditor, + ].map(type => [type._editorType, type]) ); /** @@ -160,12 +168,9 @@ class AnnotationEditorLayer { this.disableClick(); return; case AnnotationEditorType.INK: - // We always want to have an ink editor ready to draw in. - this.addInkEditorIfNeeded(false); - this.disableTextSelection(); this.togglePointerEvents(true); - this.disableClick(); + this.enableClick(); break; case AnnotationEditorType.HIGHLIGHT: this.enableTextSelection(); @@ -193,30 +198,6 @@ class AnnotationEditorLayer { return textLayer === this.#textLayer?.div; } - addInkEditorIfNeeded(isCommitting) { - if (this.#uiManager.getMode() !== AnnotationEditorType.INK) { - // We don't want to add an ink editor if we're not in ink mode! - return; - } - - if (!isCommitting) { - // We're removing an editor but an empty one can already exist so in this - // case we don't need to create a new one. - for (const editor of this.#editors.values()) { - if (editor.isEmpty()) { - editor.setInBackground(); - return; - } - } - } - - const editor = this.createAndAddNewEditor( - { offsetX: 0, offsetY: 0 }, - /* isCentered = */ false - ); - editor.setInBackground(); - } - /** * Set the editing state. * @param {boolean} isEditing @@ -233,6 +214,10 @@ class AnnotationEditorLayer { this.#uiManager.addCommands(params); } + cleanUndoStack(type) { + this.#uiManager.cleanUndoStack(type); + } + toggleDrawing(enabled = false) { this.div.classList.toggle("drawing", !enabled); } @@ -250,6 +235,7 @@ class AnnotationEditorLayer { * editor creation. */ async enable() { + this.#isEnabling = true; this.div.tabIndex = 0; this.togglePointerEvents(true); const annotationElementIds = new Set(); @@ -263,6 +249,7 @@ class AnnotationEditorLayer { } if (!this.#annotationLayer) { + this.#isEnabling = false; return; } @@ -283,6 +270,7 @@ class AnnotationEditorLayer { this.addOrRebuild(editor); editor.enableEditing(); } + this.#isEnabling = false; } /** @@ -443,9 +431,9 @@ class AnnotationEditorLayer { this.div.addEventListener("pointerdown", this.pointerdown.bind(this), { signal, }); - this.div.addEventListener("pointerup", this.pointerup.bind(this), { - signal, - }); + const pointerup = this.pointerup.bind(this); + this.div.addEventListener("pointerup", pointerup, { signal }); + this.div.addEventListener("pointercancel", pointerup, { signal }); } disableClick() { @@ -482,10 +470,6 @@ class AnnotationEditorLayer { this.#uiManager.removeEditor(editor); editor.div.remove(); editor.isAttachedToDOM = false; - - if (!this.#isCleaningUp) { - this.addInkEditorIfNeeded(/* isCommitting = */ false); - } } /** @@ -533,7 +517,7 @@ class AnnotationEditorLayer { // The editor will be correctly moved into the DOM (see fixAndSetPosition). editor.fixAndSetPosition(); - editor.onceAdded(); + editor.onceAdded(/* focus = */ !this.#isEnabling); this.#uiManager.addToAnnotationStorage(editor); editor._reportTelemetry(editor.telemetryInitialData); } @@ -637,9 +621,9 @@ class AnnotationEditorLayer { * @param {number} mode * @param {Object} params */ - pasteEditor(mode, params) { + async pasteEditor(mode, params) { this.#uiManager.updateToolbar(mode); - this.#uiManager.updateMode(mode); + await this.#uiManager.updateMode(mode); const { offsetX, offsetY } = this.#getCenterPoint(); const id = this.getNextId(); @@ -714,8 +698,12 @@ class AnnotationEditorLayer { /** * Create and add a new editor. */ - addNewEditor() { - this.createAndAddNewEditor(this.#getCenterPoint(), /* isCentered = */ true); + addNewEditor(data = {}) { + this.createAndAddNewEditor( + this.#getCenterPoint(), + /* isCentered = */ true, + data + ); } /** @@ -766,12 +754,23 @@ class AnnotationEditorLayer { } this.#hadPointerDown = false; + if ( + this.#currentEditorType?.isDrawer && + this.#currentEditorType.supportMultipleDrawings + ) { + return; + } + if (!this.#allowClick) { this.#allowClick = true; return; } - if (this.#uiManager.getMode() === AnnotationEditorType.STAMP) { + const currentMode = this.#uiManager.getMode(); + if ( + currentMode === AnnotationEditorType.STAMP || + currentMode === AnnotationEditorType.SIGNATURE + ) { this.#uiManager.unselectAll(); return; } @@ -808,10 +807,67 @@ class AnnotationEditorLayer { this.#hadPointerDown = true; + if (this.#currentEditorType?.isDrawer) { + this.startDrawingSession(event); + return; + } + const editor = this.#uiManager.getActive(); this.#allowClick = !editor || editor.isEmpty(); } + startDrawingSession(event) { + this.div.focus({ + preventScroll: true, + }); + if (this.#drawingAC) { + this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); + return; + } + + this.#uiManager.setCurrentDrawingSession(this); + this.#drawingAC = new AbortController(); + const signal = this.#uiManager.combinedSignal(this.#drawingAC); + this.div.addEventListener( + "blur", + ({ relatedTarget }) => { + if (relatedTarget && !this.div.contains(relatedTarget)) { + this.#focusedElement = null; + this.commitOrRemove(); + } + }, + { signal } + ); + this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); + } + + pause(on) { + if (on) { + const { activeElement } = document; + if (this.div.contains(activeElement)) { + this.#focusedElement = activeElement; + } + return; + } + if (this.#focusedElement) { + setTimeout(() => { + this.#focusedElement?.focus(); + this.#focusedElement = null; + }, 0); + } + } + + endDrawingSession(isAborted = false) { + if (!this.#drawingAC) { + return null; + } + this.#uiManager.setCurrentDrawingSession(null); + this.#drawingAC.abort(); + this.#drawingAC = null; + this.#focusedElement = null; + return this.#currentEditorType.endDrawing(isAborted); + } + /** * * @param {AnnotationEditor} editor @@ -828,10 +884,26 @@ class AnnotationEditorLayer { return true; } + commitOrRemove() { + if (this.#drawingAC) { + this.endDrawingSession(); + return true; + } + return false; + } + + onScaleChanging() { + if (!this.#drawingAC) { + return; + } + this.#currentEditorType.onScaleChangingWhenDrawing(this); + } + /** * Destroy the main editor. */ destroy() { + this.commitOrRemove(); if (this.#uiManager.getActive()?.parent === this) { // We need to commit the current editor before destroying the layer. this.#uiManager.commitOrRemove(); @@ -858,13 +930,11 @@ class AnnotationEditorLayer { // When we're cleaning up, some editors are removed but we don't want // to add a new one which will induce an addition in this.#editors, hence // an infinite loop. - this.#isCleaningUp = true; for (const editor of this.#editors.values()) { if (editor.isEmpty()) { editor.remove(); } } - this.#isCleaningUp = false; } /** @@ -896,6 +966,7 @@ class AnnotationEditorLayer { const oldRotation = this.viewport.rotation; const rotation = viewport.rotation; + this.viewport = viewport; setLayerDimensions(this.div, { rotation }); if (oldRotation !== rotation) { @@ -903,7 +974,6 @@ class AnnotationEditorLayer { editor.rotate(rotation); } } - this.addInkEditorIfNeeded(/* isCommitting = */ false); } /** diff --git a/src/display/editor/draw.js b/src/display/editor/draw.js new file mode 100644 index 0000000000000..e8c2e8ba46442 --- /dev/null +++ b/src/display/editor/draw.js @@ -0,0 +1,994 @@ +/* Copyright 2022 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnnotationEditorParamsType, unreachable } from "../../shared/util.js"; +import { noContextMenu, stopEvent } from "../display_utils.js"; +import { AnnotationEditor } from "./editor.js"; + +class DrawingOptions { + #svgProperties = Object.create(null); + + updateProperty(name, value) { + this[name] = value; + this.updateSVGProperty(name, value); + } + + updateProperties(properties) { + if (!properties) { + return; + } + for (const [name, value] of Object.entries(properties)) { + if (!name.startsWith("_")) { + this.updateProperty(name, value); + } + } + } + + updateSVGProperty(name, value) { + this.#svgProperties[name] = value; + } + + toSVGProperties() { + const root = this.#svgProperties; + this.#svgProperties = Object.create(null); + return { root }; + } + + reset() { + this.#svgProperties = Object.create(null); + } + + updateAll(options = this) { + this.updateProperties(options); + } + + clone() { + unreachable("Not implemented"); + } +} + +/** + * Basic draw editor. + */ +class DrawingEditor extends AnnotationEditor { + #drawOutlines = null; + + #mustBeCommitted; + + _drawId = null; + + static _currentDrawId = -1; + + static _currentParent = null; + + static #currentDraw = null; + + static #currentDrawingAC = null; + + static #currentDrawingOptions = null; + + static #currentPointerId = NaN; + + static #currentPointerType = null; + + static #currentPointerIds = null; + + static #currentMoveTimestamp = NaN; + + static _INNER_MARGIN = 3; + + constructor(params) { + super(params); + this.#mustBeCommitted = params.mustBeCommitted || false; + + this._addOutlines(params); + } + + _addOutlines(params) { + if (params.drawOutlines) { + this.#createDrawOutlines(params); + this.#addToDrawLayer(); + } + } + + #createDrawOutlines({ drawOutlines, drawId, drawingOptions }) { + this.#drawOutlines = drawOutlines; + this._drawingOptions ||= drawingOptions; + + if (drawId >= 0) { + this._drawId = drawId; + // We need to redraw the drawing because we changed the coordinates to be + // in the box coordinate system. + this.parent.drawLayer.finalizeDraw( + drawId, + drawOutlines.defaultProperties + ); + } else { + // We create a new drawing. + this._drawId = this.#createDrawing(drawOutlines, this.parent); + } + + this.#updateBbox(drawOutlines.box); + } + + #createDrawing(drawOutlines, parent) { + const { id } = parent.drawLayer.draw( + DrawingEditor._mergeSVGProperties( + this._drawingOptions.toSVGProperties(), + drawOutlines.defaultSVGProperties + ), + /* isPathUpdatable = */ false, + /* hasClip = */ false + ); + return id; + } + + static _mergeSVGProperties(p1, p2) { + const p1Keys = new Set(Object.keys(p1)); + + for (const [key, value] of Object.entries(p2)) { + if (p1Keys.has(key)) { + Object.assign(p1[key], value); + } else { + p1[key] = value; + } + } + return p1; + } + + /** + * @param {Object} options + * @return {DrawingOptions} the default options to use for a new editor. + */ + static getDefaultDrawingOptions(_options) { + unreachable("Not implemented"); + } + + /** + * @return {Map} a map between the + * parameter types and the name of the options. + */ + // eslint-disable-next-line getter-return + static get typesMap() { + unreachable("Not implemented"); + } + + static get isDrawer() { + return true; + } + + /** + * @returns {boolean} `true` if several drawings can be added to the + * annotation. + */ + static get supportMultipleDrawings() { + return false; + } + + /** @inheritdoc */ + static updateDefaultParams(type, value) { + const propertyName = this.typesMap.get(type); + if (propertyName) { + this._defaultDrawingOptions.updateProperty(propertyName, value); + } + if (this._currentParent) { + DrawingEditor.#currentDraw.updateProperty(propertyName, value); + this._currentParent.drawLayer.updateProperties( + this._currentDrawId, + this._defaultDrawingOptions.toSVGProperties() + ); + } + } + + /** @inheritdoc */ + updateParams(type, value) { + const propertyName = this.constructor.typesMap.get(type); + if (propertyName) { + this._updateProperty(type, propertyName, value); + } + } + + /** @inheritdoc */ + static get defaultPropertiesToUpdate() { + const properties = []; + const options = this._defaultDrawingOptions; + for (const [type, name] of this.typesMap) { + properties.push([type, options[name]]); + } + return properties; + } + + /** @inheritdoc */ + get propertiesToUpdate() { + const properties = []; + const { _drawingOptions } = this; + for (const [type, name] of this.constructor.typesMap) { + properties.push([type, _drawingOptions[name]]); + } + return properties; + } + + /** + * Update a property and make this action undoable. + * @param {string} color + */ + _updateProperty(type, name, value) { + const options = this._drawingOptions; + const savedValue = options[name]; + const setter = val => { + options.updateProperty(name, val); + const bbox = this.#drawOutlines.updateProperty(name, val); + if (bbox) { + this.#updateBbox(bbox); + } + this.parent?.drawLayer.updateProperties( + this._drawId, + options.toSVGProperties() + ); + }; + this.addCommands({ + cmd: setter.bind(this, value), + undo: setter.bind(this, savedValue), + post: this._uiManager.updateUI.bind(this._uiManager, this), + mustExec: true, + type, + overwriteIfSameType: true, + keepUndo: true, + }); + } + + /** @inheritdoc */ + _onResizing() { + this.parent?.drawLayer.updateProperties( + this._drawId, + DrawingEditor._mergeSVGProperties( + this.#drawOutlines.getPathResizingSVGProperties( + this.#convertToDrawSpace() + ), + { + bbox: this.#rotateBox(), + } + ) + ); + } + + /** @inheritdoc */ + _onResized() { + this.parent?.drawLayer.updateProperties( + this._drawId, + DrawingEditor._mergeSVGProperties( + this.#drawOutlines.getPathResizedSVGProperties( + this.#convertToDrawSpace() + ), + { + bbox: this.#rotateBox(), + } + ) + ); + } + + /** @inheritdoc */ + _onTranslating(_x, _y) { + this.parent?.drawLayer.updateProperties(this._drawId, { + bbox: this.#rotateBox(), + }); + } + + /** @inheritdoc */ + _onTranslated() { + this.parent?.drawLayer.updateProperties( + this._drawId, + DrawingEditor._mergeSVGProperties( + this.#drawOutlines.getPathTranslatedSVGProperties( + this.#convertToDrawSpace(), + this.parentDimensions + ), + { + bbox: this.#rotateBox(), + } + ) + ); + } + + _onStartDragging() { + this.parent?.drawLayer.updateProperties(this._drawId, { + rootClass: { + moving: true, + }, + }); + } + + _onStopDragging() { + this.parent?.drawLayer.updateProperties(this._drawId, { + rootClass: { + moving: false, + }, + }); + } + + /** @inheritdoc */ + commit() { + super.commit(); + + this.disableEditMode(); + this.disableEditing(); + } + + /** @inheritdoc */ + disableEditing() { + super.disableEditing(); + this.div.classList.toggle("disabled", true); + } + + /** @inheritdoc */ + enableEditing() { + super.enableEditing(); + this.div.classList.toggle("disabled", false); + } + + /** @inheritdoc */ + getBaseTranslation() { + // The editor itself doesn't have any CSS border (we're drawing one + // ourselves in using SVG). + return [0, 0]; + } + + /** @inheritdoc */ + get isResizable() { + return true; + } + + /** @inheritdoc */ + onceAdded(focus) { + if (!this.annotationElementId) { + this.parent.addUndoableEditor(this); + } + this._isDraggable = true; + if (this.#mustBeCommitted) { + this.#mustBeCommitted = false; + this.commit(); + this.parent.setSelected(this); + if (focus && this.isOnScreen) { + this.div.focus(); + } + } + } + + /** @inheritdoc */ + remove() { + this.#cleanDrawLayer(); + super.remove(); + } + + /** @inheritdoc */ + rebuild() { + if (!this.parent) { + return; + } + super.rebuild(); + if (this.div === null) { + return; + } + + this.#addToDrawLayer(); + this.#updateBbox(this.#drawOutlines.box); + + if (!this.isAttachedToDOM) { + // At some point this editor was removed and we're rebuilding it, + // hence we must add it to its parent. + this.parent.add(this); + } + } + + setParent(parent) { + let mustBeSelected = false; + if (this.parent && !parent) { + this._uiManager.removeShouldRescale(this); + this.#cleanDrawLayer(); + } else if (parent) { + this._uiManager.addShouldRescale(this); + this.#addToDrawLayer(parent); + // If mustBeSelected is true it means that this editor was selected + // when its parent has been destroyed, hence we must select it again. + mustBeSelected = + !this.parent && this.div?.classList.contains("selectedEditor"); + } + super.setParent(parent); + if (mustBeSelected) { + // We select it after the parent has been set. + this.select(); + } + } + + #cleanDrawLayer() { + if (this._drawId === null || !this.parent) { + return; + } + this.parent.drawLayer.remove(this._drawId); + this._drawId = null; + + // All the SVG properties must be reset in order to make it possible to + // undo. + this._drawingOptions.reset(); + } + + #addToDrawLayer(parent = this.parent) { + if (this._drawId !== null && this.parent === parent) { + return; + } + if (this._drawId !== null) { + // The parent has changed, we need to move the drawing to the new parent. + this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer); + return; + } + this._drawingOptions.updateAll(); + this._drawId = this.#createDrawing(this.#drawOutlines, parent); + } + + #convertToParentSpace([x, y, width, height]) { + const { + parentDimensions: [pW, pH], + rotation, + } = this; + switch (rotation) { + case 90: + return [y, 1 - x, width * (pH / pW), height * (pW / pH)]; + case 180: + return [1 - x, 1 - y, width, height]; + case 270: + return [1 - y, x, width * (pH / pW), height * (pW / pH)]; + default: + return [x, y, width, height]; + } + } + + #convertToDrawSpace() { + const { + x, + y, + width, + height, + parentDimensions: [pW, pH], + rotation, + } = this; + switch (rotation) { + case 90: + return [1 - y, x, width * (pW / pH), height * (pH / pW)]; + case 180: + return [1 - x, 1 - y, width, height]; + case 270: + return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; + default: + return [x, y, width, height]; + } + } + + #updateBbox(bbox) { + [this.x, this.y, this.width, this.height] = + this.#convertToParentSpace(bbox); + if (this.div) { + this.fixAndSetPosition(); + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + } + this._onResized(); + } + + #rotateBox() { + // We've to deal with two rotations: the rotation of the annotation and the + // rotation of the parent page. + // When the page is rotated, all the layers are just rotated thanks to CSS + // but there is a notable exception: the canvas wrapper. + // The canvas wrapper is not rotated but the dimensions are (or not) swapped + // and the page is redrawn with the rotation applied to the canvas. + // The drawn layer is under the canvas wrapper and is not rotated so we have + // to "manually" rotate the coordinates. + // + // The coordinates (this.x, this.y) correspond to the top-left corner of + // the editor after it has been rotated in the page coordinate system. + + const { + x, + y, + width, + height, + rotation, + parentRotation, + parentDimensions: [pW, pH], + } = this; + switch ((rotation * 4 + parentRotation) / 90) { + case 1: + // 0 -> 90 + return [1 - y - height, x, height, width]; + case 2: + // 0 -> 180 + return [1 - x - width, 1 - y - height, width, height]; + case 3: + // 0 -> 270 + return [y, 1 - x - width, height, width]; + case 4: + // 90 -> 0 + return [ + x, + y - width * (pW / pH), + height * (pH / pW), + width * (pW / pH), + ]; + case 5: + // 90 -> 90 + return [1 - y, x, width * (pW / pH), height * (pH / pW)]; + case 6: + // 90 -> 180 + return [ + 1 - x - height * (pH / pW), + 1 - y, + height * (pH / pW), + width * (pW / pH), + ]; + case 7: + // 90 -> 270 + return [ + y - width * (pW / pH), + 1 - x - height * (pH / pW), + width * (pW / pH), + height * (pH / pW), + ]; + case 8: + // 180 -> 0 + return [x - width, y - height, width, height]; + case 9: + // 180 -> 90 + return [1 - y, x - width, height, width]; + case 10: + // 180 -> 180 + return [1 - x, 1 - y, width, height]; + case 11: + // 180 -> 270 + return [y - height, 1 - x, height, width]; + case 12: + // 270 -> 0 + return [ + x - height * (pH / pW), + y, + height * (pH / pW), + width * (pW / pH), + ]; + case 13: + // 270 -> 90 + return [ + 1 - y - width * (pW / pH), + x - height * (pH / pW), + width * (pW / pH), + height * (pH / pW), + ]; + case 14: + // 270 -> 180 + return [ + 1 - x, + 1 - y - width * (pW / pH), + height * (pH / pW), + width * (pW / pH), + ]; + case 15: + // 270 -> 270 + return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; + default: + // 0 -> 0 + return [x, y, width, height]; + } + } + + /** @inheritdoc */ + rotate() { + if (!this.parent) { + return; + } + this.parent.drawLayer.updateProperties( + this._drawId, + DrawingEditor._mergeSVGProperties( + { + bbox: this.#rotateBox(), + }, + this.#drawOutlines.updateRotation( + (this.parentRotation - this.rotation + 360) % 360 + ) + ) + ); + } + + onScaleChanging() { + if (!this.parent) { + return; + } + this.#updateBbox( + this.#drawOutlines.updateParentDimensions( + this.parentDimensions, + this.parent.scale + ) + ); + } + + static onScaleChangingWhenDrawing() {} + + /** @inheritdoc */ + render() { + if (this.div) { + return this.div; + } + + let baseX, baseY; + if (this._isCopy) { + baseX = this.x; + baseY = this.y; + } + + const div = super.render(); + div.classList.add("draw"); + + const drawDiv = document.createElement("div"); + div.append(drawDiv); + drawDiv.setAttribute("aria-hidden", "true"); + drawDiv.className = "internal"; + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(this.width * parentWidth, this.height * parentHeight); + this._uiManager.addShouldRescale(this); + this.disableEditing(); + + if (this._isCopy) { + this._moveAfterPaste(baseX, baseY); + } + + return div; + } + + /** + * Create a new drawer instance. + * @param {number} x - The x coordinate of the event. + * @param {number} y - The y coordinate of the event. + * @param {number} parentWidth - The parent width. + * @param {number} parentHeight - The parent height. + * @param {number} rotation - The parent rotation. + */ + static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) { + unreachable("Not implemented"); + } + + static startDrawing(parent, uiManager, _isLTR, event) { + // The _currentPointerType is set when the user starts an empty drawing + // session. If, in the same drawing session, the user starts using a + // different type of pointer (e.g. a pen and then a finger), we just return. + // + // The _currentPointerId and _currentPointerIds are used to keep track of + // the pointers with a same type (e.g. two fingers). If the user starts to + // draw with a finger and then uses a second finger, we just stop the + // current drawing and let the user zoom the document. + + const { target, offsetX: x, offsetY: y, pointerId, pointerType } = event; + if ( + DrawingEditor.#currentPointerType && + DrawingEditor.#currentPointerType !== pointerType + ) { + return; + } + + const { + viewport: { rotation }, + } = parent; + const { width: parentWidth, height: parentHeight } = + target.getBoundingClientRect(); + + const ac = (DrawingEditor.#currentDrawingAC = new AbortController()); + const signal = parent.combinedSignal(ac); + + DrawingEditor.#currentPointerId ||= pointerId; + DrawingEditor.#currentPointerType ??= pointerType; + + window.addEventListener( + "pointerup", + e => { + if (DrawingEditor.#currentPointerId === e.pointerId) { + this._endDraw(e); + } else { + DrawingEditor.#currentPointerIds?.delete(e.pointerId); + } + }, + { signal } + ); + window.addEventListener( + "pointercancel", + e => { + if (DrawingEditor.#currentPointerId === e.pointerId) { + this._currentParent.endDrawingSession(); + } else { + DrawingEditor.#currentPointerIds?.delete(e.pointerId); + } + }, + { signal } + ); + window.addEventListener( + "pointerdown", + e => { + if (DrawingEditor.#currentPointerType !== e.pointerType) { + // For example, we started with a pen and the user + // is now using a finger. + return; + } + + // For example, the user is using a second finger. + (DrawingEditor.#currentPointerIds ||= new Set()).add(e.pointerId); + + // The first finger created a first point and a second finger just + // started, so we stop the drawing and remove this only point. + if (DrawingEditor.#currentDraw.isCancellable()) { + DrawingEditor.#currentDraw.removeLastElement(); + if (DrawingEditor.#currentDraw.isEmpty()) { + this._currentParent.endDrawingSession(/* isAborted = */ true); + } else { + this._endDraw(null); + } + } + }, + { + capture: true, + passive: false, + signal, + } + ); + window.addEventListener("contextmenu", noContextMenu, { signal }); + target.addEventListener("pointermove", this._drawMove.bind(this), { + signal, + }); + target.addEventListener( + "touchmove", + e => { + if (e.timeStamp === DrawingEditor.#currentMoveTimestamp) { + // This move event is used to draw so we don't want to scroll. + stopEvent(e); + } + }, + { signal } + ); + parent.toggleDrawing(); + uiManager._editorUndoBar?.hide(); + + if (DrawingEditor.#currentDraw) { + parent.drawLayer.updateProperties( + this._currentDrawId, + DrawingEditor.#currentDraw.startNew( + x, + y, + parentWidth, + parentHeight, + rotation + ) + ); + return; + } + + uiManager.updateUIForDefaultProperties(this); + + DrawingEditor.#currentDraw = this.createDrawerInstance( + x, + y, + parentWidth, + parentHeight, + rotation + ); + DrawingEditor.#currentDrawingOptions = this.getDefaultDrawingOptions(); + this._currentParent = parent; + + ({ id: this._currentDrawId } = parent.drawLayer.draw( + this._mergeSVGProperties( + DrawingEditor.#currentDrawingOptions.toSVGProperties(), + DrawingEditor.#currentDraw.defaultSVGProperties + ), + /* isPathUpdatable = */ true, + /* hasClip = */ false + )); + } + + static _drawMove(event) { + DrawingEditor.#currentMoveTimestamp = -1; + if (!DrawingEditor.#currentDraw) { + return; + } + const { offsetX, offsetY, pointerId } = event; + + if (DrawingEditor.#currentPointerId !== pointerId) { + return; + } + if (DrawingEditor.#currentPointerIds?.size >= 1) { + // The user is using multiple fingers and the first one is moving. + this._endDraw(event); + return; + } + this._currentParent.drawLayer.updateProperties( + this._currentDrawId, + DrawingEditor.#currentDraw.add(offsetX, offsetY) + ); + // We track the timestamp to know if the touchmove event is used to draw. + DrawingEditor.#currentMoveTimestamp = event.timeStamp; + stopEvent(event); + } + + static _cleanup(all) { + if (all) { + this._currentDrawId = -1; + this._currentParent = null; + DrawingEditor.#currentDraw = null; + DrawingEditor.#currentDrawingOptions = null; + DrawingEditor.#currentPointerType = null; + DrawingEditor.#currentMoveTimestamp = NaN; + } + + if (DrawingEditor.#currentDrawingAC) { + DrawingEditor.#currentDrawingAC.abort(); + DrawingEditor.#currentDrawingAC = null; + DrawingEditor.#currentPointerId = NaN; + DrawingEditor.#currentPointerIds = null; + } + } + + static _endDraw(event) { + const parent = this._currentParent; + if (!parent) { + return; + } + + parent.toggleDrawing(true); + this._cleanup(false); + + if (event?.target === parent.div) { + parent.drawLayer.updateProperties( + this._currentDrawId, + DrawingEditor.#currentDraw.end(event.offsetX, event.offsetY) + ); + } + if (this.supportMultipleDrawings) { + const draw = DrawingEditor.#currentDraw; + const drawId = this._currentDrawId; + const lastElement = draw.getLastElement(); + parent.addCommands({ + cmd: () => { + parent.drawLayer.updateProperties( + drawId, + draw.setLastElement(lastElement) + ); + }, + undo: () => { + parent.drawLayer.updateProperties(drawId, draw.removeLastElement()); + }, + mustExec: false, + type: AnnotationEditorParamsType.DRAW_STEP, + }); + + return; + } + + this.endDrawing(/* isAborted = */ false); + } + + static endDrawing(isAborted) { + const parent = this._currentParent; + if (!parent) { + return null; + } + parent.toggleDrawing(true); + parent.cleanUndoStack(AnnotationEditorParamsType.DRAW_STEP); + + if (!DrawingEditor.#currentDraw.isEmpty()) { + const { + pageDimensions: [pageWidth, pageHeight], + scale, + } = parent; + + const editor = parent.createAndAddNewEditor( + { offsetX: 0, offsetY: 0 }, + false, + { + drawId: this._currentDrawId, + drawOutlines: DrawingEditor.#currentDraw.getOutlines( + pageWidth * scale, + pageHeight * scale, + scale, + this._INNER_MARGIN + ), + drawingOptions: DrawingEditor.#currentDrawingOptions, + mustBeCommitted: !isAborted, + } + ); + this._cleanup(true); + return editor; + } + + parent.drawLayer.remove(this._currentDrawId); + this._cleanup(true); + return null; + } + + /** + * Create the drawing options. + * @param {Object} _data + */ + createDrawingOptions(_data) {} + + /** + * Deserialize the drawing outlines. + * @param {number} pageX - The x coordinate of the page. + * @param {number} pageY - The y coordinate of the page. + * @param {number} pageWidth - The width of the page. + * @param {number} pageHeight - The height of the page. + * @param {number} innerWidth - The inner width. + * @param {Object} data - The data to deserialize. + * @returns {Object} The deserialized outlines. + */ + static deserializeDraw( + _pageX, + _pageY, + _pageWidth, + _pageHeight, + _innerWidth, + _data + ) { + unreachable("Not implemented"); + } + + /** @inheritdoc */ + static async deserialize(data, parent, uiManager) { + const { + rawDims: { pageWidth, pageHeight, pageX, pageY }, + } = parent.viewport; + const drawOutlines = this.deserializeDraw( + pageX, + pageY, + pageWidth, + pageHeight, + this._INNER_MARGIN, + data + ); + const editor = await super.deserialize(data, parent, uiManager); + editor.createDrawingOptions(data); + editor.#createDrawOutlines({ drawOutlines }); + editor.#addToDrawLayer(); + editor.onScaleChanging(); + editor.rotate(); + + return editor; + } + + serializeDraw(isForCopying) { + const [pageX, pageY] = this.pageTranslation; + const [pageWidth, pageHeight] = this.pageDimensions; + return this.#drawOutlines.serialize( + [pageX, pageY, pageWidth, pageHeight], + isForCopying + ); + } + + /** @inheritdoc */ + renderAnnotationElement(annotation) { + annotation.updateEdited({ + rect: this.getRect(0, 0), + }); + + return null; + } + + static canCreateNewEmptyEditor() { + return false; + } +} + +export { DrawingEditor, DrawingOptions }; diff --git a/src/display/editor/drawers/contour.js b/src/display/editor/drawers/contour.js new file mode 100644 index 0000000000000..528e25d71500c --- /dev/null +++ b/src/display/editor/drawers/contour.js @@ -0,0 +1,28 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InkDrawOutline } from "./inkdraw.js"; + +class ContourDrawOutline extends InkDrawOutline { + toSVGPath() { + let path = super.toSVGPath(); + if (!path.endsWith("Z")) { + path += "Z"; + } + return path; + } +} + +export { ContourDrawOutline }; diff --git a/src/display/editor/outliner.js b/src/display/editor/drawers/freedraw.js similarity index 50% rename from src/display/editor/outliner.js rename to src/display/editor/drawers/freedraw.js index 8d52eeeac0ee5..30f73d90cd478 100644 --- a/src/display/editor/outliner.js +++ b/src/display/editor/drawers/freedraw.js @@ -1,4 +1,4 @@ -/* Copyright 2023 Mozilla Foundation +/* Copyright 2024 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,337 +13,10 @@ * limitations under the License. */ -import { Util } from "../../shared/util.js"; +import { Outline } from "./outline.js"; +import { Util } from "../../../shared/util.js"; -class Outliner { - #box; - - #verticalEdges = []; - - #intervals = []; - - /** - * Construct an outliner. - * @param {Array} boxes - An array of axis-aligned rectangles. - * @param {number} borderWidth - The width of the border of the boxes, it - * allows to make the boxes bigger (or smaller). - * @param {number} innerMargin - The margin between the boxes and the - * outlines. It's important to not have a null innerMargin when we want to - * draw the outline else the stroked outline could be clipped because of its - * width. - * @param {boolean} isLTR - true if we're in LTR mode. It's used to determine - * the last point of the boxes. - */ - constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { - let minX = Infinity; - let maxX = -Infinity; - let minY = Infinity; - let maxY = -Infinity; - - // We round the coordinates to slightly reduce the number of edges in the - // final outlines. - const NUMBER_OF_DIGITS = 4; - const EPSILON = 10 ** -NUMBER_OF_DIGITS; - - // The coordinates of the boxes are in the page coordinate system. - for (const { x, y, width, height } of boxes) { - const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; - const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; - const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; - const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; - const left = [x1, y1, y2, true]; - const right = [x2, y1, y2, false]; - this.#verticalEdges.push(left, right); - - minX = Math.min(minX, x1); - maxX = Math.max(maxX, x2); - minY = Math.min(minY, y1); - maxY = Math.max(maxY, y2); - } - - const bboxWidth = maxX - minX + 2 * innerMargin; - const bboxHeight = maxY - minY + 2 * innerMargin; - const shiftedMinX = minX - innerMargin; - const shiftedMinY = minY - innerMargin; - const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); - const lastPoint = [lastEdge[0], lastEdge[2]]; - - // Convert the coordinates of the edges into box coordinates. - for (const edge of this.#verticalEdges) { - const [x, y1, y2] = edge; - edge[0] = (x - shiftedMinX) / bboxWidth; - edge[1] = (y1 - shiftedMinY) / bboxHeight; - edge[2] = (y2 - shiftedMinY) / bboxHeight; - } - - this.#box = { - x: shiftedMinX, - y: shiftedMinY, - width: bboxWidth, - height: bboxHeight, - lastPoint, - }; - } - - getOutlines() { - // We begin to sort lexicographically the vertical edges by their abscissa, - // and then by their ordinate. - this.#verticalEdges.sort( - (a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2] - ); - - // We're now using a sweep line algorithm to find the outlines. - // We start with the leftmost vertical edge, and we're going to iterate - // over all the vertical edges from left to right. - // Each time we encounter a left edge, we're going to insert the interval - // [y1, y2] in the set of intervals. - // This set of intervals is used to break the vertical edges into chunks: - // we only take the part of the vertical edge that isn't in the union of - // the intervals. - const outlineVerticalEdges = []; - for (const edge of this.#verticalEdges) { - if (edge[3]) { - // Left edge. - outlineVerticalEdges.push(...this.#breakEdge(edge)); - this.#insert(edge); - } else { - // Right edge. - this.#remove(edge); - outlineVerticalEdges.push(...this.#breakEdge(edge)); - } - } - return this.#getOutlines(outlineVerticalEdges); - } - - #getOutlines(outlineVerticalEdges) { - const edges = []; - const allEdges = new Set(); - - for (const edge of outlineVerticalEdges) { - const [x, y1, y2] = edge; - edges.push([x, y1, edge], [x, y2, edge]); - } - - // We sort lexicographically the vertices of each edge by their ordinate and - // by their abscissa. - // Every pair (v_2i, v_{2i + 1}) of vertices defines a horizontal edge. - // So for every vertical edge, we're going to add the two vertical edges - // which are connected to it through a horizontal edge. - edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); - for (let i = 0, ii = edges.length; i < ii; i += 2) { - const edge1 = edges[i][2]; - const edge2 = edges[i + 1][2]; - edge1.push(edge2); - edge2.push(edge1); - allEdges.add(edge1); - allEdges.add(edge2); - } - const outlines = []; - let outline; - - while (allEdges.size > 0) { - const edge = allEdges.values().next().value; - let [x, y1, y2, edge1, edge2] = edge; - allEdges.delete(edge); - let lastPointX = x; - let lastPointY = y1; - - outline = [x, y2]; - outlines.push(outline); - - while (true) { - let e; - if (allEdges.has(edge1)) { - e = edge1; - } else if (allEdges.has(edge2)) { - e = edge2; - } else { - break; - } - - allEdges.delete(e); - [x, y1, y2, edge1, edge2] = e; - - if (lastPointX !== x) { - outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); - lastPointX = x; - } - lastPointY = lastPointY === y1 ? y2 : y1; - } - outline.push(lastPointX, lastPointY); - } - return new HighlightOutline(outlines, this.#box); - } - - #binarySearch(y) { - const array = this.#intervals; - let start = 0; - let end = array.length - 1; - - while (start <= end) { - const middle = (start + end) >> 1; - const y1 = array[middle][0]; - if (y1 === y) { - return middle; - } - if (y1 < y) { - start = middle + 1; - } else { - end = middle - 1; - } - } - return end + 1; - } - - #insert([, y1, y2]) { - const index = this.#binarySearch(y1); - this.#intervals.splice(index, 0, [y1, y2]); - } - - #remove([, y1, y2]) { - const index = this.#binarySearch(y1); - for (let i = index; i < this.#intervals.length; i++) { - const [start, end] = this.#intervals[i]; - if (start !== y1) { - break; - } - if (start === y1 && end === y2) { - this.#intervals.splice(i, 1); - return; - } - } - for (let i = index - 1; i >= 0; i--) { - const [start, end] = this.#intervals[i]; - if (start !== y1) { - break; - } - if (start === y1 && end === y2) { - this.#intervals.splice(i, 1); - return; - } - } - } - - #breakEdge(edge) { - const [x, y1, y2] = edge; - const results = [[x, y1, y2]]; - const index = this.#binarySearch(y2); - for (let i = 0; i < index; i++) { - const [start, end] = this.#intervals[i]; - for (let j = 0, jj = results.length; j < jj; j++) { - const [, y3, y4] = results[j]; - if (end <= y3 || y4 <= start) { - // There is no intersection between the interval and the edge, hence - // we keep it as is. - continue; - } - if (y3 >= start) { - if (y4 > end) { - results[j][1] = end; - } else { - if (jj === 1) { - return []; - } - // The edge is included in the interval, hence we remove it. - results.splice(j, 1); - j--; - jj--; - } - continue; - } - results[j][2] = start; - if (y4 > end) { - results.push([x, end, y4]); - } - } - } - return results; - } -} - -class Outline { - /** - * @returns {string} The SVG path of the outline. - */ - toSVGPath() { - throw new Error("Abstract method `toSVGPath` must be implemented."); - } - - /** - * @type {Object|null} The bounding box of the outline. - */ - get box() { - throw new Error("Abstract getter `box` must be implemented."); - } - - serialize(_bbox, _rotation) { - throw new Error("Abstract method `serialize` must be implemented."); - } - - get free() { - return this instanceof FreeHighlightOutline; - } -} - -class HighlightOutline extends Outline { - #box; - - #outlines; - - constructor(outlines, box) { - super(); - this.#outlines = outlines; - this.#box = box; - } - - toSVGPath() { - const buffer = []; - for (const polygon of this.#outlines) { - let [prevX, prevY] = polygon; - buffer.push(`M${prevX} ${prevY}`); - for (let i = 2; i < polygon.length; i += 2) { - const x = polygon[i]; - const y = polygon[i + 1]; - if (x === prevX) { - buffer.push(`V${y}`); - prevY = y; - } else if (y === prevY) { - buffer.push(`H${x}`); - prevX = x; - } - } - buffer.push("Z"); - } - return buffer.join(" "); - } - - /** - * Serialize the outlines into the PDF page coordinate system. - * @param {Array} _bbox - the bounding box of the annotation. - * @param {number} _rotation - the rotation of the annotation. - * @returns {Array>} - */ - serialize([blX, blY, trX, trY], _rotation) { - const outlines = []; - const width = trX - blX; - const height = trY - blY; - for (const outline of this.#outlines) { - const points = new Array(outline.length); - for (let i = 0; i < outline.length; i += 2) { - points[i] = blX + outline[i] * width; - points[i + 1] = trY - outline[i + 1] * height; - } - outlines.push(points); - } - return outlines; - } - - get box() { - return this.#box; - } -} - -class FreeOutliner { +class FreeDrawOutliner { #box; #bottom = []; @@ -361,7 +34,7 @@ class FreeOutliner { // We track the last 3 points in order to be able to: // - compute the normal of the line, // - compute the control points of the quadratic Bézier curve. - #last = new Float64Array(18); + #last = new Float32Array(18); #lastX; @@ -381,7 +54,7 @@ class FreeOutliner { static #MIN_DIFF = 2; - static #MIN = FreeOutliner.#MIN_DIST + FreeOutliner.#MIN_DIFF; + static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF; constructor({ x, y }, box, scaleFactor, thickness, isLTR, innerMargin = 0) { this.#box = box; @@ -389,16 +62,12 @@ class FreeOutliner { this.#isLTR = isLTR; this.#last.set([NaN, NaN, NaN, NaN, x, y], 6); this.#innerMargin = innerMargin; - this.#min_dist = FreeOutliner.#MIN_DIST * scaleFactor; - this.#min = FreeOutliner.#MIN * scaleFactor; + this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor; + this.#min = FreeDrawOutliner.#MIN * scaleFactor; this.#scaleFactor = scaleFactor; this.#points.push(x, y); } - get free() { - return true; - } - isEmpty() { // When we add a second point then this.#last.slice(6) will be something // like [NaN, NaN, firstX, firstY, secondX, secondY,...] so having a NaN @@ -544,21 +213,10 @@ class FreeOutliner { } const top = this.#top; const bottom = this.#bottom; - const lastTop = this.#last.subarray(4, 6); - const lastBottom = this.#last.subarray(16, 18); - const [x, y, width, height] = this.#box; - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = - this.#getLastCoords(); if (isNaN(this.#last[6]) && !this.isEmpty()) { // We've only two points. - return `M${(this.#last[2] - x) / width} ${ - (this.#last[3] - y) / height - } L${(this.#last[4] - x) / width} ${(this.#last[5] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${ - (this.#last[16] - x) / width - } ${(this.#last[17] - y) / height} L${(this.#last[14] - x) / width} ${ - (this.#last[15] - y) / height - } Z`; + return this.#toSVGPathTwoPoints(); } const buffer = []; @@ -575,11 +233,8 @@ class FreeOutliner { } } - buffer.push( - `L${(lastTop[0] - x) / width} ${(lastTop[1] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${ - (lastBottom[0] - x) / width - } ${(lastBottom[1] - y) / height}` - ); + this.#toSVGPathEnd(buffer); + for (let i = bottom.length - 6; i >= 6; i -= 6) { if (isNaN(bottom[i])) { buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`); @@ -591,84 +246,76 @@ class FreeOutliner { ); } } - buffer.push(`L${bottom[4]} ${bottom[5]} Z`); + + this.#toSVGPathStart(buffer); return buffer.join(" "); } + #toSVGPathTwoPoints() { + const [x, y, width, height] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = + this.#getLastCoords(); + + return `M${(this.#last[2] - x) / width} ${ + (this.#last[3] - y) / height + } L${(this.#last[4] - x) / width} ${(this.#last[5] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${ + (this.#last[16] - x) / width + } ${(this.#last[17] - y) / height} L${(this.#last[14] - x) / width} ${ + (this.#last[15] - y) / height + } Z`; + } + + #toSVGPathStart(buffer) { + const bottom = this.#bottom; + buffer.push(`L${bottom[4]} ${bottom[5]} Z`); + } + + #toSVGPathEnd(buffer) { + const [x, y, width, height] = this.#box; + const lastTop = this.#last.subarray(4, 6); + const lastBottom = this.#last.subarray(16, 18); + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = + this.#getLastCoords(); + + buffer.push( + `L${(lastTop[0] - x) / width} ${(lastTop[1] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${ + (lastBottom[0] - x) / width + } ${(lastBottom[1] - y) / height}` + ); + } + + newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { + return new FreeDrawOutline( + outline, + points, + box, + scaleFactor, + innerMargin, + isLTR + ); + } + getOutlines() { const top = this.#top; const bottom = this.#bottom; const last = this.#last; - const lastTop = last.subarray(4, 6); - const lastBottom = last.subarray(16, 18); const [layerX, layerY, layerWidth, layerHeight] = this.#box; - const points = new Float64Array((this.#points?.length ?? 0) + 2); + const points = new Float32Array((this.#points?.length ?? 0) + 2); for (let i = 0, ii = points.length - 2; i < ii; i += 2) { points[i] = (this.#points[i] - layerX) / layerWidth; points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight; } points[points.length - 2] = (this.#lastX - layerX) / layerWidth; points[points.length - 1] = (this.#lastY - layerY) / layerHeight; - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = - this.#getLastCoords(); if (isNaN(last[6]) && !this.isEmpty()) { // We've only two points. - const outline = new Float64Array(36); - outline.set( - [ - NaN, - NaN, - NaN, - NaN, - (last[2] - layerX) / layerWidth, - (last[3] - layerY) / layerHeight, - NaN, - NaN, - NaN, - NaN, - (last[4] - layerX) / layerWidth, - (last[5] - layerY) / layerHeight, - NaN, - NaN, - NaN, - NaN, - lastTopX, - lastTopY, - NaN, - NaN, - NaN, - NaN, - lastBottomX, - lastBottomY, - NaN, - NaN, - NaN, - NaN, - (last[16] - layerX) / layerWidth, - (last[17] - layerY) / layerHeight, - NaN, - NaN, - NaN, - NaN, - (last[14] - layerX) / layerWidth, - (last[15] - layerY) / layerHeight, - ], - 0 - ); - return new FreeHighlightOutline( - outline, - points, - this.#box, - this.#scaleFactor, - this.#innerMargin, - this.#isLTR - ); + return this.#getOutlineTwoPoints(points); } - const outline = new Float64Array( + const outline = new Float32Array( this.#top.length + 24 + this.#bottom.length ); let N = top.length; @@ -681,14 +328,53 @@ class FreeOutliner { outline[i + 1] = top[i + 1]; } + N = this.#getOutlineEnd(outline, N); + + for (let i = bottom.length - 6; i >= 6; i -= 6) { + for (let j = 0; j < 6; j += 2) { + if (isNaN(bottom[i + j])) { + outline[N] = outline[N + 1] = NaN; + N += 2; + continue; + } + outline[N] = bottom[i + j]; + outline[N + 1] = bottom[i + j + 1]; + N += 2; + } + } + + this.#getOutlineStart(outline, N); + + return this.newFreeDrawOutline( + outline, + points, + this.#box, + this.#scaleFactor, + this.#innerMargin, + this.#isLTR + ); + } + + #getOutlineTwoPoints(points) { + const last = this.#last; + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = + this.#getLastCoords(); + const outline = new Float32Array(36); outline.set( [ NaN, NaN, NaN, NaN, - (lastTop[0] - layerX) / layerWidth, - (lastTop[1] - layerY) / layerHeight, + (last[2] - layerX) / layerWidth, + (last[3] - layerY) / layerHeight, + NaN, + NaN, + NaN, + NaN, + (last[4] - layerX) / layerWidth, + (last[5] - layerY) / layerHeight, NaN, NaN, NaN, @@ -705,27 +391,18 @@ class FreeOutliner { NaN, NaN, NaN, - (lastBottom[0] - layerX) / layerWidth, - (lastBottom[1] - layerY) / layerHeight, + (last[16] - layerX) / layerWidth, + (last[17] - layerY) / layerHeight, + NaN, + NaN, + NaN, + NaN, + (last[14] - layerX) / layerWidth, + (last[15] - layerY) / layerHeight, ], - N + 0 ); - N += 24; - - for (let i = bottom.length - 6; i >= 6; i -= 6) { - for (let j = 0; j < 6; j += 2) { - if (isNaN(bottom[i + j])) { - outline[N] = outline[N + 1] = NaN; - N += 2; - continue; - } - outline[N] = bottom[i + j]; - outline[N + 1] = bottom[i + j + 1]; - N += 2; - } - } - outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], N); - return new FreeHighlightOutline( + return this.newFreeDrawOutline( outline, points, this.#box, @@ -734,12 +411,56 @@ class FreeOutliner { this.#isLTR ); } + + #getOutlineStart(outline, pos) { + const bottom = this.#bottom; + outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos); + return (pos += 6); + } + + #getOutlineEnd(outline, pos) { + const lastTop = this.#last.subarray(4, 6); + const lastBottom = this.#last.subarray(16, 18); + const [layerX, layerY, layerWidth, layerHeight] = this.#box; + const [lastTopX, lastTopY, lastBottomX, lastBottomY] = + this.#getLastCoords(); + outline.set( + [ + NaN, + NaN, + NaN, + NaN, + (lastTop[0] - layerX) / layerWidth, + (lastTop[1] - layerY) / layerHeight, + NaN, + NaN, + NaN, + NaN, + lastTopX, + lastTopY, + NaN, + NaN, + NaN, + NaN, + lastBottomX, + lastBottomY, + NaN, + NaN, + NaN, + NaN, + (lastBottom[0] - layerX) / layerWidth, + (lastBottom[1] - layerY) / layerHeight, + ], + pos + ); + return (pos += 24); + } } -class FreeHighlightOutline extends Outline { +class FreeDrawOutline extends Outline { #box; - #bbox = null; + #bbox = new Float32Array(4); #innerMargin; @@ -759,9 +480,10 @@ class FreeHighlightOutline extends Outline { this.#scaleFactor = scaleFactor; this.#innerMargin = innerMargin; this.#isLTR = isLTR; + this.lastPoint = [NaN, NaN]; this.#computeMinMax(isLTR); - const { x, y, width, height } = this.#bbox; + const [x, y, width, height] = this.#bbox; for (let i = 0, ii = outline.length; i < ii; i += 2) { outline[i] = (outline[i] - x) / width; outline[i + 1] = (outline[i + 1] - y) / height; @@ -796,49 +518,43 @@ class FreeHighlightOutline extends Outline { let points; switch (rotation) { case 0: - outline = this.#rescale(this.#outline, blX, trY, width, -height); - points = this.#rescale(this.#points, blX, trY, width, -height); + outline = Outline._rescale(this.#outline, blX, trY, width, -height); + points = Outline._rescale(this.#points, blX, trY, width, -height); break; case 90: - outline = this.#rescaleAndSwap(this.#outline, blX, blY, width, height); - points = this.#rescaleAndSwap(this.#points, blX, blY, width, height); + outline = Outline._rescaleAndSwap( + this.#outline, + blX, + blY, + width, + height + ); + points = Outline._rescaleAndSwap(this.#points, blX, blY, width, height); break; case 180: - outline = this.#rescale(this.#outline, trX, blY, -width, height); - points = this.#rescale(this.#points, trX, blY, -width, height); + outline = Outline._rescale(this.#outline, trX, blY, -width, height); + points = Outline._rescale(this.#points, trX, blY, -width, height); break; case 270: - outline = this.#rescaleAndSwap( + outline = Outline._rescaleAndSwap( this.#outline, trX, trY, -width, -height ); - points = this.#rescaleAndSwap(this.#points, trX, trY, -width, -height); + points = Outline._rescaleAndSwap( + this.#points, + trX, + trY, + -width, + -height + ); break; } return { outline: Array.from(outline), points: [Array.from(points)] }; } - #rescale(src, tx, ty, sx, sy) { - const dest = new Float64Array(src.length); - for (let i = 0, ii = src.length; i < ii; i += 2) { - dest[i] = tx + src[i] * sx; - dest[i + 1] = ty + src[i + 1] * sy; - } - return dest; - } - - #rescaleAndSwap(src, tx, ty, sx, sy) { - const dest = new Float64Array(src.length); - for (let i = 0, ii = src.length; i < ii; i += 2) { - dest[i] = tx + src[i + 1] * sx; - dest[i + 1] = ty + src[i] * sy; - } - return dest; - } - #computeMinMax(isLTR) { const outline = this.#outline; let lastX = outline[4]; @@ -884,26 +600,38 @@ class FreeHighlightOutline extends Outline { lastY = outline[i + 5]; } - const x = minX - this.#innerMargin, - y = minY - this.#innerMargin, - width = maxX - minX + 2 * this.#innerMargin, - height = maxY - minY + 2 * this.#innerMargin; - this.#bbox = { x, y, width, height, lastPoint: [lastPointX, lastPointY] }; + const bbox = this.#bbox; + bbox[0] = minX - this.#innerMargin; + bbox[1] = minY - this.#innerMargin; + bbox[2] = maxX - minX + 2 * this.#innerMargin; + bbox[3] = maxY - minY + 2 * this.#innerMargin; + this.lastPoint = [lastPointX, lastPointY]; } get box() { return this.#bbox; } + newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { + return new FreeDrawOutliner( + point, + box, + scaleFactor, + thickness, + isLTR, + innerMargin + ); + } + getNewOutline(thickness, innerMargin) { // Build the outline of the highlight to use as the focus outline. - const { x, y, width, height } = this.#bbox; + const [x, y, width, height] = this.#bbox; const [layerX, layerY, layerWidth, layerHeight] = this.#box; const sx = width * layerWidth; const sy = height * layerHeight; const tx = x * layerWidth + layerX; const ty = y * layerHeight + layerY; - const outliner = new FreeOutliner( + const outliner = this.newOutliner( { x: this.#points[0] * sx + tx, y: this.#points[1] * sy + ty, @@ -924,4 +652,4 @@ class FreeHighlightOutline extends Outline { } } -export { FreeOutliner, Outliner }; +export { FreeDrawOutline, FreeDrawOutliner }; diff --git a/src/display/editor/drawers/highlight.js b/src/display/editor/drawers/highlight.js new file mode 100644 index 0000000000000..a6389284a2766 --- /dev/null +++ b/src/display/editor/drawers/highlight.js @@ -0,0 +1,356 @@ +/* Copyright 2023 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FreeDrawOutline, FreeDrawOutliner } from "./freedraw.js"; +import { Outline } from "./outline.js"; + +class HighlightOutliner { + #box; + + #lastPoint; + + #verticalEdges = []; + + #intervals = []; + + /** + * Construct an outliner. + * @param {Array} boxes - An array of axis-aligned rectangles. + * @param {number} borderWidth - The width of the border of the boxes, it + * allows to make the boxes bigger (or smaller). + * @param {number} innerMargin - The margin between the boxes and the + * outlines. It's important to not have a null innerMargin when we want to + * draw the outline else the stroked outline could be clipped because of its + * width. + * @param {boolean} isLTR - true if we're in LTR mode. It's used to determine + * the last point of the boxes. + */ + constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + + // We round the coordinates to slightly reduce the number of edges in the + // final outlines. + const NUMBER_OF_DIGITS = 4; + const EPSILON = 10 ** -NUMBER_OF_DIGITS; + + // The coordinates of the boxes are in the page coordinate system. + for (const { x, y, width, height } of boxes) { + const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; + const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; + const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; + const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; + const left = [x1, y1, y2, true]; + const right = [x2, y1, y2, false]; + this.#verticalEdges.push(left, right); + + minX = Math.min(minX, x1); + maxX = Math.max(maxX, x2); + minY = Math.min(minY, y1); + maxY = Math.max(maxY, y2); + } + + const bboxWidth = maxX - minX + 2 * innerMargin; + const bboxHeight = maxY - minY + 2 * innerMargin; + const shiftedMinX = minX - innerMargin; + const shiftedMinY = minY - innerMargin; + const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); + const lastPoint = [lastEdge[0], lastEdge[2]]; + + // Convert the coordinates of the edges into box coordinates. + for (const edge of this.#verticalEdges) { + const [x, y1, y2] = edge; + edge[0] = (x - shiftedMinX) / bboxWidth; + edge[1] = (y1 - shiftedMinY) / bboxHeight; + edge[2] = (y2 - shiftedMinY) / bboxHeight; + } + + this.#box = new Float32Array([ + shiftedMinX, + shiftedMinY, + bboxWidth, + bboxHeight, + ]); + this.#lastPoint = lastPoint; + } + + getOutlines() { + // We begin to sort lexicographically the vertical edges by their abscissa, + // and then by their ordinate. + this.#verticalEdges.sort( + (a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2] + ); + + // We're now using a sweep line algorithm to find the outlines. + // We start with the leftmost vertical edge, and we're going to iterate + // over all the vertical edges from left to right. + // Each time we encounter a left edge, we're going to insert the interval + // [y1, y2] in the set of intervals. + // This set of intervals is used to break the vertical edges into chunks: + // we only take the part of the vertical edge that isn't in the union of + // the intervals. + const outlineVerticalEdges = []; + for (const edge of this.#verticalEdges) { + if (edge[3]) { + // Left edge. + outlineVerticalEdges.push(...this.#breakEdge(edge)); + this.#insert(edge); + } else { + // Right edge. + this.#remove(edge); + outlineVerticalEdges.push(...this.#breakEdge(edge)); + } + } + return this.#getOutlines(outlineVerticalEdges); + } + + #getOutlines(outlineVerticalEdges) { + const edges = []; + const allEdges = new Set(); + + for (const edge of outlineVerticalEdges) { + const [x, y1, y2] = edge; + edges.push([x, y1, edge], [x, y2, edge]); + } + + // We sort lexicographically the vertices of each edge by their ordinate and + // by their abscissa. + // Every pair (v_2i, v_{2i + 1}) of vertices defines a horizontal edge. + // So for every vertical edge, we're going to add the two vertical edges + // which are connected to it through a horizontal edge. + edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); + for (let i = 0, ii = edges.length; i < ii; i += 2) { + const edge1 = edges[i][2]; + const edge2 = edges[i + 1][2]; + edge1.push(edge2); + edge2.push(edge1); + allEdges.add(edge1); + allEdges.add(edge2); + } + const outlines = []; + let outline; + + while (allEdges.size > 0) { + const edge = allEdges.values().next().value; + let [x, y1, y2, edge1, edge2] = edge; + allEdges.delete(edge); + let lastPointX = x; + let lastPointY = y1; + + outline = [x, y2]; + outlines.push(outline); + + while (true) { + let e; + if (allEdges.has(edge1)) { + e = edge1; + } else if (allEdges.has(edge2)) { + e = edge2; + } else { + break; + } + + allEdges.delete(e); + [x, y1, y2, edge1, edge2] = e; + + if (lastPointX !== x) { + outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); + lastPointX = x; + } + lastPointY = lastPointY === y1 ? y2 : y1; + } + outline.push(lastPointX, lastPointY); + } + return new HighlightOutline(outlines, this.#box, this.#lastPoint); + } + + #binarySearch(y) { + const array = this.#intervals; + let start = 0; + let end = array.length - 1; + + while (start <= end) { + const middle = (start + end) >> 1; + const y1 = array[middle][0]; + if (y1 === y) { + return middle; + } + if (y1 < y) { + start = middle + 1; + } else { + end = middle - 1; + } + } + return end + 1; + } + + #insert([, y1, y2]) { + const index = this.#binarySearch(y1); + this.#intervals.splice(index, 0, [y1, y2]); + } + + #remove([, y1, y2]) { + const index = this.#binarySearch(y1); + for (let i = index; i < this.#intervals.length; i++) { + const [start, end] = this.#intervals[i]; + if (start !== y1) { + break; + } + if (start === y1 && end === y2) { + this.#intervals.splice(i, 1); + return; + } + } + for (let i = index - 1; i >= 0; i--) { + const [start, end] = this.#intervals[i]; + if (start !== y1) { + break; + } + if (start === y1 && end === y2) { + this.#intervals.splice(i, 1); + return; + } + } + } + + #breakEdge(edge) { + const [x, y1, y2] = edge; + const results = [[x, y1, y2]]; + const index = this.#binarySearch(y2); + for (let i = 0; i < index; i++) { + const [start, end] = this.#intervals[i]; + for (let j = 0, jj = results.length; j < jj; j++) { + const [, y3, y4] = results[j]; + if (end <= y3 || y4 <= start) { + // There is no intersection between the interval and the edge, hence + // we keep it as is. + continue; + } + if (y3 >= start) { + if (y4 > end) { + results[j][1] = end; + } else { + if (jj === 1) { + return []; + } + // The edge is included in the interval, hence we remove it. + results.splice(j, 1); + j--; + jj--; + } + continue; + } + results[j][2] = start; + if (y4 > end) { + results.push([x, end, y4]); + } + } + } + return results; + } +} + +class HighlightOutline extends Outline { + #box; + + #outlines; + + constructor(outlines, box, lastPoint) { + super(); + this.#outlines = outlines; + this.#box = box; + this.lastPoint = lastPoint; + } + + toSVGPath() { + const buffer = []; + for (const polygon of this.#outlines) { + let [prevX, prevY] = polygon; + buffer.push(`M${prevX} ${prevY}`); + for (let i = 2; i < polygon.length; i += 2) { + const x = polygon[i]; + const y = polygon[i + 1]; + if (x === prevX) { + buffer.push(`V${y}`); + prevY = y; + } else if (y === prevY) { + buffer.push(`H${x}`); + prevX = x; + } + } + buffer.push("Z"); + } + return buffer.join(" "); + } + + /** + * Serialize the outlines into the PDF page coordinate system. + * @param {Array} _bbox - the bounding box of the annotation. + * @param {number} _rotation - the rotation of the annotation. + * @returns {Array>} + */ + serialize([blX, blY, trX, trY], _rotation) { + const outlines = []; + const width = trX - blX; + const height = trY - blY; + for (const outline of this.#outlines) { + const points = new Array(outline.length); + for (let i = 0; i < outline.length; i += 2) { + points[i] = blX + outline[i] * width; + points[i + 1] = trY - outline[i + 1] * height; + } + outlines.push(points); + } + return outlines; + } + + get box() { + return this.#box; + } + + get classNamesForOutlining() { + return ["highlightOutline"]; + } +} + +class FreeHighlightOutliner extends FreeDrawOutliner { + newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { + return new FreeHighlightOutline( + outline, + points, + box, + scaleFactor, + innerMargin, + isLTR + ); + } +} + +class FreeHighlightOutline extends FreeDrawOutline { + newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { + return new FreeHighlightOutliner( + point, + box, + scaleFactor, + thickness, + isLTR, + innerMargin + ); + } +} + +export { FreeHighlightOutliner, HighlightOutliner }; diff --git a/src/display/editor/drawers/inkdraw.js b/src/display/editor/drawers/inkdraw.js new file mode 100644 index 0000000000000..2af5a48f17c1b --- /dev/null +++ b/src/display/editor/drawers/inkdraw.js @@ -0,0 +1,901 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Outline } from "./outline.js"; +import { Util } from "../../../shared/util.js"; + +class InkDrawOutliner { + // The last 3 points of the line. + #last = new Float64Array(6); + + #line; + + #lines; + + #rotation; + + #thickness; + + #points; + + #lastSVGPath = ""; + + #lastIndex = 0; + + #outlines = new InkDrawOutline(); + + #parentWidth; + + #parentHeight; + + constructor(x, y, parentWidth, parentHeight, rotation, thickness) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#rotation = rotation; + this.#thickness = thickness; + + [x, y] = this.#normalizePoint(x, y); + + const line = (this.#line = [NaN, NaN, NaN, NaN, x, y]); + this.#points = [x, y]; + this.#lines = [{ line, points: this.#points }]; + this.#last.set(line, 0); + } + + updateProperty(name, value) { + if (name === "stroke-width") { + this.#thickness = value; + } + } + + #normalizePoint(x, y) { + return Outline._normalizePoint( + x, + y, + this.#parentWidth, + this.#parentHeight, + this.#rotation + ); + } + + isEmpty() { + return !this.#lines || this.#lines.length === 0; + } + + isCancellable() { + // The user a second finger after drawing 5 points: it's small enough + // to not be a real drawing. + return this.#points.length <= 10; + } + + add(x, y) { + // The point is in canvas coordinates which means that there is no rotation. + // It's the same as parent coordinates. + [x, y] = this.#normalizePoint(x, y); + const [x1, y1, x2, y2] = this.#last.subarray(2, 6); + const diffX = x - x2; + const diffY = y - y2; + const d = Math.hypot(this.#parentWidth * diffX, this.#parentHeight * diffY); + if (d <= 2) { + // The idea is to avoid garbage points around the last point. + // When the points are too close, it just leads to bad normal vectors and + // control points. + return null; + } + + this.#points.push(x, y); + + if (isNaN(x1)) { + // We've only one point. + this.#last.set([x2, y2, x, y], 2); + this.#line.push(NaN, NaN, NaN, NaN, x, y); + return { + path: { + d: this.toSVGPath(), + }, + }; + } + + if (isNaN(this.#last[0])) { + // We've only two points. + this.#line.splice(6, 6); + } + + this.#last.set([x1, y1, x2, y2, x, y], 0); + this.#line.push(...Outline.createBezierPoints(x1, y1, x2, y2, x, y)); + + return { + path: { + d: this.toSVGPath(), + }, + }; + } + + end(x, y) { + const change = this.add(x, y); + if (change) { + return change; + } + if (this.#points.length === 2) { + // We've only one point. + return { + path: { + d: this.toSVGPath(), + }, + }; + } + return null; + } + + startNew(x, y, parentWidth, parentHeight, rotation) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#rotation = rotation; + + [x, y] = this.#normalizePoint(x, y); + + const line = (this.#line = [NaN, NaN, NaN, NaN, x, y]); + this.#points = [x, y]; + const last = this.#lines.at(-1); + if (last) { + last.line = new Float32Array(last.line); + last.points = new Float32Array(last.points); + } + this.#lines.push({ line, points: this.#points }); + this.#last.set(line, 0); + this.#lastIndex = 0; + this.toSVGPath(); + + return null; + } + + getLastElement() { + return this.#lines.at(-1); + } + + setLastElement(element) { + if (!this.#lines) { + return this.#outlines.setLastElement(element); + } + this.#lines.push(element); + this.#line = element.line; + this.#points = element.points; + this.#lastIndex = 0; + return { + path: { + d: this.toSVGPath(), + }, + }; + } + + removeLastElement() { + if (!this.#lines) { + return this.#outlines.removeLastElement(); + } + this.#lines.pop(); + this.#lastSVGPath = ""; + for (let i = 0, ii = this.#lines.length; i < ii; i++) { + const { line, points } = this.#lines[i]; + this.#line = line; + this.#points = points; + this.#lastIndex = 0; + this.toSVGPath(); + } + + return { + path: { + d: this.#lastSVGPath, + }, + }; + } + + toSVGPath() { + const firstX = Outline.svgRound(this.#line[4]); + const firstY = Outline.svgRound(this.#line[5]); + if (this.#points.length === 2) { + this.#lastSVGPath = `${this.#lastSVGPath} M ${firstX} ${firstY} Z`; + return this.#lastSVGPath; + } + + if (this.#points.length <= 6) { + // We've 2 or 3 points. + const i = this.#lastSVGPath.lastIndexOf("M"); + this.#lastSVGPath = `${this.#lastSVGPath.slice(0, i)} M ${firstX} ${firstY}`; + this.#lastIndex = 6; + } + + if (this.#points.length === 4) { + const secondX = Outline.svgRound(this.#line[10]); + const secondY = Outline.svgRound(this.#line[11]); + this.#lastSVGPath = `${this.#lastSVGPath} L ${secondX} ${secondY}`; + this.#lastIndex = 12; + return this.#lastSVGPath; + } + + const buffer = []; + if (this.#lastIndex === 0) { + buffer.push(`M ${firstX} ${firstY}`); + this.#lastIndex = 6; + } + + for (let i = this.#lastIndex, ii = this.#line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = this.#line + .slice(i, i + 6) + .map(Outline.svgRound); + buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); + } + this.#lastSVGPath += buffer.join(" "); + this.#lastIndex = this.#line.length; + + return this.#lastSVGPath; + } + + getOutlines(parentWidth, parentHeight, scale, innerMargin) { + const last = this.#lines.at(-1); + last.line = new Float32Array(last.line); + last.points = new Float32Array(last.points); + + this.#outlines.build( + this.#lines, + parentWidth, + parentHeight, + scale, + this.#rotation, + this.#thickness, + innerMargin + ); + + // We reset everything: the drawing is done. + this.#last = null; + this.#line = null; + this.#lines = null; + this.#lastSVGPath = null; + + return this.#outlines; + } + + get defaultSVGProperties() { + return { + root: { + viewBox: "0 0 10000 10000", + }, + rootClass: { + draw: true, + }, + bbox: [0, 0, 1, 1], + }; + } +} + +class InkDrawOutline extends Outline { + #bbox; + + #currentRotation = 0; + + #innerMargin; + + #lines; + + #parentWidth; + + #parentHeight; + + #parentScale; + + #rotation; + + #thickness; + + build( + lines, + parentWidth, + parentHeight, + parentScale, + rotation, + thickness, + innerMargin + ) { + this.#parentWidth = parentWidth; + this.#parentHeight = parentHeight; + this.#parentScale = parentScale; + this.#rotation = rotation; + this.#thickness = thickness; + this.#innerMargin = innerMargin ?? 0; + this.#lines = lines; + + this.#computeBbox(); + } + + get thickness() { + return this.#thickness; + } + + setLastElement(element) { + this.#lines.push(element); + return { + path: { + d: this.toSVGPath(), + }, + }; + } + + removeLastElement() { + this.#lines.pop(); + return { + path: { + d: this.toSVGPath(), + }, + }; + } + + toSVGPath() { + const buffer = []; + for (const { line } of this.#lines) { + buffer.push(`M${Outline.svgRound(line[4])} ${Outline.svgRound(line[5])}`); + if (line.length === 6) { + buffer.push("Z"); + continue; + } + if (line.length === 12 && isNaN(line[6])) { + buffer.push( + `L${Outline.svgRound(line[10])} ${Outline.svgRound(line[11])}` + ); + continue; + } + for (let i = 6, ii = line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = line + .subarray(i, i + 6) + .map(Outline.svgRound); + buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); + } + } + return buffer.join(""); + } + + serialize([pageX, pageY, pageWidth, pageHeight], isForCopying) { + const serializedLines = []; + const serializedPoints = []; + const [x, y, width, height] = this.#getBBoxWithNoMargin(); + let tx, ty, sx, sy, x1, y1, x2, y2, rescaleFn; + + switch (this.#rotation) { + case 0: + rescaleFn = Outline._rescale; + tx = pageX; + ty = pageY + pageHeight; + sx = pageWidth; + sy = -pageHeight; + x1 = pageX + x * pageWidth; + y1 = pageY + (1 - y - height) * pageHeight; + x2 = pageX + (x + width) * pageWidth; + y2 = pageY + (1 - y) * pageHeight; + break; + case 90: + rescaleFn = Outline._rescaleAndSwap; + tx = pageX; + ty = pageY; + sx = pageWidth; + sy = pageHeight; + x1 = pageX + y * pageWidth; + y1 = pageY + x * pageHeight; + x2 = pageX + (y + height) * pageWidth; + y2 = pageY + (x + width) * pageHeight; + break; + case 180: + rescaleFn = Outline._rescale; + tx = pageX + pageWidth; + ty = pageY; + sx = -pageWidth; + sy = pageHeight; + x1 = pageX + (1 - x - width) * pageWidth; + y1 = pageY + y * pageHeight; + x2 = pageX + (1 - x) * pageWidth; + y2 = pageY + (y + height) * pageHeight; + break; + case 270: + rescaleFn = Outline._rescaleAndSwap; + tx = pageX + pageWidth; + ty = pageY + pageHeight; + sx = -pageWidth; + sy = -pageHeight; + x1 = pageX + (1 - y - height) * pageWidth; + y1 = pageY + (1 - x - width) * pageHeight; + x2 = pageX + (1 - y) * pageWidth; + y2 = pageY + (1 - x) * pageHeight; + break; + } + + for (const { line, points } of this.#lines) { + serializedLines.push( + rescaleFn( + line, + tx, + ty, + sx, + sy, + isForCopying ? new Array(line.length) : null + ) + ); + serializedPoints.push( + rescaleFn( + points, + tx, + ty, + sx, + sy, + isForCopying ? new Array(points.length) : null + ) + ); + } + + return { + lines: serializedLines, + points: serializedPoints, + rect: [x1, y1, x2, y2], + }; + } + + static deserialize( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + { paths: { lines, points }, rotation, thickness } + ) { + const newLines = []; + let tx, ty, sx, sy, rescaleFn; + switch (rotation) { + case 0: + rescaleFn = Outline._rescale; + tx = -pageX / pageWidth; + ty = pageY / pageHeight + 1; + sx = 1 / pageWidth; + sy = -1 / pageHeight; + break; + case 90: + rescaleFn = Outline._rescaleAndSwap; + tx = -pageY / pageHeight; + ty = -pageX / pageWidth; + sx = 1 / pageHeight; + sy = 1 / pageWidth; + break; + case 180: + rescaleFn = Outline._rescale; + tx = pageX / pageWidth + 1; + ty = -pageY / pageHeight; + sx = -1 / pageWidth; + sy = 1 / pageHeight; + break; + case 270: + rescaleFn = Outline._rescaleAndSwap; + tx = pageY / pageHeight + 1; + ty = pageX / pageWidth + 1; + sx = -1 / pageHeight; + sy = -1 / pageWidth; + break; + } + + if (!lines) { + lines = []; + for (const point of points) { + const len = point.length; + if (len === 2) { + lines.push( + new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1]]) + ); + continue; + } + if (len === 4) { + lines.push( + new Float32Array([ + NaN, + NaN, + NaN, + NaN, + point[0], + point[1], + NaN, + NaN, + NaN, + NaN, + point[2], + point[3], + ]) + ); + continue; + } + const line = new Float32Array(3 * (len - 2)); + lines.push(line); + let [x1, y1, x2, y2] = point.subarray(0, 4); + line.set([NaN, NaN, NaN, NaN, x1, y1], 0); + for (let i = 4; i < len; i += 2) { + const x = point[i]; + const y = point[i + 1]; + line.set( + Outline.createBezierPoints(x1, y1, x2, y2, x, y), + (i - 2) * 3 + ); + [x1, y1, x2, y2] = [x2, y2, x, y]; + } + } + } + + for (let i = 0, ii = lines.length; i < ii; i++) { + newLines.push({ + line: rescaleFn( + lines[i].map(x => x ?? NaN), + tx, + ty, + sx, + sy + ), + points: rescaleFn( + points[i].map(x => x ?? NaN), + tx, + ty, + sx, + sy + ), + }); + } + + const outlines = new this.prototype.constructor(); + outlines.build( + newLines, + pageWidth, + pageHeight, + 1, + rotation, + thickness, + innerMargin + ); + + return outlines; + } + + #getMarginComponents(thickness = this.#thickness) { + const margin = this.#innerMargin + (thickness / 2) * this.#parentScale; + return this.#rotation % 180 === 0 + ? [margin / this.#parentWidth, margin / this.#parentHeight] + : [margin / this.#parentHeight, margin / this.#parentWidth]; + } + + #getBBoxWithNoMargin() { + const [x, y, width, height] = this.#bbox; + const [marginX, marginY] = this.#getMarginComponents(0); + + return [ + x + marginX, + y + marginY, + width - 2 * marginX, + height - 2 * marginY, + ]; + } + + #computeBbox() { + const bbox = (this.#bbox = new Float32Array([ + Infinity, + Infinity, + -Infinity, + -Infinity, + ])); + + for (const { line } of this.#lines) { + if (line.length <= 12) { + // We've only one or two points => no bezier curve. + for (let i = 4, ii = line.length; i < ii; i += 6) { + const [x, y] = line.subarray(i, i + 2); + bbox[0] = Math.min(bbox[0], x); + bbox[1] = Math.min(bbox[1], y); + bbox[2] = Math.max(bbox[2], x); + bbox[3] = Math.max(bbox[3], y); + } + continue; + } + let lastX = line[4], + lastY = line[5]; + for (let i = 6, ii = line.length; i < ii; i += 6) { + const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6); + Util.bezierBoundingBox(lastX, lastY, c1x, c1y, c2x, c2y, x, y, bbox); + lastX = x; + lastY = y; + } + } + + const [marginX, marginY] = this.#getMarginComponents(); + bbox[0] = Math.min(1, Math.max(0, bbox[0] - marginX)); + bbox[1] = Math.min(1, Math.max(0, bbox[1] - marginY)); + bbox[2] = Math.min(1, Math.max(0, bbox[2] + marginX)); + bbox[3] = Math.min(1, Math.max(0, bbox[3] + marginY)); + + bbox[2] -= bbox[0]; + bbox[3] -= bbox[1]; + } + + get box() { + return this.#bbox; + } + + updateProperty(name, value) { + if (name === "stroke-width") { + return this.#updateThickness(value); + } + return null; + } + + #updateThickness(thickness) { + const [oldMarginX, oldMarginY] = this.#getMarginComponents(); + this.#thickness = thickness; + const [newMarginX, newMarginY] = this.#getMarginComponents(); + const [diffMarginX, diffMarginY] = [ + newMarginX - oldMarginX, + newMarginY - oldMarginY, + ]; + const bbox = this.#bbox; + bbox[0] -= diffMarginX; + bbox[1] -= diffMarginY; + bbox[2] += 2 * diffMarginX; + bbox[3] += 2 * diffMarginY; + + return bbox; + } + + updateParentDimensions([width, height], scale) { + const [oldMarginX, oldMarginY] = this.#getMarginComponents(); + this.#parentWidth = width; + this.#parentHeight = height; + this.#parentScale = scale; + const [newMarginX, newMarginY] = this.#getMarginComponents(); + const diffMarginX = newMarginX - oldMarginX; + const diffMarginY = newMarginY - oldMarginY; + + const bbox = this.#bbox; + bbox[0] -= diffMarginX; + bbox[1] -= diffMarginY; + bbox[2] += 2 * diffMarginX; + bbox[3] += 2 * diffMarginY; + + return bbox; + } + + updateRotation(rotation) { + this.#currentRotation = rotation; + return { + path: { + transform: this.rotationTransform, + }, + }; + } + + get viewBox() { + return this.#bbox.map(Outline.svgRound).join(" "); + } + + get defaultProperties() { + const [x, y] = this.#bbox; + return { + root: { + viewBox: this.viewBox, + }, + path: { + "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`, + }, + }; + } + + get rotationTransform() { + const [, , width, height] = this.#bbox; + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0; + switch (this.#currentRotation) { + case 90: + b = height / width; + c = -width / height; + e = width; + break; + case 180: + a = -1; + d = -1; + e = width; + f = height; + break; + case 270: + b = -height / width; + c = width / height; + f = height; + break; + default: + return ""; + } + return `matrix(${a} ${b} ${c} ${d} ${Outline.svgRound(e)} ${Outline.svgRound(f)})`; + } + + getPathResizingSVGProperties([newX, newY, newWidth, newHeight]) { + const [marginX, marginY] = this.#getMarginComponents(); + const [x, y, width, height] = this.#bbox; + + if ( + Math.abs(width - marginX) <= Outline.PRECISION || + Math.abs(height - marginY) <= Outline.PRECISION + ) { + // Center the path in the new bounding box. + const tx = newX + newWidth / 2 - (x + width / 2); + const ty = newY + newHeight / 2 - (y + height / 2); + return { + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: `${this.rotationTransform} translate(${tx} ${ty})`, + }, + }; + } + + // We compute the following transform: + // 1. Translate the path to the origin (-marginX, -marginY). + // 2. Scale the path to the new size: + // ((newWidth - 2*marginX) / (bbox.width - 2*marginX), + // (newHeight - 2*marginY) / (bbox.height - 2*marginY)). + // 3. Translate the path back to its original position + // (marginX, marginY). + // 4. Scale the inverse of bbox scaling: + // (bbox.width / newWidth, bbox.height / newHeight). + + const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); + const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); + const s2x = width / newWidth; + const s2y = height / newHeight; + + return { + path: { + "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`, + transform: + `${this.rotationTransform} scale(${s2x} ${s2y}) ` + + `translate(${Outline.svgRound(marginX)} ${Outline.svgRound(marginY)}) scale(${s1x} ${s1y}) ` + + `translate(${Outline.svgRound(-marginX)} ${Outline.svgRound(-marginY)})`, + }, + }; + } + + getPathResizedSVGProperties([newX, newY, newWidth, newHeight]) { + const [marginX, marginY] = this.#getMarginComponents(); + const bbox = this.#bbox; + const [x, y, width, height] = bbox; + + bbox[0] = newX; + bbox[1] = newY; + bbox[2] = newWidth; + bbox[3] = newHeight; + + if ( + Math.abs(width - marginX) <= Outline.PRECISION || + Math.abs(height - marginY) <= Outline.PRECISION + ) { + // Center the path in the new bounding box. + const tx = newX + newWidth / 2 - (x + width / 2); + const ty = newY + newHeight / 2 - (y + height / 2); + for (const { line, points } of this.#lines) { + Outline._translate(line, tx, ty, line); + Outline._translate(points, tx, ty, points); + } + return { + root: { + viewBox: this.viewBox, + }, + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: this.rotationTransform || null, + d: this.toSVGPath(), + }, + }; + } + + // We compute the following transform: + // 1. Translate the path to the origin (-(x + marginX), -(y + marginY)). + // 2. Scale the path to the new size: + // ((newWidth - 2*marginX) / (bbox.width - 2*marginX), + // (newHeight - 2*marginY) / (bbox.height - 2*marginY)). + // 3. Translate the path back to its new position + // (newX + marginX,y newY + marginY). + + const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); + const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); + const tx = -s1x * (x + marginX) + newX + marginX; + const ty = -s1y * (y + marginY) + newY + marginY; + + if (s1x !== 1 || s1y !== 1 || tx !== 0 || ty !== 0) { + for (const { line, points } of this.#lines) { + Outline._rescale(line, tx, ty, s1x, s1y, line); + Outline._rescale(points, tx, ty, s1x, s1y, points); + } + } + + return { + root: { + viewBox: this.viewBox, + }, + path: { + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + transform: this.rotationTransform || null, + d: this.toSVGPath(), + }, + }; + } + + getPathTranslatedSVGProperties([newX, newY], parentDimensions) { + const [newParentWidth, newParentHeight] = parentDimensions; + const bbox = this.#bbox; + const tx = newX - bbox[0]; + const ty = newY - bbox[1]; + + if ( + this.#parentWidth === newParentWidth && + this.#parentHeight === newParentHeight + ) { + // We don't change the parent dimensions so it's a simple translation. + for (const { line, points } of this.#lines) { + Outline._translate(line, tx, ty, line); + Outline._translate(points, tx, ty, points); + } + } else { + const sx = this.#parentWidth / newParentWidth; + const sy = this.#parentHeight / newParentHeight; + this.#parentWidth = newParentWidth; + this.#parentHeight = newParentHeight; + + for (const { line, points } of this.#lines) { + Outline._rescale(line, tx, ty, sx, sy, line); + Outline._rescale(points, tx, ty, sx, sy, points); + } + bbox[2] *= sx; + bbox[3] *= sy; + } + bbox[0] = newX; + bbox[1] = newY; + + return { + root: { + viewBox: this.viewBox, + }, + path: { + d: this.toSVGPath(), + "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, + }, + }; + } + + get defaultSVGProperties() { + const bbox = this.#bbox; + return { + root: { + viewBox: this.viewBox, + }, + rootClass: { + draw: true, + }, + path: { + d: this.toSVGPath(), + "transform-origin": `${Outline.svgRound(bbox[0])} ${Outline.svgRound(bbox[1])}`, + transform: this.rotationTransform || null, + }, + bbox, + }; + } +} + +export { InkDrawOutline, InkDrawOutliner }; diff --git a/src/display/editor/drawers/outline.js b/src/display/editor/drawers/outline.js new file mode 100644 index 0000000000000..e06aafcb33ac9 --- /dev/null +++ b/src/display/editor/drawers/outline.js @@ -0,0 +1,113 @@ +/* Copyright 2023 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { unreachable } from "../../../shared/util.js"; + +class Outline { + static PRECISION = 1e-4; + + /** + * @returns {string} The SVG path of the outline. + */ + toSVGPath() { + unreachable("Abstract method `toSVGPath` must be implemented."); + } + + /** + * @type {Object|null} The bounding box of the outline. + */ + // eslint-disable-next-line getter-return + get box() { + unreachable("Abstract getter `box` must be implemented."); + } + + serialize(_bbox, _rotation) { + unreachable("Abstract method `serialize` must be implemented."); + } + + static _rescale(src, tx, ty, sx, sy, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i] * sx; + dest[i + 1] = ty + src[i + 1] * sy; + } + return dest; + } + + static _rescaleAndSwap(src, tx, ty, sx, sy, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i + 1] * sx; + dest[i + 1] = ty + src[i] * sy; + } + return dest; + } + + static _translate(src, tx, ty, dest) { + dest ||= new Float32Array(src.length); + for (let i = 0, ii = src.length; i < ii; i += 2) { + dest[i] = tx + src[i]; + dest[i + 1] = ty + src[i + 1]; + } + return dest; + } + + static svgRound(x) { + // 0.1234 will be 1234 and this way we economize 2 bytes per number. + // Of course, it makes sense only when the viewBox is [0 0 10000 10000]. + // And it helps to avoid bugs like: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1929340 + return Math.round(x * 10000); + } + + static _normalizePoint(x, y, parentWidth, parentHeight, rotation) { + switch (rotation) { + case 90: + return [1 - y / parentWidth, x / parentHeight]; + case 180: + return [1 - x / parentWidth, 1 - y / parentHeight]; + case 270: + return [y / parentWidth, 1 - x / parentHeight]; + default: + return [x / parentWidth, y / parentHeight]; + } + } + + static _normalizePagePoint(x, y, rotation) { + switch (rotation) { + case 90: + return [1 - y, x]; + case 180: + return [1 - x, 1 - y]; + case 270: + return [y, 1 - x]; + default: + return [x, y]; + } + } + + static createBezierPoints(x1, y1, x2, y2, x3, y3) { + return [ + (x1 + 5 * x2) / 6, + (y1 + 5 * y2) / 6, + (5 * x2 + x3) / 6, + (5 * y2 + y3) / 6, + (x2 + x3) / 2, + (y2 + y3) / 2, + ]; + } +} + +export { Outline }; diff --git a/src/display/editor/drawers/signaturedraw.js b/src/display/editor/drawers/signaturedraw.js new file mode 100644 index 0000000000000..1118421daee88 --- /dev/null +++ b/src/display/editor/drawers/signaturedraw.js @@ -0,0 +1,852 @@ +/* Copyright 2022 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { fromBase64Util, toBase64Util, warn } from "../../../shared/util.js"; +import { ContourDrawOutline } from "./contour.js"; +import { InkDrawOutline } from "./inkdraw.js"; +import { Outline } from "./outline.js"; + +const BASE_HEADER_LENGTH = 8; +const POINTS_PROPERTIES_NUMBER = 3; + +/** + * Basic text editor in order to create a Signature annotation. + */ +class SignatureExtractor { + static #PARAMETERS = { + maxDim: 512, + sigmaSFactor: 0.02, + sigmaR: 25, + kernelSize: 16, + }; + + static #neighborIndexToId(i0, j0, i, j) { + /* + The idea is to map the neighbors of a pixel into a unique id. + 3 2 1 + 4 X 0 + 5 6 7 + */ + + i -= i0; + j -= j0; + + if (i === 0) { + return j > 0 ? 0 : 4; + } + + if (i === 1) { + return j + 6; + } + + return 2 - j; + } + + static #neighborIdToIndex = new Int32Array([ + 0, 1, -1, 1, -1, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1, + ]); + + static #clockwiseNonZero(buf, width, i0, j0, i, j, offset) { + const id = this.#neighborIndexToId(i0, j0, i, j); + for (let k = 0; k < 8; k++) { + const kk = (-k + id - offset + 16) % 8; + const shiftI = this.#neighborIdToIndex[2 * kk]; + const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; + if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { + return kk; + } + } + return -1; + } + + static #counterClockwiseNonZero(buf, width, i0, j0, i, j, offset) { + const id = this.#neighborIndexToId(i0, j0, i, j); + for (let k = 0; k < 8; k++) { + const kk = (k + id + offset + 16) % 8; + const shiftI = this.#neighborIdToIndex[2 * kk]; + const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; + if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { + return kk; + } + } + return -1; + } + + static #findContours(buf, width, height, threshold) { + // Based on the Suzuki's algorithm: + // https://web.archive.org/web/20231213161741/https://www.nevis.columbia.edu/~vgenty/public/suzuki_et_al.pdf + + const N = buf.length; + const types = new Int32Array(N); + for (let i = 0; i < N; i++) { + types[i] = buf[i] <= threshold ? 1 : 0; + } + + for (let i = 1; i < height - 1; i++) { + types[i * width] = types[i * width + width - 1] = 0; + } + for (let i = 0; i < width; i++) { + types[i] = types[width * height - 1 - i] = 0; + } + + let nbd = 1; + let lnbd; + const contours = []; + + for (let i = 1; i < height - 1; i++) { + lnbd = 1; + for (let j = 1; j < width - 1; j++) { + const ij = i * width + j; + const pix = types[ij]; + if (pix === 0) { + continue; + } + + let i2 = i; + let j2 = j; + + if (pix === 1 && types[ij - 1] === 0) { + // Outer border. + nbd += 1; + j2 -= 1; + } else if (pix >= 1 && types[ij + 1] === 0) { + // Hole border. + nbd += 1; + j2 += 1; + if (pix > 1) { + lnbd = pix; + } + } else { + if (pix !== 1) { + lnbd = Math.abs(pix); + } + continue; + } + + const points = [j, i]; + const isHole = j2 === j + 1; + const contour = { + isHole, + points, + id: nbd, + parent: 0, + }; + contours.push(contour); + + let contour0; + for (const c of contours) { + if (c.id === lnbd) { + contour0 = c; + break; + } + } + + if (!contour0) { + contour.parent = isHole ? lnbd : 0; + } else if (contour0.isHole) { + contour.parent = isHole ? contour0.parent : lnbd; + } else { + contour.parent = isHole ? lnbd : contour0.parent; + } + + const k = this.#clockwiseNonZero(types, width, i, j, i2, j2, 0); + if (k === -1) { + types[ij] = -nbd; + if (types[ij] !== 1) { + lnbd = Math.abs(types[ij]); + } + continue; + } + + let shiftI = this.#neighborIdToIndex[2 * k]; + let shiftJ = this.#neighborIdToIndex[2 * k + 1]; + const i1 = i + shiftI; + const j1 = j + shiftJ; + i2 = i1; + j2 = j1; + let i3 = i; + let j3 = j; + + while (true) { + const kk = this.#counterClockwiseNonZero( + types, + width, + i3, + j3, + i2, + j2, + 1 + ); + shiftI = this.#neighborIdToIndex[2 * kk]; + shiftJ = this.#neighborIdToIndex[2 * kk + 1]; + const i4 = i3 + shiftI; + const j4 = j3 + shiftJ; + points.push(j4, i4); + const ij3 = i3 * width + j3; + if (types[ij3 + 1] === 0) { + types[ij3] = -nbd; + } else if (types[ij3] === 1) { + types[ij3] = nbd; + } + + if (i4 === i && j4 === j && i3 === i1 && j3 === j1) { + if (types[ij] !== 1) { + lnbd = Math.abs(types[ij]); + } + break; + } else { + i2 = i3; + j2 = j3; + i3 = i4; + j3 = j4; + } + } + } + } + return contours; + } + + static #douglasPeuckerHelper(points, start, end, output) { + // Based on the Douglas-Peucker algorithm: + // https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm + if (end - start <= 4) { + for (let i = start; i < end - 2; i += 2) { + output.push(points[i], points[i + 1]); + } + return; + } + + const ax = points[start]; + const ay = points[start + 1]; + const abx = points[end - 4] - ax; + const aby = points[end - 3] - ay; + const dist = Math.hypot(abx, aby); + const nabx = abx / dist; + const naby = aby / dist; + const aa = nabx * ay - naby * ax; + + // Guessing the epsilon value. + // See "A novel framework for making dominant point detection methods + // non-parametric". + const m = aby / abx; + const invS = 1 / dist; + const phi = Math.atan(m); + const cosPhi = Math.cos(phi); + const sinPhi = Math.sin(phi); + const tmax = invS * (Math.abs(cosPhi) + Math.abs(sinPhi)); + const poly = invS * (1 - tmax + tmax ** 2); + const partialPhi = Math.max( + Math.atan(Math.abs(sinPhi + cosPhi) * poly), + Math.atan(Math.abs(sinPhi - cosPhi) * poly) + ); + + let dmax = 0; + let index = start; + for (let i = start + 2; i < end - 2; i += 2) { + const d = Math.abs(aa - nabx * points[i + 1] + naby * points[i]); + if (d > dmax) { + index = i; + dmax = d; + } + } + + if (dmax > (dist * partialPhi) ** 2) { + this.#douglasPeuckerHelper(points, start, index + 2, output); + this.#douglasPeuckerHelper(points, index, end, output); + } else { + output.push(ax, ay); + } + } + + static #douglasPeucker(points) { + const output = []; + const len = points.length; + this.#douglasPeuckerHelper(points, 0, len, output); + output.push(points[len - 2], points[len - 1]); + return output.length <= 4 ? null : output; + } + + static #bilateralFilter(buf, width, height, sigmaS, sigmaR, kernelSize) { + // The bilateral filter is a nonlinear filter that does spatial averaging. + // Its main interest is to preserve edges while removing noise. + // See https://en.wikipedia.org/wiki/Bilateral_filter for more details. + // sigmaS is the standard deviation of the spatial gaussian. + // sigmaR is the standard deviation of the range (in term of pixel + // intensity) gaussian. + + // Create a gaussian kernel + const kernel = new Float32Array(kernelSize ** 2); + const sigmaS2 = -2 * sigmaS ** 2; + const halfSize = kernelSize >> 1; + + for (let i = 0; i < kernelSize; i++) { + const x = (i - halfSize) ** 2; + for (let j = 0; j < kernelSize; j++) { + kernel[i * kernelSize + j] = Math.exp( + (x + (j - halfSize) ** 2) / sigmaS2 + ); + } + } + + // Create the range values to be used with the distance between pixels. + // It's a way faster with a lookup table than computing the exponential. + const rangeValues = new Float32Array(256); + const sigmaR2 = -2 * sigmaR ** 2; + for (let i = 0; i < 256; i++) { + rangeValues[i] = Math.exp(i ** 2 / sigmaR2); + } + + const N = buf.length; + const out = new Uint8Array(N); + + // We compute the histogram here instead of doing it later: it's slightly + // faster. + const histogram = new Uint32Array(256); + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + const ij = i * width + j; + const center = buf[ij]; + let sum = 0; + let norm = 0; + + for (let k = 0; k < kernelSize; k++) { + const y = i + k - halfSize; + if (y < 0 || y >= height) { + continue; + } + for (let l = 0; l < kernelSize; l++) { + const x = j + l - halfSize; + if (x < 0 || x >= width) { + continue; + } + const neighbour = buf[y * width + x]; + const w = + kernel[k * kernelSize + l] * + rangeValues[Math.abs(neighbour - center)]; + sum += neighbour * w; + norm += w; + } + } + + const pix = (out[ij] = Math.round(sum / norm)); + histogram[pix]++; + } + } + + return [out, histogram]; + } + + static #getHistogram(buf) { + const histogram = new Uint32Array(256); + for (const g of buf) { + histogram[g]++; + } + return histogram; + } + + static #toUint8(buf) { + // We have a RGBA buffer, containing a grayscale image. + // We want to convert it into a basic G buffer. + // Also, we want to normalize the values between 0 and 255 in order to + // increase the contrast. + const N = buf.length; + const out = new Uint8ClampedArray(N >> 2); + let max = -Infinity; + let min = Infinity; + for (let i = 0, ii = out.length; i < ii; i++) { + const A = buf[(i << 2) + 3]; + if (A === 0) { + max = out[i] = 0xff; + continue; + } + const pix = (out[i] = buf[i << 2]); + if (pix > max) { + max = pix; + } + if (pix < min) { + min = pix; + } + } + const ratio = 255 / (max - min); + for (let i = 0; i < N; i++) { + out[i] = (out[i] - min) * ratio; + } + + return out; + } + + static #guessThreshold(histogram) { + // We want to find the threshold that will separate the background from the + // foreground. + // We could have used Otsu's method, but unfortunately it doesn't work well + // when the background has too much shade of greys. + // So the idea is to find a maximum in the black part of the histogram and + // figure out the value which will be the first one of the white part. + + let i; + let M = -Infinity; + let L = -Infinity; + const min = histogram.findIndex(v => v !== 0); + let pos = min; + let spos = min; + for (i = min; i < 256; i++) { + const v = histogram[i]; + if (v > M) { + if (i - pos > L) { + L = i - pos; + spos = i - 1; + } + M = v; + pos = i; + } + } + for (i = spos - 1; i >= 0; i--) { + if (histogram[i] > histogram[i + 1]) { + break; + } + } + + return i; + } + + static #getGrayPixels(bitmap) { + const originalBitmap = bitmap; + const { width, height } = bitmap; + const { maxDim } = this.#PARAMETERS; + let newWidth = width; + let newHeight = height; + + if (width > maxDim || height > maxDim) { + let prevWidth = width; + let prevHeight = height; + + let steps = Math.log2(Math.max(width, height) / maxDim); + const isteps = Math.floor(steps); + steps = steps === isteps ? isteps - 1 : isteps; + for (let i = 0; i < steps; i++) { + newWidth = prevWidth; + newHeight = prevHeight; + if (newWidth > maxDim) { + newWidth = Math.ceil(newWidth / 2); + } + if (newHeight > maxDim) { + newHeight = Math.ceil(newHeight / 2); + } + + const offscreen = new OffscreenCanvas(newWidth, newHeight); + const ctx = offscreen.getContext("2d"); + ctx.drawImage( + bitmap, + 0, + 0, + prevWidth, + prevHeight, + 0, + 0, + newWidth, + newHeight + ); + prevWidth = newWidth; + prevHeight = newHeight; + + // Release the resources associated with the bitmap. + if (bitmap !== originalBitmap) { + bitmap.close(); + } + bitmap = offscreen.transferToImageBitmap(); + } + + const ratio = Math.min(maxDim / newWidth, maxDim / newHeight); + newWidth = Math.round(newWidth * ratio); + newHeight = Math.round(newHeight * ratio); + } + const offscreen = new OffscreenCanvas(newWidth, newHeight); + const ctx = offscreen.getContext("2d", { willReadFrequently: true }); + ctx.filter = "grayscale(1)"; + ctx.drawImage( + bitmap, + 0, + 0, + bitmap.width, + bitmap.height, + 0, + 0, + newWidth, + newHeight + ); + const grayImage = ctx.getImageData(0, 0, newWidth, newHeight).data; + const uint8Buf = this.#toUint8(grayImage); + + return [uint8Buf, newWidth, newHeight]; + } + + static extractContoursFromText( + text, + { fontFamily, fontStyle, fontWeight }, + pageWidth, + pageHeight, + rotation, + innerMargin + ) { + let canvas = new OffscreenCanvas(1, 1); + let ctx = canvas.getContext("2d", { alpha: false }); + const fontSize = 200; + const font = + (ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`); + const { + actualBoundingBoxLeft, + actualBoundingBoxRight, + actualBoundingBoxAscent, + actualBoundingBoxDescent, + fontBoundingBoxAscent, + fontBoundingBoxDescent, + width, + } = ctx.measureText(text); + + // We rescale the canvas to make "sure" the text fits. + const SCALE = 1.5; + const canvasWidth = Math.ceil( + Math.max( + Math.abs(actualBoundingBoxLeft) + Math.abs(actualBoundingBoxRight) || 0, + width + ) * SCALE + ); + const canvasHeight = Math.ceil( + Math.max( + Math.abs(actualBoundingBoxAscent) + + Math.abs(actualBoundingBoxDescent) || fontSize, + Math.abs(fontBoundingBoxAscent) + Math.abs(fontBoundingBoxDescent) || + fontSize + ) * SCALE + ); + canvas = new OffscreenCanvas(canvasWidth, canvasHeight); + ctx = canvas.getContext("2d", { alpha: true, willReadFrequently: true }); + ctx.font = font; + ctx.filter = "grayscale(1)"; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + ctx.fillStyle = "black"; + ctx.fillText( + text, + (canvasWidth * (SCALE - 1)) / 2, + (canvasHeight * (3 - SCALE)) / 2 + ); + + const uint8Buf = this.#toUint8( + ctx.getImageData(0, 0, canvasWidth, canvasHeight).data + ); + const histogram = this.#getHistogram(uint8Buf); + const threshold = this.#guessThreshold(histogram); + + const contourList = this.#findContours( + uint8Buf, + canvasWidth, + canvasHeight, + threshold + ); + + return this.processDrawnLines({ + lines: { curves: contourList, width: canvasWidth, height: canvasHeight }, + pageWidth, + pageHeight, + rotation, + innerMargin, + mustSmooth: true, + areContours: true, + }); + } + + static process(bitmap, pageWidth, pageHeight, rotation, innerMargin) { + const [uint8Buf, width, height] = this.#getGrayPixels(bitmap); + const [buffer, histogram] = this.#bilateralFilter( + uint8Buf, + width, + height, + Math.hypot(width, height) * this.#PARAMETERS.sigmaSFactor, + this.#PARAMETERS.sigmaR, + this.#PARAMETERS.kernelSize + ); + + const threshold = this.#guessThreshold(histogram); + const contourList = this.#findContours(buffer, width, height, threshold); + + return this.processDrawnLines({ + lines: { curves: contourList, width, height }, + pageWidth, + pageHeight, + rotation, + innerMargin, + mustSmooth: true, + areContours: true, + }); + } + + static processDrawnLines({ + lines, + pageWidth, + pageHeight, + rotation, + innerMargin, + mustSmooth, + areContours, + }) { + if (rotation % 180 !== 0) { + [pageWidth, pageHeight] = [pageHeight, pageWidth]; + } + + const { curves, width, height } = lines; + const thickness = lines.thickness ?? 0; + const linesAndPoints = []; + const ratio = Math.min(pageWidth / width, pageHeight / height); + const xScale = ratio / pageWidth; + const yScale = ratio / pageHeight; + const newCurves = []; + + for (const { points } of curves) { + const reducedPoints = mustSmooth ? this.#douglasPeucker(points) : points; + if (!reducedPoints) { + continue; + } + newCurves.push(reducedPoints); + + const len = reducedPoints.length; + const newPoints = new Float32Array(len); + const line = new Float32Array(3 * (len === 2 ? 2 : len - 2)); + linesAndPoints.push({ line, points: newPoints }); + + if (len === 2) { + newPoints[0] = reducedPoints[0] * xScale; + newPoints[1] = reducedPoints[1] * yScale; + line.set([NaN, NaN, NaN, NaN, newPoints[0], newPoints[1]], 0); + continue; + } + + let [x1, y1, x2, y2] = reducedPoints; + x1 *= xScale; + y1 *= yScale; + x2 *= xScale; + y2 *= yScale; + newPoints.set([x1, y1, x2, y2], 0); + + line.set([NaN, NaN, NaN, NaN, x1, y1], 0); + for (let i = 4; i < len; i += 2) { + const x = (newPoints[i] = reducedPoints[i] * xScale); + const y = (newPoints[i + 1] = reducedPoints[i + 1] * yScale); + line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); + [x1, y1, x2, y2] = [x2, y2, x, y]; + } + } + + if (linesAndPoints.length === 0) { + return null; + } + + const outline = areContours + ? new ContourDrawOutline() + : new InkDrawOutline(); + + outline.build( + linesAndPoints, + pageWidth, + pageHeight, + 1, + rotation, + areContours ? 0 : thickness, + innerMargin + ); + + return { outline, newCurves, areContours, thickness, width, height }; + } + + static async compressSignature({ + outlines, + areContours, + thickness, + width, + height, + }) { + // We create a single array containing all the outlines. + // The format is the following: + // - 4 bytes: data length. + // - 4 bytes: version. + // - 4 bytes: width. + // - 4 bytes: height. + // - 4 bytes: 0 if it's a contour, 1 if it's an ink. + // - 4 bytes: thickness. + // - 4 bytes: number of drawings. + // - 4 bytes: size of the buffer containing the diff of the coordinates. + // - 4 bytes: number of points in the first drawing. + // - 4 bytes: x coordinate of the first point. + // - 4 bytes: y coordinate of the first point. + // - 4 bytes: number of points in the second drawing. + // - 4 bytes: x coordinate of the first point. + // - 4 bytes: y coordinate of the first point. + // - ... + // - The buffer containing the diff of the coordinates. + + // The coordinates are supposed to be positive integers. + + // We also compute the min and max difference between two points. + // This will help us to determine the type of the buffer (Int8, Int16 or + // Int32) in order to minimize the amount of data we have. + let minDiff = Infinity; + let maxDiff = -Infinity; + let outlinesLength = 0; + for (const points of outlines) { + outlinesLength += points.length; + for (let i = 2, ii = points.length; i < ii; i++) { + const dx = points[i] - points[i - 2]; + minDiff = Math.min(minDiff, dx); + maxDiff = Math.max(maxDiff, dx); + } + } + + let bufferType; + if (minDiff >= -128 && maxDiff <= 127) { + bufferType = Int8Array; + } else if (minDiff >= -32768 && maxDiff <= 32767) { + bufferType = Int16Array; + } else { + bufferType = Int32Array; + } + + const len = outlines.length; + const headerLength = BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * len; + const header = new Uint32Array(headerLength); + + let offset = 0; + header[offset++] = + headerLength * Uint32Array.BYTES_PER_ELEMENT + + (outlinesLength - 2 * len) * bufferType.BYTES_PER_ELEMENT; + header[offset++] = 0; // Version. + header[offset++] = width; + header[offset++] = height; + header[offset++] = areContours ? 0 : 1; + header[offset++] = Math.max(0, Math.floor(thickness ?? 0)); + header[offset++] = len; + header[offset++] = bufferType.BYTES_PER_ELEMENT; + for (const points of outlines) { + header[offset++] = points.length - 2; + header[offset++] = points[0]; + header[offset++] = points[1]; + } + + const cs = new CompressionStream("deflate-raw"); + const writer = cs.writable.getWriter(); + await writer.ready; + + writer.write(header); + const BufferCtor = bufferType.prototype.constructor; + for (const points of outlines) { + const diffs = new BufferCtor(points.length - 2); + for (let i = 2, ii = points.length; i < ii; i++) { + diffs[i - 2] = points[i] - points[i - 2]; + } + writer.write(diffs); + } + + writer.close(); + + const buf = await new Response(cs.readable).arrayBuffer(); + const bytes = new Uint8Array(buf); + + return toBase64Util(bytes); + } + + static async decompressSignature(signatureData) { + try { + const bytes = fromBase64Util(signatureData); + const { readable, writable } = new DecompressionStream("deflate-raw"); + const writer = writable.getWriter(); + await writer.ready; + + // We can't await writer.write() because it'll block until the reader + // starts which happens few lines below. + writer + .write(bytes) + .then(async () => { + await writer.ready; + await writer.close(); + }) + .catch(() => {}); + + let data = null; + let offset = 0; + for await (const chunk of readable) { + data ||= new Uint8Array(new Uint32Array(chunk.buffer, 0, 4)[0]); + data.set(chunk, offset); + offset += chunk.length; + } + + // We take a bit too much data for the header but it's fine. + const header = new Uint32Array(data.buffer, 0, data.length >> 2); + const version = header[1]; + if (version !== 0) { + throw new Error(`Invalid version: ${version}`); + } + const width = header[2]; + const height = header[3]; + const areContours = header[4] === 0; + const thickness = header[5]; + const numberOfDrawings = header[6]; + const bufferType = header[7]; + const outlines = []; + const diffsOffset = + (BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * numberOfDrawings) * + Uint32Array.BYTES_PER_ELEMENT; + let diffs; + + switch (bufferType) { + case Int8Array.BYTES_PER_ELEMENT: + diffs = new Int8Array(data.buffer, diffsOffset); + break; + case Int16Array.BYTES_PER_ELEMENT: + diffs = new Int16Array(data.buffer, diffsOffset); + break; + case Int32Array.BYTES_PER_ELEMENT: + diffs = new Int32Array(data.buffer, diffsOffset); + break; + } + + offset = 0; + for (let i = 0; i < numberOfDrawings; i++) { + const len = header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH]; + const points = new Float32Array(len + 2); + outlines.push(points); + + for (let j = 0; j < POINTS_PROPERTIES_NUMBER - 1; j++) { + points[j] = + header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH + j + 1]; + } + for (let j = 0; j < len; j++) { + points[j + 2] = points[j] + diffs[offset++]; + } + } + + return { + areContours, + thickness, + outlines, + width, + height, + }; + } catch (e) { + warn(`decompressSignature: ${e}`); + return null; + } + } +} + +export { SignatureExtractor }; diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index d345c21a9befb..5ffa85d5a8417 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -23,9 +23,10 @@ import { KeyboardManager, } from "./tools.js"; import { FeatureTest, shadow, unreachable } from "../../shared/util.js"; +import { noContextMenu, stopEvent } from "../display_utils.js"; import { AltText } from "./alt_text.js"; import { EditorToolbar } from "./toolbar.js"; -import { noContextMenu } from "../display_utils.js"; +import { TouchManager } from "../touch_manager.js"; /** * @typedef {Object} AnnotationEditorParameters @@ -48,10 +49,16 @@ class AnnotationEditor { #disabled = false; + #dragPointerId = null; + + #dragPointerType = ""; + #keepAspectRatio = false; #resizersDiv = null; + #lastPointerCoords = null; + #savedDimensions = null; #focusAC = null; @@ -60,7 +67,7 @@ class AnnotationEditor { #hasBeenClicked = false; - #initialPosition = null; + #initialRect = null; #isEditing = false; @@ -76,6 +83,10 @@ class AnnotationEditor { #telemetryTimeouts = null; + #touchManager = null; + + _isCopy = false; + _editToolbar = null; _initialOptions = Object.create(null); @@ -88,7 +99,7 @@ class AnnotationEditor { _focusEventsAllowed = true; - static _l10nPromise = null; + static _l10n = null; static _l10nResizer = null; @@ -186,6 +197,10 @@ class AnnotationEditor { return Object.getPrototypeOf(this).constructor._type; } + static get isDrawer() { + return false; + } + static get _defaultLineColor() { return shadow( this, @@ -210,6 +225,8 @@ class AnnotationEditor { * @param {Object} l10n */ static initialize(l10n, _uiManager) { + AnnotationEditor._l10n ??= l10n; + AnnotationEditor._l10nResizer ||= Object.freeze({ topLeft: "pdfjs-editor-resizer-top-left", topMiddle: "pdfjs-editor-resizer-top-middle", @@ -221,21 +238,6 @@ class AnnotationEditor { middleLeft: "pdfjs-editor-resizer-middle-left", }); - AnnotationEditor._l10nPromise ||= new Map([ - ...[ - "pdfjs-editor-alt-text-button-label", - "pdfjs-editor-alt-text-edit-button-label", - "pdfjs-editor-alt-text-decorative-tooltip", - "pdfjs-editor-new-alt-text-added-button-label", - "pdfjs-editor-new-alt-text-missing-button-label", - "pdfjs-editor-new-alt-text-to-review-button-label", - ].map(str => [str, l10n.get(str)]), - ...[ - // Strings that need l10n-arguments. - "pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", - ].map(str => [str, l10n.get.bind(l10n, str)]), - ]); - if (AnnotationEditor._borderLineWidth !== -1) { return; } @@ -442,12 +444,25 @@ class AnnotationEditor { this.fixAndSetPosition(); } + _moveAfterPaste(baseX, baseY) { + const [parentWidth, parentHeight] = this.parentDimensions; + this.setAt( + baseX * parentWidth, + baseY * parentHeight, + this.width * parentWidth, + this.height * parentHeight + ); + this._onTranslated(); + } + #translate([width, height], x, y) { [x, y] = this.screenToPageTranslation(x, y); this.x += x / width; this.y += y / height; + this._onTranslating(this.x, this.y); + this.fixAndSetPosition(); } @@ -469,14 +484,21 @@ class AnnotationEditor { * @param {number} y - y-translation in page coordinates. */ translateInPage(x, y) { - this.#initialPosition ||= [this.x, this.y]; + this.#initialRect ||= [this.x, this.y, this.width, this.height]; this.#translate(this.pageDimensions, x, y); this.div.scrollIntoView({ block: "nearest" }); } + translationDone() { + this._onTranslated(this.x, this.y); + } + drag(tx, ty) { - this.#initialPosition ||= [this.x, this.y]; - const [parentWidth, parentHeight] = this.parentDimensions; + this.#initialRect ||= [this.x, this.y, this.width, this.height]; + const { + div, + parentDimensions: [parentWidth, parentHeight], + } = this; this.x += tx / parentWidth; this.y += ty / parentHeight; if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) { @@ -503,16 +525,41 @@ class AnnotationEditor { x += bx; y += by; - this.div.style.left = `${(100 * x).toFixed(2)}%`; - this.div.style.top = `${(100 * y).toFixed(2)}%`; - this.div.scrollIntoView({ block: "nearest" }); + const { style } = div; + style.left = `${(100 * x).toFixed(2)}%`; + style.top = `${(100 * y).toFixed(2)}%`; + + this._onTranslating(x, y); + + div.scrollIntoView({ block: "nearest" }); } + /** + * Called when the editor is being translated. + * @param {number} x - in page coordinates. + * @param {number} y - in page coordinates. + */ + _onTranslating(x, y) {} + + /** + * Called when the editor has been translated. + * @param {number} x - in page coordinates. + * @param {number} y - in page coordinates. + */ + _onTranslated(x, y) {} + get _hasBeenMoved() { return ( - !!this.#initialPosition && - (this.#initialPosition[0] !== this.x || - this.#initialPosition[1] !== this.y) + !!this.#initialRect && + (this.#initialRect[0] !== this.x || this.#initialRect[1] !== this.y) + ); + } + + get _hasBeenResized() { + return ( + !!this.#initialRect && + (this.#initialRect[2] !== this.width || + this.#initialRect[3] !== this.height) ); } @@ -553,7 +600,10 @@ class AnnotationEditor { * @param {number} [rotation] - the rotation of the page. */ fixAndSetPosition(rotation = this.rotation) { - const [pageWidth, pageHeight] = this.pageDimensions; + const { + div: { style }, + pageDimensions: [pageWidth, pageHeight], + } = this; let { x, y, width, height } = this; width *= pageWidth; height *= pageHeight; @@ -588,7 +638,6 @@ class AnnotationEditor { x += bx; y += by; - const { style } = this.div; style.left = `${(100 * x).toFixed(2)}%`; style.top = `${(100 * y).toFixed(2)}%`; @@ -666,9 +715,10 @@ class AnnotationEditor { */ setDims(width, height) { const [parentWidth, parentHeight] = this.parentDimensions; - this.div.style.width = `${((100 * width) / parentWidth).toFixed(2)}%`; + const { style } = this.div; + style.width = `${((100 * width) / parentWidth).toFixed(2)}%`; if (!this.#keepAspectRatio) { - this.div.style.height = `${((100 * height) / parentHeight).toFixed(2)}%`; + style.height = `${((100 * height) / parentHeight).toFixed(2)}%`; } } @@ -686,9 +736,7 @@ class AnnotationEditor { style.width = `${((100 * parseFloat(width)) / parentWidth).toFixed(2)}%`; } if (!this.#keepAspectRatio && !heightPercent) { - style.height = `${((100 * parseFloat(height)) / parentHeight).toFixed( - 2 - )}%`; + style.height = `${((100 * parseFloat(height)) / parentHeight).toFixed(2)}%`; } } @@ -749,6 +797,7 @@ class AnnotationEditor { const savedDraggable = this._isDraggable; this._isDraggable = false; + this.#lastPointerCoords = [event.screenX, event.screenY]; const ac = new AbortController(); const signal = this._uiManager.combinedSignal(ac); @@ -759,11 +808,18 @@ class AnnotationEditor { this.#resizerPointermove.bind(this, name), { passive: true, capture: true, signal } ); + window.addEventListener( + "touchmove", + stopEvent /* Prevent the page from scrolling */, + { passive: false, signal } + ); window.addEventListener("contextmenu", noContextMenu, { signal }); - const savedX = this.x; - const savedY = this.y; - const savedWidth = this.width; - const savedHeight = this.height; + this.#savedDimensions = { + savedX: this.x, + savedY: this.y, + savedWidth: this.width, + savedHeight: this.height, + }; const savedParentCursor = this.parent.div.style.cursor; const savedCursor = this.div.style.cursor; this.div.style.cursor = this.parent.div.style.cursor = @@ -777,7 +833,7 @@ class AnnotationEditor { this.parent.div.style.cursor = savedParentCursor; this.div.style.cursor = savedCursor; - this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight); + this.#addResizeToUndoStack(); }; window.addEventListener("pointerup", pointerUpCallback, { signal }); // If the user switches to another window (with alt+tab), then we end the @@ -785,7 +841,29 @@ class AnnotationEditor { window.addEventListener("blur", pointerUpCallback, { signal }); } - #addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight) { + #resize(x, y, width, height) { + this.width = width; + this.height = height; + this.x = x; + this.y = y; + const [parentWidth, parentHeight] = this.parentDimensions; + this.setDims(parentWidth * width, parentHeight * height); + this.fixAndSetPosition(); + this._onResized(); + } + + /** + * Called when the editor has been resized. + */ + _onResized() {} + + #addResizeToUndoStack() { + if (!this.#savedDimensions) { + return; + } + const { savedX, savedY, savedWidth, savedHeight } = this.#savedDimensions; + this.#savedDimensions = null; + const newX = this.x; const newY = this.y; const newWidth = this.width; @@ -800,28 +878,19 @@ class AnnotationEditor { } this.addCommands({ - cmd: () => { - this.width = newWidth; - this.height = newHeight; - this.x = newX; - this.y = newY; - const [parentWidth, parentHeight] = this.parentDimensions; - this.setDims(parentWidth * newWidth, parentHeight * newHeight); - this.fixAndSetPosition(); - }, - undo: () => { - this.width = savedWidth; - this.height = savedHeight; - this.x = savedX; - this.y = savedY; - const [parentWidth, parentHeight] = this.parentDimensions; - this.setDims(parentWidth * savedWidth, parentHeight * savedHeight); - this.fixAndSetPosition(); - }, + cmd: this.#resize.bind(this, newX, newY, newWidth, newHeight), + undo: this.#resize.bind(this, savedX, savedY, savedWidth, savedHeight), mustExec: true, }); } + static _round(x) { + // 10000 because we multiply by 100 and use toFixed(2) in fixAndSetPosition. + // Without rounding, the positions of the corners other than the top left + // one can be slightly wrong. + return Math.round(x * 10000) / 10000; + } + #resizerPointermove(name, event) { const [parentWidth, parentHeight] = this.parentDimensions; const savedX = this.x; @@ -831,10 +900,6 @@ class AnnotationEditor { const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; - // 10000 because we multiply by 100 and use toFixed(2) in fixAndSetPosition. - // Without rounding, the positions of the corners other than the top left - // one can be slightly wrong. - const round = x => Math.round(x * 10000) / 10000; const rotationMatrix = this.#getRotationMatrix(this.rotation); const transf = (x, y) => [ rotationMatrix[0] * x + rotationMatrix[2] * y, @@ -894,15 +959,28 @@ class AnnotationEditor { const point = getPoint(savedWidth, savedHeight); const oppositePoint = getOpposite(savedWidth, savedHeight); let transfOppositePoint = transf(...oppositePoint); - const oppositeX = round(savedX + transfOppositePoint[0]); - const oppositeY = round(savedY + transfOppositePoint[1]); + const oppositeX = AnnotationEditor._round(savedX + transfOppositePoint[0]); + const oppositeY = AnnotationEditor._round(savedY + transfOppositePoint[1]); let ratioX = 1; let ratioY = 1; - let [deltaX, deltaY] = this.screenToPageTranslation( - event.movementX, - event.movementY - ); + let deltaX, deltaY; + + if (!event.fromKeyboard) { + // We can't use event.movementX/Y because they're not reliable: + // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX + // (it was buggy on a laptop with a touch screen). + const { screenX, screenY } = event; + const [lastScreenX, lastScreenY] = this.#lastPointerCoords; + [deltaX, deltaY] = this.screenToPageTranslation( + screenX - lastScreenX, + screenY - lastScreenY + ); + this.#lastPointerCoords[0] = screenX; + this.#lastPointerCoords[1] = screenY; + } else { + ({ deltaX, deltaY } = event); + } [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight); if (isDiagonal) { @@ -935,12 +1013,13 @@ class AnnotationEditor { ) / savedHeight; } - const newWidth = round(savedWidth * ratioX); - const newHeight = round(savedHeight * ratioY); + const newWidth = AnnotationEditor._round(savedWidth * ratioX); + const newHeight = AnnotationEditor._round(savedHeight * ratioY); transfOppositePoint = transf(...getOpposite(newWidth, newHeight)); const newX = oppositeX - transfOppositePoint[0]; const newY = oppositeY - transfOppositePoint[1]; + this.#initialRect ||= [this.x, this.y, this.width, this.height]; this.width = newWidth; this.height = newHeight; this.x = newX; @@ -948,8 +1027,15 @@ class AnnotationEditor { this.setDims(parentWidth * newWidth, parentHeight * newHeight); this.fixAndSetPosition(); + + this._onResizing(); } + /** + * Called when the editor is being resized. + */ + _onResizing() {} + /** * Called when the alt text dialog is closed. */ @@ -1003,7 +1089,7 @@ class AnnotationEditor { if (this.#altText) { return; } - AltText.initialize(AnnotationEditor._l10nPromise); + AltText.initialize(AnnotationEditor._l10n); this.#altText = new AltText(this); if (this.#accessibilityData) { this.#altText.data = this.#accessibilityData; @@ -1079,9 +1165,92 @@ class AnnotationEditor { bindEvents(this, this.div, ["pointerdown"]); + if (this.isResizable && this._uiManager._supportsPinchToZoom) { + this.#touchManager ||= new TouchManager({ + container: this.div, + isPinchingDisabled: () => !this.isSelected, + onPinchStart: this.#touchPinchStartCallback.bind(this), + onPinching: this.#touchPinchCallback.bind(this), + onPinchEnd: this.#touchPinchEndCallback.bind(this), + signal: this._uiManager._signal, + }); + } + + this._uiManager._editorUndoBar?.hide(); + return this.div; } + #touchPinchStartCallback() { + this.#savedDimensions = { + savedX: this.x, + savedY: this.y, + savedWidth: this.width, + savedHeight: this.height, + }; + this.#altText?.toggle(false); + this.parent.togglePointerEvents(false); + } + + #touchPinchCallback(_origin, prevDistance, distance) { + // Slightly slow down the zooming because the editor could be small and the + // user could have difficulties to rescale it as they want. + const slowDownFactor = 0.7; + let factor = + slowDownFactor * (distance / prevDistance) + 1 - slowDownFactor; + if (factor === 1) { + return; + } + + const rotationMatrix = this.#getRotationMatrix(this.rotation); + const transf = (x, y) => [ + rotationMatrix[0] * x + rotationMatrix[2] * y, + rotationMatrix[1] * x + rotationMatrix[3] * y, + ]; + + // The center of the editor is the fixed point. + const [parentWidth, parentHeight] = this.parentDimensions; + const savedX = this.x; + const savedY = this.y; + const savedWidth = this.width; + const savedHeight = this.height; + + const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; + const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; + factor = Math.max( + Math.min(factor, 1 / savedWidth, 1 / savedHeight), + minWidth / savedWidth, + minHeight / savedHeight + ); + const newWidth = AnnotationEditor._round(savedWidth * factor); + const newHeight = AnnotationEditor._round(savedHeight * factor); + if (newWidth === savedWidth && newHeight === savedHeight) { + return; + } + + this.#initialRect ||= [savedX, savedY, savedWidth, savedHeight]; + const transfCenterPoint = transf(savedWidth / 2, savedHeight / 2); + const centerX = AnnotationEditor._round(savedX + transfCenterPoint[0]); + const centerY = AnnotationEditor._round(savedY + transfCenterPoint[1]); + const newTransfCenterPoint = transf(newWidth / 2, newHeight / 2); + + this.x = centerX - newTransfCenterPoint[0]; + this.y = centerY - newTransfCenterPoint[1]; + this.width = newWidth; + this.height = newHeight; + + this.setDims(parentWidth * newWidth, parentHeight * newHeight); + this.fixAndSetPosition(); + + this._onResizing(); + } + + #touchPinchEndCallback() { + this.#altText?.toggle(true); + this.parent.togglePointerEvents(true); + this.#addResizeToUndoStack(); + } + /** * Onpointerdown callback. * @param {PointerEvent} event @@ -1093,7 +1262,6 @@ class AnnotationEditor { event.preventDefault(); return; } - this.#hasBeenClicked = true; if (this._isDraggable) { @@ -1124,41 +1292,81 @@ class AnnotationEditor { #setUpDragSession(event) { const { isSelected } = this; this._uiManager.setUpDragSession(); + let hasDraggingStarted = false; const ac = new AbortController(); const signal = this._uiManager.combinedSignal(ac); + const opts = { capture: true, passive: false, signal }; + const cancelDrag = e => { + ac.abort(); + + this.#dragPointerId = null; + this.#hasBeenClicked = false; + if (!this._uiManager.endDragSession()) { + this.#selectOnPointerEvent(e); + } + if (hasDraggingStarted) { + this._onStopDragging(); + } + }; if (isSelected) { - this.div.classList.add("moving"); this.#prevDragX = event.clientX; this.#prevDragY = event.clientY; - const pointerMoveCallback = e => { - const { clientX: x, clientY: y } = e; - const [tx, ty] = this.screenToPageTranslation( - x - this.#prevDragX, - y - this.#prevDragY - ); - this.#prevDragX = x; - this.#prevDragY = y; - this._uiManager.dragSelectedEditors(tx, ty); - }; - window.addEventListener("pointermove", pointerMoveCallback, { - passive: true, - capture: true, - signal, - }); + this.#dragPointerId = event.pointerId; + this.#dragPointerType = event.pointerType; + window.addEventListener( + "pointermove", + e => { + if (!hasDraggingStarted) { + hasDraggingStarted = true; + this._onStartDragging(); + } + const { clientX: x, clientY: y, pointerId } = e; + if (pointerId !== this.#dragPointerId) { + stopEvent(e); + return; + } + const [tx, ty] = this.screenToPageTranslation( + x - this.#prevDragX, + y - this.#prevDragY + ); + this.#prevDragX = x; + this.#prevDragY = y; + this._uiManager.dragSelectedEditors(tx, ty); + }, + opts + ); + window.addEventListener( + "touchmove", + stopEvent /* Prevent the page from scrolling */, + opts + ); + window.addEventListener( + "pointerdown", + // If the user drags with one finger and then clicks with another. + e => { + if (e.pointerType === this.#dragPointerType) { + // We've a pinch to zoom session. + // We cannot have two primaries at the same time. + // It's possible to be in this state with Firefox and Gnome when + // trying to drag with three fingers (see bug 1933716). + if (this.#touchManager || e.isPrimary) { + cancelDrag(e); + } + } + stopEvent(e); + }, + opts + ); } - const pointerUpCallback = () => { - ac.abort(); - if (isSelected) { - this.div.classList.remove("moving"); - } - - this.#hasBeenClicked = false; - if (!this._uiManager.endDragSession()) { - this.#selectOnPointerEvent(event); + const pointerUpCallback = e => { + if (!this.#dragPointerId || this.#dragPointerId === e.pointerId) { + cancelDrag(e); + return; } + stopEvent(e); }; window.addEventListener("pointerup", pointerUpCallback, { signal }); // If the user is using alt+tab during the dragging session, the pointerup @@ -1167,6 +1375,10 @@ class AnnotationEditor { window.addEventListener("blur", pointerUpCallback, { signal }); } + _onStartDragging() {} + + _onStopDragging() {} + moveInDOM() { // Moving the editor in the DOM can be expensive, so we wait a bit before. // It's important to not block the UI (for example when changing the font @@ -1177,6 +1389,11 @@ class AnnotationEditor { this.#moveInDOMTimeout = setTimeout(() => { this.#moveInDOMTimeout = null; this.parent?.moveEditorInDOM(this); + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { + this._uiManager._eventBus.dispatch("editormovedindom", { + source: this, + }); + } }, 0); } @@ -1185,6 +1402,7 @@ class AnnotationEditor { this.x = x; this.y = y; this.fixAndSetPosition(); + this._onTranslated(); } /** @@ -1260,8 +1478,9 @@ class AnnotationEditor { /** * Executed once this editor has been rendered. + * @param {boolean} focus - true if the editor should be focused. */ - onceAdded() {} + onceAdded(focus) {} /** * Check if the editor contains something. @@ -1310,6 +1529,12 @@ class AnnotationEditor { return this.div && !this.isAttachedToDOM; } + get isOnScreen() { + const { top, left, bottom, right } = this.getClientDimensions(); + const { innerHeight, innerWidth } = window; + return left < innerWidth && right > 0 && top < innerHeight && bottom > 0; + } + #addFocusListeners() { if (this.#focusAC || !this.div) { return; @@ -1331,11 +1556,16 @@ class AnnotationEditor { } /** - * Rotate the editor. + * Rotate the editor when the page is rotated. * @param {number} angle */ rotate(_angle) {} + /** + * Resize the editor when the page is resized. + */ + resize() {} + /** * Serialize the editor when it has been deleted. * @returns {Object} @@ -1380,6 +1610,7 @@ class AnnotationEditor { }); editor.rotation = data.rotation; editor.#accessibilityData = data.accessibilityData; + editor._isCopy = data.isCopy || false; const [pageWidth, pageHeight] = editor.pageDimensions; const [x, y, width, height] = editor.getRectInCurrentCoords( @@ -1438,6 +1669,8 @@ class AnnotationEditor { this.#telemetryTimeouts = null; } this.parent = null; + this.#touchManager?.destroy(); + this.#touchManager = null; } /** @@ -1572,19 +1805,16 @@ class AnnotationEditor { return; } this.#resizerPointermove(this.#focusedResizerName, { - movementX: x, - movementY: y, + deltaX: x, + deltaY: y, + fromKeyboard: true, }); } #stopResizing() { this.#isResizerEnabledForKeyboard = false; this.#setResizerTabIndex(-1); - if (this.#savedDimensions) { - const { savedX, savedY, savedWidth, savedHeight } = this.#savedDimensions; - this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight); - this.#savedDimensions = null; - } + this.#addResizeToUndoStack(); } _stopResizingWithKeyboard() { diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 0e1f1d435f63c..e17b5c3486a04 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -209,7 +209,7 @@ class FreeTextEditor extends AnnotationEditor { */ #updateFontSize(fontSize) { const setFontsize = size => { - this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`; + this.editorDiv.style.fontSize = `calc(${size}px * var(--total-scale-factor))`; this.translate(0, -(size - this.#fontSize) * this.parentScale); this.#fontSize = size; this.#setEditorDimensions(); @@ -363,13 +363,15 @@ class FreeTextEditor extends AnnotationEditor { } /** @inheritdoc */ - onceAdded() { + onceAdded(focus) { if (this.width) { // The editor was created in using ctrl+c. return; } this.enableEditMode(); - this.editorDiv.focus(); + if (focus) { + this.editorDiv.focus(); + } if (this._initialOptions?.isCentered) { this.center(); } @@ -551,7 +553,7 @@ class FreeTextEditor extends AnnotationEditor { } let baseX, baseY; - if (this.width) { + if (this._isCopy || this.annotationElementId) { baseX = this.x; baseY = this.y; } @@ -568,7 +570,7 @@ class FreeTextEditor extends AnnotationEditor { this.editorDiv.contentEditable = true; const { style } = this.editorDiv; - style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; style.color = this.#color; this.div.append(this.editorDiv); @@ -579,7 +581,7 @@ class FreeTextEditor extends AnnotationEditor { bindEvents(this, this.div, ["dblclick", "keydown"]); - if (this.width) { + if (this._isCopy || this.annotationElementId) { // This editor was created in using copy (ctrl+c). const [parentWidth, parentHeight] = this.parentDimensions; if (this.annotationElementId) { @@ -625,12 +627,7 @@ class FreeTextEditor extends AnnotationEditor { } this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); } else { - this.setAt( - baseX * parentWidth, - baseY * parentHeight, - this.width * parentWidth, - this.height * parentHeight - ); + this._moveAfterPaste(baseX, baseY); } this.#setContent(); @@ -845,6 +842,7 @@ class FreeTextEditor extends AnnotationEditor { if (isForCopying) { // Don't add the id when copying because the pasted editor mustn't be // linked to an existing annotation. + serialized.isCopy = true; return serialized; } @@ -876,7 +874,7 @@ class FreeTextEditor extends AnnotationEditor { return content; } const { style } = content; - style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; style.color = this.#color; content.replaceChildren(); diff --git a/src/display/editor/highlight.js b/src/display/editor/highlight.js index a8ef49a8bae6f..c361313a7a422 100644 --- a/src/display/editor/highlight.js +++ b/src/display/editor/highlight.js @@ -20,14 +20,17 @@ import { Util, } from "../../shared/util.js"; import { bindEvents, KeyboardManager } from "./tools.js"; -import { FreeOutliner, Outliner } from "./outliner.js"; +import { + FreeHighlightOutliner, + HighlightOutliner, +} from "./drawers/highlight.js"; import { HighlightAnnotationElement, InkAnnotationElement, } from "../annotation_layer.js"; +import { noContextMenu, stopEvent } from "../display_utils.js"; import { AnnotationEditor } from "./editor.js"; import { ColorPicker } from "./color_picker.js"; -import { noContextMenu } from "../display_utils.js"; /** * Basic draw editor in order to generate an Highlight annotation. @@ -149,16 +152,14 @@ class HighlightEditor extends AnnotationEditor { } #createOutlines() { - const outliner = new Outliner(this.#boxes, /* borderWidth = */ 0.001); + const outliner = new HighlightOutliner( + this.#boxes, + /* borderWidth = */ 0.001 + ); this.#highlightOutlines = outliner.getOutlines(); - ({ - x: this.x, - y: this.y, - width: this.width, - height: this.height, - } = this.#highlightOutlines.box); - - const outlinerForOutline = new Outliner( + [this.x, this.y, this.width, this.height] = this.#highlightOutlines.box; + + const outlinerForOutline = new HighlightOutliner( this.#boxes, /* borderWidth = */ 0.0025, /* innerMargin = */ 0.001, @@ -167,7 +168,7 @@ class HighlightEditor extends AnnotationEditor { this.#focusOutlines = outlinerForOutline.getOutlines(); // The last point is in the pages coordinate system. - const { lastPoint } = this.#focusOutlines.box; + const { lastPoint } = this.#focusOutlines; this.#lastPoint = [ (lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height, @@ -189,28 +190,44 @@ class HighlightEditor extends AnnotationEditor { this.#clipPathId = clipPathId; // We need to redraw the highlight because we change the coordinates to be // in the box coordinate system. - this.parent.drawLayer.finalizeLine(highlightId, highlightOutlines); - this.#outlineId = this.parent.drawLayer.highlightOutline( - this.#focusOutlines + this.parent.drawLayer.finalizeDraw(highlightId, { + bbox: highlightOutlines.box, + path: { + d: highlightOutlines.toSVGPath(), + }, + }); + this.#outlineId = this.parent.drawLayer.drawOutline( + { + rootClass: { + highlightOutline: true, + free: true, + }, + bbox: this.#focusOutlines.box, + path: { + d: this.#focusOutlines.toSVGPath(), + }, + }, + /* mustRemoveSelfIntersections = */ true ); } else if (this.parent) { const angle = this.parent.viewport.rotation; - this.parent.drawLayer.updateLine(this.#id, highlightOutlines); - this.parent.drawLayer.updateBox( - this.#id, - HighlightEditor.#rotateBbox( + this.parent.drawLayer.updateProperties(this.#id, { + bbox: HighlightEditor.#rotateBbox( this.#highlightOutlines.box, (angle - this.rotation + 360) % 360 - ) - ); - - this.parent.drawLayer.updateLine(this.#outlineId, this.#focusOutlines); - this.parent.drawLayer.updateBox( - this.#outlineId, - HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle) - ); + ), + path: { + d: highlightOutlines.toSVGPath(), + }, + }); + this.parent.drawLayer.updateProperties(this.#outlineId, { + bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), + path: { + d: this.#focusOutlines.toSVGPath(), + }, + }); } - const { x, y, width, height } = highlightOutlines.box; + const [x, y, width, height] = highlightOutlines.box; switch (this.rotation) { case 0: this.x = x; @@ -242,7 +259,7 @@ class HighlightEditor extends AnnotationEditor { } } - const { lastPoint } = this.#focusOutlines.box; + const { lastPoint } = this.#focusOutlines; this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height]; } @@ -320,10 +337,14 @@ class HighlightEditor extends AnnotationEditor { #updateColor(color) { const setColorAndOpacity = (col, opa) => { this.color = col; - this.parent?.drawLayer.changeColor(this.#id, col); - this.#colorPicker?.updateColor(col); this.#opacity = opa; - this.parent?.drawLayer.changeOpacity(this.#id, opa); + this.parent?.drawLayer.updateProperties(this.#id, { + root: { + fill: col, + "fill-opacity": opa, + }, + }); + this.#colorPicker?.updateColor(col); }; const savedColor = this.color; const savedOpacity = this.#opacity; @@ -418,11 +439,13 @@ class HighlightEditor extends AnnotationEditor { } /** @inheritdoc */ - onceAdded() { + onceAdded(focus) { if (!this.annotationElementId) { this.parent.addUndoableEditor(this); } - this.div.focus(); + if (focus) { + this.div.focus(); + } } /** @inheritdoc */ @@ -498,48 +521,54 @@ class HighlightEditor extends AnnotationEditor { if (this.#id !== null) { return; } - ({ id: this.#id, clipPathId: this.#clipPathId } = - parent.drawLayer.highlight( - this.#highlightOutlines, - this.color, - this.#opacity - )); - this.#outlineId = parent.drawLayer.highlightOutline(this.#focusOutlines); + ({ id: this.#id, clipPathId: this.#clipPathId } = parent.drawLayer.draw( + { + bbox: this.#highlightOutlines.box, + root: { + viewBox: "0 0 1 1", + fill: this.color, + "fill-opacity": this.#opacity, + }, + rootClass: { + highlight: true, + free: this.#isFreeHighlight, + }, + path: { + d: this.#highlightOutlines.toSVGPath(), + }, + }, + /* isPathUpdatable = */ false, + /* hasClip = */ true + )); + this.#outlineId = parent.drawLayer.drawOutline( + { + rootClass: { + highlightOutline: true, + free: this.#isFreeHighlight, + }, + bbox: this.#focusOutlines.box, + path: { + d: this.#focusOutlines.toSVGPath(), + }, + }, + /* mustRemoveSelfIntersections = */ this.#isFreeHighlight + ); + if (this.#highlightDiv) { this.#highlightDiv.style.clipPath = this.#clipPathId; } } - static #rotateBbox({ x, y, width, height }, angle) { + static #rotateBbox([x, y, width, height], angle) { switch (angle) { case 90: - return { - x: 1 - y - height, - y: x, - width: height, - height: width, - }; + return [1 - y - height, x, height, width]; case 180: - return { - x: 1 - x - width, - y: 1 - y - height, - width, - height, - }; + return [1 - x - width, 1 - y - height, width, height]; case 270: - return { - x: y, - y: 1 - x - width, - width: height, - height: width, - }; + return [y, 1 - x - width, height, width]; } - return { - x, - y, - width, - height, - }; + return [x, y, width, height]; } /** @inheritdoc */ @@ -552,15 +581,23 @@ class HighlightEditor extends AnnotationEditor { box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle); } else { // An highlight annotation is always drawn horizontally. - box = HighlightEditor.#rotateBbox(this, angle); - } - drawLayer.rotate(this.#id, angle); - drawLayer.rotate(this.#outlineId, angle); - drawLayer.updateBox(this.#id, box); - drawLayer.updateBox( - this.#outlineId, - HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle) - ); + box = HighlightEditor.#rotateBbox( + [this.x, this.y, this.width, this.height], + angle + ); + } + drawLayer.updateProperties(this.#id, { + bbox: box, + root: { + "data-main-rotation": angle, + }, + }); + drawLayer.updateProperties(this.#outlineId, { + bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), + root: { + "data-main-rotation": angle, + }, + }); } /** @inheritdoc */ @@ -597,13 +634,21 @@ class HighlightEditor extends AnnotationEditor { pointerover() { if (!this.isSelected) { - this.parent.drawLayer.addClass(this.#outlineId, "hovered"); + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: true, + }, + }); } } pointerleave() { if (!this.isSelected) { - this.parent.drawLayer.removeClass(this.#outlineId, "hovered"); + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: false, + }, + }); } } @@ -643,8 +688,12 @@ class HighlightEditor extends AnnotationEditor { if (!this.#outlineId) { return; } - this.parent?.drawLayer.removeClass(this.#outlineId, "hovered"); - this.parent?.drawLayer.addClass(this.#outlineId, "selected"); + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hovered: false, + selected: true, + }, + }); } /** @inheritdoc */ @@ -653,7 +702,11 @@ class HighlightEditor extends AnnotationEditor { if (!this.#outlineId) { return; } - this.parent?.drawLayer.removeClass(this.#outlineId, "selected"); + this.parent?.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + selected: false, + }, + }); if (!this.#isFreeHighlight) { this.#setCaret(/* start = */ false); } @@ -668,8 +721,16 @@ class HighlightEditor extends AnnotationEditor { show(visible = this._isVisible) { super.show(visible); if (this.parent) { - this.parent.drawLayer.show(this.#id, visible); - this.parent.drawLayer.show(this.#outlineId, visible); + this.parent.drawLayer.updateProperties(this.#id, { + rootClass: { + hidden: !visible, + }, + }); + this.parent.drawLayer.updateProperties(this.#outlineId, { + rootClass: { + hidden: !visible, + }, + }); } } @@ -690,15 +751,14 @@ class HighlightEditor extends AnnotationEditor { let i = 0; for (const { x, y, width, height } of boxes) { const sx = x * pageWidth + pageX; - const sy = (1 - y - height) * pageHeight + pageY; - // The specifications say that the rectangle should start from the bottom - // left corner and go counter-clockwise. - // But when opening the file in Adobe Acrobat it appears that this isn't - // correct hence the 4th and 6th numbers are just swapped. + const sy = (1 - y) * pageHeight + pageY; + // Serializes the rectangle in the Adobe Acrobat format. + // The rectangle's coordinates (b = bottom, t = top, L = left, R = right) + // are ordered as follows: tL, tR, bL, bR (bL origin). quadPoints[i] = quadPoints[i + 4] = sx; quadPoints[i + 1] = quadPoints[i + 3] = sy; quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth; - quadPoints[i + 5] = quadPoints[i + 7] = sy + height * pageHeight; + quadPoints[i + 5] = quadPoints[i + 7] = sy - height * pageHeight; i += 8; } return quadPoints; @@ -719,22 +779,21 @@ class HighlightEditor extends AnnotationEditor { const ac = new AbortController(); const signal = parent.combinedSignal(ac); - const pointerDown = e => { - // Avoid to have undesired clicks during the drawing. - e.preventDefault(); - e.stopPropagation(); - }; const pointerUpCallback = e => { ac.abort(); this.#endHighlight(parent, e); }; window.addEventListener("blur", pointerUpCallback, { signal }); window.addEventListener("pointerup", pointerUpCallback, { signal }); - window.addEventListener("pointerdown", pointerDown, { - capture: true, - passive: false, - signal, - }); + window.addEventListener( + "pointerdown", + stopEvent /* Avoid to have undesired clicks during the drawing. */, + { + capture: true, + passive: false, + signal, + } + ); window.addEventListener("contextmenu", noContextMenu, { signal }); textLayer.addEventListener( @@ -742,7 +801,7 @@ class HighlightEditor extends AnnotationEditor { this.#highlightMove.bind(this, parent), { signal } ); - this._freeHighlight = new FreeOutliner( + this._freeHighlight = new FreeHighlightOutliner( { x, y }, [layerX, layerY, parentWidth, parentHeight], parent.scale, @@ -751,18 +810,35 @@ class HighlightEditor extends AnnotationEditor { /* innerMargin = */ 0.001 ); ({ id: this._freeHighlightId, clipPathId: this._freeHighlightClipId } = - parent.drawLayer.highlight( - this._freeHighlight, - this._defaultColor, - this._defaultOpacity, - /* isPathUpdatable = */ true + parent.drawLayer.draw( + { + bbox: [0, 0, 1, 1], + root: { + viewBox: "0 0 1 1", + fill: this._defaultColor, + "fill-opacity": this._defaultOpacity, + }, + rootClass: { + highlight: true, + free: true, + }, + path: { + d: this._freeHighlight.toSVGPath(), + }, + }, + /* isPathUpdatable = */ true, + /* hasClip = */ true )); } static #highlightMove(parent, event) { if (this._freeHighlight.add(event)) { // Redraw only if the point has been added. - parent.drawLayer.updatePath(this._freeHighlightId, this._freeHighlight); + parent.drawLayer.updateProperties(this._freeHighlightId, { + path: { + d: this._freeHighlight.toSVGPath(), + }, + }); } } @@ -775,7 +851,7 @@ class HighlightEditor extends AnnotationEditor { methodOfCreation: "main_toolbar", }); } else { - parent.drawLayer.removeFreeHighlight(this._freeHighlightId); + parent.drawLayer.remove(this._freeHighlightId); } this._freeHighlightId = -1; this._freeHighlight = null; @@ -869,7 +945,7 @@ class HighlightEditor extends AnnotationEditor { x: points[0] - pageX, y: pageHeight - (points[1] - pageY), }; - const outliner = new FreeOutliner( + const outliner = new FreeHighlightOutliner( point, [0, 0, pageWidth, pageHeight], 1, @@ -882,11 +958,24 @@ class HighlightEditor extends AnnotationEditor { point.y = pageHeight - (points[i + 1] - pageY); outliner.add(point); } - const { id, clipPathId } = parent.drawLayer.highlight( - outliner, - editor.color, - editor._defaultOpacity, - /* isPathUpdatable = */ true + const { id, clipPathId } = parent.drawLayer.draw( + { + bbox: [0, 0, 1, 1], + root: { + viewBox: "0 0 1 1", + fill: editor.color, + "fill-opacity": editor._defaultOpacity, + }, + rootClass: { + highlight: true, + free: true, + }, + path: { + d: outliner.toSVGPath(), + }, + }, + /* isPathUpdatable = */ true, + /* hasClip = */ true ); editor.#createFreeOutlines({ highlightOutlines: outliner.getOutlines(), diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index c5d0def6c51bb..a3062bd030064 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -16,1221 +16,271 @@ import { AnnotationEditorParamsType, AnnotationEditorType, - assert, + shadow, Util, } from "../../shared/util.js"; +import { DrawingEditor, DrawingOptions } from "./draw.js"; +import { InkDrawOutline, InkDrawOutliner } from "./drawers/inkdraw.js"; import { AnnotationEditor } from "./editor.js"; import { InkAnnotationElement } from "../annotation_layer.js"; -import { noContextMenu } from "../display_utils.js"; -import { opacityToHex } from "./tools.js"; -/** - * Basic draw editor in order to generate an Ink annotation. - */ -class InkEditor extends AnnotationEditor { - #baseHeight = 0; - - #baseWidth = 0; - - #canvasContextMenuTimeoutId = null; - - #currentPath2D = new Path2D(); - - #disableEditing = false; - - #drawingAC = null; - - #hasSomethingToDraw = false; - - #isCanvasInitialized = false; - - #observer = null; - - #pointerdownAC = null; - - #realWidth = 0; - - #realHeight = 0; - - #requestFrameCallback = null; - - static _defaultColor = null; +class InkDrawingOptions extends DrawingOptions { + constructor(viewerParameters) { + super(); + this._viewParameters = viewerParameters; + + super.updateProperties({ + fill: "none", + stroke: AnnotationEditor._defaultLineColor, + "stroke-opacity": 1, + "stroke-width": 1, + "stroke-linecap": "round", + "stroke-linejoin": "round", + "stroke-miterlimit": 10, + }); + } - static _defaultOpacity = 1; + updateSVGProperty(name, value) { + if (name === "stroke-width") { + value ??= this["stroke-width"]; + value *= this._viewParameters.realScale; + } + super.updateSVGProperty(name, value); + } - static _defaultThickness = 1; + clone() { + const clone = new InkDrawingOptions(this._viewParameters); + clone.updateAll(this); + return clone; + } +} +/** + * Basic draw editor in order to generate an Ink annotation. + */ +class InkEditor extends DrawingEditor { static _type = "ink"; static _editorType = AnnotationEditorType.INK; + static _defaultDrawingOptions = null; + constructor(params) { super({ ...params, name: "inkEditor" }); - this.color = params.color || null; - this.thickness = params.thickness || null; - this.opacity = params.opacity || null; - this.paths = []; - this.bezierPath2D = []; - this.allRawPaths = []; - this.currentPath = []; - this.scaleFactor = 1; - this.translationX = this.translationY = 0; - this.x = 0; - this.y = 0; this._willKeepAspectRatio = true; } /** @inheritdoc */ static initialize(l10n, uiManager) { AnnotationEditor.initialize(l10n, uiManager); + this._defaultDrawingOptions = new InkDrawingOptions( + uiManager.viewParameters + ); } /** @inheritdoc */ - static updateDefaultParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.INK_THICKNESS: - InkEditor._defaultThickness = value; - break; - case AnnotationEditorParamsType.INK_COLOR: - InkEditor._defaultColor = value; - break; - case AnnotationEditorParamsType.INK_OPACITY: - InkEditor._defaultOpacity = value / 100; - break; - } - } - - /** @inheritdoc */ - updateParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.INK_THICKNESS: - this.#updateThickness(value); - break; - case AnnotationEditorParamsType.INK_COLOR: - this.#updateColor(value); - break; - case AnnotationEditorParamsType.INK_OPACITY: - this.#updateOpacity(value); - break; - } - } - - /** @inheritdoc */ - static get defaultPropertiesToUpdate() { - return [ - [AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], - [ - AnnotationEditorParamsType.INK_COLOR, - InkEditor._defaultColor || AnnotationEditor._defaultLineColor, - ], - [ - AnnotationEditorParamsType.INK_OPACITY, - Math.round(InkEditor._defaultOpacity * 100), - ], - ]; - } - - /** @inheritdoc */ - get propertiesToUpdate() { - return [ - [ - AnnotationEditorParamsType.INK_THICKNESS, - this.thickness || InkEditor._defaultThickness, - ], - [ - AnnotationEditorParamsType.INK_COLOR, - this.color || - InkEditor._defaultColor || - AnnotationEditor._defaultLineColor, - ], - [ - AnnotationEditorParamsType.INK_OPACITY, - Math.round(100 * (this.opacity ?? InkEditor._defaultOpacity)), - ], - ]; - } - - /** - * Update the thickness and make this action undoable. - * @param {number} thickness - */ - #updateThickness(thickness) { - const setThickness = th => { - this.thickness = th; - this.#fitToContent(); - }; - const savedThickness = this.thickness; - this.addCommands({ - cmd: setThickness.bind(this, thickness), - undo: setThickness.bind(this, savedThickness), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.INK_THICKNESS, - overwriteIfSameType: true, - keepUndo: true, - }); - } - - /** - * Update the color and make this action undoable. - * @param {string} color - */ - #updateColor(color) { - const setColor = col => { - this.color = col; - this.#redraw(); - }; - const savedColor = this.color; - this.addCommands({ - cmd: setColor.bind(this, color), - undo: setColor.bind(this, savedColor), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.INK_COLOR, - overwriteIfSameType: true, - keepUndo: true, - }); - } - - /** - * Update the opacity and make this action undoable. - * @param {number} opacity - */ - #updateOpacity(opacity) { - const setOpacity = op => { - this.opacity = op; - this.#redraw(); - }; - opacity /= 100; - const savedOpacity = this.opacity; - this.addCommands({ - cmd: setOpacity.bind(this, opacity), - undo: setOpacity.bind(this, savedOpacity), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.INK_OPACITY, - overwriteIfSameType: true, - keepUndo: true, - }); - } - - /** @inheritdoc */ - rebuild() { - if (!this.parent) { - return; - } - super.rebuild(); - if (this.div === null) { - return; - } - - if (!this.canvas) { - this.#createCanvas(); - this.#createObserver(); - } - - if (!this.isAttachedToDOM) { - // At some point this editor was removed and we're rebuilding it, - // hence we must add it to its parent. - this.parent.add(this); - this.#setCanvasDims(); - } - this.#fitToContent(); - } - - /** @inheritdoc */ - remove() { - if (this.canvas === null) { - return; - } - - if (!this.isEmpty()) { - this.commit(); - } - - // Destroy the canvas. - this.canvas.width = this.canvas.height = 0; - this.canvas.remove(); - this.canvas = null; - - if (this.#canvasContextMenuTimeoutId) { - clearTimeout(this.#canvasContextMenuTimeoutId); - this.#canvasContextMenuTimeoutId = null; - } - - this.#observer?.disconnect(); - this.#observer = null; - - super.remove(); - } - - setParent(parent) { - if (!this.parent && parent) { - // We've a parent hence the rescale will be handled thanks to the - // ResizeObserver. - this._uiManager.removeShouldRescale(this); - } else if (this.parent && parent === null) { - // The editor is removed from the DOM, hence we handle the rescale thanks - // to the onScaleChanging callback. - // This way, it'll be saved/printed correctly. - this._uiManager.addShouldRescale(this); - } - super.setParent(parent); - } - - onScaleChanging() { - const [parentWidth, parentHeight] = this.parentDimensions; - const width = this.width * parentWidth; - const height = this.height * parentHeight; - this.setDimensions(width, height); - } - - /** @inheritdoc */ - enableEditMode() { - if (this.#disableEditing || this.canvas === null) { - return; - } - - super.enableEditMode(); - this._isDraggable = false; - this.#addPointerdownListener(); - } - - /** @inheritdoc */ - disableEditMode() { - if (!this.isInEditMode() || this.canvas === null) { - return; - } - - super.disableEditMode(); - this._isDraggable = !this.isEmpty(); - this.div.classList.remove("editing"); - this.#removePointerdownListener(); + static getDefaultDrawingOptions(options) { + const clone = this._defaultDrawingOptions.clone(); + clone.updateProperties(options); + return clone; } /** @inheritdoc */ - onceAdded() { - this._isDraggable = !this.isEmpty(); + static get supportMultipleDrawings() { + return true; } /** @inheritdoc */ - isEmpty() { - return ( - this.paths.length === 0 || - (this.paths.length === 1 && this.paths[0].length === 0) - ); - } - - #getInitialBBox() { - const { - parentRotation, - parentDimensions: [width, height], - } = this; - switch (parentRotation) { - case 90: - return [0, height, height, width]; - case 180: - return [width, height, width, height]; - case 270: - return [width, 0, height, width]; - default: - return [0, 0, width, height]; - } - } - - /** - * Set line styles. - */ - #setStroke() { - const { ctx, color, opacity, thickness, parentScale, scaleFactor } = this; - ctx.lineWidth = (thickness * parentScale) / scaleFactor; - ctx.lineCap = "round"; - ctx.lineJoin = "round"; - ctx.miterLimit = 10; - ctx.strokeStyle = `${color}${opacityToHex(opacity)}`; - } - - /** - * Start to draw on the canvas. - * @param {number} x - * @param {number} y - */ - #startDrawing(x, y) { - this.canvas.addEventListener("contextmenu", noContextMenu, { - signal: this._uiManager._signal, - }); - this.#removePointerdownListener(); - - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { - assert( - !this.#drawingAC, - "No `this.#drawingAC` AbortController should exist." - ); - } - this.#drawingAC = new AbortController(); - const signal = this._uiManager.combinedSignal(this.#drawingAC); - - this.canvas.addEventListener( - "pointerleave", - this.canvasPointerleave.bind(this), - { signal } - ); - this.canvas.addEventListener( - "pointermove", - this.canvasPointermove.bind(this), - { signal } + static get typesMap() { + return shadow( + this, + "typesMap", + new Map([ + [AnnotationEditorParamsType.INK_THICKNESS, "stroke-width"], + [AnnotationEditorParamsType.INK_COLOR, "stroke"], + [AnnotationEditorParamsType.INK_OPACITY, "stroke-opacity"], + ]) ); - this.canvas.addEventListener("pointerup", this.canvasPointerup.bind(this), { - signal, - }); - - this.isEditing = true; - if (!this.#isCanvasInitialized) { - this.#isCanvasInitialized = true; - this.#setCanvasDims(); - this.thickness ||= InkEditor._defaultThickness; - this.color ||= - InkEditor._defaultColor || AnnotationEditor._defaultLineColor; - this.opacity ??= InkEditor._defaultOpacity; - } - this.currentPath.push([x, y]); - this.#hasSomethingToDraw = false; - this.#setStroke(); - - this.#requestFrameCallback = () => { - this.#drawPoints(); - if (this.#requestFrameCallback) { - window.requestAnimationFrame(this.#requestFrameCallback); - } - }; - window.requestAnimationFrame(this.#requestFrameCallback); } - /** - * Draw on the canvas. - * @param {number} x - * @param {number} y - */ - #draw(x, y) { - const [lastX, lastY] = this.currentPath.at(-1); - if (this.currentPath.length > 1 && x === lastX && y === lastY) { - return; - } - const currentPath = this.currentPath; - let path2D = this.#currentPath2D; - currentPath.push([x, y]); - this.#hasSomethingToDraw = true; - - if (currentPath.length <= 2) { - path2D.moveTo(...currentPath[0]); - path2D.lineTo(x, y); - return; - } - - if (currentPath.length === 3) { - this.#currentPath2D = path2D = new Path2D(); - path2D.moveTo(...currentPath[0]); - } - - this.#makeBezierCurve( - path2D, - ...currentPath.at(-3), - ...currentPath.at(-2), + /** @inheritdoc */ + static createDrawerInstance(x, y, parentWidth, parentHeight, rotation) { + return new InkDrawOutliner( x, - y + y, + parentWidth, + parentHeight, + rotation, + this._defaultDrawingOptions["stroke-width"] ); } - #endPath() { - if (this.currentPath.length === 0) { - return; - } - const lastPoint = this.currentPath.at(-1); - this.#currentPath2D.lineTo(...lastPoint); - } - - /** - * Stop to draw on the canvas. - * @param {number} x - * @param {number} y - */ - #stopDrawing(x, y) { - this.#requestFrameCallback = null; - - x = Math.min(Math.max(x, 0), this.canvas.width); - y = Math.min(Math.max(y, 0), this.canvas.height); - - this.#draw(x, y); - this.#endPath(); - - // Interpolate the path entered by the user with some - // Bezier's curves in order to have a smoother path and - // to reduce the data size used to draw it in the PDF. - let bezier; - if (this.currentPath.length !== 1) { - bezier = this.#generateBezierPoints(); - } else { - // We have only one point finally. - const xy = [x, y]; - bezier = [[xy, xy.slice(), xy.slice(), xy]]; - } - const path2D = this.#currentPath2D; - const currentPath = this.currentPath; - this.currentPath = []; - this.#currentPath2D = new Path2D(); - - const cmd = () => { - this.allRawPaths.push(currentPath); - this.paths.push(bezier); - this.bezierPath2D.push(path2D); - this._uiManager.rebuild(this); - }; - - const undo = () => { - this.allRawPaths.pop(); - this.paths.pop(); - this.bezierPath2D.pop(); - if (this.paths.length === 0) { - this.remove(); - } else { - if (!this.canvas) { - this.#createCanvas(); - this.#createObserver(); - } - this.#fitToContent(); - } - }; - - this.addCommands({ cmd, undo, mustExec: true }); - } - - #drawPoints() { - if (!this.#hasSomethingToDraw) { - return; - } - this.#hasSomethingToDraw = false; - - const thickness = Math.ceil(this.thickness * this.parentScale); - const lastPoints = this.currentPath.slice(-3); - const x = lastPoints.map(xy => xy[0]); - const y = lastPoints.map(xy => xy[1]); - const xMin = Math.min(...x) - thickness; - const xMax = Math.max(...x) + thickness; - const yMin = Math.min(...y) - thickness; - const yMax = Math.max(...y) + thickness; - - const { ctx } = this; - ctx.save(); - - if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { - // In Chrome, the clip() method doesn't work as expected. - ctx.clearRect(xMin, yMin, xMax - xMin, yMax - yMin); - ctx.beginPath(); - ctx.rect(xMin, yMin, xMax - xMin, yMax - yMin); - ctx.clip(); - } else { - ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - - for (const path of this.bezierPath2D) { - ctx.stroke(path); - } - ctx.stroke(this.#currentPath2D); - - ctx.restore(); - } - - #makeBezierCurve(path2D, x0, y0, x1, y1, x2, y2) { - const prevX = (x0 + x1) / 2; - const prevY = (y0 + y1) / 2; - const x3 = (x1 + x2) / 2; - const y3 = (y1 + y2) / 2; - - path2D.bezierCurveTo( - prevX + (2 * (x1 - prevX)) / 3, - prevY + (2 * (y1 - prevY)) / 3, - x3 + (2 * (x1 - x3)) / 3, - y3 + (2 * (y1 - y3)) / 3, - x3, - y3 + /** @inheritdoc */ + static deserializeDraw( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + data + ) { + return InkDrawOutline.deserialize( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + data ); } - #generateBezierPoints() { - const path = this.currentPath; - if (path.length <= 2) { - return [[path[0], path[0], path.at(-1), path.at(-1)]]; - } - - const bezierPoints = []; - let i; - let [x0, y0] = path[0]; - for (i = 1; i < path.length - 2; i++) { - const [x1, y1] = path[i]; - const [x2, y2] = path[i + 1]; - const x3 = (x1 + x2) / 2; - const y3 = (y1 + y2) / 2; - - // The quadratic is: [[x0, y0], [x1, y1], [x3, y3]]. - // Convert the quadratic to a cubic - // (see https://fontforge.org/docs/techref/bezier.html#converting-truetype-to-postscript) - const control1 = [x0 + (2 * (x1 - x0)) / 3, y0 + (2 * (y1 - y0)) / 3]; - const control2 = [x3 + (2 * (x1 - x3)) / 3, y3 + (2 * (y1 - y3)) / 3]; - - bezierPoints.push([[x0, y0], control1, control2, [x3, y3]]); - - [x0, y0] = [x3, y3]; - } - - const [x1, y1] = path[i]; - const [x2, y2] = path[i + 1]; - - // The quadratic is: [[x0, y0], [x1, y1], [x2, y2]]. - const control1 = [x0 + (2 * (x1 - x0)) / 3, y0 + (2 * (y1 - y0)) / 3]; - const control2 = [x2 + (2 * (x1 - x2)) / 3, y2 + (2 * (y1 - y2)) / 3]; - - bezierPoints.push([[x0, y0], control1, control2, [x2, y2]]); - return bezierPoints; - } - - /** - * Redraw all the paths. - */ - #redraw() { - if (this.isEmpty()) { - this.#updateTransform(); - return; - } - this.#setStroke(); - - const { canvas, ctx } = this; - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, canvas.width, canvas.height); - this.#updateTransform(); - - for (const path of this.bezierPath2D) { - ctx.stroke(path); - } - } - - /** - * Commit the curves we have in this editor. - */ - commit() { - if (this.#disableEditing) { - return; + /** @inheritdoc */ + static async deserialize(data, parent, uiManager) { + let initialData = null; + if (data instanceof InkAnnotationElement) { + const { + data: { + inkLists, + rect, + rotation, + id, + color, + opacity, + borderStyle: { rawWidth: thickness }, + popupRef, + }, + parent: { + page: { pageNumber }, + }, + } = data; + initialData = data = { + annotationType: AnnotationEditorType.INK, + color: Array.from(color), + thickness, + opacity, + paths: { points: inkLists }, + boxes: null, + pageIndex: pageNumber - 1, + rect: rect.slice(0), + rotation, + id, + deleted: false, + popupRef, + }; } - super.commit(); - - this.isEditing = false; - this.disableEditMode(); - - // This editor must be on top of the main ink editor. - this.setInForeground(); - - this.#disableEditing = true; - this.div.classList.add("disabled"); - - this.#fitToContent(/* firstTime = */ true); - this.select(); - - this.parent.addInkEditorIfNeeded(/* isCommitting = */ true); + const editor = await super.deserialize(data, parent, uiManager); + editor.annotationElementId = data.id || null; + editor._initialData = initialData; - // When committing, the position of this editor is changed, hence we must - // move it to the right position in the DOM. - this.moveInDOM(); - this.div.focus({ - preventScroll: true /* See issue #15744 */, - }); + return editor; } /** @inheritdoc */ - focusin(event) { - if (!this._focusEventsAllowed) { - return; - } - super.focusin(event); - this.enableEditMode(); - } - - #addPointerdownListener() { - if (this.#pointerdownAC) { + onScaleChanging() { + if (!this.parent) { return; } - this.#pointerdownAC = new AbortController(); - const signal = this._uiManager.combinedSignal(this.#pointerdownAC); - - this.canvas.addEventListener( - "pointerdown", - this.canvasPointerdown.bind(this), - { signal } + super.onScaleChanging(); + const { _drawId, _drawingOptions, parent } = this; + _drawingOptions.updateSVGProperty("stroke-width"); + parent.drawLayer.updateProperties( + _drawId, + _drawingOptions.toSVGProperties() ); } - #removePointerdownListener() { - this.pointerdownAC?.abort(); - this.pointerdownAC = null; - } - - /** - * onpointerdown callback for the canvas we're drawing on. - * @param {PointerEvent} event - */ - canvasPointerdown(event) { - if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) { + static onScaleChangingWhenDrawing() { + const parent = this._currentParent; + if (!parent) { return; } - - // We want to draw on top of any other editors. - // Since it's the last child, there's no need to give it a higher z-index. - this.setInForeground(); - - event.preventDefault(); - - if (!this.div.contains(document.activeElement)) { - this.div.focus({ - preventScroll: true /* See issue #17327 */, - }); - } - - this.#startDrawing(event.offsetX, event.offsetY); - } - - /** - * onpointermove callback for the canvas we're drawing on. - * @param {PointerEvent} event - */ - canvasPointermove(event) { - event.preventDefault(); - this.#draw(event.offsetX, event.offsetY); - } - - /** - * onpointerup callback for the canvas we're drawing on. - * @param {PointerEvent} event - */ - canvasPointerup(event) { - event.preventDefault(); - this.#endDrawing(event); - } - - /** - * onpointerleave callback for the canvas we're drawing on. - * @param {PointerEvent} event - */ - canvasPointerleave(event) { - this.#endDrawing(event); - } - - /** - * End the drawing. - * @param {PointerEvent} event - */ - #endDrawing(event) { - this.#drawingAC?.abort(); - this.#drawingAC = null; - - this.#addPointerdownListener(); - // Slight delay to avoid the context menu to appear (it can happen on a long - // tap with a pen). - if (this.#canvasContextMenuTimeoutId) { - clearTimeout(this.#canvasContextMenuTimeoutId); - } - this.#canvasContextMenuTimeoutId = setTimeout(() => { - this.#canvasContextMenuTimeoutId = null; - this.canvas.removeEventListener("contextmenu", noContextMenu); - }, 10); - - this.#stopDrawing(event.offsetX, event.offsetY); - - this.addToAnnotationStorage(); - - // Since the ink editor covers all of the page and we want to be able - // to select another editor, we just put this one in the background. - this.setInBackground(); - } - - /** - * Create the canvas element. - */ - #createCanvas() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.canvas.height = 0; - this.canvas.className = "inkEditorCanvas"; - this.canvas.setAttribute("data-l10n-id", "pdfjs-ink-canvas"); - - this.div.append(this.canvas); - this.ctx = this.canvas.getContext("2d"); - } - - /** - * Create the resize observer. - */ - #createObserver() { - this.#observer = new ResizeObserver(entries => { - const rect = entries[0].contentRect; - if (rect.width && rect.height) { - this.setDimensions(rect.width, rect.height); - } - }); - this.#observer.observe(this.div); - this._uiManager._signal.addEventListener( - "abort", - () => { - this.#observer?.disconnect(); - this.#observer = null; - }, - { once: true } + super.onScaleChangingWhenDrawing(); + this._defaultDrawingOptions.updateSVGProperty("stroke-width"); + parent.drawLayer.updateProperties( + this._currentDrawId, + this._defaultDrawingOptions.toSVGProperties() ); } /** @inheritdoc */ - get isResizable() { - return !this.isEmpty() && this.#disableEditing; + createDrawingOptions({ color, thickness, opacity }) { + this._drawingOptions = InkEditor.getDefaultDrawingOptions({ + stroke: Util.makeHexColor(...color), + "stroke-width": thickness, + "stroke-opacity": opacity, + }); } /** @inheritdoc */ - render() { - if (this.div) { - return this.div; - } - - let baseX, baseY; - if (this.width) { - baseX = this.x; - baseY = this.y; - } - - super.render(); - - this.div.setAttribute("data-l10n-id", "pdfjs-ink"); - - const [x, y, w, h] = this.#getInitialBBox(); - this.setAt(x, y, 0, 0); - this.setDims(w, h); - - this.#createCanvas(); - - if (this.width) { - // This editor was created in using copy (ctrl+c). - const [parentWidth, parentHeight] = this.parentDimensions; - this.setAspectRatio(this.width * parentWidth, this.height * parentHeight); - this.setAt( - baseX * parentWidth, - baseY * parentHeight, - this.width * parentWidth, - this.height * parentHeight - ); - this.#isCanvasInitialized = true; - this.#setCanvasDims(); - this.setDims(this.width * parentWidth, this.height * parentHeight); - this.#redraw(); - this.div.classList.add("disabled"); - } else { - this.div.classList.add("editing"); - this.enableEditMode(); - } - - this.#createObserver(); - - return this.div; - } - - #setCanvasDims() { - if (!this.#isCanvasInitialized) { - return; - } - const [parentWidth, parentHeight] = this.parentDimensions; - this.canvas.width = Math.ceil(this.width * parentWidth); - this.canvas.height = Math.ceil(this.height * parentHeight); - this.#updateTransform(); - } - - /** - * When the dimensions of the div change the inner canvas must - * renew its dimensions, hence it must redraw its own contents. - * @param {number} width - the new width of the div - * @param {number} height - the new height of the div - * @returns - */ - setDimensions(width, height) { - const roundedWidth = Math.round(width); - const roundedHeight = Math.round(height); - if ( - this.#realWidth === roundedWidth && - this.#realHeight === roundedHeight - ) { - return; - } - - this.#realWidth = roundedWidth; - this.#realHeight = roundedHeight; - - this.canvas.style.visibility = "hidden"; - - const [parentWidth, parentHeight] = this.parentDimensions; - this.width = width / parentWidth; - this.height = height / parentHeight; - this.fixAndSetPosition(); - - if (this.#disableEditing) { - this.#setScaleFactor(width, height); - } - - this.#setCanvasDims(); - this.#redraw(); - - this.canvas.style.visibility = "visible"; - - // For any reason the dimensions couldn't be in percent but in pixels, hence - // we must fix them. - this.fixDims(); - } - - #setScaleFactor(width, height) { - const padding = this.#getPadding(); - const scaleFactorW = (width - padding) / this.#baseWidth; - const scaleFactorH = (height - padding) / this.#baseHeight; - this.scaleFactor = Math.min(scaleFactorW, scaleFactorH); - } - - /** - * Update the canvas transform. - */ - #updateTransform() { - const padding = this.#getPadding() / 2; - this.ctx.setTransform( - this.scaleFactor, - 0, - 0, - this.scaleFactor, - this.translationX * this.scaleFactor + padding, - this.translationY * this.scaleFactor + padding - ); - } - - /** - * Convert into a Path2D. - * @param {Array>} bezier - * @returns {Path2D} - */ - static #buildPath2D(bezier) { - const path2D = new Path2D(); - for (let i = 0, ii = bezier.length; i < ii; i++) { - const [first, control1, control2, second] = bezier[i]; - if (i === 0) { - path2D.moveTo(...first); - } - path2D.bezierCurveTo( - control1[0], - control1[1], - control2[0], - control2[1], - second[0], - second[1] - ); - } - return path2D; - } - - static #toPDFCoordinates(points, rect, rotation) { - const [blX, blY, trX, trY] = rect; - - switch (rotation) { - case 0: - for (let i = 0, ii = points.length; i < ii; i += 2) { - points[i] += blX; - points[i + 1] = trY - points[i + 1]; - } - break; - case 90: - for (let i = 0, ii = points.length; i < ii; i += 2) { - const x = points[i]; - points[i] = points[i + 1] + blX; - points[i + 1] = x + blY; - } - break; - case 180: - for (let i = 0, ii = points.length; i < ii; i += 2) { - points[i] = trX - points[i]; - points[i + 1] += blY; - } - break; - case 270: - for (let i = 0, ii = points.length; i < ii; i += 2) { - const x = points[i]; - points[i] = trX - points[i + 1]; - points[i + 1] = trY - x; - } - break; - default: - throw new Error("Invalid rotation"); + serialize(isForCopying = false) { + if (this.isEmpty()) { + return null; } - return points; - } - - static #fromPDFCoordinates(points, rect, rotation) { - const [blX, blY, trX, trY] = rect; - switch (rotation) { - case 0: - for (let i = 0, ii = points.length; i < ii; i += 2) { - points[i] -= blX; - points[i + 1] = trY - points[i + 1]; - } - break; - case 90: - for (let i = 0, ii = points.length; i < ii; i += 2) { - const x = points[i]; - points[i] = points[i + 1] - blY; - points[i + 1] = x - blX; - } - break; - case 180: - for (let i = 0, ii = points.length; i < ii; i += 2) { - points[i] = trX - points[i]; - points[i + 1] -= blY; - } - break; - case 270: - for (let i = 0, ii = points.length; i < ii; i += 2) { - const x = points[i]; - points[i] = trY - points[i + 1]; - points[i + 1] = trX - x; - } - break; - default: - throw new Error("Invalid rotation"); + if (this.deleted) { + return this.serializeDeleted(); } - return points; - } - /** - * Transform and serialize the paths. - * @param {number} s - scale factor - * @param {number} tx - abscissa of the translation - * @param {number} ty - ordinate of the translation - * @param {Array} rect - the bounding box of the annotation - */ - #serializePaths(s, tx, ty, rect) { - const paths = []; - const padding = this.thickness / 2; - const shiftX = s * tx + padding; - const shiftY = s * ty + padding; - for (const bezier of this.paths) { - const buffer = []; - const points = []; - for (let j = 0, jj = bezier.length; j < jj; j++) { - const [first, control1, control2, second] = bezier[j]; - if (first[0] === second[0] && first[1] === second[1] && jj === 1) { - // We have only one point. - const p0 = s * first[0] + shiftX; - const p1 = s * first[1] + shiftY; - buffer.push(p0, p1); - points.push(p0, p1); - break; - } - const p10 = s * first[0] + shiftX; - const p11 = s * first[1] + shiftY; - const p20 = s * control1[0] + shiftX; - const p21 = s * control1[1] + shiftY; - const p30 = s * control2[0] + shiftX; - const p31 = s * control2[1] + shiftY; - const p40 = s * second[0] + shiftX; - const p41 = s * second[1] + shiftY; + const { lines, points, rect } = this.serializeDraw(isForCopying); + const { + _drawingOptions: { + stroke, + "stroke-opacity": opacity, + "stroke-width": thickness, + }, + } = this; + const serialized = { + annotationType: AnnotationEditorType.INK, + color: AnnotationEditor._colorManager.convert(stroke), + opacity, + thickness, + paths: { + lines, + points, + }, + pageIndex: this.pageIndex, + rect, + rotation: this.rotation, + structTreeParentId: this._structTreeParentId, + }; - if (j === 0) { - buffer.push(p10, p11); - points.push(p10, p11); - } - buffer.push(p20, p21, p30, p31, p40, p41); - points.push(p20, p21); - if (j === jj - 1) { - points.push(p40, p41); - } - } - paths.push({ - bezier: InkEditor.#toPDFCoordinates(buffer, rect, this.rotation), - points: InkEditor.#toPDFCoordinates(points, rect, this.rotation), - }); + if (isForCopying) { + serialized.isCopy = true; + return serialized; } - return paths; - } - - /** - * Get the bounding box containing all the paths. - * @returns {Array} - */ - #getBbox() { - let xMin = Infinity; - let xMax = -Infinity; - let yMin = Infinity; - let yMax = -Infinity; - - for (const path of this.paths) { - for (const [first, control1, control2, second] of path) { - const bbox = Util.bezierBoundingBox( - ...first, - ...control1, - ...control2, - ...second - ); - xMin = Math.min(xMin, bbox[0]); - yMin = Math.min(yMin, bbox[1]); - xMax = Math.max(xMax, bbox[2]); - yMax = Math.max(yMax, bbox[3]); - } + if (this.annotationElementId && !this.#hasElementChanged(serialized)) { + return null; } - return [xMin, yMin, xMax, yMax]; - } - - /** - * The bounding box is computed with null thickness, so we must take - * it into account for the display. - * It corresponds to the total padding, hence it should be divided by 2 - * in order to have left/right paddings. - * @returns {number} - */ - #getPadding() { - return this.#disableEditing - ? Math.ceil(this.thickness * this.parentScale) - : 0; + serialized.id = this.annotationElementId; + return serialized; } - /** - * Set the div position and dimensions in order to fit to - * the bounding box of the contents. - * @returns {undefined} - */ - #fitToContent(firstTime = false) { - if (this.isEmpty()) { - return; - } - - if (!this.#disableEditing) { - this.#redraw(); - return; - } - - const bbox = this.#getBbox(); - const padding = this.#getPadding(); - this.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]); - this.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]); - - const width = Math.ceil(padding + this.#baseWidth * this.scaleFactor); - const height = Math.ceil(padding + this.#baseHeight * this.scaleFactor); - - const [parentWidth, parentHeight] = this.parentDimensions; - this.width = width / parentWidth; - this.height = height / parentHeight; - - this.setAspectRatio(width, height); - - const prevTranslationX = this.translationX; - const prevTranslationY = this.translationY; - - this.translationX = -bbox[0]; - this.translationY = -bbox[1]; - this.#setCanvasDims(); - this.#redraw(); - - this.#realWidth = width; - this.#realHeight = height; - - this.setDims(width, height); - const unscaledPadding = firstTime ? padding / this.scaleFactor / 2 : 0; - this.translate( - prevTranslationX - this.translationX - unscaledPadding, - prevTranslationY - this.translationY - unscaledPadding + #hasElementChanged(serialized) { + const { color, thickness, opacity, pageIndex } = this._initialData; + return ( + this._hasBeenMoved || + this._hasBeenResized || + serialized.color.some((c, i) => c !== color[i]) || + serialized.thickness !== thickness || + serialized.opacity !== opacity || + serialized.pageIndex !== pageIndex ); } /** @inheritdoc */ - static async deserialize(data, parent, uiManager) { - if (data instanceof InkAnnotationElement) { - return null; - } - const editor = await super.deserialize(data, parent, uiManager); - - editor.thickness = data.thickness; - editor.color = Util.makeHexColor(...data.color); - editor.opacity = data.opacity; - - const [pageWidth, pageHeight] = editor.pageDimensions; - const width = editor.width * pageWidth; - const height = editor.height * pageHeight; - const scaleFactor = editor.parentScale; - const padding = data.thickness / 2; - - editor.#disableEditing = true; - editor.#realWidth = Math.round(width); - editor.#realHeight = Math.round(height); - - const { paths, rect, rotation } = data; - - for (let { bezier } of paths) { - bezier = InkEditor.#fromPDFCoordinates(bezier, rect, rotation); - const path = []; - editor.paths.push(path); - let p0 = scaleFactor * (bezier[0] - padding); - let p1 = scaleFactor * (bezier[1] - padding); - for (let i = 2, ii = bezier.length; i < ii; i += 6) { - const p10 = scaleFactor * (bezier[i] - padding); - const p11 = scaleFactor * (bezier[i + 1] - padding); - const p20 = scaleFactor * (bezier[i + 2] - padding); - const p21 = scaleFactor * (bezier[i + 3] - padding); - const p30 = scaleFactor * (bezier[i + 4] - padding); - const p31 = scaleFactor * (bezier[i + 5] - padding); - path.push([ - [p0, p1], - [p10, p11], - [p20, p21], - [p30, p31], - ]); - p0 = p30; - p1 = p31; - } - const path2D = this.#buildPath2D(path); - editor.bezierPath2D.push(path2D); - } - - const bbox = editor.#getBbox(); - editor.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]); - editor.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]); - editor.#setScaleFactor(width, height); - - return editor; - } - - /** @inheritdoc */ - serialize() { - if (this.isEmpty()) { - return null; - } - - const rect = this.getRect(0, 0); - const color = AnnotationEditor._colorManager.convert(this.ctx.strokeStyle); - - return { - annotationType: AnnotationEditorType.INK, - color, - thickness: this.thickness, - opacity: this.opacity, - paths: this.#serializePaths( - this.scaleFactor / this.parentScale, - this.translationX, - this.translationY, - rect - ), - pageIndex: this.pageIndex, + renderAnnotationElement(annotation) { + const { points, rect } = this.serializeDraw(/* isForCopying = */ false); + annotation.updateEdited({ rect, - rotation: this.rotation, - structTreeParentId: this._structTreeParentId, - }; + thickness: this._drawingOptions["stroke-width"], + points, + }); + + return null; } } -export { InkEditor }; +export { InkDrawingOptions, InkEditor }; diff --git a/src/display/editor/signature.js b/src/display/editor/signature.js new file mode 100644 index 0000000000000..6671f51bdae1e --- /dev/null +++ b/src/display/editor/signature.js @@ -0,0 +1,439 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnnotationEditorType, shadow } from "../../shared/util.js"; +import { DrawingEditor, DrawingOptions } from "./draw.js"; +import { AnnotationEditor } from "./editor.js"; +import { ContourDrawOutline } from "./drawers/contour.js"; +import { InkDrawingOptions } from "./ink.js"; +import { InkDrawOutline } from "./drawers/inkdraw.js"; +import { SignatureExtractor } from "./drawers/signaturedraw.js"; + +class SignatureOptions extends DrawingOptions { + constructor() { + super(); + + super.updateProperties({ + fill: "CanvasText", + "stroke-width": 0, + }); + } + + clone() { + const clone = new SignatureOptions(); + clone.updateAll(this); + return clone; + } +} + +class DrawnSignatureOptions extends InkDrawingOptions { + constructor(viewerParameters) { + super(viewerParameters); + + super.updateProperties({ + stroke: "CanvasText", + "stroke-width": 1, + }); + } + + clone() { + const clone = new DrawnSignatureOptions(this._viewParameters); + clone.updateAll(this); + return clone; + } +} + +/** + * Basic editor in order to generate an Stamp annotation annotation containing + * a signature drawing. + */ +class SignatureEditor extends DrawingEditor { + #isExtracted = false; + + #description = null; + + #signatureData = null; + + #signatureUUID = null; + + static _type = "signature"; + + static _editorType = AnnotationEditorType.SIGNATURE; + + static _defaultDrawingOptions = null; + + constructor(params) { + super({ ...params, mustBeCommitted: true, name: "signatureEditor" }); + this._willKeepAspectRatio = true; + this.#signatureData = params.signatureData || null; + this.#description = null; + } + + /** @inheritdoc */ + static initialize(l10n, uiManager) { + AnnotationEditor.initialize(l10n, uiManager); + + this._defaultDrawingOptions = new SignatureOptions(); + this._defaultDrawnSignatureOptions = new DrawnSignatureOptions( + uiManager.viewParameters + ); + } + + /** @inheritdoc */ + static getDefaultDrawingOptions(options) { + const clone = this._defaultDrawingOptions.clone(); + clone.updateProperties(options); + return clone; + } + + /** @inheritdoc */ + static get supportMultipleDrawings() { + return false; + } + + static get typesMap() { + return shadow(this, "typesMap", new Map()); + } + + static get isDrawer() { + return false; + } + + /** @inheritdoc */ + get telemetryFinalData() { + return { + type: "signature", + hasDescription: !!this.#description, + }; + } + + static computeTelemetryFinalData(data) { + const hasDescriptionStats = data.get("hasDescription"); + return { + hasAltText: hasDescriptionStats.get(true) ?? 0, + hasNoAltText: hasDescriptionStats.get(false) ?? 0, + }; + } + + /** @inheritdoc */ + get isResizable() { + return true; + } + + /** @inheritdoc */ + onScaleChanging() { + if (this._drawId === null) { + return; + } + super.onScaleChanging(); + } + + /** @inheritdoc */ + render() { + if (this.div) { + return this.div; + } + + let baseX, baseY; + const { _isCopy } = this; + if (_isCopy) { + // No need to adjust the position when rendering in DrawingEditor. + this._isCopy = false; + baseX = this.x; + baseY = this.y; + } + + super.render(); + this.div.setAttribute("role", "figure"); + + if (this._drawId === null) { + if (this.#signatureData) { + const { + lines, + mustSmooth, + areContours, + description, + uuid, + heightInPage, + } = this.#signatureData; + const { + rawDims: { pageWidth, pageHeight }, + rotation, + } = this.parent.viewport; + const outline = SignatureExtractor.processDrawnLines({ + lines, + pageWidth, + pageHeight, + rotation, + innerMargin: SignatureEditor._INNER_MARGIN, + mustSmooth, + areContours, + }); + this.addSignature(outline, heightInPage, description, uuid); + } else { + this.div.hidden = true; + this._uiManager.getSignature(this); + } + } + + if (_isCopy) { + this._isCopy = true; + this._moveAfterPaste(baseX, baseY); + } + + return this.div; + } + + setUuid(uuid) { + this.#signatureUUID = uuid; + this.addEditToolbar(); + } + + getUuid() { + return this.#signatureUUID; + } + + get description() { + return this.#description; + } + + set description(description) { + this.#description = description; + super.addEditToolbar().then(toolbar => { + toolbar?.updateEditSignatureButton(description); + }); + } + + getSignaturePreview() { + const { newCurves, areContours, thickness, width, height } = + this.#signatureData; + const maxDim = Math.max(width, height); + const outlineData = SignatureExtractor.processDrawnLines({ + lines: { + curves: newCurves.map(points => ({ points })), + thickness, + width, + height, + }, + pageWidth: maxDim, + pageHeight: maxDim, + rotation: 0, + innerMargin: 0, + mustSmooth: false, + areContours, + }); + return { areContours, outline: outlineData.outline }; + } + + /** @inheritdoc */ + async addEditToolbar() { + const toolbar = await super.addEditToolbar(); + if (!toolbar) { + return null; + } + if (this._uiManager.signatureManager && this.#description !== null) { + await toolbar.addEditSignatureButton( + this._uiManager.signatureManager, + this.#signatureUUID, + this.#description + ); + toolbar.show(); + } + return toolbar; + } + + addSignature(data, heightInPage, description, uuid) { + const { x: savedX, y: savedY } = this; + const { outline } = (this.#signatureData = data); + this.#isExtracted = outline instanceof ContourDrawOutline; + this.#description = description; + let drawingOptions; + if (this.#isExtracted) { + drawingOptions = SignatureEditor.getDefaultDrawingOptions(); + } else { + drawingOptions = SignatureEditor._defaultDrawnSignatureOptions.clone(); + drawingOptions.updateProperties({ "stroke-width": outline.thickness }); + } + this._addOutlines({ + drawOutlines: outline, + drawingOptions, + }); + const [parentWidth, parentHeight] = this.parentDimensions; + const [, pageHeight] = this.pageDimensions; + let newHeight = heightInPage / pageHeight; + // Ensure the signature doesn't exceed the page height. + // If the signature is too big, we scale it down to 50% of the page height. + newHeight = newHeight >= 1 ? 0.5 : newHeight; + + this.width *= newHeight / this.height; + if (this.width >= 1) { + newHeight *= 0.9 / this.width; + this.width = 0.9; + } + + this.height = newHeight; + this.setDims(parentWidth * this.width, parentHeight * this.height); + this.x = savedX; + this.y = savedY; + this.center(); + + this._onResized(); + this.onScaleChanging(); + this.rotate(); + this._uiManager.addToAnnotationStorage(this); + this.setUuid(uuid); + + this._reportTelemetry({ + action: "pdfjs.signature.inserted", + data: { + hasBeenSaved: !!uuid, + hasDescription: !!description, + }, + }); + + this.div.hidden = false; + } + + getFromImage(bitmap) { + const { + rawDims: { pageWidth, pageHeight }, + rotation, + } = this.parent.viewport; + return SignatureExtractor.process( + bitmap, + pageWidth, + pageHeight, + rotation, + SignatureEditor._INNER_MARGIN + ); + } + + getFromText(text, fontInfo) { + const { + rawDims: { pageWidth, pageHeight }, + rotation, + } = this.parent.viewport; + return SignatureExtractor.extractContoursFromText( + text, + fontInfo, + pageWidth, + pageHeight, + rotation, + SignatureEditor._INNER_MARGIN + ); + } + + getDrawnSignature(curves) { + const { + rawDims: { pageWidth, pageHeight }, + rotation, + } = this.parent.viewport; + return SignatureExtractor.processDrawnLines({ + lines: curves, + pageWidth, + pageHeight, + rotation, + innerMargin: SignatureEditor._INNER_MARGIN, + mustSmooth: false, + areContours: false, + }); + } + + /** @inheritdoc */ + createDrawingOptions({ areContours, thickness }) { + if (areContours) { + this._drawingOptions = SignatureEditor.getDefaultDrawingOptions(); + } else { + this._drawingOptions = + SignatureEditor._defaultDrawnSignatureOptions.clone(); + this._drawingOptions.updateProperties({ "stroke-width": thickness }); + } + } + + /** @inheritdoc */ + serialize(isForCopying = false) { + if (this.isEmpty()) { + return null; + } + + const { lines, points, rect } = this.serializeDraw(isForCopying); + const { + _drawingOptions: { "stroke-width": thickness }, + } = this; + const serialized = { + annotationType: AnnotationEditorType.SIGNATURE, + isSignature: true, + areContours: this.#isExtracted, + color: [0, 0, 0], + thickness: this.#isExtracted ? 0 : thickness, + pageIndex: this.pageIndex, + rect, + rotation: this.rotation, + structTreeParentId: this._structTreeParentId, + }; + if (isForCopying) { + serialized.paths = { lines, points }; + serialized.uuid = this.#signatureUUID; + serialized.isCopy = true; + } else { + serialized.lines = lines; + } + if (this.#description) { + serialized.accessibilityData = { type: "Figure", alt: this.#description }; + } + return serialized; + } + + /** @inheritdoc */ + static deserializeDraw( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + data + ) { + if (data.areContours) { + return ContourDrawOutline.deserialize( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + data + ); + } + + return InkDrawOutline.deserialize( + pageX, + pageY, + pageWidth, + pageHeight, + innerMargin, + data + ); + } + + /** @inheritdoc */ + static async deserialize(data, parent, uiManager) { + const editor = await super.deserialize(data, parent, uiManager); + editor.#isExtracted = data.areContours; + editor.#description = data.accessibilityData?.alt || ""; + editor.#signatureUUID = data.uuid; + return editor; + } +} + +export { SignatureEditor }; diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index a2d0ff3dc6f71..28b4270754b7e 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -13,12 +13,12 @@ * limitations under the License. */ +import { AnnotationEditorType, AnnotationPrefix } from "../../shared/util.js"; import { - AnnotationEditorType, - AnnotationPrefix, - shadow, -} from "../../shared/util.js"; -import { OutputScale, PixelsPerInch } from "../display_utils.js"; + OutputScale, + PixelsPerInch, + SupportedImageMimeTypes, +} from "../display_utils.js"; import { AnnotationEditor } from "./editor.js"; import { StampAnnotationElement } from "../annotation_layer.js"; @@ -40,7 +40,7 @@ class StampEditor extends AnnotationEditor { #canvas = null; - #observer = null; + #missingCanvas = false; #resizeTimeoutId = null; @@ -63,34 +63,9 @@ class StampEditor extends AnnotationEditor { AnnotationEditor.initialize(l10n, uiManager); } - static get supportedTypes() { - // See https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types - // to know which types are supported by the browser. - const types = [ - "apng", - "avif", - "bmp", - "gif", - "jpeg", - "png", - "svg+xml", - "webp", - "x-icon", - ]; - return shadow( - this, - "supportedTypes", - types.map(type => `image/${type}`) - ); - } - - static get supportedTypesStr() { - return shadow(this, "supportedTypesStr", this.supportedTypes.join(",")); - } - /** @inheritdoc */ static isHandlingMimeForPasting(mime) { - return this.supportedTypes.includes(mime); + return SupportedImageMimeTypes.includes(mime); } /** @inheritdoc */ @@ -258,7 +233,7 @@ class StampEditor extends AnnotationEditor { document.body.append(input); } input.type = "file"; - input.accept = StampEditor.supportedTypesStr; + input.accept = SupportedImageMimeTypes.join(","); const signal = this._uiManager._signal; this.#bitmapPromise = new Promise(resolve => { input.addEventListener( @@ -305,8 +280,6 @@ class StampEditor extends AnnotationEditor { this._uiManager.imageManager.deleteId(this.#bitmapId); this.#canvas?.remove(); this.#canvas = null; - this.#observer?.disconnect(); - this.#observer = null; if (this.#resizeTimeoutId) { clearTimeout(this.#resizeTimeoutId); this.#resizeTimeoutId = null; @@ -342,9 +315,11 @@ class StampEditor extends AnnotationEditor { } /** @inheritdoc */ - onceAdded() { + onceAdded(focus) { this._isDraggable = true; - this.div.focus(); + if (focus) { + this.div.focus(); + } } /** @inheritdoc */ @@ -354,7 +329,8 @@ class StampEditor extends AnnotationEditor { this.#bitmap || this.#bitmapUrl || this.#bitmapFile || - this.#bitmapId + this.#bitmapId || + this.#missingCanvas ); } @@ -370,7 +346,7 @@ class StampEditor extends AnnotationEditor { } let baseX, baseY; - if (this.width) { + if (this._isCopy) { baseX = this.x; baseY = this.y; } @@ -381,26 +357,62 @@ class StampEditor extends AnnotationEditor { this.addAltTextButton(); - if (this.#bitmap) { - this.#createCanvas(); - } else { - this.#getBitmap(); + if (!this.#missingCanvas) { + if (this.#bitmap) { + this.#createCanvas(); + } else { + this.#getBitmap(); + } } - if (this.width && !this.annotationElementId) { - // This editor was created in using copy (ctrl+c). - const [parentWidth, parentHeight] = this.parentDimensions; - this.setAt( - baseX * parentWidth, - baseY * parentHeight, - this.width * parentWidth, - this.height * parentHeight - ); + if (this._isCopy) { + this._moveAfterPaste(baseX, baseY); } + this._uiManager.addShouldRescale(this); + return this.div; } + setCanvas(annotationElementId, canvas) { + const { id: bitmapId, bitmap } = this._uiManager.imageManager.getFromCanvas( + annotationElementId, + canvas + ); + canvas.remove(); + if (bitmapId && this._uiManager.imageManager.isValidId(bitmapId)) { + this.#bitmapId = bitmapId; + if (bitmap) { + this.#bitmap = bitmap; + } + this.#missingCanvas = false; + this.#createCanvas(); + } + } + + /** @inheritdoc */ + _onResized() { + // We used a CSS-zoom during the resizing, but now it's resized we can + // rescale correctly the bitmap to fit the new dimensions. + this.onScaleChanging(); + } + + onScaleChanging() { + if (!this.parent) { + return; + } + if (this.#resizeTimeoutId !== null) { + clearTimeout(this.#resizeTimeoutId); + } + // The user's zooming the page, there is no need to redraw the bitmap at + // each step, hence we wait a bit before redrawing it. + const TIME_TO_WAIT = 200; + this.#resizeTimeoutId = setTimeout(() => { + this.#resizeTimeoutId = null; + this.#drawBitmap(); + }, TIME_TO_WAIT); + } + #createCanvas() { const { div } = this; let { width, height } = this.#bitmap; @@ -433,6 +445,15 @@ class StampEditor extends AnnotationEditor { canvas.setAttribute("role", "img"); this.addContainer(canvas); + this.width = width / pageWidth; + this.height = height / pageHeight; + if (this._initialOptions?.isCentered) { + this.center(); + } else { + this.fixAndSetPosition(); + } + this._initialOptions = null; + if ( !this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || @@ -440,8 +461,7 @@ class StampEditor extends AnnotationEditor { ) { div.hidden = false; } - this.#drawBitmap(width, height); - this.#createObserver(); + this.#drawBitmap(); if (!this.#hasBeenAddedInUndoStack) { this.parent.addUndoableEditor(this); this.#hasBeenAddedInUndoStack = true; @@ -584,37 +604,6 @@ class StampEditor extends AnnotationEditor { return { canvas, width, height, imageData }; } - /** - * When the dimensions of the div change the inner canvas must - * renew its dimensions, hence it must redraw its own contents. - * @param {number} width - the new width of the div - * @param {number} height - the new height of the div - * @returns - */ - #setDimensions(width, height) { - const [parentWidth, parentHeight] = this.parentDimensions; - this.width = width / parentWidth; - this.height = height / parentHeight; - if (this._initialOptions?.isCentered) { - this.center(); - } else { - this.fixAndSetPosition(); - } - this._initialOptions = null; - if (this.#resizeTimeoutId !== null) { - clearTimeout(this.#resizeTimeoutId); - } - // When the user is resizing the editor we just use CSS to scale the image - // to avoid redrawing it too often. - // And once the user stops resizing the editor we redraw the image in - // rescaling it correctly (see this.#scaleBitmap). - const TIME_TO_WAIT = 200; - this.#resizeTimeoutId = setTimeout(() => { - this.#resizeTimeoutId = null; - this.#drawBitmap(width, height); - }, TIME_TO_WAIT); - } - #scaleBitmap(width, height) { const { width: bitmapWidth, height: bitmapHeight } = this.#bitmap; @@ -660,18 +649,21 @@ class StampEditor extends AnnotationEditor { return bitmap; } - #drawBitmap(width, height) { + #drawBitmap() { + const [parentWidth, parentHeight] = this.parentDimensions; + const { width, height } = this; const outputScale = new OutputScale(); - const scaledWidth = Math.ceil(width * outputScale.sx); - const scaledHeight = Math.ceil(height * outputScale.sy); - + const scaledWidth = Math.ceil(width * parentWidth * outputScale.sx); + const scaledHeight = Math.ceil(height * parentHeight * outputScale.sy); const canvas = this.#canvas; + if ( !canvas || (canvas.width === scaledWidth && canvas.height === scaledHeight) ) { return; } + canvas.width = scaledWidth; canvas.height = scaledHeight; @@ -746,35 +738,10 @@ class StampEditor extends AnnotationEditor { return structuredClone(this.#bitmap); } - /** - * Create the resize observer. - */ - #createObserver() { - if (!this._uiManager._signal) { - // This method is called after the canvas has been created but the canvas - // creation is async, so it's possible that the viewer has been closed. - return; - } - this.#observer = new ResizeObserver(entries => { - const rect = entries[0].contentRect; - if (rect.width && rect.height) { - this.#setDimensions(rect.width, rect.height); - } - }); - this.#observer.observe(this.div); - this._uiManager._signal.addEventListener( - "abort", - () => { - this.#observer?.disconnect(); - this.#observer = null; - }, - { once: true } - ); - } - /** @inheritdoc */ static async deserialize(data, parent, uiManager) { let initialData = null; + let missingCanvas = false; if (data instanceof StampAnnotationElement) { const { data: { rect, rotation, id, structParent, popupRef }, @@ -782,13 +749,20 @@ class StampEditor extends AnnotationEditor { parent: { page: { pageNumber }, }, + canvas, } = data; - const canvas = container.querySelector("canvas"); - const imageData = uiManager.imageManager.getFromCanvas( - container.id, - canvas - ); - canvas.remove(); + let bitmapId, bitmap; + if (canvas) { + delete data.canvas; + ({ id: bitmapId, bitmap } = uiManager.imageManager.getFromCanvas( + container.id, + canvas + )); + canvas.remove(); + } else { + missingCanvas = true; + data._hasNoCanvas = true; + } // When switching to edit mode, we wait for the structure tree to be // ready (see pdf_viewer.js), so it's fine to use getAriaAttributesSync. @@ -799,8 +773,8 @@ class StampEditor extends AnnotationEditor { initialData = data = { annotationType: AnnotationEditorType.STAMP, - bitmapId: imageData.id, - bitmap: imageData.bitmap, + bitmapId, + bitmap, pageIndex: pageNumber - 1, rect: rect.slice(0), rotation, @@ -818,7 +792,10 @@ class StampEditor extends AnnotationEditor { const editor = await super.deserialize(data, parent, uiManager); const { rect, bitmap, bitmapUrl, bitmapId, isSvg, accessibilityData } = data; - if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { + if (missingCanvas) { + uiManager.addMissingCanvas(data.id, editor); + editor.#missingCanvas = true; + } else if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { editor.#bitmapId = bitmapId; if (bitmap) { editor.#bitmap = bitmap; @@ -870,6 +847,7 @@ class StampEditor extends AnnotationEditor { // hence we serialize the bitmap to a data url. serialized.bitmapUrl = this.#serializeBitmap(/* toUrl = */ true); serialized.accessibilityData = this.serializeAltText(true); + serialized.isCopy = true; return serialized; } @@ -921,19 +899,19 @@ class StampEditor extends AnnotationEditor { #hasElementChanged(serialized) { const { - rect, pageIndex, accessibilityData: { altText }, } = this._initialData; - const isSameRect = serialized.rect.every( - (x, i) => Math.abs(x - rect[i]) < 1 - ); const isSamePageIndex = serialized.pageIndex === pageIndex; const isSameAltText = (serialized.accessibilityData?.alt || "") === altText; return { - isSame: isSameRect && isSamePageIndex && isSameAltText, + isSame: + !this._hasBeenMoved && + !this._hasBeenResized && + isSamePageIndex && + isSameAltText, isSameAltText, }; } diff --git a/src/display/editor/toolbar.js b/src/display/editor/toolbar.js index 1d67778197096..715a4b82016af 100644 --- a/src/display/editor/toolbar.js +++ b/src/display/editor/toolbar.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { noContextMenu } from "../display_utils.js"; +import { noContextMenu, stopEvent } from "../display_utils.js"; class EditorToolbar { #toolbar = null; @@ -26,6 +26,8 @@ class EditorToolbar { #altText = null; + #signatureDescriptionButton = null; + static #l10nRemove = null; constructor(editor) { @@ -36,6 +38,7 @@ class EditorToolbar { highlight: "pdfjs-editor-remove-highlight-button", ink: "pdfjs-editor-remove-ink-button", stamp: "pdfjs-editor-remove-stamp-button", + signature: "pdfjs-editor-remove-signature-button", }); } @@ -81,14 +84,12 @@ class EditorToolbar { #focusIn(e) { this.#editor._focusEventsAllowed = false; - e.preventDefault(); - e.stopPropagation(); + stopEvent(e); } #focusOut(e) { this.#editor._focusEventsAllowed = true; - e.preventDefault(); - e.stopPropagation(); + stopEvent(e); } #addListenersToElement(element) { @@ -155,6 +156,19 @@ class EditorToolbar { this.#buttons.prepend(button, this.#divider); } + async addEditSignatureButton(signatureManager) { + const button = (this.#signatureDescriptionButton = + await signatureManager.renderEditButton(this.#editor)); + this.#addListenersToElement(button); + this.#buttons.prepend(button, this.#divider); + } + + updateEditSignatureButton(description) { + if (this.#signatureDescriptionButton) { + this.#signatureDescriptionButton.title = description; + } + } + remove() { this.#toolbar.remove(); this.#colorPicker?.destroy(); diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index 9d4fd87476720..01ff2050aecff 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -32,6 +32,7 @@ import { getColorValues, getRGB, PixelsPerInch, + stopEvent, } from "../display_utils.js"; import { HighlightToolbar } from "./toolbar.js"; @@ -166,7 +167,7 @@ class ImageManager { } data.refCounter = 1; } catch (e) { - console.error(e); + warn(e); data = null; } this.#cache.set(key, data); @@ -411,6 +412,21 @@ class CommandManager { return this.#position < this.#commands.length - 1; } + cleanType(type) { + if (this.#position === -1) { + return; + } + for (let i = this.#position; i >= 0; i--) { + if (this.#commands[i].type !== type) { + this.#commands.splice(i + 1, this.#position - i); + this.#position = i; + return; + } + } + this.#commands.length = 0; + this.#position = -1; + } + destroy() { this.#commands = null; } @@ -501,8 +517,7 @@ class KeyboardManager { // For example, ctrl+s in a FreeText must be handled by the viewer, hence // the event must bubble. if (!bubbles) { - event.stopPropagation(); - event.preventDefault(); + stopEvent(event); } } } @@ -595,6 +610,8 @@ class AnnotationEditorUIManager { #copyPasteAC = null; + #currentDrawingSession = null; + #currentPageIndex = 0; #deletedAnnotationsElementIds = new Set(); @@ -605,6 +622,8 @@ class AnnotationEditorUIManager { #editorsToRescale = new Set(); + _editorUndoBar = null; + #enableHighlightFloatingButton = false; #enableUpdatedAddImage = false; @@ -635,6 +654,8 @@ class AnnotationEditorUIManager { #mainHighlightColorPicker = null; + #missingCanvases = null; + #mlManager = null; #mode = AnnotationEditorType.NONE; @@ -643,6 +664,8 @@ class AnnotationEditorUIManager { #selectedTextNode = null; + #signatureManager = null; + #pageColors = null; #showAllStates = null; @@ -807,6 +830,7 @@ class AnnotationEditorUIManager { container, viewer, altTextManager, + signatureManager, eventBus, pdfDocument, pageColors, @@ -814,12 +838,15 @@ class AnnotationEditorUIManager { enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, - mlManager + mlManager, + editorUndoBar, + supportsPinchToZoom ) { const signal = (this._signal = this.#abortController.signal); this.#container = container; this.#viewer = viewer; this.#altTextManager = altTextManager; + this.#signatureManager = signatureManager; this._eventBus = eventBus; eventBus._on("editingaction", this.onEditingAction.bind(this), { signal }); eventBus._on("pagechanging", this.onPageChanging.bind(this), { signal }); @@ -849,6 +876,8 @@ class AnnotationEditorUIManager { rotation: 0, }; this.isShiftKeyDown = false; + this._editorUndoBar = editorUndoBar || null; + this._supportsPinchToZoom = supportsPinchToZoom !== false; if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { Object.defineProperty(this, "reset", { @@ -875,12 +904,16 @@ class AnnotationEditorUIManager { this.#allLayers.clear(); this.#allEditors.clear(); this.#editorsToRescale.clear(); + this.#missingCanvases?.clear(); this.#activeEditor = null; this.#selectedEditors.clear(); this.#commandManager.destroy(); this.#altTextManager?.destroy(); + this.#signatureManager?.destroy(); this.#highlightToolbar?.hide(); this.#highlightToolbar = null; + this.#mainHighlightColorPicker?.destroy(); + this.#mainHighlightColorPicker = null; if (this.#focusMainContainerTimeoutId) { clearTimeout(this.#focusMainContainerTimeoutId); this.#focusMainContainerTimeoutId = null; @@ -889,6 +922,7 @@ class AnnotationEditorUIManager { clearTimeout(this.#translationTimeoutId); this.#translationTimeoutId = null; } + this._editorUndoBar?.destroy(); } combinedSignal(ac) { @@ -952,6 +986,20 @@ class AnnotationEditorUIManager { ); } + /** + * Set the current drawing session. + * @param {AnnotationEditorLayer} layer + */ + setCurrentDrawingSession(layer) { + if (layer) { + this.unselectAll(); + this.disableUserSelect(true); + } else { + this.disableUserSelect(false); + } + this.#currentDrawingSession = layer; + } + setMainHighlightColorPicker(colorPicker) { this.#mainHighlightColorPicker = colorPicker; } @@ -960,6 +1008,14 @@ class AnnotationEditorUIManager { this.#altTextManager?.editAltText(this, editor, firstTime); } + getSignature(editor) { + this.#signatureManager?.getSignature({ uiManager: this, editor }); + } + + get signatureManager() { + return this.#signatureManager; + } + switchToMode(mode, callback) { // Switching to a mode can be asynchronous. this._eventBus.on("annotationeditormodechanged", callback, { @@ -1034,6 +1090,7 @@ class AnnotationEditorUIManager { for (const editor of this.#editorsToRescale) { editor.onScaleChanging(); } + this.#currentDrawingSession?.onScaleChanging(); } onRotationChanging({ pagesRotation }) { @@ -1640,9 +1697,14 @@ class AnnotationEditorUIManager { this.setEditingState(false); this.#disableAll(); + this._editorUndoBar?.hide(); + this.#updateModeCapability.resolve(); return; } + if (mode === AnnotationEditorType.SIGNATURE) { + await this.#signatureManager?.loadSignatures(); + } this.setEditingState(true); await this.#enableAll(); this.unselectAll(); @@ -1703,7 +1765,7 @@ class AnnotationEditorUIManager { switch (type) { case AnnotationEditorParamsType.CREATE: - this.currentLayer.addNewEditor(); + this.currentLayer.addNewEditor(value); return; case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: this.#mainHighlightColorPicker?.updateColor(value); @@ -1846,6 +1908,9 @@ class AnnotationEditorUIManager { }, 0); } this.#allEditors.delete(editor.id); + if (editor.annotationElementId) { + this.#missingCanvases?.delete(editor.annotationElementId); + } this.unselect(editor); if ( !editor.annotationElementId || @@ -1931,6 +1996,10 @@ class AnnotationEditorUIManager { } } + updateUIForDefaultProperties(editorType) { + this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); + } + /** * Add or remove an editor the current selection. * @param {AnnotationEditor} editor @@ -1957,6 +2026,7 @@ class AnnotationEditorUIManager { * @param {AnnotationEditor} editor */ setSelected(editor) { + this.#currentDrawingSession?.commitOrRemove(); for (const ed of this.#selectedEditors) { if (ed !== editor) { ed.unselect(); @@ -2017,6 +2087,7 @@ class AnnotationEditorUIManager { hasSomethingToRedo: true, isEmpty: this.#isEmpty(), }); + this._editorUndoBar?.hide(); } /** @@ -2044,6 +2115,10 @@ class AnnotationEditorUIManager { }); } + cleanUndoStack(type) { + this.#commandManager.cleanType(type); + } + #isEmpty() { if (this.#allEditors.size === 0) { return true; @@ -2063,12 +2138,21 @@ class AnnotationEditorUIManager { */ delete() { this.commitOrRemove(); - if (!this.hasSelection) { + const drawingEditor = this.currentLayer?.endDrawingSession( + /* isAborted = */ true + ); + if (!this.hasSelection && !drawingEditor) { return; } - const editors = [...this.#selectedEditors]; + const editors = drawingEditor + ? [drawingEditor] + : [...this.#selectedEditors]; const cmd = () => { + this._editorUndoBar?.show( + undo, + editors.length === 1 ? editors[0].editorType : editors.length + ); for (const editor of editors) { editor.remove(); } @@ -2134,6 +2218,10 @@ class AnnotationEditorUIManager { } } + if (this.#currentDrawingSession?.commitOrRemove()) { + return; + } + if (!this.hasSelection) { return; } @@ -2176,6 +2264,7 @@ class AnnotationEditorUIManager { for (const editor of editors) { if (this.#allEditors.has(editor.id)) { editor.translateInPage(totalX, totalY); + editor.translationDone(); } } }, @@ -2183,6 +2272,7 @@ class AnnotationEditorUIManager { for (const editor of editors) { if (this.#allEditors.has(editor.id)) { editor.translateInPage(-totalX, -totalY); + editor.translationDone(); } } }, @@ -2192,6 +2282,7 @@ class AnnotationEditorUIManager { for (const editor of editors) { editor.translateInPage(x, y); + editor.translationDone(); } } @@ -2450,6 +2541,19 @@ class AnnotationEditorUIManager { } editor.renderAnnotationElement(annotation); } + + setMissingCanvas(annotationId, annotationElementId, canvas) { + const editor = this.#missingCanvases?.get(annotationId); + if (!editor) { + return; + } + editor.setCanvas(annotationElementId, canvas); + this.#missingCanvases.delete(annotationId); + } + + addMissingCanvas(annotationId, editor) { + (this.#missingCanvases ||= new Map()).set(annotationId, editor); + } } export { diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js index 1d025ce8bee68..acbf3c868e37e 100644 --- a/src/display/fetch_stream.js +++ b/src/display/fetch_stream.js @@ -16,8 +16,9 @@ import { AbortException, assert, warn } from "../shared/util.js"; import { createHeaders, - createResponseStatusError, + createResponseError, extractFilenameFromHeader, + getResponseOrigin, validateRangeRequestCapabilities, validateResponseStatus, } from "./network_utils.js"; @@ -52,6 +53,8 @@ function getArrayBuffer(val) { /** @implements {IPDFStream} */ class PDFFetchStream { + _responseOrigin = null; + constructor(source) { this.source = source; this.isHttp = /^https?:/i.test(source.url); @@ -121,8 +124,10 @@ class PDFFetchStreamReader { createFetchOptions(headers, this._withCredentials, this._abortController) ) .then(response => { + stream._responseOrigin = getResponseOrigin(response.url); + if (!validateResponseStatus(response.status)) { - throw createResponseStatusError(response.status, url); + throw createResponseError(response.status, url); } this._reader = response.body.getReader(); this._headersCapability.resolve(); @@ -217,8 +222,15 @@ class PDFFetchStreamRangeReader { createFetchOptions(headers, this._withCredentials, this._abortController) ) .then(response => { + const responseOrigin = getResponseOrigin(response.url); + + if (responseOrigin !== stream._responseOrigin) { + throw new Error( + `Expected range response-origin "${responseOrigin}" to match "${stream._responseOrigin}".` + ); + } if (!validateResponseStatus(response.status)) { - throw createResponseStatusError(response.status, url); + throw createResponseError(response.status, url); } this._readCapability.resolve(); this._reader = response.body.getReader(); diff --git a/src/display/filter_factory.js b/src/display/filter_factory.js new file mode 100644 index 0000000000000..6b844db350da1 --- /dev/null +++ b/src/display/filter_factory.js @@ -0,0 +1,509 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getRGB, isDataScheme, SVG_NS } from "./display_utils.js"; +import { unreachable, Util, warn } from "../shared/util.js"; + +class BaseFilterFactory { + constructor() { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseFilterFactory + ) { + unreachable("Cannot initialize BaseFilterFactory."); + } + } + + addFilter(maps) { + return "none"; + } + + addHCMFilter(fgColor, bgColor) { + return "none"; + } + + addAlphaFilter(map) { + return "none"; + } + + addLuminosityFilter(map) { + return "none"; + } + + addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { + return "none"; + } + + destroy(keepHCM = false) {} +} + +/** + * FilterFactory aims to create some SVG filters we can use when drawing an + * image (or whatever) on a canvas. + * Filters aren't applied with ctx.putImageData because it just overwrites the + * underlying pixels. + * With these filters, it's possible for example to apply some transfer maps on + * an image without the need to apply them on the pixel arrays: the renderer + * does the magic for us. + */ +class DOMFilterFactory extends BaseFilterFactory { + #baseUrl; + + #_cache; + + #_defs; + + #docId; + + #document; + + #_hcmCache; + + #id = 0; + + constructor({ docId, ownerDocument = globalThis.document }) { + super(); + this.#docId = docId; + this.#document = ownerDocument; + } + + get #cache() { + return (this.#_cache ||= new Map()); + } + + get #hcmCache() { + return (this.#_hcmCache ||= new Map()); + } + + get #defs() { + if (!this.#_defs) { + const div = this.#document.createElement("div"); + const { style } = div; + style.visibility = "hidden"; + style.contain = "strict"; + style.width = style.height = 0; + style.position = "absolute"; + style.top = style.left = 0; + style.zIndex = -1; + + const svg = this.#document.createElementNS(SVG_NS, "svg"); + svg.setAttribute("width", 0); + svg.setAttribute("height", 0); + this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); + div.append(svg); + svg.append(this.#_defs); + this.#document.body.append(div); + } + return this.#_defs; + } + + #createTables(maps) { + if (maps.length === 1) { + const mapR = maps[0]; + const buffer = new Array(256); + for (let i = 0; i < 256; i++) { + buffer[i] = mapR[i] / 255; + } + + const table = buffer.join(","); + return [table, table, table]; + } + + const [mapR, mapG, mapB] = maps; + const bufferR = new Array(256); + const bufferG = new Array(256); + const bufferB = new Array(256); + for (let i = 0; i < 256; i++) { + bufferR[i] = mapR[i] / 255; + bufferG[i] = mapG[i] / 255; + bufferB[i] = mapB[i] / 255; + } + return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; + } + + #createUrl(id) { + if (this.#baseUrl === undefined) { + // Unless a ``-element is present a relative URL should work. + this.#baseUrl = ""; + + const url = this.#document.URL; + if (url !== this.#document.baseURI) { + if (isDataScheme(url)) { + warn('#createUrl: ignore "data:"-URL for performance reasons.'); + } else { + this.#baseUrl = url.split("#", 1)[0]; + } + } + } + return `url(${this.#baseUrl}#${id})`; + } + + addFilter(maps) { + if (!maps) { + return "none"; + } + + // When a page is zoomed the page is re-drawn but the maps are likely + // the same. + let value = this.#cache.get(maps); + if (value) { + return value; + } + + const [tableR, tableG, tableB] = this.#createTables(maps); + const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; + + value = this.#cache.get(key); + if (value) { + this.#cache.set(maps, value); + return value; + } + + // We create a SVG filter: feComponentTransferElement + // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement + + const id = `g_${this.#docId}_transfer_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(maps, url); + this.#cache.set(key, url); + + const filter = this.#createFilter(id); + this.#addTransferMapConversion(tableR, tableG, tableB, filter); + + return url; + } + + addHCMFilter(fgColor, bgColor) { + const key = `${fgColor}-${bgColor}`; + const filterName = "base"; + let info = this.#hcmCache.get(filterName); + if (info?.key === key) { + return info.url; + } + + if (info) { + info.filter?.remove(); + info.key = key; + info.url = "none"; + info.filter = null; + } else { + info = { + key, + url: "none", + filter: null, + }; + this.#hcmCache.set(filterName, info); + } + + if (!fgColor || !bgColor) { + return info.url; + } + + const fgRGB = this.#getRGB(fgColor); + fgColor = Util.makeHexColor(...fgRGB); + const bgRGB = this.#getRGB(bgColor); + bgColor = Util.makeHexColor(...bgRGB); + this.#defs.style.color = ""; + + if ( + (fgColor === "#000000" && bgColor === "#ffffff") || + fgColor === bgColor + ) { + return info.url; + } + + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance + // + // Relative luminance: + // https://www.w3.org/TR/WCAG20/#relativeluminancedef + // + // We compute the rounded luminance of the default background color. + // Then for every color in the pdf, if its rounded luminance is the + // same as the background one then it's replaced by the new + // background color else by the foreground one. + const map = new Array(256); + for (let i = 0; i <= 255; i++) { + const x = i / 255; + map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; + } + const table = map.join(","); + + const id = `g_${this.#docId}_hcm_filter`; + const filter = (info.filter = this.#createFilter(id)); + this.#addTransferMapConversion(table, table, table, filter); + this.#addGrayConversion(filter); + + const getSteps = (c, n) => { + const start = fgRGB[c] / 255; + const end = bgRGB[c] / 255; + const arr = new Array(n + 1); + for (let i = 0; i <= n; i++) { + arr[i] = start + (i / n) * (end - start); + } + return arr.join(","); + }; + this.#addTransferMapConversion( + getSteps(0, 5), + getSteps(1, 5), + getSteps(2, 5), + filter + ); + + info.url = this.#createUrl(id); + return info.url; + } + + addAlphaFilter(map) { + // When a page is zoomed the page is re-drawn but the maps are likely + // the same. + let value = this.#cache.get(map); + if (value) { + return value; + } + + const [tableA] = this.#createTables([map]); + const key = `alpha_${tableA}`; + + value = this.#cache.get(key); + if (value) { + this.#cache.set(map, value); + return value; + } + + const id = `g_${this.#docId}_alpha_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(map, url); + this.#cache.set(key, url); + + const filter = this.#createFilter(id); + this.#addTransferMapAlphaConversion(tableA, filter); + + return url; + } + + addLuminosityFilter(map) { + // When a page is zoomed the page is re-drawn but the maps are likely + // the same. + let value = this.#cache.get(map || "luminosity"); + if (value) { + return value; + } + + let tableA, key; + if (map) { + [tableA] = this.#createTables([map]); + key = `luminosity_${tableA}`; + } else { + key = "luminosity"; + } + + value = this.#cache.get(key); + if (value) { + this.#cache.set(map, value); + return value; + } + + const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; + const url = this.#createUrl(id); + this.#cache.set(map, url); + this.#cache.set(key, url); + + const filter = this.#createFilter(id); + this.#addLuminosityConversion(filter); + if (map) { + this.#addTransferMapAlphaConversion(tableA, filter); + } + + return url; + } + + addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { + const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; + let info = this.#hcmCache.get(filterName); + if (info?.key === key) { + return info.url; + } + + if (info) { + info.filter?.remove(); + info.key = key; + info.url = "none"; + info.filter = null; + } else { + info = { + key, + url: "none", + filter: null, + }; + this.#hcmCache.set(filterName, info); + } + + if (!fgColor || !bgColor) { + return info.url; + } + + const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); + let fgGray = Math.round( + 0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2] + ); + let bgGray = Math.round( + 0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2] + ); + let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map( + this.#getRGB.bind(this) + ); + if (bgGray < fgGray) { + [fgGray, bgGray, newFgRGB, newBgRGB] = [ + bgGray, + fgGray, + newBgRGB, + newFgRGB, + ]; + } + this.#defs.style.color = ""; + + // Now we can create the filters to highlight some canvas parts. + // The colors in the pdf will almost be Canvas and CanvasText, hence we + // want to filter them to finally get Highlight and HighlightText. + // Since we're in HCM the background color and the foreground color should + // be really different when converted to grayscale (if they're not then it + // means that we've a poor contrast). Once the canvas colors are converted + // to grayscale we can easily map them on their new colors. + // The grayscale step is important because if we've something like: + // fgColor = #FF.... + // bgColor = #FF.... + // then we are enable to map the red component on the new red components + // which can be different. + + const getSteps = (fg, bg, n) => { + const arr = new Array(256); + const step = (bgGray - fgGray) / n; + const newStart = fg / 255; + const newStep = (bg - fg) / (255 * n); + let prev = 0; + for (let i = 0; i <= n; i++) { + const k = Math.round(fgGray + i * step); + const value = newStart + i * newStep; + for (let j = prev; j <= k; j++) { + arr[j] = value; + } + prev = k + 1; + } + for (let i = prev; i < 256; i++) { + arr[i] = arr[prev - 1]; + } + return arr.join(","); + }; + + const id = `g_${this.#docId}_hcm_${filterName}_filter`; + const filter = (info.filter = this.#createFilter(id)); + + this.#addGrayConversion(filter); + this.#addTransferMapConversion( + getSteps(newFgRGB[0], newBgRGB[0], 5), + getSteps(newFgRGB[1], newBgRGB[1], 5), + getSteps(newFgRGB[2], newBgRGB[2], 5), + filter + ); + + info.url = this.#createUrl(id); + return info.url; + } + + destroy(keepHCM = false) { + if (keepHCM && this.#_hcmCache?.size) { + return; + } + this.#_defs?.parentNode.parentNode.remove(); + this.#_defs = null; + + this.#_cache?.clear(); + this.#_cache = null; + + this.#_hcmCache?.clear(); + this.#_hcmCache = null; + + this.#id = 0; + } + + #addLuminosityConversion(filter) { + const feColorMatrix = this.#document.createElementNS( + SVG_NS, + "feColorMatrix" + ); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute( + "values", + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0" + ); + filter.append(feColorMatrix); + } + + #addGrayConversion(filter) { + const feColorMatrix = this.#document.createElementNS( + SVG_NS, + "feColorMatrix" + ); + feColorMatrix.setAttribute("type", "matrix"); + feColorMatrix.setAttribute( + "values", + "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0" + ); + filter.append(feColorMatrix); + } + + #createFilter(id) { + const filter = this.#document.createElementNS(SVG_NS, "filter"); + filter.setAttribute("color-interpolation-filters", "sRGB"); + filter.setAttribute("id", id); + this.#defs.append(filter); + + return filter; + } + + #appendFeFunc(feComponentTransfer, func, table) { + const feFunc = this.#document.createElementNS(SVG_NS, func); + feFunc.setAttribute("type", "discrete"); + feFunc.setAttribute("tableValues", table); + feComponentTransfer.append(feFunc); + } + + #addTransferMapConversion(rTable, gTable, bTable, filter) { + const feComponentTransfer = this.#document.createElementNS( + SVG_NS, + "feComponentTransfer" + ); + filter.append(feComponentTransfer); + this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); + this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); + this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); + } + + #addTransferMapAlphaConversion(aTable, filter) { + const feComponentTransfer = this.#document.createElementNS( + SVG_NS, + "feComponentTransfer" + ); + filter.append(feComponentTransfer); + this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); + } + + #getRGB(color) { + this.#defs.style.color = color; + return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); + } +} + +export { BaseFilterFactory, DOMFilterFactory }; diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 1014c536b67e0..e41357fcee0be 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -15,11 +15,11 @@ import { assert, - bytesToString, - FontRenderOps, + FeatureTest, isNodeJS, shadow, string32, + toBase64Util, unreachable, warn, } from "../shared/util.js"; @@ -80,12 +80,16 @@ class FontLoader { } } - async loadSystemFont({ systemFontInfo: info, _inspectFont }) { + async loadSystemFont({ + systemFontInfo: info, + disableFontFace, + _inspectFont, + }) { if (!info || this.#systemFonts.has(info.loadedName)) { return; } assert( - !this.disableFontFace, + !disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set." ); @@ -177,23 +181,14 @@ class FontLoader { return shadow(this, "isSyncFontLoadingSupported", true); } - let supported = false; - if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME")) { - if (isNodeJS) { - // Node.js - we can pretend that sync font loading is supported. - supported = true; - } else if ( - typeof navigator !== "undefined" && - typeof navigator?.userAgent === "string" && - // User agent string sniffing is bad, but there is no reliable way to - // tell if the font is fully loaded and ready to be used with canvas. - /Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent) - ) { - // Firefox, from version 14, supports synchronous font loading. - supported = true; - } - } - return shadow(this, "isSyncFontLoadingSupported", supported); + // Node.js - we can pretend that sync font loading is supported. + // Firefox, from version 14, supports synchronous font loading. + return shadow( + this, + "isSyncFontLoadingSupported", + (typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME")) && + (isNodeJS || FeatureTest.platform.isFirefox) + ); } _queueLoadingCallback(callback) { @@ -360,13 +355,20 @@ class FontLoader { } class FontFaceObject { - constructor(translatedData, { disableFontFace = false, inspectFont = null }) { + constructor(translatedData, inspectFont = null) { this.compiledGlyphs = Object.create(null); // importing translated data for (const i in translatedData) { this[i] = translatedData[i]; } - this.disableFontFace = disableFontFace === true; + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + if (typeof this.disableFontFace !== "boolean") { + unreachable("disableFontFace must be available."); + } + if (typeof this.fontExtraProperties !== "boolean") { + unreachable("fontExtraProperties must be available."); + } + } this._inspectFont = inspectFont; } @@ -399,9 +401,8 @@ class FontFaceObject { if (!this.data || this.disableFontFace) { return null; } - const data = bytesToString(this.data); // Add the @font-face rule to the document. - const url = `url(data:${this.mimetype};base64,${btoa(data)});`; + const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`; let rule; if (!this.cssFontInfo) { rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; @@ -422,92 +423,20 @@ class FontFaceObject { return this.compiledGlyphs[character]; } + const objId = this.loadedName + "_path_" + character; let cmds; try { - cmds = objs.get(this.loadedName + "_path_" + character); + cmds = objs.get(objId); } catch (ex) { warn(`getPathGenerator - ignoring character: "${ex}".`); } + const path = new Path2D(cmds || ""); - if (!Array.isArray(cmds) || cmds.length === 0) { - return (this.compiledGlyphs[character] = function (c, size) { - // No-op function, to allow rendering to continue. - }); - } - - const commands = []; - for (let i = 0, ii = cmds.length; i < ii; ) { - switch (cmds[i++]) { - case FontRenderOps.BEZIER_CURVE_TO: - { - const [a, b, c, d, e, f] = cmds.slice(i, i + 6); - commands.push(ctx => ctx.bezierCurveTo(a, b, c, d, e, f)); - i += 6; - } - break; - case FontRenderOps.MOVE_TO: - { - const [a, b] = cmds.slice(i, i + 2); - commands.push(ctx => ctx.moveTo(a, b)); - i += 2; - } - break; - case FontRenderOps.LINE_TO: - { - const [a, b] = cmds.slice(i, i + 2); - commands.push(ctx => ctx.lineTo(a, b)); - i += 2; - } - break; - case FontRenderOps.QUADRATIC_CURVE_TO: - { - const [a, b, c, d] = cmds.slice(i, i + 4); - commands.push(ctx => ctx.quadraticCurveTo(a, b, c, d)); - i += 4; - } - break; - case FontRenderOps.RESTORE: - commands.push(ctx => ctx.restore()); - break; - case FontRenderOps.SAVE: - commands.push(ctx => ctx.save()); - break; - case FontRenderOps.SCALE: - // The scale command must be at the third position, after save and - // transform (for the font matrix) commands (see also - // font_renderer.js). - // The goal is to just scale the canvas and then run the commands loop - // without the need to pass the size parameter to each command. - assert( - commands.length === 2, - "Scale command is only valid at the third position." - ); - break; - case FontRenderOps.TRANSFORM: - { - const [a, b, c, d, e, f] = cmds.slice(i, i + 6); - commands.push(ctx => ctx.transform(a, b, c, d, e, f)); - i += 6; - } - break; - case FontRenderOps.TRANSLATE: - { - const [a, b] = cmds.slice(i, i + 2); - commands.push(ctx => ctx.translate(a, b)); - i += 2; - } - break; - } + if (!this.fontExtraProperties) { + // Remove the raw path-string, since we don't need it anymore. + objs.delete(objId); } - - return (this.compiledGlyphs[character] = function glyphDrawer(ctx, size) { - commands[0](ctx); - commands[1](ctx); - ctx.scale(size, -size); - for (let i = 2, ii = commands.length; i < ii; i++) { - commands[i](ctx); - } - }); + return (this.compiledGlyphs[character] = path); } } diff --git a/src/display/network.js b/src/display/network.js index 8ce4e1356cff6..34fa53ab92f5d 100644 --- a/src/display/network.js +++ b/src/display/network.js @@ -13,11 +13,12 @@ * limitations under the License. */ -import { assert, stringToBytes } from "../shared/util.js"; +import { assert, stringToBytes, warn } from "../shared/util.js"; import { createHeaders, - createResponseStatusError, + createResponseError, extractFilenameFromHeader, + getResponseOrigin, validateRangeRequestCapabilities, } from "./network_utils.js"; @@ -39,6 +40,8 @@ function getArrayBuffer(xhr) { } class NetworkManager { + _responseOrigin = null; + constructor({ url, httpHeaders, withCredentials }) { this.url = url; this.isHttp = /^https?:/i.test(url); @@ -49,21 +52,6 @@ class NetworkManager { this.pendingRequests = Object.create(null); } - requestRange(begin, end, listeners) { - const args = { - begin, - end, - }; - for (const prop in listeners) { - args[prop] = listeners[prop]; - } - return this.request(args); - } - - requestFull(listeners) { - return this.request(listeners); - } - request(args) { const xhr = new XMLHttpRequest(); const xhrId = this.currXhrId++; @@ -82,11 +70,10 @@ class NetworkManager { } xhr.responseType = "arraybuffer"; - if (args.onError) { - xhr.onerror = function (evt) { - args.onError(xhr.status); - }; - } + assert(args.onError, "Expected `onError` callback to be provided."); + xhr.onerror = () => { + args.onError(xhr.status); + }; xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); xhr.onprogress = this.onProgress.bind(this, xhrId); @@ -134,7 +121,7 @@ class NetworkManager { // Success status == 0 can be on ftp, file and other protocols. if (xhr.status === 0 && this.isHttp) { - pendingRequest.onError?.(xhr.status); + pendingRequest.onError(xhr.status); return; } const xhrStatus = xhr.status || OK_RESPONSE; @@ -150,7 +137,7 @@ class NetworkManager { !ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus ) { - pendingRequest.onError?.(xhr.status); + pendingRequest.onError(xhr.status); return; } @@ -158,17 +145,22 @@ class NetworkManager { if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { const rangeHeader = xhr.getResponseHeader("Content-Range"); const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); - pendingRequest.onDone({ - begin: parseInt(matches[1], 10), - chunk, - }); + if (matches) { + pendingRequest.onDone({ + begin: parseInt(matches[1], 10), + chunk, + }); + } else { + warn(`Missing or invalid "Content-Range" header.`); + pendingRequest.onError(0); + } } else if (chunk) { pendingRequest.onDone({ begin: 0, chunk, }); } else { - pendingRequest.onError?.(xhr.status); + pendingRequest.onError(xhr.status); } } @@ -241,14 +233,13 @@ class PDFNetworkStreamFullRequestReader { constructor(manager, source) { this._manager = manager; - const args = { + this._url = source.url; + this._fullRequestId = manager.request({ onHeadersReceived: this._onHeadersReceived.bind(this), onDone: this._onDone.bind(this), onError: this._onError.bind(this), onProgress: this._onProgress.bind(this), - }; - this._url = source.url; - this._fullRequestId = manager.requestFull(args); + }); this._headersCapability = Promise.withResolvers(); this._disableRange = source.disableRange || false; this._contentLength = source.length; // Optional @@ -273,15 +264,22 @@ class PDFNetworkStreamFullRequestReader { const fullRequestXhrId = this._fullRequestId; const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId); + this._manager._responseOrigin = getResponseOrigin( + fullRequestXhr.responseURL + ); + + const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders(); const responseHeaders = new Headers( - fullRequestXhr - .getAllResponseHeaders() - .trim() - .split(/[\r\n]+/) - .map(x => { - const [key, ...val] = x.split(": "); - return [key, val.join(": ")]; - }) + rawResponseHeaders + ? rawResponseHeaders + .trimStart() + .replace(/[^\S ]+$/, "") // Not `trimEnd`, to keep regular spaces. + .split(/[\r\n]+/) + .map(x => { + const [key, ...val] = x.split(": "); + return [key, val.join(": ")]; + }) + : [] ); const { allowRangeRequests, suggestedLength } = @@ -331,7 +329,7 @@ class PDFNetworkStreamFullRequestReader { } _onError(status) { - this._storedError = createResponseStatusError(status, this._url); + this._storedError = createResponseError(status, this._url); this._headersCapability.reject(this._storedError); for (const requestCapability of this._requests) { requestCapability.reject(this._storedError); @@ -368,6 +366,8 @@ class PDFNetworkStreamFullRequestReader { } async read() { + await this._headersCapability.promise; + if (this._storedError) { throw this._storedError; } @@ -402,13 +402,15 @@ class PDFNetworkStreamRangeRequestReader { constructor(manager, begin, end) { this._manager = manager; - const args = { + this._url = manager.url; + this._requestId = manager.request({ + begin, + end, + onHeadersReceived: this._onHeadersReceived.bind(this), onDone: this._onDone.bind(this), onError: this._onError.bind(this), onProgress: this._onProgress.bind(this), - }; - this._url = manager.url; - this._requestId = manager.requestRange(begin, end, args); + }); this._requests = []; this._queuedChunk = null; this._done = false; @@ -418,6 +420,19 @@ class PDFNetworkStreamRangeRequestReader { this.onClosed = null; } + _onHeadersReceived() { + const responseOrigin = getResponseOrigin( + this._manager.getRequestXhr(this._requestId)?.responseURL + ); + + if (responseOrigin !== this._manager._responseOrigin) { + this._storedError = new Error( + `Expected range response-origin "${responseOrigin}" to match "${this._manager._responseOrigin}".` + ); + this._onError(0); + } + } + _close() { this.onClosed?.(this); } @@ -439,7 +454,7 @@ class PDFNetworkStreamRangeRequestReader { } _onError(status) { - this._storedError = createResponseStatusError(status, this._url); + this._storedError ??= createResponseError(status, this._url); for (const requestCapability of this._requests) { requestCapability.reject(this._storedError); } diff --git a/src/display/network_utils.js b/src/display/network_utils.js index 80f89588566c4..5af942a9f32a5 100644 --- a/src/display/network_utils.js +++ b/src/display/network_utils.js @@ -13,11 +13,7 @@ * limitations under the License. */ -import { - assert, - MissingPDFException, - UnexpectedResponseException, -} from "../shared/util.js"; +import { assert, ResponseException } from "../shared/util.js"; import { getFilenameFromContentDispositionHeader } from "./content_disposition.js"; import { isPdfFile } from "./display_utils.js"; @@ -36,6 +32,11 @@ function createHeaders(isHttp, httpHeaders) { return headers; } +function getResponseOrigin(url) { + // Notably, null is distinct from "null" string (e.g. from file:-URLs). + return URL.parse(url)?.origin ?? null; +} + function validateRangeRequestCapabilities({ responseHeaders, isHttp, @@ -98,13 +99,11 @@ function extractFilenameFromHeader(responseHeaders) { return null; } -function createResponseStatusError(status, url) { - if (status === 404 || (status === 0 && url.startsWith("file:"))) { - return new MissingPDFException('Missing PDF "' + url + '".'); - } - return new UnexpectedResponseException( +function createResponseError(status, url) { + return new ResponseException( `Unexpected server response (${status}) while retrieving PDF "${url}".`, - status + status, + /* missing = */ status === 404 || (status === 0 && url.startsWith("file:")) ); } @@ -114,8 +113,9 @@ function validateResponseStatus(status) { export { createHeaders, - createResponseStatusError, + createResponseError, extractFilenameFromHeader, + getResponseOrigin, validateRangeRequestCapabilities, validateResponseStatus, }; diff --git a/src/display/node_stream.js b/src/display/node_stream.js index 6808488d62f84..36f1b1a8dd4bb 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -12,14 +12,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* globals process */ -import { AbortException, assert, MissingPDFException } from "../shared/util.js"; -import { - createHeaders, - extractFilenameFromHeader, - validateRangeRequestCapabilities, -} from "./network_utils.js"; -import { NodePackages } from "./node_utils.js"; +import { AbortException, assert } from "../shared/util.js"; +import { createResponseError } from "./network_utils.js"; if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { throw new Error( @@ -33,28 +29,18 @@ function parseUrlOrPath(sourceUrl) { if (urlRegex.test(sourceUrl)) { return new URL(sourceUrl); } - const url = NodePackages.get("url"); + const url = process.getBuiltinModule("url"); return new URL(url.pathToFileURL(sourceUrl)); } -function createRequest(url, headers, callback) { - if (url.protocol === "http:") { - const http = NodePackages.get("http"); - return http.request(url, { headers }, callback); - } - const https = NodePackages.get("https"); - return https.request(url, { headers }, callback); -} - class PDFNodeStream { constructor(source) { this.source = source; this.url = parseUrlOrPath(source.url); - this.isHttp = - this.url.protocol === "http:" || this.url.protocol === "https:"; - // Check if url refers to filesystem. - this.isFsUrl = this.url.protocol === "file:"; - this.headers = createHeaders(this.isHttp, source.httpHeaders); + assert( + this.url.protocol === "file:", + "PDFNodeStream only supports file:// URLs." + ); this._fullRequestReader = null; this._rangeRequestReaders = []; @@ -69,9 +55,7 @@ class PDFNodeStream { !this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once." ); - this._fullRequestReader = this.isFsUrl - ? new PDFNodeStreamFsFullReader(this) - : new PDFNodeStreamFullReader(this); + this._fullRequestReader = new PDFNodeStreamFsFullReader(this); return this._fullRequestReader; } @@ -79,9 +63,7 @@ class PDFNodeStream { if (end <= this._progressiveDataLength) { return null; } - const rangeReader = this.isFsUrl - ? new PDFNodeStreamFsRangeReader(this, start, end) - : new PDFNodeStreamRangeReader(this, start, end); + const rangeReader = new PDFNodeStreamFsRangeReader(this, start, end); this._rangeRequestReaders.push(rangeReader); return rangeReader; } @@ -95,7 +77,7 @@ class PDFNodeStream { } } -class BaseFullReader { +class PDFNodeStreamFsFullReader { constructor(stream) { this._url = stream.url; this._done = false; @@ -118,6 +100,24 @@ class BaseFullReader { this._readableStream = null; this._readCapability = Promise.withResolvers(); this._headersCapability = Promise.withResolvers(); + + const fs = process.getBuiltinModule("fs"); + fs.promises.lstat(this._url).then( + stat => { + // Setting right content length. + this._contentLength = stat.size; + + this._setReadableStream(fs.createReadStream(this._url)); + this._headersCapability.resolve(); + }, + error => { + if (error.code === "ENOENT") { + error = createResponseError(/* status = */ 0, this._url.href); + } + this._storedError = error; + this._headersCapability.reject(error); + } + ); } get headersReady() { @@ -210,8 +210,8 @@ class BaseFullReader { } } -class BaseRangeReader { - constructor(stream) { +class PDFNodeStreamFsRangeReader { + constructor(stream, start, end) { this._url = stream.url; this._done = false; this._storedError = null; @@ -221,6 +221,11 @@ class BaseRangeReader { this._readCapability = Promise.withResolvers(); const source = stream.source; this._isStreamingSupported = !source.disableStream; + + const fs = process.getBuiltinModule("fs"); + this._setReadableStream( + fs.createReadStream(this._url, { start, end: end - 1 }) + ); } get isStreamingSupported() { @@ -288,112 +293,4 @@ class BaseRangeReader { } } -class PDFNodeStreamFullReader extends BaseFullReader { - constructor(stream) { - super(stream); - - // Node.js requires the `headers` to be a regular Object. - const headers = Object.fromEntries(stream.headers); - - const handleResponse = response => { - if (response.statusCode === 404) { - const error = new MissingPDFException(`Missing PDF "${this._url}".`); - this._storedError = error; - this._headersCapability.reject(error); - return; - } - this._headersCapability.resolve(); - this._setReadableStream(response); - - const responseHeaders = new Headers(this._readableStream.headers); - - const { allowRangeRequests, suggestedLength } = - validateRangeRequestCapabilities({ - responseHeaders, - isHttp: stream.isHttp, - rangeChunkSize: this._rangeChunkSize, - disableRange: this._disableRange, - }); - - this._isRangeSupported = allowRangeRequests; - // Setting right content length. - this._contentLength = suggestedLength || this._contentLength; - - this._filename = extractFilenameFromHeader(responseHeaders); - }; - - this._request = createRequest(this._url, headers, handleResponse); - - this._request.on("error", reason => { - this._storedError = reason; - this._headersCapability.reject(reason); - }); - // Note: `request.end(data)` is used to write `data` to request body - // and notify end of request. But one should always call `request.end()` - // even if there is no data to write -- (to notify the end of request). - this._request.end(); - } -} - -class PDFNodeStreamRangeReader extends BaseRangeReader { - constructor(stream, start, end) { - super(stream); - - // Node.js requires the `headers` to be a regular Object. - const headers = Object.fromEntries(stream.headers); - headers.Range = `bytes=${start}-${end - 1}`; - - const handleResponse = response => { - if (response.statusCode === 404) { - const error = new MissingPDFException(`Missing PDF "${this._url}".`); - this._storedError = error; - return; - } - this._setReadableStream(response); - }; - - this._request = createRequest(this._url, headers, handleResponse); - - this._request.on("error", reason => { - this._storedError = reason; - }); - this._request.end(); - } -} - -class PDFNodeStreamFsFullReader extends BaseFullReader { - constructor(stream) { - super(stream); - - const fs = NodePackages.get("fs"); - fs.promises.lstat(this._url).then( - stat => { - // Setting right content length. - this._contentLength = stat.size; - - this._setReadableStream(fs.createReadStream(this._url)); - this._headersCapability.resolve(); - }, - error => { - if (error.code === "ENOENT") { - error = new MissingPDFException(`Missing PDF "${this._url}".`); - } - this._storedError = error; - this._headersCapability.reject(error); - } - ); - } -} - -class PDFNodeStreamFsRangeReader extends BaseRangeReader { - constructor(stream, start, end) { - super(stream); - - const fs = NodePackages.get("fs"); - this._setReadableStream( - fs.createReadStream(this._url, { start, end: end - 1 }) - ); - } -} - export { PDFNodeStream }; diff --git a/src/display/node_utils.js b/src/display/node_utils.js index c0b1e6e2ce529..0085825f68d38 100644 --- a/src/display/node_utils.js +++ b/src/display/node_utils.js @@ -12,14 +12,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* globals process */ -import { - BaseCanvasFactory, - BaseCMapReaderFactory, - BaseFilterFactory, - BaseStandardFontDataFactory, -} from "./base_factory.js"; import { isNodeJS, warn } from "../shared/util.js"; +import { BaseCanvasFactory } from "./canvas_factory.js"; +import { BaseCMapReaderFactory } from "./cmap_reader_factory.js"; +import { BaseFilterFactory } from "./filter_factory.js"; +import { BaseStandardFontDataFactory } from "./standard_fontdata_factory.js"; +import { BaseWasmFactory } from "./wasm_factory.js"; if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { throw new Error( @@ -28,93 +28,50 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { } if (isNodeJS) { - // eslint-disable-next-line no-var - var packageCapability = Promise.withResolvers(); - // eslint-disable-next-line no-var - var packageMap = null; + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("SKIP_BABEL")) { + warn("Please use the `legacy` build in Node.js environments."); + } else { + let canvas; + try { + const require = process + .getBuiltinModule("module") + .createRequire(import.meta.url); - const loadPackages = async () => { - // Native packages. - const fs = await __non_webpack_import__("fs"), - http = await __non_webpack_import__("http"), - https = await __non_webpack_import__("https"), - url = await __non_webpack_import__("url"); - - // Optional, third-party, packages. - let canvas, path2d; - if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) { - try { - canvas = await __non_webpack_import__("canvas"); - } catch {} try { - path2d = await __non_webpack_import__("path2d"); - } catch {} + canvas = require("@napi-rs/canvas"); + } catch (ex) { + warn(`Cannot load "@napi-rs/canvas" package: "${ex}".`); + } + } catch (ex) { + warn(`Cannot access the \`require\` function: "${ex}".`); } - return new Map(Object.entries({ fs, http, https, url, canvas, path2d })); - }; - - loadPackages().then( - map => { - packageMap = map; - packageCapability.resolve(); - - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("SKIP_BABEL")) { - return; + if (!globalThis.DOMMatrix) { + if (canvas?.DOMMatrix) { + globalThis.DOMMatrix = canvas.DOMMatrix; + } else { + warn("Cannot polyfill `DOMMatrix`, rendering may be broken."); } - if (!globalThis.DOMMatrix) { - const DOMMatrix = map.get("canvas")?.DOMMatrix; - - if (DOMMatrix) { - globalThis.DOMMatrix = DOMMatrix; - } else { - warn("Cannot polyfill `DOMMatrix`, rendering may be broken."); - } + } + if (!globalThis.ImageData) { + if (canvas?.ImageData) { + globalThis.ImageData = canvas.ImageData; + } else { + warn("Cannot polyfill `ImageData`, rendering may be broken."); } - if (!globalThis.Path2D) { - const CanvasRenderingContext2D = - map.get("canvas")?.CanvasRenderingContext2D; - const applyPath2DToCanvasRenderingContext = - map.get("path2d")?.applyPath2DToCanvasRenderingContext; - const Path2D = map.get("path2d")?.Path2D; - - if ( - CanvasRenderingContext2D && - applyPath2DToCanvasRenderingContext && - Path2D - ) { - try { - applyPath2DToCanvasRenderingContext(CanvasRenderingContext2D); - } catch (ex) { - warn(`applyPath2DToCanvasRenderingContext: "${ex}".`); - } - globalThis.Path2D = Path2D; - } else { - warn("Cannot polyfill `Path2D`, rendering may be broken."); - } + } + if (!globalThis.Path2D) { + if (canvas?.Path2D) { + globalThis.Path2D = canvas.Path2D; + } else { + warn("Cannot polyfill `Path2D`, rendering may be broken."); } - }, - reason => { - warn(`loadPackages: ${reason}`); - - packageMap = new Map(); - packageCapability.resolve(); } - ); -} - -class NodePackages { - static get promise() { - return packageCapability.promise; - } - - static get(name) { - return packageMap?.get(name); } } async function fetchData(url) { - const fs = NodePackages.get("fs"); + const fs = process.getBuiltinModule("fs"); const data = await fs.promises.readFile(url); return new Uint8Array(data); } @@ -126,7 +83,10 @@ class NodeCanvasFactory extends BaseCanvasFactory { * @ignore */ _createCanvas(width, height) { - const canvas = NodePackages.get("canvas"); + const require = process + .getBuiltinModule("module") + .createRequire(import.meta.url); + const canvas = require("@napi-rs/canvas"); return canvas.createCanvas(width, height); } } @@ -149,11 +109,20 @@ class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { } } +class NodeWasmFactory extends BaseWasmFactory { + /** + * @ignore + */ + async _fetch(url) { + return fetchData(url); + } +} + export { fetchData, NodeCanvasFactory, NodeCMapReaderFactory, NodeFilterFactory, - NodePackages, NodeStandardFontDataFactory, + NodeWasmFactory, }; diff --git a/src/display/pattern_helper.js b/src/display/pattern_helper.js index 4241695a23aa1..077ec990be87f 100644 --- a/src/display/pattern_helper.js +++ b/src/display/pattern_helper.js @@ -313,8 +313,8 @@ class MeshShadingPattern extends BaseShadingPattern { this._colors = IR[3]; this._figures = IR[4]; this._bounds = IR[5]; - this._bbox = IR[7]; - this._background = IR[8]; + this._bbox = IR[6]; + this._background = IR[7]; this.matrix = null; } diff --git a/src/display/standard_fontdata_factory.js b/src/display/standard_fontdata_factory.js new file mode 100644 index 0000000000000..bfd8af3c0cf68 --- /dev/null +++ b/src/display/standard_fontdata_factory.js @@ -0,0 +1,65 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { fetchData } from "./display_utils.js"; +import { unreachable } from "../shared/util.js"; + +class BaseStandardFontDataFactory { + constructor({ baseUrl = null }) { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseStandardFontDataFactory + ) { + unreachable("Cannot initialize BaseStandardFontDataFactory."); + } + this.baseUrl = baseUrl; + } + + async fetch({ filename }) { + if (!this.baseUrl) { + throw new Error( + "Ensure that the `standardFontDataUrl` API parameter is provided." + ); + } + if (!filename) { + throw new Error("Font filename must be specified."); + } + const url = `${this.baseUrl}${filename}`; + + return this._fetch(url).catch(reason => { + throw new Error(`Unable to load font data at: ${url}`); + }); + } + + /** + * @ignore + * @returns {Promise} + */ + async _fetch(url) { + unreachable("Abstract method `_fetch` called."); + } +} + +class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { + /** + * @ignore + */ + async _fetch(url) { + const data = await fetchData(url, /* type = */ "arraybuffer"); + return new Uint8Array(data); + } +} + +export { BaseStandardFontDataFactory, DOMStandardFontDataFactory }; diff --git a/src/display/stubs.js b/src/display/stubs.js index 0b3b9a0bbb788..beec23c406f0c 100644 --- a/src/display/stubs.js +++ b/src/display/stubs.js @@ -13,21 +13,27 @@ * limitations under the License. */ +const DOMCMapReaderFactory = null; +const DOMWasmFactory = null; +const DOMStandardFontDataFactory = null; const NodeCanvasFactory = null; const NodeCMapReaderFactory = null; const NodeFilterFactory = null; -const NodePackages = null; +const NodeWasmFactory = null; const NodeStandardFontDataFactory = null; const PDFFetchStream = null; const PDFNetworkStream = null; const PDFNodeStream = null; export { + DOMCMapReaderFactory, + DOMStandardFontDataFactory, + DOMWasmFactory, NodeCanvasFactory, NodeCMapReaderFactory, NodeFilterFactory, - NodePackages, NodeStandardFontDataFactory, + NodeWasmFactory, PDFFetchStream, PDFNetworkStream, PDFNodeStream, diff --git a/src/display/svg_factory.js b/src/display/svg_factory.js new file mode 100644 index 0000000000000..f9591a818499f --- /dev/null +++ b/src/display/svg_factory.js @@ -0,0 +1,71 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SVG_NS } from "./display_utils.js"; +import { unreachable } from "../shared/util.js"; + +class BaseSVGFactory { + constructor() { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseSVGFactory + ) { + unreachable("Cannot initialize BaseSVGFactory."); + } + } + + create(width, height, skipDimensions = false) { + if (width <= 0 || height <= 0) { + throw new Error("Invalid SVG dimensions"); + } + const svg = this._createSVG("svg:svg"); + svg.setAttribute("version", "1.1"); + + if (!skipDimensions) { + svg.setAttribute("width", `${width}px`); + svg.setAttribute("height", `${height}px`); + } + + svg.setAttribute("preserveAspectRatio", "none"); + svg.setAttribute("viewBox", `0 0 ${width} ${height}`); + + return svg; + } + + createElement(type) { + if (typeof type !== "string") { + throw new Error("Invalid SVG element type"); + } + return this._createSVG(type); + } + + /** + * @ignore + */ + _createSVG(type) { + unreachable("Abstract method `_createSVG` called."); + } +} + +class DOMSVGFactory extends BaseSVGFactory { + /** + * @ignore + */ + _createSVG(type) { + return document.createElementNS(SVG_NS, type); + } +} + +export { BaseSVGFactory, DOMSVGFactory }; diff --git a/src/display/text_layer.js b/src/display/text_layer.js index 4bcfde930174c..1e3ba2d82aabf 100644 --- a/src/display/text_layer.js +++ b/src/display/text_layer.js @@ -46,7 +46,6 @@ import { setLayerDimensions } from "./display_utils.js"; const MAX_TEXT_DIVS_TO_RENDER = 100000; const DEFAULT_FONT_SIZE = 30; -const DEFAULT_FONT_ASCENT = 0.8; class TextLayer { #capability = Promise.withResolvers(); @@ -332,7 +331,7 @@ class TextLayer { fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily; const fontHeight = Math.hypot(tx[2], tx[3]); const fontAscent = - fontHeight * TextLayer.#getAscent(fontFamily, this.#lang); + fontHeight * TextLayer.#getAscent(fontFamily, style, this.#lang); let left, top; if (angle === 0) { @@ -343,7 +342,7 @@ class TextLayer { top = tx[5] - fontAscent * Math.cos(angle); } - const scaleFactorStr = "calc(var(--scale-factor)*"; + const scaleFactorStr = "calc(var(--total-scale-factor) *"; const divStyle = textDiv.style; // Setting the style properties individually, rather than all at once, // should be OK since the `textDiv` isn't appended to the document yet. @@ -523,7 +522,7 @@ class TextLayer { div.remove(); } - static #getAscent(fontFamily, lang) { + static #getAscent(fontFamily, style, lang) { const cachedAscent = this.#ascentCache.get(fontFamily); if (cachedAscent) { return cachedAscent; @@ -534,55 +533,31 @@ class TextLayer { this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily); const metrics = ctx.measureText(""); - // Both properties aren't available by default in Firefox. - let ascent = metrics.fontBoundingBoxAscent; - let descent = Math.abs(metrics.fontBoundingBoxDescent); - if (ascent) { - const ratio = ascent / (ascent + descent); - this.#ascentCache.set(fontFamily, ratio); + const ascent = metrics.fontBoundingBoxAscent; + const descent = Math.abs(metrics.fontBoundingBoxDescent); - ctx.canvas.width = ctx.canvas.height = 0; - return ratio; - } + ctx.canvas.width = ctx.canvas.height = 0; + let ratio = 0.8; // DEFAULT_FONT_ASCENT - // Try basic heuristic to guess ascent/descent. - // Draw a g with baseline at 0,0 and then get the line - // number where a pixel has non-null red component (starting - // from bottom). - ctx.strokeStyle = "red"; - ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); - ctx.strokeText("g", 0, 0); - let pixels = ctx.getImageData( - 0, - 0, - DEFAULT_FONT_SIZE, - DEFAULT_FONT_SIZE - ).data; - descent = 0; - for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) { - if (pixels[i] > 0) { - descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE); - break; + if (ascent) { + ratio = ascent / (ascent + descent); + } else { + if ( + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || + FeatureTest.platform.isFirefox + ) { + warn( + "Enable the `dom.textMetrics.fontBoundingBox.enabled` preference " + + "in `about:config` to improve TextLayer rendering." + ); } - } - - // Draw an A with baseline at 0,DEFAULT_FONT_SIZE and then get the line - // number where a pixel has non-null red component (starting - // from top). - ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); - ctx.strokeText("A", 0, DEFAULT_FONT_SIZE); - pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data; - ascent = 0; - for (let i = 0, ii = pixels.length; i < ii; i += 4) { - if (pixels[i] > 0) { - ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE); - break; + if (style.ascent) { + ratio = style.ascent; + } else if (style.descent) { + ratio = 1 + style.descent; } } - ctx.canvas.width = ctx.canvas.height = 0; - - const ratio = ascent ? ascent / (ascent + descent) : DEFAULT_FONT_ASCENT; this.#ascentCache.set(fontFamily, ratio); return ratio; } diff --git a/src/display/touch_manager.js b/src/display/touch_manager.js new file mode 100644 index 0000000000000..69f6a9f6d2022 --- /dev/null +++ b/src/display/touch_manager.js @@ -0,0 +1,239 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { shadow } from "../shared/util.js"; +import { stopEvent } from "./display_utils.js"; + +class TouchManager { + #container; + + #isPinching = false; + + #isPinchingStopped = null; + + #isPinchingDisabled; + + #onPinchStart; + + #onPinching; + + #onPinchEnd; + + #pointerDownAC = null; + + #signal; + + #touchInfo = null; + + #touchManagerAC; + + #touchMoveAC = null; + + constructor({ + container, + isPinchingDisabled = null, + isPinchingStopped = null, + onPinchStart = null, + onPinching = null, + onPinchEnd = null, + signal, + }) { + this.#container = container; + this.#isPinchingStopped = isPinchingStopped; + this.#isPinchingDisabled = isPinchingDisabled; + this.#onPinchStart = onPinchStart; + this.#onPinching = onPinching; + this.#onPinchEnd = onPinchEnd; + this.#touchManagerAC = new AbortController(); + this.#signal = AbortSignal.any([signal, this.#touchManagerAC.signal]); + + container.addEventListener("touchstart", this.#onTouchStart.bind(this), { + passive: false, + signal: this.#signal, + }); + } + + get MIN_TOUCH_DISTANCE_TO_PINCH() { + // The 35 is coming from: + // https://searchfox.org/mozilla-central/source/gfx/layers/apz/src/GestureEventListener.cpp#36 + // + // The properties TouchEvent::screenX/Y are in screen CSS pixels: + // https://developer.mozilla.org/en-US/docs/Web/API/Touch/screenX#examples + // MIN_TOUCH_DISTANCE_TO_PINCH is in CSS pixels. + return shadow( + this, + "MIN_TOUCH_DISTANCE_TO_PINCH", + 35 / (window.devicePixelRatio || 1) + ); + } + + #onTouchStart(evt) { + if (this.#isPinchingDisabled?.()) { + return; + } + + if (evt.touches.length === 1) { + if (this.#pointerDownAC) { + return; + } + const pointerDownAC = (this.#pointerDownAC = new AbortController()); + const signal = AbortSignal.any([this.#signal, pointerDownAC.signal]); + const container = this.#container; + + // We want to have the events at the capture phase to make sure we can + // cancel them. + const opts = { capture: true, signal, passive: false }; + const cancelPointerDown = e => { + if (e.pointerType === "touch") { + this.#pointerDownAC?.abort(); + this.#pointerDownAC = null; + } + }; + container.addEventListener( + "pointerdown", + e => { + if (e.pointerType === "touch") { + // This is the second finger so we don't want it select something + // or whatever. + stopEvent(e); + cancelPointerDown(e); + } + }, + opts + ); + container.addEventListener("pointerup", cancelPointerDown, opts); + container.addEventListener("pointercancel", cancelPointerDown, opts); + return; + } + + if (!this.#touchMoveAC) { + this.#touchMoveAC = new AbortController(); + const signal = AbortSignal.any([this.#signal, this.#touchMoveAC.signal]); + const container = this.#container; + + const opt = { signal, capture: false, passive: false }; + container.addEventListener( + "touchmove", + this.#onTouchMove.bind(this), + opt + ); + const onTouchEnd = this.#onTouchEnd.bind(this); + container.addEventListener("touchend", onTouchEnd, opt); + container.addEventListener("touchcancel", onTouchEnd, opt); + + opt.capture = true; + container.addEventListener("pointerdown", stopEvent, opt); + container.addEventListener("pointermove", stopEvent, opt); + container.addEventListener("pointercancel", stopEvent, opt); + container.addEventListener("pointerup", stopEvent, opt); + this.#onPinchStart?.(); + } + + stopEvent(evt); + + if (evt.touches.length !== 2 || this.#isPinchingStopped?.()) { + this.#touchInfo = null; + return; + } + + let [touch0, touch1] = evt.touches; + if (touch0.identifier > touch1.identifier) { + [touch0, touch1] = [touch1, touch0]; + } + this.#touchInfo = { + touch0X: touch0.screenX, + touch0Y: touch0.screenY, + touch1X: touch1.screenX, + touch1Y: touch1.screenY, + }; + } + + #onTouchMove(evt) { + if (!this.#touchInfo || evt.touches.length !== 2) { + return; + } + + stopEvent(evt); + + let [touch0, touch1] = evt.touches; + if (touch0.identifier > touch1.identifier) { + [touch0, touch1] = [touch1, touch0]; + } + const { screenX: screen0X, screenY: screen0Y } = touch0; + const { screenX: screen1X, screenY: screen1Y } = touch1; + const touchInfo = this.#touchInfo; + const { + touch0X: pTouch0X, + touch0Y: pTouch0Y, + touch1X: pTouch1X, + touch1Y: pTouch1Y, + } = touchInfo; + + const prevGapX = pTouch1X - pTouch0X; + const prevGapY = pTouch1Y - pTouch0Y; + const currGapX = screen1X - screen0X; + const currGapY = screen1Y - screen0Y; + + const distance = Math.hypot(currGapX, currGapY) || 1; + const pDistance = Math.hypot(prevGapX, prevGapY) || 1; + if ( + !this.#isPinching && + Math.abs(pDistance - distance) <= TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH + ) { + return; + } + + touchInfo.touch0X = screen0X; + touchInfo.touch0Y = screen0Y; + touchInfo.touch1X = screen1X; + touchInfo.touch1Y = screen1Y; + + if (!this.#isPinching) { + // Start pinching. + this.#isPinching = true; + + // We return here else the first pinch is a bit too much + return; + } + + const origin = [(screen0X + screen1X) / 2, (screen0Y + screen1Y) / 2]; + this.#onPinching?.(origin, pDistance, distance); + } + + #onTouchEnd(evt) { + if (evt.touches.length >= 2) { + return; + } + this.#touchMoveAC.abort(); + this.#touchMoveAC = null; + this.#onPinchEnd?.(); + + if (!this.#touchInfo) { + return; + } + stopEvent(evt); + this.#touchInfo = null; + this.#isPinching = false; + } + + destroy() { + this.#touchManagerAC?.abort(); + this.#touchManagerAC = null; + this.#pointerDownAC?.abort(); + this.#pointerDownAC = null; + } +} + +export { TouchManager }; diff --git a/src/display/wasm_factory.js b/src/display/wasm_factory.js new file mode 100644 index 0000000000000..297e8e0e34e02 --- /dev/null +++ b/src/display/wasm_factory.js @@ -0,0 +1,63 @@ +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { fetchData } from "./display_utils.js"; +import { unreachable } from "../shared/util.js"; + +class BaseWasmFactory { + constructor({ baseUrl = null }) { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && + this.constructor === BaseWasmFactory + ) { + unreachable("Cannot initialize BaseWasmFactory."); + } + this.baseUrl = baseUrl; + } + + async fetch({ filename }) { + if (!this.baseUrl) { + throw new Error("Ensure that the `wasmUrl` API parameter is provided."); + } + if (!filename) { + throw new Error("Wasm filename must be specified."); + } + const url = `${this.baseUrl}${filename}`; + + return this._fetch(url).catch(reason => { + throw new Error(`Unable to load wasm data at: ${url}`); + }); + } + + /** + * @ignore + * @returns {Promise} + */ + async _fetch(url) { + unreachable("Abstract method `_fetch` called."); + } +} + +class DOMWasmFactory extends BaseWasmFactory { + /** + * @ignore + */ + async _fetch(url) { + const data = await fetchData(url, /* type = */ "arraybuffer"); + return new Uint8Array(data); + } +} + +export { BaseWasmFactory, DOMWasmFactory }; diff --git a/src/interfaces.js b/src/interfaces.js index e0ecb54746c57..15493bdb819f2 100644 --- a/src/interfaces.js +++ b/src/interfaces.js @@ -30,6 +30,10 @@ class IPDFStream { /** * Gets a reader for the range of the PDF data. + * + * NOTE: Currently this method is only expected to be invoked *after* + * the `IPDFStreamReader.prototype.headersReady` promise has resolved. + * * @param {number} begin - the start offset of the data. * @param {number} end - the end offset of the data. * @returns {IPDFStreamRangeReader} diff --git a/src/pdf.js b/src/pdf.js index c78d27e14524d..eea401cd6c893 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -27,29 +27,30 @@ import { AnnotationEditorParamsType, AnnotationEditorType, AnnotationMode, + AnnotationType, createValidAbsoluteUrl, FeatureTest, + getUuid, ImageKind, InvalidPDFException, - MissingPDFException, normalizeUnicode, OPS, PasswordResponses, PermissionFlag, + ResponseException, shadow, - UnexpectedResponseException, Util, VerbosityLevel, } from "./shared/util.js"; import { build, getDocument, + isValidExplicitDest, PDFDataRangeTransport, PDFWorker, version, } from "./display/api.js"; import { - DOMSVGFactory, fetchData, getFilenameFromUrl, getPdfFilenameFromUrl, @@ -62,15 +63,20 @@ import { PixelsPerInch, RenderingCancelledException, setLayerDimensions, + stopEvent, + SupportedImageMimeTypes, } from "./display/display_utils.js"; import { AnnotationEditorLayer } from "./display/editor/annotation_editor_layer.js"; import { AnnotationEditorUIManager } from "./display/editor/tools.js"; import { AnnotationLayer } from "./display/annotation_layer.js"; import { ColorPicker } from "./display/editor/color_picker.js"; +import { DOMSVGFactory } from "./display/svg_factory.js"; import { DrawLayer } from "./display/draw_layer.js"; import { GlobalWorkerOptions } from "./display/worker_options.js"; -import { Outliner } from "./display/editor/outliner.js"; +import { HighlightOutliner } from "./display/editor/drawers/highlight.js"; +import { SignatureExtractor } from "./display/editor/drawers/signaturedraw.js"; import { TextLayer } from "./display/text_layer.js"; +import { TouchManager } from "./display/touch_manager.js"; import { XfaLayer } from "./display/xfa_layer.js"; /* eslint-disable-next-line no-unused-vars */ @@ -80,9 +86,9 @@ const pdfjsVersion = const pdfjsBuild = typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0; -if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { +if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING || GENERIC")) { globalThis.pdfjsTestingUtils = { - Outliner, + HighlightOutliner, }; } @@ -94,6 +100,7 @@ export { AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, + AnnotationType, build, ColorPicker, createValidAbsoluteUrl, @@ -104,13 +111,14 @@ export { getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, + getUuid, getXfaPageViewport, GlobalWorkerOptions, ImageKind, InvalidPDFException, isDataScheme, isPdfFile, - MissingPDFException, + isValidExplicitDest, noContextMenu, normalizeUnicode, OPS, @@ -122,10 +130,14 @@ export { PermissionFlag, PixelsPerInch, RenderingCancelledException, + ResponseException, setLayerDimensions, shadow, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, TextLayer, - UnexpectedResponseException, + TouchManager, Util, VerbosityLevel, version, diff --git a/src/pdf.sandbox.js b/src/pdf.sandbox.js index adced961f5426..79cb05ec1afff 100644 --- a/src/pdf.sandbox.js +++ b/src/pdf.sandbox.js @@ -84,6 +84,7 @@ class Sandbox { [buf, this._alertOnError] ); } catch (error) { + // eslint-disable-next-line no-console console.error(error); } finally { if (buf) { diff --git a/src/scripting_api/aform.js b/src/scripting_api/aform.js index 355fc8450f976..5f9c67c90844a 100644 --- a/src/scripting_api/aform.js +++ b/src/scripting_api/aform.js @@ -38,7 +38,6 @@ class AForm { "m/d/yy HH:MM", ]; this._timeFormats = ["HH:MM", "h:MM tt", "HH:MM:ss", "h:MM:ss tt"]; - this._dateActionsCache = new Map(); // The e-mail address regex below originates from: // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address @@ -53,120 +52,20 @@ class AForm { return event.target ? `[ ${event.target.name} ]` : ""; } - _tryToGuessDate(cFormat, cDate) { - // We use the format to know the order of day, month, year, ... - - let actions = this._dateActionsCache.get(cFormat); - if (!actions) { - actions = []; - this._dateActionsCache.set(cFormat, actions); - cFormat.replaceAll( - /(d+)|(m+)|(y+)|(H+)|(M+)|(s+)/g, - function (match, d, m, y, H, M, s) { - if (d) { - actions.push((n, date) => { - if (n >= 1 && n <= 31) { - date.setDate(n); - return true; - } - return false; - }); - } else if (m) { - actions.push((n, date) => { - if (n >= 1 && n <= 12) { - date.setMonth(n - 1); - return true; - } - return false; - }); - } else if (y) { - actions.push((n, date) => { - if (n < 50) { - n += 2000; - } else if (n < 100) { - n += 1900; - } - date.setYear(n); - return true; - }); - } else if (H) { - actions.push((n, date) => { - if (n >= 0 && n <= 23) { - date.setHours(n); - return true; - } - return false; - }); - } else if (M) { - actions.push((n, date) => { - if (n >= 0 && n <= 59) { - date.setMinutes(n); - return true; - } - return false; - }); - } else if (s) { - actions.push((n, date) => { - if (n >= 0 && n <= 59) { - date.setSeconds(n); - return true; - } - return false; - }); - } - return ""; - } - ); - } - - const number = /\d+/g; - let i = 0; - let array; - const date = new Date(); - while ((array = number.exec(cDate)) !== null) { - if (i < actions.length) { - if (!actions[i++](parseInt(array[0]), date)) { - return null; - } - } else { - break; - } - } - - if (i === 0) { - return null; - } - - return date; - } - _parseDate(cFormat, cDate, strict = false) { let date = null; try { - date = this._util.scand(cFormat, cDate); + date = this._util._scand(cFormat, cDate, strict); } catch {} - if (!date) { - if (strict) { - return null; - } - let format = cFormat; - if (/mm(?!m)/.test(format)) { - format = format.replace("mm", "m"); - } - if (/dd(?!d)/.test(format)) { - format = format.replace("dd", "d"); - } - try { - date = this._util.scand(format, cDate); - } catch {} + if (date) { + return date; } - if (!date) { - date = Date.parse(cDate); - date = isNaN(date) - ? this._tryToGuessDate(cFormat, cDate) - : new Date(date); + if (strict) { + return null; } - return date; + + date = Date.parse(cDate); + return isNaN(date) ? null : new Date(date); } AFMergeChange(event = globalThis.event) { @@ -566,22 +465,22 @@ class AForm { // specific to the format because the user could enter 1234567 when the // format is 999-9999. const simplifiedFormatStr = cMask.replaceAll(/[^9AOX]/g, ""); - this.#AFSpecial_KeystrokeEx_helper(simplifiedFormatStr, false); + this.#AFSpecial_KeystrokeEx_helper(simplifiedFormatStr, null, false); if (event.rc) { return; } event.rc = true; - this.#AFSpecial_KeystrokeEx_helper(cMask, true); + this.#AFSpecial_KeystrokeEx_helper(cMask, null, true); } - #AFSpecial_KeystrokeEx_helper(cMask, warn) { + #AFSpecial_KeystrokeEx_helper(cMask, value, warn) { if (!cMask) { return; } const event = globalThis.event; - const value = this.AFMergeChange(event); + value ||= this.AFMergeChange(event); if (!value) { return; } @@ -664,7 +563,8 @@ class AForm { const event = globalThis.event; psf = this.AFMakeNumber(psf); - let formatStr; + let value = this.AFMergeChange(event); + let formatStr, secondFormatStr; switch (psf) { case 0: formatStr = "99999"; @@ -673,11 +573,8 @@ class AForm { formatStr = "99999-9999"; break; case 2: - const value = this.AFMergeChange(event); - formatStr = - value.startsWith("(") || (value.length > 7 && /^\p{N}+$/.test(value)) - ? "(999) 999-9999" - : "999-9999"; + formatStr = "999-9999"; + secondFormatStr = "(999) 999-9999"; break; case 3: formatStr = "999-99-9999"; @@ -685,8 +582,36 @@ class AForm { default: throw new Error("Invalid psf in AFSpecial_Keystroke"); } + const formats = secondFormatStr + ? [formatStr, secondFormatStr] + : [formatStr]; + for (const format of formats) { + this.#AFSpecial_KeystrokeEx_helper(format, value, false); + if (event.rc) { + return; + } + event.rc = true; + } + + const re = /([-()]|\s)+/g; + value = value.replaceAll(re, ""); + for (const format of formats) { + this.#AFSpecial_KeystrokeEx_helper( + format.replaceAll(re, ""), + value, + false + ); + if (event.rc) { + return; + } + event.rc = true; + } - this.AFSpecial_KeystrokeEx(formatStr); + this.AFSpecial_KeystrokeEx( + ((secondFormatStr && value.match(/\d/g)) || []).length > 7 + ? secondFormatStr + : formatStr + ); } AFTime_FormatEx(cFormat) { diff --git a/src/scripting_api/app.js b/src/scripting_api/app.js index acf1a7e228b21..74dba793d19a7 100644 --- a/src/scripting_api/app.js +++ b/src/scripting_api/app.js @@ -447,6 +447,9 @@ class App extends PDFObject { cMsg = cMsg.cMsg; } cMsg = (cMsg || "").toString(); + if (!cMsg) { + return 0; + } nType = typeof nType !== "number" || isNaN(nType) || nType < 0 || nType > 3 ? 0 diff --git a/src/scripting_api/doc.js b/src/scripting_api/doc.js index a9cbe90d8d7a8..0481f02e5f089 100644 --- a/src/scripting_api/doc.js +++ b/src/scripting_api/doc.js @@ -175,9 +175,16 @@ class Doc extends PDFObject { _runActions(name) { const actions = this._actions.get(name); - if (actions) { - for (const action of actions) { + if (!actions) { + return; + } + for (const action of actions) { + try { this._globalEval(action); + } catch (error) { + const serializedError = serializeError(error); + serializedError.value = `Error when executing "${name}" for document\n${serializedError.value}`; + this._send(serializedError); } } } diff --git a/src/scripting_api/event.js b/src/scripting_api/event.js index 4683f9b8f36d6..f84d76688934a 100644 --- a/src/scripting_api/event.js +++ b/src/scripting_api/event.js @@ -14,7 +14,6 @@ */ import { - serializeError, USERACTIVATION_CALLBACKID, USERACTIVATION_MAXTIME_VALIDITY, } from "./app_utils.js"; @@ -311,12 +310,7 @@ class EventDispatcher { const source = this._objects[first]; globalThis.event = new Event({}); - try { - this.runCalculate(source, globalThis.event); - } catch (error) { - this._isCalculating = false; - throw error; - } + this.runCalculate(source, globalThis.event); this._isCalculating = false; } @@ -345,15 +339,8 @@ class EventDispatcher { event.value = null; const target = this._objects[targetId]; let savedValue = target.obj._getValue(); - try { - this.runActions(source, target, event, "Calculate"); - } catch (error) { - const fieldId = target.obj._id; - const serializedError = serializeError(error); - serializedError.value = `Error when calculating value for field "${fieldId}"\n${serializedError.value}`; - this._externalCall("send", [serializedError]); - continue; - } + this.runActions(source, target, event, "Calculate"); + if (!event.rc) { continue; } diff --git a/src/scripting_api/field.js b/src/scripting_api/field.js index 0188d484492f6..e484150aada73 100644 --- a/src/scripting_api/field.js +++ b/src/scripting_api/field.js @@ -16,6 +16,7 @@ import { createActionsMap, FieldType, getFieldType } from "./common.js"; import { Color } from "./color.js"; import { PDFObject } from "./pdf_object.js"; +import { serializeError } from "./app_utils.js"; class Field extends PDFObject { constructor(data) { @@ -552,14 +553,15 @@ class Field extends PDFObject { } const actions = this._actions.get(eventName); - try { - for (const action of actions) { + for (const action of actions) { + try { // Action evaluation must happen in the global scope this._globalEval(action); + } catch (error) { + const serializedError = serializeError(error); + serializedError.value = `Error when executing "${eventName}" for field "${this._id}"\n${serializedError.value}`; + this._send(serializedError); } - } catch (error) { - event.rc = false; - throw error; } return true; diff --git a/src/scripting_api/util.js b/src/scripting_api/util.js index 551a3fb600797..66f014c50fd9e 100644 --- a/src/scripting_api/util.js +++ b/src/scripting_api/util.js @@ -16,6 +16,8 @@ import { PDFObject } from "./pdf_object.js"; class Util extends PDFObject { + #dateActionsCache = null; + constructor(data) { super(data); @@ -338,7 +340,112 @@ class Util extends PDFObject { return buf.join(""); } + #tryToGuessDate(cFormat, cDate) { + // We use the format to know the order of day, month, year, ... + + let actions = (this.#dateActionsCache ||= new Map()).get(cFormat); + if (!actions) { + actions = []; + this.#dateActionsCache.set(cFormat, actions); + cFormat.replaceAll( + /(d+)|(m+)|(y+)|(H+)|(M+)|(s+)/g, + function (_match, d, m, y, H, M, s) { + if (d) { + actions.push((n, data) => { + if (n >= 1 && n <= 31) { + data.day = n; + return true; + } + return false; + }); + } else if (m) { + actions.push((n, data) => { + if (n >= 1 && n <= 12) { + data.month = n - 1; + return true; + } + return false; + }); + } else if (y) { + actions.push((n, data) => { + if (n < 50) { + n += 2000; + } else if (n < 100) { + n += 1900; + } + data.year = n; + return true; + }); + } else if (H) { + actions.push((n, data) => { + if (n >= 0 && n <= 23) { + data.hours = n; + return true; + } + return false; + }); + } else if (M) { + actions.push((n, data) => { + if (n >= 0 && n <= 59) { + data.minutes = n; + return true; + } + return false; + }); + } else if (s) { + actions.push((n, data) => { + if (n >= 0 && n <= 59) { + data.seconds = n; + return true; + } + return false; + }); + } + return ""; + } + ); + } + + const number = /\d+/g; + let i = 0; + let array; + const data = { + year: new Date().getFullYear(), + month: 0, + day: 1, + hours: 12, + minutes: 0, + seconds: 0, + }; + while ((array = number.exec(cDate)) !== null) { + if (i < actions.length) { + if (!actions[i++](parseInt(array[0]), data)) { + return null; + } + } else { + break; + } + } + + if (i === 0) { + return null; + } + + return new Date( + data.year, + data.month, + data.day, + data.hours, + data.minutes, + data.seconds + ); + } + scand(cFormat, cDate) { + return this._scand(cFormat, cDate); + } + + _scand(cFormat, cDate, strict = false) { if (typeof cDate !== "string") { return new Date(cDate); } @@ -508,14 +615,14 @@ class Util extends PDFObject { const matches = new RegExp(`^${re}$`, "g").exec(cDate); if (!matches || matches.length !== actions.length + 1) { - return null; + return strict ? null : this.#tryToGuessDate(cFormat, cDate); } const data = { - year: 2000, + year: new Date().getFullYear(), month: 0, day: 1, - hours: 0, + hours: 12, minutes: 0, seconds: 0, am: null, diff --git a/src/shared/image_utils.js b/src/shared/image_utils.js index e2e5c04e63203..b88342ccbfc31 100644 --- a/src/shared/image_utils.js +++ b/src/shared/image_utils.js @@ -77,7 +77,8 @@ function convertRGBToRGBA({ height, }) { let i = 0; - const len32 = src.length >> 2; + const len = width * height * 3; + const len32 = len >> 2; const src32 = new Uint32Array(src.buffer, srcPos, len32); if (FeatureTest.isLittleEndian) { @@ -94,7 +95,7 @@ function convertRGBToRGBA({ dest[destPos + 3] = (s3 >>> 8) | 0xff000000; } - for (let j = i * 4, jj = src.length; j < jj; j += 3) { + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { dest[destPos++] = src[j] | (src[j + 1] << 8) | (src[j + 2] << 16) | 0xff000000; } @@ -110,13 +111,13 @@ function convertRGBToRGBA({ dest[destPos + 3] = (s3 << 8) | 0xff; } - for (let j = i * 4, jj = src.length; j < jj; j += 3) { + for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { dest[destPos++] = (src[j] << 24) | (src[j + 1] << 16) | (src[j + 2] << 8) | 0xff; } } - return { srcPos, destPos }; + return { srcPos: srcPos + len, destPos }; } function grayToRGBA(src, dest) { diff --git a/src/shared/message_handler.js b/src/shared/message_handler.js index 6d74a01fd671d..a1af0aab27b54 100644 --- a/src/shared/message_handler.js +++ b/src/shared/message_handler.js @@ -16,21 +16,19 @@ import { AbortException, assert, - MissingPDFException, + InvalidPDFException, PasswordException, - UnexpectedResponseException, + ResponseException, UnknownErrorException, unreachable, } from "./util.js"; const CallbackKind = { - UNKNOWN: 0, DATA: 1, ERROR: 2, }; const StreamKind = { - UNKNOWN: 0, CANCEL: 1, CANCEL_COMPLETE: 2, CLOSE: 3, @@ -41,31 +39,38 @@ const StreamKind = { START_COMPLETE: 8, }; -function wrapReason(reason) { +function onFn() {} + +function wrapReason(ex) { if ( - !( - reason instanceof Error || - (typeof reason === "object" && reason !== null) - ) + ex instanceof AbortException || + ex instanceof InvalidPDFException || + ex instanceof PasswordException || + ex instanceof ResponseException || + ex instanceof UnknownErrorException ) { + // Avoid re-creating the exception when its type is already correct. + return ex; + } + + if (!(ex instanceof Error || (typeof ex === "object" && ex !== null))) { unreachable( 'wrapReason: Expected "reason" to be a (possibly cloned) Error.' ); } - switch (reason.name) { + switch (ex.name) { case "AbortException": - return new AbortException(reason.message); - case "MissingPDFException": - return new MissingPDFException(reason.message); + return new AbortException(ex.message); + case "InvalidPDFException": + return new InvalidPDFException(ex.message); case "PasswordException": - return new PasswordException(reason.message, reason.code); - case "UnexpectedResponseException": - return new UnexpectedResponseException(reason.message, reason.status); + return new PasswordException(ex.message, ex.code); + case "ResponseException": + return new ResponseException(ex.message, ex.status, ex.missing); case "UnknownErrorException": - return new UnknownErrorException(reason.message, reason.details); - default: - return new UnknownErrorException(reason.message, reason.toString()); + return new UnknownErrorException(ex.message, ex.details); } + return new UnknownErrorException(ex.message, ex.toString()); } class MessageHandler { @@ -121,9 +126,7 @@ class MessageHandler { targetName = data.sourceName, comObj = this.comObj; - new Promise(function (resolve) { - resolve(action(data.data)); - }).then( + Promise.try(action, data.data).then( function (result) { comObj.postMessage({ sourceName, @@ -365,9 +368,7 @@ class MessageHandler { streamSink.ready = streamSink.sinkCapability.promise; this.streamSinks[streamId] = streamSink; - new Promise(function (resolve) { - resolve(action(data.data, streamSink)); - }).then( + Promise.try(action, data.data, streamSink).then( function () { comObj.postMessage({ sourceName, @@ -432,9 +433,7 @@ class MessageHandler { // Reset desiredSize property of sink on every pull. streamSink.desiredSize = data.desiredSize; - new Promise(function (resolve) { - resolve(streamSink.onPull?.()); - }).then( + Promise.try(streamSink.onPull || onFn).then( function () { comObj.postMessage({ sourceName, @@ -488,10 +487,9 @@ class MessageHandler { if (!streamSink) { break; } + const dataReason = wrapReason(data.reason); - new Promise(function (resolve) { - resolve(streamSink.onCancel?.(wrapReason(data.reason))); - }).then( + Promise.try(streamSink.onCancel || onFn, dataReason).then( function () { comObj.postMessage({ sourceName, @@ -511,7 +509,7 @@ class MessageHandler { }); } ); - streamSink.sinkCapability.reject(wrapReason(data.reason)); + streamSink.sinkCapability.reject(dataReason); streamSink.isCancelled = true; delete this.streamSinks[streamId]; break; @@ -537,4 +535,4 @@ class MessageHandler { } } -export { MessageHandler }; +export { MessageHandler, wrapReason }; diff --git a/src/shared/util.js b/src/shared/util.js index eccd578974b92..5775c0d4bfb6e 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -28,8 +28,6 @@ const isNodeJS = const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; -const MAX_IMAGE_SIZE_TO_CACHE = 10e6; // Ten megabytes. - // Represent the percentage of the height of a single-line field over // the font size. Acrobat seems to use this value. const LINE_FACTOR = 1.35; @@ -78,6 +76,7 @@ const AnnotationEditorType = { HIGHLIGHT: 9, STAMP: 13, INK: 15, + SIGNATURE: 101, }; const AnnotationEditorParamsType = { @@ -94,6 +93,7 @@ const AnnotationEditorParamsType = { HIGHLIGHT_THICKNESS: 33, HIGHLIGHT_FREE: 34, HIGHLIGHT_SHOW_ALL: 35, + DRAW_STEP: 41, }; // Permission flags from Table 22, Section 7.6.3.2 of the PDF specification. @@ -363,6 +363,7 @@ function getVerbosityLevel() { // end users. function info(msg) { if (verbosity >= VerbosityLevel.INFOS) { + // eslint-disable-next-line no-console console.log(`Info: ${msg}`); } } @@ -370,6 +371,7 @@ function info(msg) { // Non-fatal warnings. function warn(msg) { if (verbosity >= VerbosityLevel.WARNINGS) { + // eslint-disable-next-line no-console console.log(`Warning: ${msg}`); } } @@ -410,35 +412,28 @@ function createValidAbsoluteUrl(url, baseUrl = null, options = null) { if (!url) { return null; } - try { - if (options && typeof url === "string") { - // Let URLs beginning with "www." default to using the "http://" protocol. - if (options.addDefaultProtocol && url.startsWith("www.")) { - const dots = url.match(/\./g); - // Avoid accidentally matching a *relative* URL pointing to a file named - // e.g. "www.pdf" or similar. - if (dots?.length >= 2) { - url = `http://${url}`; - } - } - - // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded - // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding; see bug 1122280. - if (options.tryConvertEncoding) { - try { - url = stringToUTF8String(url); - } catch {} + if (options && typeof url === "string") { + // Let URLs beginning with "www." default to using the "http://" protocol. + if (options.addDefaultProtocol && url.startsWith("www.")) { + const dots = url.match(/\./g); + // Avoid accidentally matching a *relative* URL pointing to a file named + // e.g. "www.pdf" or similar. + if (dots?.length >= 2) { + url = `http://${url}`; } } - const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); - if (_isValidProtocol(absoluteUrl)) { - return absoluteUrl; + // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded + // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding; see bug 1122280. + if (options.tryConvertEncoding) { + try { + url = stringToUTF8String(url); + } catch {} } - } catch { - /* `new URL()` will throw on incorrect data. */ } - return null; + + const absoluteUrl = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); + return _isValidProtocol(absoluteUrl) ? absoluteUrl : null; } function shadow(obj, prop, value, nonSerializable = false) { @@ -498,16 +493,11 @@ class InvalidPDFException extends BaseException { } } -class MissingPDFException extends BaseException { - constructor(msg) { - super(msg, "MissingPDFException"); - } -} - -class UnexpectedResponseException extends BaseException { - constructor(msg, status) { - super(msg, "UnexpectedResponseException"); +class ResponseException extends BaseException { + constructor(msg, status, missing) { + super(msg, "ResponseException"); this.status = status; + this.missing = missing; } } @@ -623,22 +613,36 @@ class FeatureTest { ); } + static get isImageDecoderSupported() { + return shadow( + this, + "isImageDecoderSupported", + typeof ImageDecoder !== "undefined" + ); + } + static get platform() { if ( (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || (typeof navigator !== "undefined" && - typeof navigator?.platform === "string") + typeof navigator?.platform === "string" && + typeof navigator?.userAgent === "string") ) { + const { platform, userAgent } = navigator; + return shadow(this, "platform", { - isMac: navigator.platform.includes("Mac"), - isWindows: navigator.platform.includes("Win"), + isAndroid: userAgent.includes("Android"), + isLinux: platform.includes("Linux"), + isMac: platform.includes("Mac"), + isWindows: platform.includes("Win"), isFirefox: (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || - (typeof navigator?.userAgent === "string" && - navigator.userAgent.includes("Firefox")), + userAgent.includes("Firefox"), }); } return shadow(this, "platform", { + isAndroid: false, + isLinux: false, isMac: false, isWindows: false, isFirefox: false, @@ -1065,39 +1069,105 @@ function normalizeUnicode(str) { function getUuid() { if ( (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || - (typeof crypto !== "undefined" && typeof crypto?.randomUUID === "function") + typeof crypto.randomUUID === "function" ) { return crypto.randomUUID(); } const buf = new Uint8Array(32); - if ( - typeof crypto !== "undefined" && - typeof crypto?.getRandomValues === "function" - ) { - crypto.getRandomValues(buf); - } else { - for (let i = 0; i < 32; i++) { - buf[i] = Math.floor(Math.random() * 255); - } - } + crypto.getRandomValues(buf); return bytesToString(buf); } const AnnotationPrefix = "pdfjs_internal_id_"; -const FontRenderOps = { - BEZIER_CURVE_TO: 0, - MOVE_TO: 1, - LINE_TO: 2, - QUADRATIC_CURVE_TO: 3, - RESTORE: 4, - SAVE: 5, - SCALE: 6, - TRANSFORM: 7, - TRANSLATE: 8, -}; +function _isValidExplicitDest(validRef, validName, dest) { + if (!Array.isArray(dest) || dest.length < 2) { + return false; + } + const [page, zoom, ...args] = dest; + if (!validRef(page) && !Number.isInteger(page)) { + return false; + } + if (!validName(zoom)) { + return false; + } + const argsLen = args.length; + let allowNull = true; + switch (zoom.name) { + case "XYZ": + if (argsLen < 2 || argsLen > 3) { + return false; + } + break; + case "Fit": + case "FitB": + return argsLen === 0; + case "FitH": + case "FitBH": + case "FitV": + case "FitBV": + if (argsLen > 1) { + return false; + } + break; + case "FitR": + if (argsLen !== 4) { + return false; + } + allowNull = false; + break; + default: + return false; + } + for (const arg of args) { + if (typeof arg === "number" || (allowNull && arg === null)) { + continue; + } + return false; + } + return true; +} + +// TODO: Remove this once `Uint8Array.prototype.toHex` is generally available. +function toHexUtil(arr) { + if (Uint8Array.prototype.toHex) { + return arr.toHex(); + } + return Array.from(arr, num => hexNumbers[num]).join(""); +} + +// TODO: Remove this once `Uint8Array.prototype.toBase64` is generally +// available. +function toBase64Util(arr) { + if (Uint8Array.prototype.toBase64) { + return arr.toBase64(); + } + return btoa(bytesToString(arr)); +} + +// TODO: Remove this once `Uint8Array.fromBase64` is generally available. +function fromBase64Util(str) { + if (Uint8Array.fromBase64) { + return Uint8Array.fromBase64(str); + } + return stringToBytes(atob(str)); +} + +// TODO: Remove this once https://bugzilla.mozilla.org/show_bug.cgi?id=1928493 +// is fixed. +if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("SKIP_BABEL")) && + typeof Promise.try !== "function" +) { + Promise.try = function (fn, ...args) { + return new Promise(resolve => { + resolve(fn(...args)); + }); + }; +} export { + _isValidExplicitDest, AbortException, AnnotationActionEventType, AnnotationBorderStyleType, @@ -1118,11 +1188,12 @@ export { DocumentActionEventType, FeatureTest, FONT_IDENTITY_MATRIX, - FontRenderOps, FormatError, + fromBase64Util, getModificationDate, getUuid, getVerbosityLevel, + hexNumbers, IDENTITY_MATRIX, ImageKind, info, @@ -1131,8 +1202,6 @@ export { isNodeJS, LINE_DESCENT_FACTOR, LINE_FACTOR, - MAX_IMAGE_SIZE_TO_CACHE, - MissingPDFException, normalizeUnicode, objectFromMap, objectSize, @@ -1142,6 +1211,7 @@ export { PasswordResponses, PermissionFlag, RenderingIntentFlag, + ResponseException, setVerbosityLevel, shadow, string32, @@ -1149,7 +1219,8 @@ export { stringToPDFString, stringToUTF8String, TextRenderingMode, - UnexpectedResponseException, + toBase64Util, + toHexUtil, UnknownErrorException, unreachable, utf8StringToString, diff --git a/test/.eslintrc b/test/.eslintrc deleted file mode 100644 index 4f371e4248581..0000000000000 --- a/test/.eslintrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "plugins": ["jasmine"], - "extends": ["../.eslintrc", "plugin:jasmine/recommended"], - "env": { - "node": true, - "jasmine": true - }, - "rules": { - "jasmine/new-line-before-expect": "off", - "jasmine/new-line-between-declarations": "off", - "jasmine/no-focused-tests": "error", - "jasmine/no-pending-tests": "off", - "jasmine/no-spec-dupes": ["error", "branch"], - "jasmine/no-suite-dupes": ["error", "branch"], - "jasmine/prefer-jasmine-matcher": "off", - "jasmine/prefer-toHaveBeenCalledWith": "off" - } -} diff --git a/test/downloadutils.mjs b/test/downloadutils.mjs index 682e916e320f7..aee1f4ae709b2 100644 --- a/test/downloadutils.mjs +++ b/test/downloadutils.mjs @@ -42,6 +42,7 @@ function downloadFile(file, url, redirects = 0) { .get(url, async function (response) { if ([301, 302, 307, 308].includes(response.statusCode)) { if (redirects > 10) { + response.resume(); reject(new Error("Too many redirects")); return; } @@ -50,12 +51,14 @@ function downloadFile(file, url, redirects = 0) { await downloadFile(file, redirectTo, ++redirects); resolve(); } catch (ex) { + response.resume(); reject(ex); } return; } if (response.statusCode !== 200) { + response.resume(); reject(new Error(`HTTP ${response.statusCode}`)); return; } @@ -86,9 +89,9 @@ async function downloadManifestFiles(manifest) { try { await downloadFile(file, url); } catch (ex) { - console.error(`Error during downloading of ${url}: ${ex}`); + console.error(`Error during downloading of ${url}:`, ex); fs.writeFileSync(file, ""); // making it empty file - fs.writeFileSync(`${file}.error`, ex); + fs.writeFileSync(`${file}.error`, ex.toString()); } } } diff --git a/test/driver.js b/test/driver.js index 8ba30b0bfd3ce..c02c455cb9cd4 100644 --- a/test/driver.js +++ b/test/driver.js @@ -25,12 +25,13 @@ const { TextLayer, XfaLayer, } = pdfjsLib; -const { Outliner } = pdfjsTestingUtils; +const { HighlightOutliner } = pdfjsTestingUtils; const { GenericL10n, parseQueryString, SimpleLinkService } = pdfjsViewer; const WAITING_TIME = 100; // ms const CMAP_URL = "/build/generic/web/cmaps/"; const STANDARD_FONT_DATA_URL = "/build/generic/web/standard_fonts/"; +const WASM_URL = "/build/generic/web/wasm/"; const IMAGE_RESOURCES_PATH = "/web/images/"; const VIEWER_CSS = "../build/components/pdf_viewer.css"; const VIEWER_LOCALE = "en-US"; @@ -105,33 +106,33 @@ async function inlineImages(node, silentErrors = false) { } return response.blob(); }) - // eslint-disable-next-line arrow-body-style - .then(blob => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - resolve(reader.result); - }; - reader.onerror = reject; - - reader.readAsDataURL(blob); - }); - }) - // eslint-disable-next-line arrow-body-style - .then(dataUrl => { - return new Promise((resolve, reject) => { - image.onload = resolve; - image.onerror = evt => { - if (silentErrors) { - resolve(); - return; - } - reject(evt); - }; + .then( + blob => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + resolve(reader.result); + }; + reader.onerror = reject; + + reader.readAsDataURL(blob); + }) + ) + .then( + dataUrl => + new Promise((resolve, reject) => { + image.onload = resolve; + image.onerror = evt => { + if (silentErrors) { + resolve(); + return; + } + reject(evt); + }; - image.src = dataUrl; - }); - }) + image.src = dataUrl; + }) + ) .catch(reason => { throw new Error(`Error inlining image (${url}): ${reason}`); }) @@ -214,6 +215,18 @@ class Rasterize { return { svg, foreignObject, style, div }; } + static createRootCSS(viewport) { + const { scale, userUnit } = viewport; + return [ + ":root {", + " --scale-round-x: 1px; --scale-round-y: 1px;", + ` --scale-factor: ${scale};`, + ` --user-unit: ${userUnit};`, + ` --total-scale-factor: ${scale * userUnit};`, + "}", + ].join("\n"); + } + static async annotationLayer( ctx, viewport, @@ -231,9 +244,7 @@ class Rasterize { div.className = "annotationLayer"; const [common, overrides] = await this.annotationStylePromise; - style.textContent = - `${common}\n${overrides}\n` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; const annotationViewport = viewport.clone({ dontFlip: true }); const annotationImageMap = await convertCanvasesToImages( @@ -292,9 +303,7 @@ class Rasterize { svg.setAttribute("font-size", 1); const [common, overrides] = await this.textStylePromise; - style.textContent = - `${common}\n${overrides}\n` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; // Rendering text layer as HTML. const textLayer = new TextLayer({ @@ -321,9 +330,7 @@ class Rasterize { svg.setAttribute("font-size", 1); const [common, overrides] = await this.drawLayerStylePromise; - style.textContent = - `${common}\n${overrides}` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; // Rendering text layer as HTML. const textLayer = new TextLayer({ @@ -345,9 +352,9 @@ class Rasterize { let x = parseFloat(left) / 100; let y = parseFloat(top) / 100; if (isNaN(x)) { - posRegex ||= /^calc\(var\(--scale-factor\)\*(.*)px\)$/; + posRegex ||= /^calc\(var\(--total-scale-factor\)\s*\*(.*)px\)$/; // The element is tagged so we've to extract the position from the - // string, e.g. `calc(var(--scale-factor)*66.32px)`. + // string, e.g. `calc(var(--total-scale-factor)*66.32px)`. let match = left.match(posRegex); if (match) { x = parseFloat(match[1]) / pageWidth; @@ -370,19 +377,51 @@ class Rasterize { } // We set the borderWidth to 0.001 to slighly increase the size of the // boxes so that they can be merged together. - const outliner = new Outliner(boxes, /* borderWidth = */ 0.001); + const outliner = new HighlightOutliner(boxes, /* borderWidth = */ 0.001); // We set the borderWidth to 0.0025 in order to have an outline which is // slightly bigger than the highlight itself. // We must add an inner margin to avoid to have a partial outline. - const outlinerForOutline = new Outliner( + const outlinerForOutline = new HighlightOutliner( boxes, /* borderWidth = */ 0.0025, /* innerMargin = */ 0.001 ); const drawLayer = new DrawLayer({ pageIndex: 0 }); drawLayer.setParent(div); - drawLayer.highlight(outliner.getOutlines(), "orange", 0.4); - drawLayer.highlightOutline(outlinerForOutline.getOutlines()); + const outlines = outliner.getOutlines(); + drawLayer.draw( + { + bbox: outlines.box, + root: { + viewBox: "0 0 1 1", + fill: "orange", + "fill-opacity": 0.4, + }, + rootClass: { + highlight: true, + free: false, + }, + path: { + d: outlines.toSVGPath(), + }, + }, + /* isPathUpdatable = */ false, + /* hasClip = */ true + ); + const focusLine = outlinerForOutline.getOutlines(); + drawLayer.drawOutline( + { + rootClass: { + highlightOutline: true, + free: false, + }, + bbox: focusLine.box, + path: { + d: focusLine.toSVGPath(), + }, + }, + /* mustRemoveSelfIntersections = */ false + ); svg.append(foreignObject); @@ -592,25 +631,29 @@ class Driver { } const isOffscreenCanvasSupported = task.isOffscreenCanvasSupported === false ? false : undefined; + const disableFontFace = task.disableFontFace === true; const loadingTask = getDocument({ url: new URL(task.file, window.location), password: task.password, cMapUrl: CMAP_URL, standardFontDataUrl: STANDARD_FONT_DATA_URL, + wasmUrl: WASM_URL, disableAutoFetch: !task.enableAutoFetch, pdfBug: true, useSystemFonts: task.useSystemFonts, + useWasm: task.useWasm, useWorkerFetch: task.useWorkerFetch, enableXfa: task.enableXfa, isOffscreenCanvasSupported, styleElement: xfaStyleElement, + disableFontFace, }); let promise = loadingTask.promise; if (task.annotationStorage) { for (const annotation of Object.values(task.annotationStorage)) { - const { bitmapName, quadPoints } = annotation; + const { bitmapName, quadPoints, paths, outlines } = annotation; if (bitmapName) { promise = promise.then(async doc => { const response = await fetch( @@ -648,6 +691,36 @@ class Driver { // like IRL (in order to avoid bugs like bug 1907958). annotation.quadPoints = new Float32Array(quadPoints); } + if (paths) { + for (let i = 0, ii = paths.lines.length; i < ii; i++) { + paths.lines[i] = Float32Array.from( + paths.lines[i], + x => x ?? NaN + ); + } + for (let i = 0, ii = paths.points.length; i < ii; i++) { + paths.points[i] = Float32Array.from( + paths.points[i], + x => x ?? NaN + ); + } + } + if (outlines) { + if (Array.isArray(outlines)) { + for (let i = 0, ii = outlines.length; i < ii; i++) { + outlines[i] = Float32Array.from(outlines[i], x => x ?? NaN); + } + } else { + outlines.outline = Float32Array.from( + outlines.outline, + x => x ?? NaN + ); + outlines.points = Float32Array.from( + outlines.points, + x => x ?? NaN + ); + } + } } } @@ -1130,7 +1203,7 @@ class Driver { resolve(); }) .catch(reason => { - console.warn(`Driver._send failed (${url}): ${reason}`); + console.warn(`Driver._send failed (${url}):`, reason); this.inFlightRequests--; resolve(); diff --git a/test/font/README.md b/test/font/README.md index c3f31cc9e52f2..ec0bfd5ba3fa4 100644 --- a/test/font/README.md +++ b/test/font/README.md @@ -32,5 +32,5 @@ it before running the font tests: python3 -m venv venv source venv/bin/activate pip install fonttools -gulp fonttest +npx gulp fonttest ``` diff --git a/test/font/font_fpgm_spec.js b/test/font/font_fpgm_spec.js index 66ca8752bac49..45501608e9759 100644 --- a/test/font/font_fpgm_spec.js +++ b/test/font/font_fpgm_spec.js @@ -16,17 +16,22 @@ describe("font_fpgm", function () { const cMap = await CMapFactory.create({ encoding: Name.get("Identity-H"), }); - const font = new Font("font", new Stream(font2324), { - loadedName: "font", - type: "CIDFontType2", - differences: [], - defaultEncoding: [], - cMap, - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font2324), + { + loadedName: "font", + type: "CIDFontType2", + differences: [], + defaultEncoding: [], + cMap, + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); diff --git a/test/font/font_os2_spec.js b/test/font/font_os2_spec.js index 04359d316067d..d348a6b4ca3ac 100644 --- a/test/font/font_os2_spec.js +++ b/test/font/font_os2_spec.js @@ -17,16 +17,21 @@ describe("font_post", function () { describe("OS/2 table removal on bad post table values", function () { it("has invalid version number", async function () { - const font = new Font("font", new Stream(font2154), { - loadedName: "font", - type: "TrueType", - differences: [], - defaultEncoding: [], - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font2154), + { + loadedName: "font", + type: "TrueType", + differences: [], + defaultEncoding: [], + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); @@ -39,17 +44,22 @@ describe("font_post", function () { const cMap = await CMapFactory.create({ encoding: Name.get("Identity-H"), }); - const font = new Font("font", new Stream(font1282), { - loadedName: "font", - type: "CIDFontType2", - differences: [], - defaultEncoding: [], - cMap, - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font1282), + { + loadedName: "font", + type: "CIDFontType2", + differences: [], + defaultEncoding: [], + cMap, + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); diff --git a/test/font/font_post_spec.js b/test/font/font_post_spec.js index d00ddcdbe615f..116d7716951d0 100644 --- a/test/font/font_post_spec.js +++ b/test/font/font_post_spec.js @@ -24,17 +24,22 @@ describe("font_post", function () { const cMap = await CMapFactory.create({ encoding: Name.get("Identity-H"), }); - const font = new Font("font", new Stream(font2109), { - loadedName: "font", - type: "CIDFontType2", - differences: [], - defaultEncoding: [], - cMap, - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font2109), + { + loadedName: "font", + type: "CIDFontType2", + differences: [], + defaultEncoding: [], + cMap, + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); @@ -42,16 +47,21 @@ describe("font_post", function () { }); it("has invalid glyph name indexes", async function () { - const font = new Font("font", new Stream(font2189), { - loadedName: "font", - type: "TrueType", - differences: [], - defaultEncoding: [], - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font2189), + { + loadedName: "font", + type: "TrueType", + differences: [], + defaultEncoding: [], + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); @@ -59,16 +69,21 @@ describe("font_post", function () { }); it("has right amount of glyphs specified", async function () { - const font = new Font("font", new Stream(font2374), { - loadedName: "font", - type: "TrueType", - differences: [], - defaultEncoding: [], - toUnicode: new ToUnicodeMap([]), - xHeight: 0, - capHeight: 0, - italicAngle: 0, - }); + const font = new Font( + "font", + new Stream(font2374), + { + loadedName: "font", + type: "TrueType", + differences: [], + defaultEncoding: [], + toUnicode: new ToUnicodeMap([]), + xHeight: 0, + capHeight: 0, + italicAngle: 0, + }, + {} + ); const output = await ttx(font.data); verifyTtxOutput(output); diff --git a/test/font/font_test.html b/test/font/font_test.html index dd361c6990b41..c8b09b700e9c6 100644 --- a/test/font/font_test.html +++ b/test/font/font_test.html @@ -1,7 +1,7 @@ - pdf.js unit test + PDF.js font tests diff --git a/test/font/jasmine-boot.js b/test/font/jasmine-boot.js index e9646db4242de..71e1ba4962792 100644 --- a/test/font/jasmine-boot.js +++ b/test/font/jasmine-boot.js @@ -40,7 +40,7 @@ "use strict"; -import { TestReporter } from "../unit/testreporter.js"; +import { TestReporter } from "../reporter.js"; async function initializePDFJS(callback) { await Promise.all( @@ -49,10 +49,7 @@ async function initializePDFJS(callback) { "pdfjs-test/font/font_os2_spec.js", "pdfjs-test/font/font_post_spec.js", "pdfjs-test/font/font_fpgm_spec.js", - ].map(function (moduleName) { - // eslint-disable-next-line no-unsanitized/method - return import(moduleName); - }) + ].map(moduleName => import(moduleName)) // eslint-disable-line no-unsanitized/method ); callback(); diff --git a/test/fuzz/.eslintrc b/test/fuzz/.eslintrc deleted file mode 100644 index 7d611b5c61737..0000000000000 --- a/test/fuzz/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "rules": { - "import/no-unresolved": ["error", { "ignore": [".*/build/image_decoders/.*"] }], - }, -} diff --git a/test/fuzz/README.md b/test/fuzz/README.md deleted file mode 100644 index ef931492a8cd0..0000000000000 --- a/test/fuzz/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Fuzz Testing - -Fuzz testing is: - -> An automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a program. - -We use coverage guided fuzz testing to automatically discover bugs in PDF.js. - -This `fuzz/` directory contains the configuration and the fuzz tests for PDF.js. -To generate and run fuzz tests, we use the [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js/) library. - -## Running a fuzzer - -This directory contains fuzzers like for example `jpeg_image.fuzz`. You can run it with: - -Generate image decoders: -```sh -$ gulp image_decoders -``` - -Run fuzz target: -```sh -$ npx jazzer fuzz/jpeg_image.fuzz --sync -``` - -You should see output that looks something like this: - -``` -#2 INITED exec/s: 0 rss: 128Mb -#65536 pulse corp: 1/1b lim: 652 exec/s: 32768 rss: 140Mb -#131072 pulse corp: 1/1b lim: 1300 exec/s: 32768 rss: 140Mb -#262144 pulse corp: 1/1b lim: 2611 exec/s: 32768 rss: 140Mb -#524288 pulse corp: 1/1b lim: 4096 exec/s: 30840 rss: 140Mb -#1048576 pulse corp: 1/1b lim: 4096 exec/s: 29959 rss: 140Mb -#2097152 pulse corp: 1/1b lim: 4096 exec/s: 29537 rss: 140Mb -``` - -It will continue to generate random inputs forever, until it finds a -bug or is terminated. The testcases for bugs it finds can be seen in -the form of `crash-*` or `timeout-*` at the place from where command is run. -You can rerun the fuzzer on a single input by passing it on the -command line `npx jazzer fuzz/jpeg_image.fuzz /path/to/testcase`. diff --git a/test/fuzz/jbig2_image.fuzz.js b/test/fuzz/jbig2_image.fuzz.js deleted file mode 100644 index edf1c1d3dd577..0000000000000 --- a/test/fuzz/jbig2_image.fuzz.js +++ /dev/null @@ -1,33 +0,0 @@ -import { - Jbig2Error, - Jbig2Image, - setVerbosityLevel, - VerbosityLevel, -} from "../../build/image_decoders/pdf.image_decoders.mjs"; - -// Avoid unnecessary console "spam", by ignoring `info`/`warn` calls. -setVerbosityLevel(VerbosityLevel.ERRORS); - -const ignored = ["Cannot read properties"]; - -function ignoredError(error) { - if (error instanceof Jbig2Error) { - return true; - } - return ignored.some(message => error.message.includes(message)); -} - -/** - * @param {Buffer} data - */ -function fuzz(data) { - try { - new Jbig2Image().parse(new Uint8Array(data)); - } catch (error) { - if (error.message && !ignoredError(error)) { - throw error; - } - } -} - -export { fuzz }; diff --git a/test/fuzz/jpeg_image.fuzz.js b/test/fuzz/jpeg_image.fuzz.js deleted file mode 100644 index 8f4a0075a8c21..0000000000000 --- a/test/fuzz/jpeg_image.fuzz.js +++ /dev/null @@ -1,33 +0,0 @@ -import { - JpegError, - JpegImage, - setVerbosityLevel, - VerbosityLevel, -} from "../../build/image_decoders/pdf.image_decoders.mjs"; - -// Avoid unnecessary console "spam", by ignoring `info`/`warn` calls. -setVerbosityLevel(VerbosityLevel.ERRORS); - -const ignored = ["Cannot read properties"]; - -function ignoredError(error) { - if (error instanceof JpegError) { - return true; - } - return ignored.some(message => error.message.includes(message)); -} - -/** - * @param {Buffer} data - */ -function fuzz(data) { - try { - new JpegImage().parse(new Uint8Array(data)); - } catch (error) { - if (error.message && !ignoredError(error)) { - throw error; - } - } -} - -export { fuzz }; diff --git a/test/fuzz/jpx_image.fuzz.js b/test/fuzz/jpx_image.fuzz.js deleted file mode 100644 index 0063580ab49d9..0000000000000 --- a/test/fuzz/jpx_image.fuzz.js +++ /dev/null @@ -1,33 +0,0 @@ -import { - JpxError, - JpxImage, - setVerbosityLevel, - VerbosityLevel, -} from "../../build/image_decoders/pdf.image_decoders.mjs"; - -// Avoid unnecessary console "spam", by ignoring `info`/`warn` calls. -setVerbosityLevel(VerbosityLevel.ERRORS); - -const ignored = ["Cannot read properties"]; - -function ignoredError(error) { - if (error instanceof JpxError) { - return true; - } - return ignored.some(message => error.message.includes(message)); -} - -/** - * @param {Buffer} data - */ -function fuzz(data) { - try { - JpxImage.decode(new Uint8Array(data)); - } catch (error) { - if (error.message && !ignoredError(error)) { - throw error; - } - } -} - -export { fuzz }; diff --git a/test/integration/.eslintrc b/test/integration/.eslintrc deleted file mode 100644 index 8aedbe37f89f8..0000000000000 --- a/test/integration/.eslintrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "rules": { - "no-restricted-syntax": ["error", - { - "selector": "CallExpression[callee.name='waitForTimeout']", - "message": "`waitForTimeout` can cause intermittent failures and should not be used (see issue #17656 for replacements).", - }, - ], - }, -} diff --git a/test/integration/annotation_spec.mjs b/test/integration/annotation_spec.mjs index 52e33698d9c99..4e444f5a452e0 100644 --- a/test/integration/annotation_spec.mjs +++ b/test/integration/annotation_spec.mjs @@ -16,6 +16,7 @@ import { closePages, getQuerySelector, + getRect, getSelector, loadAndWait, } from "./test_utils.mjs"; @@ -503,14 +504,6 @@ describe("ResetForm action", () => { it("must check that the Ink annotation has a popup", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - if (browserName) { - // TODO - pending( - "Re-enable this test when the Ink annotation has been made editable." - ); - return; - } - await page.waitForFunction( `document.querySelector("[data-annotation-id='25R']").hidden === false` ); @@ -654,4 +647,38 @@ describe("ResetForm action", () => { }); }); }); + + describe("Rotated annotation and its clickable area", () => { + describe("issue14438.pdf", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "rotated_ink.pdf", + "[data-annotation-id='18R']" + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that the clickable area has been rotated", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const rect = await getRect(page, "[data-annotation-id='18R']"); + const promisePopup = page.waitForSelector( + "[data-annotation-id='19R']", + { visible: true } + ); + await page.mouse.move( + rect.x + rect.width * 0.1, + rect.y + rect.height * 0.9 + ); + await promisePopup; + }) + ); + }); + }); + }); }); diff --git a/test/integration/autolinker_spec.mjs b/test/integration/autolinker_spec.mjs new file mode 100644 index 0000000000000..a7cbecd29c5f4 --- /dev/null +++ b/test/integration/autolinker_spec.mjs @@ -0,0 +1,262 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + awaitPromise, + closePages, + createPromise, + loadAndWait, +} from "./test_utils.mjs"; + +function waitForLinkAnnotations(page, pageNumber) { + return page.evaluateHandle( + number => [ + new Promise(resolve => { + const { eventBus } = window.PDFViewerApplication; + eventBus.on("linkannotationsadded", function listener(e) { + if (number === undefined || e.pageNumber === number) { + resolve(); + eventBus.off("linkannotationsadded", listener); + } + }); + }), + ], + pageNumber + ); +} + +function recordInitialLinkAnnotationsEvent(eventBus) { + globalThis.initialLinkAnnotationsEventFired = false; + eventBus.on( + "linkannotationsadded", + () => { + globalThis.initialLinkAnnotationsEventFired = true; + }, + { once: true } + ); +} +function waitForInitialLinkAnnotations(page) { + return createPromise(page, resolve => { + if (globalThis.initialLinkAnnotationsEventFired) { + resolve(); + return; + } + window.PDFViewerApplication.eventBus.on("linkannotationsadded", resolve, { + once: true, + }); + }); +} + +describe("autolinker", function () { + describe("bug1019475_2.pdf", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "bug1019475_2.pdf", + ".annotationLayer", + null, + null, + { + enableAutoLinking: true, + } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must appropriately add link annotations when relevant", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); + const url = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + expect(url.length).withContext(`In ${browserName}`).toEqual(1); + expect(url[0]) + .withContext(`In ${browserName}`) + .toEqual("http://www.mozilla.org/"); + }) + ); + }); + }); + + describe("bug1019475_1.pdf", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "bug1019475_1.pdf", + ".annotationLayer", + null, + null, + { + enableAutoLinking: true, + } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must not add links when unnecessary", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); + const linkIds = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => + annotations.map(a => a.getAttribute("data-element-id")) + ); + expect(linkIds.length).withContext(`In ${browserName}`).toEqual(3); + linkIds.forEach(id => + expect(id) + .withContext(`In ${browserName}`) + .not.toContain("inferred_link_") + ); + }) + ); + }); + }); + + describe("pr19449.pdf", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("pr19449.pdf", ".annotationLayer", null, null, { + docBaseUrl: "http://example.com", + enableAutoLinking: true, + }); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must not add links that overlap even if the URLs are different", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); + const linkIds = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => + annotations.map(a => a.getAttribute("data-element-id")) + ); + expect(linkIds.length).withContext(`In ${browserName}`).toEqual(1); + linkIds.forEach(id => + expect(id) + .withContext(`In ${browserName}`) + .not.toContain("inferred_link_") + ); + }) + ); + }); + }); + + describe("PR 19470", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "bug1019475_2.pdf", + ".annotationLayer", + null, + null, + { + enableAutoLinking: true, + } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must not repeatedly add link annotations redundantly", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); + let url = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + expect(url.length).withContext(`In ${browserName}`).toEqual(1); + + await page.evaluate(() => + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 2, + }) + ); + await waitForLinkAnnotations(page); + url = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + expect(url.length).withContext(`In ${browserName}`).toEqual(1); + }) + ); + }); + }); + + describe("when highlighting search results", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "issue3115r.pdf", + ".annotationLayer", + null, + { eventBusSetup: recordInitialLinkAnnotationsEvent }, + { enableAutoLinking: true } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must find links that overlap with search results", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await awaitPromise(await waitForInitialLinkAnnotations(page)); + + const linkAnnotationsPromise = await waitForLinkAnnotations(page, 36); + + // Search for "rich.edu" + await page.click("#viewFindButton"); + await page.waitForSelector("#viewFindButton", { hidden: false }); + await page.type("#findInput", "rich.edu"); + await page.waitForSelector(".textLayer .highlight"); + + await awaitPromise(linkAnnotationsPromise); + + const urls = await page.$$eval( + ".page[data-page-number='36'] > .annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + + expect(urls) + .withContext(`In ${browserName}`) + .toContain(jasmine.stringContaining("rich.edu")); + }) + ); + }); + }); +}); diff --git a/test/integration/find_spec.mjs b/test/integration/find_spec.mjs index b73f499aee2cc..e6f05072e6f70 100644 --- a/test/integration/find_spec.mjs +++ b/test/integration/find_spec.mjs @@ -126,4 +126,31 @@ describe("find bar", () => { ); }); }); + + describe("issue19207.pdf", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue19207.pdf", ".textLayer", 200); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must scroll to the search result text", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + // Search for "40" + await page.click("#viewFindButton"); + await page.waitForSelector("#viewFindButton", { hidden: false }); + await page.type("#findInput", "40"); + + const highlight = await page.waitForSelector(".textLayer .highlight"); + + expect(await highlight.isIntersectingViewport()).toBeTrue(); + }) + ); + }); + }); }); diff --git a/test/integration/freetext_editor_spec.mjs b/test/integration/freetext_editor_spec.mjs index 29e89f28ae32e..29c24a4871133 100644 --- a/test/integration/freetext_editor_spec.mjs +++ b/test/integration/freetext_editor_spec.mjs @@ -15,19 +15,20 @@ import { awaitPromise, + clearEditors, closePages, copy, copyToClipboard, createPromise, - dragAndDropAnnotation, + dragAndDrop, firstPageOnTop, getEditors, getEditorSelector, getFirstSerialized, getRect, - getSelectedEditors, getSerialized, hover, + isCanvasWhite, kbBigMoveDown, kbBigMoveLeft, kbBigMoveRight, @@ -37,13 +38,16 @@ import { kbModifierDown, kbModifierUp, kbRedo, - kbSelectAll, kbUndo, loadAndWait, + moveEditor, paste, pasteFromClipboard, scrollIntoView, + selectEditor, + selectEditors, switchToEditor, + unselectEditor, waitForAnnotationEditorLayer, waitForAnnotationModeChanged, waitForSelectedEditor, @@ -54,38 +58,16 @@ import { } from "./test_utils.mjs"; import { PNG } from "pngjs"; -const selectAll = async page => { - await kbSelectAll(page); - await page.waitForFunction( - () => !document.querySelector(".freeTextEditor:not(.selectedEditor)") - ); -}; - -const clearAll = async page => { - await selectAll(page); - await page.keyboard.down("Control"); - await page.keyboard.press("Backspace"); - await page.keyboard.up("Control"); - await waitForStorageEntries(page, 0); -}; +const selectAll = selectEditors.bind(null, "freeText"); -const switchToFreeText = switchToEditor.bind(null, "FreeText"); +const clearAll = clearEditors.bind(null, "freeText"); -const getXY = async (page, selector) => { - const rect = await getRect(page, selector); - return `${rect.x}::${rect.y}`; +const commit = async page => { + await page.keyboard.press("Escape"); + await page.waitForSelector(".freeTextEditor.selectedEditor .overlay.enabled"); }; -const waitForPositionChange = (page, selector, xy) => - page.waitForFunction( - (sel, currentXY) => { - const bbox = document.querySelector(sel).getBoundingClientRect(); - return `${bbox.x}::${bbox.y}` !== currentXY; - }, - {}, - selector, - xy - ); +const switchToFreeText = switchToEditor.bind(null, "FreeText"); const cancelFocusIn = async (page, selector) => { page.evaluate(sel => { @@ -119,29 +101,17 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - const editorRect = await getRect(page, getEditorSelector(0)); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - await waitForSelectedEditor(page, getEditorSelector(0)); + await waitForSelectedEditor(page, editorSelector); await waitForStorageEntries(page, 1); - let content = await page.$eval(getEditorSelector(0), el => + let content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); @@ -149,16 +119,11 @@ describe("FreeText Editor", () => { // Edit again. await page.keyboard.press("Enter"); await page.waitForSelector( - `${getEditorSelector(0)} .overlay:not(.enabled)` - ); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` + `${editorSelector} .overlay:not(.enabled)` ); + await commit(page); - content = await page.$eval(getEditorSelector(0), el => + content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); @@ -169,27 +134,19 @@ describe("FreeText Editor", () => { it("must copy/paste", async () => { // Run sequentially to avoid clipboard issues. for (const [browserName, page] of pages) { - const editorRect = await getRect(page, getEditorSelector(0)); - - // Select the editor created previously. - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); - - await waitForSelectedEditor(page, getEditorSelector(0)); + const firstEditorSelector = getEditorSelector(0); + await selectEditor(page, firstEditorSelector); await copy(page); await paste(page); - await page.waitForSelector(getEditorSelector(1), { - visible: true, - }); + const secondEditorSelector = getEditorSelector(1); + await page.waitForSelector(secondEditorSelector, { visible: true }); await waitForStorageEntries(page, 2); - const content = await page.$eval(getEditorSelector(0), el => + const content = await page.$eval(firstEditorSelector, el => el.innerText.trimEnd().replaceAll("\xa0", " ") ); - let pastedContent = await page.$eval(getEditorSelector(1), el => + let pastedContent = await page.$eval(secondEditorSelector, el => el.innerText.trimEnd().replaceAll("\xa0", " ") ); @@ -197,12 +154,11 @@ describe("FreeText Editor", () => { await copy(page); await paste(page); - await page.waitForSelector(getEditorSelector(2), { - visible: true, - }); + const thirdEditorSelector = getEditorSelector(2); + await page.waitForSelector(thirdEditorSelector, { visible: true }); await waitForStorageEntries(page, 3); - pastedContent = await page.$eval(getEditorSelector(2), el => + pastedContent = await page.$eval(thirdEditorSelector, el => el.innerText.trimEnd().replaceAll("\xa0", " ") ); expect(pastedContent).withContext(`In ${browserName}`).toEqual(content); @@ -232,41 +188,24 @@ describe("FreeText Editor", () => { // Run sequentially to avoid clipboard issues. for (const [, page] of pages) { const rect = await getRect(page, ".annotationEditorLayer"); - + let editorSelector = getEditorSelector(3); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(3), { - visible: true, - }); - await page.type(`${getEditorSelector(3)} .internal`, data); - - const editorRect = await getRect(page, getEditorSelector(3)); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector(`${getEditorSelector(3)} .overlay.enabled`); - - // And select it again. - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - await waitForSelectedEditor(page, getEditorSelector(3)); + await selectEditor(page, editorSelector); await copy(page); await paste(page); - await page.waitForSelector(getEditorSelector(4), { - visible: true, - }); + editorSelector = getEditorSelector(4); + await page.waitForSelector(editorSelector, { visible: true }); await kbUndo(page); await page.waitForFunction( sel => !document.querySelector(sel), {}, - getEditorSelector(4) + editorSelector ); for (let i = 0; i < 2; i++) { @@ -307,21 +246,15 @@ describe("FreeText Editor", () => { expect(oldAriaOwns).withContext(`In ${browserName}`).toEqual(null); + const editorSelector = getEditorSelector(7); const data = "Hello PDF.js World !!"; await page.mouse.click( stacksRect.x + stacksRect.width + 1, stacksRect.y + stacksRect.height / 2 ); - await page.waitForSelector(getEditorSelector(7), { - visible: true, - }); - await page.type(`${getEditorSelector(7)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(7)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); const ariaOwns = await page.$eval(".textLayer", el => { const span = el.querySelector(`span[pdfjs="true"]`); @@ -343,22 +276,14 @@ describe("FreeText Editor", () => { await clearAll(page); + const editorSelector = getEditorSelector(8); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(8), { - visible: true, - }); - await page.type(`${getEditorSelector(8)} .internal`, data); - - const editorRect = await getRect(page, getEditorSelector(8)); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(8)} .overlay.enabled` - ); - - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([8]); @@ -367,14 +292,8 @@ describe("FreeText Editor", () => { () => !document.querySelector(".selectedEditor") ); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); - - await waitForSelectedEditor(page, getEditorSelector(8)); - - expect(await getSelectedEditors(page)) + await selectEditor(page, editorSelector); + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([8]); @@ -404,32 +323,14 @@ describe("FreeText Editor", () => { const editorSelector = getEditorSelector(9); await page.mouse.click(rect.x + 200, rect.y + 100); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); for (let i = 0; i < 5; i++) { await page.type(`${editorSelector} .internal`, "A"); - - const editorRect = await getRect(page, editorSelector); - - // Commit. - await page.mouse.click( - editorRect.x + 1.5 * editorRect.width, - editorRect.y - ); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); if (i < 4) { - // And select it again. - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector( - `${editorSelector} .overlay:not(.enabled)` - ); + await selectEditor(page, editorSelector, /* count = */ 2); } } @@ -482,31 +383,15 @@ describe("FreeText Editor", () => { ); await kbRedo(page); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); text = await getText(); expect(text).withContext(`In ${browserName}`).toEqual("A"); // Add a new A. - let editorRect = await getRect(page, editorSelector); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector(`${editorSelector} .overlay:not(.enabled)`); + await selectEditor(page, editorSelector, /* count = */ 2); await page.type(`${editorSelector} .internal`, "A"); - - editorRect = await getRect(page, editorSelector); - - // Commit. - await page.mouse.click( - editorRect.x + 1.5 * editorRect.width, - editorRect.y - ); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); text = await getText(); expect(text).withContext(`In ${browserName}`).toEqual("AA"); @@ -535,33 +420,24 @@ describe("FreeText Editor", () => { const editorCenters = []; let lastX = rect.x + rect.width / 10; for (let i = 0; i < 4; i++) { + const editorSelector = getEditorSelector(i); const data = `FreeText ${i}`; await page.mouse.click(lastX, rect.y + rect.height / 10); - await page.waitForSelector(getEditorSelector(i), { - visible: true, - }); - await page.type(`${getEditorSelector(i)} .internal`, data); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - const editorRect = await getRect(page, getEditorSelector(i)); + const editorRect = await getRect(page, editorSelector); lastX = editorRect.x + editorRect.width + 10; editorCenters.push({ x: editorRect.x + editorRect.width / 2, y: editorRect.y + editorRect.height / 2, }); - - // Commit. - await page.mouse.click( - editorRect.x + 1.5 * editorRect.width, - editorRect.y - ); - await page.waitForSelector( - `${getEditorSelector(i)} .overlay.enabled` - ); } await selectAll(page); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 1, 2, 3]); @@ -570,14 +446,14 @@ describe("FreeText Editor", () => { await page.mouse.click(editorCenters[1].x, editorCenters[1].y); await waitForUnselectedEditor(page, getEditorSelector(1)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 2, 3]); await page.mouse.click(editorCenters[2].x, editorCenters[2].y); await waitForUnselectedEditor(page, getEditorSelector(2)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 3]); @@ -585,31 +461,29 @@ describe("FreeText Editor", () => { await kbModifierUp(page); await waitForSelectedEditor(page, getEditorSelector(1)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 1, 3]); await copy(page); await paste(page); - await page.waitForSelector(getEditorSelector(6), { - visible: true, - }); + await page.waitForSelector(getEditorSelector(6), { visible: true }); // 0,1,3 are unselected and new pasted editors are selected. - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([4, 5, 6]); // No ctrl here, hence all are unselected and 2 is selected. await page.mouse.click(editorCenters[2].x, editorCenters[2].y); await waitForSelectedEditor(page, getEditorSelector(2)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([2]); await page.mouse.click(editorCenters[1].x, editorCenters[1].y); await waitForSelectedEditor(page, getEditorSelector(1)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([1]); @@ -617,7 +491,7 @@ describe("FreeText Editor", () => { await page.mouse.click(editorCenters[3].x, editorCenters[3].y); await waitForSelectedEditor(page, getEditorSelector(3)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([1, 3]); @@ -633,7 +507,7 @@ describe("FreeText Editor", () => { await selectAll(page); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 2, 4, 5, 6]); @@ -642,17 +516,15 @@ describe("FreeText Editor", () => { rect.x + (rect.width / 10) * 7, rect.y + rect.height / 10 ); - await page.waitForSelector(getEditorSelector(7), { - visible: true, - }); - expect(await getSelectedEditors(page)) + await page.waitForSelector(getEditorSelector(7), { visible: true }); + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([7]); // Set the focus to 2 and check that only 2 is selected. await page.mouse.click(editorCenters[2].x, editorCenters[2].y); await waitForSelectedEditor(page, getEditorSelector(2)); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([2]); @@ -661,10 +533,8 @@ describe("FreeText Editor", () => { rect.x + (rect.width / 10) * 8, rect.y + rect.height / 10 ); - await page.waitForSelector(getEditorSelector(8), { - visible: true, - }); - expect(await getSelectedEditors(page)) + await page.waitForSelector(getEditorSelector(8), { visible: true }); + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([8]); // Dismiss it. @@ -679,7 +549,7 @@ describe("FreeText Editor", () => { // Check that all the editors are correctly selected (and the focus // didn't move to the body when the empty editor was removed). - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0, 2, 4, 5, 6]); } @@ -719,25 +589,18 @@ describe("FreeText Editor", () => { } const rect = await getRect(page, annotationLayerSelector); - + const editorSelector = getEditorSelector(currentId); const data = `Hello PDF.js World !! on page ${pageNumber}`; expected.push(data); await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(currentId), { - visible: true, - }); - await page.type(`${getEditorSelector(currentId)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(currentId)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - await waitForSelectedEditor(page, getEditorSelector(currentId)); + await waitForSelectedEditor(page, editorSelector); await waitForStorageEntries(page, currentId + 1); - const content = await page.$eval(getEditorSelector(currentId), el => + const content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); @@ -857,21 +720,14 @@ describe("FreeText Editor", () => { for (let step = 0; step < 3; step++) { await firstPageOnTop(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(currentId); const data = `Hello ${step}`; const x = Math.max(rect.x + 0.1 * rect.width, 10); const y = Math.max(rect.y + 0.1 * rect.height, 10); await page.mouse.click(x, y); - await page.waitForSelector(getEditorSelector(currentId), { - visible: true, - }); - await page.type(`${getEditorSelector(currentId)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(currentId)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); const promise = await waitForAnnotationEditorLayer(page); await page.evaluate(() => { @@ -953,19 +809,11 @@ describe("FreeText Editor", () => { const serialized = await getSerialized(page); expect(serialized).withContext(`In ${browserName}`).toEqual([]); - const editorRect = await getRect(page, getEditorSelector(0)); - // Select the annotation we want to move. - await page.mouse.click(editorRect.x + 2, editorRect.y + 2); - await waitForSelectedEditor(page, getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await selectEditor(page, editorSelector); - await dragAndDropAnnotation( - page, - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - 100, - 100 - ); + await dragAndDrop(page, editorSelector, [[100, 100]]); await waitForSerialized(page, 1); }) ); @@ -988,64 +836,30 @@ describe("FreeText Editor", () => { pages.map(async ([browserName, page]) => { await switchToFreeText(page); - const isEditorWhite = editorRect => - page.evaluate(rect => { - const canvas = document.querySelector(".canvasWrapper canvas"); - const ctx = canvas.getContext("2d"); - rect ||= { - x: 0, - y: 0, - width: canvas.width, - height: canvas.height, - }; - const { data } = ctx.getImageData( - rect.x, - rect.y, - rect.width, - rect.height - ); - return data.every(x => x === 0xff); - }, editorRect); - // The page has been re-rendered but with no freetext annotations. - let isWhite = await isEditorWhite(); + let isWhite = await isCanvasWhite(page, 1); expect(isWhite).withContext(`In ${browserName}`).toBeTrue(); let editorIds = await getEditors(page, "freeText"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(6); - const editorRect = await getRect(page, getEditorSelector(0)); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay:not(.enabled)` - ); - + const editorSelector = getEditorSelector(0); + const editorRect = await getRect(page, editorSelector); + await selectEditor(page, editorSelector, /* count = */ 2); await kbGoToEnd(page); await page.waitForFunction( sel => document.getSelection().anchorOffset === document.querySelector(sel).innerText.length, {}, - `${getEditorSelector(0)} .internal` + `${editorSelector} .internal` ); await page.type( - `${getEditorSelector(0)} .internal`, + `${editorSelector} .internal`, " and edited in Firefox" ); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await commit(page); const serialized = await getSerialized(page); expect(serialized.length).withContext(`In ${browserName}`).toEqual(1); @@ -1066,7 +880,7 @@ describe("FreeText Editor", () => { editorIds = await getEditors(page, "freeText"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1); - isWhite = await isEditorWhite(editorRect); + isWhite = await isCanvasWhite(page, 1, editorRect); expect(isWhite).withContext(`In ${browserName}`).toBeTrue(); // Check we've now a div containing the text. @@ -1118,27 +932,15 @@ describe("FreeText Editor", () => { await Promise.all( pages.map(async ([browserName, page]) => { // Show the popup on "Hello World from Firefox" - await page.click(`[data-annotation-id='32R']`); - await page.waitForSelector(`[data-annotation-id='popup_32R']`, { - visible: true, - }); + await page.click("[data-annotation-id='32R']"); + const popupSelector = "[data-annotation-id='popup_32R']"; + await page.waitForSelector(popupSelector, { visible: true }); await switchToFreeText(page); - await page.waitForSelector(`[data-annotation-id='popup_32R']`, { - visible: false, - }); + await page.waitForSelector(popupSelector, { visible: false }); const editorSelector = getEditorSelector(1); - const editorRect = await getRect(page, editorSelector); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector( - `${editorSelector} .overlay:not(.enabled)` - ); - + await selectEditor(page, editorSelector, /* count = */ 2); await kbGoToEnd(page); await page.waitForFunction( sel => @@ -1152,20 +954,14 @@ describe("FreeText Editor", () => { `${editorSelector} .internal`, " and edited in Firefox" ); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); // Disable editing mode. await switchToFreeText(page, /* disable = */ true); - - await page.waitForSelector(`[data-annotation-id='popup_32R']`, { - visible: true, - }); + await page.waitForSelector(popupSelector, { visible: true }); const newPopupText = await page.$eval( - "[data-annotation-id='popup_32R'] .popupContent", + `${popupSelector} .popupContent`, el => el.innerText.replaceAll("\xa0", " ") ); expect(newPopupText) @@ -1218,17 +1014,13 @@ describe("FreeText Editor", () => { let editorIds = await getEditors(page, "freeText"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(6); - const editorRect = await getRect(page, getEditorSelector(3)); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); - await waitForSelectedEditor(page, getEditorSelector(3)); + const editorSelector = getEditorSelector(3); + await selectEditor(page, editorSelector); await page.keyboard.press("Backspace"); await page.waitForFunction( sel => !document.querySelector(sel), {}, - getEditorSelector(3) + editorSelector ); const serialized = await getSerialized(page); @@ -1283,18 +1075,10 @@ describe("FreeText Editor", () => { const editorIds = await getEditors(page, "freeText"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(6); - const editorRect = await getRect(page, getEditorSelector(1)); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); - await waitForSelectedEditor(page, getEditorSelector(1)); - + await selectEditor(page, getEditorSelector(1)); await copy(page); await paste(page); - await page.waitForSelector(getEditorSelector(6), { - visible: true, - }); + await page.waitForSelector(getEditorSelector(6), { visible: true }); await waitForStorageEntries(page, 7); } }); @@ -1440,24 +1224,12 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - const editorRect = await getRect(page, getEditorSelector(0)); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); // Make Chrome happy. await page.waitForFunction(() => { @@ -1561,19 +1333,12 @@ describe("FreeText Editor", () => { } const rect = await getRect(page, annotationLayerSelector); - + const editorSelector = getEditorSelector(currentId); const data = `Hello PDF.js World !! on page ${pageNumber}`; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(currentId), { - visible: true, - }); - await page.type(`${getEditorSelector(currentId)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(currentId)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); currentId += 1; } @@ -1910,24 +1675,12 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - const editorRect = await getRect(page, getEditorSelector(0)); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); await page.focus("#editorFreeTextColor"); await kbUndo(page); @@ -1935,14 +1688,14 @@ describe("FreeText Editor", () => { await page.waitForFunction( sel => !document.querySelector(sel), {}, - getEditorSelector(0) + editorSelector ); await kbRedo(page); await page.waitForFunction( sel => !!document.querySelector(sel), {}, - getEditorSelector(0) + editorSelector ); }) ); @@ -1968,30 +1721,17 @@ describe("FreeText Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); const data = "Hello PDF.js World !!"; - const selectorEditor = getEditorSelector(0); + const editorSelector = getEditorSelector(0); await page.mouse.click(rect.x + 200, rect.y + 200); - await page.waitForSelector(selectorEditor, { - visible: true, - }); - await page.type(`${selectorEditor} .internal`, data); - - const editorRect = await getRect(page, selectorEditor); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); const [pageX, pageY] = await getFirstSerialized(page, x => x.rect); - let xy = await getXY(page, selectorEditor); - for (let i = 0; i < 20; i++) { - await page.keyboard.press("ArrowRight"); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 20, () => + page.keyboard.press("ArrowRight") + ); let [newX, newY] = await getFirstSerialized(page, x => x.rect); expect(Math.round(newX)) @@ -2001,11 +1741,9 @@ describe("FreeText Editor", () => { .withContext(`In ${browserName}`) .toEqual(Math.round(pageY)); - for (let i = 0; i < 20; i++) { - await page.keyboard.press("ArrowDown"); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 20, () => + page.keyboard.press("ArrowDown") + ); [newX, newY] = await getFirstSerialized(page, x => x.rect); expect(Math.round(newX)) @@ -2015,11 +1753,7 @@ describe("FreeText Editor", () => { .withContext(`In ${browserName}`) .toEqual(Math.round(pageY - 20)); - for (let i = 0; i < 2; i++) { - await kbBigMoveLeft(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveLeft(page)); [newX, newY] = await getFirstSerialized(page, x => x.rect); expect(Math.round(newX)) @@ -2029,11 +1763,7 @@ describe("FreeText Editor", () => { .withContext(`In ${browserName}`) .toEqual(Math.round(pageY - 20)); - for (let i = 0; i < 2; i++) { - await kbBigMoveUp(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveUp(page)); [newX, newY] = await getFirstSerialized(page, x => x.rect); expect(Math.round(newX)) @@ -2058,13 +1788,10 @@ describe("FreeText Editor", () => { ); const pageWidth = page2X - page1X; - const selectorEditor = getEditorSelector(0); - let xy = await getXY(page, selectorEditor); - for (let i = 0; i < 5; i++) { - await page.keyboard.press("ArrowRight"); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + const editorSelector = getEditorSelector(0); + await moveEditor(page, editorSelector, 5, () => + page.keyboard.press("ArrowRight") + ); const [new1X, , new2X] = await getFirstSerialized(page, x => x.rect); const newWidth = new2X - new1X; @@ -2086,71 +1813,37 @@ describe("FreeText Editor", () => { const rect = await getRect(page, ".annotationEditorLayer"); const data = "Hello PDF.js World !!"; - let selectorEditor = getEditorSelector(1); + let editorSelector = getEditorSelector(1); await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(selectorEditor, { - visible: true, - }); - await page.type(`${selectorEditor} .internal`, data); - - const editorRect = await getRect(page, selectorEditor); - - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); const [pageX, pageY] = await getFirstSerialized(page, x => x.rect); await clearAll(page); - selectorEditor = getEditorSelector(2); + editorSelector = getEditorSelector(2); await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(selectorEditor, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); - let xy = await getXY(page, selectorEditor); - for (let i = 0; i < 20; i++) { - await page.keyboard.press("ArrowRight"); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 20, () => + page.keyboard.press("ArrowRight") + ); - for (let i = 0; i < 2; i++) { - await kbBigMoveDown(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveDown(page)); - for (let i = 0; i < 20; i++) { - await page.keyboard.press("ArrowLeft"); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 20, () => + page.keyboard.press("ArrowLeft") + ); - for (let i = 0; i < 2; i++) { - await kbBigMoveUp(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveUp(page)); - for (let i = 0; i < 2; i++) { - await kbBigMoveRight(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveRight(page)); - for (let i = 0; i < 2; i++) { - await kbBigMoveLeft(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 2, () => kbBigMoveLeft(page)); - await page.type(`${selectorEditor} .internal`, data); - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.type(`${editorSelector} .internal`, data); + await commit(page); const [newX, newY] = await getFirstSerialized(page, x => x.rect); expect(Math.round(newX)) @@ -2193,19 +1886,13 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - // Commit. - await cancelFocusIn(page, getEditorSelector(0)); - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await cancelFocusIn(page, editorSelector); + await commit(page); const oneToFourteen = Array.from(new Array(14).keys(), x => x + 1); @@ -2253,78 +1940,56 @@ describe("FreeText Editor", () => { let rect = await getRect(page, ".annotationEditorLayer"); + const firstEditorSelector = getEditorSelector(0); await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, "A"); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); - - rect = await getRect(page, getEditorSelector(0)); + await page.waitForSelector(firstEditorSelector, { visible: true }); + await page.type(`${firstEditorSelector} .internal`, "A"); + await commit(page); // Create a new editor. + rect = await getRect(page, firstEditorSelector); + const secondEditorSelector = getEditorSelector(1); await page.mouse.click( rect.x + 5 * rect.width, rect.y + 5 * rect.height ); - await page.waitForSelector(getEditorSelector(1), { - visible: true, - }); - await page.type(`${getEditorSelector(1)} .internal`, "B"); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(1)} .overlay.enabled` - ); + await page.waitForSelector(secondEditorSelector, { visible: true }); + await page.type(`${secondEditorSelector} .internal`, "B"); + await commit(page); // Select the second editor. - rect = await getRect(page, getEditorSelector(1)); - - await page.mouse.click( - rect.x + 0.5 * rect.width, - rect.y + 0.5 * rect.height - ); - await waitForSelectedEditor(page, getEditorSelector(1)); + await selectEditor(page, secondEditorSelector); - const pos = n => + const pos = selector => page.evaluate(sel => { const editor = document.querySelector(sel); return Array.prototype.indexOf.call( editor.parentNode.childNodes, editor ); - }, getEditorSelector(n)); + }, selector); - expect(await pos(0)) + expect(await pos(firstEditorSelector)) .withContext(`In ${browserName}`) .toEqual(0); - expect(await pos(1)) + expect(await pos(secondEditorSelector)) .withContext(`In ${browserName}`) .toEqual(1); - const { y: y0, height } = await getRect(page, getEditorSelector(0)); - const selectorEditor = getEditorSelector(1); - let xy = await getXY(page, selectorEditor); - while ((await getRect(page, selectorEditor)).y > y0 - height) { - await kbBigMoveUp(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); + const { y: y0, height } = await getRect(page, firstEditorSelector); + const editorSelector = secondEditorSelector; + while ((await getRect(page, editorSelector)).y > y0 - height) { + await moveEditor(page, editorSelector, 1, () => kbBigMoveUp(page)); } // The editor must be moved in the DOM and potentially the focus // will be lost, hence there's a callback will get back the focus. - await page.waitForSelector(`${getEditorSelector(1)}:focus`); + await page.waitForSelector(`${secondEditorSelector}:focus`); - expect(await pos(0)) + expect(await pos(firstEditorSelector)) .withContext(`In ${browserName}`) .toEqual(1); - expect(await pos(1)) + expect(await pos(secondEditorSelector)) .withContext(`In ${browserName}`) .toEqual(0); }) @@ -2353,29 +2018,24 @@ describe("FreeText Editor", () => { const allPositions = []; for (let i = 0; i < 10; i++) { + const editorSelector = getEditorSelector(i); await page.mouse.click(rect.x + 10 + 30 * i, rect.y + 100 + 5 * i); - await page.waitForSelector(getEditorSelector(i), { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); await page.type( - `${getEditorSelector(i)} .internal`, + `${editorSelector} .internal`, String.fromCharCode(65 + i) ); + await commit(page); - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(i)} .overlay.enabled` - ); - - allPositions.push(await getRect(page, getEditorSelector(i))); + allPositions.push(await getRect(page, editorSelector)); } await selectAll(page); - await dragAndDropAnnotation(page, rect.x + 161, rect.y + 126, 39, 74); + await dragAndDrop(page, getEditorSelector(4), [[39, 74]]); for (let i = 0; i < 10; i++) { - const pos = await getRect(page, getEditorSelector(i)); + const editorSelector = getEditorSelector(i); + const pos = await getRect(page, editorSelector); const oldPos = allPositions[i]; expect(Math.abs(Math.round(pos.x - oldPos.x) - 39)) .withContext(`In ${browserName}`) @@ -2417,20 +2077,13 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await cancelFocusIn(page, getEditorSelector(0)); - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await cancelFocusIn(page, editorSelector); + await commit(page); await page.evaluate(() => { window.editingEvents = []; @@ -2477,29 +2130,18 @@ describe("FreeText Editor", () => { const page1Selector = `.page[data-page-number = "1"] > .annotationEditorLayer.freetextEditing`; let rect = await getRect(page, page1Selector); - const selectorEditor = getEditorSelector(0); + const firstEditorSelector = getEditorSelector(0); await page.mouse.click(rect.x + 10, rect.y + 10); - await page.waitForSelector(selectorEditor, { - visible: true, - }); - await page.type(`${selectorEditor} .internal`, "Hello"); - - // Commit. - await cancelFocusIn(page, selectorEditor); - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.waitForSelector(firstEditorSelector, { visible: true }); + await page.type(`${firstEditorSelector} .internal`, "Hello"); + await cancelFocusIn(page, firstEditorSelector); + await commit(page); // Unselect. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, selectorEditor); - - const editorRect = await getRect(page, selectorEditor); + await unselectEditor(page, firstEditorSelector); // Select the editor created previously. - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); + await selectEditor(page, firstEditorSelector); // Go to the last page. await scrollIntoView(page, `.page[data-page-number = "14"]`); @@ -2511,16 +2153,11 @@ describe("FreeText Editor", () => { }); rect = await getRect(page, page14Selector); + const secondEditorSelector = getEditorSelector(1); await page.mouse.click(rect.x + 10, rect.y + 10); - await page.waitForSelector(getEditorSelector(1), { - visible: true, - }); - await page.type(`${getEditorSelector(1)} .internal`, "World"); - - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(secondEditorSelector, { visible: true }); + await page.type(`${secondEditorSelector} .internal`, "World"); + await commit(page); for (let i = 0; i < 13; i++) { await page.keyboard.press("P"); @@ -2533,19 +2170,9 @@ describe("FreeText Editor", () => { }); } - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - - rect = await getRect(page, getEditorSelector(0)); - await page.mouse.click( - rect.x + rect.width / 2, - rect.y + rect.height / 2 - ); - - await waitForSelectedEditor(page, getEditorSelector(0)); - - const content = await page.$eval(getEditorSelector(0), el => + await page.waitForSelector(firstEditorSelector, { visible: true }); + await selectEditor(page, firstEditorSelector); + const content = await page.$eval(firstEditorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual("Hello"); @@ -2572,38 +2199,24 @@ describe("FreeText Editor", () => { const page1Selector = `.page[data-page-number = "1"] > .annotationEditorLayer.freetextEditing`; const rect = await getRect(page, page1Selector); - const selectorEditor = getEditorSelector(0); + const editorSelector = getEditorSelector(0); await page.mouse.click(rect.x + 10, rect.y + 10); - await page.waitForSelector(selectorEditor, { - visible: true, - }); - await page.type(`${selectorEditor} .internal`, "Hello"); - - // Commit. - await cancelFocusIn(page, selectorEditor); - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, "Hello"); + await cancelFocusIn(page, editorSelector); + await commit(page); // Unselect. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, selectorEditor); - - const editorRect = await getRect(page, selectorEditor); + await unselectEditor(page, editorSelector); // Select the editor created previously. - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); + await selectEditor(page, editorSelector); // Go to the last page. await scrollIntoView(page, `.page[data-page-number = "14"]`); await page.waitForSelector( `.page[data-page-number = "14"] > .annotationEditorLayer.freetextEditing`, - { - visible: true, - timeout: 0, - } + { visible: true, timeout: 0 } ); await clearAll(page); @@ -2621,9 +2234,7 @@ describe("FreeText Editor", () => { await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); }) ); }); @@ -2646,6 +2257,7 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const parentId = "p3R_mc8"; + const editorSelector = getEditorSelector(0); const rect = await page.evaluate(id => { const parent = document.getElementById(id); let span = null; @@ -2662,16 +2274,9 @@ describe("FreeText Editor", () => { rect.x + rect.width + 5, rect.y + rect.height / 2 ); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, "Hello Wolrd"); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, "Hello Wolrd"); + await commit(page); await waitForStorageEntries(page, 1); @@ -2699,21 +2304,17 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - const internalEditorSelector = `${getEditorSelector(0)} .internal`; + await page.waitForSelector(editorSelector, { visible: true }); + const internalEditorSelector = `${editorSelector} .internal`; await page.type(internalEditorSelector, data); - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); - await page.click(getEditorSelector(0), { count: 2 }); + await commit(page); + + await page.click(editorSelector, { count: 2 }); await page.waitForSelector( - `${getEditorSelector(0)} .overlay:not(.enabled)` + `${editorSelector} .overlay:not(.enabled)` ); await page.click(internalEditorSelector, { count: 3, @@ -2745,26 +2346,16 @@ describe("FreeText Editor", () => { await page.focus("#editorFreeTextButton"); await page.keyboard.press("Enter"); - let selectorEditor = getEditorSelector(0); - await page.waitForSelector(selectorEditor, { - visible: true, - }); + let editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector, { visible: true }); - let xy = await getXY(page, selectorEditor); - for (let i = 0; i < 5; i++) { - await kbBigMoveUp(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 5, () => kbBigMoveUp(page)); const data = "Hello PDF.js World !!"; - await page.type(`${selectorEditor} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.type(`${editorSelector} .internal`, data); + await commit(page); - let content = await page.$eval(selectorEditor, el => + let content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); @@ -2775,29 +2366,18 @@ describe("FreeText Editor", () => { await page.focus("#editorFreeTextButton"); await page.keyboard.press(" "); - selectorEditor = getEditorSelector(1); - await page.waitForSelector(selectorEditor, { - visible: true, - }); + editorSelector = getEditorSelector(1); + await page.waitForSelector(editorSelector, { visible: true }); - xy = await getXY(page, selectorEditor); - for (let i = 0; i < 5; i++) { - await kbBigMoveDown(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 5, () => kbBigMoveDown(page)); - await page.type(`${selectorEditor} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.type(`${editorSelector} .internal`, data); + await commit(page); // Unselect. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, selectorEditor); + await unselectEditor(page, editorSelector); - content = await page.$eval(getEditorSelector(1), el => + content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); @@ -2810,59 +2390,39 @@ describe("FreeText Editor", () => { await Promise.all( pages.map(async ([browserName, page]) => { await page.keyboard.press("Enter"); - let selectorEditor = getEditorSelector(2); - await page.waitForSelector(selectorEditor, { - visible: true, - }); + let editorSelector = getEditorSelector(2); + await page.waitForSelector(editorSelector, { visible: true }); - let xy = await getXY(page, selectorEditor); - for (let i = 0; i < 10; i++) { - await kbBigMoveLeft(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + await moveEditor(page, editorSelector, 10, () => kbBigMoveLeft(page)); const data = "Hello PDF.js World !!"; - await page.type(`${selectorEditor} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.type(`${editorSelector} .internal`, data); + await commit(page); // Unselect. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, selectorEditor); + await unselectEditor(page, editorSelector); - let content = await page.$eval(getEditorSelector(2), el => + let content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); await page.keyboard.press(" "); - selectorEditor = getEditorSelector(3); - await page.waitForSelector(selectorEditor, { - visible: true, - }); - - xy = await getXY(page, selectorEditor); - for (let i = 0; i < 10; i++) { - await kbBigMoveRight(page); - await waitForPositionChange(page, selectorEditor, xy); - xy = await getXY(page, selectorEditor); - } + editorSelector = getEditorSelector(3); + await page.waitForSelector(editorSelector, { visible: true }); - await page.type(`${selectorEditor} .internal`, data); + await moveEditor(page, editorSelector, 10, () => + kbBigMoveRight(page) + ); - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${selectorEditor} .overlay.enabled`); + await page.type(`${editorSelector} .internal`, data); + await commit(page); // Unselect. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, selectorEditor); + await unselectEditor(page, editorSelector); - content = await page.$eval(selectorEditor, el => + content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); @@ -2889,19 +2449,12 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); let handle = await createPromise(page, resolve => { document.addEventListener("selectionchange", resolve, { @@ -2924,7 +2477,7 @@ describe("FreeText Editor", () => { ); expect(content).withContext(`In ${browserName}`).toEqual(""); - content = await page.$eval(getEditorSelector(0), el => + content = await page.$eval(editorSelector, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); @@ -2950,26 +2503,19 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); // Delete it in using the button. - await page.click(`${getEditorSelector(0)} button.delete`); + await page.click(`${editorSelector} button.delete`); await page.waitForFunction( sel => !document.querySelector(sel), {}, - getEditorSelector(0) + editorSelector ); await waitForStorageEntries(page, 0); @@ -2977,9 +2523,7 @@ describe("FreeText Editor", () => { await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); }) ); }); @@ -3008,47 +2552,32 @@ describe("FreeText Editor", () => { for (let i = 1; i <= 2; i++) { const editorSelector = getEditorSelector(i - 1); await page.mouse.click(rect.x + i * 100, rect.y + i * 100); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); await page.type(`${editorSelector} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); } // Select the editor created previously. - const editorRect = await getRect(page, getEditorSelector(0)); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2 - ); - await waitForSelectedEditor(page, getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await selectEditor(page, editorSelector); await selectAll(page); // Delete it in using the button. - await page.focus(`${getEditorSelector(0)} button.delete`); + await page.focus(`${editorSelector} button.delete`); await page.keyboard.press("Enter"); await page.waitForFunction( sel => !document.querySelector(sel), {}, - getEditorSelector(0) + editorSelector ); await waitForStorageEntries(page, 0); // Undo. await kbUndo(page); await waitForSerialized(page, 2); - - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - - await page.waitForSelector(getEditorSelector(1), { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); + await page.waitForSelector(getEditorSelector(1), { visible: true }); }) ); }); @@ -3069,8 +2598,10 @@ describe("FreeText Editor", () => { await Promise.all( pages.map(async ([browserName, page]) => { await switchToFreeText(page); - await page.click(getEditorSelector(0), { count: 2 }); - await page.type(`${getEditorSelector(0)} .internal`, "C"); + + const editorSelector = getEditorSelector(0); + await page.click(editorSelector, { count: 2 }); + await page.type(`${editorSelector} .internal`, "C"); await switchToFreeText(page, /* disable = */ true); @@ -3098,8 +2629,10 @@ describe("FreeText Editor", () => { await Promise.all( pages.map(async ([browserName, page]) => { await switchToFreeText(page); - await page.click(getEditorSelector(0), { count: 2 }); - await page.type(`${getEditorSelector(0)} .internal`, "Z"); + + const editorSelector = getEditorSelector(0); + await page.click(editorSelector, { count: 2 }); + await page.type(`${editorSelector} .internal`, "Z"); await switchToFreeText(page, /* disable = */ true); @@ -3129,19 +2662,12 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello\nPDF.js\nWorld\n!!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); await waitForSerialized(page, 1); const serialized = (await getSerialized(page))[0]; @@ -3170,19 +2696,12 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); await page.evaluate(() => { window.PDFViewerApplication.eventBus.dispatch( @@ -3278,23 +2797,16 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2); @@ -3312,7 +2824,7 @@ describe("FreeText Editor", () => { await scrollIntoView(page, pageSelector); } - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -3335,23 +2847,16 @@ describe("FreeText Editor", () => { await switchToFreeText(page); const rect = await getRect(page, ".annotationEditorLayer"); - + const editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(getEditorSelector(0), { - visible: true, - }); - await page.type(`${getEditorSelector(0)} .internal`, data); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)} .overlay.enabled` - ); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToOne = Array.from(new Array(13).keys(), n => n + 2).concat( @@ -3364,7 +2869,7 @@ describe("FreeText Editor", () => { await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -3391,15 +2896,9 @@ describe("FreeText Editor", () => { let editorSelector = getEditorSelector(0); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); await page.type(`${editorSelector} .internal`, data); - const editorRect = await getRect(page, editorSelector); - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); const waitForTextChange = (previous, edSelector) => page.waitForFunction( @@ -3411,12 +2910,7 @@ describe("FreeText Editor", () => { const getText = edSelector => page.$eval(`${edSelector} .internal`, el => el.innerText.trimEnd()); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector(`${editorSelector} .overlay:not(.enabled)`); + await selectEditor(page, editorSelector, /* count = */ 2); const select = position => page.evaluate( @@ -3484,15 +2978,11 @@ describe("FreeText Editor", () => { const html = await getHTML(); expect(html).withContext(`In ${browserName}`).toEqual(prevHTML); - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); editorSelector = getEditorSelector(1); await page.mouse.click(rect.x + 200, rect.y + 200); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); const fooBar = "Foo\nBar\nOof"; await copyToClipboard(page, { @@ -3529,15 +3019,7 @@ describe("FreeText Editor", () => { const editorSelector = getEditorSelector(0); const editorRect = await getRect(page, editorSelector); - await page.mouse.click( - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - { count: 2 } - ); - await page.waitForSelector( - `${editorSelector} .overlay:not(.enabled)` - ); - + await selectEditor(page, editorSelector, /* count = */ 2); await kbGoToEnd(page); await page.waitForFunction( sel => @@ -3667,19 +3149,14 @@ describe("FreeText Editor", () => { const data = "Hello\nPDF.js\nWorld\n!!"; await page.mouse.click(rect.x + 100, rect.y + 100); - await page.waitForSelector(editorSelector, { - visible: true, - }); + await page.waitForSelector(editorSelector, { visible: true }); for (const line of data.split("\n")) { await page.type(`${editorSelector} .internal`, line); await page.keyboard.down("Shift"); await page.keyboard.press("Enter"); await page.keyboard.up("Shift"); } - - // Commit. - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector} .overlay.enabled`); + await commit(page); await waitForSerialized(page, 1); const serialized = await getSerialized(page, x => x.value); @@ -3688,4 +3165,106 @@ describe("FreeText Editor", () => { ); }); }); + + describe("Undo deletion popup has the expected behaviour", () => { + let pages; + const editorSelector = getEditorSelector(0); + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that deleting a FreeText editor can be undone using the undo button", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToFreeText(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + const data = "Hello PDF.js World !!"; + await page.mouse.click(rect.x + 100, rect.y + 100); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForSelector("#editorUndoBar:not([hidden])"); + await page.click("#editorUndoBarUndoButton"); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + }) + ); + }); + + it("must check that the undo deletion popup displays the correct message", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToFreeText(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + const data = "Hello PDF.js World !!"; + await page.mouse.click(rect.x + 100, rect.y + 100); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForFunction(() => { + const messageElement = document.querySelector( + "#editorUndoBarMessage" + ); + return messageElement && messageElement.textContent.trim() !== ""; + }); + const message = await page.waitForSelector("#editorUndoBarMessage"); + const messageText = await page.evaluate( + el => el.textContent, + message + ); + expect(messageText).toContain("Text removed"); + }) + ); + }); + + it("must check that the popup disappears when a new textbox is created", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToFreeText(page); + + let rect = await getRect(page, ".annotationEditorLayer"); + const data = "Hello PDF.js World !!"; + await page.mouse.click(rect.x + 100, rect.y + 100); + await page.waitForSelector(editorSelector, { visible: true }); + await page.type(`${editorSelector} .internal`, data); + await commit(page); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForSelector("#editorUndoBar:not([hidden])"); + rect = await getRect(page, ".annotationEditorLayer"); + const secondEditorSelector = getEditorSelector(1); + const newData = "This is a new text box!"; + await page.mouse.click(rect.x + 150, rect.y + 150); + await page.waitForSelector(secondEditorSelector, { visible: true }); + await page.type(`${secondEditorSelector} .internal`, newData); + await commit(page); + await waitForSerialized(page, 1); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + }); }); diff --git a/test/integration/highlight_editor_spec.mjs b/test/integration/highlight_editor_spec.mjs index 13d30c31aae71..f9d4199c20dda 100644 --- a/test/integration/highlight_editor_spec.mjs +++ b/test/integration/highlight_editor_spec.mjs @@ -16,47 +16,41 @@ import { awaitPromise, closePages, - createPromise, getEditorSelector, getFirstSerialized, getRect, getSerialized, getSpanRectFromText, + getXY, kbBigMoveLeft, kbBigMoveUp, kbFocusNext, kbFocusPrevious, - kbSelectAll, + kbSave, kbUndo, loadAndWait, scrollIntoView, + selectEditors, setCaretAt, switchToEditor, + unselectEditor, waitAndClick, waitForAnnotationModeChanged, + waitForPointerUp, waitForSelectedEditor, waitForSerialized, + waitForTimeout, } from "./test_utils.mjs"; +import { fileURLToPath } from "url"; +import fs from "fs"; +import path from "path"; -const selectAll = async page => { - await kbSelectAll(page); - await page.waitForFunction( - () => !document.querySelector(".highlightEditor:not(.selectedEditor)") - ); -}; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const waitForPointerUp = page => - createPromise(page, resolve => { - window.addEventListener("pointerup", resolve, { once: true }); - }); +const selectAll = selectEditors.bind(null, "highlight"); const switchToHighlight = switchToEditor.bind(null, "Highlight"); -const getXY = async (page, selector) => { - const rect = await getRect(page, selector); - return `${rect.x}::${rect.y}`; -}; - describe("Highlight Editor", () => { describe("Editor must be removed without exception", () => { let pages; @@ -97,9 +91,7 @@ describe("Highlight Editor", () => { await page.waitForSelector( `.page[data-page-number = "1"] svg.highlight`, - { - visible: true, - } + { visible: true } ); }) ); @@ -244,22 +236,22 @@ describe("Highlight Editor", () => { x = rect.x + rect.width / 2; y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(`${getEditorSelector(1)}`); + + const editorSelector = getEditorSelector(1); + await page.waitForSelector(editorSelector); await page.waitForSelector( `.page[data-page-number = "14"] svg.highlightOutline.selected` ); await selectAll(page); await page.waitForSelector( - `${getEditorSelector(1)} .editToolbar button.colorPicker` - ); - await page.click( - `${getEditorSelector(1)} .editToolbar button.colorPicker` + `${editorSelector} .editToolbar button.colorPicker` ); + await page.click(`${editorSelector} .editToolbar button.colorPicker`); await page.waitForSelector( - `${getEditorSelector(1)} .editToolbar button[title = "Green"]` + `${editorSelector} .editToolbar button[title = "Green"]` ); await page.click( - `${getEditorSelector(1)} .editToolbar button[title = "Green"]` + `${editorSelector} .editToolbar button[title = "Green"]` ); await page.waitForSelector( `.page[data-page-number = "14"] svg.highlight[fill = "#53FFBC"]` @@ -481,24 +473,25 @@ describe("Highlight Editor", () => { const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(`${getEditorSelector(0)}`); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await page.waitForSelector( `.page[data-page-number = "1"] svg.highlightOutline.selected` ); - await page.focus(getEditorSelector(0)); + await page.focus(editorSelector); - const xy = await getXY(page, getEditorSelector(0)); + const xy = await getXY(page, editorSelector); for (let i = 0; i < 5; i++) { await kbBigMoveLeft(page); } - expect(await getXY(page, getEditorSelector(0))) + expect(await getXY(page, editorSelector)) .withContext(`In ${browserName}`) .toEqual(xy); for (let i = 0; i < 5; i++) { await kbBigMoveUp(page); } - expect(await getXY(page, getEditorSelector(0))) + expect(await getXY(page, editorSelector)) .withContext(`In ${browserName}`) .toEqual(xy); }) @@ -630,10 +623,8 @@ describe("Highlight Editor", () => { await selectAll(page); - const { width: prevWidth } = await getRect( - page, - getEditorSelector(0) - ); + const editorSelector = getEditorSelector(0); + const { width: prevWidth } = await getRect(page, editorSelector); value = 24; page.evaluate(val => { @@ -653,7 +644,7 @@ describe("Highlight Editor", () => { document.querySelector(sel).getBoundingClientRect().width !== w, {}, prevWidth, - getEditorSelector(0) + editorSelector ); await waitForSerialized(page, 3); @@ -697,7 +688,12 @@ describe("Highlight Editor", () => { await page.keyboard.press("ArrowRight"); } await page.keyboard.press("ArrowDown"); - await page.keyboard.press("ArrowDown"); + // Here and elsewhere, we add a small delay between press and release + // to make sure that a keyup event for Shift is triggered after + // selectionchange (it's why adding the delay on the last before + // releasing shift is enough). + // It works with a value of 10ms, but we use 100ms to be sure. + await page.keyboard.press("ArrowDown", { delay: 100 }); await page.keyboard.up("Shift"); await page.waitForSelector(sel); @@ -755,7 +751,8 @@ describe("Highlight Editor", () => { await page.mouse.up(); await awaitPromise(clickHandle); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await page.evaluate(() => { window.PDFViewerApplication.rotatePages(90); @@ -765,10 +762,7 @@ describe("Highlight Editor", () => { ); await selectAll(page); - const { width: prevWidth } = await getRect( - page, - getEditorSelector(0) - ); + const { width: prevWidth } = await getRect(page, editorSelector); page.evaluate(val => { window.PDFViewerApplication.eventBus.dispatch( @@ -787,10 +781,10 @@ describe("Highlight Editor", () => { document.querySelector(sel).getBoundingClientRect().width !== w, {}, prevWidth, - getEditorSelector(0) + editorSelector ); - const rectDiv = await getRect(page, getEditorSelector(0)); + const rectDiv = await getRect(page, editorSelector); const rectSVG = await getRect(page, "svg.highlight.free"); expect(Math.abs(rectDiv.x - rectSVG.x) <= 2) @@ -1030,11 +1024,10 @@ describe("Highlight Editor", () => { const x = rect.x + rect.width / 2; const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(`${getEditorSelector(0)}`); - await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(0)}:not(.selectedEditor)` - ); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); + await unselectEditor(page, editorSelector); await setCaretAt( page, @@ -1066,7 +1059,7 @@ describe("Highlight Editor", () => { 15 ); await page.keyboard.down("Shift"); - await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown", { delay: 100 }); await page.keyboard.up("Shift"); await page.waitForSelector(getEditorSelector(0)); @@ -1179,11 +1172,12 @@ describe("Highlight Editor", () => { const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); - await page.focus(`${getEditorSelector(0)} button.delete`); + await page.focus(`${editorSelector} button.delete`); await page.keyboard.press(" "); await waitForSerialized(page, 0); @@ -1215,7 +1209,8 @@ describe("Highlight Editor", () => { { count: 2, delay: 100 } ); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); rect = await getRect(page, ".annotationEditorLayer"); @@ -1231,8 +1226,8 @@ describe("Highlight Editor", () => { "#editorFreeHighlightThickness:not([disabled])" ); - await page.click(getEditorSelector(0)); - await page.waitForSelector(getEditorSelector(0)); + await page.click(editorSelector); + await page.waitForSelector(editorSelector); await page.waitForSelector("#editorFreeHighlightThickness[disabled]"); await switchToHighlight(page, /* disable */ true); @@ -1272,7 +1267,8 @@ describe("Highlight Editor", () => { await page.waitForSelector(getEditorSelector(0)); await waitForSerialized(page, 1); const quadPoints = await getFirstSerialized(page, e => e.quadPoints); - const expected = [263, 674, 346, 674, 263, 696, 346, 696]; + // Expected quadPoints tL, tR, bL, bR with bL coordinate. + const expected = [263, 696, 346, 696, 263, 674, 346, 674]; expect(quadPoints.every((x, i) => Math.abs(x - expected[i]) <= 5)) .withContext(`In ${browserName}`) .toBeTrue(); @@ -1307,7 +1303,8 @@ describe("Highlight Editor", () => { await page.waitForSelector(getEditorSelector(0)); await waitForSerialized(page, 1); const quadPoints = await getFirstSerialized(page, e => e.quadPoints); - const expected = [148, 624, 176, 624, 148, 637, 176, 637]; + // Expected quadPoints tL, tR, bL, bR with bL coordinate. + const expected = [148, 637, 176, 637, 148, 624, 176, 624]; expect(quadPoints.every((x, i) => Math.abs(x - expected[i]) <= 5)) .withContext(`In ${browserName} (got ${quadPoints})`) .toBeTrue(); @@ -1337,11 +1334,12 @@ describe("Highlight Editor", () => { const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(0)); - await page.focus(`${getEditorSelector(0)} button.colorPicker`); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); + await page.focus(`${editorSelector} button.colorPicker`); await page.keyboard.press("Escape"); - await page.focus(`${getEditorSelector(0)}:not(selectedEditor)`); + await page.focus(`${editorSelector}:not(selectedEditor)`); }) ); }); @@ -1372,9 +1370,10 @@ describe("Highlight Editor", () => { await page.mouse.up(); await awaitPromise(clickHandle); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); - const { x: editorX } = await getRect(page, getEditorSelector(0)); + const { x: editorX } = await getRect(page, editorSelector); expect(editorX < rect.x) .withContext(`In ${browserName}`) @@ -1407,7 +1406,9 @@ describe("Highlight Editor", () => { await page.mouse.move(rect.x + 20, rect.y + 120); await page.mouse.up(); await awaitPromise(clickHandle); - await page.waitForSelector(getEditorSelector(0)); + + const firstEditorSelector = getEditorSelector(0); + await page.waitForSelector(firstEditorSelector); rect = await getSpanRectFromText(page, 1, "Languages"); await page.mouse.click( @@ -1415,19 +1416,21 @@ describe("Highlight Editor", () => { rect.y + rect.height / 2, { count: 2, delay: 100 } ); - await page.waitForSelector(getEditorSelector(1)); + + const secondEditorSelector = getEditorSelector(1); + await page.waitForSelector(secondEditorSelector); await page.click("#editorHighlightShowAll"); - await page.waitForSelector(`${getEditorSelector(0)}.hidden`); - await page.waitForSelector(`${getEditorSelector(1)}.hidden`); + await page.waitForSelector(`${firstEditorSelector}.hidden`); + await page.waitForSelector(`${secondEditorSelector}.hidden`); await page.click("#editorHighlightShowAll"); - await page.waitForSelector(`${getEditorSelector(0)}:not(.hidden)`); - await page.waitForSelector(`${getEditorSelector(1)}:not(.hidden)`); + await page.waitForSelector(`${firstEditorSelector}:not(.hidden)`); + await page.waitForSelector(`${secondEditorSelector}:not(.hidden)`); await page.click("#editorHighlightShowAll"); - await page.waitForSelector(`${getEditorSelector(0)}.hidden`); - await page.waitForSelector(`${getEditorSelector(1)}.hidden`); + await page.waitForSelector(`${firstEditorSelector}.hidden`); + await page.waitForSelector(`${secondEditorSelector}.hidden`); const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat( Array.from(new Array(13).keys(), n => 13 - n) @@ -1442,8 +1445,8 @@ describe("Highlight Editor", () => { } } - await page.waitForSelector(`${getEditorSelector(0)}:not(.hidden)`); - await page.waitForSelector(`${getEditorSelector(1)}:not(.hidden)`); + await page.waitForSelector(`${firstEditorSelector}:not(.hidden)`); + await page.waitForSelector(`${secondEditorSelector}:not(.hidden)`); }) ); }); @@ -1469,23 +1472,33 @@ describe("Highlight Editor", () => { it("must check that clicking on the highlight floating button triggers an highlight", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - const rect = await getSpanRectFromText(page, 1, "Abstract"); - const x = rect.x + rect.width / 2; - const y = rect.y + rect.height / 2; - await page.mouse.click(x, y, { count: 2, delay: 100 }); + async function floatingHighlight(text, editorId) { + const rect = await getSpanRectFromText(page, 1, text); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + + await page.waitForSelector(".textLayer .highlightButton"); + await page.click(".textLayer .highlightButton"); + + await page.waitForSelector(getEditorSelector(editorId)); + const usedColor = await page.evaluate(() => { + const highlight = document.querySelector( + `.page[data-page-number = "1"] .canvasWrapper > svg.highlight` + ); + return highlight.getAttribute("fill"); + }); - await page.waitForSelector(".textLayer .highlightButton"); - await page.click(".textLayer .highlightButton"); + expect(usedColor) + .withContext(`In ${browserName}`) + .toEqual("#AB0000"); + } - await page.waitForSelector(getEditorSelector(0)); - const usedColor = await page.evaluate(() => { - const highlight = document.querySelector( - `.page[data-page-number = "1"] .canvasWrapper > svg.highlight` - ); - return highlight.getAttribute("fill"); - }); + await floatingHighlight("Abstract", 0); - expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000"); + // Disable editing mode, and highlight another string (issue 19369). + await switchToHighlight(page, /* disable */ true); + await floatingHighlight("Introduction", 1); }) ); }); @@ -1517,8 +1530,10 @@ describe("Highlight Editor", () => { x = rect.x + rect.width / 2; y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(1)); - await page.focus(getEditorSelector(1)); + + const editorSelector = getEditorSelector(1); + await page.waitForSelector(editorSelector); + await page.focus(editorSelector); await kbFocusPrevious(page); await page.waitForSelector( @@ -1526,7 +1541,7 @@ describe("Highlight Editor", () => { ); await kbFocusNext(page); - await page.waitForSelector(`${getEditorSelector(1)}:focus`); + await page.waitForSelector(`${editorSelector}:focus`); }) ); }); @@ -1552,16 +1567,18 @@ describe("Highlight Editor", () => { const x = rect.x + rect.width / 2; const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(0)); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -1596,11 +1613,13 @@ describe("Highlight Editor", () => { const x = rect.x + rect.width / 2; const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(0)); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2); @@ -1618,7 +1637,7 @@ describe("Highlight Editor", () => { await scrollIntoView(page, pageSelector); } - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); await page.waitForSelector( `.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]` ); @@ -1656,11 +1675,13 @@ describe("Highlight Editor", () => { const x = rect.x + rect.width / 2; const y = rect.y + rect.height / 2; await page.mouse.click(x, y, { count: 2, delay: 100 }); - await page.waitForSelector(getEditorSelector(0)); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToOne = Array.from(new Array(13).keys(), n => n + 2).concat( @@ -1673,7 +1694,7 @@ describe("Highlight Editor", () => { await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); await page.waitForSelector( `.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]` ); @@ -1715,7 +1736,7 @@ describe("Highlight Editor", () => { ); await page.keyboard.down("Shift"); for (let i = 0; i < 3; i++) { - await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown", { delay: 100 }); } await page.keyboard.up("Shift"); @@ -1730,7 +1751,7 @@ describe("Highlight Editor", () => { ); await page.keyboard.down("Shift"); for (let i = 0; i < 3; i++) { - await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown", { delay: 100 }); } await page.keyboard.up("Shift"); await page.waitForSelector(getEditorSelector(1)); @@ -1782,8 +1803,7 @@ describe("Highlight Editor", () => { await page.mouse.click(x, y, { count: 2, delay: 100 }); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector}:not(.selectedEditor)`); + await unselectEditor(page, editorSelector); const clickHandle = await waitForPointerUp(page); y = rect.y - rect.height; @@ -1853,8 +1873,7 @@ describe("Highlight Editor", () => { await page.mouse.click(x, y, { count: 3, delay: 100 }); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.keyboard.press("Escape"); - await page.waitForSelector(`${editorSelector}:not(.selectedEditor)`); + await unselectEditor(page, editorSelector); const clickHandle = await waitForPointerUp(page); y = rect.y - 3 * rect.height; @@ -2158,4 +2177,501 @@ describe("Highlight Editor", () => { ); }); }); + + describe("Undo deletion popup has the expected behaviour", () => { + let pages; + const editorSelector = getEditorSelector(0); + + beforeEach(async () => { + pages = await loadAndWait( + "tracemonkey.pdf", + ".annotationEditorLayer", + null, + null, + { + highlightEditorColors: + "yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000", + } + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that deleting a highlight can be undone using the undo button", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.click("#editorUndoBarUndoButton"); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + await page.waitForSelector( + `.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]` + ); + }) + ); + }); + + it("must check that the popup disappears when the undo button is clicked", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.click("#editorUndoBarUndoButton"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when the close button is clicked", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.waitForSelector("#editorUndoBarCloseButton"); + await page.click("#editorUndoBarCloseButton"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when a new annotation is created", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + const newRect = await getSpanRectFromText(page, 1, "Introduction"); + const newX = newRect.x + newRect.width / 2; + const newY = newRect.y + newRect.height / 2; + await page.mouse.click(newX, newY, { count: 2, delay: 100 }); + + await page.waitForSelector(getEditorSelector(1)); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when the print dialog is opened", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.evaluate(() => window.print()); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when the user clicks on the print button", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.click("#printButton"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when the save dialog is opened", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await kbSave(page); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when an option from the secondaryToolbar is used", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.click("#secondaryToolbarToggleButton"); + await page.click("#lastPage"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when highlight mode is disabled", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await switchToHighlight(page, /* disable */ true); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the popup disappears when a PDF is drag-and-dropped", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + const pdfPath = path.join(__dirname, "../pdfs/basicapi.pdf"); + const pdfData = fs.readFileSync(pdfPath).toString("base64"); + const dataTransfer = await page.evaluateHandle(data => { + const transfer = new DataTransfer(); + const view = Uint8Array.from(atob(data), code => + code.charCodeAt(0) + ); + const file = new File([view], "basicapi.pdf", { + type: "application/pdf", + }); + transfer.items.add(file); + return transfer; + }, pdfData); + + const dropSelector = "#viewer"; + await page.evaluate( + (transfer, selector) => { + const dropTarget = document.querySelector(selector); + const event = new DragEvent("dragstart", { + dataTransfer: transfer, + }); + dropTarget.dispatchEvent(event); + }, + dataTransfer, + dropSelector + ); + + await page.evaluate( + (transfer, selector) => { + const dropTarget = document.querySelector(selector); + const event = new DragEvent("drop", { + dataTransfer: transfer, + bubbles: true, + }); + dropTarget.dispatchEvent(event); + }, + dataTransfer, + dropSelector + ); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + + it("must check that the undo deletion popup displays the correct message", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForFunction(() => { + const messageElement = document.querySelector( + "#editorUndoBarMessage" + ); + return messageElement && messageElement.textContent.trim() !== ""; + }); + + const message = await page.waitForSelector("#editorUndoBarMessage"); + const messageText = await page.evaluate( + el => el.textContent, + message + ); + expect(messageText).toContain("Highlight removed"); + }) + ); + }); + + it("must display correct message for multiple highlights", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + let rect = await getSpanRectFromText(page, 1, "Abstract"); + let x = rect.x + rect.width / 2; + let y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + + rect = await getSpanRectFromText(page, 1, "Languages"); + x = rect.x + rect.width / 2; + y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(getEditorSelector(1)); + + await selectAll(page); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForFunction(() => { + const messageElement = document.querySelector( + "#editorUndoBarMessage" + ); + return messageElement && messageElement.textContent.trim() !== ""; + }); + + const message = await page.waitForSelector("#editorUndoBarMessage"); + const messageText = await page.evaluate( + el => el.textContent, + message + ); + + // Cleans the message text by removing all non-ASCII characters. + // It eliminates any invisible characters such as directional marks + // that interfere with string comparisons + const cleanMessage = messageText.replaceAll(/\P{ASCII}/gu, ""); + expect(cleanMessage).toContain(`2 annotations removed`); + }) + ); + }); + + it("must work properly when selecting undo by keyboard", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.focus("#editorUndoBarUndoButton"); // we have to simulate focus like this to avoid the wait + await page.keyboard.press("Enter"); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + await page.waitForSelector( + `.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]` + ); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.focus("#editorUndoBarUndoButton"); // we have to simulate focus like this to avoid the wait + await page.keyboard.press(" "); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + await page.waitForSelector( + `.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]` + ); + }) + ); + }); + + it("must dismiss itself when user presses space/enter key and undo key isn't focused", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const rect = await getSpanRectFromText(page, 1, "Abstract"); + const x = rect.x + rect.width / 2; + const y = rect.y + rect.height / 2; + await page.mouse.click(x, y, { count: 2, delay: 100 }); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.focus("#editorUndoBar"); + await page.keyboard.press("Enter"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + }); + + describe("Highlight mustn't trigger a scroll when edited", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue18911.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that there is no scroll because of focus", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToHighlight(page); + + const page4Selector = ".page[data-page-number = '4']"; + const page5Selector = ".page[data-page-number = '5']"; + await scrollIntoView(page, page4Selector); + await page.waitForSelector(`${page5Selector} .annotationEditorLayer`); + + // When moving to page 4, the highlight editor mustn't be focused (it + // was causing a scroll to page 5). + // So here we're waiting a bit and checking that the page is still 4. + // eslint-disable-next-line no-restricted-syntax + await waitForTimeout(100); + + // Get the length of the intersection between two ranges. + const inter = ([a, b], [c, d]) => + d < a || b < c ? 0 : Math.min(b, d) - Math.max(a, c); + + const page4Rect = await getRect(page, page4Selector); + const page5Rect = await getRect(page, page5Selector); + const viewportRect = await getRect(page, "#viewerContainer"); + const viewportRange = [ + viewportRect.y, + viewportRect.y + viewportRect.height, + ]; + + const interPage4 = inter( + [page4Rect.y, page4Rect.y + page4Rect.height], + viewportRange + ); + const interPage5 = inter( + [page5Rect.y, page5Rect.y + page5Rect.height], + viewportRange + ); + expect(interPage4) + .withContext(`In ${browserName}`) + .toBeGreaterThan(0.5 * interPage5); + }) + ); + }); + }); }); diff --git a/test/integration/ink_editor_spec.mjs b/test/integration/ink_editor_spec.mjs index 8cf5059dfb2ae..ea6634a5a2db3 100644 --- a/test/integration/ink_editor_spec.mjs +++ b/test/integration/ink_editor_spec.mjs @@ -15,38 +15,35 @@ import { awaitPromise, + clearEditors, closePages, - createPromise, + dragAndDrop, + getAnnotationSelector, + getEditors, getEditorSelector, getRect, - getSelectedEditors, + getSerialized, + isCanvasWhite, kbRedo, - kbSelectAll, kbUndo, loadAndWait, + moveEditor, scrollIntoView, + selectEditor, + selectEditors, switchToEditor, + waitForAnnotationModeChanged, + waitForNoElement, + waitForPointerUp, + waitForSelectedEditor, waitForSerialized, waitForStorageEntries, + waitForTimeout, } from "./test_utils.mjs"; -const waitForPointerUp = page => - createPromise(page, resolve => { - window.addEventListener("pointerup", resolve, { once: true }); - }); - -const selectAll = async page => { - await kbSelectAll(page); - await page.waitForFunction( - () => !document.querySelector(".inkEditor.disabled:not(.selectedEditor)") - ); -}; +const selectAll = selectEditors.bind(null, "ink"); -const clearAll = async page => { - await selectAll(page); - await page.keyboard.press("Backspace"); - await waitForStorageEntries(page, 0); -}; +const clearAll = clearEditors.bind(null, "ink"); const commit = async page => { await page.keyboard.press("Escape"); @@ -59,11 +56,11 @@ describe("Ink Editor", () => { describe("Basic operations", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -92,7 +89,7 @@ describe("Ink Editor", () => { await kbUndo(page); await waitForStorageEntries(page, 3); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([]); }) @@ -102,7 +99,7 @@ describe("Ink Editor", () => { it("must draw, undo/redo and check that the editor don't move", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await clearAll(page); + await switchToInk(page); const rect = await getRect(page, ".annotationEditorLayer"); @@ -117,7 +114,7 @@ describe("Ink Editor", () => { await commit(page); - const rectBefore = await getRect(page, ".inkEditor canvas"); + const rectBefore = await getRect(page, ".canvasWrapper .draw"); for (let i = 0; i < 30; i++) { await kbUndo(page); @@ -126,7 +123,7 @@ describe("Ink Editor", () => { await waitForStorageEntries(page, 1); } - const rectAfter = await getRect(page, ".inkEditor canvas"); + const rectAfter = await getRect(page, ".canvasWrapper .draw"); expect(Math.round(rectBefore.x)) .withContext(`In ${browserName}`) @@ -138,16 +135,60 @@ describe("Ink Editor", () => { }) ); }); + + it("must draw and move with the keyboard", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + + const x = rect.x + 100; + const y = rect.y + 100; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 50, y + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + + await commit(page); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); + const rectBefore = (await getSerialized(page, s => s.rect))[0]; + + const N = 20; + await moveEditor(page, editorSelector, N, () => + page.keyboard.press("ArrowDown") + ); + const rectAfter = (await getSerialized(page, s => s.rect))[0]; + + expect(Math.abs(rectBefore[0] - rectAfter[0])) + .withContext(`In ${browserName}`) + .toBeLessThan(1e-2); + expect(Math.abs(rectBefore[1] - N - rectAfter[1])) + .withContext(`In ${browserName}`) + .toBeLessThan(1e-2); + expect(Math.abs(rectBefore[2] - rectAfter[2])) + .withContext(`In ${browserName}`) + .toBeLessThan(1e-2); + expect(Math.abs(rectBefore[3] - N - rectAfter[3])) + .withContext(`In ${browserName}`) + .toBeLessThan(1e-2); + }) + ); + }); }); describe("with a rotated pdf", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("issue16278.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -171,7 +212,7 @@ describe("Ink Editor", () => { await selectAll(page); - expect(await getSelectedEditors(page)) + expect(await getEditors(page, "selected")) .withContext(`In ${browserName}`) .toEqual([0]); }) @@ -182,11 +223,11 @@ describe("Ink Editor", () => { describe("Invisible layers must be disabled", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -237,11 +278,11 @@ describe("Ink Editor", () => { describe("Ink editor must be committed when blurred", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -271,11 +312,11 @@ describe("Ink Editor", () => { describe("Undo a draw", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -296,16 +337,17 @@ describe("Ink Editor", () => { await awaitPromise(clickHandle); await commit(page); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -314,11 +356,11 @@ describe("Ink Editor", () => { describe("Delete a draw and undo it on another page", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -339,11 +381,12 @@ describe("Ink Editor", () => { await awaitPromise(clickHandle); await commit(page); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2); @@ -361,7 +404,7 @@ describe("Ink Editor", () => { await scrollIntoView(page, pageSelector); } - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -370,11 +413,11 @@ describe("Ink Editor", () => { describe("Delete a draw, scroll and undo it", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -395,11 +438,12 @@ describe("Ink Editor", () => { await awaitPromise(clickHandle); await commit(page); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${getEditorSelector(0)} button.delete`); - await page.click(`${getEditorSelector(0)} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToOne = Array.from(new Array(13).keys(), n => n + 2).concat( @@ -412,7 +456,7 @@ describe("Ink Editor", () => { await kbUndo(page); await waitForSerialized(page, 1); - await page.waitForSelector(getEditorSelector(0)); + await page.waitForSelector(editorSelector); }) ); }); @@ -421,11 +465,11 @@ describe("Ink Editor", () => { describe("Draw several times in the same editor", () => { let pages; - beforeAll(async () => { + beforeEach(async () => { pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); }); - afterAll(async () => { + afterEach(async () => { await closePages(pages); }); @@ -453,4 +497,721 @@ describe("Ink Editor", () => { ); }); }); + + describe("Drawing must unselect all", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that when we start to draw then the editors are unselected", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + const rect = await getRect(page, ".annotationEditorLayer"); + + let xStart = rect.x + 10; + const yStart = rect.y + 10; + for (let i = 0; i < 2; i++) { + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(xStart, yStart); + await page.mouse.down(); + if (i === 1) { + expect(await getEditors(page, "selected")) + .withContext(`In ${browserName}`) + .toEqual([]); + } + await page.mouse.move(xStart + 50, yStart + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + xStart += 70; + } + }) + ); + }); + }); + + describe("Selected editor must be updated even if the page has been destroyed", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the color has been changed", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + + const x = rect.x + 20; + const y = rect.y + 20; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 50, y + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + + await commit(page); + + const drawSelector = `.page[data-page-number = "1"] .canvasWrapper .draw`; + await page.waitForSelector(drawSelector, { visible: true }); + let color = await page.evaluate(sel => { + const el = document.querySelector(sel); + return el.getAttribute("stroke"); + }, drawSelector); + expect(color).toEqual("#000000"); + + const oneToFourteen = Array.from(new Array(13).keys(), n => n + 2); + for (const pageNumber of oneToFourteen) { + await scrollIntoView( + page, + `.page[data-page-number = "${pageNumber}"]` + ); + } + + const red = "#ff0000"; + page.evaluate(value => { + window.PDFViewerApplication.eventBus.dispatch( + "switchannotationeditorparams", + { + source: null, + type: window.pdfjsLib.AnnotationEditorParamsType.INK_COLOR, + value, + } + ); + }, red); + + const fourteenToOne = Array.from(new Array(13).keys(), n => 13 - n); + for (const pageNumber of fourteenToOne) { + await scrollIntoView( + page, + `.page[data-page-number = "${pageNumber}"]` + ); + } + await page.waitForSelector(drawSelector, { visible: true }); + color = await page.evaluate(sel => { + const el = document.querySelector(sel); + return el.getAttribute("stroke"); + }, drawSelector); + expect(color).toEqual(red); + }) + ); + }); + }); + + describe("Can delete the drawing in progress and undo the deletion", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the deletion has been undid", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + + const x = rect.x + 20; + const y = rect.y + 20; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 50, y + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + + const drawSelector = `.canvasWrapper svg.draw path[d]:not([d=""])`; + await page.waitForSelector(drawSelector); + + await page.keyboard.press("Backspace"); + + const editorSelector = getEditorSelector(0); + await waitForNoElement(page, drawSelector); + await waitForNoElement(page, editorSelector); + + await kbUndo(page); + await page.waitForSelector(editorSelector, { visible: true }); + await page.waitForSelector(drawSelector); + }) + ); + }); + }); + + describe("Annotation mustn't take focus if it isn't visible", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the focus isn't taken", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + + const x = rect.x + 20; + const y = rect.y + 20; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 50, y + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + + await page.evaluate(() => { + window.focusedIds = []; + window.focusCallback = e => { + window.focusedIds.push(e.target.id); + }; + window.addEventListener("focusin", window.focusCallback); + }); + + const oneToFourteen = Array.from(new Array(13).keys(), n => n + 2); + for (const pageNumber of oneToFourteen) { + await scrollIntoView( + page, + `.page[data-page-number = "${pageNumber}"]` + ); + } + + const ids = await page.evaluate(() => { + const { focusedIds, focusCallback } = window; + window.removeEventListener("focusin", focusCallback); + delete window.focusCallback; + delete window.focusedIds; + return focusedIds; + }); + + expect(ids).withContext(`In ${browserName}`).toEqual([]); + }) + ); + }); + }); + + describe("Ink (update existing)", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("inks.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must update an existing annotation", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const annotationsRect = await page.evaluate(() => { + let xm = Infinity, + xM = -Infinity, + ym = Infinity, + yM = -Infinity; + for (const el of document.querySelectorAll( + "section.inkAnnotation" + )) { + const { x, y, width, height } = el.getBoundingClientRect(); + xm = Math.min(xm, x); + xM = Math.max(xM, x + width); + ym = Math.min(ym, y); + yM = Math.max(yM, y + height); + } + return { x: xm, y: ym, width: xM - xm, height: yM - ym }; + }); + + await switchToInk(page); + + // The page has been re-rendered but with no ink annotations. + let isWhite = await isCanvasWhite(page, 1, annotationsRect); + expect(isWhite).withContext(`In ${browserName}`).toBeTrue(); + + let editorIds = await getEditors(page, "ink"); + expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15); + + const pdfjsA = getEditorSelector(0); + const editorRect = await getRect(page, pdfjsA); + await selectEditor(page, pdfjsA); + + const red = "#ff0000"; + page.evaluate(value => { + window.PDFViewerApplication.eventBus.dispatch( + "switchannotationeditorparams", + { + source: null, + type: window.pdfjsLib.AnnotationEditorParamsType.INK_COLOR, + value, + } + ); + }, red); + + const serialized = await getSerialized(page); + expect(serialized.length).withContext(`In ${browserName}`).toEqual(1); + expect(serialized[0].color).toEqual([255, 0, 0]); + + // Disable editing mode. + await switchToInk(page, /* disable = */ true); + + // We want to check that the editor is displayed but not the original + // canvas. + editorIds = await getEditors(page, "ink"); + expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1); + + isWhite = await isCanvasWhite(page, 1, editorRect); + expect(isWhite).withContext(`In ${browserName}`).toBeTrue(); + + // Check we've now a svg with a red stroke. + await page.waitForSelector("svg[stroke = '#ff0000']", { + visible: true, + }); + + // Re-enable editing mode. + await switchToInk(page); + await page.focus(".annotationEditorLayer"); + + await kbUndo(page); + await waitForSerialized(page, 0); + + editorIds = await getEditors(page, "ink"); + expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15); + + // Undo again. + await kbUndo(page); + // Nothing should happen, it's why we can't wait for something + // specific! + // eslint-disable-next-line no-restricted-syntax + await waitForTimeout(200); + + // We check that the editor hasn't been removed. + editorIds = await getEditors(page, "ink"); + expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15); + }) + ); + }); + }); + + describe("Ink (move existing)", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("inks.pdf", getAnnotationSelector("277R")); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must move an annotation", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const modeChangedHandle = await waitForAnnotationModeChanged(page); + const inkRect = await getRect(page, getAnnotationSelector("277R")); + await page.mouse.click( + inkRect.x + inkRect.width / 2, + inkRect.y + inkRect.height / 2, + { count: 2 } + ); + await awaitPromise(modeChangedHandle); + const edgeB = getEditorSelector(10); + await waitForSelectedEditor(page, edgeB); + + const editorIds = await getEditors(page, "ink"); + expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15); + + // All the current annotations should be serialized as null objects + // because they haven't been edited yet. + const serialized = await getSerialized(page); + expect(serialized).withContext(`In ${browserName}`).toEqual([]); + + // Select the annotation we want to move. + await selectEditor(page, edgeB); + + await dragAndDrop(page, edgeB, [[100, 100]]); + await waitForSerialized(page, 1); + }) + ); + }); + }); + + describe("Undo deletion popup has the expected behaviour", () => { + let pages; + const editorSelector = getEditorSelector(0); + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that deleting a drawing can be undone using the undo button", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + const xStart = rect.x + 300; + const yStart = rect.y + 300; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(xStart, yStart); + await page.mouse.down(); + await page.mouse.move(xStart + 50, yStart + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForSelector("#editorUndoBar:not([hidden])"); + await page.click("#editorUndoBarUndoButton"); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + }) + ); + }); + + it("must check that the undo deletion popup displays the correct message", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + const xStart = rect.x + 300; + const yStart = rect.y + 300; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(xStart, yStart); + await page.mouse.down(); + await page.mouse.move(xStart + 50, yStart + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForFunction(() => { + const messageElement = document.querySelector( + "#editorUndoBarMessage" + ); + return messageElement && messageElement.textContent.trim() !== ""; + }); + const message = await page.waitForSelector("#editorUndoBarMessage"); + const messageText = await page.evaluate( + el => el.textContent, + message + ); + expect(messageText).toContain("Drawing removed"); + }) + ); + }); + + it("must check that the popup disappears when a new drawing is created", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + const xStart = rect.x + 300; + const yStart = rect.y + 300; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(xStart, yStart); + await page.mouse.down(); + await page.mouse.move(xStart + 50, yStart + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + const newRect = await getRect(page, ".annotationEditorLayer"); + const newXStart = newRect.x + 300; + const newYStart = newRect.y + 300; + const newClickHandle = await waitForPointerUp(page); + await page.mouse.move(newXStart, newYStart); + await page.mouse.down(); + await page.mouse.move(newXStart + 50, newYStart + 50); + await page.mouse.up(); + await awaitPromise(newClickHandle); + await commit(page); + + await page.waitForSelector(getEditorSelector(1)); + await waitForSerialized(page, 1); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + }); + + describe("Ink must update its stroke width when not the current active layer", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the stroke width has been updated after zooming", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + const rect = await getRect(page, ".annotationEditorLayer"); + + const x = rect.x + 20; + const y = rect.y + 20; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 50, y + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + + const svgSelector = ".canvasWrapper svg.draw"; + const strokeWidth = await page.$eval(svgSelector, el => + parseFloat(el.getAttribute("stroke-width")) + ); + + await scrollIntoView(page, `.page[data-page-number = "2"]`); + + const rectPageTwo = await getRect( + page, + `.page[data-page-number = "2"] .annotationEditorLayer` + ); + const originX = rectPageTwo.x + rectPageTwo.width / 2; + const originY = rectPageTwo.y + rectPageTwo.height / 2; + await page.evaluate( + origin => { + window.PDFViewerApplication.pdfViewer.increaseScale({ + scaleFactor: 1.5, + origin, + }); + }, + [originX, originY] + ); + + const newStrokeWidth = await page.$eval(svgSelector, el => + parseFloat(el.getAttribute("stroke-width")) + ); + + expect(newStrokeWidth) + .withContext(`In ${browserName}`) + .not.toEqual(strokeWidth); + }) + ); + }); + }); + + describe("Draw annotations on several page, move one of them and delete it", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "tracemonkey.pdf", + ".annotationEditorLayer", + 10 + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the first annotation is correctly associated with its SVG", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + for (let i = 0; i < 2; i++) { + const pageSelector = `.page[data-page-number = "${i + 1}"]`; + const rect = await getRect( + page, + `${pageSelector} .annotationEditorLayer` + ); + const xStart = rect.x + 10; + const yStart = rect.y + 10; + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(xStart, yStart); + await page.mouse.down(); + await page.mouse.move(xStart + 10, yStart + 10); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + } + + const pageOneSelector = `.page[data-page-number = "1"]`; + const initialRect = await getRect(page, `${pageOneSelector} svg`); + + let editorSelector = getEditorSelector(1); + await waitForSelectedEditor(page, editorSelector); + await dragAndDrop(page, editorSelector, [[0, -30]], /* steps = */ 10); + await waitForSerialized(page, 2); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 1); + await page.click("#editorUndoBarUndoButton"); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + + editorSelector = getEditorSelector(0); + await selectEditor(page, editorSelector); + + await dragAndDrop(page, editorSelector, [[30, 30]], /* steps = */ 10); + const finalRect = await getRect(page, `${pageOneSelector} svg`); + + expect(initialRect) + .withContext(`In ${browserName}`) + .not.toEqual(finalRect); + }) + ); + }); + }); + + describe("Page position should remain unchanged after drawing", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the page position remains the same after drawing", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const pageInitialPosition = await getRect( + page, + ".page[data-page-number='1']" + ); + + await switchToInk(page); + + const editorLayerRect = await getRect(page, ".annotationEditorLayer"); + const drawStartX = editorLayerRect.x + 100; + const drawStartY = editorLayerRect.y + 100; + + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(drawStartX, drawStartY); + await page.mouse.down(); + await page.mouse.move(drawStartX + 50, drawStartY + 50); + await page.mouse.up(); + await awaitPromise(clickHandle); + await commit(page); + + const pageFinalPosition = await getRect( + page, + ".page[data-page-number='1']" + ); + + expect(pageInitialPosition.x) + .withContext(`In ${browserName}`) + .toEqual(pageFinalPosition.x); + + expect(pageInitialPosition.y) + .withContext(`In ${browserName}`) + .toEqual(pageFinalPosition.y); + }) + ); + }); + }); +}); + +describe("The pen-drawn shape must maintain correct curvature regardless of the page it is drawn on or whether the curve's endpoint lies within or beyond the page boundaries", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + const getCurveOnPage = async ({ pageNumber = 1, page }) => { + const clickHandle = await waitForPointerUp(page); + const pageSelector = `.page[data-page-number = "${pageNumber}"]`; + await scrollIntoView(page, pageSelector); + await page.waitForSelector(pageSelector); + const rect = await getRect(page, `${pageSelector} .annotationEditorLayer`); + const x = rect.x + 100; + const y = rect.y + 200; + await page.mouse.move(x, y); + await page.mouse.down(); + // Create a reference curve on first page. + await page.mouse.move(x - 100, y); + if (page !== 1) { + // Add a move to create a curve that extends beyond the page boundary. + await page.mouse.move(x - 200, y); + } + await page.mouse.up(); + await awaitPromise(clickHandle); + const d = await page.$eval( + `${pageSelector} .canvasWrapper svg.draw path[d]:not([d=""])`, + el => el.getAttribute("d") + ); + return d; + }; + + it("must retain correct curvature regardless of the page or the curve's endpoint location", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToInk(page); + + // Creating a reference curve on the first page with end + // within the page boundaries. + const d1 = await getCurveOnPage({ pageNumber: 1, page }); + + // Creating a curve on the second page with end + // beyond the page boundaries. + const d2 = await getCurveOnPage({ pageNumber: 2, page }); + + // Expect that the endpoint beyond the boundaries is ignored, + // ensuring both curves have the same shape on both pages. + expect(d1).withContext(`In ${browserName}`).toEqual(d2); + }) + ); + }); }); diff --git a/test/integration-boot.mjs b/test/integration/jasmine-boot.js similarity index 95% rename from test/integration-boot.mjs rename to test/integration/jasmine-boot.js index 4e04bb6f1f5d6..5c74aa958d4fa 100644 --- a/test/integration-boot.mjs +++ b/test/integration/jasmine-boot.js @@ -13,6 +13,8 @@ * limitations under the License. */ +/* eslint-disable no-console */ + import Jasmine from "jasmine"; async function runTests(results) { @@ -26,6 +28,7 @@ async function runTests(results) { spec_files: [ "accessibility_spec.mjs", "annotation_spec.mjs", + "autolinker_spec.mjs", "caret_browsing_spec.mjs", "copy_paste_spec.mjs", "find_spec.mjs", @@ -33,6 +36,7 @@ async function runTests(results) { "highlight_editor_spec.mjs", "ink_editor_spec.mjs", "scripting_spec.mjs", + "signature_editor_spec.mjs", "stamp_editor_spec.mjs", "text_field_spec.mjs", "text_layer_spec.mjs", diff --git a/test/integration/scripting_spec.mjs b/test/integration/scripting_spec.mjs index 715857f7022f3..9ada96fb50b95 100644 --- a/test/integration/scripting_spec.mjs +++ b/test/integration/scripting_spec.mjs @@ -1060,38 +1060,26 @@ describe("Interaction", () => { it("must check input for US zip format", async () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. - for (const [browserName, page] of pages) { + for (const [, page] of pages) { await waitForScripting(page); - await clearInput(page, getSelector("29R")); - await clearInput(page, getSelector("30R")); - await page.focus(getSelector("29R")); await typeAndWaitForSandbox(page, getSelector("29R"), "12A"); - await page.waitForFunction( - `${getQuerySelector("29R")}.value !== "12A"` - ); - - let text = await page.$eval(getSelector(`29R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("12"); + await page.waitForFunction(`${getQuerySelector("29R")}.value === "12"`); await page.focus(getSelector("29R")); await typeAndWaitForSandbox(page, getSelector("29R"), "34"); await page.click("[data-annotation-id='30R']"); - - await page.waitForFunction( - `${getQuerySelector("29R")}.value !== "1234"` - ); - - text = await page.$eval(getSelector(`29R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual(""); + await waitForSandboxTrip(page); + await page.waitForFunction(`${getQuerySelector("29R")}.value === ""`); await page.focus(getSelector("29R")); await typeAndWaitForSandbox(page, getSelector("29R"), "12345"); await page.click("[data-annotation-id='30R']"); - - text = await page.$eval(getSelector(`29R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("12345"); + await waitForSandboxTrip(page); + await page.waitForFunction( + `${getQuerySelector("29R")}.value === "12345"` + ); } }); }); @@ -1115,38 +1103,28 @@ describe("Interaction", () => { it("must check input for US phone number (long) format", async () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. - for (const [browserName, page] of pages) { + for (const [, page] of pages) { await waitForScripting(page); - await clearInput(page, getSelector("29R")); - await clearInput(page, getSelector("30R")); - await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "(123) 456A"); await page.waitForFunction( - `${getQuerySelector("30R")}.value !== "(123) 456A"` + `${getQuerySelector("30R")}.value === "(123) 456"` ); - let text = await page.$eval(getSelector(`30R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("(123) 456"); - await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "-789"); await page.click("[data-annotation-id='29R']"); - - await page.waitForFunction( - `${getQuerySelector("30R")}.value !== "(123) 456-789"` - ); - - text = await page.$eval(getSelector(`30R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual(""); + await waitForSandboxTrip(page); + await page.waitForFunction(`${getQuerySelector("30R")}.value === ""`); await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "(123) 456-7890"); await page.click("[data-annotation-id='29R']"); - - text = await page.$eval(getSelector("30R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("(123) 456-7890"); + await waitForSandboxTrip(page); + await page.waitForFunction( + `${getQuerySelector("30R")}.value === "(123) 456-7890"` + ); } }); }); @@ -1170,38 +1148,28 @@ describe("Interaction", () => { it("must check input for US phone number (short) format", async () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. - for (const [browserName, page] of pages) { + for (const [, page] of pages) { await waitForScripting(page); - await clearInput(page, getSelector("29R")); - await clearInput(page, getSelector("30R")); - await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "123A"); await page.waitForFunction( - `${getQuerySelector("30R")}.value !== "123A"` + `${getQuerySelector("30R")}.value === "123"` ); - let text = await page.$eval(getSelector(`30R`), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("123"); - await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "-456"); await page.click("[data-annotation-id='29R']"); - - await page.waitForFunction( - `${getQuerySelector("30R")}.value !== "123-456"` - ); - - text = await page.$eval(getSelector("30R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual(""); + await waitForSandboxTrip(page); + await page.waitForFunction(`${getQuerySelector("30R")}.value === ""`); await page.focus(getSelector("30R")); await typeAndWaitForSandbox(page, getSelector("30R"), "123-4567"); await page.click("[data-annotation-id='29R']"); - - text = await page.$eval(getSelector("30R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("123-4567"); + await waitForSandboxTrip(page); + await page.waitForFunction( + `${getQuerySelector("30R")}.value === "123-4567"` + ); } }); }); @@ -1229,59 +1197,50 @@ describe("Interaction", () => { await typeAndWaitForSandbox(page, getSelector("27R"), "Hello"); await page.waitForFunction( - `${getQuerySelector("27R")}.value !== "Hello"` + `${getQuerySelector("27R")}.value === "HELLO"` ); - let text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HELLO"); - await typeAndWaitForSandbox(page, getSelector("27R"), " world"); await page.waitForFunction( - `${getQuerySelector("27R")}.value !== "HELLO world"` + `${getQuerySelector("27R")}.value === "HELLO WORLD"` ); - text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HELLO WORLD"); - await page.keyboard.press("Backspace"); + await waitForSandboxTrip(page); await page.keyboard.press("Backspace"); - + await waitForSandboxTrip(page); await page.waitForFunction( - `${getQuerySelector("27R")}.value !== "HELLO WORLD"` + `${getQuerySelector("27R")}.value === "HELLO WOR"` ); - text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HELLO WOR"); - await typeAndWaitForSandbox(page, getSelector("27R"), "12.dL"); - await page.waitForFunction( - `${getQuerySelector("27R")}.value !== "HELLO WOR"` + `${getQuerySelector("27R")}.value === "HELLO WORDL"` ); - text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HELLO WORDL"); - await typeAndWaitForSandbox(page, getSelector("27R"), " "); - await kbDeleteLastWord(page); - + await waitForSandboxTrip(page); await page.waitForFunction( - `${getQuerySelector("27R")}.value !== "HELLO WORDL "` + `${getQuerySelector("27R")}.value === "HELLO "` ); - text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HELLO "); - await page.$eval(getSelector("27R"), el => { // Select LL el.selectionStart = 2; el.selectionEnd = 4; }); + await typeAndWaitForSandbox(page, getSelector("27R"), "a"); + await page.waitForFunction( + `${getQuerySelector("27R")}.value === "HEAO "` + ); - await page.keyboard.press("a"); - text = await page.$eval(getSelector("27R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("HEAO "); + // The typing actions in the first textbox caused sandbox events to be + // queued. We don't close the document between tests, so we have to + // flush them here, by clicking the second textbox, so they don't leak + // through to the following test. + await page.click(getSelector("28R")); + await waitForSandboxTrip(page); }) ); }); @@ -1292,30 +1251,19 @@ describe("Interaction", () => { await waitForScripting(page); await page.click(getSelector("28R")); - await page.$eval(getSelector("28R"), el => - el.setSelectionRange(0, 0) - ); - + await page.keyboard.press("Home"); await page.type(getSelector("28R"), "Hello"); await page.waitForFunction( - `${getQuerySelector("28R")}.value !== "123"` + `${getQuerySelector("28R")}.value === "Hello123"` ); - let text = await page.$eval(getSelector("28R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("Hello123"); - - // The action will trigger a calculateNow which itself - // will trigger a resetForm (inducing a calculateNow) and a - // calculateNow. + // The action triggers a `calculateNow` which in turn triggers a + // `resetForm (inducing a `calculateNow`) and a `calculateNow`. + // Without infinite loop prevention the field would be empty. await page.click("[data-annotation-id='31R']"); - await page.waitForFunction( - `${getQuerySelector("28R")}.value !== "Hello123"` + `${getQuerySelector("28R")}.value === "123"` ); - - // Without preventing against infinite loop the field is empty. - text = await page.$eval(getSelector("28R"), el => el.value); - expect(text).withContext(`In ${browserName}`).toEqual("123"); }) ); }); @@ -2542,4 +2490,71 @@ describe("Interaction", () => { ); }); }); + + describe("Date creation must be timezone consistent", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("bug1934157.pdf", "[data-annotation-id='24R']"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that date entered by the user is consistent", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + if (browserName === "firefox") { + // Skip the test for Firefox as it doesn't support the timezone + // feature yet with BiDi. + // See https://github.com/puppeteer/puppeteer/issues/13344. + // TODO: Remove this check once the issue is fixed. + return; + } + + await waitForScripting(page); + + await page.emulateTimezone("Pacific/Honolulu"); + + const expectedDate = "02/01/2000"; + await page.type(getSelector("24R"), expectedDate); + await page.click(getSelector("25R")); + await waitForSandboxTrip(page); + + const date = await page.$eval(getSelector("24R"), el => el.value); + expect(date).withContext(`In ${browserName}`).toEqual(expectedDate); + }) + ); + }); + }); + + describe("Skip throwing actions (issue 19505)", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue19505.pdf", "[data-annotation-id='24R']"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that date entered are in the input", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForScripting(page); + + const fieldSelector = getSelector("24R"); + for (const c of "Hello World") { + await page.type(fieldSelector, c); + await waitForSandboxTrip(page); + } + + const value = await page.$eval(fieldSelector, el => el.value); + expect(value).withContext(`In ${browserName}`).toEqual("Hello World"); + }) + ); + }); + }); }); diff --git a/test/integration/signature_editor_spec.mjs b/test/integration/signature_editor_spec.mjs new file mode 100644 index 0000000000000..45bfde375d006 --- /dev/null +++ b/test/integration/signature_editor_spec.mjs @@ -0,0 +1,468 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + awaitPromise, + closePages, + copy, + getEditorSelector, + getRect, + loadAndWait, + paste, + switchToEditor, + waitForPointerUp, + waitForTimeout, +} from "./test_utils.mjs"; + +import { fileURLToPath } from "url"; +import path from "path"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const switchToSignature = switchToEditor.bind(null, "Signature"); + +describe("Signature Editor", () => { + const descriptionInputSelector = "#addSignatureDescription > input"; + const addButtonSelector = "#addSignatureAddButton"; + + describe("Basic operations", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the editor has been removed when the dialog is cancelled", async () => { + await Promise.all( + pages.map(async ([_, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + + // An invisible editor is created but invisible. + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector, { visible: false }); + + await page.click("#addSignatureCancelButton"); + + // The editor should have been removed. + await page.waitForSelector(`:not(${editorSelector})`); + }) + ); + }); + + it("must check that the basic and common elements are working as expected", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + + await page.waitForSelector( + "#addSignatureTypeButton[aria-selected=true]" + ); + await page.click("#addSignatureTypeInput"); + await page.waitForSelector( + "#addSignatureSaveContainer[disabled=true]" + ); + let description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual(""); + await page.waitForSelector(`${addButtonSelector}:disabled`); + + await page.type("#addSignatureTypeInput", "PDF.js"); + await page.waitForSelector(`${addButtonSelector}:not(:disabled)`); + + // The save button should be enabled now. + await page.waitForSelector( + "#addSignatureSaveContainer:not([disabled])" + ); + await page.waitForSelector("#addSignatureSaveCheckbox[checked=true]"); + + // The description has been filled in automatically. + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value !== ""` + ); + description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual("PDF.js"); + + // Clear the description. + await page.click("#addSignatureDescription > button"); + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value === ""` + ); + + // Clear the text for the signature. + await page.click("#clearSignatureButton"); + await page.waitForFunction( + `document.querySelector("#addSignatureTypeInput").value === ""` + ); + // The save button should be disabled now. + await page.waitForSelector( + "#addSignatureSaveContainer[disabled=true]" + ); + await page.waitForSelector(`${addButtonSelector}:disabled`); + + await page.type("#addSignatureTypeInput", "PDF.js"); + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value !== ""` + ); + + // Clearing the signature type should clear the description. + await page.click("#clearSignatureButton"); + await page.waitForFunction( + `document.querySelector("#addSignatureTypeInput").value === ""` + ); + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value === ""` + ); + + // Add a signature and change the description. + await page.type("#addSignatureTypeInput", "PDF.js"); + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value !== ""` + ); + await page.click("#addSignatureDescription > button"); + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value === ""` + ); + await page.type(descriptionInputSelector, "Hello World"); + await page.type("#addSignatureTypeInput", "Hello"); + + // The description mustn't be changed. + // eslint-disable-next-line no-restricted-syntax + await waitForTimeout(100); + description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual("Hello World"); + + await page.click("#addSignatureAddButton"); + await page.waitForSelector("#addSignatureDialog", { + visible: false, + }); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector, { visible: true }); + await page.waitForSelector( + `.canvasWrapper > svg use[href="#path_p1_0"]`, + { visible: true } + ); + + // Check the tooltip. + await page.waitForSelector( + `.altText.editDescription[title="Hello World"]` + ); + + // Edit the description. + await page.click(`.altText.editDescription`); + + await page.waitForSelector("#editSignatureDescriptionDialog", { + visible: true, + }); + await page.waitForSelector("#editSignatureUpdateButton:disabled"); + await page.waitForSelector( + `#editSignatureDescriptionDialog svg[aria-label="Hello World"]` + ); + const editDescriptionInput = "#editSignatureDescription > input"; + description = await page.$eval(editDescriptionInput, el => el.value); + expect(description).withContext(browserName).toEqual("Hello World"); + await page.click("#editSignatureDescription > button"); + await page.waitForFunction( + `document.querySelector("${editDescriptionInput}").value === ""` + ); + await page.waitForSelector( + "#editSignatureUpdateButton:not(:disabled)" + ); + await page.type(editDescriptionInput, "Hello PDF.js World"); + await page.waitForSelector( + "#editSignatureUpdateButton:not(:disabled)" + ); + + await page.click("#editSignatureUpdateButton"); + + // Check the tooltip. + await page.waitForSelector( + `.altText.editDescription[title="Hello PDF.js World"]` + ); + }) + ); + }); + + it("must check drawing with the mouse", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + + await page.click("#addSignatureDrawButton"); + const drawSelector = "#addSignatureDraw"; + await page.waitForSelector(drawSelector, { visible: true }); + + let description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual(""); + await page.waitForSelector(`${addButtonSelector}:disabled`); + + const { x, y, width, height } = await getRect(page, drawSelector); + const clickHandle = await waitForPointerUp(page); + await page.mouse.move(x + 0.1 * width, y + 0.1 * height); + await page.mouse.down(); + await page.mouse.move(x + 0.3 * width, y + 0.3 * height); + await page.mouse.up(); + await awaitPromise(clickHandle); + await page.waitForSelector(`${addButtonSelector}:not(:disabled)`); + + // The save button should be enabled now. + await page.waitForSelector( + "#addSignatureSaveContainer:not([disabled])" + ); + await page.waitForSelector("#addSignatureSaveCheckbox[checked=true]"); + + // The description has been filled in automatically. + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value !== ""` + ); + description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual("Signature"); + + await page.click("#addSignatureAddButton"); + await page.waitForSelector("#addSignatureDialog", { + visible: false, + }); + + await page.waitForSelector( + ".canvasWrapper > svg use[href='#path_p1_0']" + ); + }) + ); + }); + + it("must check adding an image", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + + await page.click("#addSignatureImageButton"); + await page.waitForSelector("#addSignatureImagePlaceholder", { + visible: true, + }); + + let description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description).withContext(browserName).toEqual(""); + await page.waitForSelector(`${addButtonSelector}:disabled`); + + const input = await page.$("#addSignatureFilePicker"); + await input.uploadFile( + `${path.join(__dirname, "../images/firefox_logo.png")}` + ); + await page.waitForSelector(`#addSignatureImage > path:not([d=""])`); + + // The save button should be enabled now. + await page.waitForSelector( + "#addSignatureSaveContainer:not([disabled])" + ); + await page.waitForSelector("#addSignatureSaveCheckbox[checked=true]"); + + // The description has been filled in automatically. + await page.waitForFunction( + `document.querySelector("${descriptionInputSelector}").value !== ""` + ); + description = await page.$eval( + descriptionInputSelector, + el => el.value + ); + expect(description) + .withContext(browserName) + .toEqual("firefox_logo.png"); + + await page.click("#addSignatureAddButton"); + await page.waitForSelector("#addSignatureDialog", { + visible: false, + }); + + await page.waitForSelector( + ".canvasWrapper > svg use[href='#path_p1_0']" + ); + }) + ); + }); + + it("must check copy and paste", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + await page.type("#addSignatureTypeInput", "Hello"); + await page.waitForSelector(`${addButtonSelector}:not(:disabled)`); + await page.click("#addSignatureAddButton"); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector, { visible: true }); + const originalRect = await getRect(page, editorSelector); + const originalDescription = await page.$eval( + `${editorSelector} .altText.editDescription`, + el => el.title + ); + + await copy(page); + await paste(page); + + const pastedEditorSelector = getEditorSelector(1); + await page.waitForSelector(pastedEditorSelector, { visible: true }); + const pastedRect = await getRect(page, pastedEditorSelector); + const pastedDescription = await page.$eval( + `${pastedEditorSelector} .altText.editDescription`, + el => el.title + ); + + expect(pastedRect) + .withContext(`In ${browserName}`) + .not.toEqual(originalRect); + expect(pastedDescription) + .withContext(`In ${browserName}`) + .toEqual(originalDescription); + }) + ); + }); + }); + + describe("Bug 1948741", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the editor isn't too large", async () => { + await Promise.all( + pages.map(async ([_, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + await page.type( + "#addSignatureTypeInput", + "[18:50:03] asset pdf.scripting.mjs 105 KiB [emitted] [javascript module] (name: main)" + ); + await page.waitForSelector(`${addButtonSelector}:not(:disabled)`); + await page.click("#addSignatureAddButton"); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector, { visible: true }); + await page.waitForSelector( + `.canvasWrapper > svg use[href="#path_p1_0"]`, + { visible: true } + ); + + const { width } = await getRect(page, editorSelector); + const { width: pageWidth } = await getRect(page, ".page"); + expect(width).toBeLessThanOrEqual(pageWidth); + }) + ); + }); + }); + + describe("Bug 1949201", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the error panel is correctly removed", async () => { + await Promise.all( + pages.map(async ([_, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + await page.click("#addSignatureImageButton"); + await page.waitForSelector("#addSignatureImagePlaceholder", { + visible: true, + }); + const input = await page.$("#addSignatureFilePicker"); + await input.uploadFile( + `${path.join(__dirname, "./signature_editor_spec.mjs")}` + ); + await page.waitForSelector("#addSignatureError", { visible: true }); + await page.click("#addSignatureErrorCloseButton"); + await page.waitForSelector("#addSignatureError", { visible: false }); + + await input.uploadFile( + `${path.join(__dirname, "./stamp_editor_spec.mjs")}` + ); + await page.waitForSelector("#addSignatureError", { visible: true }); + + await page.click("#addSignatureTypeButton"); + await page.waitForSelector( + "#addSignatureTypeButton[aria-selected=true]" + ); + await page.waitForSelector("#addSignatureError", { visible: false }); + await page.click("#addSignatureCancelButton"); + }) + ); + }); + }); +}); diff --git a/test/integration/stamp_editor_spec.mjs b/test/integration/stamp_editor_spec.mjs index b7603a0f99db9..fbd153762d7e2 100644 --- a/test/integration/stamp_editor_spec.mjs +++ b/test/integration/stamp_editor_spec.mjs @@ -16,11 +16,13 @@ import { applyFunctionToEditor, awaitPromise, + cleanupEditing, + clearEditors, clearInput, closePages, copy, copyToClipboard, - dragAndDropAnnotation, + dragAndDrop, getAnnotationSelector, getEditorDimensions, getEditors, @@ -31,21 +33,21 @@ import { isVisible, kbBigMoveDown, kbBigMoveRight, - kbSelectAll, kbUndo, loadAndWait, paste, pasteFromClipboard, scrollIntoView, + selectEditor, serializeBitmapDimensions, switchToEditor, + unselectEditor, waitForAnnotationEditorLayer, + waitForAnnotationModeChanged, waitForEntryInStorage, waitForSelectedEditor, waitForSerialized, - waitForStorageEntries, waitForTimeout, - waitForUnselectedEditor, } from "./test_utils.mjs"; import { fileURLToPath } from "url"; import fs from "fs"; @@ -54,18 +56,7 @@ import { PNG } from "pngjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const selectAll = async page => { - await kbSelectAll(page); - await page.waitForFunction( - () => !document.querySelector(".stampEditor:not(.selectedEditor)") - ); -}; - -const clearAll = async page => { - await selectAll(page); - await page.keyboard.press("Backspace"); - await waitForStorageEntries(page, 0); -}; +const clearAll = clearEditors.bind(null, "stamp"); const waitForImage = async (page, selector) => { await page.waitForSelector(`${selector} canvas`); @@ -83,7 +74,7 @@ const waitForImage = async (page, selector) => { await page.waitForSelector(`${selector} .altText`); }; -const copyImage = async (page, imagePath, number) => { +const copyImage = async (page, imagePath, selector) => { const data = fs .readFileSync(path.join(__dirname, imagePath)) .toString("base64"); @@ -91,9 +82,17 @@ const copyImage = async (page, imagePath, number) => { await copyToClipboard(page, { "image/png": `data:image/png;base64,${data}` }); await pasteFromClipboard(page); - await waitForImage(page, getEditorSelector(number)); + await waitForImage(page, selector); }; +async function waitForTranslation(page) { + return page.evaluate(async () => { + await new Promise(resolve => { + window.requestAnimationFrame(resolve); + }); + }); +} + const switchToStamp = switchToEditor.bind(null, "Stamp"); describe("Stamp Editor", () => { @@ -111,13 +110,7 @@ describe("Stamp Editor", () => { }); afterEach(async () => { - for (const [, page] of pages) { - await page.evaluate(() => { - window.uiManager.reset(); - }); - // Disable editing mode. - await switchToStamp(page, /* disable */ true); - } + await cleanupEditing(pages, switchToStamp); }); afterAll(async () => { @@ -134,9 +127,10 @@ describe("Stamp Editor", () => { await input.uploadFile( `${path.join(__dirname, "../images/firefox_logo.png")}` ); - await waitForImage(page, getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await waitForImage(page, editorSelector); - const { width } = await getEditorDimensions(page, 0); + const { width } = await getEditorDimensions(page, editorSelector); // The image is bigger than the page, so it has been scaled down to // 75% of the page width. @@ -158,9 +152,10 @@ describe("Stamp Editor", () => { await input.uploadFile( `${path.join(__dirname, "../images/firefox_logo.svg")}` ); - await waitForImage(page, getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await waitForImage(page, editorSelector); - const { width } = await getEditorDimensions(page, 0); + const { width } = await getEditorDimensions(page, editorSelector); expect(Math.round(parseFloat(width))).toEqual(40); @@ -187,14 +182,14 @@ describe("Stamp Editor", () => { ); const editorSelector = getEditorSelector(0); await waitForImage(page, editorSelector); - await waitForSerialized(page, 1); + await page.waitForSelector(`${editorSelector} button.delete`); await page.click(`${editorSelector} button.delete`); - await waitForSerialized(page, 0); await kbUndo(page); + await waitForImage(page, editorSelector); await waitForSerialized(page, 1); await waitForSelectedEditor(page, editorSelector); @@ -207,13 +202,23 @@ describe("Stamp Editor", () => { let pages; beforeAll(async () => { - pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50); + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50, { + eventBusSetup: eventBus => { + eventBus.on("annotationeditoruimanager", ({ uiManager }) => { + window.uiManager = uiManager; + }); + }, + }); }); afterAll(async () => { await closePages(pages); }); + afterEach(async () => { + await cleanupEditing(pages, switchToStamp); + }); + it("must check that an added image stay within the page", async () => { await Promise.all( pages.map(async ([browserName, page]) => { @@ -230,14 +235,13 @@ describe("Stamp Editor", () => { await input.uploadFile( `${path.join(__dirname, "../images/firefox_logo.png")}` ); - await waitForImage(page, getEditorSelector(i)); - await page.waitForSelector(`${getEditorSelector(i)} .altText`); + const editorSelector = getEditorSelector(i); + await waitForImage(page, editorSelector); + await page.waitForSelector(`${editorSelector} .altText`); for (let j = 0; j < 4; j++) { await page.keyboard.press("Escape"); - await page.waitForSelector( - `${getEditorSelector(i)} .resizers.hidden` - ); + await page.waitForSelector(`${editorSelector} .resizers.hidden`); const handle = await waitForAnnotationEditorLayer(page); await page.evaluate(() => { @@ -246,10 +250,10 @@ describe("Stamp Editor", () => { await awaitPromise(handle); await page.focus(".stampEditor"); - await waitForSelectedEditor(page, getEditorSelector(i)); + await waitForSelectedEditor(page, editorSelector); await page.waitForSelector( - `${getEditorSelector(i)} .resizers:not(.hidden)` + `${editorSelector} .resizers:not(.hidden)` ); const stampRect = await getRect(page, ".stampEditor"); @@ -276,6 +280,44 @@ describe("Stamp Editor", () => { }) ); }); + + it("must check that the opposite corner doesn't move", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToStamp(page); + + await page.click("#editorStampAddImage"); + const input = await page.$("#stampEditorFileInput"); + await input.uploadFile( + `${path.join(__dirname, "../images/firefox_logo.png")}` + ); + const editorSelector = getEditorSelector(0); + await waitForImage(page, editorSelector); + await page.waitForSelector(`${editorSelector} .resizer.topLeft`); + const baseRect = await getRect(page, editorSelector); + const bRX = baseRect.x + baseRect.width; + const bRY = baseRect.y + baseRect.height; + + await dragAndDrop(page, `${editorSelector} .resizer.topLeft`, [ + [-10, -10], + [20, 20], + [-10, -10], + [20, 20], + ]); + + const newRect = await getRect(page, editorSelector); + const newBRX = newRect.x + newRect.width; + const newBRY = newRect.y + newRect.height; + + expect(Math.abs(bRX - newBRX) <= 1) + .withContext(`In ${browserName}`) + .toBeTrue(); + expect(Math.abs(bRY - newBRY) <= 1) + .withContext(`In ${browserName}`) + .toBeTrue(); + }) + ); + }); }); describe("Alt text dialog", () => { @@ -294,10 +336,11 @@ describe("Stamp Editor", () => { for (const [browserName, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); // Wait for the alt-text button to be visible. - const buttonSelector = `${getEditorSelector(0)} button.altText`; + const buttonSelector = `${editorSelector} button.altText`; await page.waitForSelector(buttonSelector); // Click on the alt-text button. @@ -317,7 +360,7 @@ describe("Stamp Editor", () => { // Check that the canvas has an aria-describedby attribute. await page.waitForSelector( - `${getEditorSelector(0)} canvas[aria-describedby]` + `${editorSelector} canvas[aria-describedby]` ); // Wait for the alt-text button to have the correct icon. @@ -337,13 +380,14 @@ describe("Stamp Editor", () => { expect(tooltipText).toEqual("Hello World"); // Now we change the alt-text and check that the tooltip is updated. + const longString = "a".repeat(512); await page.click(buttonSelector); await page.waitForSelector("#altTextDialog", { visible: true }); await page.evaluate(sel => { document.querySelector(`${sel}`).value = ""; }, textareaSelector); await page.click(textareaSelector); - await page.type(textareaSelector, "Dlrow Olleh"); + await page.type(textareaSelector, longString); await page.click(saveButtonSelector); await page.waitForSelector(`${buttonSelector}.done`); await page.hover(buttonSelector); @@ -352,7 +396,14 @@ describe("Stamp Editor", () => { sel => document.querySelector(`${sel}`).innerText, tooltipSelector ); - expect(tooltipText).toEqual("Dlrow Olleh"); + expect(tooltipText).toEqual(longString); + const dims = await page.evaluate(sel => { + const { width, height } = document + .querySelector(`${sel}`) + .getBoundingClientRect(); + return { width, height }; + }, tooltipSelector); + expect(dims.width / dims.height).toBeLessThan(2); // Now we just check that cancel didn't change anything. await page.click(buttonSelector); @@ -371,8 +422,8 @@ describe("Stamp Editor", () => { sel => document.querySelector(`${sel}`).innerText, tooltipSelector ); - // The tooltip should still be "Dlrow Olleh". - expect(tooltipText).toEqual("Dlrow Olleh"); + // The tooltip should still be longString. + expect(tooltipText).toEqual(longString); // Now we switch to decorative. await page.click(buttonSelector); @@ -402,7 +453,7 @@ describe("Stamp Editor", () => { sel => document.querySelector(`${sel}`).innerText, tooltipSelector ); - expect(tooltipText).toEqual("Dlrow Olleh"); + expect(tooltipText).toEqual(longString); // Now we remove the alt-text and check that the tooltip is removed. await page.click(buttonSelector); @@ -462,12 +513,10 @@ describe("Stamp Editor", () => { for (const [browserName, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); - const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); - await page.click(editorSelector); - await waitForSelectedEditor(page, editorSelector); + await selectEditor(page, editorSelector); await page.waitForSelector( `${editorSelector} .resizer.topLeft[tabindex="-1"]` @@ -598,7 +647,8 @@ describe("Stamp Editor", () => { await page1.bringToFront(); await switchToStamp(page1); - await copyImage(page1, "../images/firefox_logo.png", 0); + const editorSelector = getEditorSelector(0); + await copyImage(page1, "../images/firefox_logo.png", editorSelector); await copy(page1); const [, page2] = pages2[i]; @@ -607,7 +657,7 @@ describe("Stamp Editor", () => { await paste(page2); - await waitForImage(page2, getEditorSelector(0)); + await waitForImage(page2, editorSelector); } }); }); @@ -627,19 +677,19 @@ describe("Stamp Editor", () => { // Run sequentially to avoid clipboard issues. for (const [, page] of pages) { await switchToStamp(page); - const selector = getEditorSelector(0); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(selector); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${selector} button.delete`); - await page.click(`${selector} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); await kbUndo(page); + await waitForImage(page, editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${selector} canvas`); } }); }); @@ -659,14 +709,14 @@ describe("Stamp Editor", () => { // Run sequentially to avoid clipboard issues. for (const [, page] of pages) { await switchToStamp(page); - const selector = getEditorSelector(0); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(selector); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${selector} button.delete`); - await page.click(`${selector} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2); @@ -684,7 +734,7 @@ describe("Stamp Editor", () => { await scrollIntoView(page, pageSelector); } - await page.waitForSelector(`${selector} canvas`); + await page.waitForSelector(`${editorSelector} canvas`); } }); }); @@ -704,14 +754,14 @@ describe("Stamp Editor", () => { // Run sequentially to avoid clipboard issues. for (const [, page] of pages) { await switchToStamp(page); - const selector = getEditorSelector(0); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(selector); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${selector} button.delete`); - await page.click(`${selector} button.delete`); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); await waitForSerialized(page, 0); const twoToOne = Array.from(new Array(13).keys(), n => n + 2).concat( @@ -723,8 +773,8 @@ describe("Stamp Editor", () => { } await kbUndo(page); + await waitForImage(page, editorSelector); await waitForSerialized(page, 1); - await page.waitForSelector(`${selector} canvas`); } }); }); @@ -745,8 +795,9 @@ describe("Stamp Editor", () => { for (const [, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); const serializedRect = await getFirstSerialized(page, x => x.rect); @@ -766,11 +817,8 @@ describe("Stamp Editor", () => { (x, y) => x !== y ); - const canvasRect = await getRect( - page, - `${getEditorSelector(0)} canvas` - ); - const stampRect = await getRect(page, getEditorSelector(0)); + const canvasRect = await getRect(page, `${editorSelector} canvas`); + const stampRect = await getRect(page, editorSelector); expect( ["x", "y", "width", "height"].every( @@ -805,15 +853,13 @@ describe("Stamp Editor", () => { for (const [, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); - const canvasRect = await getRect( - page, - `${getEditorSelector(0)} canvas` - ); - const stampRect = await getRect(page, getEditorSelector(0)); + const canvasRect = await getRect(page, `${editorSelector} canvas`); + const stampRect = await getRect(page, editorSelector); expect( ["x", "y", "width", "height"].every( @@ -840,8 +886,9 @@ describe("Stamp Editor", () => { for (const [browserName, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); - await page.waitForSelector(getEditorSelector(0)); + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); await applyFunctionToEditor(page, "pdfjs_internal_editor_0", editor => { editor.altTextData = { @@ -849,7 +896,7 @@ describe("Stamp Editor", () => { decorative: false, }; }); - await page.waitForSelector(`${getEditorSelector(0)} .altText.done`); + await page.waitForSelector(`${editorSelector} .altText.done`); await copy(page); await paste(page); @@ -881,6 +928,9 @@ describe("Stamp Editor", () => { eventBus.on("annotationeditoruimanager", ({ uiManager }) => { window.uiManager = uiManager; }); + eventBus.on("reporttelemetry", ({ details }) => { + (window.telemetry ||= []).push(structuredClone(details)); + }); }, }, { @@ -901,6 +951,7 @@ describe("Stamp Editor", () => { } await page.evaluate(() => { window.uiManager.reset(); + window.telemetry = []; }); // Disable editing mode. await switchToStamp(page, /* disable */ true); @@ -917,8 +968,8 @@ describe("Stamp Editor", () => { await switchToStamp(page); // Add an image. - await copyImage(page, "../images/firefox_logo.png", 0); const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); @@ -937,7 +988,7 @@ describe("Stamp Editor", () => { // Check that AI guessed the correct alt text. await page.waitForFunction( `document.getElementById("newAltTextDescriptionTextarea").value === - "Fake alt text"` + "Fake alt text."` ); // Check that the dialog has the correct title: "Edit..." @@ -979,6 +1030,7 @@ describe("Stamp Editor", () => { const buttonSelector = `${editorSelector} button.altText.new`; await page.waitForSelector(buttonSelector, { visible: true }); + await waitForTranslation(page); // Check the text in the button. let text = await page.evaluate( sel => document.querySelector(sel).textContent, @@ -993,8 +1045,7 @@ describe("Stamp Editor", () => { .toEqual("Review alt text"); // Unselect and select the editor and check that the badge is visible. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, editorSelector); + await unselectEditor(page, editorSelector); await page.waitForSelector(".editToolbar", { visible: false }); await page.waitForSelector(".noAltTextBadge", { visible: true }); @@ -1028,6 +1079,7 @@ describe("Stamp Editor", () => { await waitForSelectedEditor(page, editorSelector); await page.waitForSelector(buttonSelector, { visible: true }); + await waitForTranslation(page); // Check the text in the button. text = await page.evaluate( sel => document.querySelector(sel).textContent, @@ -1042,8 +1094,7 @@ describe("Stamp Editor", () => { .toEqual("Missing alt text"); // Unselect and select the editor and check that the badge is visible. - await page.keyboard.press("Escape"); - await waitForUnselectedEditor(page, editorSelector); + await unselectEditor(page, editorSelector); await page.waitForSelector(".editToolbar", { visible: false }); await page.waitForSelector(".noAltTextBadge", { visible: true }); await page.evaluate(() => { @@ -1070,6 +1121,7 @@ describe("Stamp Editor", () => { await page.click("#newAltTextSave"); await page.waitForSelector("#newAltTextDialog", { visible: false }); + await waitForTranslation(page); // Check the text in the button. text = await page.evaluate( sel => document.querySelector(sel).firstChild.textContent, @@ -1110,8 +1162,8 @@ describe("Stamp Editor", () => { await switchToStamp(page); // Add an image. - await copyImage(page, "../images/firefox_logo.png", 0); const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); @@ -1151,8 +1203,8 @@ describe("Stamp Editor", () => { await switchToStamp(page); // Add an image. - await copyImage(page, "../images/firefox_logo.png", 0); const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); @@ -1163,6 +1215,82 @@ describe("Stamp Editor", () => { await page.waitForSelector("#newAltTextDisclaimer[hidden]"); } }); + + it("must check that the data in telemetry are correct", async () => { + // Run sequentially to avoid clipboard issues. + for (const [browserName, page] of pages) { + await page.evaluate(() => { + window.PDFViewerApplication.mlManager.enableAltTextModelDownload = true; + }); + await switchToStamp(page); + + // Add an image. + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + // Wait for the dialog to be visible. + await page.waitForSelector("#newAltTextDialog", { visible: true }); + + // Check that AI guessed the correct alt text. + await page.waitForFunction( + `document.getElementById("newAltTextDescriptionTextarea").value === + "Fake alt text."` + ); + // Clear the input and check that the title changes to "Add..." + await clearInput( + page, + "#newAltTextDescriptionTextarea", + /* waitForInputEvent = */ true + ); + // Save the empty text. + await page.click("#newAltTextSave"); + await page.waitForSelector("#newAltTextDialog", { visible: false }); + + // Get the telemetry data and clean. + let telemetry = await page.evaluate(() => { + const tel = window.telemetry; + window.telemetry = []; + return tel; + }); + let saveTelemetry = telemetry.find( + details => details.data.action === "pdfjs.image.alt_text.user_edit" + ); + expect(saveTelemetry.data.data) + .withContext(`In ${browserName}`) + .toEqual({ + total_words: 3, + words_removed: 3, + words_added: 0, + }); + + // Click on the Review button. + const buttonSelector = `${editorSelector} button.altText.new`; + await page.waitForSelector(buttonSelector, { visible: true }); + await page.click(buttonSelector); + await page.waitForSelector("#newAltTextDialog", { visible: true }); + + // Add a new alt text and check that the title changes to "Edit..." + await page.type("#newAltTextDescriptionTextarea", "Fake text alt foo."); + + // Save the empty text. + await page.click("#newAltTextSave"); + await page.waitForSelector("#newAltTextDialog", { visible: false }); + + telemetry = await page.evaluate(() => window.telemetry); + saveTelemetry = telemetry.find( + details => details.data.action === "pdfjs.image.alt_text.user_edit" + ); + expect(saveTelemetry.data.data) + .withContext(`In ${browserName}`) + .toEqual({ + total_words: 3, + words_removed: 0, + words_added: 1, + }); + } + }); }); describe("New alt-text flow (bug 1920515)", () => { @@ -1215,8 +1343,8 @@ describe("Stamp Editor", () => { await switchToStamp(page); // Add an image. - await copyImage(page, "../images/firefox_logo.png", 0); const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); @@ -1251,7 +1379,7 @@ describe("Stamp Editor", () => { for (const [, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/firefox_logo.png", 0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); } @@ -1292,13 +1420,11 @@ describe("Stamp Editor", () => { it("must check that a stamp editor isn't on top of the secondary toolbar", async () => { // Run sequentially to avoid clipboard issues. - const editorSelector = getEditorSelector(0); - for (const [, page] of pages) { await switchToStamp(page); - await copyImage(page, "../images/red.png", 0); - + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/red.png", editorSelector); await page.waitForSelector(editorSelector); await waitForSerialized(page, 1); } @@ -1338,8 +1464,11 @@ describe("Stamp Editor", () => { it("must move an annotation", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + const modeChangedHandle = await waitForAnnotationModeChanged(page); await page.click(getAnnotationSelector("25R"), { count: 2 }); - await waitForSelectedEditor(page, getEditorSelector(0)); + await awaitPromise(modeChangedHandle); + const editorSelector = getEditorSelector(0); + await waitForSelectedEditor(page, editorSelector); const editorIds = await getEditors(page, "stamp"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(5); @@ -1349,22 +1478,10 @@ describe("Stamp Editor", () => { const serialized = await getSerialized(page); expect(serialized).withContext(`In ${browserName}`).toEqual([]); - const editorRect = await page.$eval(getEditorSelector(0), el => { - const { x, y, width, height } = el.getBoundingClientRect(); - return { x, y, width, height }; - }); - // Select the annotation we want to move. - await page.mouse.click(editorRect.x + 2, editorRect.y + 2); - await waitForSelectedEditor(page, getEditorSelector(0)); + await selectEditor(page, editorSelector); - await dragAndDropAnnotation( - page, - editorRect.x + editorRect.width / 2, - editorRect.y + editorRect.height / 2, - 100, - 100 - ); + await dragAndDrop(page, editorSelector, [[100, 100]]); await waitForSerialized(page, 1); }) ); @@ -1385,13 +1502,16 @@ describe("Stamp Editor", () => { it("must update an existing alt-text", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + const modeChangedHandle = await waitForAnnotationModeChanged(page); await page.click(getAnnotationSelector("58R"), { count: 2 }); - await waitForSelectedEditor(page, getEditorSelector(4)); + await awaitPromise(modeChangedHandle); + const editorSelector = getEditorSelector(4); + await waitForSelectedEditor(page, editorSelector); const editorIds = await getEditors(page, "stamp"); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(5); - await page.click(`${getEditorSelector(4)} button.altText`); + await page.click(`${editorSelector} button.altText`); await page.waitForSelector("#altTextDialog", { visible: true }); const textareaSelector = "#altTextDialog textarea"; @@ -1442,7 +1562,9 @@ describe("Stamp Editor", () => { it("must check that the annotation is correctly restored", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + const modeChangedHandle = await waitForAnnotationModeChanged(page); await page.click(getAnnotationSelector("37R"), { count: 2 }); + await awaitPromise(modeChangedHandle); const editorSelector = getEditorSelector(2); await waitForSelectedEditor(page, editorSelector); @@ -1473,4 +1595,228 @@ describe("Stamp Editor", () => { ); }); }); + + describe("Drag a stamp annotation and click on a touchscreen", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that the annotation isn't unselected when an other finger taps on the screen", async () => { + // Run sequentially to avoid clipboard issues. + for (const [, page] of pages) { + await switchToStamp(page); + + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + const stampRect = await getRect(page, editorSelector); + + await page.touchscreen.tap(stampRect.x + 10, stampRect.y + 10); + await waitForSelectedEditor(page, editorSelector); + + await page.touchscreen.touchStart(stampRect.x + 10, stampRect.y + 10); + await page.touchscreen.touchMove(stampRect.x + 20, stampRect.y + 20); + await page.touchscreen.tap(stampRect.x - 10, stampRect.y - 10); + await page.touchscreen.touchEnd(); + + await waitForSelectedEditor(page, editorSelector); + } + }); + }); + + describe("Undo deletion popup has the expected behaviour", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that deleting an image can be undone using the undo button", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToStamp(page); + + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForSelector("#editorUndoBar:not([hidden])"); + + await page.click("#editorUndoBarUndoButton"); + await waitForSerialized(page, 1); + await page.waitForSelector(editorSelector); + await page.waitForSelector(`${editorSelector} canvas`); + }) + ); + }); + + it("must check that the undo deletion popup displays the correct message", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToStamp(page); + + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForFunction(() => { + const messageElement = document.querySelector( + "#editorUndoBarMessage" + ); + return messageElement && messageElement.textContent.trim() !== ""; + }); + const message = await page.waitForSelector("#editorUndoBarMessage"); + const messageText = await page.evaluate( + el => el.textContent, + message + ); + expect(messageText).toContain("Image removed"); + }) + ); + }); + + it("must check that the popup disappears when a new image is inserted", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToStamp(page); + + const editorSelector = getEditorSelector(0); + await copyImage(page, "../images/firefox_logo.png", editorSelector); + await page.waitForSelector(editorSelector); + await waitForSerialized(page, 1); + + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 0); + + await page.waitForSelector("#editorUndoBar:not([hidden])"); + await page.click("#editorStampAddImage"); + const newInput = await page.$("#stampEditorFileInput"); + await newInput.uploadFile( + `${path.join(__dirname, "../images/firefox_logo.png")}` + ); + await waitForImage(page, getEditorSelector(1)); + await waitForSerialized(page, 1); + await page.waitForSelector("#editorUndoBar", { hidden: true }); + }) + ); + }); + }); + + describe("Switch to edit mode a pdf with an existing stamp annotation on an invisible and rendered page", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue19239.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must move on the second page", async () => { + await Promise.all( + pages.map(async ([, page]) => { + const pageOneSelector = `.page[data-page-number = "1"]`; + const pageTwoSelector = `.page[data-page-number = "2"]`; + await scrollIntoView(page, pageTwoSelector); + await page.waitForSelector(pageOneSelector, { visible: false }); + + await switchToStamp(page); + await scrollIntoView(page, pageOneSelector); + await page.waitForSelector( + `${pageOneSelector} .annotationEditorLayer canvas`, + { visible: true } + ); + }) + ); + }); + }); + + describe("Switch to edit mode a pdf with an existing stamp annotation on an invisible and unrendered page", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue19239.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must move on the last page", async () => { + await Promise.all( + pages.map(async ([, page]) => { + const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2); + for (const pageNumber of twoToFourteen) { + const pageSelector = `.page[data-page-number = "${pageNumber}"]`; + await scrollIntoView(page, pageSelector); + } + + await switchToStamp(page); + + const thirteenToOne = Array.from(new Array(13).keys(), n => 13 - n); + for (const pageNumber of thirteenToOne) { + const pageSelector = `.page[data-page-number = "${pageNumber}"]`; + await scrollIntoView(page, pageSelector); + } + + await page.waitForSelector( + `.page[data-page-number = "1"] .annotationEditorLayer canvas`, + { visible: true } + ); + }) + ); + }); + }); + + describe("Switch to edit mode by double clicking on an existing stamp annotation", () => { + const annotationSelector = getAnnotationSelector("999R"); + + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue19239.pdf", annotationSelector); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must switch to edit mode", async () => { + await Promise.all( + pages.map(async ([, page]) => { + await page.waitForSelector(annotationSelector); + await scrollIntoView(page, annotationSelector); + + await page.click(annotationSelector, { count: 2 }); + + await page.waitForFunction(() => + document + .querySelector(".annotationEditorLayer") + .classList.contains("stampEditing") + ); + }) + ); + }); + }); }); diff --git a/test/integration/test_utils.mjs b/test/integration/test_utils.mjs index 7e6ec548b0f22..3d67f7fcd4e47 100644 --- a/test/integration/test_utils.mjs +++ b/test/integration/test_utils.mjs @@ -17,11 +17,15 @@ import os from "os"; const isMac = os.platform() === "darwin"; -function loadAndWait(filename, selector, zoom, setups, options) { +function loadAndWait(filename, selector, zoom, setups, options, viewport) { return Promise.all( global.integrationSessions.map(async session => { const page = await session.browser.newPage(); + if (viewport) { + await page.setViewport(viewport); + } + // In order to avoid errors because of checks which depend on // a locale. await page.evaluateOnNewDocument(() => { @@ -199,6 +203,12 @@ async function waitAndClick(page, selector, clickOptions = {}) { await page.click(selector, clickOptions); } +function waitForPointerUp(page) { + return createPromise(page, resolve => { + window.addEventListener("pointerup", resolve, { once: true }); + }); +} + function getSelector(id) { return `[data-element-id="${id}"]`; } @@ -229,18 +239,6 @@ function getAnnotationSelector(id) { return `[data-annotation-id="${id}"]`; } -function getSelectedEditors(page) { - return page.evaluate(() => { - const elements = document.querySelectorAll(".selectedEditor"); - const results = []; - for (const { id } of elements) { - results.push(parseInt(id.split("_").at(-1))); - } - results.sort(); - return results; - }); -} - async function getSpanRectFromText(page, pageNumber, text) { await page.waitForSelector( `.page[data-page-number="${pageNumber}"] > .textLayer .endOfContent` @@ -309,9 +307,11 @@ async function waitForEvent({ const success = await awaitPromise(handle); if (success === null) { - console.log(`waitForEvent: ${eventName} didn't trigger within the timeout`); + console.warn( + `waitForEvent: ${eventName} didn't trigger within the timeout` + ); } else if (!success) { - console.log(`waitForEvent: ${eventName} triggered, but validation failed`); + console.warn(`waitForEvent: ${eventName} triggered, but validation failed`); } } @@ -325,9 +325,18 @@ async function waitForStorageEntries(page, nEntries) { async function waitForSerialized(page, nEntries) { return page.waitForFunction( - n => - (window.PDFViewerApplication.pdfDocument.annotationStorage.serializable - .map?.size ?? 0) === n, + n => { + try { + return ( + (window.PDFViewerApplication.pdfDocument.annotationStorage + .serializable.map?.size ?? 0) === n + ); + } catch { + // When serializing a stamp annotation with a SVG, the transfer + // can fail because of the SVG, so we just retry. + return false; + } + }, {}, nEntries ); @@ -348,10 +357,25 @@ async function applyFunctionToEditor(page, editorId, func) { ); } +async function selectEditor(page, selector, count = 1) { + const editorRect = await getRect(page, selector); + await page.mouse.click( + editorRect.x + editorRect.width / 2, + editorRect.y + editorRect.height / 2, + { count } + ); + await waitForSelectedEditor(page, selector); +} + async function waitForSelectedEditor(page, selector) { return page.waitForSelector(`${selector}.selectedEditor`); } +async function unselectEditor(page, selector) { + await page.keyboard.press("Escape"); + await waitForUnselectedEditor(page, selector); +} + async function waitForUnselectedEditor(page, selector) { return page.waitForSelector(`${selector}:not(.selectedEditor)`); } @@ -468,23 +492,23 @@ function getEditors(page, kind) { const elements = document.querySelectorAll(`.${aKind}Editor`); const results = []; for (const { id } of elements) { - results.push(id); + results.push(parseInt(id.split("_").at(-1))); } + results.sort(); return results; }, kind); } -function getEditorDimensions(page, id) { - return page.evaluate(n => { - const element = document.getElementById(`pdfjs_internal_editor_${n}`); - const { style } = element; +function getEditorDimensions(page, selector) { + return page.evaluate(sel => { + const { style } = document.querySelector(sel); return { left: style.left, top: style.top, width: style.width, height: style.height, }; - }, id); + }, selector); } async function serializeBitmapDimensions(page) { @@ -511,10 +535,15 @@ async function serializeBitmapDimensions(page) { }); } -async function dragAndDropAnnotation(page, startX, startY, tX, tY) { +async function dragAndDrop(page, selector, translations, steps = 1) { + const rect = await getRect(page, selector); + const startX = rect.x + rect.width / 2; + const startY = rect.y + rect.height / 2; await page.mouse.move(startX, startY); await page.mouse.down(); - await page.mouse.move(startX + tX, startY + tY); + for (const [tX, tY] of translations) { + await page.mouse.move(startX + tX, startY + tY, { steps }); + } await page.mouse.up(); await page.waitForSelector("#viewer:not(.noUserSelect)"); } @@ -539,9 +568,29 @@ function waitForAnnotationModeChanged(page) { }); } -function waitForPageRendered(page) { +function waitForPageRendered(page, pageNumber) { + return page.evaluateHandle( + number => [ + new Promise(resolve => { + const { eventBus } = window.PDFViewerApplication; + eventBus.on("pagerendered", function handler(e) { + if ( + !e.isDetailView && + (number === undefined || e.pageNumber === number) + ) { + resolve(); + eventBus.off("pagerendered", handler); + } + }); + }), + ], + pageNumber + ); +} + +function waitForEditorMovedInDOM(page) { return createPromise(page, resolve => { - window.PDFViewerApplication.eventBus.on("pagerendered", resolve, { + window.PDFViewerApplication.eventBus.on("editormovedindom", resolve, { once: true, }); }); @@ -745,6 +794,12 @@ async function kbFocusPrevious(page) { await awaitPromise(handle); } +async function kbSave(page) { + await page.keyboard.down(modifier); + await page.keyboard.press("s"); + await page.keyboard.up(modifier); +} + async function switchToEditor(name, page, disable = false) { const modeChangedHandle = await createPromise(page, resolve => { window.PDFViewerApplication.eventBus.on( @@ -762,16 +817,99 @@ async function switchToEditor(name, page, disable = false) { await awaitPromise(modeChangedHandle); } +async function selectEditors(name, page) { + await kbSelectAll(page); + await page.waitForFunction( + () => !document.querySelector(`.${name}Editor:not(.selectedEditor)`) + ); +} + +async function clearEditors(name, page) { + await selectEditors(name, page); + await page.keyboard.press("Backspace"); + await waitForStorageEntries(page, 0); +} + +function waitForNoElement(page, selector) { + return page.waitForFunction( + sel => !document.querySelector(sel), + {}, + selector + ); +} + +function isCanvasWhite(page, pageNumber, rectangle) { + return page.evaluate( + (rect, pageN) => { + const canvas = document.querySelector( + `.page[data-page-number = "${pageN}"] .canvasWrapper canvas` + ); + const canvasRect = canvas.getBoundingClientRect(); + const ctx = canvas.getContext("2d"); + rect ||= canvasRect; + const { data } = ctx.getImageData( + rect.x - canvasRect.x, + rect.y - canvasRect.y, + rect.width, + rect.height + ); + return new Uint32Array(data.buffer).every(x => x === 0xffffffff); + }, + rectangle, + pageNumber + ); +} + +async function cleanupEditing(pages, switcher) { + for (const [, page] of pages) { + await page.evaluate(() => { + window.uiManager.reset(); + }); + // Disable editing mode. + await switcher(page, /* disable */ true); + } +} + +async function getXY(page, selector) { + const rect = await getRect(page, selector); + return `${rect.x}::${rect.y}`; +} + +function waitForPositionChange(page, selector, xy) { + return page.waitForFunction( + (sel, currentXY) => { + const bbox = document.querySelector(sel).getBoundingClientRect(); + return `${bbox.x}::${bbox.y}` !== currentXY; + }, + {}, + selector, + xy + ); +} + +async function moveEditor(page, selector, n, pressKey) { + let xy = await getXY(page, selector); + for (let i = 0; i < n; i++) { + const handle = await waitForEditorMovedInDOM(page); + await pressKey(); + await awaitPromise(handle); + await waitForPositionChange(page, selector, xy); + xy = await getXY(page, selector); + } +} + export { applyFunctionToEditor, awaitPromise, + cleanupEditing, + clearEditors, clearInput, closePages, closeSinglePage, copy, copyToClipboard, createPromise, - dragAndDropAnnotation, + dragAndDrop, firstPageOnTop, getAnnotationSelector, getAnnotationStorage, @@ -782,11 +920,12 @@ export { getFirstSerialized, getQuerySelector, getRect, - getSelectedEditors, getSelector, getSerialized, getSpanRectFromText, + getXY, hover, + isCanvasWhite, isVisible, kbBigMoveDown, kbBigMoveLeft, @@ -800,22 +939,29 @@ export { kbModifierDown, kbModifierUp, kbRedo, + kbSave, kbSelectAll, kbUndo, loadAndWait, mockClipboard, + moveEditor, paste, pasteFromClipboard, scrollIntoView, + selectEditor, + selectEditors, serializeBitmapDimensions, setCaretAt, switchToEditor, + unselectEditor, waitAndClick, waitForAnnotationEditorLayer, waitForAnnotationModeChanged, waitForEntryInStorage, waitForEvent, + waitForNoElement, waitForPageRendered, + waitForPointerUp, waitForSandboxTrip, waitForSelectedEditor, waitForSerialized, diff --git a/test/integration/viewer_spec.mjs b/test/integration/viewer_spec.mjs index 4d14649d28bc8..8bc58fee87ff6 100644 --- a/test/integration/viewer_spec.mjs +++ b/test/integration/viewer_spec.mjs @@ -276,9 +276,20 @@ describe("PDF viewer", () => { beforeEach(async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.evaluate(() => { - window.PDFViewerApplication.pdfViewer.currentScale = 0.5; - }); + const handle = await waitForPageRendered(page); + if ( + await page.evaluate(() => { + if ( + window.PDFViewerApplication.pdfViewer.currentScale !== 0.5 + ) { + window.PDFViewerApplication.pdfViewer.currentScale = 0.5; + return true; + } + return false; + }) + ) { + await awaitPromise(handle); + } }) ); }); @@ -317,12 +328,14 @@ describe("PDF viewer", () => { const originalCanvasSize = await getCanvasSize(page); const factor = 2; + const handle = await waitForPageRendered(page, 1); await page.evaluate(scaleFactor => { window.PDFViewerApplication.pdfViewer.increaseScale({ drawingDelay: 0, scaleFactor, }); }, factor); + await awaitPromise(handle); const canvasSize = await getCanvasSize(page); @@ -343,12 +356,14 @@ describe("PDF viewer", () => { const originalCanvasSize = await getCanvasSize(page); const factor = 4; + const handle = await waitForPageRendered(page, 1); await page.evaluate(scaleFactor => { window.PDFViewerApplication.pdfViewer.increaseScale({ drawingDelay: 0, scaleFactor, }); }, factor); + await awaitPromise(handle); const canvasSize = await getCanvasSize(page); @@ -439,4 +454,646 @@ describe("PDF viewer", () => { ); }); }); + + describe("Detail view on zoom", () => { + const BASE_MAX_CANVAS_PIXELS = 1e6; + + function setupPages(zoom, devicePixelRatio, setups = {}) { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "colors.pdf", + null, + zoom, + { + // When running Firefox with Puppeteer, setting the + // devicePixelRatio Puppeteer option does not properly set + // the `window.devicePixelRatio` value. Set it manually. + earlySetup: `() => { + window.devicePixelRatio = ${devicePixelRatio}; + }`, + ...setups, + }, + { maxCanvasPixels: BASE_MAX_CANVAS_PIXELS * devicePixelRatio ** 2 }, + { height: 600, width: 800, devicePixelRatio } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + return function forEachPage(fn) { + return Promise.all( + pages.map(([browserName, page]) => fn(browserName, page)) + ); + }; + } + + function extractCanvases(pageNumber) { + const pageOne = document.querySelector( + `.page[data-page-number='${pageNumber}']` + ); + return Array.from(pageOne.querySelectorAll("canvas"), canvas => { + const { width, height } = canvas; + const ctx = canvas.getContext("2d"); + const topLeft = ctx.getImageData(2, 2, 1, 1).data; + const bottomRight = ctx.getImageData(width - 3, height - 3, 1, 1).data; + return { + size: width * height, + topLeft: globalThis.pdfjsLib.Util.makeHexColor(...topLeft), + bottomRight: globalThis.pdfjsLib.Util.makeHexColor(...bottomRight), + }; + }); + } + + function waitForDetailRendered(page) { + return createPromise(page, resolve => { + const controller = new AbortController(); + window.PDFViewerApplication.eventBus.on( + "pagerendered", + ({ isDetailView }) => { + if (isDetailView) { + resolve(); + controller.abort(); + } + }, + { signal: controller.signal } + ); + }); + } + + for (const pixelRatio of [1, 2]) { + describe(`with pixel ratio ${pixelRatio}`, () => { + describe("setupPages()", () => { + const forEachPage = setupPages("100%", pixelRatio); + + it("sets the proper devicePixelRatio", async () => { + await forEachPage(async (browserName, page) => { + const devicePixelRatio = await page.evaluate( + () => window.devicePixelRatio + ); + + expect(devicePixelRatio) + .withContext(`In ${browserName}`) + .toBe(pixelRatio); + }); + }); + }); + + describe("when zooming in past max canvas size", () => { + const forEachPage = setupPages("100%", pixelRatio); + + it("must render the detail view", async () => { + await forEachPage(async (browserName, page) => { + await page.waitForSelector( + ".page[data-page-number='1'] .textLayer" + ); + + const before = await page.evaluate(extractCanvases, 1); + + expect(before.length) + .withContext(`In ${browserName}, before`) + .toBe(1); + expect(before[0].size) + .withContext(`In ${browserName}, before`) + .toBeLessThan(BASE_MAX_CANVAS_PIXELS * pixelRatio ** 2); + expect(before[0]) + .withContext(`In ${browserName}, before`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#85200c", // dark berry + bottomRight: "#b6d7a8", // light green + }) + ); + + const factor = 3; + // Check that we are going to trigger CSS zoom. + expect(before[0].size * factor ** 2) + .withContext(`In ${browserName}`) + .toBeGreaterThan(BASE_MAX_CANVAS_PIXELS * pixelRatio ** 2); + + const handle = await waitForDetailRendered(page); + await page.evaluate(scaleFactor => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: 0, + scaleFactor, + }); + }, factor); + await awaitPromise(handle); + + const after = await page.evaluate(extractCanvases, 1); + + expect(after.length) + .withContext(`In ${browserName}, after`) + .toBe(2); + expect(after[0].size) + .withContext(`In ${browserName}, after (first)`) + .toBeLessThan(4e6); + expect(after[0]) + .withContext(`In ${browserName}, after (first)`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#85200c", // dark berry + bottomRight: "#b6d7a8", // light green + }) + ); + expect(after[1].size) + .withContext(`In ${browserName}, after (second)`) + .toBeLessThan(4e6); + expect(after[1]) + .withContext(`In ${browserName}, after (second)`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#85200c", // dark berry + bottomRight: "#ff0000", // bright red + }) + ); + }); + }); + }); + + describe("when starting already zoomed in past max canvas size", () => { + const forEachPage = setupPages("300%", pixelRatio); + + it("must render the detail view", async () => { + await forEachPage(async (browserName, page) => { + await page.waitForSelector( + ".page[data-page-number='1'] canvas:nth-child(2)" + ); + + const canvases = await page.evaluate(extractCanvases, 1); + + expect(canvases.length).withContext(`In ${browserName}`).toBe(2); + expect(canvases[0].size) + .withContext(`In ${browserName} (first)`) + .toBeLessThan(4e6); + expect(canvases[0]) + .withContext(`In ${browserName} (first)`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#85200c", // dark berry + bottomRight: "#b6d7a8", // light green + }) + ); + expect(canvases[1].size) + .withContext(`In ${browserName} (second)`) + .toBeLessThan(4e6); + expect(canvases[1]) + .withContext(`In ${browserName} (second)`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#85200c", // dark berry + bottomRight: "#ff0000", // bright red + }) + ); + }); + }); + }); + + describe("when scrolling", () => { + const forEachPage = setupPages("300%", pixelRatio); + + it("must update the detail view", async () => { + await forEachPage(async (browserName, page) => { + await page.waitForSelector( + ".page[data-page-number='1'] canvas:nth-child(2)" + ); + + const handle = await waitForDetailRendered(page); + await page.evaluate(() => { + const container = document.getElementById("viewerContainer"); + container.scrollTop += 1600; + container.scrollLeft += 1100; + }); + await awaitPromise(handle); + + const canvases = await page.evaluate(extractCanvases, 1); + + expect(canvases.length).withContext(`In ${browserName}`).toBe(2); + expect(canvases[1].size) + .withContext(`In ${browserName}`) + .toBeLessThan(4e6); + expect(canvases[1]) + .withContext(`In ${browserName}`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#ff9900", // bright orange + bottomRight: "#ffe599", // light yellow + }) + ); + }); + }); + }); + + describe("when scrolling little enough that the existing detail covers the new viewport", () => { + const forEachPage = setupPages("300%", pixelRatio); + + it("must not re-create the detail canvas", async () => { + await forEachPage(async (browserName, page) => { + const detailCanvasSelector = + ".page[data-page-number='1'] canvas:nth-child(2)"; + + await page.waitForSelector(detailCanvasSelector); + + const detailCanvasHandle = await page.$(detailCanvasSelector); + + let rendered = false; + const handle = await waitForDetailRendered(page); + await page.evaluate(() => { + const container = document.getElementById("viewerContainer"); + container.scrollTop += 10; + container.scrollLeft += 10; + }); + awaitPromise(handle) + .then(() => { + rendered = true; + }) + .catch(() => {}); + + // Give some time to the page to re-render. If it re-renders it's + // a bug, but without waiting we would never catch it. + await new Promise(resolve => { + setTimeout(resolve, 100); + }); + + const isSame = await page.evaluate( + (prev, selector) => prev === document.querySelector(selector), + detailCanvasHandle, + detailCanvasSelector + ); + + expect(isSame).withContext(`In ${browserName}`).toBe(true); + expect(rendered).withContext(`In ${browserName}`).toBe(false); + }); + }); + }); + + describe("when scrolling to have two visible pages", () => { + const forEachPage = setupPages("300%", pixelRatio); + + it("must update the detail view", async () => { + await forEachPage(async (browserName, page) => { + await page.waitForSelector( + ".page[data-page-number='1'] canvas:nth-child(2)" + ); + + const handle = await createPromise(page, resolve => { + // wait for two 'pagerendered' events for detail views + let second = false; + const { eventBus } = window.PDFViewerApplication; + eventBus.on( + "pagerendered", + function onPageRendered({ isDetailView }) { + if (!isDetailView) { + return; + } + if (!second) { + second = true; + return; + } + eventBus.off("pagerendered", onPageRendered); + resolve(); + } + ); + }); + await page.evaluate(() => { + const container = document.getElementById("viewerContainer"); + container.scrollLeft += 600; + container.scrollTop += 3000; + }); + await awaitPromise(handle); + + const [canvases1, canvases2] = await Promise.all([ + page.evaluate(extractCanvases, 1), + page.evaluate(extractCanvases, 2), + ]); + + expect(canvases1.length) + .withContext(`In ${browserName}, first page`) + .toBe(2); + expect(canvases1[1].size) + .withContext(`In ${browserName}, first page`) + .toBeLessThan(4e6); + expect(canvases1[1]) + .withContext(`In ${browserName}, first page`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#38761d", // dark green + bottomRight: "#b6d7a8", // light green + }) + ); + + expect(canvases2.length) + .withContext(`In ${browserName}, second page`) + .toBe(2); + expect(canvases2[1].size) + .withContext(`In ${browserName}, second page`) + .toBeLessThan(4e6); + expect(canvases2[1]) + .withContext(`In ${browserName}, second page`) + .toEqual( + jasmine.objectContaining({ + topLeft: "#134f5c", // dark cyan + bottomRight: "#a2c4c9", // light cyan + }) + ); + }); + }); + }); + + describe("pagerendered event", () => { + const forEachPage = setupPages("100%", pixelRatio, { + eventBusSetup: eventBus => { + globalThis.__pageRenderedEvents = []; + + eventBus.on( + "pagerendered", + ({ pageNumber, isDetailView, cssTransform }) => { + globalThis.__pageRenderedEvents.push({ + pageNumber, + isDetailView, + cssTransform, + }); + } + ); + }, + }); + + it("is dispatched properly", async () => { + await forEachPage(async (browserName, page) => { + const getPageRenderedEvents = () => + page.evaluate(() => { + const events = globalThis.__pageRenderedEvents; + globalThis.__pageRenderedEvents = []; + return events; + }); + const waitForPageRenderedEvent = filter => + page.waitForFunction( + filterStr => + // eslint-disable-next-line no-eval + globalThis.__pageRenderedEvents.some(eval(filterStr)), + { polling: 50 }, + String(filter) + ); + + // Initial render + await waitForPageRenderedEvent(e => e.pageNumber === 2); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, initial render`) + .toEqual([ + { pageNumber: 1, isDetailView: false, cssTransform: false }, + { pageNumber: 2, isDetailView: false, cssTransform: false }, + ]); + + // Zoom-in without triggering the detail view + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 1.05, + }); + }); + await waitForPageRenderedEvent(e => e.pageNumber === 2); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, first zoom`) + .toEqual([ + { pageNumber: 1, isDetailView: false, cssTransform: false }, + { pageNumber: 2, isDetailView: false, cssTransform: false }, + ]); + + // Zoom-in on the first page, triggering the detail view + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 2, + }); + }); + await Promise.all([ + waitForPageRenderedEvent( + e => e.isDetailView && e.pageNumber === 1 + ), + waitForPageRenderedEvent(e => e.pageNumber === 2), + ]); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, second zoom`) + .toEqual([ + { pageNumber: 1, isDetailView: false, cssTransform: false }, + { pageNumber: 1, isDetailView: true, cssTransform: false }, + { pageNumber: 2, isDetailView: false, cssTransform: false }, + ]); + + // Zoom-in more + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 2, + }); + }); + await Promise.all([ + waitForPageRenderedEvent( + e => e.isDetailView && e.pageNumber === 1 + ), + waitForPageRenderedEvent(e => e.pageNumber === 2), + ]); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, third zoom`) + .toEqual([ + { pageNumber: 1, isDetailView: false, cssTransform: true }, + { pageNumber: 2, isDetailView: false, cssTransform: true }, + { pageNumber: 1, isDetailView: true, cssTransform: false }, + ]); + + // Scroll to another area of the first page + await page.evaluate(() => { + const container = document.getElementById("viewerContainer"); + container.scrollTop += 1600; + container.scrollLeft += 1100; + }); + await waitForPageRenderedEvent(e => e.isDetailView); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, first scroll`) + .toEqual([ + { pageNumber: 1, isDetailView: true, cssTransform: false }, + ]); + + // Scroll to the second page + await page.evaluate(() => { + const container = document.getElementById("viewerContainer"); + const pageElement = document.querySelector(".page"); + container.scrollTop += + pageElement.getBoundingClientRect().height; + }); + await waitForPageRenderedEvent(e => e.isDetailView); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, second scroll`) + .toEqual([ + { pageNumber: 2, isDetailView: true, cssTransform: false }, + ]); + + // Zoom-out, to not have the detail view anymore + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 0.25, + }); + }); + await Promise.all([ + waitForPageRenderedEvent(e => e.pageNumber === 1), + waitForPageRenderedEvent(e => e.pageNumber === 2), + ]); + expect(await getPageRenderedEvents()) + .withContext(`In ${browserName}, second zoom`) + .toEqual([ + { pageNumber: 2, isDetailView: false, cssTransform: false }, + { pageNumber: 1, isDetailView: false, cssTransform: false }, + ]); + + const canvasesPerPage = await page.evaluate(() => + Array.from( + document.querySelectorAll(".canvasWrapper"), + wrapper => wrapper.childElementCount + ) + ); + expect(canvasesPerPage) + .withContext(`In ${browserName}, number of canvases`) + .toEqual([1, 1]); + }); + }); + }); + }); + } + + describe("when immediately cancelled and re-rendered", () => { + const forEachPage = setupPages("100%", 1, { + eventBusSetup: eventBus => { + globalThis.__pageRenderedEvents = []; + eventBus.on("pagerendered", ({ pageNumber, isDetailView }) => { + globalThis.__pageRenderedEvents.push({ pageNumber, isDetailView }); + }); + }, + }); + + it("propely cleans up old canvases from the dom", async () => { + await forEachPage(async (browserName, page) => { + const waitForPageRenderedEvent = filter => + page.waitForFunction( + filterStr => { + // eslint-disable-next-line no-eval + if (globalThis.__pageRenderedEvents.some(eval(filterStr))) { + globalThis.__pageRenderedEvents = []; + return true; + } + return false; + }, + { polling: 50 }, + String(filter) + ); + const getCanvasCount = () => + page.evaluate( + () => + document.querySelector("[data-page-number='1'] .canvasWrapper") + .childElementCount + ); + + await waitForPageRenderedEvent(e => e.pageNumber === 1); + + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 4, + }); + }); + await waitForPageRenderedEvent( + e => e.pageNumber === 1 && e.isDetailView + ); + expect(await getCanvasCount()) + .withContext(`In ${browserName}, after the first zoom-in`) + .toBe(2); + + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 0.75, + }); + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 0.25, + }); + }); + await waitForPageRenderedEvent(e => e.pageNumber === 1); + expect(await getCanvasCount()) + .withContext(`In ${browserName}, after the two zoom-out`) + .toBe(1); + }); + }); + }); + + describe("when cancelled and re-rendered after 1 microtick", () => { + const forEachPage = setupPages("100%", 1, { + eventBusSetup: eventBus => { + globalThis.__pageRenderedEvents = []; + eventBus.on("pagerendered", ({ pageNumber, isDetailView }) => { + globalThis.__pageRenderedEvents.push({ pageNumber, isDetailView }); + }); + }, + }); + + it("propely cleans up old canvases from the dom", async () => { + await forEachPage(async (browserName, page) => { + const waitForPageRenderedEvent = filter => + page.waitForFunction( + filterStr => { + // eslint-disable-next-line no-eval + if (globalThis.__pageRenderedEvents.some(eval(filterStr))) { + globalThis.__pageRenderedEvents = []; + return true; + } + return false; + }, + { polling: 50 }, + String(filter) + ); + const getCanvasCount = () => + page.evaluate( + () => + document.querySelector("[data-page-number='1'] .canvasWrapper") + .childElementCount + ); + + await waitForPageRenderedEvent(e => e.pageNumber === 1); + + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 4, + }); + }); + await waitForPageRenderedEvent( + e => e.pageNumber === 1 && e.isDetailView + ); + expect(await getCanvasCount()) + .withContext(`In ${browserName}, after the first zoom-in`) + .toBe(2); + + await page.evaluate(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 0.75, + }); + Promise.resolve().then(() => { + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 0.25, + }); + }); + }); + await waitForPageRenderedEvent(e => e.pageNumber === 1); + expect(await getCanvasCount()) + .withContext(`In ${browserName}, after the two zoom-out`) + .toBe(1); + }); + }); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index b10be1106d6b6..6247849814c5c 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -94,6 +94,7 @@ !issue8424.pdf !issue8480.pdf !bug1650302_reduced.pdf +!issue18816.pdf !issue8570.pdf !issue8697.pdf !issue8702.pdf @@ -105,6 +106,7 @@ !issue9084.pdf !issue12963.pdf !issue9105_reduced.pdf +!issue19484_2.pdf !issue9105_other.pdf !issue9252.pdf !issue9262_reduced.pdf @@ -121,6 +123,7 @@ !issue10542_reduced.pdf !issue10665_reduced.pdf !issue11016_reduced.pdf +!issue19532.pdf !issue15516_reduced.pdf !issue11045.pdf !bug1057544.pdf @@ -156,6 +159,7 @@ !bug1020858.pdf !prefilled_f1040.pdf !bug1050040.pdf +!issue18986.pdf !bug1200096.pdf !bug1068432.pdf !issue12295.pdf @@ -231,6 +235,7 @@ !issue5954.pdf !issue6612.pdf !alphatrans.pdf +!issue14200.pdf !pattern_text_embedded_font.pdf !devicen.pdf !cmykjpeg.pdf @@ -273,6 +278,7 @@ !pr6531_1.pdf !pr6531_2.pdf !pr7352.pdf +!pr19449.pdf !bug900822.pdf !bug1392647.pdf !issue918.pdf @@ -476,6 +482,7 @@ !openoffice.pdf !js-buttons.pdf !issue7014.pdf +!issue19484_1.pdf !issue8187.pdf !annotation-link-text-popup.pdf !issue9278.pdf @@ -500,6 +507,7 @@ !annotation-line-without-appearance.pdf !bug1669099.pdf !annotation-square-circle.pdf +!bug1942064.pdf !annotation-square-circle-without-appearance.pdf !annotation-stamp.pdf !issue14048.pdf @@ -526,7 +534,9 @@ !bug1743245.pdf !quadpoints.pdf !transparent.pdf +!issue19326.pdf !issue13931.pdf +!issue19474.pdf !xobject-image.pdf !issue15441.pdf !issue6605.pdf @@ -552,6 +562,9 @@ !poppler-67295-0.pdf !poppler-85140-0.pdf !issue15012.pdf +!issue19176.pdf +!bug1947248_text.pdf +!bug1947248_forms.pdf !issue15150.pdf !poppler-395-0-fuzzed.pdf !issue14165.pdf @@ -603,6 +616,7 @@ !issue16021.pdf !bug1770750.pdf !issue16063.pdf +!issue19389.pdf !issue16067.pdf !bug1820909.1.pdf !issue16221.pdf @@ -647,6 +661,7 @@ !bug1868759.pdf !issue17730.pdf !bug1883609.pdf +!issue19550.pdf !issue17808.pdf !issue17871_bottom_right.pdf !issue17871_top_right.pdf @@ -680,3 +695,18 @@ !issue18894.pdf !bug1922766.pdf !issue18956.pdf +!issue19083.pdf +!issue19120.pdf +!bug1934157.pdf +!rotated_ink.pdf +!inks.pdf +!inks_basic.pdf +!issue19182.pdf +!issue18911.pdf +!issue19207.pdf +!issue19239.pdf +!issue19360.pdf +!bug1019475_1.pdf +!bug1019475_2.pdf +!issue19505.pdf +!colors.pdf diff --git a/test/pdfs/bug1019475_1.pdf b/test/pdfs/bug1019475_1.pdf new file mode 100644 index 0000000000000..54e80411fad1f Binary files /dev/null and b/test/pdfs/bug1019475_1.pdf differ diff --git a/test/pdfs/bug1019475_2.pdf b/test/pdfs/bug1019475_2.pdf new file mode 100644 index 0000000000000..569b5f6ce212b Binary files /dev/null and b/test/pdfs/bug1019475_2.pdf differ diff --git a/test/pdfs/bug1934157.pdf b/test/pdfs/bug1934157.pdf new file mode 100755 index 0000000000000..f0feacbccbff2 Binary files /dev/null and b/test/pdfs/bug1934157.pdf differ diff --git a/test/pdfs/bug1942064.pdf b/test/pdfs/bug1942064.pdf new file mode 100644 index 0000000000000..cb9dff3344578 Binary files /dev/null and b/test/pdfs/bug1942064.pdf differ diff --git a/test/pdfs/bug1947248_forms.pdf b/test/pdfs/bug1947248_forms.pdf new file mode 100644 index 0000000000000..12876fda6a220 Binary files /dev/null and b/test/pdfs/bug1947248_forms.pdf differ diff --git a/test/pdfs/bug1947248_text.pdf b/test/pdfs/bug1947248_text.pdf new file mode 100644 index 0000000000000..1354de747391c Binary files /dev/null and b/test/pdfs/bug1947248_text.pdf differ diff --git a/test/pdfs/colors.pdf b/test/pdfs/colors.pdf new file mode 100644 index 0000000000000..4a6a8d663b710 Binary files /dev/null and b/test/pdfs/colors.pdf differ diff --git a/test/pdfs/inks.pdf b/test/pdfs/inks.pdf new file mode 100644 index 0000000000000..371144239fe0e Binary files /dev/null and b/test/pdfs/inks.pdf differ diff --git a/test/pdfs/inks_basic.pdf b/test/pdfs/inks_basic.pdf new file mode 100644 index 0000000000000..de82e5a18de4b Binary files /dev/null and b/test/pdfs/inks_basic.pdf differ diff --git a/test/pdfs/issue14200.pdf b/test/pdfs/issue14200.pdf new file mode 100644 index 0000000000000..1c8d1d6f2c0d4 Binary files /dev/null and b/test/pdfs/issue14200.pdf differ diff --git a/test/pdfs/issue17190.pdf.link b/test/pdfs/issue17190.pdf.link new file mode 100644 index 0000000000000..618accffb1023 --- /dev/null +++ b/test/pdfs/issue17190.pdf.link @@ -0,0 +1 @@ +https://github.com/mozilla/pdf.js/files/13180762/org_AVA89V01U0.Black.pdf diff --git a/test/pdfs/issue17190_1.pdf.link b/test/pdfs/issue17190_1.pdf.link new file mode 100644 index 0000000000000..506054eb5da31 --- /dev/null +++ b/test/pdfs/issue17190_1.pdf.link @@ -0,0 +1 @@ +https://github.com/mozilla/pdf.js/files/13670664/abc.pdf diff --git a/test/pdfs/issue18816.pdf b/test/pdfs/issue18816.pdf new file mode 100644 index 0000000000000..79b1c75e5f264 Binary files /dev/null and b/test/pdfs/issue18816.pdf differ diff --git a/test/pdfs/issue18911.pdf b/test/pdfs/issue18911.pdf new file mode 100755 index 0000000000000..9e3aec3a856eb Binary files /dev/null and b/test/pdfs/issue18911.pdf differ diff --git a/test/pdfs/issue18973.pdf.link b/test/pdfs/issue18973.pdf.link new file mode 100644 index 0000000000000..cb4580ff37831 --- /dev/null +++ b/test/pdfs/issue18973.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/17550758/doc1520828609.pdf diff --git a/test/pdfs/issue18986.pdf b/test/pdfs/issue18986.pdf new file mode 100644 index 0000000000000..f23047bf73c0b Binary files /dev/null and b/test/pdfs/issue18986.pdf differ diff --git a/test/pdfs/issue19022.pdf.link b/test/pdfs/issue19022.pdf.link new file mode 100644 index 0000000000000..dc560b6072c31 --- /dev/null +++ b/test/pdfs/issue19022.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/17703776/linear-gradient-on-rect_text.pdf diff --git a/test/pdfs/issue19083.pdf b/test/pdfs/issue19083.pdf new file mode 100755 index 0000000000000..508b7db9630cd Binary files /dev/null and b/test/pdfs/issue19083.pdf differ diff --git a/test/pdfs/issue19120.pdf b/test/pdfs/issue19120.pdf new file mode 100755 index 0000000000000..a271b5eb699eb Binary files /dev/null and b/test/pdfs/issue19120.pdf differ diff --git a/test/pdfs/issue19176.pdf b/test/pdfs/issue19176.pdf new file mode 100644 index 0000000000000..7bf465aeed5bf Binary files /dev/null and b/test/pdfs/issue19176.pdf differ diff --git a/test/pdfs/issue19182.pdf b/test/pdfs/issue19182.pdf new file mode 100644 index 0000000000000..93ad565396dd6 Binary files /dev/null and b/test/pdfs/issue19182.pdf differ diff --git a/test/pdfs/issue19207.pdf b/test/pdfs/issue19207.pdf new file mode 100644 index 0000000000000..4c9d01ffc7745 Binary files /dev/null and b/test/pdfs/issue19207.pdf differ diff --git a/test/pdfs/issue19234.pdf.link b/test/pdfs/issue19234.pdf.link new file mode 100644 index 0000000000000..00576c280b665 --- /dev/null +++ b/test/pdfs/issue19234.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/18172261/Tax3921.pdf diff --git a/test/pdfs/issue19239.pdf b/test/pdfs/issue19239.pdf new file mode 100755 index 0000000000000..4e1b5d92a4863 Binary files /dev/null and b/test/pdfs/issue19239.pdf differ diff --git a/test/pdfs/issue19281.pdf.link b/test/pdfs/issue19281.pdf.link new file mode 100644 index 0000000000000..1aeac656d4208 --- /dev/null +++ b/test/pdfs/issue19281.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/18304780/Antarctica-Grid.pdf diff --git a/test/pdfs/issue19319.pdf.link b/test/pdfs/issue19319.pdf.link new file mode 100644 index 0000000000000..98295cd81fbe0 --- /dev/null +++ b/test/pdfs/issue19319.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/18396493/2023-ESG-report-eng.pdf diff --git a/test/pdfs/issue19326.pdf b/test/pdfs/issue19326.pdf new file mode 100644 index 0000000000000..800d86883866b Binary files /dev/null and b/test/pdfs/issue19326.pdf differ diff --git a/test/pdfs/issue19360.pdf b/test/pdfs/issue19360.pdf new file mode 100755 index 0000000000000..558b3c59ddb9b Binary files /dev/null and b/test/pdfs/issue19360.pdf differ diff --git a/test/pdfs/issue19389.pdf b/test/pdfs/issue19389.pdf new file mode 100644 index 0000000000000..44a95188e84e8 Binary files /dev/null and b/test/pdfs/issue19389.pdf differ diff --git a/test/pdfs/issue19474.pdf b/test/pdfs/issue19474.pdf new file mode 100644 index 0000000000000..33e9963936a2e Binary files /dev/null and b/test/pdfs/issue19474.pdf differ diff --git a/test/pdfs/issue19484_1.pdf b/test/pdfs/issue19484_1.pdf new file mode 100644 index 0000000000000..2e8a37de01e6b Binary files /dev/null and b/test/pdfs/issue19484_1.pdf differ diff --git a/test/pdfs/issue19484_2.pdf b/test/pdfs/issue19484_2.pdf new file mode 100644 index 0000000000000..4a8caeb74cfff Binary files /dev/null and b/test/pdfs/issue19484_2.pdf differ diff --git a/test/pdfs/issue19494.pdf.link b/test/pdfs/issue19494.pdf.link new file mode 100644 index 0000000000000..a6093208279b1 --- /dev/null +++ b/test/pdfs/issue19494.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/18810079/VERAPDF-1335-InlineImage.pdf diff --git a/test/pdfs/issue19505.pdf b/test/pdfs/issue19505.pdf new file mode 100755 index 0000000000000..67f1628941dc7 Binary files /dev/null and b/test/pdfs/issue19505.pdf differ diff --git a/test/pdfs/issue19510.pdf.link b/test/pdfs/issue19510.pdf.link new file mode 100644 index 0000000000000..91015fdd23240 --- /dev/null +++ b/test/pdfs/issue19510.pdf.link @@ -0,0 +1 @@ +https://github.com/user-attachments/files/18841919/geht_nicht_02.pdf diff --git a/test/pdfs/issue19532.pdf b/test/pdfs/issue19532.pdf new file mode 100644 index 0000000000000..1ddf578b7b21e Binary files /dev/null and b/test/pdfs/issue19532.pdf differ diff --git a/test/pdfs/issue19550.pdf b/test/pdfs/issue19550.pdf new file mode 100644 index 0000000000000..a52d052f01b2f Binary files /dev/null and b/test/pdfs/issue19550.pdf differ diff --git a/test/pdfs/issue5939.pdf.link b/test/pdfs/issue5939.pdf.link index 5fd510ed69b23..f4167f77ab2cc 100644 --- a/test/pdfs/issue5939.pdf.link +++ b/test/pdfs/issue5939.pdf.link @@ -1 +1 @@ -https://web.archive.org/web/20150613184455/https://www.usenix.org/system/files/login/articles/login_apr15_02_grosvenor_041315.pdf +https://web.archive.org/web/20250128144709/https://www.usenix.org/system/files/login/articles/login_apr15_02_grosvenor_041315.pdf diff --git a/test/pdfs/pr19449.pdf b/test/pdfs/pr19449.pdf new file mode 100644 index 0000000000000..90c15d22a7250 Binary files /dev/null and b/test/pdfs/pr19449.pdf differ diff --git a/test/pdfs/rotated_ink.pdf b/test/pdfs/rotated_ink.pdf new file mode 100644 index 0000000000000..8b69361eaf3f6 Binary files /dev/null and b/test/pdfs/rotated_ink.pdf differ diff --git a/test/unit/testreporter.js b/test/reporter.js similarity index 97% rename from test/unit/testreporter.js rename to test/reporter.js index 05abc51847961..4c5b6b8905798 100644 --- a/test/unit/testreporter.js +++ b/test/reporter.js @@ -18,7 +18,7 @@ const TestReporter = function (browser) { resolve(); }) .catch(reason => { - console.warn(`TestReporter - send failed (${action}): ${reason}`); + console.warn(`TestReporter - send failed (${action}):`, reason); resolve(); send(action, json); diff --git a/test/resources/favicon.ico b/test/resources/favicon.ico index d44438903b751..39040bb937c9e 100644 Binary files a/test/resources/favicon.ico and b/test/resources/favicon.ico differ diff --git a/test/stats/statcmp.js b/test/stats/statcmp.js index e5ba75441b707..eca8457bb0527 100644 --- a/test/stats/statcmp.js +++ b/test/stats/statcmp.js @@ -64,9 +64,7 @@ function flatten(stats) { }); // Use only overall results if not grouped by 'stat' if (!options.groupBy.includes("stat")) { - rows = rows.filter(function (s) { - return s.stat === "Overall"; - }); + rows = rows.filter(s => s.stat === "Overall"); } return rows; } @@ -129,9 +127,7 @@ function stat(baseline, current) { } const rows = []; // collect rows and measure column widths - const width = labels.map(function (s) { - return s.length; - }); + const width = labels.map(s => s.length); rows.push(labels); for (const key of keys) { const baselineMean = mean(baselineGroup[key]); @@ -162,9 +158,7 @@ function stat(baseline, current) { } // add horizontal line - const hline = width.map(function (w) { - return new Array(w + 1).join("-"); - }); + const hline = width.map(w => new Array(w + 1).join("-")); rows.splice(1, 0, hline); // print output diff --git a/test/test.mjs b/test/test.mjs index 4d16c8ea1e656..d837f46ac14e1 100644 --- a/test/test.mjs +++ b/test/test.mjs @@ -806,7 +806,7 @@ async function startIntegrationTest() { onAllSessionsClosed = onAllSessionsClosedAfterTests("integration"); startServer(); - const { runTests } = await import("./integration-boot.mjs"); + const { runTests } = await import("./integration/jasmine-boot.js"); await startBrowsers({ baseUrl: null, initializeSession: session => { @@ -952,6 +952,8 @@ async function startBrowser({ "browser.newtabpage.enabled": false, // Disable network connections to Contile. "browser.topsites.contile.enabled": false, + // Disable logging for remote settings. + "services.settings.loglevel": "off", ...extraPrefsFirefox, }; } @@ -1049,9 +1051,7 @@ async function closeSession(browser) { await session.browser.close(); } session.closed = true; - const allClosed = sessions.every(function (s) { - return s.closed; - }); + const allClosed = sessions.every(s => s.closed); if (allClosed) { if (tempDir) { fs.rmSync(tempDir, { recursive: true, force: true }); diff --git a/test/test_manifest.json b/test/test_manifest.json index da0e82f7f77c3..c42928757278e 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -1251,6 +1251,14 @@ "type": "eq", "about": "XObject with BBox array containing indirect object." }, + { + "id": "issue19389", + "file": "pdfs/issue19389.pdf", + "md5": "419357cd829b067347c64b9ae838af4a", + "rounds": 1, + "type": "eq", + "forms": true + }, { "id": "issue7115", "file": "pdfs/issue7115.pdf", @@ -2057,6 +2065,15 @@ "firstPage": 3, "type": "eq" }, + { + "id": "issue18973", + "file": "pdfs/issue18973.pdf", + "md5": "b9bbd312269862bf39bb2a31023a9d02", + "link": true, + "rounds": 1, + "firstPage": 45, + "type": "eq" + }, { "id": "issue9262", "file": "pdfs/issue9262_reduced.pdf", @@ -2725,6 +2742,28 @@ "type": "eq", "about": "TrueType font with (0, 1) cmap." }, + { + "id": "issue19176", + "file": "pdfs/issue19176.pdf", + "md5": "c307418412a72997671518fa978439e0", + "rounds": 1, + "type": "eq" + }, + { + "id": "bug1947248-text", + "file": "pdfs/bug1947248_text.pdf", + "md5": "491f1df75b77d2762ff96ce51f5e019b", + "rounds": 1, + "type": "text" + }, + { + "id": "bug1947248-forms", + "file": "pdfs/bug1947248_forms.pdf", + "md5": "456c974d7d4351719f36ef10e603d29c", + "rounds": 1, + "type": "eq", + "forms": true + }, { "id": "issue4801", "file": "pdfs/issue4801.pdf", @@ -3590,6 +3629,14 @@ "link": false, "type": "text" }, + { + "id": "issue19484_1", + "file": "pdfs/issue19484_1.pdf", + "md5": "4e9e78a84226dbdddbd735388ccc2dcd", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "issue5644", "file": "pdfs/issue5644.pdf", @@ -3859,6 +3906,13 @@ "7R": false } }, + { + "id": "issue19550", + "file": "pdfs/issue19550.pdf", + "md5": "4c978bd99348789e05dad1d2a919ddf0", + "rounds": 1, + "type": "eq" + }, { "id": "issue1127-text", "file": "pdfs/issue1127.pdf", @@ -4718,6 +4772,13 @@ "link": false, "type": "eq" }, + { + "id": "issue18986", + "file": "pdfs/issue18986.pdf", + "md5": "e147084fabd9677366f6ae3586dd311b", + "rounds": 1, + "type": "load" + }, { "id": "issue6652", "file": "pdfs/issue6652.pdf", @@ -4997,6 +5058,14 @@ "rounds": 1, "type": "eq" }, + { + "id": "issue19484_2", + "file": "pdfs/issue19484_2.pdf", + "md5": "cd3050eda9fa18a7e6a78c702f9890bb", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "bug894572", "file": "pdfs/bug894572.pdf", @@ -5232,6 +5301,13 @@ "link": true, "type": "eq" }, + { + "id": "issue14200", + "file": "pdfs/issue14200.pdf", + "md5": "4dba2cde1c6e65abe53e66eefc97a7f1", + "rounds": 1, + "type": "eq" + }, { "id": "jbig2_huffman_1", "file": "pdfs/jbig2_huffman_1.pdf", @@ -5564,6 +5640,23 @@ "rounds": 1, "type": "eq" }, + { + "id": "issue19494", + "file": "pdfs/issue19494.pdf", + "md5": "2ec2ccbe6aa7e622ef981ca5ca443d55", + "link": true, + "rounds": 1, + "type": "eq", + "lastPage": 1 + }, + { + "id": "issue19510", + "file": "pdfs/issue19510.pdf", + "md5": "3ff133f633cea3e2f13f08f8c3414cc6", + "link": true, + "rounds": 1, + "type": "eq" + }, { "id": "issue11768", "file": "pdfs/issue11768_reduced.pdf", @@ -5730,6 +5823,13 @@ "rounds": 1, "type": "eq" }, + { + "id": "issue19532", + "file": "pdfs/issue19532.pdf", + "md5": "87bf39acbb306479fa8994f441a5cb1e", + "rounds": 1, + "type": "eq" + }, { "id": "issue5540", "file": "pdfs/issue5540.pdf", @@ -6334,6 +6434,29 @@ "type": "eq", "about": "Fixes one part of bug 1731483." }, + { + "id": "issue19326", + "file": "pdfs/issue19326.pdf", + "md5": "b4d937017daf439a6318501428e0c6ba", + "rounds": 1, + "type": "eq" + }, + { + "id": "issue19326_nowasm", + "file": "pdfs/issue19326.pdf", + "md5": "b4d937017daf439a6318501428e0c6ba", + "rounds": 1, + "type": "eq", + "useWasm": false + }, + { + "id": "issue19326_main_thread_fetch", + "file": "pdfs/issue19326.pdf", + "md5": "b4d937017daf439a6318501428e0c6ba", + "rounds": 1, + "type": "eq", + "useWorkerFetch": false + }, { "id": "bug1140761", "file": "pdfs/bug1140761.pdf", @@ -8228,24 +8351,44 @@ "color": [255, 0, 0], "thickness": 3, "opacity": 1, - "paths": [ - { - "bezier": [ - 73, 560.2277710847244, 74.30408044851005, 561.5318515332344, - 76.89681158113368, 557.7555609512324, 77.5, 557.2277710847244, - 81.95407020558315, 553.3304596548392, 87.4811839685984, - 550.8645311043504, 92.5, 547.7277710847244, 97.38795894206055, - 544.6727967459365, 109.48854351637208, 540.2392275683522, 113.5, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 73, + 560.2277710847244, + 74.30408044851005, + 561.5318515332344, + 76.89681158113368, + 557.7555609512324, + 77.5, + 557.2277710847244, + 81.95407020558315, + 553.3304596548392, + 87.4811839685984, + 550.8645311043504, + 92.5, + 547.7277710847244, + 97.38795894206055, + 544.6727967459365, + 109.48854351637208, + 540.2392275683522, + 113.5, 536.2277710847244 - ], - "points": [ + ] + ], + "points": [ + [ 73, 560.2277710847244, 76.7257911988625, 558.1025687477292, 75.5128345111164, 559.4147224528562, 77.5, 557.2277710847244, 92.5, 547.7277710847244, 109.21378602219673, 539.2873735223628, 103.32868842191223, 542.3364518890394, 113.5, 536.2277710847244 ] - } - ], + ] + }, "pageIndex": 0, "rect": [71.5, 534.5, 115, 562], "rotation": 0 @@ -8314,22 +8457,37 @@ "color": [255, 0, 0], "thickness": 1, "opacity": 1, - "paths": [ - { - "bezier": [ - 417.61538461538464, 520.3461538461538, 419.15384615384613, - 520.3461538461538, 421.0769230769231, 520.3461538461538, - 423.38461538461536, 520.3461538461538, 425.6923076923077, - 520.3461538461538, 429.15384615384613, 519.9615384615385, - 433.7692307692308, 519.1923076923076 - ], - "points": [ + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 417.61538461538464, + 520.3461538461538, + 419.15384615384613, + 520.3461538461538, + 421.0769230769231, + 520.3461538461538, + 423.38461538461536, + 520.3461538461538, + 425.6923076923077, + 520.3461538461538, + 429.15384615384613, + 519.9615384615385, + 433.7692307692308, + 519.1923076923076 + ] + ], + "points": [ + [ 417.61538461538464, 520.3461538461538, 419.15384615384613, 520.3461538461538, 425.6923076923077, 520.3461538461538, 433.7692307692308, 519.1923076923076 ] - } - ], + ] + }, "pageIndex": 0, "rect": [ 417.11538461538464, 510.46153846153845, 434.42307692307696, @@ -8342,22 +8500,37 @@ "color": [0, 255, 0], "thickness": 1, "opacity": 1, - "paths": [ - { - "bezier": [ - 449.92307692307696, 526.6538461538462, 449.92307692307696, - 527.423076923077, 449.6346153846154, 528.8653846153846, - 449.0576923076924, 530.9807692307693, 448.4807692307693, - 533.0961538461539, 447.8076923076924, 536.6538461538462, - 447.0384615384616, 541.6538461538462 - ], - "points": [ + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 449.92307692307696, + 526.6538461538462, + 449.92307692307696, + 527.423076923077, + 449.6346153846154, + 528.8653846153846, + 449.0576923076924, + 530.9807692307693, + 448.4807692307693, + 533.0961538461539, + 447.8076923076924, + 536.6538461538462, + 447.0384615384616, + 541.6538461538462 + ] + ], + "points": [ + [ 449.92307692307696, 526.6538461538462, 449.92307692307696, 527.423076923077, 448.4807692307693, 533.0961538461539, 447.0384615384616, 541.6538461538462 ] - } - ], + ] + }, "pageIndex": 0, "rect": [ 446.5384615384616, 526.1538461538462, 456.92307692307696, @@ -8370,22 +8543,37 @@ "color": [0, 0, 255], "thickness": 1, "opacity": 1, - "paths": [ - { - "bezier": [ - 482.8461538461538, 511.6538461538462, 482.07692307692304, - 511.6538461538462, 480.53846153846155, 511.6538461538462, - 478.23076923076917, 511.6538461538462, 475.9230769230769, - 511.6538461538462, 472.46153846153845, 511.6538461538462, - 467.8461538461538, 511.6538461538462 - ], - "points": [ + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 482.8461538461538, + 511.6538461538462, + 482.07692307692304, + 511.6538461538462, + 480.53846153846155, + 511.6538461538462, + 478.23076923076917, + 511.6538461538462, + 475.9230769230769, + 511.6538461538462, + 472.46153846153845, + 511.6538461538462, + 467.8461538461538, + 511.6538461538462 + ] + ], + "points": [ + [ 482.8461538461538, 511.6538461538462, 482.07692307692304, 511.6538461538462, 475.9230769230769, 511.6538461538462, 467.8461538461538, 511.6538461538462 ] - } - ], + ] + }, "pageIndex": 0, "rect": [ 467.1923076923077, 511.1538461538462, 483.3461538461538, @@ -8398,22 +8586,37 @@ "color": [0, 255, 255], "thickness": 1, "opacity": 1, - "paths": [ - { - "bezier": [ - 445.9230769230769, 509.3846153846154, 445.5384615384615, - 509.3846153846154, 445.15384615384613, 508.1346153846154, - 444.7692307692307, 505.6346153846154, 444.38461538461536, - 503.1346153846154, 443.23076923076917, 499.00000000000006, - 441.30769230769226, 493.2307692307693 - ], - "points": [ + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 445.9230769230769, + 509.3846153846154, + 445.5384615384615, + 509.3846153846154, + 445.15384615384613, + 508.1346153846154, + 444.7692307692307, + 505.6346153846154, + 444.38461538461536, + 503.1346153846154, + 443.23076923076917, + 499.00000000000006, + 441.30769230769226, + 493.2307692307693 + ] + ], + "points": [ + [ 445.9230769230769, 509.3846153846154, 445.5384615384615, 509.3846153846154, 444.38461538461536, 503.1346153846154, 441.30769230769226, 493.2307692307693 ] - } - ], + ] + }, "pageIndex": 0, "rect": [ 436.03846153846155, 492.5769230769231, 446.4230769230769, @@ -8872,6 +9075,13 @@ "link": true, "type": "other" }, + { + "id": "bug1942064", + "file": "pdfs/bug1942064.pdf", + "md5": "d50b5ebb8cab1211609d16faa54ec47d", + "rounds": 1, + "type": "eq" + }, { "id": "issue16221-text", "file": "pdfs/issue16221.pdf", @@ -9583,12 +9793,12 @@ "color": [53, 228, 47], "thickness": 20, "opacity": 1, - "paths": [ - { - "bezier": [279.9183673469388, 477.0105263157895], - "points": [279.9183673469388, 477.0105263157895] - } - ], + "paths": { + "lines": [ + [null, null, null, null, 279.9183673469388, 477.0105263157895] + ], + "points": [[279.9183673469388, 477.0105263157895]] + }, "pageIndex": 0, "rect": [ 269.9183673469388, 443.93684210526317, 312.9387755102041, @@ -9968,6 +10178,13 @@ "annotations": true, "type": "eq" }, + { + "id": "issue18816", + "file": "pdfs/issue18816.pdf", + "md5": "45f863f5b227f700ffea1210e4f80403", + "rounds": 1, + "type": "eq" + }, { "id": "issue17794", "file": "pdfs/issue17794.pdf", @@ -10741,5 +10958,993 @@ "md5": "73e8cd32bd063e42fcc4b270c78549b1", "rounds": 1, "type": "eq" + }, + { + "id": "issue19022", + "file": "pdfs/issue19022.pdf", + "md5": "7d7a9c45f93a9db269800855ccffe7cd", + "rounds": 1, + "type": "eq", + "link": true + }, + { + "id": "issue19083", + "file": "pdfs/issue19083.pdf", + "md5": "2e2aa6c1904c1e6a89a5a6ec95d5ce7e", + "rounds": 1, + "forms": true, + "talos": false, + "type": "eq" + }, + { + "id": "issue17190", + "file": "pdfs/issue17190.pdf", + "md5": "06e3ce6492dc0b5815a63965b5d7144c", + "rounds": 1, + "talos": false, + "type": "eq", + "link": true + }, + { + "id": "issue17190_1", + "file": "pdfs/issue17190_1.pdf", + "md5": "63bbc71a6c2cdec11bb20c5744232c47", + "rounds": 1, + "talos": false, + "type": "eq", + "link": true + }, + { + "id": "inks_basic-editor-save-print", + "file": "pdfs/inks_basic.pdf", + "md5": "2615610de59b4849993dcc2614ebb266", + "rounds": 1, + "lastPage": 1, + "type": "eq", + "save": true, + "print": true, + "annotationStorage": { + "pdfjs_internal_editor_0": { + "annotationType": 15, + "color": [241, 17, 41], + "opacity": 1, + "thickness": 20, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 114, + 691.5, + null, + null, + null, + null, + 159.75, + 631.5 + ] + ], + "points": [[114, 691.5, 159.75, 631.5]] + }, + "pageIndex": 0, + "rect": [ + 104.0000076523194, 621.5000049701105, 169.7500193485847, + 701.5000020036331 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "16R" + }, + "pdfjs_internal_editor_1": { + "id": "17R", + "deleted": true, + "pageIndex": 0, + "popupRef": "" + }, + "pdfjs_internal_editor_2": { + "annotationType": 15, + "color": [0, 0, 0], + "opacity": 1, + "thickness": 3, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 475.0961608886719, + 647.4615478515625, + null, + null, + null, + null, + 520.8461303710938, + 587.4615478515625 + ] + ], + "points": [ + [ + 475.0961608886719, 647.4615478515625, 520.8461303710938, + 587.4615478515625 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 473.59618278650134, 585.9615278473268, 522.3461772570244, + 648.9615135559669 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "18R" + }, + "pdfjs_internal_editor_3": { + "annotationType": 15, + "color": [250, 23, 28], + "opacity": 0.55, + "thickness": 3, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 79.04861450195312, + 532.353759765625, + null, + null, + null, + null, + 198.18736267089844, + 378.6045837402344 + ] + ], + "points": [ + [ + 79.04861450195312, 532.353759765625, 198.18736267089844, + 378.6045837402344 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 77.54861622361037, 377.10462435392236, 199.6873893210521, + 533.8537948498359 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "19R" + }, + "pdfjs_internal_editor_4": { + "annotationType": 15, + "color": [70, 108, 241], + "opacity": 0.7, + "thickness": 20, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 273.6300048828125, + 535.47998046875, + null, + null, + null, + null, + 319.3800048828125, + 475.4800109863281 + ] + ], + "points": [ + [ + 273.6300048828125, 535.47998046875, 319.3800048828125, + 475.4800109863281 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 263.629992209948, 465.48002000955444, 329.38000390621335, + 545.4799757370582 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "20R" + } + } + }, + { + "id": "inks_basic-editor-print", + "file": "pdfs/inks_basic.pdf", + "md5": "2615610de59b4849993dcc2614ebb266", + "rounds": 1, + "lastPage": 1, + "type": "eq", + "print": true, + "annotationStorage": { + "pdfjs_internal_editor_0": { + "annotationType": 15, + "color": [241, 17, 41], + "opacity": 1, + "thickness": 20, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 114, + 691.5, + null, + null, + null, + null, + 159.75, + 631.5 + ] + ], + "points": [[114, 691.5, 159.75, 631.5]] + }, + "pageIndex": 0, + "rect": [ + 104.0000076523194, 621.5000049701105, 169.7500193485847, + 701.5000020036331 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "16R" + }, + "pdfjs_internal_editor_1": { + "id": "17R", + "deleted": true, + "pageIndex": 0, + "popupRef": "" + }, + "pdfjs_internal_editor_2": { + "annotationType": 15, + "color": [0, 0, 0], + "opacity": 1, + "thickness": 3, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 475.0961608886719, + 647.4615478515625, + null, + null, + null, + null, + 520.8461303710938, + 587.4615478515625 + ] + ], + "points": [ + [ + 475.0961608886719, 647.4615478515625, 520.8461303710938, + 587.4615478515625 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 473.59618278650134, 585.9615278473268, 522.3461772570244, + 648.9615135559669 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "18R" + }, + "pdfjs_internal_editor_3": { + "annotationType": 15, + "color": [250, 23, 28], + "opacity": 0.55, + "thickness": 3, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 79.04861450195312, + 532.353759765625, + null, + null, + null, + null, + 198.18736267089844, + 378.6045837402344 + ] + ], + "points": [ + [ + 79.04861450195312, 532.353759765625, 198.18736267089844, + 378.6045837402344 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 77.54861622361037, 377.10462435392236, 199.6873893210521, + 533.8537948498359 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "19R" + }, + "pdfjs_internal_editor_4": { + "annotationType": 15, + "color": [70, 108, 241], + "opacity": 0.7, + "thickness": 20, + "paths": { + "lines": [ + [ + null, + null, + null, + null, + 273.6300048828125, + 535.47998046875, + null, + null, + null, + null, + 319.3800048828125, + 475.4800109863281 + ] + ], + "points": [ + [ + 273.6300048828125, 535.47998046875, 319.3800048828125, + 475.4800109863281 + ] + ] + }, + "pageIndex": 0, + "rect": [ + 263.629992209948, 465.48002000955444, 329.38000390621335, + 545.4799757370582 + ], + "rotation": 0, + "structTreeParentId": null, + "id": "20R" + } + } + }, + { + "id": "issue19182", + "file": "pdfs/issue19182.pdf", + "md5": "497370ab55cbb9c60bb0ea4b5d9e4d08", + "rounds": 1, + "type": "eq" + }, + { + "id": "tracemonkey-disableFontFace-eq", + "file": "pdfs/tracemonkey.pdf", + "md5": "9a192d8b1a7dc652a19835f6f08098bd", + "rounds": 1, + "disableFontFace": true, + "type": "eq" + }, + { + "id": "issue19234", + "file": "pdfs/issue19234.pdf", + "md5": "2b57e3c43fa0ae4a606c13df007c202f", + "rounds": 1, + "link": true, + "disableFontFace": true, + "type": "eq" + }, + { + "id": "issue19281", + "file": "pdfs/issue19281.pdf", + "md5": "c5db558965e4189cc6db2720cdaa3e55", + "rounds": 1, + "link": true, + "type": "other" + }, + { + "id": "issue19319", + "file": "pdfs/issue19319.pdf", + "md5": "8612d3f0cf2dd067ea4aec9c8bf98763", + "rounds": 1, + "link": true, + "firstPage": 2, + "lastPage": 2, + "type": "eq" + }, + { + "id": "issue19360", + "file": "pdfs/issue19360.pdf", + "md5": "b2de376f7e96fa2b6afc00dac016c40a", + "rounds": 1, + "type": "eq" + }, + { + "id": "signature-rotation-print", + "file": "pdfs/tracemonkey.pdf", + "md5": "9a192d8b1a7dc652a19835f6f08098bd", + "rounds": 1, + "lastPage": 1, + "type": "eq", + "print": true, + "annotationStorage": { + "pdfjs_internal_editor_0": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 30.33709076317874, 528.4258915511044, 92.2465096088973, + 605.8765082034198 + ], + "rotation": 0, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 30.337093353271484, + 605.8765258789062, + 30.337093353271484, + 598.2304077148438, + 34.63264083862305, + 596.6561889648438, + 43.22381591796875, + 596.5662841796875, + 51.8149528503418, + 596.476318359375, + 56.11055374145508, + 585.0970458984375, + 56.11055374145508, + 562.4286499023438, + 56.11055374145508, + 539.7601928710938, + 57.83761978149414, + 528.4259643554688, + 61.29179763793945, + 528.4259643554688, + 64.74597930908203, + 528.4259643554688, + 66.4730453491211, + 539.7601928710938, + 66.4730453491211, + 562.4286499023438, + 66.4730453491211, + 585.0970458984375, + 70.76863098144531, + 596.476318359375, + 79.35980224609375, + 596.5662841796875, + 87.9509048461914, + 596.6561889648438, + 92.24649810791016, + 598.2304077148438, + 92.24649810791016, + 601.288818359375, + 92.24649810791016, + 604.3472900390625, + 81.9282455444336, + 605.8765258789062, + 61.29179763793945, + 605.8765258789062 + ] + ] + }, + "pdfjs_internal_editor_1": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 115.2826878157529, 438.45786562832916, 250.9453054937449, + 540.3920820626346 + ], + "rotation": 90, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 115.2826919555664, + 438.4578552246094, + 128.67564392089844, + 438.4578552246094, + 131.4329833984375, + 445.5304870605469, + 131.59056091308594, + 459.6759033203125, + 131.7481231689453, + 473.8213806152344, + 151.6800079345703, + 480.8940734863281, + 191.38607788085938, + 480.8940734863281, + 231.09228515625, + 480.8940734863281, + 250.94525146484375, + 483.7376708984375, + 250.94525146484375, + 489.425048828125, + 250.94525146484375, + 495.1123046875, + 231.09228515625, + 497.955810546875, + 191.38607788085938, + 497.955810546875, + 151.6800079345703, + 497.955810546875, + 131.7481231689453, + 505.0286865234375, + 131.59056091308594, + 519.1740112304688, + 131.4329833984375, + 533.3192749023438, + 128.67564392089844, + 540.3919067382812, + 123.31848907470703, + 540.3919067382812, + 117.96131134033203, + 540.3919067382812, + 115.2826919555664, + 523.4029541015625, + 115.2826919555664, + 489.425048828125 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + }, + "pdfjs_internal_editor_2": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 394.41588899764145, 521.6146706667813, 429.2137094410983, + 565.6428880366412 + ], + "rotation": 180, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 429.2137145996094, + 521.6146850585938, + 429.2137145996094, + 525.9612426757812, + 426.7992858886719, + 526.8561401367188, + 421.97039794921875, + 526.9072265625, + 417.1415100097656, + 526.9583740234375, + 414.72705078125, + 533.4271240234375, + 414.72705078125, + 546.3134155273438, + 414.72705078125, + 559.1997680664062, + 413.7563171386719, + 565.6429443359375, + 411.8147888183594, + 565.6429443359375, + 409.873291015625, + 565.6429443359375, + 408.9025573730469, + 559.1997680664062, + 408.9025573730469, + 546.3134155273438, + 408.9025573730469, + 533.4271240234375, + 406.48809814453125, + 526.9583740234375, + 401.6592102050781, + 526.9072265625, + 396.8303527832031, + 526.8561401367188, + 394.4158935546875, + 525.9612426757812, + 394.4158935546875, + 524.2225952148438, + 394.4158935546875, + 522.4840087890625, + 400.2155456542969, + 521.6146850585938, + 411.8147888183594, + 521.6146850585938 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + }, + "pdfjs_internal_editor_3": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 527.772727521983, 549.8299978104504, 563.1363680037585, + 575.0790592106906 + ], + "rotation": 270, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 563.1363525390625, + 575.0791015625, + 559.6452026367188, + 575.0791015625, + 558.9263916015625, + 573.3272094726562, + 558.8853149414062, + 569.8233642578125, + 558.8442993164062, + 566.319580078125, + 553.6485595703125, + 564.5676879882812, + 543.2982177734375, + 564.5676879882812, + 532.9478759765625, + 564.5676879882812, + 527.772705078125, + 563.86328125, + 527.772705078125, + 562.4545288085938, + 527.772705078125, + 561.0458374023438, + 532.9478759765625, + 560.3414306640625, + 543.2982177734375, + 560.3414306640625, + 553.6485595703125, + 560.3414306640625, + 558.8442993164062, + 558.5894775390625, + 558.8853149414062, + 555.085693359375, + 558.9263916015625, + 551.5819091796875, + 559.6452026367188, + 549.8300170898438, + 561.0416259765625, + 549.8300170898438, + 562.4381103515625, + 549.8300170898438, + 563.1363525390625, + 554.0382080078125, + 563.1363525390625, + 562.4545288085938 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + } + } + }, + { + "id": "signature-rotation-save", + "file": "pdfs/tracemonkey.pdf", + "md5": "9a192d8b1a7dc652a19835f6f08098bd", + "rounds": 1, + "lastPage": 1, + "type": "eq", + "print": true, + "save": true, + "annotationStorage": { + "pdfjs_internal_editor_0": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 30.33709076317874, 528.4258915511044, 92.2465096088973, + 605.8765082034198 + ], + "rotation": 0, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 30.337093353271484, + 605.8765258789062, + 30.337093353271484, + 598.2304077148438, + 34.63264083862305, + 596.6561889648438, + 43.22381591796875, + 596.5662841796875, + 51.8149528503418, + 596.476318359375, + 56.11055374145508, + 585.0970458984375, + 56.11055374145508, + 562.4286499023438, + 56.11055374145508, + 539.7601928710938, + 57.83761978149414, + 528.4259643554688, + 61.29179763793945, + 528.4259643554688, + 64.74597930908203, + 528.4259643554688, + 66.4730453491211, + 539.7601928710938, + 66.4730453491211, + 562.4286499023438, + 66.4730453491211, + 585.0970458984375, + 70.76863098144531, + 596.476318359375, + 79.35980224609375, + 596.5662841796875, + 87.9509048461914, + 596.6561889648438, + 92.24649810791016, + 598.2304077148438, + 92.24649810791016, + 601.288818359375, + 92.24649810791016, + 604.3472900390625, + 81.9282455444336, + 605.8765258789062, + 61.29179763793945, + 605.8765258789062 + ] + ] + }, + "pdfjs_internal_editor_1": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 115.2826878157529, 438.45786562832916, 250.9453054937449, + 540.3920820626346 + ], + "rotation": 90, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 115.2826919555664, + 438.4578552246094, + 128.67564392089844, + 438.4578552246094, + 131.4329833984375, + 445.5304870605469, + 131.59056091308594, + 459.6759033203125, + 131.7481231689453, + 473.8213806152344, + 151.6800079345703, + 480.8940734863281, + 191.38607788085938, + 480.8940734863281, + 231.09228515625, + 480.8940734863281, + 250.94525146484375, + 483.7376708984375, + 250.94525146484375, + 489.425048828125, + 250.94525146484375, + 495.1123046875, + 231.09228515625, + 497.955810546875, + 191.38607788085938, + 497.955810546875, + 151.6800079345703, + 497.955810546875, + 131.7481231689453, + 505.0286865234375, + 131.59056091308594, + 519.1740112304688, + 131.4329833984375, + 533.3192749023438, + 128.67564392089844, + 540.3919067382812, + 123.31848907470703, + 540.3919067382812, + 117.96131134033203, + 540.3919067382812, + 115.2826919555664, + 523.4029541015625, + 115.2826919555664, + 489.425048828125 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + }, + "pdfjs_internal_editor_2": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 394.41588899764145, 521.6146706667813, 429.2137094410983, + 565.6428880366412 + ], + "rotation": 180, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 429.2137145996094, + 521.6146850585938, + 429.2137145996094, + 525.9612426757812, + 426.7992858886719, + 526.8561401367188, + 421.97039794921875, + 526.9072265625, + 417.1415100097656, + 526.9583740234375, + 414.72705078125, + 533.4271240234375, + 414.72705078125, + 546.3134155273438, + 414.72705078125, + 559.1997680664062, + 413.7563171386719, + 565.6429443359375, + 411.8147888183594, + 565.6429443359375, + 409.873291015625, + 565.6429443359375, + 408.9025573730469, + 559.1997680664062, + 408.9025573730469, + 546.3134155273438, + 408.9025573730469, + 533.4271240234375, + 406.48809814453125, + 526.9583740234375, + 401.6592102050781, + 526.9072265625, + 396.8303527832031, + 526.8561401367188, + 394.4158935546875, + 525.9612426757812, + 394.4158935546875, + 524.2225952148438, + 394.4158935546875, + 522.4840087890625, + 400.2155456542969, + 521.6146850585938, + 411.8147888183594, + 521.6146850585938 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + }, + "pdfjs_internal_editor_3": { + "annotationType": 101, + "isSignature": true, + "areContours": true, + "color": [0, 0, 0], + "thickness": 0, + "pageIndex": 0, + "rect": [ + 527.772727521983, 549.8299978104504, 563.1363680037585, + 575.0790592106906 + ], + "rotation": 270, + "structTreeParentId": null, + "lines": [ + [ + null, + null, + null, + null, + 563.1363525390625, + 575.0791015625, + 559.6452026367188, + 575.0791015625, + 558.9263916015625, + 573.3272094726562, + 558.8853149414062, + 569.8233642578125, + 558.8442993164062, + 566.319580078125, + 553.6485595703125, + 564.5676879882812, + 543.2982177734375, + 564.5676879882812, + 532.9478759765625, + 564.5676879882812, + 527.772705078125, + 563.86328125, + 527.772705078125, + 562.4545288085938, + 527.772705078125, + 561.0458374023438, + 532.9478759765625, + 560.3414306640625, + 543.2982177734375, + 560.3414306640625, + 553.6485595703125, + 560.3414306640625, + 558.8442993164062, + 558.5894775390625, + 558.8853149414062, + 555.085693359375, + 558.9263916015625, + 551.5819091796875, + 559.6452026367188, + 549.8300170898438, + 561.0416259765625, + 549.8300170898438, + 562.4381103515625, + 549.8300170898438, + 563.1363525390625, + 554.0382080078125, + 563.1363525390625, + 562.4545288085938 + ] + ], + "accessibilityData": { + "type": "Figure", + "alt": "T" + } + } + } } ] diff --git a/test/unit/.eslintrc b/test/unit/.eslintrc deleted file mode 100644 index 69af6d92d1199..0000000000000 --- a/test/unit/.eslintrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": [ - "../.eslintrc" - ], - - "rules": { - // Plugins - "import/no-unresolved": ["error", { "ignore": ["pdfjs/"] }], - }, -} diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 9ef611407352f..8124fe6cf294a 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -34,19 +34,18 @@ import { import { CMAP_URL, createIdFactory, + DefaultCMapReaderFactory, + DefaultStandardFontDataFactory, STANDARD_FONT_DATA_URL, XRefMock, } from "./test_utils.js"; -import { - DefaultCMapReaderFactory, - DefaultStandardFontDataFactory, -} from "../../src/display/api.js"; import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js"; import { Lexer, Parser } from "../../src/core/parser.js"; import { FlateStream } from "../../src/core/flate_stream.js"; import { PartialEvaluator } from "../../src/core/evaluator.js"; import { StringStream } from "../../src/core/stream.js"; import { WorkerTask } from "../../src/core/worker.js"; +import { writeChanges } from "../../src/core/writer.js"; describe("annotation", function () { class PDFManagerMock { @@ -2120,14 +2119,12 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: "hello world" }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -2146,6 +2143,81 @@ describe("annotation", function () { ); }); + it("should save the text in two fields with the same name", async function () { + const textWidget1Ref = Ref.get(123, 0); + const textWidget2Ref = Ref.get(124, 0); + + const parentRef = Ref.get(125, 0); + textWidgetDict.set("Parent", parentRef); + const parentDict = new Dict(); + parentDict.set("Kids", [textWidget1Ref, textWidget2Ref]); + parentDict.set("T", "foo"); + const textWidget2Dict = textWidgetDict.clone(); + + const xref = new XRefMock([ + { ref: textWidget1Ref, data: textWidgetDict }, + { ref: textWidget2Ref, data: textWidget2Dict }, + { ref: parentRef, data: parentDict }, + helvRefObj, + ]); + partialEvaluator.xref = xref; + const task = new WorkerTask("test save"); + + const annotation1 = await AnnotationFactory.create( + xref, + textWidget1Ref, + annotationGlobalsMock, + idFactoryMock + ); + const annotation2 = await AnnotationFactory.create( + xref, + textWidget2Ref, + annotationGlobalsMock, + idFactoryMock + ); + const annotationStorage = new Map(); + annotationStorage.set(annotation1.data.id, { value: "hello world" }); + annotationStorage.set(annotation2.data.id, { value: "hello world" }); + const changes = new RefSetCache(); + + await annotation1.save( + partialEvaluator, + task, + annotationStorage, + changes + ); + await annotation2.save( + partialEvaluator, + task, + annotationStorage, + changes + ); + const data = await writeChanges(changes, xref); + expect(data.length).toEqual(5); + const [, , data1, data2, parentData] = data; + expect(data1.ref).toEqual(textWidget1Ref); + expect(data2.ref).toEqual(textWidget2Ref); + expect(parentData.ref).toEqual(parentRef); + + data1.data = data1.data.replace(/\(D:\d+\)/, "(date)"); + data2.data = data2.data.replace(/\(D:\d+\)/, "(date)"); + expect(data1.data).toEqual( + "123 0 obj\n" + + "<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " + + "<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " + + "/Parent 125 0 R /AP << /N 4 0 R>> /M (date)>>\nendobj\n" + ); + expect(data2.data).toEqual( + "124 0 obj\n" + + "<< /Type /Annot /Subtype /Widget /FT /Tx /DA (/Helv 5 Tf) /DR " + + "<< /Font << /Helv 314 0 R>>>> /Rect [0 0 32 10] " + + "/Parent 125 0 R /AP << /N 5 0 R>> /M (date)>>\nendobj\n" + ); + expect(parentData.data).toEqual( + "125 0 obj\n<< /Kids [123 0 R 124 0 R] /T (foo) /V (hello world)>>\nendobj\n" + ); + }); + it("should save rotated text", async function () { const textWidgetRef = Ref.get(123, 0); const xref = new XRefMock([ @@ -2166,14 +2238,12 @@ describe("annotation", function () { value: "hello world", rotation: 90, }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -2210,14 +2280,12 @@ describe("annotation", function () { const annotationStorage = new Map(); const value = "a".repeat(256); annotationStorage.set(annotation.data.id, { value }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -2356,16 +2424,14 @@ describe("annotation", function () { annotationStorage.set(annotation.data.id, { value: "こんにちは世界の", }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); const utf16String = "\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e"; expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -2771,12 +2837,10 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: true }); + const changes = new RefSetCache(); - const [oldData] = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const [oldData] = await writeChanges(changes, xref); oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)"); expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(oldData.data).toEqual( @@ -2788,12 +2852,9 @@ describe("annotation", function () { annotationStorage.set(annotation.data.id, { value: false }); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); - expect(data).toEqual(null); + changes.clear(); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + expect(changes.size).toEqual(0); }); it("should save rotated checkboxes", async function () { @@ -2822,12 +2883,10 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: true, rotation: 180 }); + const changes = new RefSetCache(); - const [oldData] = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const [oldData] = await writeChanges(changes, xref); oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)"); expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(oldData.data).toEqual( @@ -2839,12 +2898,9 @@ describe("annotation", function () { annotationStorage.set(annotation.data.id, { value: false }); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); - expect(data).toEqual(null); + changes.clear(); + await annotation.save(partialEvaluator, task, annotationStorage); + expect(changes.size).toEqual(0); }); it("should handle radio buttons with a field value", async function () { @@ -3097,6 +3153,7 @@ describe("annotation", function () { const parentDict = new Dict(); parentDict.set("V", Name.get("Off")); parentDict.set("Kids", [buttonWidgetRef]); + parentDict.set("T", "RadioGroup"); buttonWidgetDict.set("Parent", parentRef); const xref = new XRefMock([ @@ -3117,12 +3174,10 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: true }); + const changes = new RefSetCache(); - let data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); const [radioData, parentData] = data; radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)"); @@ -3135,13 +3190,14 @@ describe("annotation", function () { ); expect(parentData.ref).toEqual(Ref.get(456, 0)); expect(parentData.data).toEqual( - "456 0 obj\n<< /V /Checked /Kids [123 0 R]>>\nendobj\n" + "456 0 obj\n<< /V /Checked /Kids [123 0 R] /T (RadioGroup)>>\nendobj\n" ); annotationStorage.set(annotation.data.id, { value: false }); - data = await annotation.save(partialEvaluator, task, annotationStorage); - expect(data).toEqual(null); + changes.clear(); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + expect(changes.size).toEqual(0); }); it("should save radio buttons without a field value", async function () { @@ -3160,6 +3216,7 @@ describe("annotation", function () { const parentDict = new Dict(); parentDict.set("Kids", [buttonWidgetRef]); + parentDict.set("T", "RadioGroup"); buttonWidgetDict.set("Parent", parentRef); const xref = new XRefMock([ @@ -3180,12 +3237,10 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: true }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); const [radioData, parentData] = data; radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)"); @@ -3198,7 +3253,7 @@ describe("annotation", function () { ); expect(parentData.ref).toEqual(Ref.get(456, 0)); expect(parentData.data).toEqual( - "456 0 obj\n<< /Kids [123 0 R] /V /Checked>>\nendobj\n" + "456 0 obj\n<< /Kids [123 0 R] /T (RadioGroup) /V /Checked>>\nendobj\n" ); }); @@ -3216,13 +3271,10 @@ describe("annotation", function () { idFactoryMock ); const annotationStorage = new Map(); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); - expect(data).toEqual(null); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + expect(changes.size).toEqual(0); }); it("should handle push buttons", async function () { @@ -3734,14 +3786,12 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: "C", rotation: 270 }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -3795,14 +3845,12 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: "C" }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -3860,15 +3908,12 @@ describe("annotation", function () { ); const annotationStorage = new Map(); annotationStorage.set(annotation.data.id, { value: ["B", "C"] }); + const changes = new RefSetCache(); - const data = await annotation.save( - partialEvaluator, - task, - annotationStorage - ); - + await annotation.save(partialEvaluator, task, annotationStorage, changes); + const data = await writeChanges(changes, xref); expect(data.length).toEqual(2); - const [oldData, newData] = data; + const [newData, oldData] = data; expect(oldData.ref).toEqual(Ref.get(123, 0)); expect(newData.ref).toEqual(Ref.get(2, 0)); @@ -4154,9 +4199,10 @@ describe("annotation", function () { describe("FreeTextAnnotation", () => { it("should create a new FreeText annotation", async () => { - partialEvaluator.xref = new XRefMock(); + const xref = (partialEvaluator.xref = new XRefMock()); const task = new WorkerTask("test FreeText creation"); - const data = await AnnotationFactory.saveNewAnnotations( + const changes = new RefSetCache(); + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4168,10 +4214,13 @@ describe("annotation", function () { color: [0, 0, 0], value: "Hello PDF.js World!", }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)"); + const base = data[1].data.replace(/\(D:\d+\)/, "(date)"); expect(base).toEqual( "2 0 obj\n" + "<< /Type /Annot /Subtype /FreeText /CreationDate (date) " + @@ -4180,7 +4229,7 @@ describe("annotation", function () { "endobj\n" ); - const font = data.dependencies[0].data; + const font = data[0].data; expect(font).toEqual( "1 0 obj\n" + "<< /BaseFont /Helvetica /Type /Font /Subtype /Type1 /Encoding " + @@ -4188,7 +4237,7 @@ describe("annotation", function () { "endobj\n" ); - const appearance = data.dependencies[1].data; + const appearance = data[2].data; expect(appearance).toEqual( "3 0 obj\n" + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " + @@ -4265,12 +4314,13 @@ describe("annotation", function () { freeTextDict.set("Foo", Name.get("Bar")); const freeTextRef = Ref.get(143, 0); - partialEvaluator.xref = new XRefMock([ + const xref = (partialEvaluator.xref = new XRefMock([ { ref: freeTextRef, data: freeTextDict }, - ]); + ])); + const changes = new RefSetCache(); const task = new WorkerTask("test FreeText update"); - const data = await AnnotationFactory.saveNewAnnotations( + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4285,10 +4335,13 @@ describe("annotation", function () { ref: freeTextRef, oldAnnotation: freeTextDict, }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replaceAll(/\(D:\d+\)/g, "(date)"); + const base = data[2].data.replaceAll(/\(D:\d+\)/g, "(date)"); expect(base).toEqual( "143 0 obj\n" + "<< /Type /Annot /Subtype /FreeText /CreationDate (date) /Foo /Bar /M (date) " + @@ -4379,9 +4432,10 @@ describe("annotation", function () { }); it("should create a new Ink annotation", async function () { - partialEvaluator.xref = new XRefMock(); + const xref = (partialEvaluator.xref = new XRefMock()); + const changes = new RefSetCache(); const task = new WorkerTask("test Ink creation"); - const data = await AnnotationFactory.saveNewAnnotations( + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4392,26 +4446,62 @@ describe("annotation", function () { thickness: 1, opacity: 1, color: [0, 0, 0], - paths: [ - { - bezier: [ - 10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 24, 25, 26, 27, + paths: { + lines: [ + [ + NaN, + NaN, + NaN, + NaN, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 22, + 23, + 24, + 25, + 26, + 27, ], - points: [1, 2, 3, 4, 5, 6, 7, 8], - }, - { - bezier: [ - 910, 911, 912, 913, 914, 915, 916, 917, 922, 923, 924, 925, - 926, 927, + [ + NaN, + NaN, + NaN, + NaN, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 922, + 923, + 924, + 925, + 926, + 927, ], - points: [91, 92, 93, 94, 95, 96, 97, 98], - }, - ], + ], + points: [ + [1, 2, 3, 4, 5, 6, 7, 8], + [91, 92, 93, 94, 95, 96, 97, 98], + ], + }, }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)"); + const base = data[0].data.replace(/\(D:\d+\)/, "(date)"); expect(base).toEqual( "1 0 obj\n" + "<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " + @@ -4420,16 +4510,15 @@ describe("annotation", function () { "endobj\n" ); - const appearance = data.dependencies[0].data; + const appearance = data[1].data; expect(appearance).toEqual( "2 0 obj\n" + - "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 129>> stream\n" + + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 127>> stream\n" + "1 w 1 J 1 j\n" + "0 G\n" + "10 11 m\n" + "12 13 14 15 16 17 c\n" + "22 23 24 25 26 27 c\n" + - "S\n" + "910 911 m\n" + "912 913 914 915 916 917 c\n" + "922 923 924 925 926 927 c\n" + @@ -4440,9 +4529,10 @@ describe("annotation", function () { }); it("should create a new Ink annotation with some transparency", async function () { - partialEvaluator.xref = new XRefMock(); + const xref = (partialEvaluator.xref = new XRefMock()); + const changes = new RefSetCache(); const task = new WorkerTask("test Ink creation"); - const data = await AnnotationFactory.saveNewAnnotations( + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4453,26 +4543,62 @@ describe("annotation", function () { thickness: 1, opacity: 0.12, color: [0, 0, 0], - paths: [ - { - bezier: [ - 10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 24, 25, 26, 27, + paths: { + lines: [ + [ + NaN, + NaN, + NaN, + NaN, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 22, + 23, + 24, + 25, + 26, + 27, ], - points: [1, 2, 3, 4, 5, 6, 7, 8], - }, - { - bezier: [ - 910, 911, 912, 913, 914, 915, 916, 917, 922, 923, 924, 925, - 926, 927, + [ + NaN, + NaN, + NaN, + NaN, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 922, + 923, + 924, + 925, + 926, + 927, ], - points: [91, 92, 93, 94, 95, 96, 97, 98], - }, - ], + ], + points: [ + [1, 2, 3, 4, 5, 6, 7, 8], + [91, 92, 93, 94, 95, 96, 97, 98], + ], + }, }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)"); + const base = data[0].data.replace(/\(D:\d+\)/, "(date)"); expect(base).toEqual( "1 0 obj\n" + "<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " + @@ -4481,10 +4607,10 @@ describe("annotation", function () { "endobj\n" ); - const appearance = data.dependencies[0].data; + const appearance = data[1].data; expect(appearance).toEqual( "2 0 obj\n" + - "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 136 /Resources " + + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 134 /Resources " + "<< /ExtGState << /R0 << /CA 0.12 /Type /ExtGState>>>>>>>> stream\n" + "1 w 1 J 1 j\n" + "0 G\n" + @@ -4492,7 +4618,6 @@ describe("annotation", function () { "10 11 m\n" + "12 13 14 15 16 17 c\n" + "22 23 24 25 26 27 c\n" + - "S\n" + "910 911 m\n" + "912 913 914 915 916 917 c\n" + "922 923 924 925 926 927 c\n" + @@ -4518,13 +4643,10 @@ describe("annotation", function () { thickness: 3, opacity: 1, color: [0, 255, 0], - paths: [ - { - bezier: [1, 2, 3, 4, 5, 6, 7, 8], - // Useless in the printing case. - points: [1, 2, 3, 4, 5, 6, 7, 8], - }, - ], + paths: { + lines: [[NaN, NaN, NaN, NaN, 1, 2, 3, 4, 5, 6, 7, 8]], + points: [[1, 2, 3, 4, 5, 6, 7, 8]], + }, }, ] ) @@ -4627,9 +4749,10 @@ describe("annotation", function () { }); it("should create a new Highlight annotation", async function () { - partialEvaluator.xref = new XRefMock(); + const xref = (partialEvaluator.xref = new XRefMock()); + const changes = new RefSetCache(); const task = new WorkerTask("test Highlight creation"); - const data = await AnnotationFactory.saveNewAnnotations( + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4645,10 +4768,13 @@ describe("annotation", function () { [12, 13, 14, 15], ], }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)"); + const base = data[0].data.replace(/\(D:\d+\)/, "(date)"); expect(base).toEqual( "1 0 obj\n" + "<< /Type /Annot /Subtype /Highlight /CreationDate (date) /Rect [12 34 56 78] " + @@ -4657,7 +4783,7 @@ describe("annotation", function () { "endobj\n" ); - const appearance = data.dependencies[0].data; + const appearance = data[1].data; expect(appearance).toEqual( "2 0 obj\n" + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " + @@ -4717,9 +4843,10 @@ describe("annotation", function () { }); it("should create a new free Highlight annotation", async function () { - partialEvaluator.xref = new XRefMock(); + const xref = (partialEvaluator.xref = new XRefMock()); + const changes = new RefSetCache(); const task = new WorkerTask("test free Highlight creation"); - const data = await AnnotationFactory.saveNewAnnotations( + await AnnotationFactory.saveNewAnnotations( partialEvaluator, task, [ @@ -4749,10 +4876,13 @@ describe("annotation", function () { points: [Float32Array.from([16, 17, 18, 19])], }, }, - ] + ], + null, + changes ); + const data = await writeChanges(changes, xref); - const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)"); + const base = data[0].data.replace(/\(D:\d+\)/, "(date)"); expect(base).toEqual( "1 0 obj\n" + "<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " + @@ -4761,7 +4891,7 @@ describe("annotation", function () { "endobj\n" ); - const appearance = data.dependencies[0].data; + const appearance = data[1].data; expect(appearance).toEqual( "2 0 obj\n" + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " + @@ -4960,4 +5090,55 @@ describe("annotation", function () { ); }); }); + + describe("StampAnnotation for signatures", function () { + it("should create a new Stamp annotation", async function () { + const xref = (partialEvaluator.xref = new XRefMock()); + const changes = new RefSetCache(); + const task = new WorkerTask("test Stamp creation"); + await AnnotationFactory.saveNewAnnotations( + partialEvaluator, + task, + [ + { + annotationType: 101, + isSignature: true, + areContours: true, + color: [0, 0, 0], + thickness: 0, + pageIndex: 0, + rect: [12, 34, 56, 78], + rotation: 0, + structTreeParentId: null, + lines: [[NaN, NaN, NaN, NaN, 1, 2, 3, 4, 5, 6, 7, 8]], + }, + ], + null, + changes + ); + const data = await writeChanges(changes, xref); + + const base = data[0].data.replace(/\(D:\d+\)/, "(date)"); + expect(base).toEqual( + "1 0 obj\n" + + "<< /Type /Annot /Subtype /Stamp /CreationDate (date) /Rect [12 34 56 78] " + + "/F 4 /Border [0 0 0] " + + "/Rotate 0 /AP << /N 2 0 R>>>>\n" + + "endobj\n" + ); + + const appearance = data[1].data; + expect(appearance).toEqual( + "2 0 obj\n" + + "<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 37>> stream\n" + + "0 w 1 J 1 j\n" + + "0 g\n" + + "1 2 m\n" + + "3 4 5 6 7 8 c\n" + + "F\n" + + "endstream\n" + + "endobj\n" + ); + }); + }); }); diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index bb78c9c32b31d..3acc4c38f7a19 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -20,23 +20,29 @@ import { ImageKind, InvalidPDFException, isNodeJS, - MissingPDFException, objectSize, OPS, PasswordException, PasswordResponses, PermissionFlag, + ResponseException, UnknownErrorException, } from "../../src/shared/util.js"; import { buildGetDocumentParams, CMAP_URL, - createTemporaryNodeServer, DefaultFileReaderFactory, + getCrossOriginHostname, TEST_PDFS_PATH, + TestPdfsServer, } from "./test_utils.js"; import { - DefaultCanvasFactory, + fetchData as fetchDataDOM, + PageViewport, + RenderingCancelledException, + StatTimer, +} from "../../src/display/display_utils.js"; +import { getDocument, PDFDataRangeTransport, PDFDocumentLoadingTask, @@ -45,11 +51,6 @@ import { PDFWorker, RenderTask, } from "../../src/display/api.js"; -import { - PageViewport, - RenderingCancelledException, - StatTimer, -} from "../../src/display/display_utils.js"; import { AutoPrintRegExp } from "../../web/ui_utils.js"; import { GlobalImageCache } from "../../src/core/image_utils.js"; import { GlobalWorkerOptions } from "../../src/display/worker_options.js"; @@ -65,28 +66,12 @@ describe("api", function () { const tracemonkeyGetDocumentParams = buildGetDocumentParams(tracemonkeyFileName); - let CanvasFactory; - let tempServer = null; - - beforeAll(function () { - CanvasFactory = new DefaultCanvasFactory({}); - - if (isNodeJS) { - tempServer = createTemporaryNodeServer(); - } + beforeAll(async function () { + await TestPdfsServer.ensureStarted(); }); - afterAll(function () { - CanvasFactory = null; - - if (isNodeJS) { - // Close the server from accepting new connections after all test - // finishes. - const { server } = tempServer; - server.close(); - - tempServer = null; - } + afterAll(async function () { + await TestPdfsServer.ensureStopped(); }); function waitSome(callback) { @@ -117,6 +102,21 @@ describe("api", function () { return node; } + async function getImageBlob(filename) { + if (isNodeJS) { + throw new Error("Not implemented."); + } + const TEST_IMAGES_PATH = "../images/"; + const url = new URL(TEST_IMAGES_PATH + filename, window.location).href; + + return fetchDataDOM(url, /* type = */ "blob"); + } + + async function getImageBitmap(filename) { + const blob = await getImageBlob(filename); + return createImageBitmap(blob); + } + describe("getDocument", function () { it("creates pdf doc from URL-string", async function () { const urlStr = TEST_PDFS_PATH + basicApiFileName; @@ -132,9 +132,7 @@ describe("api", function () { }); it("creates pdf doc from URL-object", async function () { - const urlObj = isNodeJS - ? new URL(`http://127.0.0.1:${tempServer.port}/${basicApiFileName}`) - : new URL(TEST_PDFS_PATH + basicApiFileName, window.location); + const urlObj = TestPdfsServer.resolveURL(basicApiFileName); const loadingTask = getDocument(urlObj); expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); @@ -194,9 +192,9 @@ describe("api", function () { expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); // This can be somewhat random -- we cannot guarantee perfect // 'Terminate' message to the worker before/after setting up pdfManager. - const destroyed = loadingTask._worker.promise.then(function () { - return loadingTask.destroy(); - }); + const destroyed = loadingTask._worker.promise.then(() => + loadingTask.destroy() + ); await destroyed; expect(true).toEqual(true); @@ -292,7 +290,9 @@ describe("api", function () { // Shouldn't get here. expect(false).toEqual(true); } catch (reason) { - expect(reason instanceof MissingPDFException).toEqual(true); + expect(reason instanceof ResponseException).toEqual(true); + expect(reason.status).toEqual(isNodeJS ? 0 : 404); + expect(reason.missing).toEqual(true); } await loadingTask.destroy(); @@ -623,7 +623,7 @@ describe("api", function () { expect(false).toEqual(true); } catch (reason) { expect(reason instanceof InvalidPDFException).toEqual(true); - expect(reason.message).toEqual("Invalid PDF structure."); + expect(reason.message).toEqual("Invalid Root reference."); } await loadingTask.destroy(); @@ -861,9 +861,9 @@ describe("api", function () { expect(!!worker).toEqual(true); }); - const destroyPromise = loadingTask.promise.then(function () { - return loadingTask.destroy(); - }); + const destroyPromise = loadingTask.promise.then(() => + loadingTask.destroy() + ); await destroyPromise; const destroyedWorker = loadingTask._worker; @@ -890,9 +890,9 @@ describe("api", function () { expect(messageHandlerPort === worker.port).toEqual(true); }); - const destroyPromise = loadingTask.promise.then(function () { - return loadingTask.destroy(); - }); + const destroyPromise = loadingTask.promise.then(() => + loadingTask.destroy() + ); await destroyPromise; expect(worker.destroyed).toEqual(false); @@ -1141,8 +1141,8 @@ describe("api", function () { buildGetDocumentParams("Pages-tree-refs.pdf") ); - const page1 = loadingTask.promise.then(function (pdfDoc) { - return pdfDoc.getPage(1).then( + const page1 = loadingTask.promise.then(pdfDoc => + pdfDoc.getPage(1).then( function (pdfPage) { expect(pdfPage instanceof PDFPageProxy).toEqual(true); expect(pdfPage.ref).toEqual({ num: 6, gen: 0 }); @@ -1150,11 +1150,11 @@ describe("api", function () { function (reason) { throw new Error("shall not fail for valid page"); } - ); - }); + ) + ); - const page2 = loadingTask.promise.then(function (pdfDoc) { - return pdfDoc.getPage(2).then( + const page2 = loadingTask.promise.then(pdfDoc => + pdfDoc.getPage(2).then( function (pdfPage) { throw new Error("shall fail for invalid page"); }, @@ -1164,8 +1164,8 @@ describe("api", function () { "Pages tree contains circular reference." ); } - ); - }); + ) + ); await Promise.all([page1, page2]); await loadingTask.destroy(); @@ -1261,6 +1261,19 @@ describe("api", function () { await loadingTask.destroy(); }); + it("gets destinations, from /Names (NameTree) respectively /Dests dictionary", async function () { + const loadingTask = getDocument(buildGetDocumentParams("issue19474.pdf")); + const pdfDoc = await loadingTask.promise; + const destinations = await pdfDoc.getDestinations(); + expect(destinations).toEqual({ + A: [{ num: 1, gen: 0 }, { name: "Fit" }], + B: [{ num: 4, gen: 0 }, { name: "Fit" }], + C: [{ num: 5, gen: 0 }, { name: "Fit" }], + }); + + await loadingTask.destroy(); + }); + it("gets a destination, from /Names (NameTree) dictionary", async function () { const loadingTask = getDocument(buildGetDocumentParams("issue6204.pdf")); const pdfDoc = await loadingTask.promise; @@ -1320,6 +1333,22 @@ describe("api", function () { await loadingTask.destroy(); }); + it("gets a destination, from /Names (NameTree) respectively /Dests dictionary", async function () { + const loadingTask = getDocument(buildGetDocumentParams("issue19474.pdf")); + const pdfDoc = await loadingTask.promise; + + const destA = await pdfDoc.getDestination("A"); + expect(destA).toEqual([{ num: 1, gen: 0 }, { name: "Fit" }]); + + const destB = await pdfDoc.getDestination("B"); + expect(destB).toEqual([{ num: 4, gen: 0 }, { name: "Fit" }]); + + const destC = await pdfDoc.getDestination("C"); + expect(destC).toEqual([{ num: 5, gen: 0 }, { name: "Fit" }]); + + await loadingTask.destroy(); + }); + it("gets non-string destination", async function () { let numberPromise = pdfDocument.getDestination(4.3); let booleanPromise = pdfDocument.getDestination(true); @@ -1367,29 +1396,29 @@ describe("api", function () { it("gets page labels", async function () { // PageLabels with Roman/Arabic numerals. const loadingTask0 = getDocument(buildGetDocumentParams("bug793632.pdf")); - const promise0 = loadingTask0.promise.then(function (pdfDoc) { - return pdfDoc.getPageLabels(); - }); + const promise0 = loadingTask0.promise.then(pdfDoc => + pdfDoc.getPageLabels() + ); // PageLabels with only a label prefix. const loadingTask1 = getDocument(buildGetDocumentParams("issue1453.pdf")); - const promise1 = loadingTask1.promise.then(function (pdfDoc) { - return pdfDoc.getPageLabels(); - }); + const promise1 = loadingTask1.promise.then(pdfDoc => + pdfDoc.getPageLabels() + ); // PageLabels identical to standard page numbering. const loadingTask2 = getDocument(buildGetDocumentParams("rotation.pdf")); - const promise2 = loadingTask2.promise.then(function (pdfDoc) { - return pdfDoc.getPageLabels(); - }); + const promise2 = loadingTask2.promise.then(pdfDoc => + pdfDoc.getPageLabels() + ); // PageLabels with bad "Prefix" entries. const loadingTask3 = getDocument( buildGetDocumentParams("bad-PageLabels.pdf") ); - const promise3 = loadingTask3.promise.then(function (pdfDoc) { - return pdfDoc.getPageLabels(); - }); + const promise3 = loadingTask3.promise.then(pdfDoc => + pdfDoc.getPageLabels() + ); const pageLabels = await Promise.all([ promise0, @@ -1483,9 +1512,7 @@ describe("api", function () { ); const promise1 = loadingTask1.promise - .then(function (pdfDoc) { - return pdfDoc.getOpenAction(); - }) + .then(pdfDoc => pdfDoc.getOpenAction()) .then(function (openAction) { expect(openAction.dest).toBeUndefined(); expect(openAction.action).toEqual("Print"); @@ -1493,9 +1520,7 @@ describe("api", function () { return loadingTask1.destroy(); }); const promise2 = loadingTask2.promise - .then(function (pdfDoc) { - return pdfDoc.getOpenAction(); - }) + .then(pdfDoc => pdfDoc.getOpenAction()) .then(function (openAction) { expect(openAction.dest).toBeUndefined(); expect(openAction.action).toEqual("Print"); @@ -1708,6 +1733,20 @@ describe("api", function () { await loadingTask.destroy(); }); + it("gets fieldObjects and skipping LinkAnnotations", async function () { + if (isNodeJS) { + pending("Linked test-cases are not supported in Node.js."); + } + + const loadingTask = getDocument(buildGetDocumentParams("issue19281.pdf")); + const pdfDoc = await loadingTask.promise; + const fieldObjects = await pdfDoc.getFieldObjects(); + + expect(fieldObjects).toEqual(null); + + await loadingTask.destroy(); + }); + it("gets non-existent calculationOrder", async function () { const calculationOrder = await pdfDocument.getCalculationOrderIds(); expect(calculationOrder).toEqual(null); @@ -2021,25 +2060,25 @@ describe("api", function () { const loadingTask0 = getDocument( buildGetDocumentParams("issue9972-1.pdf") ); - const promise0 = loadingTask0.promise.then(function (pdfDoc) { - return pdfDoc.getPermissions(); - }); + const promise0 = loadingTask0.promise.then(pdfDoc => + pdfDoc.getPermissions() + ); // Printing not allowed. const loadingTask1 = getDocument( buildGetDocumentParams("issue9972-2.pdf") ); - const promise1 = loadingTask1.promise.then(function (pdfDoc) { - return pdfDoc.getPermissions(); - }); + const promise1 = loadingTask1.promise.then(pdfDoc => + pdfDoc.getPermissions() + ); // Copying not allowed. const loadingTask2 = getDocument( buildGetDocumentParams("issue9972-3.pdf") ); - const promise2 = loadingTask2.promise.then(function (pdfDoc) { - return pdfDoc.getPermissions(); - }); + const promise2 = loadingTask2.promise.then(pdfDoc => + pdfDoc.getPermissions() + ); const totalPermissionCount = Object.keys(PermissionFlag).length; const permissions = await Promise.all([promise0, promise1, promise2]); @@ -2192,6 +2231,29 @@ describe("api", function () { expect(data.length).toEqual(basicApiFileLength); }); + it("gets data from PDF document with JPEG image containing EXIF-data (bug 1942064)", async function () { + const typedArrayPdf = await DefaultFileReaderFactory.fetch({ + path: TEST_PDFS_PATH + "bug1942064.pdf", + }); + + // Sanity check to make sure that we fetched the entire PDF file. + expect(typedArrayPdf instanceof Uint8Array).toEqual(true); + expect(typedArrayPdf.length).toEqual(10719); + + const loadingTask = getDocument(typedArrayPdf.slice()); + const pdfDoc = await loadingTask.promise; + const page = await pdfDoc.getPage(1); + // Trigger parsing of the JPEG image. + await page.getOperatorList(); + + const data = await pdfDoc.getData(); + expect(data instanceof Uint8Array).toEqual(true); + // Ensure that the EXIF-block wasn't modified. + expect(typedArrayPdf).toEqual(data); + + await loadingTask.destroy(); + }); + it("gets download info", async function () { const downloadInfo = await pdfDocument.getDownloadInfo(); expect(downloadInfo).toEqual({ length: basicApiFileLength }); @@ -2422,19 +2484,19 @@ describe("api", function () { const manifesto = ` The Mozilla Manifesto Addendum Pledge for a Healthy Internet - + The open, global internet is the most powerful communication and collaboration resource we have ever seen. It embodies some of our deepest hopes for human progress. It enables new opportunities for learning, building a sense of shared humanity, and solving the pressing problems facing people everywhere. - + Over the last decade we have seen this promise fulfilled in many ways. We have also seen the power of the internet used to magnify divisiveness, incite violence, promote hatred, and intentionally manipulate fact and reality. We have learned that we should more explicitly set out our aspirations for the human experience of the internet. We do so now. `.repeat(100); - expect(manifesto.length).toEqual(80500); + expect(manifesto.length).toEqual(79300); let loadingTask = getDocument(buildGetDocumentParams("empty.pdf")); let pdfDoc = await loadingTask.promise; @@ -2472,14 +2534,7 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } - - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); - const bitmap = await createImageBitmap(blob); + const bitmap = await getImageBitmap("firefox_logo.png"); let loadingTask = getDocument(buildGetDocumentParams("empty.pdf")); let pdfDoc = await loadingTask.promise; @@ -2524,14 +2579,7 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } - - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); - const bitmap = await createImageBitmap(blob); + const bitmap = await getImageBitmap("firefox_logo.png"); let loadingTask = getDocument(buildGetDocumentParams("bug1823296.pdf")); let pdfDoc = await loadingTask.promise; @@ -2636,14 +2684,7 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } - - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); - const bitmap = await createImageBitmap(blob); + const bitmap = await getImageBitmap("firefox_logo.png"); let loadingTask = getDocument( buildGetDocumentParams("pdfjs_wikipedia.pdf") @@ -2734,13 +2775,8 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } + const blob = await getImageBlob("firefox_logo.png"); - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); let loadingTask, pdfDoc; let data = buildGetDocumentParams("empty.pdf"); @@ -2804,14 +2840,7 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } - - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); - const bitmap = await createImageBitmap(blob); + const bitmap = await getImageBitmap("firefox_logo.png"); let loadingTask = getDocument(buildGetDocumentParams("empty.pdf")); let pdfDoc = await loadingTask.promise; @@ -2860,14 +2889,7 @@ describe("api", function () { if (isNodeJS) { pending("Cannot create a bitmap from Node.js."); } - - const TEST_IMAGES_PATH = "../images/"; - const filename = "firefox_logo.png"; - const path = new URL(TEST_IMAGES_PATH + filename, window.location).href; - - const response = await fetch(path); - const blob = await response.blob(); - const bitmap = await createImageBitmap(blob); + const bitmap = await getImageBitmap("firefox_logo.png"); let loadingTask = getDocument(buildGetDocumentParams("empty.pdf")); let pdfDoc = await loadingTask.promise; @@ -3013,23 +3035,18 @@ describe("api", function () { let loadingTask; function _checkCanLoad(expectSuccess, filename, options) { if (isNodeJS) { + // We can simulate cross-origin requests, but since Node.js does not + // enforce the Same Origin Policy, requests are expected to be allowed + // independently of withCredentials. pending("Cannot simulate cross-origin requests in Node.js"); } const params = buildGetDocumentParams(filename, options); const url = new URL(params.url); - if (url.hostname === "localhost") { - url.hostname = "127.0.0.1"; - } else if (params.url.hostname === "127.0.0.1") { - url.hostname = "localhost"; - } else { - pending("Can only run cross-origin test on localhost!"); - } + url.hostname = getCrossOriginHostname(url.hostname); params.url = url.href; loadingTask = getDocument(params); return loadingTask.promise - .then(function (pdf) { - return pdf.destroy(); - }) + .then(pdf => pdf.destroy()) .then( function () { expect(expectSuccess).toEqual(true); @@ -3119,10 +3136,21 @@ describe("api", function () { expect(page.ref).toEqual({ num: 15, gen: 0 }); }); - it("gets userUnit", function () { + it("gets default userUnit", function () { expect(page.userUnit).toEqual(1.0); }); + it("gets non-default userUnit", async function () { + const loadingTask = getDocument(buildGetDocumentParams("issue19176.pdf")); + + const pdfDoc = await loadingTask.promise; + const pdfPage = await pdfDoc.getPage(1); + + expect(pdfPage.userUnit).toEqual(72); + + await loadingTask.destroy(); + }); + it("gets view", function () { expect(page.view).toEqual([0, 0, 595.28, 841.89]); }); @@ -3154,6 +3182,7 @@ describe("api", function () { expect(viewport instanceof PageViewport).toEqual(true); expect(viewport.viewBox).toEqual(page.view); + expect(viewport.userUnit).toEqual(page.userUnit); expect(viewport.scale).toEqual(1.5); expect(viewport.rotation).toEqual(90); expect(viewport.transform).toEqual([0, 1.5, 1.5, 0, 0, 0]); @@ -3161,6 +3190,26 @@ describe("api", function () { expect(viewport.height).toEqual(892.92); }); + it("gets viewport with non-default userUnit", async function () { + const loadingTask = getDocument(buildGetDocumentParams("issue19176.pdf")); + + const pdfDoc = await loadingTask.promise; + const pdfPage = await pdfDoc.getPage(1); + + const viewport = pdfPage.getViewport({ scale: 1 }); + expect(viewport instanceof PageViewport).toEqual(true); + + expect(viewport.viewBox).toEqual(pdfPage.view); + expect(viewport.userUnit).toEqual(pdfPage.userUnit); + expect(viewport.scale).toEqual(1); + expect(viewport.rotation).toEqual(0); + expect(viewport.transform).toEqual([72, 0, 0, -72, 0, 792]); + expect(viewport.width).toEqual(612); + expect(viewport.height).toEqual(792); + + await loadingTask.destroy(); + }); + it('gets viewport with "offsetX/offsetY" arguments', function () { const viewport = page.getViewport({ scale: 1, @@ -3238,10 +3287,9 @@ describe("api", function () { const filename = "bug766086.pdf"; const defaultLoadingTask = getDocument(buildGetDocumentParams(filename)); - const defaultPromise = defaultLoadingTask.promise.then(function (pdfDoc) { - return pdfDoc.getPage(1).then(function (pdfPage) { - return pdfPage.getAnnotations(); - }); + const defaultPromise = defaultLoadingTask.promise.then(async pdfDoc => { + const pdfPage = await pdfDoc.getPage(1); + return pdfPage.getAnnotations(); }); const docBaseUrlLoadingTask = getDocument( @@ -3250,10 +3298,9 @@ describe("api", function () { }) ); const docBaseUrlPromise = docBaseUrlLoadingTask.promise.then( - function (pdfDoc) { - return pdfDoc.getPage(1).then(function (pdfPage) { - return pdfPage.getAnnotations(); - }); + async pdfDoc => { + const pdfPage = await pdfDoc.getPage(1); + return pdfPage.getAnnotations(); } ); @@ -3263,10 +3310,9 @@ describe("api", function () { }) ); const invalidDocBaseUrlPromise = - invalidDocBaseUrlLoadingTask.promise.then(function (pdfDoc) { - return pdfDoc.getPage(1).then(function (pdfPage) { - return pdfPage.getAnnotations(); - }); + invalidDocBaseUrlLoadingTask.promise.then(async pdfDoc => { + const pdfPage = await pdfDoc.getPage(1); + return pdfPage.getAnnotations(); }); const [ @@ -4078,34 +4124,28 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) }) ); - // eslint-disable-next-line arrow-body-style - const result1 = loadingTask1.promise.then(pdfDoc => { - // eslint-disable-next-line arrow-body-style - return pdfDoc.getPage(1).then(pdfPage => { - return pdfPage.getOperatorList().then(opList => { - expect(opList.fnArray.length).toBeGreaterThan(100); - expect(opList.argsArray.length).toBeGreaterThan(100); - expect(opList.lastChunk).toEqual(true); - expect(opList.separateAnnots).toEqual(null); - - return loadingTask1.destroy(); - }); - }); + const result1 = loadingTask1.promise.then(async pdfDoc => { + const pdfPage = await pdfDoc.getPage(1); + const opList = await pdfPage.getOperatorList(); + + expect(opList.fnArray.length).toBeGreaterThan(100); + expect(opList.argsArray.length).toBeGreaterThan(100); + expect(opList.lastChunk).toEqual(true); + expect(opList.separateAnnots).toEqual(null); + + await loadingTask1.destroy(); }); - // eslint-disable-next-line arrow-body-style - const result2 = loadingTask2.promise.then(pdfDoc => { - // eslint-disable-next-line arrow-body-style - return pdfDoc.getPage(1).then(pdfPage => { - return pdfPage.getOperatorList().then(opList => { - expect(opList.fnArray.length).toEqual(0); - expect(opList.argsArray.length).toEqual(0); - expect(opList.lastChunk).toEqual(true); - expect(opList.separateAnnots).toEqual(null); - - return loadingTask2.destroy(); - }); - }); + const result2 = loadingTask2.promise.then(async pdfDoc => { + const pdfPage = await pdfDoc.getPage(1); + const opList = await pdfPage.getOperatorList(); + + expect(opList.fnArray.length).toEqual(0); + expect(opList.argsArray.length).toEqual(0); + expect(opList.lastChunk).toEqual(true); + expect(opList.separateAnnots).toEqual(null); + + await loadingTask2.destroy(); }); await Promise.all([result1, result2]); @@ -4270,7 +4310,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = pdfPage.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDoc; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4297,7 +4338,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) expect(statEntryThree.name).toEqual("Overall"); expect(statEntryThree.end - statEntryThree.start).toBeGreaterThan(0); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); await loadingTask.destroy(); }); @@ -4305,7 +4346,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = page.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDocument; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4328,14 +4370,15 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) expect(reason.extraDelay).toEqual(0); } - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); }); it("re-render page, using the same canvas, after cancelling rendering", async function () { const viewport = page.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDocument; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4365,7 +4408,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) await reRenderTask.promise; expect(reRenderTask.separateAnnots).toEqual(false); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); }); it("multiple render() on the same canvas", async function () { @@ -4375,7 +4418,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = page.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDocument; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4406,6 +4450,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) } ), ]); + + canvasFactory.destroy(canvasAndCtx); }); it("cleans up document resources after rendering of page", async function () { @@ -4416,7 +4462,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = pdfPage.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDoc; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4432,7 +4479,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) await pdfDoc.cleanup(); expect(true).toEqual(true); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); await loadingTask.destroy(); }); @@ -4444,7 +4491,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = pdfPage.getViewport({ scale: 1 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdfDoc; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4479,7 +4527,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const { data } = canvasAndCtx.context.getImageData(0, 0, 1, 1); expect(data).toEqual(new Uint8ClampedArray([255, 0, 0, 255])); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); await loadingTask.destroy(); }); @@ -4495,6 +4543,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) }) ); const pdfDoc = await loadingTask.promise; + const { canvasFactory } = pdfDoc; let checkedCopyLocalImage = false, firstImgData = null, firstStatsOverall = null; @@ -4503,7 +4552,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const pdfPage = await pdfDoc.getPage(i); const viewport = pdfPage.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create( + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4516,7 +4565,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const opList = renderTask.getOperatorList(); // The canvas is no longer necessary, since we only care about // the image-data below. - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); const [statsOverall] = pdfPage.stats.times .filter(time => time.name === "Overall") @@ -4599,6 +4648,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) }) ); const pdfDoc = await loadingTask.promise; + const { canvasFactory } = pdfDoc; let checkedCopyLocalImage = false, firstStatsOverall = null; @@ -4606,7 +4656,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const pdfPage = await pdfDoc.getPage(i); const viewport = pdfPage.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create( + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4618,7 +4668,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) await renderTask.promise; // The canvas is no longer necessary, since we only care about // the stats below. - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); const [statsOverall] = pdfPage.stats.times .filter(time => time.name === "Overall") @@ -4646,13 +4696,14 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const loadingTask = getDocument(buildGetDocumentParams("issue18042.pdf")); const pdfDoc = await loadingTask.promise; + const { canvasFactory } = pdfDoc; let checkedGlobalDecodeFailed = false; for (let i = 1; i <= pdfDoc.numPages; i++) { const pdfPage = await pdfDoc.getPage(i); const viewport = pdfPage.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create( + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4665,7 +4716,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const opList = renderTask.getOperatorList(); // The canvas is no longer necessary, since we only care about // the image-data below. - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); const { commonObjs, objs } = pdfPage; const imgIndex = opList.fnArray.indexOf(OPS.paintImageXObject); @@ -4702,7 +4753,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) it("render for printing, with `printAnnotationStorage` set", async function () { async function getPrintData(printAnnotationStorage = null) { - const canvasAndCtx = CanvasFactory.create( + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4718,7 +4769,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) expect(renderTask.separateAnnots).toEqual(false); const printData = canvasAndCtx.canvas.toDataURL(); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); return printData; } @@ -4727,6 +4778,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) buildGetDocumentParams("annotation-tx.pdf") ); const pdfDoc = await loadingTask.promise; + const { canvasFactory } = pdfDoc; const pdfPage = await pdfDoc.getPage(1); const viewport = pdfPage.getViewport({ scale: 1 }); @@ -4787,7 +4839,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) const viewport = page.getViewport({ scale: 1.2 }); expect(viewport instanceof PageViewport).toEqual(true); - const canvasAndCtx = CanvasFactory.create( + const { canvasFactory } = pdf; + const canvasAndCtx = canvasFactory.create( viewport.width, viewport.height ); @@ -4799,7 +4852,7 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) expect(renderTask.separateAnnots).toEqual(false); const data = canvasAndCtx.canvas.toDataURL(); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); return data; } @@ -4807,9 +4860,9 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`) // Issue 6205 reported an issue with font rendering, so clear the loaded // fonts so that we can see whether loading PDFs in parallel does not // cause any issues with the rendered fonts. - const destroyPromises = loadingTasks.map(function (loadingTask) { - return loadingTask.destroy(); - }); + const destroyPromises = loadingTasks.map(loadingTask => + loadingTask.destroy() + ); await Promise.all(destroyPromises); }); diff --git a/test/unit/autolinker_spec.js b/test/unit/autolinker_spec.js new file mode 100644 index 0000000000000..2d2e4d6731cf1 --- /dev/null +++ b/test/unit/autolinker_spec.js @@ -0,0 +1,198 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Autolinker } from "../../web/autolinker.js"; + +function testLinks(links) { + const matches = Autolinker.findLinks(links.map(link => link[0]).join("\n")); + expect(matches.length).toEqual(links.length); + for (let i = 0; i < links.length; i++) { + expect(matches[i].url).toEqual(links[i][1]); + } +} + +describe("autolinker", function () { + it("should correctly find URLs", function () { + const [matched] = Autolinker.findLinks("http://www.example.com"); + expect(matched.url).toEqual("http://www.example.com/"); + }); + + it("should correctly find simple valid URLs", function () { + testLinks([ + [ + "http://subdomain.example.com/path/to/page?query=param", + "http://subdomain.example.com/path/to/page?query=param", + ], + [ + "www.example.com/path/to/resource", + "http://www.example.com/path/to/resource", + ], + [ + "http://example.com/path?query=value#fragment", + "http://example.com/path?query=value#fragment", + ], + ]); + }); + + it("should correctly find emails", function () { + testLinks([ + ["mailto:username@example.com", "mailto:username@example.com"], + [ + "mailto:someone@subdomain.example.com", + "mailto:someone@subdomain.example.com", + ], + ["peter@abc.de", "mailto:peter@abc.de"], + ["red.teddy.b@abc.com", "mailto:red.teddy.b@abc.com"], + [ + "abc_@gmail.com", // '_' is ok before '@'. + "mailto:abc_@gmail.com", + ], + [ + "dummy-hi@gmail.com", // '-' is ok in user name. + "mailto:dummy-hi@gmail.com", + ], + [ + "a..df@gmail.com", // Stop at consecutive '.'. + "mailto:a..df@gmail.com", + ], + [ + ".john@yahoo.com", // Remove heading '.'. + "mailto:john@yahoo.com", + ], + [ + "abc@xyz.org?/", // Trim ending invalid chars. + "mailto:abc@xyz.org", + ], + [ + "fan{abc@xyz.org", // Trim beginning invalid chars. + "mailto:abc@xyz.org", + ], + [ + "fan@g.com..", // Trim the ending periods. + "mailto:fan@g.com", + ], + [ + "CAP.cap@Gmail.Com", // Keep the original case. + "mailto:CAP.cap@Gmail.Com", + ], + ["partl@mail.boku.ac.at", "mailto:partl@mail.boku.ac.at"], + ["Irene.Hyna@bmwf.ac.at", "mailto:Irene.Hyna@bmwf.ac.at"], + ["", "mailto:hi@foo.bar.baz"], + ]); + }); + + it("should correctly handle complex or edge cases", function () { + testLinks([ + [ + "https://example.com/path/to/page?query=param&another=val#section", + "https://example.com/path/to/page?query=param&another=val#section", + ], + [ + "www.example.com/resource/(parentheses)-allowed/", + "http://www.example.com/resource/(parentheses)-allowed/", + ], + [ + "http://example.com/path_with_underscores", + "http://example.com/path_with_underscores", + ], + [ + "http://www.example.com:8080/port/test", + "http://www.example.com:8080/port/test", + ], + [ + "https://example.com/encoded%20spaces%20in%20path", + "https://example.com/encoded%20spaces%20in%20path", + ], + ["mailto:hello+world@example.com", "mailto:hello+world@example.com"], + ["www.a.com/#a=@?q=rr&r=y", "http://www.a.com/#a=@?q=rr&r=y"], + ["http://a.com/1/2/3/4\\5\\6", "http://a.com/1/2/3/4/5/6"], + ["http://www.example.com/foo;bar", "http://www.example.com/foo;bar"], + // ["www.abc.com/#%%^&&*(", "http://www.abc.com/#%%^&&*("], TODO: Patch the regex to accept the whole URL. + ]); + }); + + it("shouldn't find false positives", function () { + const matches = Autolinker.findLinks( + [ + "not a valid URL", + "htp://misspelled-protocol.com", + "example.com (missing protocol)", + "https://[::1] (IPv6 loopback)", + "http:// (just protocol)", + "", // Blank. + "http", // No colon. + "www.", // Missing domain. + "https-and-www", // Dash not colon. + "http:/abc.com", // Missing slash. + "http://((()),", // Only invalid chars in host name. + "ftp://example.com", // Ftp scheme is not supported. + "http:example.com", // Missing slashes. + "http//[example.com", // Invalid IPv6 address. + "http//[00:00:00:00:00:00", // Invalid IPv6 address. + "http//[]", // Empty IPv6 address. + "abc.example.com", // URL without scheme. + "JD?M$0QP)lKn06l1apKDC@\\qJ4B!!(5m+j.7F790m", // Not a valid email. + ].join("\n") + ); + expect(matches.length).toEqual(0); + }); + + it("should correctly find links among mixed content", function () { + const matches = Autolinker.findLinks( + [ + "Here's a URL: https://example.com and an email: mailto:test@example.com", + "www.example.com and more text", + "Check this: http://example.com/path?query=1 and this mailto:info@domain.com", + ].join("\n") + ); + expect(matches.length).toEqual(5); + expect(matches[0].url).toEqual("https://example.com/"); + expect(matches[1].url).toEqual("mailto:test@example.com"); + expect(matches[2].url).toEqual("http://www.example.com/"); + expect(matches[3].url).toEqual("http://example.com/path?query=1"); + expect(matches[4].url).toEqual("mailto:info@domain.com"); + }); + + it("should correctly work with special characters", function () { + testLinks([ + [ + "https://example.com/path/to/page?query=value&symbol=£", + "https://example.com/path/to/page?query=value&symbol=%C2%A3", + ], + [ + "mailto:user.name+alias@example-domain.com", + "mailto:user.name+alias@example-domain.com", + ], + ["http://example.com/@user", "http://example.com/@user"], + ["https://example.com/path#@anchor", "https://example.com/path#@anchor"], + ["www.测试.net", "http://www.xn--0zwm56d.net/"], + ["www.测试.net;", "http://www.xn--0zwm56d.net/"], + // [ "www.测试。net。", "http://www.xn--0zwm56d.net/" ] TODO: Patch `createValidAbsoluteUrl` to accept this. + ]); + }); + + it("should correctly find links with dashes and newlines between numbers", function () { + const matches = Autolinker.findLinks("http://abcd.efg/test1-\n2/test.html"); + expect(matches.length).toEqual(1); + expect(matches[0].url).toEqual("http://abcd.efg/test1-2/test.html"); + }); + + it("should correctly identify emails with special prefixes", function () { + testLinks([ + ["wwwtest@email.com", "mailto:wwwtest@email.com"], + ["httptest@email.com", "mailto:httptest@email.com"], + ]); + }); +}); diff --git a/test/unit/canvas_factory_spec.js b/test/unit/canvas_factory_spec.js new file mode 100644 index 0000000000000..23a1dba610680 --- /dev/null +++ b/test/unit/canvas_factory_spec.js @@ -0,0 +1,111 @@ +/* Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DOMCanvasFactory } from "../../src/display/canvas_factory.js"; +import { isNodeJS } from "../../src/shared/util.js"; + +describe("canvas_factory", function () { + describe("DOMCanvasFactory", function () { + let canvasFactory; + + beforeAll(function () { + canvasFactory = new DOMCanvasFactory({}); + }); + + afterAll(function () { + canvasFactory = null; + }); + + it("`create` should throw an error if the dimensions are invalid", function () { + // Invalid width. + expect(function () { + return canvasFactory.create(-1, 1); + }).toThrow(new Error("Invalid canvas size")); + + // Invalid height. + expect(function () { + return canvasFactory.create(1, -1); + }).toThrow(new Error("Invalid canvas size")); + }); + + it("`create` should return a canvas if the dimensions are valid", function () { + if (isNodeJS) { + pending("Document is not supported in Node.js."); + } + + const { canvas, context } = canvasFactory.create(20, 40); + expect(canvas instanceof HTMLCanvasElement).toBe(true); + expect(context instanceof CanvasRenderingContext2D).toBe(true); + expect(canvas.width).toBe(20); + expect(canvas.height).toBe(40); + }); + + it("`reset` should throw an error if no canvas is provided", function () { + const canvasAndContext = { canvas: null, context: null }; + + expect(function () { + return canvasFactory.reset(canvasAndContext, 20, 40); + }).toThrow(new Error("Canvas is not specified")); + }); + + it("`reset` should throw an error if the dimensions are invalid", function () { + const canvasAndContext = { canvas: "foo", context: "bar" }; + + // Invalid width. + expect(function () { + return canvasFactory.reset(canvasAndContext, -1, 1); + }).toThrow(new Error("Invalid canvas size")); + + // Invalid height. + expect(function () { + return canvasFactory.reset(canvasAndContext, 1, -1); + }).toThrow(new Error("Invalid canvas size")); + }); + + it("`reset` should alter the canvas/context if the dimensions are valid", function () { + if (isNodeJS) { + pending("Document is not supported in Node.js."); + } + + const canvasAndContext = canvasFactory.create(20, 40); + canvasFactory.reset(canvasAndContext, 60, 80); + + const { canvas, context } = canvasAndContext; + expect(canvas instanceof HTMLCanvasElement).toBe(true); + expect(context instanceof CanvasRenderingContext2D).toBe(true); + expect(canvas.width).toBe(60); + expect(canvas.height).toBe(80); + }); + + it("`destroy` should throw an error if no canvas is provided", function () { + expect(function () { + return canvasFactory.destroy({}); + }).toThrow(new Error("Canvas is not specified")); + }); + + it("`destroy` should clear the canvas/context", function () { + if (isNodeJS) { + pending("Document is not supported in Node.js."); + } + + const canvasAndContext = canvasFactory.create(20, 40); + canvasFactory.destroy(canvasAndContext); + + const { canvas, context } = canvasAndContext; + expect(canvas).toBe(null); + expect(context).toBe(null); + }); + }); +}); diff --git a/test/unit/clitests.json b/test/unit/clitests.json index 0516f6def31a6..1328b612461bc 100644 --- a/test/unit/clitests.json +++ b/test/unit/clitests.json @@ -8,7 +8,9 @@ "annotation_storage_spec.js", "api_spec.js", "app_options_spec.js", + "autolinker_spec.js", "bidi_spec.js", + "canvas_factory_spec.js", "cff_parser_spec.js", "cmap_spec.js", "colorspace_spec.js", @@ -42,6 +44,7 @@ "primitives_spec.js", "stream_spec.js", "struct_tree_spec.js", + "svg_factory_spec.js", "text_layer_spec.js", "type1_parser_spec.js", "ui_utils_spec.js", diff --git a/test/unit/clitests_helper.js b/test/unit/clitests_helper.js index a5a1c788e2de0..d86eeacbbc2d3 100644 --- a/test/unit/clitests_helper.js +++ b/test/unit/clitests_helper.js @@ -18,7 +18,6 @@ import { setVerbosityLevel, VerbosityLevel, } from "../../src/shared/util.js"; -import { NodePackages } from "../../src/display/node_utils.js"; // Sets longer timeout, similar to `jasmine-boot.js`. jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; @@ -30,9 +29,6 @@ if (!isNodeJS) { ); } -// Ensure that all Node.js packages/polyfills have loaded. -await NodePackages.promise; - // Reduce the amount of console "spam", by ignoring `info`/`warn` calls, // when running the unit-tests in Node.js/Travis. setVerbosityLevel(VerbosityLevel.ERRORS); diff --git a/test/unit/cmap_spec.js b/test/unit/cmap_spec.js index 109856825b2f0..be065530f6205 100644 --- a/test/unit/cmap_spec.js +++ b/test/unit/cmap_spec.js @@ -14,8 +14,7 @@ */ import { CMap, CMapFactory, IdentityCMap } from "../../src/core/cmap.js"; -import { CMAP_URL } from "./test_utils.js"; -import { DefaultCMapReaderFactory } from "../../src/display/api.js"; +import { CMAP_URL, DefaultCMapReaderFactory } from "./test_utils.js"; import { Name } from "../../src/core/primitives.js"; import { StringStream } from "../../src/core/stream.js"; diff --git a/test/unit/colorspace_spec.js b/test/unit/colorspace_spec.js index ca1a742a48b8e..f9da88ccbacbc 100644 --- a/test/unit/colorspace_spec.js +++ b/test/unit/colorspace_spec.js @@ -14,9 +14,12 @@ */ import { Dict, Name, Ref } from "../../src/core/primitives.js"; +import { + GlobalColorSpaceCache, + LocalColorSpaceCache, +} from "../../src/core/image_utils.js"; import { Stream, StringStream } from "../../src/core/stream.js"; import { ColorSpace } from "../../src/core/colorspace.js"; -import { LocalColorSpaceCache } from "../../src/core/image_utils.js"; import { PDFFunctionFactory } from "../../src/core/function.js"; import { XRefMock } from "./test_utils.js"; @@ -50,13 +53,15 @@ describe("colorspace", function () { }); describe("ColorSpace caching", function () { - let localColorSpaceCache = null; + let globalColorSpaceCache, localColorSpaceCache; beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); localColorSpaceCache = new LocalColorSpaceCache(); }); afterAll(function () { + globalColorSpaceCache = null; localColorSpaceCache = null; }); @@ -71,6 +76,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace1.name).toEqual("Pattern"); @@ -80,6 +86,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace2.name).toEqual("Pattern"); @@ -89,6 +96,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache: new GlobalColorSpaceCache(), localColorSpaceCache: new LocalColorSpaceCache(), }); expect(colorSpaceNonCached.name).toEqual("Pattern"); @@ -98,6 +106,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpaceOther.name).toEqual("DeviceRGB"); @@ -140,6 +149,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace1.name).toEqual("CalGray"); @@ -149,6 +159,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace2.name).toEqual("CalGray"); @@ -158,6 +169,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache: new GlobalColorSpaceCache(), localColorSpaceCache: new LocalColorSpaceCache(), }); expect(colorSpaceNonCached.name).toEqual("CalGray"); @@ -167,6 +179,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpaceOther.name).toEqual("CalRGB"); @@ -180,6 +193,16 @@ describe("colorspace", function () { }); describe("DeviceGrayCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceGray"); const xref = new XRefMock([ @@ -198,6 +221,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -249,6 +273,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -278,6 +303,16 @@ describe("colorspace", function () { }); describe("DeviceRgbCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceRGB"); const xref = new XRefMock([ @@ -296,6 +331,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -353,6 +389,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -388,6 +425,16 @@ describe("colorspace", function () { }); describe("DeviceCmykCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceCMYK"); const xref = new XRefMock([ @@ -406,6 +453,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -463,6 +511,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -498,6 +547,16 @@ describe("colorspace", function () { }); describe("CalGrayCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -521,6 +580,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -557,6 +617,16 @@ describe("colorspace", function () { }); describe("CalRGBCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -581,6 +651,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -616,6 +687,16 @@ describe("colorspace", function () { }); describe("LabCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -639,6 +720,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -675,6 +757,16 @@ describe("colorspace", function () { }); describe("IndexedCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { // prettier-ignore const lookup = new Stream( @@ -701,6 +793,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -730,6 +823,16 @@ describe("colorspace", function () { }); describe("AlternateCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const fnDict = new Dict(); fnDict.set("FunctionType", 4); @@ -769,6 +872,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); diff --git a/test/unit/common_pdfstream_tests.js b/test/unit/common_pdfstream_tests.js new file mode 100644 index 0000000000000..b96620df9f5c2 --- /dev/null +++ b/test/unit/common_pdfstream_tests.js @@ -0,0 +1,90 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbortException, isNodeJS } from "../../src/shared/util.js"; +import { getCrossOriginHostname, TestPdfsServer } from "./test_utils.js"; + +// Common tests to verify behavior across implementations of the IPDFStream +// interface: +// - PDFNetworkStream by network_spec.js +// - PDFFetchStream by fetch_stream_spec.js +async function testCrossOriginRedirects({ + PDFStreamClass, + redirectIfRange, + testRangeReader, +}) { + const basicApiUrl = TestPdfsServer.resolveURL("basicapi.pdf").href; + const basicApiFileLength = 105779; + + const rangeSize = 32768; + const stream = new PDFStreamClass({ + url: getCrossOriginUrlWithRedirects(basicApiUrl, redirectIfRange), + length: basicApiFileLength, + rangeChunkSize: rangeSize, + disableStream: true, + disableRange: false, + }); + + const fullReader = stream.getFullReader(); + + await fullReader.headersReady; + // Sanity check: We can only test range requests if supported: + expect(fullReader.isRangeSupported).toEqual(true); + // ^ When range requests are supported (and streaming is disabled), the full + // initial request is aborted and we do not need to call fullReader.cancel(). + + const rangeReader = stream.getRangeReader( + basicApiFileLength - rangeSize, + basicApiFileLength + ); + + try { + await testRangeReader(rangeReader); + } finally { + rangeReader.cancel(new AbortException("Don't need rangeReader")); + } +} + +/** + * @param {string} testserverUrl - A URL handled that supports CORS and + * redirects (see crossOriginHandler and redirectHandler in webserver.mjs). + * @param {boolean} redirectIfRange - Whether Range requests should be + * redirected to a different origin compared to the initial request. + * @returns {string} A URL that will be redirected by the server. + */ +function getCrossOriginUrlWithRedirects(testserverUrl, redirectIfRange) { + const url = new URL(testserverUrl); + if (!isNodeJS) { + // The responses are going to be cross-origin. In Node.js, fetch() allows + // cross-origin requests for any request, but in browser environments we + // need to enable CORS. + // This option depends on crossOriginHandler in webserver.mjs. + url.searchParams.set("cors", "withoutCredentials"); + } + + // This redirect options depend on redirectHandler in webserver.mjs. + + // We will change the host to a cross-origin domain so that the initial + // request will be cross-origin. Set "redirectToHost" to the original host + // to force a cross-origin redirect (relative to the initial URL). + url.searchParams.set("redirectToHost", url.hostname); + url.hostname = getCrossOriginHostname(url.hostname); + if (redirectIfRange) { + url.searchParams.set("redirectIfRange", "1"); + } + return url.href; +} + +export { testCrossOriginRedirects }; diff --git a/test/unit/custom_spec.js b/test/unit/custom_spec.js index 052c60d641c41..2888fb020f0ed 100644 --- a/test/unit/custom_spec.js +++ b/test/unit/custom_spec.js @@ -13,8 +13,8 @@ * limitations under the License. */ -import { DefaultCanvasFactory, getDocument } from "../../src/display/api.js"; import { buildGetDocumentParams } from "./test_utils.js"; +import { getDocument } from "../../src/display/api.js"; function getTopLeftPixel(canvasContext) { const imgData = canvasContext.getImageData(0, 0, 1, 1); @@ -30,28 +30,24 @@ describe("custom canvas rendering", function () { const transparentGetDocumentParams = buildGetDocumentParams("transparent.pdf"); - let CanvasFactory; - let loadingTask; - let page; + let loadingTask, doc, page; beforeAll(async function () { - CanvasFactory = new DefaultCanvasFactory({}); - loadingTask = getDocument(transparentGetDocumentParams); - const doc = await loadingTask.promise; - const data = await doc.getPage(1); - page = data; + doc = await loadingTask.promise; + page = await doc.getPage(1); }); afterAll(async function () { - CanvasFactory = null; + doc = null; page = null; await loadingTask.destroy(); }); it("renders to canvas with a default white background", async function () { const viewport = page.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + const { canvasFactory } = doc; + const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height); const renderTask = page.render({ canvasContext: canvasAndCtx.context, @@ -65,12 +61,13 @@ describe("custom canvas rendering", function () { b: 255, a: 255, }); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); }); it("renders to canvas with a custom background", async function () { const viewport = page.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + const { canvasFactory } = doc; + const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height); const renderTask = page.render({ canvasContext: canvasAndCtx.context, @@ -85,7 +82,7 @@ describe("custom canvas rendering", function () { b: 0, a: 255, }); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); }); }); @@ -137,17 +134,15 @@ describe("custom ownerDocument", function () { getElementsByTagName: () => [{ append: () => {} }], }, }; - const CanvasFactory = new DefaultCanvasFactory({ ownerDocument }); return { elements, ownerDocument, - CanvasFactory, }; } it("should use given document for loading fonts (with Font Loading API)", async function () { - const { ownerDocument, elements, CanvasFactory } = getMocks(); + const { ownerDocument, elements } = getMocks(); const getDocumentParams = buildGetDocumentParams( "TrueType_without_cmap.pdf", { @@ -161,7 +156,8 @@ describe("custom ownerDocument", function () { const page = await doc.getPage(1); const viewport = page.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + const { canvasFactory } = doc; + const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height); await page.render({ canvasContext: canvasAndCtx.context, @@ -174,12 +170,12 @@ describe("custom ownerDocument", function () { expect(Array.from(ownerDocument.fonts).find(checkFont)).toBeTruthy(); await loadingTask.destroy(); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); expect(ownerDocument.fonts.size).toBe(0); }); it("should use given document for loading fonts (with CSS rules)", async function () { - const { ownerDocument, elements, CanvasFactory } = getMocks(); + const { ownerDocument, elements } = getMocks(); ownerDocument.fonts = null; const getDocumentParams = buildGetDocumentParams( "TrueType_without_cmap.pdf", @@ -194,7 +190,8 @@ describe("custom ownerDocument", function () { const page = await doc.getPage(1); const viewport = page.getViewport({ scale: 1 }); - const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + const { canvasFactory } = doc; + const canvasAndCtx = canvasFactory.create(viewport.width, viewport.height); await page.render({ canvasContext: canvasAndCtx.context, @@ -206,7 +203,7 @@ describe("custom ownerDocument", function () { expect(style.sheet.cssRules.find(checkFontFaceRule)).toBeTruthy(); await loadingTask.destroy(); - CanvasFactory.destroy(canvasAndCtx); + canvasFactory.destroy(canvasAndCtx); expect(style.remove.called).toBe(true); }); }); diff --git a/test/unit/default_appearance_spec.js b/test/unit/default_appearance_spec.js index 94a844921c1f4..5599cedacf033 100644 --- a/test/unit/default_appearance_spec.js +++ b/test/unit/default_appearance_spec.js @@ -20,6 +20,7 @@ import { } from "../../src/core/default_appearance.js"; import { Dict, Name } from "../../src/core/primitives.js"; import { NullStream, StringStream } from "../../src/core/stream.js"; +import { GlobalColorSpaceCache } from "../../src/core/image_utils.js"; import { XRefMock } from "./test_utils.js"; describe("Default appearance", function () { @@ -56,7 +57,7 @@ describe("Default appearance", function () { }); describe("parseAppearanceStream", () => { - let evaluatorOptions, xref; + let evaluatorOptions, xref, globalColorSpaceCache; beforeAll(function () { evaluatorOptions = { @@ -64,11 +65,13 @@ describe("Default appearance", function () { isOffscreenCanvasSupported: false, }; xref = new XRefMock(); + globalColorSpaceCache = new GlobalColorSpaceCache(); }); afterAll(function () { evaluatorOptions = null; xref = null; + globalColorSpaceCache = null; }); it("should parse a FreeText (from Acrobat) appearance", () => { @@ -101,9 +104,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([107, 217, 41]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -122,9 +130,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([237, 43, 112]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -159,9 +172,14 @@ describe("Default appearance", function () { fontName: "TT1", fontColor: new Uint8ClampedArray([135, 78, 254]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -182,9 +200,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([16, 124, 16]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -208,9 +231,14 @@ describe("Default appearance", function () { fontName: "FXF0", fontColor: new Uint8ClampedArray([149, 63, 60]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -232,9 +260,14 @@ describe("Default appearance", function () { fontName: "Invalid_font", fontColor: new Uint8ClampedArray([0, 85, 127]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); }); diff --git a/test/unit/display_utils_spec.js b/test/unit/display_utils_spec.js index e152d5d9c5677..eef734a836966 100644 --- a/test/unit/display_utils_spec.js +++ b/test/unit/display_utils_spec.js @@ -13,162 +13,15 @@ * limitations under the License. */ -import { bytesToString, isNodeJS } from "../../src/shared/util.js"; import { - DOMCanvasFactory, - DOMSVGFactory, getFilenameFromUrl, getPdfFilenameFromUrl, isValidFetchUrl, PDFDateString, } from "../../src/display/display_utils.js"; +import { toBase64Util } from "../../src/shared/util.js"; describe("display_utils", function () { - describe("DOMCanvasFactory", function () { - let canvasFactory; - - beforeAll(function () { - canvasFactory = new DOMCanvasFactory({}); - }); - - afterAll(function () { - canvasFactory = null; - }); - - it("`create` should throw an error if the dimensions are invalid", function () { - // Invalid width. - expect(function () { - return canvasFactory.create(-1, 1); - }).toThrow(new Error("Invalid canvas size")); - - // Invalid height. - expect(function () { - return canvasFactory.create(1, -1); - }).toThrow(new Error("Invalid canvas size")); - }); - - it("`create` should return a canvas if the dimensions are valid", function () { - if (isNodeJS) { - pending("Document is not supported in Node.js."); - } - - const { canvas, context } = canvasFactory.create(20, 40); - expect(canvas instanceof HTMLCanvasElement).toBe(true); - expect(context instanceof CanvasRenderingContext2D).toBe(true); - expect(canvas.width).toBe(20); - expect(canvas.height).toBe(40); - }); - - it("`reset` should throw an error if no canvas is provided", function () { - const canvasAndContext = { canvas: null, context: null }; - - expect(function () { - return canvasFactory.reset(canvasAndContext, 20, 40); - }).toThrow(new Error("Canvas is not specified")); - }); - - it("`reset` should throw an error if the dimensions are invalid", function () { - const canvasAndContext = { canvas: "foo", context: "bar" }; - - // Invalid width. - expect(function () { - return canvasFactory.reset(canvasAndContext, -1, 1); - }).toThrow(new Error("Invalid canvas size")); - - // Invalid height. - expect(function () { - return canvasFactory.reset(canvasAndContext, 1, -1); - }).toThrow(new Error("Invalid canvas size")); - }); - - it("`reset` should alter the canvas/context if the dimensions are valid", function () { - if (isNodeJS) { - pending("Document is not supported in Node.js."); - } - - const canvasAndContext = canvasFactory.create(20, 40); - canvasFactory.reset(canvasAndContext, 60, 80); - - const { canvas, context } = canvasAndContext; - expect(canvas instanceof HTMLCanvasElement).toBe(true); - expect(context instanceof CanvasRenderingContext2D).toBe(true); - expect(canvas.width).toBe(60); - expect(canvas.height).toBe(80); - }); - - it("`destroy` should throw an error if no canvas is provided", function () { - expect(function () { - return canvasFactory.destroy({}); - }).toThrow(new Error("Canvas is not specified")); - }); - - it("`destroy` should clear the canvas/context", function () { - if (isNodeJS) { - pending("Document is not supported in Node.js."); - } - - const canvasAndContext = canvasFactory.create(20, 40); - canvasFactory.destroy(canvasAndContext); - - const { canvas, context } = canvasAndContext; - expect(canvas).toBe(null); - expect(context).toBe(null); - }); - }); - - describe("DOMSVGFactory", function () { - let svgFactory; - - beforeAll(function () { - svgFactory = new DOMSVGFactory(); - }); - - afterAll(function () { - svgFactory = null; - }); - - it("`create` should throw an error if the dimensions are invalid", function () { - // Invalid width. - expect(function () { - return svgFactory.create(-1, 0); - }).toThrow(new Error("Invalid SVG dimensions")); - - // Invalid height. - expect(function () { - return svgFactory.create(0, -1); - }).toThrow(new Error("Invalid SVG dimensions")); - }); - - it("`create` should return an SVG element if the dimensions are valid", function () { - if (isNodeJS) { - pending("Document is not supported in Node.js."); - } - - const svg = svgFactory.create(20, 40); - expect(svg instanceof SVGSVGElement).toBe(true); - expect(svg.getAttribute("version")).toBe("1.1"); - expect(svg.getAttribute("width")).toBe("20px"); - expect(svg.getAttribute("height")).toBe("40px"); - expect(svg.getAttribute("preserveAspectRatio")).toBe("none"); - expect(svg.getAttribute("viewBox")).toBe("0 0 20 40"); - }); - - it("`createElement` should throw an error if the type is not a string", function () { - expect(function () { - return svgFactory.createElement(true); - }).toThrow(new Error("Invalid SVG element type")); - }); - - it("`createElement` should return an SVG element if the type is valid", function () { - if (isNodeJS) { - pending("Document is not supported in Node.js."); - } - - const svg = svgFactory.createElement("svg:rect"); - expect(svg instanceof SVGRectElement).toBe(true); - }); - }); - describe("getFilenameFromUrl", function () { it("should get the filename from an absolute URL", function () { const url = "https://server.org/filename.pdf"; @@ -315,9 +168,6 @@ describe("display_utils", function () { }); it('gets PDF filename from query string appended to "blob:" URL', function () { - if (isNodeJS) { - pending("Blob in not supported in Node.js."); - } const typedArray = new Uint8Array([1, 2, 3, 4, 5]); const blobUrl = URL.createObjectURL( new Blob([typedArray], { type: "application/pdf" }) @@ -329,9 +179,8 @@ describe("display_utils", function () { }); it('gets fallback filename from query string appended to "data:" URL', function () { - const typedArray = new Uint8Array([1, 2, 3, 4, 5]), - str = bytesToString(typedArray); - const dataUrl = `data:application/pdf;base64,${btoa(str)}`; + const typedArray = new Uint8Array([1, 2, 3, 4, 5]); + const dataUrl = `data:application/pdf;base64,${toBase64Util(typedArray)}`; // Sanity check to ensure that a "data:" URL was returned. expect(dataUrl.startsWith("data:")).toEqual(true); diff --git a/test/unit/editor_spec.js b/test/unit/editor_spec.js index 7adda95881c90..aa8aa458a6b1d 100644 --- a/test/unit/editor_spec.js +++ b/test/unit/editor_spec.js @@ -14,6 +14,7 @@ */ import { CommandManager } from "../../src/display/editor/tools.js"; +import { SignatureExtractor } from "../../src/display/editor/drawers/signaturedraw.js"; describe("editor", function () { describe("Command Manager", function () { @@ -90,4 +91,51 @@ describe("editor", function () { manager.add({ ...makeDoUndo(5), mustExec: true }); expect(x).toEqual(11); }); + + it("should check signature compression/decompression", async () => { + let gen = n => new Float32Array(crypto.getRandomValues(new Uint16Array(n))); + let outlines = [102, 28, 254, 4536, 10, 14532, 512].map(gen); + const signature = { + outlines, + areContours: false, + thickness: 1, + width: 123, + height: 456, + }; + let compressed = await SignatureExtractor.compressSignature(signature); + let decompressed = await SignatureExtractor.decompressSignature(compressed); + expect(decompressed).toEqual(signature); + + signature.thickness = 2; + compressed = await SignatureExtractor.compressSignature(signature); + decompressed = await SignatureExtractor.decompressSignature(compressed); + expect(decompressed).toEqual(signature); + + signature.areContours = true; + compressed = await SignatureExtractor.compressSignature(signature); + decompressed = await SignatureExtractor.decompressSignature(compressed); + expect(decompressed).toEqual(signature); + + // Numbers are small enough to be compressed with Uint8Array. + gen = n => + new Float32Array( + crypto.getRandomValues(new Uint8Array(n)).map(x => x / 10) + ); + outlines = [100, 200, 300, 10, 80].map(gen); + signature.outlines = outlines; + compressed = await SignatureExtractor.compressSignature(signature); + decompressed = await SignatureExtractor.decompressSignature(compressed); + expect(decompressed).toEqual(signature); + + // Numbers are large enough to be compressed with Uint16Array. + gen = n => + new Float32Array( + crypto.getRandomValues(new Uint16Array(n)).map(x => x / 10) + ); + outlines = [100, 200, 300, 10, 80].map(gen); + signature.outlines = outlines; + compressed = await SignatureExtractor.compressSignature(signature); + decompressed = await SignatureExtractor.decompressSignature(compressed); + expect(decompressed).toEqual(signature); + }); }); diff --git a/test/unit/fetch_stream_spec.js b/test/unit/fetch_stream_spec.js index 75bda5d2caf4f..181818a594a77 100644 --- a/test/unit/fetch_stream_spec.js +++ b/test/unit/fetch_stream_spec.js @@ -13,35 +13,23 @@ * limitations under the License. */ -import { AbortException, isNodeJS } from "../../src/shared/util.js"; -import { createTemporaryNodeServer } from "./test_utils.js"; +import { AbortException } from "../../src/shared/util.js"; import { PDFFetchStream } from "../../src/display/fetch_stream.js"; +import { testCrossOriginRedirects } from "./common_pdfstream_tests.js"; +import { TestPdfsServer } from "./test_utils.js"; describe("fetch_stream", function () { - let tempServer = null; - function getPdfUrl() { - return isNodeJS - ? `http://127.0.0.1:${tempServer.port}/tracemonkey.pdf` - : new URL("../pdfs/tracemonkey.pdf", window.location).href; + return TestPdfsServer.resolveURL("tracemonkey.pdf").href; } const pdfLength = 1016315; - beforeAll(function () { - if (isNodeJS) { - tempServer = createTemporaryNodeServer(); - } + beforeAll(async function () { + await TestPdfsServer.ensureStarted(); }); - afterAll(function () { - if (isNodeJS) { - // Close the server from accepting new connections after all test - // finishes. - const { server } = tempServer; - server.close(); - - tempServer = null; - } + afterAll(async function () { + await TestPdfsServer.ensureStopped(); }); it("read with streaming", async function () { @@ -54,7 +42,7 @@ describe("fetch_stream", function () { const fullReader = stream.getFullReader(); let isStreamingSupported, isRangeSupported; - const promise = fullReader.headersReady.then(function () { + await fullReader.headersReady.then(function () { isStreamingSupported = fullReader.isStreamingSupported; isRangeSupported = fullReader.isRangeSupported; }); @@ -71,7 +59,7 @@ describe("fetch_stream", function () { }); }; - await Promise.all([read(), promise]); + await read(); expect(len).toEqual(pdfLength); expect(isStreamingSupported).toEqual(true); @@ -90,7 +78,7 @@ describe("fetch_stream", function () { const fullReader = stream.getFullReader(); let isStreamingSupported, isRangeSupported, fullReaderCancelled; - const promise = fullReader.headersReady.then(function () { + await fullReader.headersReady.then(function () { isStreamingSupported = fullReader.isStreamingSupported; isRangeSupported = fullReader.isRangeSupported; // We shall be able to close full reader without any issue. @@ -121,7 +109,6 @@ describe("fetch_stream", function () { await Promise.all([ read(rangeReader1, result1), read(rangeReader2, result2), - promise, ]); expect(isStreamingSupported).toEqual(true); @@ -130,4 +117,33 @@ describe("fetch_stream", function () { expect(result1.value).toEqual(rangeSize); expect(result2.value).toEqual(tailSize); }); + + describe("Redirects", function () { + it("redirects allowed if all responses are same-origin", async function () { + await testCrossOriginRedirects({ + PDFStreamClass: PDFFetchStream, + redirectIfRange: false, + async testRangeReader(rangeReader) { + await expectAsync(rangeReader.read()).toBeResolved(); + }, + }); + }); + + it("redirects blocked if any response is cross-origin", async function () { + await testCrossOriginRedirects({ + PDFStreamClass: PDFFetchStream, + redirectIfRange: true, + async testRangeReader(rangeReader) { + // When read (sync), error should be reported. + await expectAsync(rangeReader.read()).toBeRejectedWithError( + /^Expected range response-origin "http:.*" to match "http:.*"\.$/ + ); + // When read again (async), error should be consistent. + await expectAsync(rangeReader.read()).toBeRejectedWithError( + /^Expected range response-origin "http:.*" to match "http:.*"\.$/ + ); + }, + }); + }); + }); }); diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js index da98ceeb31377..7dd0a09869c81 100644 --- a/test/unit/jasmine-boot.js +++ b/test/unit/jasmine-boot.js @@ -42,7 +42,7 @@ import { GlobalWorkerOptions } from "pdfjs/display/worker_options.js"; import { isNodeJS } from "../../src/shared/util.js"; -import { TestReporter } from "./testreporter.js"; +import { TestReporter } from "../reporter.js"; async function initializePDFJS(callback) { await Promise.all( @@ -51,7 +51,9 @@ async function initializePDFJS(callback) { "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/app_options_spec.js", + "pdfjs-test/unit/autolinker_spec.js", "pdfjs-test/unit/bidi_spec.js", + "pdfjs-test/unit/canvas_factory_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", @@ -86,6 +88,7 @@ async function initializePDFJS(callback) { "pdfjs-test/unit/scripting_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/struct_tree_spec.js", + "pdfjs-test/unit/svg_factory_spec.js", "pdfjs-test/unit/text_layer_spec.js", "pdfjs-test/unit/type1_parser_spec.js", "pdfjs-test/unit/ui_utils_spec.js", @@ -97,10 +100,7 @@ async function initializePDFJS(callback) { "pdfjs-test/unit/xfa_serialize_data_spec.js", "pdfjs-test/unit/xfa_tohtml_spec.js", "pdfjs-test/unit/xml_spec.js", - ].map(function (moduleName) { - // eslint-disable-next-line no-unsanitized/method - return import(moduleName); - }) + ].map(moduleName => import(moduleName)) // eslint-disable-line no-unsanitized/method ); if (isNodeJS) { diff --git a/test/unit/network_spec.js b/test/unit/network_spec.js index e8b4b9f4c8e1e..f24284cd97ece 100644 --- a/test/unit/network_spec.js +++ b/test/unit/network_spec.js @@ -13,8 +13,10 @@ * limitations under the License. */ -import { AbortException } from "../../src/shared/util.js"; +import { AbortException, ResponseException } from "../../src/shared/util.js"; import { PDFNetworkStream } from "../../src/display/network.js"; +import { testCrossOriginRedirects } from "./common_pdfstream_tests.js"; +import { TestPdfsServer } from "./test_utils.js"; describe("network", function () { const pdf1 = new URL("../pdfs/tracemonkey.pdf", window.location).href; @@ -31,7 +33,7 @@ describe("network", function () { const fullReader = stream.getFullReader(); let isStreamingSupported, isRangeSupported; - const promise = fullReader.headersReady.then(function () { + await fullReader.headersReady.then(function () { isStreamingSupported = fullReader.isStreamingSupported; isRangeSupported = fullReader.isRangeSupported; }); @@ -49,7 +51,7 @@ describe("network", function () { }); }; - await Promise.all([read(), promise]); + await read(); expect(len).toEqual(pdf1Length); expect(count).toEqual(1); @@ -72,7 +74,7 @@ describe("network", function () { const fullReader = stream.getFullReader(); let isStreamingSupported, isRangeSupported, fullReaderCancelled; - const promise = fullReader.headersReady.then(function () { + await fullReader.headersReady.then(function () { isStreamingSupported = fullReader.isStreamingSupported; isRangeSupported = fullReader.isRangeSupported; // we shall be able to close the full reader without issues @@ -107,7 +109,6 @@ describe("network", function () { await Promise.all([ read(range1Reader, result1), read(range2Reader, result2), - promise, ]); expect(result1.value).toEqual(rangeSize); @@ -116,4 +117,84 @@ describe("network", function () { expect(isRangeSupported).toEqual(true); expect(fullReaderCancelled).toEqual(true); }); + + it(`handle reading ranges with missing/invalid "Content-Range" header`, async function () { + if (globalThis.chrome) { + pending("Fails intermittently in Google Chrome."); + } + + async function readRanges(mode) { + const rangeSize = 32768; + const stream = new PDFNetworkStream({ + url: `${pdf1}?test-network-break-ranges=${mode}`, + length: pdf1Length, + rangeChunkSize: rangeSize, + disableStream: true, + disableRange: false, + }); + + const fullReader = stream.getFullReader(); + + await fullReader.headersReady; + // Ensure that range requests are supported. + expect(fullReader.isRangeSupported).toEqual(true); + // We shall be able to close the full reader without issues. + fullReader.cancel(new AbortException("Don't need fullReader.")); + + const rangeReader = stream.getRangeReader( + pdf1Length - rangeSize, + pdf1Length + ); + + try { + await rangeReader.read(); + + // Shouldn't get here. + expect(false).toEqual(true); + } catch (ex) { + expect(ex instanceof ResponseException).toEqual(true); + expect(ex.status).toEqual(0); + expect(ex.missing).toEqual(false); + } + } + + await Promise.all([readRanges("missing"), readRanges("invalid")]); + }); + + describe("Redirects", function () { + beforeAll(async function () { + await TestPdfsServer.ensureStarted(); + }); + + afterAll(async function () { + await TestPdfsServer.ensureStopped(); + }); + + it("redirects allowed if all responses are same-origin", async function () { + await testCrossOriginRedirects({ + PDFStreamClass: PDFNetworkStream, + redirectIfRange: false, + async testRangeReader(rangeReader) { + await expectAsync(rangeReader.read()).toBeResolved(); + }, + }); + }); + + it("redirects blocked if any response is cross-origin", async function () { + await testCrossOriginRedirects({ + PDFStreamClass: PDFNetworkStream, + redirectIfRange: true, + async testRangeReader(rangeReader) { + // When read (sync), error should be reported. + await expectAsync(rangeReader.read()).toBeRejectedWithError( + /^Expected range response-origin "http:.*" to match "http:.*"\.$/ + ); + // When read again (async), error should be consistent. + await expectAsync(rangeReader.read()).toBeRejectedWithError( + /^Expected range response-origin "http:.*" to match "http:.*"\.$/ + ); + }, + }); + }); + }); }); diff --git a/test/unit/network_utils_spec.js b/test/unit/network_utils_spec.js index e697e24deeb74..2b68c4dbf36fa 100644 --- a/test/unit/network_utils_spec.js +++ b/test/unit/network_utils_spec.js @@ -15,15 +15,12 @@ import { createHeaders, - createResponseStatusError, + createResponseError, extractFilenameFromHeader, validateRangeRequestCapabilities, validateResponseStatus, } from "../../src/display/network_utils.js"; -import { - MissingPDFException, - UnexpectedResponseException, -} from "../../src/shared/util.js"; +import { ResponseException } from "../../src/shared/util.js"; describe("network_utils", function () { describe("createHeaders", function () { @@ -370,31 +367,28 @@ describe("network_utils", function () { }); }); - describe("createResponseStatusError", function () { - it("handles missing PDF file responses", function () { - expect(createResponseStatusError(404, "https://foo.com/bar.pdf")).toEqual( - new MissingPDFException('Missing PDF "https://foo.com/bar.pdf".') - ); + describe("createResponseError", function () { + function testCreateResponseError(url, status, missing) { + const error = createResponseError(status, url); - expect(createResponseStatusError(0, "file://foo.pdf")).toEqual( - new MissingPDFException('Missing PDF "file://foo.pdf".') + expect(error instanceof ResponseException).toEqual(true); + expect(error.message).toEqual( + `Unexpected server response (${status}) while retrieving PDF "${url}".` ); + expect(error.status).toEqual(status); + expect(error.missing).toEqual(missing); + } + + it("handles missing PDF file responses", function () { + testCreateResponseError("https://foo.com/bar.pdf", 404, true); + + testCreateResponseError("file://foo.pdf", 0, true); }); it("handles unexpected responses", function () { - expect(createResponseStatusError(302, "https://foo.com/bar.pdf")).toEqual( - new UnexpectedResponseException( - "Unexpected server response (302) while retrieving PDF " + - '"https://foo.com/bar.pdf".' - ) - ); + testCreateResponseError("https://foo.com/bar.pdf", 302, false); - expect(createResponseStatusError(0, "https://foo.com/bar.pdf")).toEqual( - new UnexpectedResponseException( - "Unexpected server response (0) while retrieving PDF " + - '"https://foo.com/bar.pdf".' - ) - ); + testCreateResponseError("https://foo.com/bar.pdf", 0, false); }); }); diff --git a/test/unit/node_stream_spec.js b/test/unit/node_stream_spec.js index 775131caa3f50..cab12391b09cc 100644 --- a/test/unit/node_stream_spec.js +++ b/test/unit/node_stream_spec.js @@ -14,7 +14,6 @@ */ import { AbortException, isNodeJS } from "../../src/shared/util.js"; -import { createTemporaryNodeServer } from "./test_utils.js"; import { PDFNodeStream } from "../../src/display/node_stream.js"; // Ensure that these tests only run in Node.js environments. @@ -24,98 +23,49 @@ if (!isNodeJS) { ); } -const url = await __non_webpack_import__("url"); - describe("node_stream", function () { - let tempServer = null; - + const url = process.getBuiltinModule("url"); const cwdURL = url.pathToFileURL(process.cwd()) + "/"; const pdf = new URL("./test/pdfs/tracemonkey.pdf", cwdURL).href; const pdfLength = 1016315; - beforeAll(function () { - tempServer = createTemporaryNodeServer(); - }); - - afterAll(function () { - // Close the server from accepting new connections after all test finishes. - const { server } = tempServer; - server.close(); - - tempServer = null; - }); - - it("read both http(s) and filesystem pdf files", async function () { - const stream1 = new PDFNodeStream({ - url: `http://127.0.0.1:${tempServer.port}/tracemonkey.pdf`, - rangeChunkSize: 65536, - disableStream: true, - disableRange: true, - }); - - const stream2 = new PDFNodeStream({ + it("read filesystem pdf files", async function () { + const stream = new PDFNodeStream({ url: pdf, rangeChunkSize: 65536, disableStream: true, disableRange: true, }); - const fullReader1 = stream1.getFullReader(); - const fullReader2 = stream2.getFullReader(); - - let isStreamingSupported1, isRangeSupported1; - const promise1 = fullReader1.headersReady.then(() => { - isStreamingSupported1 = fullReader1.isStreamingSupported; - isRangeSupported1 = fullReader1.isRangeSupported; - }); + const fullReader = stream.getFullReader(); - let isStreamingSupported2, isRangeSupported2; - const promise2 = fullReader2.headersReady.then(() => { - isStreamingSupported2 = fullReader2.isStreamingSupported; - isRangeSupported2 = fullReader2.isRangeSupported; + let isStreamingSupported, isRangeSupported; + const promise = fullReader.headersReady.then(() => { + isStreamingSupported = fullReader.isStreamingSupported; + isRangeSupported = fullReader.isRangeSupported; }); - let len1 = 0, - len2 = 0; - const read1 = function () { - return fullReader1.read().then(function (result) { - if (result.done) { - return undefined; - } - len1 += result.value.byteLength; - return read1(); - }); - }; - const read2 = function () { - return fullReader2.read().then(function (result) { + let len = 0; + const read = function () { + return fullReader.read().then(function (result) { if (result.done) { return undefined; } - len2 += result.value.byteLength; - return read2(); + len += result.value.byteLength; + return read(); }); }; - await Promise.all([read1(), read2(), promise1, promise2]); + await Promise.all([read(), promise]); - expect(isStreamingSupported1).toEqual(false); - expect(isRangeSupported1).toEqual(false); - expect(isStreamingSupported2).toEqual(false); - expect(isRangeSupported2).toEqual(false); - expect(len1).toEqual(pdfLength); - expect(len1).toEqual(len2); + expect(isStreamingSupported).toEqual(false); + expect(isRangeSupported).toEqual(false); + expect(len).toEqual(pdfLength); }); - it("read custom ranges for both http(s) and filesystem urls", async function () { + it("read custom ranges for filesystem urls", async function () { const rangeSize = 32768; - const stream1 = new PDFNodeStream({ - url: `http://127.0.0.1:${tempServer.port}/tracemonkey.pdf`, - length: pdfLength, - rangeChunkSize: rangeSize, - disableStream: true, - disableRange: false, - }); - const stream2 = new PDFNodeStream({ + const stream = new PDFNodeStream({ url: pdf, length: pdfLength, rangeChunkSize: rangeSize, @@ -123,53 +73,28 @@ describe("node_stream", function () { disableRange: false, }); - const fullReader1 = stream1.getFullReader(); - const fullReader2 = stream2.getFullReader(); + const fullReader = stream.getFullReader(); - let isStreamingSupported1, isRangeSupported1, fullReaderCancelled1; - let isStreamingSupported2, isRangeSupported2, fullReaderCancelled2; - - const promise1 = fullReader1.headersReady.then(function () { - isStreamingSupported1 = fullReader1.isStreamingSupported; - isRangeSupported1 = fullReader1.isRangeSupported; + let isStreamingSupported, isRangeSupported, fullReaderCancelled; + const promise = fullReader.headersReady.then(function () { + isStreamingSupported = fullReader.isStreamingSupported; + isRangeSupported = fullReader.isRangeSupported; // we shall be able to close the full reader without issues - fullReader1.cancel(new AbortException("Don't need fullReader1.")); - fullReaderCancelled1 = true; - }); - - const promise2 = fullReader2.headersReady.then(function () { - isStreamingSupported2 = fullReader2.isStreamingSupported; - isRangeSupported2 = fullReader2.isRangeSupported; - fullReader2.cancel(new AbortException("Don't need fullReader2.")); - fullReaderCancelled2 = true; + fullReader.cancel(new AbortException("Don't need fullReader.")); + fullReaderCancelled = true; }); // Skipping fullReader results, requesting something from the PDF end. const tailSize = pdfLength % rangeSize || rangeSize; - const range11Reader = stream1.getRangeReader( - pdfLength - tailSize - rangeSize, - pdfLength - tailSize - ); - const range12Reader = stream1.getRangeReader( - pdfLength - tailSize, - pdfLength - ); - - const range21Reader = stream2.getRangeReader( + const range1Reader = stream.getRangeReader( pdfLength - tailSize - rangeSize, pdfLength - tailSize ); - const range22Reader = stream2.getRangeReader( - pdfLength - tailSize, - pdfLength - ); - - const result11 = { value: 0 }, - result12 = { value: 0 }; - const result21 = { value: 0 }, - result22 = { value: 0 }; + const range2Reader = stream.getRangeReader(pdfLength - tailSize, pdfLength); + const result1 = { value: 0 }, + result2 = { value: 0 }; const read = function (reader, lenResult) { return reader.read().then(function (result) { if (result.done) { @@ -181,23 +106,15 @@ describe("node_stream", function () { }; await Promise.all([ - read(range11Reader, result11), - read(range12Reader, result12), - read(range21Reader, result21), - read(range22Reader, result22), - promise1, - promise2, + read(range1Reader, result1), + read(range2Reader, result2), + promise, ]); - expect(result11.value).toEqual(rangeSize); - expect(result12.value).toEqual(tailSize); - expect(result21.value).toEqual(rangeSize); - expect(result22.value).toEqual(tailSize); - expect(isStreamingSupported1).toEqual(false); - expect(isRangeSupported1).toEqual(true); - expect(fullReaderCancelled1).toEqual(true); - expect(isStreamingSupported2).toEqual(false); - expect(isRangeSupported2).toEqual(true); - expect(fullReaderCancelled2).toEqual(true); + expect(result1.value).toEqual(rangeSize); + expect(result2.value).toEqual(tailSize); + expect(isStreamingSupported).toEqual(false); + expect(isRangeSupported).toEqual(true); + expect(fullReaderCancelled).toEqual(true); }); }); diff --git a/test/unit/pdf_find_controller_spec.js b/test/unit/pdf_find_controller_spec.js index 408cba67d7aca..76ac0485fd5bf 100644 --- a/test/unit/pdf_find_controller_spec.js +++ b/test/unit/pdf_find_controller_spec.js @@ -1062,15 +1062,16 @@ describe("pdf_find_controller", function () { await testOnFind({ eventBus }); }); - it("performs a search in a text with compound word on two lines", async function () { + it("performs a search in a text with a compound word on two lines", async function () { const { eventBus, pdfFindController } = await initPdfFindController("issue18693.pdf"); + const query = "hel-Lo"; await testSearch({ eventBus, pdfFindController, state: { - query: "hel-Lo", + query, }, matchesPerPage: [1], selectedMatch: { @@ -1078,7 +1079,62 @@ describe("pdf_find_controller", function () { matchIndex: 0, }, pageMatches: [[6]], - pageMatchesLength: [[7]], + pageMatchesLength: [[query.length]], + }); + }); + + it("performs a search after a compound word on two lines", async function () { + const { eventBus, pdfFindController } = + await initPdfFindController("issue19120.pdf"); + + const query = "a"; + await testSearch({ + eventBus, + pdfFindController, + state: { + query, + }, + matchesPerPage: [3], + selectedMatch: { + pageIndex: 0, + matchIndex: 0, + }, + pageMatches: [[0, 4, 15]], + pageMatchesLength: [[query.length, query.length, query.length]], + }); + }); + + it("performs a search with a dash between two digits", async () => { + const { eventBus, pdfFindController } = await initPdfFindController(); + + await testSearch({ + eventBus, + pdfFindController, + state: { + query: "2008-02", + }, + matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + selectedMatch: { + pageIndex: 13, + matchIndex: 0, + }, + pageMatches: [[], [], [], [], [], [], [], [], [], [], [], [], [], [314]], + pageMatchesLength: [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [7], + ], }); }); diff --git a/test/unit/pdf_spec.js b/test/unit/pdf_spec.js index 269a26ff7c60e..95267085ea6cf 100644 --- a/test/unit/pdf_spec.js +++ b/test/unit/pdf_spec.js @@ -18,30 +18,30 @@ import { AnnotationEditorParamsType, AnnotationEditorType, AnnotationMode, + AnnotationType, createValidAbsoluteUrl, FeatureTest, + getUuid, ImageKind, InvalidPDFException, - isNodeJS, - MissingPDFException, normalizeUnicode, OPS, PasswordResponses, PermissionFlag, + ResponseException, shadow, - UnexpectedResponseException, Util, VerbosityLevel, } from "../../src/shared/util.js"; import { build, getDocument, + isValidExplicitDest, PDFDataRangeTransport, PDFWorker, version, } from "../../src/display/api.js"; import { - DOMSVGFactory, fetchData, getFilenameFromUrl, getPdfFilenameFromUrl, @@ -54,14 +54,19 @@ import { PixelsPerInch, RenderingCancelledException, setLayerDimensions, + stopEvent, + SupportedImageMimeTypes, } from "../../src/display/display_utils.js"; import { AnnotationEditorLayer } from "../../src/display/editor/annotation_editor_layer.js"; import { AnnotationEditorUIManager } from "../../src/display/editor/tools.js"; import { AnnotationLayer } from "../../src/display/annotation_layer.js"; import { ColorPicker } from "../../src/display/editor/color_picker.js"; +import { DOMSVGFactory } from "../../src/display/svg_factory.js"; import { DrawLayer } from "../../src/display/draw_layer.js"; import { GlobalWorkerOptions } from "../../src/display/worker_options.js"; +import { SignatureExtractor } from "../../src/display/editor/drawers/signaturedraw.js"; import { TextLayer } from "../../src/display/text_layer.js"; +import { TouchManager } from "../../src/display/touch_manager.js"; import { XfaLayer } from "../../src/display/xfa_layer.js"; const expectedAPI = Object.freeze({ @@ -72,6 +77,7 @@ const expectedAPI = Object.freeze({ AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, + AnnotationType, build, ColorPicker, createValidAbsoluteUrl, @@ -82,13 +88,14 @@ const expectedAPI = Object.freeze({ getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, + getUuid, getXfaPageViewport, GlobalWorkerOptions, ImageKind, InvalidPDFException, isDataScheme, isPdfFile, - MissingPDFException, + isValidExplicitDest, noContextMenu, normalizeUnicode, OPS, @@ -100,10 +107,14 @@ const expectedAPI = Object.freeze({ PermissionFlag, PixelsPerInch, RenderingCancelledException, + ResponseException, setLayerDimensions, shadow, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, TextLayer, - UnexpectedResponseException, + TouchManager, Util, VerbosityLevel, version, @@ -127,13 +138,20 @@ describe("pdfjs_api", function () { describe("web_pdfjsLib", function () { it("checks that the viewer re-exports the expected API functionality", async function () { - if (isNodeJS) { - pending("loadScript is not supported in Node.js."); - } - const apiPath = "../../build/generic/build/pdf.mjs"; - await import(apiPath); + // Load the API globally, as the viewer does. + // eslint-disable-next-line no-unsanitized/method + await import( + typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") + ? "../../../generic-legacy/build/pdf.mjs" + : "../../build/generic/build/pdf.mjs" + ); - const webPdfjsLib = await import("../../web/pdfjs.js"); + // eslint-disable-next-line no-unsanitized/method + const webPdfjsLib = await import( + typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") + ? "../../../../web/pdfjs.js" + : "../../web/pdfjs.js" + ); expect(Object.keys(webPdfjsLib).sort()).toEqual( Object.keys(expectedAPI).sort() diff --git a/test/unit/primitives_spec.js b/test/unit/primitives_spec.js index caefdf0250419..00c491004818c 100644 --- a/test/unit/primitives_spec.js +++ b/test/unit/primitives_spec.js @@ -221,17 +221,12 @@ describe("primitives", function () { expect(values[2]).toEqual(testFontFile); }); - it("should callback for each stored key", function () { - const callbackSpy = jasmine.createSpy("spy on callback in dictionary"); - - dictWithManyKeys.forEach(callbackSpy); - - expect(callbackSpy).toHaveBeenCalled(); - const callbackSpyCalls = callbackSpy.calls; - expect(callbackSpyCalls.argsFor(0)).toEqual(["FontFile", testFontFile]); - expect(callbackSpyCalls.argsFor(1)).toEqual(["FontFile2", testFontFile2]); - expect(callbackSpyCalls.argsFor(2)).toEqual(["FontFile3", testFontFile3]); - expect(callbackSpyCalls.count()).toEqual(3); + it("should iterate through each stored key", function () { + expect([...dictWithManyKeys]).toEqual([ + ["FontFile", testFontFile], + ["FontFile2", testFontFile2], + ["FontFile3", testFontFile3], + ]); }); it("should handle keys pointing to indirect objects, both sync and async", async function () { diff --git a/test/unit/scripting_spec.js b/test/unit/scripting_spec.js index 12c7161c21c9c..d570908662a90 100644 --- a/test/unit/scripting_spec.js +++ b/test/unit/scripting_spec.js @@ -243,6 +243,12 @@ describe("Scripting", function () { value = await myeval(`util.scand(2, "4/15/07 3:14:15 am").toString()`); expect(new Date(value)).toEqual(date); + + value = await myeval(`util.scand("mmddyyyy", "07/15/2007").toString()`); + expect(new Date(value)).toEqual(new Date("07/15/2007 12:00:00")); + + value = await myeval(`util.scand("mmddyyyy", "07a15b2007").toString()`); + expect(new Date(value)).toEqual(new Date("07/15/2007 12:00:00")); }); }); @@ -601,6 +607,52 @@ describe("Scripting", function () { value = await myeval(`app.platform = "hello"`); expect(value).toEqual("app.platform is read-only"); }); + + it("shouldn't display an alert", async () => { + const refId = getId(); + const data = { + objects: { + field: [ + { + id: refId, + value: "", + actions: { + Validate: [`app.alert(event.value);`], + }, + type: "text", + name: "MyField", + }, + ], + }, + appInfo: { language: "en-US", platform: "Linux x86_64" }, + calculationOrder: [], + dispatchEventName: "_dispatchMe", + }; + + sandbox.createSandbox(data); + await sandbox.dispatchEventInSandbox({ + id: refId, + value: "hello", + name: "Keystroke", + willCommit: true, + }); + expect(send_queue.has("alert")).toEqual(true); + expect(send_queue.get("alert")).toEqual({ + command: "alert", + value: "hello", + }); + send_queue.delete(refId); + send_queue.delete("alert"); + + await sandbox.dispatchEventInSandbox({ + id: refId, + value: "", + name: "Keystroke", + willCommit: true, + }); + expect(send_queue.has("alert")).toEqual(false); + send_queue.delete(refId); + }); }); describe("AForm", function () { @@ -624,8 +676,9 @@ describe("Scripting", function () { ); }; - await check("05", "dd", "2000/01/05"); - await check("12", "mm", "2000/12/01"); + const year = new Date().getFullYear(); + await check("05", "dd", `${year}/01/05`); + await check("12", "mm", `${year}/12/01`); await check("2022", "yyyy", "2022/01/01"); await check("a1$9bbbb21", "dd/mm/yyyy", "2021/09/01"); await check("1/2/2024", "dd/mm/yyyy", "2024/02/01"); @@ -1632,6 +1685,67 @@ describe("Scripting", function () { send_queue.delete(refId); }); + it("should validate a US phone number with digits and dashes (long) on a keystroke event", async () => { + const refId = getId(); + const data = { + objects: { + field: [ + { + id: refId, + value: "", + actions: { + Keystroke: [`AFSpecial_Keystroke(2);`], + }, + type: "text", + }, + ], + }, + appInfo: { language: "en-US", platform: "Linux x86_64" }, + calculationOrder: [], + dispatchEventName: "_dispatchMe", + }; + sandbox.createSandbox(data); + + let value = ""; + const changes = "123-456-7890"; + let i = 0; + + for (; i < changes.length; i++) { + const change = changes.charAt(i); + await sandbox.dispatchEventInSandbox({ + id: refId, + value, + change, + name: "Keystroke", + willCommit: false, + selStart: i, + selEnd: i, + }); + expect(send_queue.has(refId)).toEqual(true); + send_queue.delete(refId); + value += change; + } + + await sandbox.dispatchEventInSandbox({ + id: refId, + value, + change: "A", + name: "Keystroke", + willCommit: false, + selStart: i, + selEnd: i, + }); + expect(send_queue.has(refId)).toEqual(true); + expect(send_queue.get(refId)).toEqual({ + id: refId, + siblings: null, + value, + selRange: [i, i], + }); + + send_queue.delete(refId); + }); + it("should validate a US phone number (short) on a keystroke event", async () => { const refId = getId(); const data = { diff --git a/test/unit/svg_factory_spec.js b/test/unit/svg_factory_spec.js new file mode 100644 index 0000000000000..77c5f19dbe379 --- /dev/null +++ b/test/unit/svg_factory_spec.js @@ -0,0 +1,72 @@ +/* Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DOMSVGFactory } from "../../src/display/svg_factory.js"; +import { isNodeJS } from "../../src/shared/util.js"; + +describe("svg_factory", function () { + describe("DOMSVGFactory", function () { + let svgFactory; + + beforeAll(function () { + svgFactory = new DOMSVGFactory(); + }); + + afterAll(function () { + svgFactory = null; + }); + + it("`create` should throw an error if the dimensions are invalid", function () { + // Invalid width. + expect(function () { + return svgFactory.create(-1, 0); + }).toThrow(new Error("Invalid SVG dimensions")); + + // Invalid height. + expect(function () { + return svgFactory.create(0, -1); + }).toThrow(new Error("Invalid SVG dimensions")); + }); + + it("`create` should return an SVG element if the dimensions are valid", function () { + if (isNodeJS) { + pending("Document is not supported in Node.js."); + } + + const svg = svgFactory.create(20, 40); + expect(svg instanceof SVGSVGElement).toBe(true); + expect(svg.getAttribute("version")).toBe("1.1"); + expect(svg.getAttribute("width")).toBe("20px"); + expect(svg.getAttribute("height")).toBe("40px"); + expect(svg.getAttribute("preserveAspectRatio")).toBe("none"); + expect(svg.getAttribute("viewBox")).toBe("0 0 20 40"); + }); + + it("`createElement` should throw an error if the type is not a string", function () { + expect(function () { + return svgFactory.createElement(true); + }).toThrow(new Error("Invalid SVG element type")); + }); + + it("`createElement` should return an SVG element if the type is valid", function () { + if (isNodeJS) { + pending("Document is not supported in Node.js."); + } + + const svg = svgFactory.createElement("svg:rect"); + expect(svg instanceof SVGRectElement).toBe(true); + }); + }); +}); diff --git a/test/unit/test_utils.js b/test/unit/test_utils.js index 473b292e83935..15b5fce06f50a 100644 --- a/test/unit/test_utils.js +++ b/test/unit/test_utils.js @@ -14,19 +14,18 @@ */ import { assert, isNodeJS } from "../../src/shared/util.js"; +import { + fetchData as fetchDataNode, + NodeCMapReaderFactory, + NodeStandardFontDataFactory, +} from "../../src/display/node_utils.js"; import { NullStream, StringStream } from "../../src/core/stream.js"; import { Page, PDFDocument } from "../../src/core/document.js"; +import { DOMCMapReaderFactory } from "../../src/display/cmap_reader_factory.js"; +import { DOMStandardFontDataFactory } from "../../src/display/standard_fontdata_factory.js"; import { fetchData as fetchDataDOM } from "../../src/display/display_utils.js"; -import { fetchData as fetchDataNode } from "../../src/display/node_utils.js"; import { Ref } from "../../src/core/primitives.js"; -let fs, http; -if (isNodeJS) { - // Native packages. - fs = await __non_webpack_import__("fs"); - http = await __non_webpack_import__("http"); -} - const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/"; const CMAP_URL = isNodeJS ? "./external/bcmaps/" : "../../external/bcmaps/"; @@ -35,6 +34,8 @@ const STANDARD_FONT_DATA_URL = isNodeJS ? "./external/standard_fonts/" : "../../external/standard_fonts/"; +const WASM_URL = isNodeJS ? "./external/openjpeg/" : "../../external/openjpeg/"; + class DefaultFileReaderFactory { static async fetch(params) { if (isNodeJS) { @@ -45,12 +46,23 @@ class DefaultFileReaderFactory { } } +const DefaultCMapReaderFactory = + typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeCMapReaderFactory + : DOMCMapReaderFactory; + +const DefaultStandardFontDataFactory = + typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS + ? NodeStandardFontDataFactory + : DOMStandardFontDataFactory; + function buildGetDocumentParams(filename, options) { const params = Object.create(null); params.url = isNodeJS ? TEST_PDFS_PATH + filename : new URL(TEST_PDFS_PATH + filename, window.location).href; params.standardFontDataUrl = STANDARD_FONT_DATA_URL; + params.wasmUrl = WASM_URL; for (const option in options) { params[option] = options[option]; @@ -58,6 +70,22 @@ function buildGetDocumentParams(filename, options) { return params; } +function getCrossOriginHostname(hostname) { + if (hostname === "localhost") { + // Note: This does not work if localhost is listening on IPv6 only. + // As a work-around, visit the IPv6 version at: + // http://[::1]:8888/test/unit/unit_test.html?spec=Cross-origin + return "127.0.0.1"; + } + + if (hostname === "127.0.0.1" || hostname === "[::1]") { + return "localhost"; + } + + // FQDN are cross-origin and browsers usually resolve them to the same server. + return hostname.endsWith(".") ? hostname.slice(0, -1) : hostname + "."; +} + class XRefMock { constructor(array) { this._map = Object.create(null); @@ -129,57 +157,105 @@ function createIdFactory(pageIndex) { return page._localIdFactory; } -function createTemporaryNodeServer() { - assert(isNodeJS, "Should only be used in Node.js environments."); - - // Create http server to serve pdf data for tests. - const server = http - .createServer((request, response) => { - const filePath = process.cwd() + "/test/pdfs" + request.url; - fs.promises.lstat(filePath).then( - stat => { - if (!request.headers.range) { - const contentLength = stat.size; - const stream = fs.createReadStream(filePath); - response.writeHead(200, { - "Content-Type": "application/pdf", - "Content-Length": contentLength, - "Accept-Ranges": "bytes", - }); - stream.pipe(response); - } else { - const [start, end] = request.headers.range - .split("=")[1] - .split("-") - .map(x => Number(x)); - const stream = fs.createReadStream(filePath, { start, end }); - response.writeHead(206, { - "Content-Type": "application/pdf", - }); - stream.pipe(response); - } - }, - error => { - response.writeHead(404); - response.end(`File ${request.url} not found!`); - } - ); - }) - .listen(0); /* Listen on a random free port */ - - return { - server, - port: server.address().port, - }; +// Some tests rely on special behavior from webserver.mjs. When loaded in the +// browser, the page is already served from WebServer. When running from +// Node.js, that is not the case. This helper starts the WebServer if needed, +// and offers a mechanism to resolve the URL in a uniform way. +class TestPdfsServer { + static #webServer; + + static #startCount = 0; + + static #startPromise; + + static async ensureStarted() { + if (this.#startCount++) { + // Already started before. E.g. from another beforeAll call. + return this.#startPromise; + } + if (!isNodeJS) { + // In web browsers, tests are presumably served by webserver.mjs. + return undefined; + } + + this.#startPromise = this.#startServer().finally(() => { + this.#startPromise = null; + }); + return this.#startPromise; + } + + static async #startServer() { + // WebServer from webserver.mjs is imported dynamically instead of + // statically because we do not need it when running from the browser. + let WebServer; + if (import.meta.url.endsWith("/lib-legacy/test/unit/test_utils.js")) { + // When "gulp unittestcli" is used to run tests, the tests are run from + // pdf.js/build/lib-legacy/test/ instead of directly from pdf.js/test/. + // eslint-disable-next-line import/no-unresolved + ({ WebServer } = await import("../../../../test/webserver.mjs")); + } else { + ({ WebServer } = await import("../webserver.mjs")); + } + this.#webServer = new WebServer({ + host: "127.0.0.1", + root: TEST_PDFS_PATH, + }); + await new Promise(resolve => { + this.#webServer.start(resolve); + }); + } + + static async ensureStopped() { + assert(this.#startCount > 0, "ensureStarted() should be called first"); + assert(!this.#startPromise, "ensureStarted() should have resolved"); + if (--this.#startCount) { + // Keep server alive as long as there is an ensureStarted() that was not + // followed by an ensureStopped() call. + // This could happen if ensureStarted() was called again before + // ensureStopped() was called from afterAll(). + return; + } + if (!isNodeJS) { + // Web browsers cannot stop the server. + return; + } + + await new Promise(resolve => { + this.#webServer.stop(resolve); + this.#webServer = null; + }); + } + + /** + * @param {string} path - path to file within test/unit/pdf/ (TEST_PDFS_PATH). + * @returns {URL} + */ + static resolveURL(path) { + assert(this.#startCount > 0, "ensureStarted() should be called first"); + assert(!this.#startPromise, "ensureStarted() should have resolved"); + + if (isNodeJS) { + // Note: TestPdfsServer.ensureStarted() should be called first. + return new URL(path, `http://127.0.0.1:${this.#webServer.port}/`); + } + // When "gulp server" is used, our URL looks like + // http://localhost:8888/test/unit/unit_test.html + // The PDFs are served from: + // http://localhost:8888/test/pdfs/ + return new URL(TEST_PDFS_PATH + path, window.location); + } } export { buildGetDocumentParams, CMAP_URL, createIdFactory, - createTemporaryNodeServer, + DefaultCMapReaderFactory, DefaultFileReaderFactory, + DefaultStandardFontDataFactory, + getCrossOriginHostname, STANDARD_FONT_DATA_URL, TEST_PDFS_PATH, + TestPdfsServer, XRefMock, }; diff --git a/test/unit/ui_utils_spec.js b/test/unit/ui_utils_spec.js index 0ead190432d90..41af41e542030 100644 --- a/test/unit/ui_utils_spec.js +++ b/test/unit/ui_utils_spec.js @@ -279,12 +279,11 @@ describe("ui_utils", function () { viewTop < scrollBottom && viewBottom > scrollTop ) { - const hiddenHeight = - Math.max(0, scrollTop - viewTop) + - Math.max(0, viewBottom - scrollBottom); - const hiddenWidth = - Math.max(0, scrollLeft - viewLeft) + - Math.max(0, viewRight - scrollRight); + const minY = Math.max(0, scrollTop - viewTop); + const minX = Math.max(0, scrollLeft - viewLeft); + + const hiddenHeight = minY + Math.max(0, viewBottom - scrollBottom); + const hiddenWidth = minX + Math.max(0, viewRight - scrollRight); const fractionHeight = (div.clientHeight - hiddenHeight) / div.clientHeight; @@ -292,12 +291,23 @@ describe("ui_utils", function () { (div.clientWidth - hiddenWidth) / div.clientWidth; const percent = (fractionHeight * fractionWidth * 100) | 0; + let visibleArea = null; + if (percent < 100) { + visibleArea = { + minX, + minY, + maxX: Math.min(viewRight, scrollRight) - viewLeft, + maxY: Math.min(viewBottom, scrollBottom) - viewTop, + }; + } + views.push({ id: view.id, x: viewLeft, y: viewTop, view, percent, + visibleArea, widthPercent: (fractionWidth * 100) | 0, }); ids.add(view.id); diff --git a/test/unit/unit_test.html b/test/unit/unit_test.html index 2ddbd960a8be1..536ebf35babeb 100644 --- a/test/unit/unit_test.html +++ b/test/unit/unit_test.html @@ -1,7 +1,7 @@ - pdf.js unit test + PDF.js unit tests @@ -20,6 +20,9 @@ "fluent-dom": "../../node_modules/@fluent/dom/esm/index.js", "cached-iterable": "../../node_modules/cached-iterable/src/index.mjs", + "display-cmap_reader_factory": "../../src/display/cmap_reader_factory.js", + "display-standard_fontdata_factory": "../../src/display/standard_fontdata_factory.js", + "display-wasm_factory": "../../src/display/wasm_factory.js", "display-fetch_stream": "../../src/display/fetch_stream.js", "display-network": "../../src/display/network.js", "display-node_stream": "../../src/display/stubs.js", diff --git a/test/unit/util_spec.js b/test/unit/util_spec.js index c5a9e07da1a72..9d96e118e11a1 100644 --- a/test/unit/util_spec.js +++ b/test/unit/util_spec.js @@ -18,6 +18,7 @@ import { bytesToString, createValidAbsoluteUrl, getModificationDate, + getUuid, string32, stringToBytes, stringToPDFString, @@ -248,4 +249,12 @@ describe("util", function () { expect(getModificationDate(date)).toEqual("31410609020653"); }); }); + + describe("getUuid", function () { + it("should get uuid string", function () { + const uuid = getUuid(); + expect(typeof uuid).toEqual("string"); + expect(uuid.length).toBeGreaterThanOrEqual(32); + }); + }); }); diff --git a/test/unit/writer_spec.js b/test/unit/writer_spec.js index 4298e0ba8a87b..5ebb37a9933f3 100644 --- a/test/unit/writer_spec.js +++ b/test/unit/writer_spec.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Dict, Name, Ref } from "../../src/core/primitives.js"; +import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js"; import { incrementalUpdate, writeDict } from "../../src/core/writer.js"; import { bytesToString } from "../../src/shared/util.js"; import { StringStream } from "../../src/core/stream.js"; @@ -22,10 +22,9 @@ describe("Writer", function () { describe("Incremental update", function () { it("should update a file with new objects", async function () { const originalData = new Uint8Array(); - const newRefs = [ - { ref: Ref.get(123, 0x2d), data: "abc\n" }, - { ref: Ref.get(456, 0x4e), data: "defg\n" }, - ]; + const changes = new RefSetCache(); + changes.put(Ref.get(123, 0x2d), { data: "abc\n" }); + changes.put(Ref.get(456, 0x4e), { data: "defg\n" }); const xrefInfo = { newRef: Ref.get(789, 0), startXRef: 314, @@ -40,7 +39,8 @@ describe("Writer", function () { let data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, + xref: {}, useXrefStream: true, }); data = bytesToString(data); @@ -65,7 +65,8 @@ describe("Writer", function () { data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, + xref: {}, useXrefStream: false, }); data = bytesToString(data); @@ -91,7 +92,8 @@ describe("Writer", function () { it("should update a file, missing the /ID-entry, with new objects", async function () { const originalData = new Uint8Array(); - const newRefs = [{ ref: Ref.get(123, 0x2d), data: "abc\n" }]; + const changes = new RefSetCache(); + changes.put(Ref.get(123, 0x2d), { data: "abc\n" }); const xrefInfo = { newRef: Ref.get(789, 0), startXRef: 314, @@ -106,7 +108,8 @@ describe("Writer", function () { let data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, + xref: {}, useXrefStream: true, }); data = bytesToString(data); @@ -185,7 +188,7 @@ describe("Writer", function () { describe("XFA", function () { it("should update AcroForm when no datasets in XFA array", async function () { const originalData = new Uint8Array(); - const newRefs = []; + const changes = new RefSetCache(); const acroForm = new Dict(null); acroForm.set("XFA", [ @@ -212,7 +215,7 @@ describe("Writer", function () { let data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, hasXfa: true, xfaDatasetsRef, hasXfaDatasetsEntry: false, @@ -230,8 +233,7 @@ describe("Writer", function () { "<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" + "endobj\n" + "101112 0 obj\n" + - "<< /Type /EmbeddedFile /Length 20>>\n" + - "stream\n" + + "<< /Type /EmbeddedFile /Length 20>> stream\n" + "world\n" + "endstream\n" + "endobj\n" + @@ -250,10 +252,9 @@ describe("Writer", function () { it("should update a file with a deleted object", async function () { const originalData = new Uint8Array(); - const newRefs = [ - { ref: Ref.get(123, 0x2d), data: null }, - { ref: Ref.get(456, 0x4e), data: "abc\n" }, - ]; + const changes = new RefSetCache(); + changes.put(Ref.get(123, 0x2d), { data: null }); + changes.put(Ref.get(456, 0x4e), { data: "abc\n" }); const xrefInfo = { newRef: Ref.get(789, 0), startXRef: 314, @@ -268,7 +269,8 @@ describe("Writer", function () { let data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, + xref: {}, useXrefStream: true, }); data = bytesToString(data); @@ -292,7 +294,8 @@ describe("Writer", function () { data = await incrementalUpdate({ originalData, xrefInfo, - newRefs, + changes, + xref: {}, useXrefStream: false, }); data = bytesToString(data); diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js index 294573d89f3ac..615ba3c123d75 100644 --- a/test/unit/xfa_tohtml_spec.js +++ b/test/unit/xfa_tohtml_spec.js @@ -13,7 +13,6 @@ * limitations under the License. */ -import { isNodeJS } from "../../src/shared/util.js"; import { XFAFactory } from "../../src/core/xfa/factory.js"; describe("XFAFactory", function () { @@ -145,9 +144,6 @@ describe("XFAFactory", function () { }); it("should have an alt attribute from toolTip", async () => { - if (isNodeJS) { - pending("Image is not supported in Node.js."); - } const xml = ` @@ -646,4 +642,45 @@ describe("XFAFactory", function () { expect(a.attributes.href).toEqual("https://github.com/allizom/pdf.js"); expect(a.attributes.newWindow).toEqual(false); }); + + it("should take the absolute value of the font size", async () => { + const xml = ` + + + + + + + + + `; + const factory = new XFAFactory({ "xdp:xdp": xml }); + + expect(await factory.getNumPages()).toEqual(1); + + const pages = await factory.getPages(); + const p = searchHtmlNode(pages, "name", "p"); + expect(p.attributes.style.fontSize).toEqual("13.86px"); + }); }); diff --git a/test/webserver.mjs b/test/webserver.mjs index e0295f3a2865e..52511ed637ce2 100644 --- a/test/webserver.mjs +++ b/test/webserver.mjs @@ -38,6 +38,7 @@ const MIME_TYPES = { ".log": "text/plain", ".bcmap": "application/octet-stream", ".ftl": "text/plain", + ".wasm": "application/wasm", }; const DEFAULT_MIME_TYPE = "application/octet-stream"; @@ -52,7 +53,7 @@ class WebServer { this.cacheExpirationTime = cacheExpirationTime || 0; this.disableRangeRequests = false; this.hooks = { - GET: [crossOriginHandler], + GET: [crossOriginHandler, redirectHandler], POST: [], }; } @@ -177,6 +178,7 @@ class WebServer { this.#serveFileRange( response, localURL, + url.searchParams, fileSize, start, isNaN(end) ? fileSize : end + 1 @@ -307,7 +309,12 @@ class WebServer { stream.pipe(response); } - #serveFileRange(response, fileURL, fileSize, start, end) { + #serveFileRange(response, fileURL, searchParams, fileSize, start, end) { + if (end > fileSize || start > end) { + response.writeHead(416); + response.end(); + return; + } const stream = fs.createReadStream(fileURL, { flags: "rs", start, @@ -325,6 +332,16 @@ class WebServer { "Content-Range", `bytes ${start}-${end - 1}/${fileSize}` ); + + // Support test in `test/unit/network_spec.js`. + switch (searchParams.get("test-network-break-ranges")) { + case "missing": + response.removeHeader("Content-Range"); + break; + case "invalid": + response.setHeader("Content-Range", "bytes abc-def/qwerty"); + break; + } response.writeHead(206); stream.pipe(response); } @@ -336,18 +353,65 @@ class WebServer { } // This supports the "Cross-origin" test in test/unit/api_spec.js -// It is here instead of test.js so that when the test will still complete as +// and "Redirects" in test/unit/network_spec.js and +// test/unit/fetch_stream_spec.js via test/unit/common_pdfstream_tests.js. +// It is here instead of test.mjs so that when the test will still complete as // expected if the user does "gulp server" and then visits // http://localhost:8888/test/unit/unit_test.html?spec=Cross-origin function crossOriginHandler(url, request, response) { if (url.pathname === "/test/pdfs/basicapi.pdf") { + if (!url.searchParams.has("cors") || !request.headers.origin) { + return; + } + response.setHeader("Access-Control-Allow-Origin", request.headers.origin); if (url.searchParams.get("cors") === "withCredentials") { - response.setHeader("Access-Control-Allow-Origin", request.headers.origin); response.setHeader("Access-Control-Allow-Credentials", "true"); - } else if (url.searchParams.get("cors") === "withoutCredentials") { - response.setHeader("Access-Control-Allow-Origin", request.headers.origin); + } // withoutCredentials does not include Access-Control-Allow-Credentials. + response.setHeader( + "Access-Control-Expose-Headers", + "Accept-Ranges,Content-Range" + ); + response.setHeader("Vary", "Origin"); + } +} + +// This supports the "Redirects" test in test/unit/network_spec.js and +// test/unit/fetch_stream_spec.js via test/unit/common_pdfstream_tests.js. +// It is here instead of test.mjs so that when the test will still complete as +// expected if the user does "gulp server" and then visits +// http://localhost:8888/test/unit/unit_test.html?spec=Redirects +function redirectHandler(url, request, response) { + const redirectToHost = url.searchParams.get("redirectToHost"); + if (redirectToHost) { + // Chrome may serve byte range requests directly from the cache, potentially + // from a full request or a different range, without involving the server. + // To prevent this from happening, make sure that the response is never + // cached, so that Range requests are never served from the browser cache. + response.setHeader("Cache-Control", "no-store,max-age=0"); + + if (url.searchParams.get("redirectIfRange") && !request.headers.range) { + return false; + } + try { + const newURL = new URL(url); + newURL.hostname = redirectToHost; + // Delete test-only query parameters to avoid infinite redirects. + newURL.searchParams.delete("redirectToHost"); + newURL.searchParams.delete("redirectIfRange"); + if (newURL.hostname !== redirectToHost) { + throw new Error(`Invalid hostname: ${redirectToHost}`); + } + response.setHeader("Location", newURL.href); + } catch { + response.writeHead(500); + response.end(); + return true; } + response.writeHead(302); + response.end(); + return true; } + return false; } export { WebServer }; diff --git a/tsconfig.json b/tsconfig.json index 21d240d7d23dd..e5368ec253cc1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,11 @@ "moduleResolution": "node", "paths": { "pdfjs-lib": ["./src/pdf"], + "display-cmap_reader_factory": ["./src/display/cmap_reader_factory"], + "display-standard_fontdata_factory": [ + "./src/display/standard_fontdata_factory" + ], + "display-wasm_factory": ["./src/display/wasm_factory"], "display-fetch_stream": ["./src/display/fetch_stream"], "display-network": ["./src/display/network"], "display-node_stream": ["./src/display/node_stream"], diff --git a/web/.gitignore b/web/.gitignore index ae623f7cd33e4..334d49db079c2 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,3 +1,3 @@ -locale.properties locale/ cmaps/ +wasm/ diff --git a/web/alt_text_manager.js b/web/alt_text_manager.js index 4c228ab30d25d..9245d42d6c23f 100644 --- a/web/alt_text_manager.js +++ b/web/alt_text_manager.js @@ -245,9 +245,7 @@ class AltTextManager { } #finish() { - if (this.#overlayManager.active === this.#dialog) { - this.#overlayManager.close(this.#dialog); - } + this.#overlayManager.closeIfActive(this.#dialog); } #close() { diff --git a/web/annotation_editor_layer_builder.css b/web/annotation_editor_layer_builder.css index 82129fa72c225..d6a5e6827f4aa 100644 --- a/web/annotation_editor_layer_builder.css +++ b/web/annotation_editor_layer_builder.css @@ -15,6 +15,7 @@ @import url(draw_layer_builder.css); @import url(toggle_button.css); +@import url(signature_manager.css); :root { --outline-width: 2px; @@ -40,12 +41,10 @@ ); --editorFreeText-editing-cursor: text; --editorInk-editing-cursor: url(images/cursor-editorInk.svg) 0 16, pointer; - --editorHighlight-editing-cursor: url(images/cursor-editorTextHighlight.svg) - 24 24, - text; - --editorFreeHighlight-editing-cursor: url(images/cursor-editorFreeHighlight.svg) - 1 18, - pointer; + --editorHighlight-editing-cursor: + url(images/cursor-editorTextHighlight.svg) 24 24, text; + --editorFreeHighlight-editing-cursor: + url(images/cursor-editorFreeHighlight.svg) 1 18, pointer; --new-alt-text-warning-image: url(images/altText_warning.svg); } @@ -66,19 +65,21 @@ font-size: 0; } -.textLayer.highlighting { - cursor: var(--editorFreeHighlight-editing-cursor); +.textLayer { + &.highlighting { + cursor: var(--editorFreeHighlight-editing-cursor); - &:not(.free) span { - cursor: var(--editorHighlight-editing-cursor); + &:not(.free) span { + cursor: var(--editorHighlight-editing-cursor); - &[role="img"] { - cursor: var(--editorFreeHighlight-editing-cursor); + &[role="img"] { + cursor: var(--editorFreeHighlight-editing-cursor); + } } - } - &.free span { - cursor: var(--editorFreeHighlight-editing-cursor); + &.free span { + cursor: var(--editorFreeHighlight-editing-cursor); + } } } @@ -91,8 +92,8 @@ @media (min-resolution: 1.1dppx) { :root { - --editorFreeText-editing-cursor: url(images/cursor-editorFreeText.svg) 0 16, - text; + --editorFreeText-editing-cursor: + url(images/cursor-editorFreeText.svg) 0 16, text; } } @@ -122,7 +123,7 @@ background: transparent; position: absolute; inset: 0; - font-size: calc(100px * var(--scale-factor)); + font-size: calc(100px * var(--total-scale-factor)); transform-origin: 0 0; cursor: auto; @@ -156,7 +157,12 @@ cursor: var(--editorInk-editing-cursor); } -.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) { +.annotationEditorLayer .draw { + box-sizing: border-box; +} + +.annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .signatureEditor) { position: absolute; background: transparent; z-index: 1; @@ -170,10 +176,6 @@ cursor: move; } - &.moving { - touch-action: none; - } - &.selectedEditor { border: var(--focus-outline); outline: var(--focus-outline-around); @@ -218,7 +220,13 @@ } .annotationEditorLayer - :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor, + .signatureEditor + ), .textLayer { .editToolbar { --editor-toolbar-delete-image: url(images/editor-toolbar-delete.svg); @@ -309,6 +317,10 @@ gap: 0; height: 100%; + button { + padding: 0; + } + .divider { width: 0; height: calc( @@ -451,6 +463,7 @@ .tooltip { display: none; + word-wrap: anywhere; &.show { --alt-text-tooltip-bg: #f0f0f4; @@ -499,7 +512,7 @@ } .annotationEditorLayer .freeTextEditor { - padding: calc(var(--freetext-padding) * var(--scale-factor)); + padding: calc(var(--freetext-padding) * var(--total-scale-factor)); width: auto; height: auto; touch-action: none; @@ -615,7 +628,7 @@ } .annotationEditorLayer { - :is(.freeTextEditor, .inkEditor, .stampEditor) { + :is(.freeTextEditor, .inkEditor, .stampEditor, .signatureEditor) { & > .resizers { position: absolute; inset: 0; diff --git a/web/annotation_editor_layer_builder.js b/web/annotation_editor_layer_builder.js index 46082c163f838..9709628b61cc8 100644 --- a/web/annotation_editor_layer_builder.js +++ b/web/annotation_editor_layer_builder.js @@ -42,6 +42,12 @@ import { GenericL10n } from "web-null_l10n"; * @property {function} [onAppend] */ +/** + * @typedef {Object} AnnotationEditorLayerBuilderRenderOptions + * @property {PageViewport} viewport + * @property {string} [intent] - The default value is "display". + */ + class AnnotationEditorLayerBuilder { #annotationLayer = null; @@ -77,10 +83,10 @@ class AnnotationEditorLayerBuilder { } /** - * @param {PageViewport} viewport - * @param {string} intent (default value is 'display') + * @param {AnnotationEditorLayerBuilderRenderOptions} options + * @returns {Promise} */ - async render(viewport, intent = "display") { + async render({ viewport, intent = "display" }) { if (intent !== "display") { return; } @@ -140,6 +146,7 @@ class AnnotationEditorLayerBuilder { if (!this.div) { return; } + this.annotationEditorLayer.pause(/* on */ true); this.div.hidden = true; } @@ -148,6 +155,7 @@ class AnnotationEditorLayerBuilder { return; } this.div.hidden = false; + this.annotationEditorLayer.pause(/* on */ false); } } diff --git a/web/annotation_editor_params.js b/web/annotation_editor_params.js index a69206214b3c9..436902eedeafc 100644 --- a/web/annotation_editor_params.js +++ b/web/annotation_editor_params.js @@ -27,6 +27,7 @@ import { AnnotationEditorParamsType } from "pdfjs-lib"; * @property {HTMLButtonElement} editorStampAddImage * @property {HTMLInputElement} editorFreeHighlightThickness * @property {HTMLButtonElement} editorHighlightShowAll + * @property {HTMLButtonElement} editorSignatureAddSignature */ class AnnotationEditorParams { @@ -51,9 +52,12 @@ class AnnotationEditorParams { editorStampAddImage, editorFreeHighlightThickness, editorHighlightShowAll, + editorSignatureAddSignature, }) { + const { eventBus } = this; + const dispatchEvent = (typeStr, value) => { - this.eventBus.dispatch("switchannotationeditorparams", { + eventBus.dispatch("switchannotationeditorparams", { source: this, type: AnnotationEditorParamsType[typeStr], value, @@ -75,7 +79,7 @@ class AnnotationEditorParams { dispatchEvent("INK_OPACITY", this.valueAsNumber); }); editorStampAddImage.addEventListener("click", () => { - this.eventBus.dispatch("reporttelemetry", { + eventBus.dispatch("reporttelemetry", { source: this, details: { type: "editing", @@ -92,8 +96,11 @@ class AnnotationEditorParams { this.setAttribute("aria-pressed", !checked); dispatchEvent("HIGHLIGHT_SHOW_ALL", !checked); }); + editorSignatureAddSignature.addEventListener("click", () => { + dispatchEvent("CREATE"); + }); - this.eventBus._on("annotationeditorparamschanged", evt => { + eventBus._on("annotationeditorparamschanged", evt => { for (const [type, value] of evt.details) { switch (type) { case AnnotationEditorParamsType.FREETEXT_SIZE: @@ -111,6 +118,12 @@ class AnnotationEditorParams { case AnnotationEditorParamsType.INK_OPACITY: editorInkOpacity.value = value; break; + case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: + eventBus.dispatch("mainhighlightcolorpickerupdatecolor", { + source: this, + value, + }); + break; case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: editorFreeHighlightThickness.value = value; break; diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index 3047adbb2ecfd..ea2ed830feee2 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -50,7 +50,7 @@ } .popupAnnotation .popup { - outline: calc(1.5px * var(--scale-factor)) solid CanvasText !important; + outline: calc(1.5px * var(--total-scale-factor)) solid CanvasText !important; background-color: ButtonFace !important; color: ButtonText !important; } @@ -67,7 +67,7 @@ } .popupAnnotation.focused .popup { - outline: calc(3px * var(--scale-factor)) solid Highlight !important; + outline: calc(3px * var(--total-scale-factor)) solid Highlight !important; } } @@ -108,7 +108,6 @@ white-space: nowrap; font: 10px sans-serif; line-height: 1.35; - user-select: none; } } @@ -118,6 +117,7 @@ pointer-events: auto; box-sizing: border-box; transform-origin: 0 0; + user-select: none; &:has(div.annotationContent) { canvas.annotationContent { @@ -169,7 +169,7 @@ background-image: var(--annotation-unfocused-field-background); border: 2px solid var(--input-unfocused-border-color); box-sizing: border-box; - font: calc(9px * var(--scale-factor)) sans-serif; + font: calc(9px * var(--total-scale-factor)) sans-serif; height: 100%; margin: 0; vertical-align: top; @@ -296,7 +296,7 @@ .popupAnnotation { position: absolute; - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); pointer-events: none; width: max-content; max-width: 45%; @@ -305,16 +305,17 @@ .popup { background-color: rgb(255 255 153); - box-shadow: 0 calc(2px * var(--scale-factor)) - calc(5px * var(--scale-factor)) rgb(136 136 136); - border-radius: calc(2px * var(--scale-factor)); + box-shadow: 0 calc(2px * var(--total-scale-factor)) + calc(5px * var(--total-scale-factor)) rgb(136 136 136); + border-radius: calc(2px * var(--total-scale-factor)); outline: 1.5px solid rgb(255 255 74); - padding: calc(6px * var(--scale-factor)); + padding: calc(6px * var(--total-scale-factor)); cursor: pointer; font: message-box; white-space: normal; word-wrap: break-word; pointer-events: auto; + user-select: text; } .popupAnnotation.focused .popup { @@ -322,7 +323,7 @@ } .popup * { - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); } .popup > .header { @@ -335,19 +336,19 @@ .popup > .header .popupDate { display: inline-block; - margin-left: calc(5px * var(--scale-factor)); + margin-left: calc(5px * var(--total-scale-factor)); width: fit-content; } .popupContent { border-top: 1px solid rgb(51 51 51); - margin-top: calc(2px * var(--scale-factor)); - padding-top: calc(2px * var(--scale-factor)); + margin-top: calc(2px * var(--total-scale-factor)); + padding-top: calc(2px * var(--total-scale-factor)); } .richText > * { white-space: pre-wrap; - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); } .popupTriggerArea { diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index 4961d6a4bd148..ba6ad0f9b822e 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -21,11 +21,18 @@ /** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ // eslint-disable-next-line max-len +/** @typedef {import("./struct_tree_layer_builder.js").StructTreeLayerBuilder} StructTreeLayerBuilder */ +// eslint-disable-next-line max-len /** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ // eslint-disable-next-line max-len /** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */ -import { AnnotationLayer } from "pdfjs-lib"; +import { + AnnotationLayer, + AnnotationType, + setLayerDimensions, + Util, +} from "pdfjs-lib"; import { PresentationModeState } from "./ui_utils.js"; /** @@ -47,11 +54,31 @@ import { PresentationModeState } from "./ui_utils.js"; * @property {function} [onAppend] */ +/** + * @typedef {Object} AnnotationLayerBuilderRenderOptions + * @property {PageViewport} viewport + * @property {string} [intent] - The default value is "display". + * @property {StructTreeLayerBuilder} [structTreeLayer] + */ + +/** + * @typedef {Object} InjectLinkAnnotationsOptions + * @property {Array} inferredLinks + * @property {PageViewport} viewport + * @property {StructTreeLayerBuilder} [structTreeLayer] + */ + class AnnotationLayerBuilder { + #annotations = null; + + #externalHide = false; + #onAppend = null; #eventAbortController = null; + #linksInjected = false; + /** * @param {AnnotationLayerBuilderOptions} options */ @@ -91,13 +118,11 @@ class AnnotationLayerBuilder { } /** - * @param {PageViewport} viewport - * @param {Object} options - * @param {string} intent (default value is 'display') + * @param {AnnotationLayerBuilderRenderOptions} options * @returns {Promise} A promise that is resolved when rendering of the * annotations is complete. */ - async render(viewport, options, intent = "display") { + async render({ viewport, intent = "display", structTreeLayer = null }) { if (this.div) { if (this._cancelled || !this.annotationLayer) { return; @@ -126,19 +151,13 @@ class AnnotationLayerBuilder { this.#onAppend?.(div); if (annotations.length === 0) { - this.hide(); + this.#annotations = annotations; + + this.hide(/* internal = */ true); return; } - this.annotationLayer = new AnnotationLayer({ - div, - accessibilityManager: this._accessibilityManager, - annotationCanvasMap: this._annotationCanvasMap, - annotationEditorUIManager: this._annotationEditorUIManager, - page: this.pdfPage, - viewport: viewport.clone({ dontFlip: true }), - structTreeLayer: options?.structTreeLayer || null, - }); + this.#initAnnotationLayer(viewport, structTreeLayer); await this.annotationLayer.render({ annotations, @@ -152,6 +171,8 @@ class AnnotationLayerBuilder { fieldObjects, }); + this.#annotations = annotations; + // Ensure that interactive form elements in the annotationLayer are // disabled while PresentationMode is active (see issue 12232). if (this.linkService.isInPresentationMode) { @@ -170,6 +191,18 @@ class AnnotationLayerBuilder { } } + #initAnnotationLayer(viewport, structTreeLayer) { + this.annotationLayer = new AnnotationLayer({ + div: this.div, + accessibilityManager: this._accessibilityManager, + annotationCanvasMap: this._annotationCanvasMap, + annotationEditorUIManager: this._annotationEditorUIManager, + page: this.pdfPage, + viewport: viewport.clone({ dontFlip: true }), + structTreeLayer, + }); + } + cancel() { this._cancelled = true; @@ -177,7 +210,8 @@ class AnnotationLayerBuilder { this.#eventAbortController = null; } - hide() { + hide(internal = false) { + this.#externalHide = !internal; if (!this.div) { return; } @@ -188,6 +222,46 @@ class AnnotationLayerBuilder { return !!this.annotationLayer?.hasEditableAnnotations(); } + /** + * @param {InjectLinkAnnotationsOptions} options + * @returns {Promise} A promise that is resolved when the inferred links + * are added to the annotation layer. + */ + async injectLinkAnnotations({ + inferredLinks, + viewport, + structTreeLayer = null, + }) { + if (this.#annotations === null) { + throw new Error( + "`render` method must be called before `injectLinkAnnotations`." + ); + } + if (this._cancelled || this.#linksInjected) { + return; + } + this.#linksInjected = true; + + const newLinks = this.#annotations.length + ? this.#checkInferredLinks(inferredLinks) + : inferredLinks; + + if (!newLinks.length) { + return; + } + + if (!this.annotationLayer) { + this.#initAnnotationLayer(viewport, structTreeLayer); + setLayerDimensions(this.div, viewport); + } + + await this.annotationLayer.addLinkAnnotations(newLinks, this.linkService); + // Don't show the annotation layer if it was explicitly hidden previously. + if (!this.#externalHide) { + this.div.hidden = false; + } + } + #updatePresentationModeState(state) { if (!this.div) { return; @@ -210,6 +284,75 @@ class AnnotationLayerBuilder { section.inert = disableFormElements; } } + + #checkInferredLinks(inferredLinks) { + function annotationRects(annot) { + if (!annot.quadPoints) { + return [annot.rect]; + } + const rects = []; + for (let i = 2, ii = annot.quadPoints.length; i < ii; i += 8) { + const trX = annot.quadPoints[i]; + const trY = annot.quadPoints[i + 1]; + const blX = annot.quadPoints[i + 2]; + const blY = annot.quadPoints[i + 3]; + rects.push([blX, blY, trX, trY]); + } + return rects; + } + + function intersectAnnotations(annot1, annot2) { + const intersections = []; + const annot1Rects = annotationRects(annot1); + const annot2Rects = annotationRects(annot2); + for (const rect1 of annot1Rects) { + for (const rect2 of annot2Rects) { + const intersection = Util.intersect(rect1, rect2); + if (intersection) { + intersections.push(intersection); + } + } + } + return intersections; + } + + function areaRects(rects) { + let totalArea = 0; + for (const rect of rects) { + totalArea += Math.abs((rect[2] - rect[0]) * (rect[3] - rect[1])); + } + return totalArea; + } + + return inferredLinks.filter(link => { + let linkAreaRects; + + for (const annotation of this.#annotations) { + if ( + annotation.annotationType !== AnnotationType.LINK || + !annotation.url + ) { + continue; + } + // TODO: Add a test case to verify that we can find the intersection + // between two annotations with quadPoints properly. + const intersections = intersectAnnotations(annotation, link); + + if (intersections.length === 0) { + continue; + } + linkAreaRects ??= areaRects(annotationRects(link)); + + if ( + areaRects(intersections) / linkAreaRects > + 0.5 /* If the overlap is more than 50%. */ + ) { + return false; + } + } + return true; + }); + } } export { AnnotationLayerBuilder }; diff --git a/web/app.js b/web/app.js index ba06bb5e968c4..4ad53c4752257 100644 --- a/web/app.js +++ b/web/app.js @@ -50,10 +50,11 @@ import { InvalidPDFException, isDataScheme, isPdfFile, - MissingPDFException, PDFWorker, + ResponseException, shadow, - UnexpectedResponseException, + // stopEvent, + TouchManager, version, } from "pdfjs-lib"; import { AppOptions, OptionKind } from "./app_options.js"; @@ -68,6 +69,7 @@ import { AltTextManager } from "web-alt_text_manager"; import { AnnotationEditorParams } from "web-annotation_editor_params"; import { CaretBrowsingMode } from "./caret_browsing.js"; import { DownloadManager } from "web-download_manager"; +import { EditorUndoBar } from "./editor_undo_bar.js"; import { OverlayManager } from "./overlay_manager.js"; import { PasswordPrompt } from "./password_prompt.js"; import { PDFAttachmentViewer } from "web-pdf_attachment_viewer"; @@ -87,6 +89,7 @@ import { PDFThumbnailViewer } from "web-pdf_thumbnail_viewer"; import { PDFViewer } from "./pdf_viewer.js"; import { Preferences } from "web-preferences"; import { SecondaryToolbar } from "web-secondary_toolbar"; +import { SignatureManager } from "web-signature_manager"; import { Toolbar } from "web-toolbar"; import { ViewHistory } from "./view_history.js"; @@ -172,16 +175,17 @@ const PDFViewerApplication = { _saveInProgress: false, _wheelUnusedTicks: 0, _wheelUnusedFactor: 1, + _touchManager: null, _touchUnusedTicks: 0, _touchUnusedFactor: 1, _PDFBug: null, _hasAnnotationEditors: false, _title: document.title, _printAnnotationStoragePromise: null, - _touchInfo: null, _isCtrlKeyDown: false, _caretBrowsing: null, _isScrolling: false, + editorUndoBar: null, // Called once when the document is loaded. async initialize(appConfig) { @@ -192,45 +196,46 @@ const PDFViewerApplication = { try { await this.preferences.initializedPromise; } catch (ex) { - console.error(`initialize: "${ex.message}".`); + console.error("initialize:", ex); } if (AppOptions.get("pdfBugEnabled")) { await this._parseHashParams(); } - if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { - let mode; - switch (AppOptions.get("viewerCssTheme")) { - case 1: - mode = "is-light"; - break; - case 2: - mode = "is-dark"; - break; - } - if (mode) { - document.documentElement.classList.add(mode); + let mode; + switch (AppOptions.get("viewerCssTheme")) { + case 1: + mode = "is-light"; + break; + case 2: + mode = "is-dark"; + break; + } + if (mode) { + document.documentElement.classList.add(mode); + } + + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + if (AppOptions.get("enableFakeMLManager")) { + this.mlManager = + MLManager.getFakeMLManager?.({ + enableGuessAltText: AppOptions.get("enableGuessAltText"), + enableAltTextModelDownload: AppOptions.get( + "enableAltTextModelDownload" + ), + }) || null; } - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { - if (AppOptions.get("enableFakeMLManager")) { - this.mlManager = - MLManager.getFakeMLManager?.({ - enableGuessAltText: AppOptions.get("enableGuessAltText"), - enableAltTextModelDownload: AppOptions.get( - "enableAltTextModelDownload" - ), - }) || null; - } + } else if (PDFJSDev.test("MOZCENTRAL")) { + if (AppOptions.get("enableAltText")) { + // We want to load the image-to-text AI engine as soon as possible. + this.mlManager = new MLManager({ + enableGuessAltText: AppOptions.get("enableGuessAltText"), + enableAltTextModelDownload: AppOptions.get( + "enableAltTextModelDownload" + ), + altTextLearnMoreUrl: AppOptions.get("altTextLearnMoreUrl"), + }); } - } else if (AppOptions.get("enableAltText")) { - // We want to load the image-to-text AI engine as soon as possible. - this.mlManager = new MLManager({ - enableGuessAltText: AppOptions.get("enableGuessAltText"), - enableAltTextModelDownload: AppOptions.get( - "enableAltTextModelDownload" - ), - altTextLearnMoreUrl: AppOptions.get("altTextLearnMoreUrl"), - }); } // Ensure that the `L10n`-instance has been initialized before creating @@ -296,7 +301,7 @@ const PDFViewerApplication = { await __non_webpack_import__(PDFWorker.workerSrc); } } catch (ex) { - console.error(`_parseHashParams: "${ex.message}".`); + console.error("_parseHashParams:", ex); } } if (params.has("textlayer")) { @@ -312,7 +317,7 @@ const PDFViewerApplication = { await loadPDFBug(); this._PDFBug.loadCSS(); } catch (ex) { - console.error(`_parseHashParams: "${ex.message}".`); + console.error("_parseHashParams:", ex); } break; } @@ -325,7 +330,7 @@ const PDFViewerApplication = { await loadPDFBug(); this._PDFBug.init(mainContainer, enabled); } catch (ex) { - console.error(`_parseHashParams: "${ex.message}".`); + console.error("_parseHashParams:", ex); } } // It is not possible to change locale for the (various) extension builds. @@ -349,7 +354,9 @@ const PDFViewerApplication = { // Set some specific preferences for tests. if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { Object.assign(opts, { + docBaseUrl: x => x, enableAltText: x => x === "true", + enableAutoLinking: x => x === "true", enableFakeMLManager: x => x === "true", enableGuessAltText: x => x === "true", enableUpdatedAddImage: x => x === "true", @@ -374,7 +381,8 @@ const PDFViewerApplication = { * @private */ async _initializeViewerComponents() { - const { appConfig, externalServices, l10n } = this; + const { appConfig, externalServices, l10n, mlManager } = this; + const abortSignal = this._globalAbortController.signal; const eventBus = typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL") @@ -385,7 +393,7 @@ const PDFViewerApplication = { ) : new EventBus(); this.eventBus = AppOptions.eventBus = eventBus; - this.mlManager?.setEventBus(eventBus, this._globalAbortController.signal); + mlManager?.setEventBus(eventBus, abortSignal); this.overlayManager = new OverlayManager(); @@ -451,6 +459,24 @@ const PDFViewerApplication = { : null; } + if (appConfig.editorUndoBar) { + this.editorUndoBar = new EditorUndoBar(appConfig.editorUndoBar, eventBus); + } + + const signatureManager = + AppOptions.get("enableSignatureEditor") && appConfig.addSignatureDialog + ? new SignatureManager( + appConfig.addSignatureDialog, + appConfig.editSignatureDialog, + appConfig.annotationEditorParams?.editorSignatureAddSignature || + null, + this.overlayManager, + l10n, + externalServices.createSignatureStorage(eventBus, abortSignal), + eventBus + ) + : null; + const enableHWA = AppOptions.get("enableHWA"); const pdfViewer = new PDFViewer({ container, @@ -460,6 +486,8 @@ const PDFViewerApplication = { linkService: pdfLinkService, downloadManager, altTextManager, + signatureManager, + editorUndoBar: this.editorUndoBar, findController, scriptingManager: AppOptions.get("enableScripting") && pdfScriptingManager, @@ -478,11 +506,14 @@ const PDFViewerApplication = { imageResourcesPath: AppOptions.get("imageResourcesPath"), enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"), maxCanvasPixels: AppOptions.get("maxCanvasPixels"), + enableDetailCanvas: AppOptions.get("enableDetailCanvas"), enablePermissions: AppOptions.get("enablePermissions"), pageColors, - mlManager: this.mlManager, - abortSignal: this._globalAbortController.signal, + mlManager, + abortSignal, enableHWA, + supportsPinchToZoom: this.supportsPinchToZoom, + enableAutoLinking: AppOptions.get("enableAutoLinking"), }); this.pdfViewer = pdfViewer; @@ -497,7 +528,7 @@ const PDFViewerApplication = { renderingQueue: pdfRenderingQueue, linkService: pdfLinkService, pageColors, - abortSignal: this._globalAbortController.signal, + abortSignal, enableHWA, }); pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); @@ -527,6 +558,10 @@ const PDFViewerApplication = { typeof AbortSignal.any === "function") && annotationEditorMode !== AnnotationEditorType.DISABLE ) { + const editorSignatureButton = appConfig.toolbar?.editorSignatureButton; + if (editorSignatureButton && AppOptions.get("enableSignatureEditor")) { + editorSignatureButton.parentElement.hidden = false; + } this.annotationEditorParams = new AnnotationEditorParams( appConfig.annotationEditorParams, eventBus @@ -538,15 +573,12 @@ const PDFViewerApplication = { } } - if ( - this.mlManager && - appConfig.secondaryToolbar?.imageAltTextSettingsButton - ) { + if (mlManager && appConfig.secondaryToolbar?.imageAltTextSettingsButton) { this.imageAltTextSettings = new ImageAltTextSettings( appConfig.altTextSettingsDialog, this.overlayManager, eventBus, - this.mlManager + mlManager ); } @@ -716,8 +748,7 @@ const PDFViewerApplication = { if (item.type === "application/pdf") { evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === "copy" ? "copy" : "move"; - evt.preventDefault(); - evt.stopPropagation(); + stopEvent(evt); return; } } @@ -726,8 +757,7 @@ const PDFViewerApplication = { if (evt.dataTransfer.files?.[0].type !== "application/pdf") { return; } - evt.preventDefault(); - evt.stopPropagation(); + stopEvent(evt); eventBus.dispatch("fileinputchange", { source: this, fileInput: evt.dataTransfer, @@ -812,6 +842,29 @@ const PDFViewerApplication = { this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE; }, + touchPinchCallback(origin, prevDistance, distance) { + if (this.supportsPinchToZoom) { + const newScaleFactor = this._accumulateFactor( + this.pdfViewer.currentScale, + distance / prevDistance, + "_touchUnusedFactor" + ); + this.updateZoom(null, newScaleFactor, origin); + } else { + const PIXELS_PER_LINE_SCALE = 30; + const ticks = this._accumulateTicks( + (distance - prevDistance) / PIXELS_PER_LINE_SCALE, + "_touchUnusedTicks" + ); + this.updateZoom(ticks, null, origin); + } + }, + + touchPinchEndCallback() { + this._touchUnusedTicks = 0; + this._touchUnusedFactor = 1; + }, + get pagesCount() { return this.pdfDocument ? this.pdfDocument.numPages : 0; }, @@ -1079,10 +1132,10 @@ const PDFViewerApplication = { let key = "pdfjs-loading-error"; if (reason instanceof InvalidPDFException) { key = "pdfjs-invalid-file-error"; - } else if (reason instanceof MissingPDFException) { - key = "pdfjs-missing-file-error"; - } else if (reason instanceof UnexpectedResponseException) { - key = "pdfjs-unexpected-response-error"; + } else if (reason instanceof ResponseException) { + key = reason.missing + ? "pdfjs-missing-file-error" + : "pdfjs-unexpected-response-error"; } return this._documentError(key, { message: reason.message }).then( () => { @@ -1115,7 +1168,7 @@ const PDFViewerApplication = { this.downloadManager.download(data, this._downloadUrl, this._docFilename); } catch (reason) { // When the PDF document isn't ready, fallback to a "regular" download. - console.error(`Error when saving the document: ${reason.message}`); + console.error(`Error when saving the document:`, reason); await this.download(); } finally { await this.pdfScriptingManager.dispatchDidSave(); @@ -2030,6 +2083,20 @@ const PDFViewerApplication = { _windowAbortController: { signal }, } = this; + if ( + (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || + typeof AbortSignal.any === "function" + ) { + this._touchManager = new TouchManager({ + container: window, + isPinchingDisabled: () => pdfViewer.isInPresentationMode, + isPinchingStopped: () => this.overlayManager?.active, + onPinching: this.touchPinchCallback.bind(this), + onPinchEnd: this.touchPinchEndCallback.bind(this), + signal, + }); + } + function addWindowResolutionChange(evt = null) { if (evt) { pdfViewer.refresh(); @@ -2048,18 +2115,6 @@ const PDFViewerApplication = { passive: false, signal, }); - window.addEventListener("touchstart", onTouchStart.bind(this), { - passive: false, - signal, - }); - window.addEventListener("touchmove", onTouchMove.bind(this), { - passive: false, - signal, - }); - window.addEventListener("touchend", onTouchEnd.bind(this), { - passive: false, - signal, - }); window.addEventListener("click", onClick.bind(this), { signal }); window.addEventListener("keydown", onKeyDown.bind(this), { signal }); window.addEventListener("keyup", onKeyUp.bind(this), { signal }); @@ -2158,6 +2213,7 @@ const PDFViewerApplication = { unbindWindowEvents() { this._windowAbortController?.abort(); this._windowAbortController = null; + this._touchManager = null; }, /** @@ -2288,7 +2344,7 @@ function onPageRender({ pageNumber }) { } } -function onPageRendered({ pageNumber, error }) { +function onPageRendered({ pageNumber, isDetailView, error }) { // If the page is still visible when it has finished rendering, // ensure that the page number input loading indicator is hidden. if (pageNumber === this.page) { @@ -2296,7 +2352,7 @@ function onPageRendered({ pageNumber, error }) { } // Use the rendered page to set the corresponding thumbnail image. - if (this.pdfSidebar?.visibleView === SidebarView.THUMBS) { + if (!isDetailView && this.pdfSidebar?.visibleView === SidebarView.THUMBS) { const pageView = this.pdfViewer.getPageView(/* index = */ pageNumber - 1); const thumbnailView = this.pdfThumbnailViewer?.getThumbnail( /* index = */ pageNumber - 1 @@ -2638,131 +2694,7 @@ function onWheel(evt) { } } -function onTouchStart(evt) { - if (this.pdfViewer.isInPresentationMode || evt.touches.length < 2) { - return; - } - evt.preventDefault(); - - if (evt.touches.length !== 2 || this.overlayManager.active) { - this._touchInfo = null; - return; - } - - let [touch0, touch1] = evt.touches; - if (touch0.identifier > touch1.identifier) { - [touch0, touch1] = [touch1, touch0]; - } - this._touchInfo = { - touch0X: touch0.pageX, - touch0Y: touch0.pageY, - touch1X: touch1.pageX, - touch1Y: touch1.pageY, - }; -} - -function onTouchMove(evt) { - if (!this._touchInfo || evt.touches.length !== 2) { - return; - } - - const { pdfViewer, _touchInfo, supportsPinchToZoom } = this; - let [touch0, touch1] = evt.touches; - if (touch0.identifier > touch1.identifier) { - [touch0, touch1] = [touch1, touch0]; - } - const { pageX: page0X, pageY: page0Y } = touch0; - const { pageX: page1X, pageY: page1Y } = touch1; - const { - touch0X: pTouch0X, - touch0Y: pTouch0Y, - touch1X: pTouch1X, - touch1Y: pTouch1Y, - } = _touchInfo; - - if ( - Math.abs(pTouch0X - page0X) <= 1 && - Math.abs(pTouch0Y - page0Y) <= 1 && - Math.abs(pTouch1X - page1X) <= 1 && - Math.abs(pTouch1Y - page1Y) <= 1 - ) { - // Touches are really too close and it's hard do some basic - // geometry in order to guess something. - return; - } - - _touchInfo.touch0X = page0X; - _touchInfo.touch0Y = page0Y; - _touchInfo.touch1X = page1X; - _touchInfo.touch1Y = page1Y; - - if (pTouch0X === page0X && pTouch0Y === page0Y) { - // First touch is fixed, if the vectors are collinear then we've a pinch. - const v1X = pTouch1X - page0X; - const v1Y = pTouch1Y - page0Y; - const v2X = page1X - page0X; - const v2Y = page1Y - page0Y; - const det = v1X * v2Y - v1Y * v2X; - // 0.02 is approximatively sin(0.15deg). - if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) { - return; - } - } else if (pTouch1X === page1X && pTouch1Y === page1Y) { - // Second touch is fixed, if the vectors are collinear then we've a pinch. - const v1X = pTouch0X - page1X; - const v1Y = pTouch0Y - page1Y; - const v2X = page0X - page1X; - const v2Y = page0Y - page1Y; - const det = v1X * v2Y - v1Y * v2X; - if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) { - return; - } - } else { - const diff0X = page0X - pTouch0X; - const diff1X = page1X - pTouch1X; - const diff0Y = page0Y - pTouch0Y; - const diff1Y = page1Y - pTouch1Y; - const dotProduct = diff0X * diff1X + diff0Y * diff1Y; - if (dotProduct >= 0) { - // The two touches go in almost the same direction. - return; - } - } - - evt.preventDefault(); - - const origin = [(page0X + page1X) / 2, (page0Y + page1Y) / 2]; - const distance = Math.hypot(page0X - page1X, page0Y - page1Y) || 1; - const pDistance = Math.hypot(pTouch0X - pTouch1X, pTouch0Y - pTouch1Y) || 1; - if (supportsPinchToZoom) { - const newScaleFactor = this._accumulateFactor( - pdfViewer.currentScale, - distance / pDistance, - "_touchUnusedFactor" - ); - this.updateZoom(null, newScaleFactor, origin); - } else { - const PIXELS_PER_LINE_SCALE = 30; - const ticks = this._accumulateTicks( - (distance - pDistance) / PIXELS_PER_LINE_SCALE, - "_touchUnusedTicks" - ); - this.updateZoom(ticks, null, origin); - } -} - -function onTouchEnd(evt) { - if (!this._touchInfo) { - return; - } - - evt.preventDefault(); - this._touchInfo = null; - this._touchUnusedTicks = 0; - this._touchUnusedFactor = 1; -} - -function onClick(evt) { +function closeSecondaryToolbar(evt) { if (!this.secondaryToolbar?.isOpen) { return; } @@ -2779,6 +2711,20 @@ function onClick(evt) { } } +function closeEditorUndoBar(evt) { + if (!this.editorUndoBar?.isOpen) { + return; + } + if (this.appConfig.secondaryToolbar?.toolbar.contains(evt.target)) { + this.editorUndoBar.hide(); + } +} + +function onClick(evt) { + closeSecondaryToolbar.call(this, evt); + closeEditorUndoBar.call(this, evt); +} + function onKeyUp(evt) { // evt.ctrlKey is false hence we use evt.key. if (evt.key === "Control") { @@ -2789,6 +2735,20 @@ function onKeyUp(evt) { function onKeyDown(evt) { this._isCtrlKeyDown = evt.key === "Control"; + if ( + this.editorUndoBar?.isOpen && + evt.keyCode !== 9 && + evt.keyCode !== 16 && + !( + (evt.keyCode === 13 || evt.keyCode === 32) && + getActiveOrFocusedElement() === this.appConfig.editorUndoBar.undoButton + ) + ) { + // Hide undo bar on keypress except for Shift, Tab, Shift+Tab. + // Also avoid hiding if the undo button is triggered. + this.editorUndoBar.hide(); + } + if (this.overlayManager.active) { return; } diff --git a/web/app_options.js b/web/app_options.js index 4c7f420900e20..0d14fbcef8866 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -195,6 +195,16 @@ const defaultOptions = { value: true, kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH, }, + enableAutoLinking: { + /** @type {boolean} */ + value: typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL"), + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, + enableDetailCanvas: { + /** @type {boolean} */ + value: true, + kind: OptionKind.VIEWER, + }, enableGuessAltText: { /** @type {boolean} */ value: true, @@ -228,6 +238,11 @@ const defaultOptions = { value: typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME"), kind: OptionKind.VIEWER + OptionKind.PREFERENCE, }, + enableSignatureEditor: { + /** @type {boolean} */ + value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"), + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, enableUpdatedAddImage: { // We'll probably want to make some experiments before enabling this // in Firefox release, but it has to be temporary. @@ -319,6 +334,11 @@ const defaultOptions = { value: 1, kind: OptionKind.VIEWER + OptionKind.PREFERENCE, }, + viewerCssTheme: { + /** @type {number} */ + value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME") ? 2 : 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, viewOnLoad: { /** @type {boolean} */ value: 0, @@ -431,6 +451,14 @@ const defaultOptions = { value: 1, kind: OptionKind.API, }, + wasmUrl: { + /** @type {string} */ + value: + typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL") + ? "resource://pdf.js/web/wasm/" + : "../web/wasm/", + kind: OptionKind.API, + }, workerPort: { /** @type {Object} */ diff --git a/web/autolinker.js b/web/autolinker.js new file mode 100644 index 0000000000000..fe28235c2bdd4 --- /dev/null +++ b/web/autolinker.js @@ -0,0 +1,184 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnnotationType, createValidAbsoluteUrl, Util } from "pdfjs-lib"; +import { getOriginalIndex, normalize } from "./pdf_find_controller.js"; + +function DOMRectToPDF({ width, height, left, top }, pdfPageView) { + if (width === 0 || height === 0) { + return null; + } + + const pageBox = pdfPageView.textLayer.div.getBoundingClientRect(); + const bottomLeft = pdfPageView.getPagePoint( + left - pageBox.left, + top - pageBox.top + ); + const topRight = pdfPageView.getPagePoint( + left - pageBox.left + width, + top - pageBox.top + height + ); + + return Util.normalizeRect([ + bottomLeft[0], + bottomLeft[1], + topRight[0], + topRight[1], + ]); +} + +function calculateLinkPosition(range, pdfPageView) { + const rangeRects = range.getClientRects(); + if (rangeRects.length === 1) { + return { rect: DOMRectToPDF(rangeRects[0], pdfPageView) }; + } + + const rect = [Infinity, Infinity, -Infinity, -Infinity]; + const quadPoints = []; + let i = 0; + for (const domRect of rangeRects) { + const normalized = DOMRectToPDF(domRect, pdfPageView); + if (normalized === null) { + continue; + } + + quadPoints[i] = quadPoints[i + 4] = normalized[0]; + quadPoints[i + 1] = quadPoints[i + 3] = normalized[3]; + quadPoints[i + 2] = quadPoints[i + 6] = normalized[2]; + quadPoints[i + 5] = quadPoints[i + 7] = normalized[1]; + + rect[0] = Math.min(rect[0], normalized[0]); + rect[1] = Math.min(rect[1], normalized[1]); + rect[2] = Math.max(rect[2], normalized[2]); + rect[3] = Math.max(rect[3], normalized[3]); + + i += 8; + } + return { quadPoints, rect }; +} + +/** + * Given a DOM node `container` and an index into its text contents `offset`, + * returns a pair consisting of text node that the `offset` actually points + * to, together with the offset relative to that text node. + * When the offset points at the boundary between two node, the result will + * point to the first text node in depth-first traversal order. + * + * For example, given this DOM: + *

abcdefghi

+ * + * textPosition(p, 0) -> [#text "abc", 0] (before `a`) + * textPosition(p, 2) -> [#text "abc", 2] (between `b` and `c`) + * textPosition(p, 3) -> [#text "abc", 3] (after `c`) + * textPosition(p, 5) -> [#text "def", 2] (between `e` and `f`) + * textPosition(p, 6) -> [#text "def", 3] (after `f`) + */ +function textPosition(container, offset) { + let currentContainer = container; + do { + if (currentContainer.nodeType === Node.TEXT_NODE) { + const currentLength = currentContainer.textContent.length; + if (offset <= currentLength) { + return [currentContainer, offset]; + } + offset -= currentLength; + } else if (currentContainer.firstChild) { + currentContainer = currentContainer.firstChild; + continue; + } + + while (!currentContainer.nextSibling && currentContainer !== container) { + currentContainer = currentContainer.parentNode; + } + if (currentContainer !== container) { + currentContainer = currentContainer.nextSibling; + } + } while (currentContainer !== container); + throw new Error("Offset is bigger than container's contents length."); +} + +function createLinkAnnotation({ url, index, length }, pdfPageView, id) { + const highlighter = pdfPageView._textHighlighter; + const [{ begin, end }] = highlighter._convertMatches([index], [length]); + + const range = new Range(); + range.setStart( + ...textPosition(highlighter.textDivs[begin.divIdx], begin.offset) + ); + range.setEnd(...textPosition(highlighter.textDivs[end.divIdx], end.offset)); + + return { + id: `inferred_link_${id}`, + unsafeUrl: url, + url, + annotationType: AnnotationType.LINK, + rotation: 0, + ...calculateLinkPosition(range, pdfPageView), + // Populated in the annotationLayer to avoid unnecessary object creation, + // since most inferred links overlap existing LinkAnnotations: + borderStyle: null, + }; +} + +class Autolinker { + static #index = 0; + + static #regex; + + static findLinks(text) { + // Regex can be tested and verified at https://regex101.com/r/rXoLiT/2. + this.#regex ??= + /\b(?:https?:\/\/|mailto:|www\.)(?:[\S--[\p{P}<>]]|\/|[\S--[\[\]]]+[\S--[\p{P}<>]])+|\b[\S--[@\p{Ps}\p{Pe}<>]]+@([\S--[\p{P}<>]]+(?:\.[\S--[\p{P}<>]]+)+)/gmv; + + const [normalizedText, diffs] = normalize(text); + const matches = normalizedText.matchAll(this.#regex); + const links = []; + for (const match of matches) { + const [url, emailDomain] = match; + let raw; + if ( + url.startsWith("www.") || + url.startsWith("http://") || + url.startsWith("https://") + ) { + raw = url; + } else if (URL.canParse(`http://${emailDomain}`)) { + raw = url.startsWith("mailto:") ? url : `mailto:${url}`; + } else { + continue; + } + const absoluteURL = createValidAbsoluteUrl(raw, null, { + addDefaultProtocol: true, + }); + if (absoluteURL) { + const [index, length] = getOriginalIndex( + diffs, + match.index, + url.length + ); + links.push({ url: absoluteURL.href, index, length }); + } + } + return links; + } + + static processLinks(pdfPageView) { + return this.findLinks( + pdfPageView._textHighlighter.textContentItemsStr.join("\n") + ).map(link => createLinkAnnotation(link, pdfPageView, this.#index++)); + } +} + +export { Autolinker }; diff --git a/web/base_pdf_page_view.js b/web/base_pdf_page_view.js new file mode 100644 index 0000000000000..fa37052a6e670 --- /dev/null +++ b/web/base_pdf_page_view.js @@ -0,0 +1,227 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RenderingCancelledException } from "pdfjs-lib"; +import { RenderingStates } from "./ui_utils.js"; + +class BasePDFPageView { + #enableHWA = false; + + #loadingId = null; + + #renderError = null; + + #renderingState = RenderingStates.INITIAL; + + #showCanvas = null; + + canvas = null; + + /** @type {null | HTMLDivElement} */ + div = null; + + eventBus = null; + + id = null; + + pageColors = null; + + renderingQueue = null; + + renderTask = null; + + resume = null; + + constructor(options) { + this.#enableHWA = + #enableHWA in options ? options.#enableHWA : options.enableHWA || false; + this.eventBus = options.eventBus; + this.id = options.id; + this.pageColors = options.pageColors || null; + this.renderingQueue = options.renderingQueue; + } + + get renderingState() { + return this.#renderingState; + } + + set renderingState(state) { + if (state === this.#renderingState) { + return; + } + this.#renderingState = state; + + if (this.#loadingId) { + clearTimeout(this.#loadingId); + this.#loadingId = null; + } + + switch (state) { + case RenderingStates.PAUSED: + this.div.classList.remove("loading"); + break; + case RenderingStates.RUNNING: + this.div.classList.add("loadingIcon"); + this.#loadingId = setTimeout(() => { + // Adding the loading class is slightly postponed in order to not have + // it with loadingIcon. + // If we don't do that the visibility of the background is changed but + // the transition isn't triggered. + this.div.classList.add("loading"); + this.#loadingId = null; + }, 0); + break; + case RenderingStates.INITIAL: + case RenderingStates.FINISHED: + this.div.classList.remove("loadingIcon", "loading"); + break; + } + } + + _createCanvas(onShow, hideUntilComplete = false) { + const { pageColors } = this; + const hasHCM = !!(pageColors?.background && pageColors?.foreground); + const prevCanvas = this.canvas; + + // In HCM, a final filter is applied on the canvas which means that + // before it's applied we've normal colors. Consequently, to avoid to + // have a final flash we just display it once all the drawing is done. + const updateOnFirstShow = !prevCanvas && !hasHCM && !hideUntilComplete; + + const canvas = (this.canvas = document.createElement("canvas")); + + this.#showCanvas = isLastShow => { + if (updateOnFirstShow) { + // Don't add the canvas until the first draw callback, or until + // drawing is complete when `!this.renderingQueue`, to prevent black + // flickering. + onShow(canvas); + this.#showCanvas = null; + return; + } + if (!isLastShow) { + return; + } + + if (prevCanvas) { + prevCanvas.replaceWith(canvas); + prevCanvas.width = prevCanvas.height = 0; + } else { + onShow(canvas); + } + }; + + const ctx = canvas.getContext("2d", { + alpha: false, + willReadFrequently: !this.#enableHWA, + }); + + return { canvas, prevCanvas, ctx }; + } + + #renderContinueCallback = cont => { + this.#showCanvas?.(false); + if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) { + this.renderingState = RenderingStates.PAUSED; + this.resume = () => { + this.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; + + _resetCanvas() { + const { canvas } = this; + if (!canvas) { + return; + } + canvas.remove(); + canvas.width = canvas.height = 0; + this.canvas = null; + } + + async _drawCanvas(options, onCancel, onFinish) { + const renderTask = (this.renderTask = this.pdfPage.render(options)); + renderTask.onContinue = this.#renderContinueCallback; + renderTask.onError = error => { + if (error instanceof RenderingCancelledException) { + onCancel(); + this.#renderError = null; + } + }; + + let error = null; + try { + await renderTask.promise; + this.#showCanvas?.(true); + } catch (e) { + // When zooming with a `drawingDelay` set, avoid temporarily showing + // a black canvas if rendering was cancelled before the `onContinue`- + // callback had been invoked at least once. + if (e instanceof RenderingCancelledException) { + return; + } + error = e; + + this.#showCanvas?.(true); + } finally { + this.#renderError = error; + + // The renderTask may have been replaced by a new one, so only remove + // the reference to the renderTask if it matches the one that is + // triggering this callback. + if (renderTask === this.renderTask) { + this.renderTask = null; + } + } + this.renderingState = RenderingStates.FINISHED; + + onFinish(renderTask); + + if (error) { + throw error; + } + } + + cancelRendering({ cancelExtraDelay = 0 } = {}) { + if (this.renderTask) { + this.renderTask.cancel(cancelExtraDelay); + this.renderTask = null; + } + this.resume = null; + } + + dispatchPageRender() { + this.eventBus.dispatch("pagerender", { + source: this, + pageNumber: this.id, + }); + } + + dispatchPageRendered(cssTransform, isDetailView) { + this.eventBus.dispatch("pagerendered", { + source: this, + pageNumber: this.id, + cssTransform, + isDetailView, + timestamp: performance.now(), + error: this.#renderError, + }); + } +} + +export { BasePDFPageView }; diff --git a/web/chromecom.js b/web/chromecom.js index 1ece304791c1d..6129fa2dd4329 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -19,6 +19,11 @@ import { BaseExternalServices } from "./external_services.js"; import { BasePreferences } from "./preferences.js"; import { GenericL10n } from "./genericl10n.js"; import { GenericScripting } from "./generic_scripting.js"; +import { SignatureStorage } from "./generic_signature_storage.js"; + +// These strings are from chrome/app/resources/generated_resources_*.xtb. +// eslint-disable-next-line sort-imports +import i18nFileAccessLabels from "./chrome-i18n-allow-access-to-file-urls.json" with { type: "json" }; if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME")) { throw new Error( @@ -194,11 +199,8 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) { // Use Chrome's definition of UI language instead of PDF.js's #lang=..., // because the shown string should match the UI at chrome://extensions. - // These strings are from chrome/app/resources/generated_resources_*.xtb. - const i18nFileAccessLabel = PDFJSDev.json( - "$ROOT/web/chrome-i18n-allow-access-to-file-urls.json" - )[chrome.i18n.getUILanguage?.()]; - + const i18nFileAccessLabel = + i18nFileAccessLabels[chrome.i18n.getUILanguage?.()]; if (i18nFileAccessLabel) { document.getElementById("chrome-file-access-label").textContent = i18nFileAccessLabel; @@ -419,6 +421,10 @@ class ExternalServices extends BaseExternalServices { createScripting() { return new GenericScripting(AppOptions.get("sandboxBundleSrc")); } + + createSignatureStorage(eventBus, signal) { + return new SignatureStorage(eventBus, signal); + } } class MLManager { diff --git a/web/debugger.mjs b/web/debugger.mjs index 59c1871b3eb32..c85b31f7a49e2 100644 --- a/web/debugger.mjs +++ b/web/debugger.mjs @@ -398,9 +398,7 @@ class Stepper { } getNextBreakPoint() { - this.breakPoints.sort(function (a, b) { - return a - b; - }); + this.breakPoints.sort((a, b) => a - b); for (const breakPoint of this.breakPoints) { if (breakPoint > this.currentIdx) { return breakPoint; @@ -487,9 +485,7 @@ const Stats = (function Stats() { statsDiv.textContent = stat.toString(); wrapper.append(title, statsDiv); stats.push({ pageNumber, div: wrapper }); - stats.sort(function (a, b) { - return a.pageNumber - b.pageNumber; - }); + stats.sort((a, b) => a.pageNumber - b.pageNumber); clear(this.panel); for (const entry of stats) { this.panel.append(entry.div); diff --git a/web/dialog.css b/web/dialog.css index c36393fc35011..e45b6a8467307 100644 --- a/web/dialog.css +++ b/web/dialog.css @@ -20,8 +20,6 @@ --text-primary-color: #15141a; --text-secondary-color: #5b5b66; --hover-filter: brightness(0.9); - --focus-ring-color: #0060df; - --focus-ring-outline: 2px solid var(--focus-ring-color); --link-fg-color: #0060df; --link-hover-fg-color: #0250bb; --separator-color: #f0f0f4; @@ -49,13 +47,15 @@ --button-primary-hover-fg-color: var(--button-primary-fg-color); --button-primary-hover-border-color: var(--button-primary-hover-bg-color); + --button-disabled-bg-color: color-mix(in srgb, currentcolor, transparent 60%); + --button-disabled-fg-color: var(--button-disabled-bg-color); + @media (prefers-color-scheme: dark) { --dialog-bg-color: #1c1b22; --dialog-border-color: #1c1b22; --dialog-shadow: 0 2px 14px 0 #15141a; --text-primary-color: #fbfbfe; --text-secondary-color: #cfcfd8; - --focus-ring-color: #0df; --hover-filter: brightness(1.4); --link-fg-color: #0df; --link-hover-fg-color: #80ebff; @@ -79,7 +79,6 @@ --text-primary-color: CanvasText; --text-secondary-color: CanvasText; --hover-filter: none; - --focus-ring-color: ButtonBorder; --link-fg-color: LinkText; --link-hover-fg-color: LinkText; --separator-color: CanvasText; @@ -103,6 +102,9 @@ --button-primary-fg-color: ButtonFace; --button-primary-hover-bg-color: AccentColor; --button-primary-hover-fg-color: AccentColorText; + + --button-disabled-bg-color: GrayText; + --button-disabled-fg-color: ButtonFace; } font: message-box; @@ -199,7 +201,7 @@ } } - button:not(:is(.toggle-button, .closeButton)) { + button:not(:is(.toggle-button, .closeButton, .clearInputButton)) { border-radius: 4px; border: 1px solid; font: menu; @@ -213,6 +215,10 @@ filter: var(--hover-filter); } + > span { + color: inherit; + } + &.secondaryButton { color: var(--button-secondary-fg-color); background-color: var(--button-secondary-bg-color); @@ -237,6 +243,13 @@ border-color: var(--button-primary-hover-border-color); } } + + &:disabled { + color: var(--button-disabled-fg-color) !important; + background-color: var(--button-disabled-bg-color); + border-color: var(--button-disabled-bg-color); + pointer-events: none; + } } a { @@ -270,29 +283,17 @@ } .messageBar { - --message-bar-warning-icon: url(images/messageBar_warning.svg); - --closing-button-icon: url(images/messageBar_closingButton.svg); - --message-bar-bg-color: #ffebcd; --message-bar-fg-color: #15141a; --message-bar-border-color: rgb(0 0 0 / 0.08); + --message-bar-icon: url(images/messageBar_warning.svg); --message-bar-icon-color: #cd411e; - --message-bar-close-button-border-radius: 4px; - --message-bar-close-button-border: none; - --message-bar-close-button-color: var(--text-primary-color); - --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); - --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); - --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); - --message-bar-close-button-color-hover: var(--text-primary-color); @media (prefers-color-scheme: dark) { --message-bar-bg-color: #5a3100; --message-bar-fg-color: #fbfbfe; --message-bar-border-color: rgb(255 255 255 / 0.08); --message-bar-icon-color: #e49c49; - --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); - --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); - --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); } @media screen and (forced-colors: active) { @@ -300,43 +301,14 @@ --message-bar-fg-color: CanvasText; --message-bar-border-color: CanvasText; --message-bar-icon-color: CanvasText; - --message-bar-close-button-color: ButtonText; - --message-bar-close-button-border: 1px solid ButtonText; - --message-bar-close-button-hover-bg-color: ButtonText; - --message-bar-close-button-active-bg-color: ButtonText; - --message-bar-close-button-focus-bg-color: ButtonText; - --message-bar-close-button-color-hover: HighlightText; } - display: flex; - position: relative; - padding: 12px 8px 12px 0; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 8px; align-self: stretch; - border-radius: 4px; - border: 1px solid var(--message-bar-border-color); - background: var(--message-bar-bg-color); - color: var(--message-bar-fg-color); - > div { - display: flex; - padding-inline-start: 16px; - align-items: flex-start; - gap: 8px; - align-self: stretch; - - &::before { - content: ""; - display: inline-block; - width: 16px; - height: 16px; - mask-image: var(--message-bar-warning-icon); - mask-size: cover; - background-color: var(--message-bar-icon-color); + &::before, + > div { + margin-block: 4px; } > div { @@ -356,50 +328,6 @@ } } } - - .closeButton { - position: absolute; - width: 32px; - height: 32px; - inset-inline-end: 8px; - inset-block-start: 8px; - background: none; - border-radius: var(--message-bar-close-button-border-radius); - border: var(--message-bar-close-button-border); - - &::before { - content: ""; - display: inline-block; - width: 16px; - height: 16px; - mask-image: var(--closing-button-icon); - mask-size: cover; - background-color: var(--message-bar-close-button-color); - } - - &:is(:hover, :active, :focus)::before { - background-color: var(--message-bar-close-button-color-hover); - } - - &:hover { - background-color: var(--message-bar-close-button-hover-bg-color); - } - - &:active { - background-color: var(--message-bar-close-button-active-bg-color); - } - - &:focus { - background-color: var(--message-bar-close-button-focus-bg-color); - } - - > span { - display: inline-block; - width: 0; - height: 0; - overflow: hidden; - } - } } .toggler { diff --git a/web/download_manager.js b/web/download_manager.js index 255806f039837..26d6200b3cbed 100644 --- a/web/download_manager.js +++ b/web/download_manager.js @@ -93,7 +93,7 @@ class DownloadManager { window.open(viewerUrl); return true; } catch (ex) { - console.error(`openOrDownloadData: ${ex}`); + console.error("openOrDownloadData:", ex); // Release the `blobUrl`, since opening it failed, and fallback to // downloading the PDF file. URL.revokeObjectURL(blobUrl); diff --git a/web/draw_layer_builder.css b/web/draw_layer_builder.css index 2adeebc811243..5ea206ced7657 100644 --- a/web/draw_layer_builder.css +++ b/web/draw_layer_builder.css @@ -17,24 +17,48 @@ svg { transform: none; - &[data-main-rotation="90"] { - mask, - use:not(.clip, .mask) { - transform: matrix(0, 1, -1, 0, 1, 0); - } + &.moving { + z-index: 100000; } - &[data-main-rotation="180"] { - mask, - use:not(.clip, .mask) { - transform: matrix(-1, 0, 0, -1, 1, 1); + &.highlight, + &.highlightOutline { + &[data-main-rotation="90"] { + mask, + use:not(.clip, .mask) { + transform: matrix(0, 1, -1, 0, 1, 0); + } + } + + &[data-main-rotation="180"] { + mask, + use:not(.clip, .mask) { + transform: matrix(-1, 0, 0, -1, 1, 1); + } + } + + &[data-main-rotation="270"] { + mask, + use:not(.clip, .mask) { + transform: matrix(0, -1, 1, 0, 0, 1); + } } } - &[data-main-rotation="270"] { - mask, - use:not(.clip, .mask) { - transform: matrix(0, -1, 1, 0, 0, 1); + &.draw { + position: absolute; + mix-blend-mode: normal; + + &[data-draw-rotation="90"] { + transform: rotate(90deg); + } + + &[data-draw-rotation="180"] { + transform: rotate(180deg); + } + + &[data-draw-rotation="270"] { + transform: rotate(270deg); } } diff --git a/web/draw_layer_builder.js b/web/draw_layer_builder.js index 6f349b6cf134c..003a84e36897a 100644 --- a/web/draw_layer_builder.js +++ b/web/draw_layer_builder.js @@ -20,6 +20,11 @@ import { DrawLayer } from "pdfjs-lib"; * @property {number} pageIndex */ +/** + * @typedef {Object} DrawLayerBuilderRenderOptions + * @property {string} [intent] - The default value is "display". + */ + class DrawLayerBuilder { #drawLayer = null; @@ -31,9 +36,10 @@ class DrawLayerBuilder { } /** - * @param {string} intent (default value is 'display') + * @param {DrawLayerBuilderRenderOptions} options + * @returns {Promise} */ - async render(intent = "display") { + async render({ intent = "display" }) { if (intent !== "display" || this.#drawLayer || this._cancelled) { return; } diff --git a/web/editor_undo_bar.js b/web/editor_undo_bar.js new file mode 100644 index 0000000000000..096547062db4c --- /dev/null +++ b/web/editor_undo_bar.js @@ -0,0 +1,129 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { noContextMenu } from "pdfjs-lib"; + +class EditorUndoBar { + #closeButton = null; + + #container; + + #eventBus = null; + + #focusTimeout = null; + + #initController = null; + + isOpen = false; + + #message; + + #showController = null; + + #undoButton; + + static #l10nMessages = Object.freeze({ + highlight: "pdfjs-editor-undo-bar-message-highlight", + freetext: "pdfjs-editor-undo-bar-message-freetext", + stamp: "pdfjs-editor-undo-bar-message-stamp", + ink: "pdfjs-editor-undo-bar-message-ink", + signature: "pdfjs-editor-undo-bar-message-signature", + _multiple: "pdfjs-editor-undo-bar-message-multiple", + }); + + constructor({ container, message, undoButton, closeButton }, eventBus) { + this.#container = container; + this.#message = message; + this.#undoButton = undoButton; + this.#closeButton = closeButton; + this.#eventBus = eventBus; + } + + destroy() { + this.#initController?.abort(); + this.#initController = null; + + this.hide(); + } + + show(undoAction, messageData) { + if (!this.#initController) { + this.#initController = new AbortController(); + const opts = { signal: this.#initController.signal }; + const boundHide = this.hide.bind(this); + + this.#container.addEventListener("contextmenu", noContextMenu, opts); + this.#closeButton.addEventListener("click", boundHide, opts); + this.#eventBus._on("beforeprint", boundHide, opts); + this.#eventBus._on("download", boundHide, opts); + } + + this.hide(); + + if (typeof messageData === "string") { + this.#message.setAttribute( + "data-l10n-id", + EditorUndoBar.#l10nMessages[messageData] + ); + } else { + this.#message.setAttribute( + "data-l10n-id", + EditorUndoBar.#l10nMessages._multiple + ); + this.#message.setAttribute( + "data-l10n-args", + JSON.stringify({ count: messageData }) + ); + } + this.isOpen = true; + this.#container.hidden = false; + + this.#showController = new AbortController(); + + this.#undoButton.addEventListener( + "click", + () => { + undoAction(); + this.hide(); + }, + { signal: this.#showController.signal } + ); + + // Without the setTimeout, VoiceOver will read out the document title + // instead of the popup label. + this.#focusTimeout = setTimeout(() => { + this.#container.focus(); + this.#focusTimeout = null; + }, 100); + } + + hide() { + if (!this.isOpen) { + return; + } + this.isOpen = false; + this.#container.hidden = true; + + this.#showController?.abort(); + this.#showController = null; + + if (this.#focusTimeout) { + clearTimeout(this.#focusTimeout); + this.#focusTimeout = null; + } + } +} + +export { EditorUndoBar }; diff --git a/web/external_services.js b/web/external_services.js index 772a15ef81405..b9b199bd4bb14 100644 --- a/web/external_services.js +++ b/web/external_services.js @@ -44,6 +44,10 @@ class BaseExternalServices { throw new Error("Not implemented: createScripting"); } + createSignatureStorage() { + throw new Error("Not implemented: createSignatureStorage"); + } + updateEditorStates(data) { throw new Error("Not implemented: updateEditorStates"); } diff --git a/web/firefoxcom.js b/web/firefoxcom.js index f8e711686b998..40ff9e0c22459 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -121,7 +121,7 @@ class DownloadManager { window.open(viewerUrl); return true; } catch (ex) { - console.error(`openOrDownloadData: ${ex}`); + console.error("openOrDownloadData:", ex); // Release the `blobUrl`, since opening it failed, and fallback to // downloading the PDF file. URL.revokeObjectURL(blobUrl); @@ -495,6 +495,83 @@ class MLManager { } } +class SignatureStorage { + #eventBus = null; + + #signatures = null; + + #signal = null; + + constructor(eventBus, signal) { + this.#eventBus = eventBus; + this.#signal = signal; + } + + #handleSignature(data) { + return FirefoxCom.requestAsync("handleSignature", data); + } + + async getAll() { + if (this.#signal) { + window.addEventListener( + "storedSignaturesChanged", + () => { + this.#signatures = null; + this.#eventBus?.dispatch("storedsignatureschanged", { source: this }); + }, + { signal: this.#signal } + ); + this.#signal = null; + } + if (!this.#signatures) { + this.#signatures = new Map(); + const data = await this.#handleSignature({ action: "get" }); + if (data) { + for (const { uuid, description, signatureData } of data) { + this.#signatures.set(uuid, { description, signatureData }); + } + } + } + return this.#signatures; + } + + async isFull() { + // We want to store at most 5 signatures. + return (await this.size()) === 5; + } + + async size() { + return (await this.getAll()).size; + } + + async create(data) { + if (await this.isFull()) { + return null; + } + const uuid = await this.#handleSignature({ + action: "create", + ...data, + }); + if (!uuid) { + return null; + } + this.#signatures.set(uuid, data); + return uuid; + } + + async delete(uuid) { + const signatures = await this.getAll(); + if (!signatures.has(uuid)) { + return false; + } + if (await this.#handleSignature({ action: "delete", uuid })) { + signatures.delete(uuid); + return true; + } + return false; + } +} + class ExternalServices extends BaseExternalServices { updateFindControlState(data) { FirefoxCom.request("updateFindControlState", data); @@ -581,6 +658,10 @@ class ExternalServices extends BaseExternalServices { return FirefoxScripting; } + createSignatureStorage(eventBus, signal) { + return new SignatureStorage(eventBus, signal); + } + dispatchGlobalEvent(event) { FirefoxCom.request("dispatchGlobalEvent", event); } diff --git a/web/generic_scripting.js b/web/generic_scripting.js index 7883c5c5024d1..b29554dad3210 100644 --- a/web/generic_scripting.js +++ b/web/generic_scripting.js @@ -18,19 +18,13 @@ import { getPdfFilenameFromUrl } from "pdfjs-lib"; async function docProperties(pdfDocument) { const url = "", baseUrl = url.split("#", 1)[0]; - // eslint-disable-next-line prefer-const - let { info, metadata, contentDispositionFilename, contentLength } = + const { info, metadata, contentDispositionFilename, contentLength } = await pdfDocument.getMetadata(); - if (!contentLength) { - const { length } = await pdfDocument.getDownloadInfo(); - contentLength = length; - } - return { ...info, baseURL: baseUrl, - filesize: contentLength, + filesize: contentLength || (await pdfDocument.getDownloadInfo()).length, filename: contentDispositionFilename || getPdfFilenameFromUrl(url), metadata: metadata?.getRaw(), authors: metadata?.get("dc:creator"), diff --git a/web/generic_signature_storage.js b/web/generic_signature_storage.js new file mode 100644 index 0000000000000..7f5f6bcf923d0 --- /dev/null +++ b/web/generic_signature_storage.js @@ -0,0 +1,103 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getUuid } from "pdfjs-lib"; + +const KEY_STORAGE = "pdfjs.signature"; + +class SignatureStorage { + // TODO: Encrypt the data in using a password and add a UI for entering it. + // We could use the Web Crypto API for this (see https://bradyjoslin.com/blog/encryption-webcrypto/ + // for an example). + + #eventBus; + + #signatures = null; + + #signal = null; + + constructor(eventBus, signal) { + this.#eventBus = eventBus; + this.#signal = signal; + } + + #save() { + localStorage.setItem( + KEY_STORAGE, + JSON.stringify(Object.fromEntries(this.#signatures.entries())) + ); + } + + async getAll() { + if (this.#signal) { + window.addEventListener( + "storage", + ({ key }) => { + if (key === KEY_STORAGE) { + this.#signatures = null; + this.#eventBus?.dispatch("storedsignatureschanged", { + source: this, + }); + } + }, + { signal: this.#signal } + ); + this.#signal = null; + } + if (!this.#signatures) { + this.#signatures = new Map(); + const data = localStorage.getItem(KEY_STORAGE); + if (data) { + for (const [key, value] of Object.entries(JSON.parse(data))) { + this.#signatures.set(key, value); + } + } + } + return this.#signatures; + } + + async isFull() { + // Only allow 5 signatures to be saved. + return (await this.size()) === 5; + } + + async size() { + return (await this.getAll()).size; + } + + async create(data) { + if (await this.isFull()) { + return null; + } + const uuid = getUuid(); + this.#signatures.set(uuid, data); + this.#save(); + + return uuid; + } + + async delete(uuid) { + const signatures = await this.getAll(); + if (!signatures.has(uuid)) { + return false; + } + signatures.delete(uuid); + this.#save(); + + return true; + } +} + +export { SignatureStorage }; diff --git a/web/genericcom.js b/web/genericcom.js index 9bddcbc456eb5..a2d74ba09317a 100644 --- a/web/genericcom.js +++ b/web/genericcom.js @@ -18,6 +18,7 @@ import { BaseExternalServices } from "./external_services.js"; import { BasePreferences } from "./preferences.js"; import { GenericL10n } from "./genericl10n.js"; import { GenericScripting } from "./generic_scripting.js"; +import { SignatureStorage } from "./generic_signature_storage.js"; if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) { throw new Error( @@ -45,9 +46,19 @@ class ExternalServices extends BaseExternalServices { createScripting() { return new GenericScripting(AppOptions.get("sandboxBundleSrc")); } + + createSignatureStorage(eventBus, signal) { + return new SignatureStorage(eventBus, signal); + } } class MLManager { + static { + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + this.getFakeMLManager = options => new FakeMLManager(options); + } + } + async isEnabledFor(_name) { return false; } @@ -63,83 +74,82 @@ class MLManager { guess(_data) {} toggleService(_name, _enabled) {} - - static getFakeMLManager(options) { - return new FakeMLManager(options); - } } -class FakeMLManager { - eventBus = null; - - hasProgress = false; - - constructor({ enableGuessAltText, enableAltTextModelDownload }) { - this.enableGuessAltText = enableGuessAltText; - this.enableAltTextModelDownload = enableAltTextModelDownload; - } - - setEventBus(eventBus, abortSignal) { - this.eventBus = eventBus; - } - - async isEnabledFor(_name) { - return this.enableGuessAltText; - } - - async deleteModel(_name) { - this.enableAltTextModelDownload = false; - return null; - } - - async loadModel(_name) {} - - async downloadModel(_name) { - // Simulate downloading the model but with progress. - // The progress can be seen in the new alt-text dialog. - this.hasProgress = true; - - const { promise, resolve } = Promise.withResolvers(); - const total = 1e8; - const end = 1.5 * total; - const increment = 5e6; - let loaded = 0; - const id = setInterval(() => { - loaded += increment; - if (loaded <= end) { - this.eventBus.dispatch("loadaiengineprogress", { - source: this, - detail: { - total, - totalLoaded: loaded, - finished: loaded + increment >= end, - }, - }); - return; - } - clearInterval(id); - this.hasProgress = false; - this.enableAltTextModelDownload = true; - resolve(true); - }, 900); - return promise; - } - - isReady(_name) { - return this.enableAltTextModelDownload; - } - - guess({ request: { data } }) { - return new Promise(resolve => { - setTimeout(() => { - resolve(data ? { output: "Fake alt text" } : { error: true }); - }, 3000); - }); - } - - toggleService(_name, enabled) { - this.enableGuessAltText = enabled; - } +if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + // eslint-disable-next-line no-var + var FakeMLManager = class { + eventBus = null; + + hasProgress = false; + + constructor({ enableGuessAltText, enableAltTextModelDownload }) { + this.enableGuessAltText = enableGuessAltText; + this.enableAltTextModelDownload = enableAltTextModelDownload; + } + + setEventBus(eventBus, abortSignal) { + this.eventBus = eventBus; + } + + async isEnabledFor(_name) { + return this.enableGuessAltText; + } + + async deleteModel(_name) { + this.enableAltTextModelDownload = false; + return null; + } + + async loadModel(_name) {} + + async downloadModel(_name) { + // Simulate downloading the model but with progress. + // The progress can be seen in the new alt-text dialog. + this.hasProgress = true; + + const { promise, resolve } = Promise.withResolvers(); + const total = 1e8; + const end = 1.5 * total; + const increment = 5e6; + let loaded = 0; + const id = setInterval(() => { + loaded += increment; + if (loaded <= end) { + this.eventBus.dispatch("loadaiengineprogress", { + source: this, + detail: { + total, + totalLoaded: loaded, + finished: loaded + increment >= end, + }, + }); + return; + } + clearInterval(id); + this.hasProgress = false; + this.enableAltTextModelDownload = true; + resolve(true); + }, 900); + return promise; + } + + isReady(_name) { + return this.enableAltTextModelDownload; + } + + guess({ request: { data } }) { + return new Promise(resolve => { + setTimeout(() => { + resolve(data ? { output: "Fake alt text." } : { error: true }); + }, 3000); + }); + } + + toggleService(_name, enabled) { + this.enableGuessAltText = enabled; + } + }; } export { ExternalServices, initCom, MLManager, Preferences }; diff --git a/web/genericl10n.js b/web/genericl10n.js index 74b9e23854ec3..2390faec69f57 100644 --- a/web/genericl10n.js +++ b/web/genericl10n.js @@ -15,14 +15,33 @@ /** @typedef {import("./interfaces").IL10n} IL10n */ +import { FeatureTest, fetchData } from "pdfjs-lib"; import { FluentBundle, FluentResource } from "fluent-bundle"; import { DOMLocalization } from "fluent-dom"; -import { fetchData } from "pdfjs-lib"; import { L10n } from "./l10n.js"; +function PLATFORM() { + const { isAndroid, isLinux, isMac, isWindows } = FeatureTest.platform; + if (isLinux) { + return "linux"; + } + if (isWindows) { + return "windows"; + } + if (isMac) { + return "macos"; + } + if (isAndroid) { + return "android"; + } + return "other"; +} + function createBundle(lang, text) { const resource = new FluentResource(text); - const bundle = new FluentBundle(lang); + const bundle = new FluentBundle(lang, { + functions: { PLATFORM }, + }); const errors = bundle.addResource(resource); if (errors.length) { console.error("L10n errors", errors); @@ -70,8 +89,14 @@ class GenericL10n extends L10n { } langs.push(defaultLang); } - for (const lang of langs) { - const bundle = await this.#createBundle(lang, baseURL, paths); + // Trigger fetching of bundles in parallel, to reduce overall load time. + const bundles = langs.map(lang => [ + lang, + this.#createBundle(lang, baseURL, paths), + ]); + + for (const [lang, bundlePromise] of bundles) { + const bundle = await bundlePromise; if (bundle) { yield bundle; } else if (lang === "en-us") { diff --git a/web/grab_to_pan.js b/web/grab_to_pan.js index b59e064182b81..05316a024975c 100644 --- a/web/grab_to_pan.js +++ b/web/grab_to_pan.js @@ -14,6 +14,8 @@ * limitations under the License. */ +import { stopEvent } from "pdfjs-lib"; + // Class name of element which can be grabbed. const CSS_CLASS_GRAB = "grab-to-pan-grab"; @@ -131,8 +133,7 @@ class GrabToPan { capture: true, signal: this.#scrollAC.signal, }); - event.preventDefault(); - event.stopPropagation(); + stopEvent(event); const focusedElement = document.activeElement; if (focusedElement && !focusedElement.contains(event.target)) { diff --git a/web/images/editor-toolbar-edit.svg b/web/images/editor-toolbar-edit.svg new file mode 100644 index 0000000000000..692de38d32c5b --- /dev/null +++ b/web/images/editor-toolbar-edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/images/toolbarButton-editorSignature.svg b/web/images/toolbarButton-editorSignature.svg new file mode 100644 index 0000000000000..0b4e9049feea7 --- /dev/null +++ b/web/images/toolbarButton-editorSignature.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web/l10n.js b/web/l10n.js index bd348753d1596..ad469a5266be6 100644 --- a/web/l10n.js +++ b/web/l10n.js @@ -23,7 +23,7 @@ class L10n { #dir; - #elements = new Set(); + #elements; #lang; @@ -71,7 +71,7 @@ class L10n { /** @inheritdoc */ async translate(element) { - this.#elements.add(element); + (this.#elements ||= new Set()).add(element); try { this.#l10n.connectRoot(element); await this.#l10n.translateRoots(); @@ -85,16 +85,19 @@ class L10n { try { await this.#l10n.translateElements([element]); } catch (ex) { - console.error(`translateOnce: "${ex}".`); + console.error("translateOnce:", ex); } } /** @inheritdoc */ async destroy() { - for (const element of this.#elements) { - this.#l10n.disconnectRoot(element); + if (this.#elements) { + for (const element of this.#elements) { + this.#l10n.disconnectRoot(element); + } + this.#elements.clear(); + this.#elements = null; } - this.#elements.clear(); this.#l10n.pauseObserving(); } diff --git a/web/message_bar.css b/web/message_bar.css new file mode 100644 index 0000000000000..9cc647b3fd75d --- /dev/null +++ b/web/message_bar.css @@ -0,0 +1,218 @@ +/* Copyright 2024 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.messageBar { + --closing-button-icon: url(images/messageBar_closingButton.svg); + --message-bar-close-button-color: var(--text-primary-color); + --message-bar-close-button-color-hover: var(--text-primary-color); + --message-bar-close-button-border-radius: 4px; + --message-bar-close-button-border: none; + --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); + --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); + + @media (prefers-color-scheme: dark) { + --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); + } + + @media screen and (forced-colors: active) { + --message-bar-close-button-color: ButtonText; + --message-bar-close-button-border: 1px solid ButtonText; + --message-bar-close-button-hover-bg-color: ButtonText; + --message-bar-close-button-active-bg-color: ButtonText; + --message-bar-close-button-focus-bg-color: ButtonText; + --message-bar-close-button-color-hover: HighlightText; + } + + display: flex; + position: relative; + padding: 8px 8px 8px 16px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + user-select: none; + + border-radius: 4px; + + border: 1px solid var(--message-bar-border-color); + background: var(--message-bar-bg-color); + color: var(--message-bar-fg-color); + + > div { + display: flex; + align-items: flex-start; + gap: 8px; + align-self: stretch; + + &::before { + content: ""; + display: inline-block; + width: 16px; + height: 16px; + mask-image: var(--message-bar-icon); + mask-size: cover; + background-color: var(--message-bar-icon-color); + flex-shrink: 0; + } + } + + button { + cursor: pointer; + + &:focus-visible { + outline: var(--focus-ring-outline); + outline-offset: 2px; + } + } + + .closeButton { + width: 32px; + height: 32px; + background: none; + border-radius: var(--message-bar-close-button-border-radius); + border: var(--message-bar-close-button-border); + + display: flex; + align-items: center; + justify-content: center; + + &::before { + content: ""; + display: inline-block; + width: 16px; + height: 16px; + mask-image: var(--closing-button-icon); + mask-size: cover; + background-color: var(--message-bar-close-button-color); + } + + &:is(:hover, :active, :focus)::before { + background-color: var(--message-bar-close-button-color-hover); + } + + &:hover { + background-color: var(--message-bar-close-button-hover-bg-color); + } + + &:active { + background-color: var(--message-bar-close-button-active-bg-color); + } + + &:focus { + background-color: var(--message-bar-close-button-focus-bg-color); + } + + > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; + } + } +} + +#editorUndoBar { + --text-primary-color: #15141a; + + --message-bar-icon: url(images/secondaryToolbarButton-documentProperties.svg); + --message-bar-icon-color: #0060df; + --message-bar-bg-color: #deeafc; + --message-bar-fg-color: var(--text-primary-color); + --message-bar-border-color: rgb(0 0 0 / 0.08); + + --undo-button-bg-color: rgb(21 20 26 / 0.07); + --undo-button-bg-color-hover: rgb(21 20 26 / 0.14); + --undo-button-bg-color-active: rgb(21 20 26 / 0.21); + + --undo-button-fg-color: var(--message-bar-fg-color); + --undo-button-fg-color-hover: var(--undo-button-fg-color); + --undo-button-fg-color-active: var(--undo-button-fg-color); + + @media (prefers-color-scheme: dark) { + --text-primary-color: #fbfbfe; + + --message-bar-icon-color: #73a7f3; + --message-bar-bg-color: #003070; + --message-bar-border-color: rgb(255 255 255 / 0.08); + + --undo-button-bg-color: rgb(255 255 255 / 0.08); + --undo-button-bg-color-hover: rgb(255 255 255 / 0.14); + --undo-button-bg-color-active: rgb(255 255 255 / 0.21); + } + + @media screen and (forced-colors: active) { + --text-primary-color: CanvasText; + + --message-bar-icon-color: CanvasText; + --message-bar-bg-color: Canvas; + --message-bar-border-color: CanvasText; + + --undo-button-bg-color: ButtonText; + --undo-button-bg-color-hover: SelectedItem; + --undo-button-bg-color-active: SelectedItem; + + --undo-button-fg-color: ButtonFace; + --undo-button-fg-color-hover: SelectedItemText; + --undo-button-fg-color-active: SelectedItemText; + } + + position: fixed; + top: 50px; + left: 50%; + transform: translateX(-50%); + z-index: 10; + + padding-block: 8px; + padding-inline: 16px 8px; + + font: menu; + font-size: 15px; + + cursor: default; + + button { + cursor: pointer; + } + + #editorUndoBarUndoButton { + border-radius: 4px; + font-weight: 590; + line-height: 19.5px; + color: var(--undo-button-fg-color); + border: none; + padding: 4px 16px; + margin-inline-start: 8px; + height: 32px; + + background-color: var(--undo-button-bg-color); + + &:hover { + background-color: var(--undo-button-bg-color-hover); + color: var(--undo-button-fg-color-hover); + } + + &:active { + background-color: var(--undo-button-bg-color-active); + color: var(--undo-button-fg-color-active); + } + } + + > div { + align-items: center; + } +} diff --git a/web/new_alt_text_manager.js b/web/new_alt_text_manager.js index d9b7c80be406e..d9554b7798aa9 100644 --- a/web/new_alt_text_manager.js +++ b/web/new_alt_text_manager.js @@ -440,9 +440,7 @@ class NewAltTextManager { } #finish() { - if (this.#overlayManager.active === this.#dialog) { - this.#overlayManager.close(this.#dialog); - } + this.#overlayManager.closeIfActive(this.#dialog); } #close() { @@ -460,6 +458,15 @@ class NewAltTextManager { this.#uiManager = null; } + #extractWords(text) { + return new Set( + text + .toLowerCase() + .split(/[^\p{L}\p{N}]+/gu) + .filter(x => !!x) + ); + } + #save() { const altText = this.#textarea.value.trim(); this.#currentEditor.altTextData = { @@ -469,8 +476,8 @@ class NewAltTextManager { this.#currentEditor.altTextData.guessedAltText = this.#guessedAltText; if (this.#guessedAltText && this.#guessedAltText !== altText) { - const guessedWords = new Set(this.#guessedAltText.split(/\s+/)); - const words = new Set(altText.split(/\s+/)); + const guessedWords = this.#extractWords(this.#guessedAltText); + const words = this.#extractWords(altText); this.#currentEditor._reportTelemetry({ action: "pdfjs.image.alt_text.user_edit", data: { @@ -687,9 +694,7 @@ class ImageAltTextSettings { } #finish() { - if (this.#overlayManager.active === this.#dialog) { - this.#overlayManager.close(this.#dialog); - } + this.#overlayManager.closeIfActive(this.#dialog); } } diff --git a/web/overlay_manager.js b/web/overlay_manager.js index 891e3a253f2f9..8ceb7dad3354d 100644 --- a/web/overlay_manager.js +++ b/web/overlay_manager.js @@ -37,8 +37,10 @@ class OverlayManager { } this.#overlays.set(dialog, { canForceClose }); - dialog.addEventListener("cancel", evt => { - this.#active = null; + dialog.addEventListener("cancel", ({ target }) => { + if (this.#active === target) { + this.#active = null; + } }); } @@ -79,6 +81,17 @@ class OverlayManager { dialog.close(); this.#active = null; } + + /** + * @param {HTMLDialogElement} dialog - The overlay's DOM element. + * @returns {Promise} A promise that is resolved when the overlay has been + * closed. + */ + async closeIfActive(dialog) { + if (this.#active === dialog) { + await this.close(dialog); + } + } } export { OverlayManager }; diff --git a/web/password_prompt.js b/web/password_prompt.js index 6d9f41e2db1be..4f99428f63676 100644 --- a/web/password_prompt.js +++ b/web/password_prompt.js @@ -89,9 +89,7 @@ class PasswordPrompt { } async close() { - if (this.overlayManager.active === this.dialog) { - this.overlayManager.close(this.dialog); - } + this.overlayManager.closeIfActive(this.dialog); } #verify() { diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js index 3448f439b88cd..58cdee4da706d 100644 --- a/web/pdf_document_properties.js +++ b/web/pdf_document_properties.js @@ -112,12 +112,13 @@ class PDFDocumentProperties { } // Get the document properties. - const { - info, - /* metadata, */ - /* contentDispositionFilename, */ - contentLength, - } = await this.pdfDocument.getMetadata(); + const [ + { info, /* metadata, contentDispositionFilename, */ contentLength }, + pdfPage, + ] = await Promise.all([ + this.pdfDocument.getMetadata(), + this.pdfDocument.getPage(currentPageNumber), + ]); const [ fileName, @@ -131,10 +132,7 @@ class PDFDocumentProperties { this.#parseFileSize(contentLength), this.#parseDate(info.CreationDate), this.#parseDate(info.ModDate), - // eslint-disable-next-line arrow-body-style - this.pdfDocument.getPage(currentPageNumber).then(pdfPage => { - return this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation); - }), + this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation), this.#parseLinearization(info.IsLinearized), ]); diff --git a/web/pdf_find_bar.js b/web/pdf_find_bar.js index 44787f2505a9b..d76d4fe3dc43d 100644 --- a/web/pdf_find_bar.js +++ b/web/pdf_find_bar.js @@ -46,6 +46,13 @@ class PDFFindBar { this.eventBus = eventBus; this.#mainContainer = mainContainer; + const checkedInputs = new Map([ + [this.highlightAll, "highlightallchange"], + [this.caseSensitive, "casesensitivitychange"], + [this.entireWord, "entirewordchange"], + [this.matchDiacritics, "diacriticmatchingchange"], + ]); + // Add event listeners to the DOM elements. this.toggleButton.addEventListener("click", () => { this.toggle(); @@ -55,11 +62,14 @@ class PDFFindBar { this.dispatchEvent(""); }); - this.bar.addEventListener("keydown", e => { - switch (e.keyCode) { + this.bar.addEventListener("keydown", ({ keyCode, shiftKey, target }) => { + switch (keyCode) { case 13: // Enter - if (e.target === this.findField) { - this.dispatchEvent("again", e.shiftKey); + if (target === this.findField) { + this.dispatchEvent("again", shiftKey); + } else if (checkedInputs.has(target)) { + target.checked = !target.checked; + this.dispatchEvent(/* evtName = */ checkedInputs.get(target)); } break; case 27: // Escape @@ -71,26 +81,15 @@ class PDFFindBar { this.findPreviousButton.addEventListener("click", () => { this.dispatchEvent("again", true); }); - this.findNextButton.addEventListener("click", () => { this.dispatchEvent("again", false); }); - this.highlightAll.addEventListener("click", () => { - this.dispatchEvent("highlightallchange"); - }); - - this.caseSensitive.addEventListener("click", () => { - this.dispatchEvent("casesensitivitychange"); - }); - - this.entireWord.addEventListener("click", () => { - this.dispatchEvent("entirewordchange"); - }); - - this.matchDiacritics.addEventListener("click", () => { - this.dispatchEvent("diacriticmatchingchange"); - }); + for (const [elem, evtName] of checkedInputs) { + elem.addEventListener("click", () => { + this.dispatchEvent(evtName); + }); + } } reset() { diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 71855127a4b7b..6e87c3149c21a 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -117,10 +117,12 @@ function normalize(text) { } } + const hasSyllables = syllablePositions.length > 0; + let normalizationRegex; - if (syllablePositions.length === 0 && noSyllablesRegExp) { + if (!hasSyllables && noSyllablesRegExp) { normalizationRegex = noSyllablesRegExp; - } else if (syllablePositions.length > 0 && withSyllablesRegExp) { + } else if (hasSyllables && withSyllablesRegExp) { normalizationRegex = withSyllablesRegExp; } else { // Compile the regular expression for text normalization once. @@ -131,22 +133,33 @@ function normalize(text) { // 30A0-30FF: Katakana const CJK = "(?:\\p{Ideographic}|[\u3040-\u30FF])"; const HKDiacritics = "(?:\u3099|\u309A)"; - const CompoundWord = "\\p{Ll}-\\n\\p{Lu}"; - const regexp = `([${replace}])|([${toNormalizeWithNFKC}])|(${HKDiacritics}\\n)|(\\p{M}+(?:-\\n)?)|(${CompoundWord})|(\\S-\\n)|(${CJK}\\n)|(\\n)`; - - if (syllablePositions.length === 0) { - // Most of the syllables belong to Hangul so there are no need - // to search for them in a non-Hangul document. - // We use the \0 in order to have the same number of groups. - normalizationRegex = noSyllablesRegExp = new RegExp( - regexp + "|(\\u0000)", - "gum" - ); + const BrokenWord = `\\p{Ll}-\\n(?=\\p{Ll})|\\p{Lu}-\\n(?=\\p{L})`; + + const regexps = [ + /* p1 */ `[${replace}]`, + /* p2 */ `[${toNormalizeWithNFKC}]`, + /* p3 */ `${HKDiacritics}\\n`, + /* p4 */ "\\p{M}+(?:-\\n)?", + /* p5 */ `${BrokenWord}`, + /* p6 */ "\\S-\\n", + /* p7 */ `${CJK}\\n`, + /* p8 */ "\\n", + /* p9 */ hasSyllables + ? FIRST_CHAR_SYLLABLES_REG_EXP + : // Most of the syllables belong to Hangul so there are no need + // to search for them in a non-Hangul document. + // We use the \0 in order to have the same number of groups. + "\\u0000", + ]; + normalizationRegex = new RegExp( + regexps.map(r => `(${r})`).join("|"), + "gum" + ); + + if (hasSyllables) { + withSyllablesRegExp = normalizationRegex; } else { - normalizationRegex = withSyllablesRegExp = new RegExp( - regexp + `|(${FIRST_CHAR_SYLLABLES_REG_EXP})`, - "gum" - ); + noSyllablesRegExp = normalizationRegex; } } @@ -184,7 +197,7 @@ function normalize(text) { } let normalized = text.normalize("NFD"); - const positions = [[0, 0]]; + const positions = [0, 0]; let rawDiacriticsIndex = 0; let syllableIndex = 0; let shift = 0; @@ -201,7 +214,7 @@ function normalize(text) { const replacement = CHARACTERS_TO_NORMALIZE[p1]; const jj = replacement.length; for (let j = 1; j < jj; j++) { - positions.push([i - shift + j, shift - j]); + positions.push(i - shift + j, shift - j); } shift -= jj - 1; return replacement; @@ -216,7 +229,7 @@ function normalize(text) { } const jj = replacement.length; for (let j = 1; j < jj; j++) { - positions.push([i - shift + j, shift - j]); + positions.push(i - shift + j, shift - j); } shift -= jj - 1; return replacement; @@ -233,13 +246,13 @@ function normalize(text) { } else { // i is the position of the first diacritic // so (i - 1) is the position for the letter before. - positions.push([i - 1 - shift + 1, shift - 1]); + positions.push(i - 1 - shift + 1, shift - 1); shift -= 1; shiftOrigin += 1; } // End-of-line. - positions.push([i - shift + 1, shift]); + positions.push(i - shift + 1, shift); shiftOrigin += 1; eol += 1; @@ -261,7 +274,7 @@ function normalize(text) { for (let j = 1; j <= jj; j++) { // i is the position of the first diacritic // so (i - 1) is the position for the letter before. - positions.push([i - 1 - shift + j, shift - j]); + positions.push(i - 1 - shift + j, shift - j); } shift -= jj; shiftOrigin += jj; @@ -270,7 +283,7 @@ function normalize(text) { // Diacritics are followed by a -\n. // See comments in `if (p6)` block. i += len - 1; - positions.push([i - shift + 1, 1 + shift]); + positions.push(i - shift + 1, 1 + shift); shift += 1; shiftOrigin += 1; eol += 1; @@ -281,27 +294,27 @@ function normalize(text) { } if (p5) { - // Compound word with a line break after the hyphen. - positions.push([i - shift + 3, 1 + shift]); + // In "X-\ny", "-\n" is removed because an hyphen at the end of a line + // between two letters is likely here to mark a break in a word. + // If X is encoded with UTF-32 then it can have a length greater than 1. + // The \n isn't in the original text so here y = i, n = X.len - 2 and + // o = X.len - 1. + const len = p5.length - 2; + positions.push(i - shift + len, 1 + shift); shift += 1; shiftOrigin += 1; eol += 1; - return p5.replace("\n", ""); + return p5.slice(0, -2); } if (p6) { - // "X-\n" is removed because an hyphen at the end of a line - // with not a space before is likely here to mark a break - // in a word. - // If X is encoded with UTF-32 then it can have a length greater than 1. - // The \n isn't in the original text so here y = i, n = X.len - 2 and - // o = X.len - 1. - const len = p6.length - 2; - positions.push([i - shift + len, 1 + shift]); - shift += 1; + // A - following a non-space character that is not detected as the + // hyphen breaking a word in two lines needs to be preserved. It could + // be, for example, in a compound word or in a date. + // Only remove the newline. shiftOrigin += 1; eol += 1; - return p6.slice(0, -2); + return p6.slice(0, -1); } if (p7) { @@ -309,7 +322,7 @@ function normalize(text) { // white space. // A CJK can be encoded in UTF-32, hence their length isn't always 1. const len = p7.length - 1; - positions.push([i - shift + len, shift]); + positions.push(i - shift + len, shift); shiftOrigin += 1; eol += 1; return p7.slice(0, -1); @@ -318,21 +331,21 @@ function normalize(text) { if (p8) { // eol is replaced by space: "foo\nbar" is likely equivalent to // "foo bar". - positions.push([i - shift + 1, shift - 1]); + positions.push(i - shift + 1, shift - 1); shift -= 1; shiftOrigin += 1; eol += 1; return " "; } - // p8 + // p9 if (i + eol === syllablePositions[syllableIndex]?.[1]) { // A syllable (1 char) is replaced with several chars (n) so // newCharsLen = n - 1. const newCharLen = syllablePositions[syllableIndex][0] - 1; ++syllableIndex; for (let j = 1; j <= newCharLen; j++) { - positions.push([i - (shift - j), shift - j]); + positions.push(i - (shift - j), shift - j); } shift -= newCharLen; shiftOrigin += newCharLen; @@ -341,9 +354,15 @@ function normalize(text) { } ); - positions.push([normalized.length, shift]); + positions.push(normalized.length, shift); + const starts = new Uint32Array(positions.length >> 1); + const shifts = new Int32Array(positions.length >> 1); + for (let i = 0, ii = positions.length; i < ii; i += 2) { + starts[i >> 1] = positions[i]; + shifts[i >> 1] = positions[i + 1]; + } - return [normalized, positions, hasDiacritics]; + return [normalized, [starts, shifts], hasDiacritics]; } // Determine the original, non-normalized, match index such that highlighting of @@ -354,25 +373,26 @@ function getOriginalIndex(diffs, pos, len) { return [pos, len]; } + const [starts, shifts] = diffs; // First char in the new string. const start = pos; // Last char in the new string. const end = pos + len - 1; - let i = binarySearchFirstItem(diffs, x => x[0] >= start); - if (diffs[i][0] > start) { + let i = binarySearchFirstItem(starts, x => x >= start); + if (starts[i] > start) { --i; } - let j = binarySearchFirstItem(diffs, x => x[0] >= end, i); - if (diffs[j][0] > end) { + let j = binarySearchFirstItem(starts, x => x >= end, i); + if (starts[j] > end) { --j; } // First char in the old string. - const oldStart = start + diffs[i][1]; + const oldStart = start + shifts[i]; // Last char in the old string. - const oldEnd = end + diffs[j][1]; + const oldEnd = end + shifts[j]; const oldLen = oldEnd + 1 - oldStart; return [oldStart, oldLen]; @@ -866,9 +886,8 @@ class PDFFindController { const { promise, resolve } = Promise.withResolvers(); this._extractTextPromises[i] = promise; - // eslint-disable-next-line arrow-body-style - deferred = deferred.then(() => { - return this._pdfDocument + deferred = deferred.then(() => + this._pdfDocument .getPage(i + 1) .then(pdfPage => pdfPage.getTextContent(textOptions)) .then( @@ -901,8 +920,8 @@ class PDFFindController { this._hasDiacritics[i] = false; resolve(); } - ); - }); + ) + ); } } @@ -1165,4 +1184,4 @@ class PDFFindController { } } -export { FindState, PDFFindController }; +export { FindState, getOriginalIndex, normalize, PDFFindController }; diff --git a/web/pdf_link_service.js b/web/pdf_link_service.js index 14ffb9c10134b..e22243639abc1 100644 --- a/web/pdf_link_service.js +++ b/web/pdf_link_service.js @@ -16,6 +16,7 @@ /** @typedef {import("./event_utils").EventBus} EventBus */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ +import { isValidExplicitDest } from "pdfjs-lib"; import { parseQueryString } from "./ui_utils.js"; const DEFAULT_LINK_REL = "noopener noreferrer nofollow"; @@ -415,7 +416,7 @@ class PDFLinkService { } } catch {} - if (typeof dest === "string" || PDFLinkService.#isValidExplicitDest(dest)) { + if (typeof dest === "string" || isValidExplicitDest(dest)) { this.goToDestination(dest); return; } @@ -486,60 +487,6 @@ class PDFLinkService { optionalContentConfig ); } - - static #isValidExplicitDest(dest) { - if (!Array.isArray(dest) || dest.length < 2) { - return false; - } - const [page, zoom, ...args] = dest; - if ( - !( - typeof page === "object" && - Number.isInteger(page?.num) && - Number.isInteger(page?.gen) - ) && - !Number.isInteger(page) - ) { - return false; - } - if (!(typeof zoom === "object" && typeof zoom?.name === "string")) { - return false; - } - const argsLen = args.length; - let allowNull = true; - switch (zoom.name) { - case "XYZ": - if (argsLen < 2 || argsLen > 3) { - return false; - } - break; - case "Fit": - case "FitB": - return argsLen === 0; - case "FitH": - case "FitBH": - case "FitV": - case "FitBV": - if (argsLen > 1) { - return false; - } - break; - case "FitR": - if (argsLen !== 4) { - return false; - } - allowNull = false; - break; - default: - return false; - } - for (const arg of args) { - if (!(typeof arg === "number" || (allowNull && arg === null))) { - return false; - } - } - return true; - } } /** diff --git a/web/pdf_page_detail_view.js b/web/pdf_page_detail_view.js new file mode 100644 index 0000000000000..72d3eba60d95b --- /dev/null +++ b/web/pdf_page_detail_view.js @@ -0,0 +1,272 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePDFPageView } from "./base_pdf_page_view.js"; +import { RenderingStates } from "./ui_utils.js"; + +/** @typedef {import("./interfaces").IRenderableView} IRenderableView */ + +/** + * @implements {IRenderableView} + */ +class PDFPageDetailView extends BasePDFPageView { + #detailArea = null; + + /** + * @type {boolean} True when the last rendering attempt of the view was + * cancelled due to a `.reset()` call. This will happen when + * the visible area changes so much during the rendering that + * we need to cancel the rendering and start over. + */ + renderingCancelled = false; + + constructor({ pageView }) { + super(pageView); + + this.pageView = pageView; + this.renderingId = "detail" + this.id; + + this.div = pageView.div; + } + + setPdfPage(pdfPage) { + this.pageView.setPdfPage(pdfPage); + } + + get pdfPage() { + return this.pageView.pdfPage; + } + + get renderingState() { + return super.renderingState; + } + + set renderingState(value) { + this.renderingCancelled = false; + super.renderingState = value; + } + + reset({ keepCanvas = false } = {}) { + const renderingCancelled = + this.renderingCancelled || + this.renderingState === RenderingStates.RUNNING || + this.renderingState === RenderingStates.PAUSED; + this.cancelRendering(); + this.renderingState = RenderingStates.INITIAL; + this.renderingCancelled = renderingCancelled; + + if (!keepCanvas) { + this._resetCanvas(); + } + } + + #shouldRenderDifferentArea(visibleArea) { + if (!this.#detailArea) { + return true; + } + + const minDetailX = this.#detailArea.minX; + const minDetailY = this.#detailArea.minY; + const maxDetailX = this.#detailArea.width + minDetailX; + const maxDetailY = this.#detailArea.height + minDetailY; + + if ( + visibleArea.minX < minDetailX || + visibleArea.minY < minDetailY || + visibleArea.maxX > maxDetailX || + visibleArea.maxY > maxDetailY + ) { + return true; + } + + const { + width: maxWidth, + height: maxHeight, + scale, + } = this.pageView.viewport; + + if (this.#detailArea.scale !== scale) { + return true; + } + + const paddingLeftSize = visibleArea.minX - minDetailX; + const paddingRightSize = maxDetailX - visibleArea.maxX; + const paddingTopSize = visibleArea.minY - minDetailY; + const paddingBottomSize = maxDetailY - visibleArea.maxY; + + // If the user is moving in any direction such that the remaining area + // rendered outside of the screen is less than MOVEMENT_THRESHOLD of the + // padding we render on each side, trigger a re-render. This is so that if + // the user then keeps scrolling in that direction, we have a chance of + // finishing rendering the new detail before they get past the rendered + // area. + + const MOVEMENT_THRESHOLD = 0.5; + const ratio = (1 + MOVEMENT_THRESHOLD) / MOVEMENT_THRESHOLD; + + if ( + (minDetailX > 0 && paddingRightSize / paddingLeftSize > ratio) || + (maxDetailX < maxWidth && paddingLeftSize / paddingRightSize > ratio) || + (minDetailY > 0 && paddingBottomSize / paddingTopSize > ratio) || + (maxDetailY < maxHeight && paddingTopSize / paddingBottomSize > ratio) + ) { + return true; + } + + return false; + } + + update({ visibleArea = null, underlyingViewUpdated = false } = {}) { + if (underlyingViewUpdated) { + this.cancelRendering(); + this.renderingState = RenderingStates.INITIAL; + return; + } + + if (!this.#shouldRenderDifferentArea(visibleArea)) { + return; + } + + const { viewport, maxCanvasPixels } = this.pageView; + + const visibleWidth = visibleArea.maxX - visibleArea.minX; + const visibleHeight = visibleArea.maxY - visibleArea.minY; + + // "overflowScale" represents which percentage of the width and of the + // height the detail area extends outside of the visible area. We want to + // draw a larger area so that we don't have to constantly re-draw while + // scrolling. The detail area's dimensions thus become + // visibleLength * (2 * overflowScale + 1). + // We default to adding a whole height/length of detail area on each side, + // but we can reduce it to make sure that we stay within the maxCanvasPixels + // limit. + const visiblePixels = + visibleWidth * visibleHeight * (window.devicePixelRatio || 1) ** 2; + const maxDetailToVisibleLinearRatio = Math.sqrt( + maxCanvasPixels / visiblePixels + ); + const maxOverflowScale = (maxDetailToVisibleLinearRatio - 1) / 2; + let overflowScale = Math.min(1, maxOverflowScale); + if (overflowScale < 0) { + overflowScale = 0; + // In this case, we render a detail view that is exactly as big as the + // visible area, but we ignore the .maxCanvasPixels limit. + // TODO: We should probably instead give up and not render the detail view + // in this case. It's quite rare to hit it though, because usually + // .maxCanvasPixels will at least have enough pixels to cover the visible + // screen. + } + + const overflowWidth = visibleWidth * overflowScale; + const overflowHeight = visibleHeight * overflowScale; + + const minX = Math.max(0, visibleArea.minX - overflowWidth); + const maxX = Math.min(viewport.width, visibleArea.maxX + overflowWidth); + const minY = Math.max(0, visibleArea.minY - overflowHeight); + const maxY = Math.min(viewport.height, visibleArea.maxY + overflowHeight); + const width = maxX - minX; + const height = maxY - minY; + + this.#detailArea = { minX, minY, width, height, scale: viewport.scale }; + + this.reset({ keepCanvas: true }); + } + + async draw() { + // The PDFPageView might have already dropped this PDFPageDetailView. In + // that case, simply do nothing. + if (this.pageView.detailView !== this) { + return undefined; + } + + // If there is already the lower resolution canvas behind, + // we don't show the new one until when it's fully ready. + const hideUntilComplete = + this.pageView.renderingState === RenderingStates.FINISHED || + this.renderingState === RenderingStates.FINISHED; + + if (this.renderingState !== RenderingStates.INITIAL) { + console.error("Must be in new state before drawing"); + this.reset(); // Ensure that we reset all state to prevent issues. + } + const { div, pdfPage, viewport } = this.pageView; + + if (!pdfPage) { + this.renderingState = RenderingStates.FINISHED; + throw new Error("pdfPage is not loaded"); + } + + this.renderingState = RenderingStates.RUNNING; + + const canvasWrapper = this.pageView._ensureCanvasWrapper(); + + const { canvas, prevCanvas, ctx } = this._createCanvas(newCanvas => { + // If there is already the background canvas, inject this new canvas + // after it. We cannot simply use .append because all canvases must + // be before the SVG elements used for drawings. + if (canvasWrapper.firstElementChild?.tagName === "CANVAS") { + canvasWrapper.firstElementChild.after(newCanvas); + } else { + canvasWrapper.prepend(newCanvas); + } + }, hideUntilComplete); + canvas.setAttribute("aria-hidden", "true"); + + const { width, height } = viewport; + + const area = this.#detailArea; + + const { devicePixelRatio = 1 } = window; + const transform = [ + devicePixelRatio, + 0, + 0, + devicePixelRatio, + -area.minX * devicePixelRatio, + -area.minY * devicePixelRatio, + ]; + + canvas.width = area.width * devicePixelRatio; + canvas.height = area.height * devicePixelRatio; + const { style } = canvas; + style.width = `${(area.width * 100) / width}%`; + style.height = `${(area.height * 100) / height}%`; + style.top = `${(area.minY * 100) / height}%`; + style.left = `${(area.minX * 100) / width}%`; + + const renderingPromise = this._drawCanvas( + this.pageView._getRenderingContext(ctx, transform), + () => { + // If the rendering is cancelled, keep the old canvas visible. + this.canvas?.remove(); + this.canvas = prevCanvas; + }, + () => { + this.dispatchPageRendered( + /* cssTransform */ false, + /* isDetailView */ true + ); + } + ); + + div.setAttribute("data-loaded", true); + + this.dispatchPageRender(); + + return renderingPromise; + } +} + +export { PDFPageDetailView }; diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 010aa18ae437c..618b6b6c5c7b4 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -28,7 +28,6 @@ import { AnnotationMode, OutputScale, PixelsPerInch, - RenderingCancelledException, setLayerDimensions, shadow, } from "pdfjs-lib"; @@ -43,8 +42,11 @@ import { import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.js"; import { AnnotationLayerBuilder } from "./annotation_layer_builder.js"; import { AppOptions } from "./app_options.js"; +import { Autolinker } from "./autolinker.js"; +import { BasePDFPageView } from "./base_pdf_page_view.js"; import { DrawLayerBuilder } from "./draw_layer_builder.js"; import { GenericL10n } from "web-null_l10n"; +import { PDFPageDetailView } from "./pdf_page_detail_view.js"; import { SimpleLinkService } from "./pdf_link_service.js"; import { StructTreeLayerBuilder } from "./struct_tree_layer_builder.js"; import { TextAccessibilityManager } from "./text_accessibility.js"; @@ -76,6 +78,12 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js"; * @property {number} [maxCanvasPixels] - The maximum supported canvas size in * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + * @property {boolean} [enableDetailCanvas] - When enabled, if the rendered + * pages would need a canvas that is larger than `maxCanvasPixels`, it will + * draw a second canvas on top of the CSS-zoomed one, that only renders the + * part of the page that is close to the viewport. The default value is + * `true`. + * @property {Object} [pageColors] - Overwrites background and foreground colors * with user defined ones in order to improve readability in high contrast * mode. @@ -84,6 +92,8 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js"; * the necessary layer-properties. * @property {boolean} [enableHWA] - Enables hardware acceleration for * rendering. The default value is `false`. + * @property {boolean} [enableAutoLinking] - Enable creation of hyperlinks from + * text that look like URLs. The default value is `false`. */ const DEFAULT_LAYER_PROPERTIES = @@ -113,10 +123,12 @@ const LAYERS_ORDER = new Map([ /** * @implements {IRenderableView} */ -class PDFPageView { +class PDFPageView extends BasePDFPageView { #annotationMode = AnnotationMode.ENABLE_FORMS; - #enableHWA = false; + #canvasWrapper = null; + + #enableAutoLinking = false; #hasRestrictedScaling = false; @@ -124,7 +136,9 @@ class PDFPageView { #layerProperties = null; - #loadingId = null; + #needsRestrictedScaling = false; + + #originalViewport = null; #previousRotation = null; @@ -132,30 +146,27 @@ class PDFPageView { #scaleRoundY = 1; - #renderError = null; - - #renderingState = RenderingStates.INITIAL; - #textLayerMode = TextLayerMode.ENABLE; + #userUnit = 1; + #useThumbnailCanvas = { directDrawing: true, initialOptionalContent: true, regularAnnotations: true, }; - #viewportMap = new WeakMap(); - #layers = [null, null, null, null]; /** * @param {PDFPageViewOptions} options */ constructor(options) { + super(options); + const container = options.container; const defaultViewport = options.defaultViewport; - this.id = options.id; this.renderingId = "page" + this.id; this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES; @@ -171,20 +182,16 @@ class PDFPageView { this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS; this.imageResourcesPath = options.imageResourcesPath || ""; + this.enableDetailCanvas = options.enableDetailCanvas ?? true; this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels"); - this.pageColors = options.pageColors || null; - this.#enableHWA = options.enableHWA || false; + this.#enableAutoLinking = options.enableAutoLinking || false; - this.eventBus = options.eventBus; - this.renderingQueue = options.renderingQueue; this.l10n = options.l10n; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { this.l10n ||= new GenericL10n(); } - this.renderTask = null; - this.resume = null; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { this._isStandalone = !this.renderingQueue?.hasViewer(); this._container = container; @@ -195,11 +202,12 @@ class PDFPageView { this.annotationLayer = null; this.annotationEditorLayer = null; this.textLayer = null; - this.zoomLayer = null; this.xfaLayer = null; this.structTreeLayer = null; this.drawLayer = null; + this.detailView = null; + const div = document.createElement("div"); div.className = "page"; div.setAttribute("data-page-number", this.id); @@ -269,45 +277,17 @@ class PDFPageView { this.div.prepend(div); } - get renderingState() { - return this.#renderingState; - } - - set renderingState(state) { - if (state === this.#renderingState) { - return; - } - this.#renderingState = state; - - if (this.#loadingId) { - clearTimeout(this.#loadingId); - this.#loadingId = null; - } + #setDimensions() { + const { div, viewport } = this; - switch (state) { - case RenderingStates.PAUSED: - this.div.classList.remove("loading"); - break; - case RenderingStates.RUNNING: - this.div.classList.add("loadingIcon"); - this.#loadingId = setTimeout(() => { - // Adding the loading class is slightly postponed in order to not have - // it with loadingIcon. - // If we don't do that the visibility of the background is changed but - // the transition isn't triggered. - this.div.classList.add("loading"); - this.#loadingId = null; - }, 0); - break; - case RenderingStates.INITIAL: - case RenderingStates.FINISHED: - this.div.classList.remove("loadingIcon", "loading"); - break; + if (viewport.userUnit !== this.#userUnit) { + if (viewport.userUnit !== 1) { + div.style.setProperty("--user-unit", viewport.userUnit); + } else { + div.style.removeProperty("--user-unit"); + } + this.#userUnit = viewport.userUnit; } - } - - #setDimensions() { - const { viewport } = this; if (this.pdfPage) { if (this.#previousRotation === viewport.rotation) { return; @@ -316,7 +296,7 @@ class PDFPageView { } setLayerDimensions( - this.div, + div, viewport, /* mustFlip = */ true, /* mustRotate = */ false @@ -395,13 +375,13 @@ class PDFPageView { async #renderAnnotationLayer() { let error = null; try { - await this.annotationLayer.render( - this.viewport, - { structTreeLayer: this.structTreeLayer }, - "display" - ); + await this.annotationLayer.render({ + viewport: this.viewport, + intent: "display", + structTreeLayer: this.structTreeLayer, + }); } catch (ex) { - console.error(`#renderAnnotationLayer: "${ex}".`); + console.error("#renderAnnotationLayer:", ex); error = ex; } finally { this.#dispatchLayerRendered("annotationlayerrendered", error); @@ -411,9 +391,12 @@ class PDFPageView { async #renderAnnotationEditorLayer() { let error = null; try { - await this.annotationEditorLayer.render(this.viewport, "display"); + await this.annotationEditorLayer.render({ + viewport: this.viewport, + intent: "display", + }); } catch (ex) { - console.error(`#renderAnnotationEditorLayer: "${ex}".`); + console.error("#renderAnnotationEditorLayer:", ex); error = ex; } finally { this.#dispatchLayerRendered("annotationeditorlayerrendered", error); @@ -422,16 +405,21 @@ class PDFPageView { async #renderDrawLayer() { try { - await this.drawLayer.render("display"); + await this.drawLayer.render({ + intent: "display", + }); } catch (ex) { - console.error(`#renderDrawLayer: "${ex}".`); + console.error("#renderDrawLayer:", ex); } } async #renderXfaLayer() { let error = null; try { - const result = await this.xfaLayer.render(this.viewport, "display"); + const result = await this.xfaLayer.render({ + viewport: this.viewport, + intent: "display", + }); if (result?.textDivs && this._textHighlighter) { // Given that the following method fetches the text asynchronously we // can invoke it *before* appending the xfaLayer to the DOM (below), @@ -440,7 +428,7 @@ class PDFPageView { this.#buildXfaTextContentItems(result.textDivs); } } catch (ex) { - console.error(`#renderXfaLayer: "${ex}".`); + console.error("#renderXfaLayer:", ex); error = ex; } finally { if (this.xfaLayer?.div) { @@ -460,12 +448,14 @@ class PDFPageView { let error = null; try { - await this.textLayer.render(this.viewport); + await this.textLayer.render({ + viewport: this.viewport, + }); } catch (ex) { if (ex instanceof AbortException) { return; } - console.error(`#renderTextLayer: "${ex}".`); + console.error("#renderTextLayer:", ex); error = ex; } this.#dispatchLayerRendered("textlayerrendered", error); @@ -508,33 +498,40 @@ class PDFPageView { this._textHighlighter.enable(); } - /** - * @private - */ - _resetZoomLayer(removeFromDOM = false) { - if (!this.zoomLayer) { - return; + async #injectLinkAnnotations(textLayerPromise) { + let error = null; + try { + await textLayerPromise; + + if (!this.annotationLayer) { + return; // Rendering was cancelled while the textLayerPromise resolved. + } + await this.annotationLayer.injectLinkAnnotations({ + inferredLinks: Autolinker.processLinks(this), + viewport: this.viewport, + structTreeLayer: this.structTreeLayer, + }); + } catch (ex) { + console.error("#injectLinkAnnotations:", ex); + error = ex; } - const zoomLayerCanvas = this.zoomLayer.firstChild; - this.#viewportMap.delete(zoomLayerCanvas); - // Zeroing the width and height causes Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - zoomLayerCanvas.width = 0; - zoomLayerCanvas.height = 0; - - if (removeFromDOM) { - // Note: `ChildNode.remove` doesn't throw if the parent node is undefined. - this.zoomLayer.remove(); + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + this.#dispatchLayerRendered("linkannotationsadded", error); } - this.zoomLayer = null; + } + + _resetCanvas() { + super._resetCanvas(); + this.#originalViewport = null; } reset({ - keepZoomLayer = false, keepAnnotationLayer = false, keepAnnotationEditorLayer = false, keepXfaLayer = false, keepTextLayer = false, + keepCanvasWrapper = false, + preserveDetailViewState = false, } = {}) { this.cancelRendering({ keepAnnotationLayer, @@ -547,21 +544,21 @@ class PDFPageView { const div = this.div; const childNodes = div.childNodes, - zoomLayerNode = (keepZoomLayer && this.zoomLayer) || null, annotationLayerNode = (keepAnnotationLayer && this.annotationLayer?.div) || null, annotationEditorLayerNode = (keepAnnotationEditorLayer && this.annotationEditorLayer?.div) || null, xfaLayerNode = (keepXfaLayer && this.xfaLayer?.div) || null, - textLayerNode = (keepTextLayer && this.textLayer?.div) || null; + textLayerNode = (keepTextLayer && this.textLayer?.div) || null, + canvasWrapperNode = (keepCanvasWrapper && this.#canvasWrapper) || null; for (let i = childNodes.length - 1; i >= 0; i--) { const node = childNodes[i]; switch (node) { - case zoomLayerNode: case annotationLayerNode: case annotationEditorLayerNode: case xfaLayerNode: case textLayerNode: + case canvasWrapperNode: continue; } node.remove(); @@ -590,33 +587,57 @@ class PDFPageView { } this.structTreeLayer?.hide(); - if (!zoomLayerNode) { - if (this.canvas) { - this.#viewportMap.delete(this.canvas); - // Zeroing the width and height causes Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - this.canvas.width = 0; - this.canvas.height = 0; - delete this.canvas; + if (!keepCanvasWrapper && this.#canvasWrapper) { + this.#canvasWrapper = null; + this._resetCanvas(); + } + + if (!preserveDetailViewState) { + this.detailView?.reset({ keepCanvas: keepCanvasWrapper }); + + // If we are keeping the canvas around we must also keep the `detailView` + // object, so that next time we need a detail view we'll update the + // existing canvas rather than creating a new one. + if (!keepCanvasWrapper) { + this.detailView = null; } - this._resetZoomLayer(); } } toggleEditingMode(isEditing) { + // The page can be invisible, consequently there's no annotation layer and + // we can't know if there are editable annotations. + // So to avoid any issue when the page is rendered the #isEditing flag must + // be set. + this.#isEditing = isEditing; if (!this.hasEditableAnnotations()) { return; } - this.#isEditing = isEditing; this.reset({ - keepZoomLayer: true, keepAnnotationLayer: true, keepAnnotationEditorLayer: true, keepXfaLayer: true, keepTextLayer: true, + keepCanvasWrapper: true, }); } + updateVisibleArea(visibleArea) { + if (this.enableDetailCanvas) { + if ( + this.#needsRestrictedScaling && + this.maxCanvasPixels > 0 && + visibleArea + ) { + this.detailView ??= new PDFPageDetailView({ pageView: this }); + this.detailView.update({ visibleArea }); + } else if (this.detailView) { + this.detailView.reset(); + this.detailView = null; + } + } + } + /** * @typedef {Object} PDFPageViewUpdateParameters * @property {number} [scale] The new scale, if specified. @@ -672,22 +693,11 @@ class PDFPageView { this._container?.style.setProperty("--scale-factor", this.viewport.scale); } + this.#computeScale(); + if (this.canvas) { - let onlyCssZoom = false; - if (this.#hasRestrictedScaling) { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && - this.maxCanvasPixels === 0 - ) { - onlyCssZoom = true; - } else if (this.maxCanvasPixels > 0) { - const { width, height } = this.viewport; - const { sx, sy } = this.outputScale; - onlyCssZoom = - ((Math.floor(width) * sx) | 0) * ((Math.floor(height) * sy) | 0) > - this.maxCanvasPixels; - } - } + const onlyCssZoom = + this.#hasRestrictedScaling && this.#needsRestrictedScaling; const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000; if (postponeDrawing || onlyCssZoom) { @@ -697,7 +707,6 @@ class PDFPageView { this.renderingState !== RenderingStates.FINISHED ) { this.cancelRendering({ - keepZoomLayer: true, keepAnnotationLayer: true, keepAnnotationEditorLayer: true, keepXfaLayer: true, @@ -715,7 +724,6 @@ class PDFPageView { } this.cssTransform({ - target: this.canvas, redrawAnnotationLayer: true, redrawAnnotationEditorLayer: true, redrawXfaLayer: true, @@ -723,35 +731,58 @@ class PDFPageView { hideTextLayer: postponeDrawing, }); - if (postponeDrawing) { - // The "pagerendered"-event will be dispatched once the actual - // rendering is done, hence don't dispatch it here as well. - return; + // The "pagerendered"-event will be dispatched once the actual + // rendering is done, hence don't dispatch it here as well. + if (!postponeDrawing) { + this.detailView?.update({ underlyingViewUpdated: true }); + + this.dispatchPageRendered( + /* cssTransform */ true, + /* isDetailView */ false + ); } - this.eventBus.dispatch("pagerendered", { - source: this, - pageNumber: this.id, - cssTransform: true, - timestamp: performance.now(), - error: this.#renderError, - }); return; } - if (!this.zoomLayer && !this.canvas.hidden) { - this.zoomLayer = this.canvas.parentNode; - this.zoomLayer.style.position = "absolute"; - } - } - if (this.zoomLayer) { - this.cssTransform({ target: this.zoomLayer.firstChild }); } + this.cssTransform({}); this.reset({ - keepZoomLayer: true, keepAnnotationLayer: true, keepAnnotationEditorLayer: true, keepXfaLayer: true, keepTextLayer: true, + keepCanvasWrapper: true, + // It will be reset by the .update call below + preserveDetailViewState: true, }); + + this.detailView?.update({ underlyingViewUpdated: true }); + } + + #computeScale() { + const { width, height } = this.viewport; + const outputScale = (this.outputScale = new OutputScale()); + + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && + this.maxCanvasPixels === 0 + ) { + const invScale = 1 / this.scale; + // Use a scale that makes the canvas have the originally intended size + // of the page. + outputScale.sx *= invScale; + outputScale.sy *= invScale; + this.#needsRestrictedScaling = true; + } else if (this.maxCanvasPixels > 0) { + const pixelsInViewport = width * height; + const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport); + if (outputScale.sx > maxScale || outputScale.sy > maxScale) { + outputScale.sx = maxScale; + outputScale.sy = maxScale; + this.#needsRestrictedScaling = true; + } else { + this.#needsRestrictedScaling = false; + } + } } /** @@ -765,11 +796,7 @@ class PDFPageView { keepTextLayer = false, cancelExtraDelay = 0, } = {}) { - if (this.renderTask) { - this.renderTask.cancel(cancelExtraDelay); - this.renderTask = null; - } - this.resume = null; + super.cancelRendering({ cancelExtraDelay }); if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) { this.textLayer.cancel(); @@ -805,41 +832,32 @@ class PDFPageView { } cssTransform({ - target, redrawAnnotationLayer = false, redrawAnnotationEditorLayer = false, redrawXfaLayer = false, redrawTextLayer = false, hideTextLayer = false, }) { - // Scale target (canvas), its wrapper and page container. - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - !(target instanceof HTMLCanvasElement) - ) { - throw new Error("Expected `target` to be a canvas."); - } - if (!target.hasAttribute("zooming")) { - target.setAttribute("zooming", true); - const { style } = target; - style.width = style.height = ""; + const { canvas } = this; + if (!canvas) { + return; } - const originalViewport = this.#viewportMap.get(target); + const originalViewport = this.#originalViewport; if (this.viewport !== originalViewport) { // The canvas may have been originally rotated; rotate relative to that. const relativeRotation = - this.viewport.rotation - originalViewport.rotation; - const absRotation = Math.abs(relativeRotation); - let scaleX = 1, - scaleY = 1; - if (absRotation === 90 || absRotation === 270) { + (360 + this.viewport.rotation - originalViewport.rotation) % 360; + if (relativeRotation === 90 || relativeRotation === 270) { const { width, height } = this.viewport; // Scale x and y because of the rotation. - scaleX = height / width; - scaleY = width / height; + const scaleX = height / width; + const scaleY = width / height; + canvas.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX},${scaleY})`; + } else { + canvas.style.transform = + relativeRotation === 0 ? "" : `rotate(${relativeRotation}deg)`; } - target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`; } if (redrawAnnotationLayer && this.annotationLayer) { @@ -877,38 +895,29 @@ class PDFPageView { return this.viewport.convertToPdfPoint(x, y); } - async #finishRenderTask(renderTask, error = null) { - // The renderTask may have been replaced by a new one, so only remove - // the reference to the renderTask if it matches the one that is - // triggering this callback. - if (renderTask === this.renderTask) { - this.renderTask = null; - } - - if (error instanceof RenderingCancelledException) { - this.#renderError = null; - return; + // Wrap the canvas so that if it has a CSS transform for high DPI the + // overflow will be hidden in Firefox. + _ensureCanvasWrapper() { + let canvasWrapper = this.#canvasWrapper; + if (!canvasWrapper) { + canvasWrapper = this.#canvasWrapper = document.createElement("div"); + canvasWrapper.classList.add("canvasWrapper"); + this.#addLayer(canvasWrapper, "canvasWrapper"); } - this.#renderError = error; - - this.renderingState = RenderingStates.FINISHED; - this._resetZoomLayer(/* removeFromDOM = */ true); - - // Ensure that the thumbnails won't become partially (or fully) blank, - // for documents that contain interactive form elements. - this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots; - - this.eventBus.dispatch("pagerendered", { - source: this, - pageNumber: this.id, - cssTransform: false, - timestamp: performance.now(), - error: this.#renderError, - }); + return canvasWrapper; + } - if (error) { - throw error; - } + _getRenderingContext(canvasContext, transform) { + return { + canvasContext, + transform, + viewport: this.viewport, + annotationMode: this.#annotationMode, + optionalContentConfigPromise: this._optionalContentConfigPromise, + annotationCanvasMap: this._annotationCanvasMap, + pageColors: this.pageColors, + isEditing: this.#isEditing, + }; } async draw() { @@ -916,7 +925,7 @@ class PDFPageView { console.error("Must be in new state before drawing"); this.reset(); // Ensure that we reset all state to prevent issues. } - const { div, l10n, pageColors, pdfPage, viewport } = this; + const { div, l10n, pdfPage, viewport } = this; if (!pdfPage) { this.renderingState = RenderingStates.FINISHED; @@ -925,11 +934,7 @@ class PDFPageView { this.renderingState = RenderingStates.RUNNING; - // Wrap the canvas so that if it has a CSS transform for high DPI the - // overflow will be hidden in Firefox. - const canvasWrapper = document.createElement("div"); - canvasWrapper.classList.add("canvasWrapper"); - this.#addLayer(canvasWrapper, "canvasWrapper"); + const canvasWrapper = this._ensureCanvasWrapper(); if ( !this.textLayer && @@ -987,67 +992,21 @@ class PDFPageView { }); } - const renderContinueCallback = cont => { - showCanvas?.(false); - if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) { - this.renderingState = RenderingStates.PAUSED; - this.resume = () => { - this.renderingState = RenderingStates.RUNNING; - cont(); - }; - return; - } - cont(); - }; - const { width, height } = viewport; - const canvas = document.createElement("canvas"); - canvas.setAttribute("role", "presentation"); - - // Keep the canvas hidden until the first draw callback, or until drawing - // is complete when `!this.renderingQueue`, to prevent black flickering. - canvas.hidden = true; - const hasHCM = !!(pageColors?.background && pageColors?.foreground); - - let showCanvas = isLastShow => { - // In HCM, a final filter is applied on the canvas which means that - // before it's applied we've normal colors. Consequently, to avoid to have - // a final flash we just display it once all the drawing is done. - if (!hasHCM || isLastShow) { - canvas.hidden = false; - showCanvas = null; // Only invoke the function once. - } - }; - canvasWrapper.append(canvas); - this.canvas = canvas; + this.#originalViewport = viewport; - const ctx = canvas.getContext("2d", { - alpha: false, - willReadFrequently: !this.#enableHWA, + const { canvas, prevCanvas, ctx } = this._createCanvas(newCanvas => { + // Always inject the canvas as the first element in the wrapper. + canvasWrapper.prepend(newCanvas); }); - const outputScale = (this.outputScale = new OutputScale()); + canvas.setAttribute("role", "presentation"); - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && - this.maxCanvasPixels === 0 - ) { - const invScale = 1 / this.scale; - // Use a scale that makes the canvas have the originally intended size - // of the page. - outputScale.sx *= invScale; - outputScale.sy *= invScale; - this.#hasRestrictedScaling = true; - } else if (this.maxCanvasPixels > 0) { - const pixelsInViewport = width * height; - const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport); - if (outputScale.sx > maxScale || outputScale.sy > maxScale) { - outputScale.sx = maxScale; - outputScale.sy = maxScale; - this.#hasRestrictedScaling = true; - } else { - this.#hasRestrictedScaling = false; - } + if (!this.outputScale) { + this.#computeScale(); } + const { outputScale } = this; + this.#hasRestrictedScaling = this.#needsRestrictedScaling; + const sfx = approximateFraction(outputScale.sx); const sfy = approximateFraction(outputScale.sy); @@ -1073,78 +1032,69 @@ class PDFPageView { this.#scaleRoundY = sfy[1]; } - // Add the viewport so it's known what it was originally drawn with. - this.#viewportMap.set(canvas, viewport); - // Rendering area const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null; - const renderContext = { - canvasContext: ctx, - transform, - viewport, - annotationMode: this.#annotationMode, - optionalContentConfigPromise: this._optionalContentConfigPromise, - annotationCanvasMap: this._annotationCanvasMap, - pageColors, - isEditing: this.#isEditing, - }; - const renderTask = (this.renderTask = pdfPage.render(renderContext)); - renderTask.onContinue = renderContinueCallback; - - const resultPromise = renderTask.promise.then( - async () => { - showCanvas?.(true); - await this.#finishRenderTask(renderTask); - - this.structTreeLayer ||= new StructTreeLayerBuilder( - pdfPage, - viewport.rawDims + const resultPromise = this._drawCanvas( + this._getRenderingContext(ctx, transform), + () => { + prevCanvas?.remove(); + this._resetCanvas(); + }, + renderTask => { + // Ensure that the thumbnails won't become partially (or fully) blank, + // for documents that contain interactive form elements. + this.#useThumbnailCanvas.regularAnnotations = + !renderTask.separateAnnots; + + this.dispatchPageRendered( + /* cssTransform */ false, + /* isDetailView */ false ); + } + ).then(async () => { + this.structTreeLayer ||= new StructTreeLayerBuilder( + pdfPage, + viewport.rawDims + ); - this.#renderTextLayer(); - - if (this.annotationLayer) { - await this.#renderAnnotationLayer(); - } + const textLayerPromise = this.#renderTextLayer(); - const { annotationEditorUIManager } = this.#layerProperties; + if (this.annotationLayer) { + await this.#renderAnnotationLayer(); - if (!annotationEditorUIManager) { - return; + if (this.#enableAutoLinking && this.annotationLayer && this.textLayer) { + await this.#injectLinkAnnotations(textLayerPromise); } - this.drawLayer ||= new DrawLayerBuilder({ - pageIndex: this.id, - }); - await this.#renderDrawLayer(); - this.drawLayer.setParent(canvasWrapper); + } - this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({ - uiManager: annotationEditorUIManager, - pdfPage, - l10n, - structTreeLayer: this.structTreeLayer, - accessibilityManager: this._accessibilityManager, - annotationLayer: this.annotationLayer?.annotationLayer, - textLayer: this.textLayer, - drawLayer: this.drawLayer.getDrawLayer(), - onAppend: annotationEditorLayerDiv => { - this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer"); - }, - }); - this.#renderAnnotationEditorLayer(); - }, - error => { - // When zooming with a `drawingDelay` set, avoid temporarily showing - // a black canvas if rendering was cancelled before the `onContinue`- - // callback had been invoked at least once. - if (!(error instanceof RenderingCancelledException)) { - showCanvas?.(true); - } - return this.#finishRenderTask(renderTask, error); + const { annotationEditorUIManager } = this.#layerProperties; + + if (!annotationEditorUIManager) { + return; } - ); + this.drawLayer ||= new DrawLayerBuilder({ + pageIndex: this.id, + }); + await this.#renderDrawLayer(); + this.drawLayer.setParent(canvasWrapper); + + this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({ + uiManager: annotationEditorUIManager, + pdfPage, + l10n, + structTreeLayer: this.structTreeLayer, + accessibilityManager: this._accessibilityManager, + annotationLayer: this.annotationLayer?.annotationLayer, + textLayer: this.textLayer, + drawLayer: this.drawLayer.getDrawLayer(), + onAppend: annotationEditorLayerDiv => { + this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer"); + }, + }); + this.#renderAnnotationEditorLayer(); + }); if (pdfPage.isPureXfa) { if (!this.xfaLayer) { @@ -1161,10 +1111,8 @@ class PDFPageView { div.setAttribute("data-loaded", true); - this.eventBus.dispatch("pagerender", { - source: this, - pageNumber: this.id, - }); + this.dispatchPageRender(); + return resultPromise; } diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index 97bf548fdb136..370b491d1dbed 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -148,9 +148,7 @@ class PDFPrintService { this.scratchCanvas = null; activeService = null; ensureOverlay().then(function () { - if (overlayManager.active === dialog) { - overlayManager.close(dialog); - } + overlayManager.closeIfActive(dialog); }); } @@ -260,31 +258,27 @@ window.print = function () { if (!activeService) { console.error("Expected print service to be initialized."); ensureOverlay().then(function () { - if (overlayManager.active === dialog) { - overlayManager.close(dialog); - } + overlayManager.closeIfActive(dialog); }); - return; // eslint-disable-line no-unsafe-finally + } else { + const activeServiceOnEntry = activeService; + activeService + .renderPages() + .then(() => activeServiceOnEntry.performPrint()) + .catch(() => { + // Ignore any error messages. + }) + .then(() => { + // aborts acts on the "active" print request, so we need to check + // whether the print request (activeServiceOnEntry) is still active. + // Without the check, an unrelated print request (created after + // aborting this print request while the pages were being generated) + // would be aborted. + if (activeServiceOnEntry.active) { + abort(); + } + }); } - const activeServiceOnEntry = activeService; - activeService - .renderPages() - .then(function () { - return activeServiceOnEntry.performPrint(); - }) - .catch(function () { - // Ignore any error messages. - }) - .then(function () { - // aborts acts on the "active" print request, so we need to check - // whether the print request (activeServiceOnEntry) is still active. - // Without the check, an unrelated print request (created after aborting - // this print request while the pages were being generated) would be - // aborted. - if (activeServiceOnEntry.active) { - abort(); - } - }); } }; diff --git a/web/pdf_rendering_queue.js b/web/pdf_rendering_queue.js index 60e885e225c4c..bbcb51fc3ee7a 100644 --- a/web/pdf_rendering_queue.js +++ b/web/pdf_rendering_queue.js @@ -102,15 +102,23 @@ class PDFRenderingQueue { * @param {Array} views * @param {boolean} scrolledDown * @param {boolean} [preRenderExtra] + * @param {boolean} [ignoreDetailViews] */ - getHighestPriority(visible, views, scrolledDown, preRenderExtra = false) { + getHighestPriority( + visible, + views, + scrolledDown, + preRenderExtra = false, + ignoreDetailViews = false + ) { /** * The state has changed. Figure out which page has the highest priority to * render next (if any). * * Priority: * 1. visible pages - * 2. if last scrolled down, the page after the visible pages, or + * 2. zoomed-in partial views of visible pages, unless `ignoreDetailViews` + * 3. if last scrolled down, the page after the visible pages, or * if last scrolled up, the page before the visible pages */ const visibleViews = visible.views, @@ -125,6 +133,16 @@ class PDFRenderingQueue { return view; } } + + if (!ignoreDetailViews) { + for (let i = 0; i < numVisible; i++) { + const { detailView } = visibleViews[i].view; + if (detailView && !this.isViewFinished(detailView)) { + return detailView; + } + } + } + const firstId = visible.first.id, lastId = visible.last.id; @@ -201,7 +219,7 @@ class PDFRenderingQueue { if (reason instanceof RenderingCancelledException) { return; } - console.error(`renderView: "${reason}"`); + console.error("renderView:", reason); }); break; } diff --git a/web/pdf_scripting_manager.js b/web/pdf_scripting_manager.js index 63d08c60459ef..402613f87f507 100644 --- a/web/pdf_scripting_manager.js +++ b/web/pdf_scripting_manager.js @@ -105,7 +105,7 @@ class PDFScriptingManager { try { this.#scripting = this.#initScripting(); } catch (error) { - console.error(`setDocument: "${error.message}".`); + console.error("setDocument:", error); await this.#destroyScripting(); return; @@ -192,7 +192,7 @@ class PDFScriptingManager { eventBus.dispatch("sandboxcreated", { source: this }); } catch (error) { - console.error(`setDocument: "${error.message}".`); + console.error("setDocument:", error); await this.#destroyScripting(); return; diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js index 7ec1b98376fcd..b6bdf36f39921 100644 --- a/web/pdf_thumbnail_viewer.js +++ b/web/pdf_thumbnail_viewer.js @@ -292,7 +292,9 @@ class PDFThumbnailViewer { const thumbView = this.renderingQueue.getHighestPriority( visibleThumbs, this._thumbnails, - scrollAhead + scrollAhead, + /* preRenderExtra */ false, + /* ignoreDetailViews */ true ); if (thumbView) { this.#ensurePdfPageLoaded(thumbView).then(() => { diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css index ec7bceb69a9aa..d12767910e9b6 100644 --- a/web/pdf_viewer.css +++ b/web/pdf_viewer.css @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import url(message_bar.css); @import url(dialog.css); @import url(text_layer_builder.css); @import url(annotation_layer_builder.css); @@ -26,14 +27,19 @@ --page-border: 9px solid transparent; --spreadHorizontalWrapped-margin-LR: -3.5px; --loading-icon-delay: 400ms; -} + --focus-ring-color: #0060df; + --focus-ring-outline: 2px solid var(--focus-ring-color); + + @media (prefers-color-scheme: dark) { + --focus-ring-color: #0df; + } -@media screen and (forced-colors: active) { - :root { + @media screen and (forced-colors: active) { --pdfViewer-padding-bottom: 9px; --page-margin: 8px auto -1px; --page-border: 1px solid CanvasText; --spreadHorizontalWrapped-margin-LR: 3.5px; + --focus-ring-color: CanvasText; } } @@ -82,19 +88,14 @@ height: 100%; canvas { + position: absolute; + top: 0; + left: 0; margin: 0; display: block; width: 100%; height: 100%; - - &[hidden] { - display: none; - } - - &[zooming] { - width: 100%; - height: 100%; - } + contain: content; .structTree { contain: strict; @@ -104,6 +105,8 @@ } .pdfViewer .page { + --user-unit: 1; + --total-scale-factor: calc(var(--scale-factor) * var(--user-unit)); --scale-round-x: 1px; --scale-round-y: 1px; diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 9db9c6adcfdc6..f0ef305d5f36d 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -35,6 +35,7 @@ import { PermissionFlag, PixelsPerInch, shadow, + stopEvent, version, } from "pdfjs-lib"; import { @@ -117,6 +118,11 @@ function isValidAnnotationEditorMode(mode) { * @property {number} [maxCanvasPixels] - The maximum supported canvas size in * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + * @property {boolean} [enableDetailCanvas] - When enabled, if the rendered + * pages would need a canvas that is larger than `maxCanvasPixels`, it will + * draw a second canvas on top of the CSS-zoomed one, that only renders the + * part of the page that is close to the viewport. The default value is + * `true`. * @property {IL10n} [l10n] - Localization service. * @property {boolean} [enablePermissions] - Enables PDF document permissions, * when they exist. The default value is `false`. @@ -125,6 +131,10 @@ function isValidAnnotationEditorMode(mode) { * mode. * @property {boolean} [enableHWA] - Enables hardware acceleration for * rendering. The default value is `false`. + * @property {boolean} [supportsPinchToZoom] - Enable zooming on pinch gesture. + * The default value is `true`. + * @property {boolean} [enableAutoLinking] - Enable creation of hyperlinks from + * text that look like URLs. The default value is `false`. */ class PDFPageViewBuffer { @@ -213,6 +223,8 @@ class PDFViewer { #containerTopLeft = null; + #editorUndoBar = null; + #enableHWA = false; #enableHighlightFloatingButton = false; @@ -223,10 +235,14 @@ class PDFViewer { #enableNewAltTextWhenAddingImage = false; + #enableAutoLinking = false; + #eventAbortController = null; #mlManager = null; + #scrollTimeoutId = null; + #switchAnnotationEditorModeAC = null; #switchAnnotationEditorModeTimeoutId = null; @@ -245,6 +261,10 @@ class PDFViewer { #scaleTimeoutId = null; + #signatureManager = null; + + #supportsPinchToZoom = true; + #textLayerMode = TextLayerMode.ENABLE; /** @@ -280,6 +300,8 @@ class PDFViewer { this.downloadManager = options.downloadManager || null; this.findController = options.findController || null; this.#altTextManager = options.altTextManager || null; + this.#signatureManager = options.signatureManager || null; + this.#editorUndoBar = options.editorUndoBar || null; if (this.findController) { this.findController.onIsPageVisible = pageNumber => @@ -304,6 +326,7 @@ class PDFViewer { this.removePageBorders = options.removePageBorders || false; } this.maxCanvasPixels = options.maxCanvasPixels; + this.enableDetailCanvas = options.enableDetailCanvas ?? true; this.l10n = options.l10n; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { this.l10n ||= new GenericL10n(); @@ -312,6 +335,8 @@ class PDFViewer { this.pageColors = options.pageColors || null; this.#mlManager = options.mlManager || null; this.#enableHWA = options.enableHWA || false; + this.#supportsPinchToZoom = options.supportsPinchToZoom !== false; + this.#enableAutoLinking = options.enableAutoLinking || false; this.defaultRenderingQueue = !options.renderingQueue; if ( @@ -748,8 +773,7 @@ class PDFViewer { this.#getAllTextInProgress || textLayerMode === TextLayerMode.ENABLE_PERMISSIONS ) { - event.preventDefault(); - event.stopPropagation(); + stopEvent(event); return; } this.#getAllTextInProgress = true; @@ -786,8 +810,7 @@ class PDFViewer { classList.remove("copyAll"); }); - event.preventDefault(); - event.stopPropagation(); + stopEvent(event); } } @@ -858,7 +881,7 @@ class PDFViewer { eventBus._on("pagerender", onBeforeDraw, { signal }); const onAfterDraw = evt => { - if (evt.cssTransform) { + if (evt.cssTransform || evt.isDetailView) { return; } this._onePageRenderedCapability.resolve({ timestamp: evt.timestamp }); @@ -901,6 +924,7 @@ class PDFViewer { this.container, viewer, this.#altTextManager, + this.#signatureManager, eventBus, pdfDocument, pageColors, @@ -908,16 +932,16 @@ class PDFViewer { this.#enableHighlightFloatingButton, this.#enableUpdatedAddImage, this.#enableNewAltTextWhenAddingImage, - this.#mlManager + this.#mlManager, + this.#editorUndoBar, + this.#supportsPinchToZoom ); eventBus.dispatch("annotationeditoruimanager", { source: this, uiManager: this.#annotationEditorUIManager, }); if (mode !== AnnotationEditorType.NONE) { - if (mode === AnnotationEditorType.STAMP) { - this.#mlManager?.loadModel("altText"); - } + this.#preloadEditingData(mode); this.#annotationEditorUIManager.updateMode(mode); } } else { @@ -977,10 +1001,12 @@ class PDFViewer { annotationMode, imageResourcesPath: this.imageResourcesPath, maxCanvasPixels: this.maxCanvasPixels, + enableDetailCanvas: this.enableDetailCanvas, pageColors, l10n: this.l10n, layerProperties: this._layerProperties, enableHWA: this.#enableHWA, + enableAutoLinking: this.#enableAutoLinking, }); this._pages.push(pageView); } @@ -1147,6 +1173,7 @@ class PDFViewer { this.#hiddenCopyElement?.remove(); this.#hiddenCopyElement = null; + this.#cleanupTimeouts(); this.#cleanupSwitchAnnotationEditorMode(); } @@ -1217,6 +1244,15 @@ class PDFViewer { if (this.pagesCount === 0) { return; } + + if (this.#scrollTimeoutId) { + clearTimeout(this.#scrollTimeoutId); + } + this.#scrollTimeoutId = setTimeout(() => { + this.#scrollTimeoutId = null; + this.update(); + }, 100); + this.update(); } @@ -1655,6 +1691,15 @@ class PDFViewer { const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1); this.#buffer.resize(newCacheSize, visible.ids); + for (const { view, visibleArea } of visiblePages) { + view.updateVisibleArea(visibleArea); + } + for (const view of this.#buffer) { + if (!visible.ids.has(view.id)) { + view.updateVisibleArea(null); + } + } + this.renderingQueue.renderHighestPriority(visible); const isSimpleLayout = @@ -1818,11 +1863,21 @@ class PDFViewer { this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL; + // If we are scrolling and the rendering of a detail view was just + // cancelled, it's because the user is scrolling too quickly and so + // we constantly need to re-render a different area. + // Don't attempt to re-render it: this will be done once the user + // stops scrolling. + const ignoreDetailViews = + this.#scrollTimeoutId !== null && + visiblePages.views.some(page => page.detailView?.renderingCancelled); + const pageView = this.renderingQueue.getHighestPriority( visiblePages, this._pages, scrollAhead, - preRenderExtra + preRenderExtra, + ignoreDetailViews ); if (pageView) { @@ -2286,6 +2341,17 @@ class PDFViewer { ]); } + #cleanupTimeouts() { + if (this.#scaleTimeoutId !== null) { + clearTimeout(this.#scaleTimeoutId); + this.#scaleTimeoutId = null; + } + if (this.#scrollTimeoutId !== null) { + clearTimeout(this.#scrollTimeoutId); + this.#scrollTimeoutId = null; + } + } + #cleanupSwitchAnnotationEditorMode() { this.#switchAnnotationEditorModeAC?.abort(); this.#switchAnnotationEditorModeAC = null; @@ -2296,6 +2362,18 @@ class PDFViewer { } } + #preloadEditingData(mode) { + switch (mode) { + case AnnotationEditorType.STAMP: + this.#mlManager?.loadModel("altText"); + break; + case AnnotationEditorType.SIGNATURE: + // Start to load the signature data. + this.#signatureManager?.loadSignatures(); + break; + } + } + get annotationEditorMode() { return this.#annotationEditorUIManager ? this.#annotationEditorMode @@ -2326,15 +2404,24 @@ class PDFViewer { if (!this.pdfDocument) { return; } - if (mode === AnnotationEditorType.STAMP) { - this.#mlManager?.loadModel("altText"); - } + this.#preloadEditingData(mode); - const { eventBus } = this; - const updater = () => { + const { eventBus, pdfDocument } = this; + const updater = async () => { this.#cleanupSwitchAnnotationEditorMode(); this.#annotationEditorMode = mode; - this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard); + await this.#annotationEditorUIManager.updateMode( + mode, + editId, + isFromKeyboard + ); + if ( + mode !== this.#annotationEditorMode || + pdfDocument !== this.pdfDocument + ) { + // Since `updateMode` is async, the active mode could have changed. + return; + } eventBus.dispatch("annotationeditormodechanged", { source: this, mode, @@ -2391,10 +2478,8 @@ class PDFViewer { for (const pageView of this._pages) { pageView.update(updateArgs); } - if (this.#scaleTimeoutId !== null) { - clearTimeout(this.#scaleTimeoutId); - this.#scaleTimeoutId = null; - } + this.#cleanupTimeouts(); + if (!noUpdate) { this.update(); } diff --git a/web/pdfjs.js b/web/pdfjs.js index e9b956dbdf401..2c8f7fff7e48f 100644 --- a/web/pdfjs.js +++ b/web/pdfjs.js @@ -21,6 +21,7 @@ const { AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, + AnnotationType, build, ColorPicker, createValidAbsoluteUrl, @@ -31,13 +32,14 @@ const { getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, + getUuid, getXfaPageViewport, GlobalWorkerOptions, ImageKind, InvalidPDFException, isDataScheme, isPdfFile, - MissingPDFException, + isValidExplicitDest, noContextMenu, normalizeUnicode, OPS, @@ -49,10 +51,14 @@ const { PermissionFlag, PixelsPerInch, RenderingCancelledException, + ResponseException, setLayerDimensions, shadow, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, TextLayer, - UnexpectedResponseException, + TouchManager, Util, VerbosityLevel, version, @@ -67,6 +73,7 @@ export { AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, + AnnotationType, build, ColorPicker, createValidAbsoluteUrl, @@ -77,13 +84,14 @@ export { getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, + getUuid, getXfaPageViewport, GlobalWorkerOptions, ImageKind, InvalidPDFException, isDataScheme, isPdfFile, - MissingPDFException, + isValidExplicitDest, noContextMenu, normalizeUnicode, OPS, @@ -95,10 +103,14 @@ export { PermissionFlag, PixelsPerInch, RenderingCancelledException, + ResponseException, setLayerDimensions, shadow, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, TextLayer, - UnexpectedResponseException, + TouchManager, Util, VerbosityLevel, version, diff --git a/web/signature_manager.css b/web/signature_manager.css new file mode 100644 index 0000000000000..85a6543972a27 --- /dev/null +++ b/web/signature_manager.css @@ -0,0 +1,826 @@ +/* Copyright 2022 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:root { + --clear-signature-button-icon: url(images/editor-toolbar-delete.svg); + --signature-bg: #f9f9fb; + --signature-hover-bg: #f0f0f4; + --button-signature-bg: transparent; + --button-signature-color: var(--main-color); + --button-signature-active-bg: #cfcfd8; + --button-signature-active-border: none; + --button-signature-active-color: var(--button-signature-color); + --button-signature-border: none; + --button-signature-hover-bg: #e0e0e6; + --button-signature-hover-color: var(--button-signature-color); + + @media (prefers-color-scheme: dark) { + --signature-bg: #2b2a33; + --signature-hover-bg: var(--signature-bg); + --button-signature-active-bg: #5b5b66; + --button-signature-hover-bg: #52525e; + } + + @media screen and (forced-colors: active) { + --signature-bg: HighlightText; + --signature-hover-bg: var(--signature-bg); + --button-signature-bg: HighlightText; + --button-signature-color: ButtonText; + --button-signature-active-bg: ButtonText; + --button-signature-active-color: HighlightText; + --button-signature-border: 1px solid ButtonText; + --button-signature-hover-bg: Highlight; + --button-signature-hover-color: HighlightText; + } +} + +.signatureDialog { + --primary-color: var(--text-primary-color); + --description-input-color: var(--primary-color); + --border-color: #8f8f9d; + + @media screen and (forced-colors: active) { + --primary-color: ButtonText; + --border-color: ButtonText; + } + + width: 570px; + max-width: 100%; + min-width: 300px; + padding: 16px 0; + + .mainContainer { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + + span:not([role="sectionhead"]) { + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: normal; + } + + .title { + margin-inline-start: 16px; + } + } + + .inputWithClearButton { + --button-dimension: 24px; + + --closing-button-icon: url(images/messageBar_closingButton.svg); + --closing-button-color: var(--primary-color); + + width: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + + > input { + width: 100%; + height: 32px; + padding-inline: 8px calc(4px + var(--button-dimension)); + box-sizing: border-box; + background-color: transparent; + border-radius: 4px; + border: 1px solid var(--border-color); + color: var(--description-input-color); + } + + .clearInputButton { + position: absolute; + inset-block-start: 4px; + inset-inline-end: 4px; + display: inline-block; + width: var(--button-dimension); + height: var(--button-dimension); + background-color: var(--closing-button-color); + mask-size: cover; + mask-image: var(--closing-button-icon); + padding: 0; + border: 0; + } + } +} + +#addSignatureDialog { + --secondary-color: var(--text-secondary-color); + --bg-hover: #e0e0e6; + --tab-top-line-active-color: #0060df; + --tab-top-line-active-hover-color: var(--tab-text-hover-color); + --tab-top-line-hover-color: #8f8f9d; + --tab-top-line-inactive-color: #cfcfd8; + --tab-bottom-line-active-color: var(--tab-top-line-inactive-color); + --tab-bottom-line-hover-color: var(--tab-top-line-inactive-color); + --tab-bottom-line-inactive-color: var(--tab-top-line-inactive-color); + --tab-bg: var(--dialog-bg-color); + --tab-bg-active-color: var(--tab-bg); + --tab-bg-active-hover-color: var(--bg-hover); + --tab-bg-hover: var(--bg-hover); + --tab-text-color: var(--primary-color); + --tab-text-active-color: var(--tab-top-line-active-color); + --tab-text-active-hover-color: var(--tab-text-hover-color); + --tab-text-hover-color: var(--tab-text-color); + --signature-placeholder-color: var(--secondary-color); + --signature-draw-placeholder-color: var(--primary-color); + --signature-color: var(--primary-color); + --clear-signature-button-border-width: 0; + --clear-signature-button-border-style: solid; + --clear-signature-button-border-color: transparent; + --clear-signature-button-border-disabled-color: transparent; + --clear-signature-button-color: var(--primary-color); + --clear-signature-button-hover-color: var(--clear-signature-button-color); + --clear-signature-button-active-color: var(--clear-signature-button-color); + --clear-signature-button-disabled-color: var(--clear-signature-button-color); + --clear-signature-button-focus-color: var(--clear-signature-button-color); + --clear-signature-button-bg: var(--dialog-bg-color); + --clear-signature-button-bg-hover: var(--bg-hover); + --clear-signature-button-bg-active: #cfcfd8; + --clear-signature-button-bg-focus: #f0f0f4; + --clear-signature-button-bg-disabled: color-mix( + in srgb, + #f0f0f4, + transparent 40% + ); + --save-warning-color: var(--secondary-color); + --thickness-bg: var(--dialog-bg-color); + --thickness-label-color: var(--primary-color); + --thickness-slider-color: var(--primary-color); + --draw-cursor: url(images/cursor-editorInk.svg) 0 16, pointer; + + @media (prefers-color-scheme: dark) { + /* TODO: Update the dialog colors for dark mode but in dialog.css */ + --dialog-bg-color: #42414d; + --bg-hover: #52525e; + --primary-color: #fbfbfe; + --secondary-color: #cfcfd8; + --tab-top-line-active-color: #0df; + --tab-top-line-inactive-color: #8f8f9d; + --clear-signature-button-bg-active: #5b5b66; + --clear-signature-button-bg-focus: #2b2a33; + --clear-signature-button-bg-disabled: color-mix( + in srgb, + #2b2a33, + transparent 40% + ); + } + + @media screen and (forced-colors: active) { + --secondary-color: ButtonText; + --bg: HighlightText; + --bg-hover: var(--bg); + --tab-top-line-active-color: ButtonText; + --tab-top-line-active-hover-color: HighlightText; + --tab-top-line-hover-color: SelectedItem; + --tab-top-line-inactive-color: ButtonText; + --tab-bottom-line-active-color: var(--tab-top-line-active-color); + --tab-bottom-line-hover-color: var(--tab-top-line-hover-color); + --tab-bg: var(--bg); + --tab-bg-active-color: SelectedItem; + --tab-bg-active-hover-color: SelectedItem; + --tab-text-color: ButtonText; + --tab-text-active-color: HighlightText; + --tab-text-active-hover-color: HighlightText; + --tab-text-hover-color: SelectedItem; + --signature-color: ButtonText; + --clear-signature-button-border-width: 1px; + --clear-signature-button-border-style: solid; + --clear-signature-button-border-color: ButtonText; + --clear-signature-button-border-disabled-color: GrayText; + --clear-signature-button-color: ButtonText; + --clear-signature-button-hover-color: HighlightText; + --clear-signature-button-active-color: SelectedItem; + --clear-signature-button-focus-color: CanvasText; + --clear-signature-button-disabled-color: GrayText; + --clear-signature-button-bg: var(--bg); + --clear-signature-button-bg-hover: SelectedItem; + --clear-signature-button-bg-active: var(--bg); + --clear-signature-button-bg-focus: var(--bg); + --clear-signature-button-bg-disabled: var(--bg); + --thickness-bg: Canvas; + --thickness-label-color: CanvasText; + --thickness-slider-color: ButtonText; + } + + #addSignatureDialogLabel { + overflow: hidden; + position: absolute; + inset: 0; + width: 0; + height: 0; + } + + &.waiting::after { + content: ""; + cursor: wait; + position: absolute; + inset: 0; + width: 100%; + height: 100%; + } + + .mainContainer { + [role="tablist"] { + width: 100%; + display: flex; + align-items: flex-start; + gap: 0; + + > [role="tab"] { + flex: 1 0 0; + align-self: stretch; + background-color: var(--tab-bg); + padding-inline: 0; + cursor: default; + + border-inline: 0; + border-block-width: 1px; + border-block-style: solid; + border-block-start-color: var(--tab-top-line-inactive-color); + border-block-end-color: var(--tab-bottom-line-inactive-color); + border-radius: 0; + + font: menu; + font-size: 13px; + font-style: normal; + line-height: normal; + font-weight: 400; + color: var(--tab-text-color); + + &:hover { + border-block-start-width: 2px; + border-block-start-color: var(--tab-top-line-hover-color); + border-block-end-color: var(--tab-bottom-line-hover-color); + background-color: var(--tab-bg-hover); + color: var(--tab-text-hover-color); + } + + &:focus-visible { + outline: 2px solid var(--tab-top-line-active-color); + outline-offset: -2px; + } + + &[aria-selected="true"] { + border-block-start-width: 2px; + border-block-start-color: var(--tab-top-line-active-color); + border-block-end-color: var(--tab-bottom-line-active-color); + background-color: var(--tab-bg-active-color); + font-weight: 590; + color: var(--tab-text-active-color); + + &:hover { + border-block-start-color: var(--tab-top-line-active-hover-color); + background-color: var(--tab-bg-active-hover-color); + color: var(--tab-text-active-hover-color); + } + } + } + } + + #addSignatureActionContainer { + width: 100%; + height: auto; + display: flex; + flex-direction: column; + align-items: flex-end; + align-self: stretch; + gap: 12px; + padding-inline: 16px; + box-sizing: border-box; + + > [role="tabpanel"] { + position: relative; + width: 100%; + height: 220px; + background-color: var(--signature-bg); + border-radius: 4px; + + > svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + background-color: transparent; + } + + &#addSignatureTypeContainer { + display: none; + + #addSignatureTypeInput { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + border: 0; + padding: 0; + text-align: center; + color: var(--signature-color); + background-color: transparent; + + font-family: + "Brush script", "Apple Chancery", "Segoe script", + "Freestyle Script", "Palace Script MT", "Brush Script MT", TK, + cursive, serif; + font-size: 44px; + font-style: italic; + font-weight: 400; + + &::placeholder { + color: var(--signature-placeholder-color); + text-align: center; + + font: menu; + font-style: normal; + font-weight: 274; + font-size: 44px; + line-height: normal; + } + } + } + + &#addSignatureDrawContainer { + display: none; + + > span { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: grid; + align-items: center; + justify-content: center; + + background-color: transparent; + color: var(--signature-placeholder-color); + user-select: none; + } + + > svg { + stroke: var(--primary-color); + fill: none; + stroke-opacity: 1; + stroke-linecap: round; + stroke-linejoin: round; + stroke-miterlimit: 10; + + &:hover { + cursor: var(--draw-cursor); + } + } + + #thickness { + position: absolute; + width: 100%; + inset-block-end: 0; + display: grid; + align-items: center; + justify-content: center; + pointer-events: none; + + > span { + color: var(--signature-draw-placeholder-color); + } + + > div { + width: auto; + height: auto; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 6px 8px; + margin: 0; + background-color: var(--thickness-bg); + border-radius: 4px 4px 0 0; + pointer-events: auto; + + > label { + color: var(--thickness-label-color); + } + + > input { + width: 100px; + height: 14px; + background-color: transparent; + + /*#if !MOZCENTRAL*/ + &::-webkit-slider-runnable-track, + /*#endif*/ + &::-moz-range-track, + &::-moz-range-progress { + background-color: var(--thickness-slider-color); + } + + /*#if !MOZCENTRAL*/ + &::-webkit-slider-thumb, + /*#endif*/ + &::-moz-range-thumb { + background-color: var(--thickness-bg); + } + + border-radius: 4.5px; + border: 0; + color: var(--signature-color); + } + } + } + } + + &#addSignatureImageContainer { + display: none; + + > svg { + stroke: none; + stroke-width: 0; + fill: var(--primary-color); + fill-opacity: 1; + } + + #addSignatureImagePlaceholder { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: transparent; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + a { + text-decoration: underline; + cursor: pointer; + } + } + + #addSignatureFilePicker { + visibility: hidden; + position: relative; + width: 0; + height: 0; + } + } + } + + &[data-selected="type"] > #addSignatureTypeContainer, + &[data-selected="draw"] > #addSignatureDrawContainer, + &[data-selected="image"] > #addSignatureImageContainer { + display: block; + } + + #addSignatureControls { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 12px; + align-self: stretch; + + #horizontalContainer { + display: flex; + align-items: flex-end; + gap: 16px; + align-self: stretch; + + #addSignatureDescriptionContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + flex: 1 0 0; + + > label { + width: auto; + } + } + + #clearSignatureButton { + display: flex; + height: 32px; + padding: 4px 8px; + align-items: center; + background-color: var(--clear-signature-button-bg); + border-width: var(--clear-signature-button-border-width); + border-style: var(--clear-signature-button-border-style); + border-color: var(--clear-signature-button-border-color); + border-radius: 4px; + + > span { + display: flex; + height: 24px; + align-items: center; + gap: 4px; + flex-shrink: 0; + + color: var(--clear-signature-button-color); + + &::after { + content: ""; + display: inline-block; + width: 16px; + height: 16px; + mask-image: var(--clear-signature-button-icon); + mask-size: cover; + background-color: var(--clear-signature-button-color); + flex-shrink: 0; + } + } + + &:hover { + background-color: var(--clear-signature-button-bg-hover); + + > span { + color: var(--clear-signature-button-hover-color); + &::after { + background-color: var(--clear-signature-button-hover-color); + } + } + } + + &:active { + background-color: var(--clear-signature-button-bg-active); + + > span { + color: var(--clear-signature-button-active-color); + &::after { + background-color: var(--clear-signature-button-active-color); + } + } + } + + &:focus-visible { + background-color: var(--clear-signature-button-bg-focus); + + > span { + color: var(--clear-signature-button-focus-color); + &::after { + background-color: var(--clear-signature-button-focus-color); + } + } + } + + &:disabled { + background-color: var(--clear-signature-button-bg-disabled); + border-color: var(--clear-signature-button-border-disabled-color); + + > span { + color: var(--clear-signature-button-disabled-color); + &::after { + background-color: var( + --clear-signature-button-disabled-color + ); + } + } + } + } + } + + #addSignatureSaveContainer { + display: grid; + grid-template-columns: max-content max-content; + gap: 4px; + width: 100%; + + > input { + margin: 0; + } + + > label { + user-select: none; + } + + &:not(.fullStorage) #addSignatureSaveWarning { + display: none; + } + + &.fullStorage #addSignatureSaveWarning { + display: block; + opacity: 1; + color: var(--save-warning-color); + font-size: 11px; + } + + &:is([disabled], .fullStorage) { + pointer-events: none; + + > :not(#addSignatureSaveWarning) { + opacity: 0.4; + } + } + } + } + } + } +} + +#editSignatureDescriptionDialog { + .mainContainer { + padding-inline: 16px; + box-sizing: border-box; + + .title { + margin-inline-start: 0; + } + + #editSignatureDescriptionAndView { + width: auto; + display: flex; + justify-content: flex-end; + align-items: flex-start; + gap: 12px; + align-self: stretch; + + #editSignatureDescriptionContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + flex: 1 1 auto; + } + + > svg { + width: 210px; + height: 180px; + padding: 8px; + background-color: var(--signature-bg); + + > path { + stroke: var(--button-signature-color); + stroke-width: 1px; + stroke-linecap: round; + stroke-linejoin: round; + stroke-miterlimit: 10; + vector-effect: non-scaling-stroke; + fill: none; + + &.contours { + fill: var(--button-signature-color); + stroke-width: 0.5px; + } + } + } + } + } +} + +#editorSignatureParamsToolbar { + padding: 8px; + + #addSignatureDoorHanger { + gap: 8px; + padding: 2px; + + .toolbarAddSignatureButtonContainer { + height: 32px; + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + gap: 8px; + + button { + border: var(--button-signature-border); + border-radius: 4px; + background-color: var(--button-signature-bg); + color: var(--button-signature-color); + + &:hover { + background-color: var(--button-signature-hover-bg); + } + + &:active { + border: var(--button-signature-active-border); + background-color: var(--button-signature-active-bg); + color: var(--button-signature-active-color); + + &::before { + background-color: var(--button-signature-active-color); + } + } + + &:focus-visible { + outline: var(--focus-ring-outline); + + &::before { + background-color: var(--button-signature-color); + } + } + } + + .deleteButton { + &::before { + mask-image: var(--clear-signature-button-icon); + } + } + + .toolbarAddSignatureButton { + width: auto; + height: 100%; + min-height: var(--menuitem-height); + aspect-ratio: unset; + display: flex; + align-items: center; + justify-content: flex-start; + outline: none; + border-radius: 4px; + box-sizing: border-box; + font: message-box; + position: relative; + flex: 1 1 auto; + padding: 0; + gap: 8px; + text-align: start; + white-space: normal; + cursor: default; + overflow: hidden; + + > svg { + display: inline-block; + height: 100%; + aspect-ratio: 1; + background-color: var(--signature-bg); + flex: none; + padding: 4px; + box-sizing: border-box; + border: none; + border-radius: 4px; + + > path { + stroke: var(--button-signature-color); + stroke-width: 1px; + stroke-linecap: round; + stroke-linejoin: round; + stroke-miterlimit: 10; + vector-effect: non-scaling-stroke; + fill: none; + + &.contours { + fill: var(--button-signature-color); + stroke-width: 0.5px; + } + } + } + + &:is(:hover, :active) > svg { + border-radius: 4px 0 0 4px; + background-color: var(--signature-hover-bg); + } + + &:hover { + > span { + color: var(--button-signature-hover-color); + } + } + + &:active { + background-color: var(--button-signature-active-bg); + } + + &:is([disabled="disabled"], [disabled]) { + opacity: 0.5; + pointer-events: none; + } + + > span { + height: auto; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 1 auto; + font: menu; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: normal; + overflow: hidden; + } + } + } + } +} + +.editDescription.altText { + --alt-text-add-image: url(images/editor-toolbar-edit.svg) !important; + + &::before { + width: 16px !important; + height: 16px !important; + } +} diff --git a/web/signature_manager.js b/web/signature_manager.js new file mode 100644 index 0000000000000..84429df843e69 --- /dev/null +++ b/web/signature_manager.js @@ -0,0 +1,1062 @@ +/* Copyright 2025 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AnnotationEditorParamsType, + DOMSVGFactory, + noContextMenu, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, +} from "pdfjs-lib"; + +// Default height of the added signature in page coordinates. +const DEFAULT_HEIGHT_IN_PAGE = 40; + +class SignatureManager { + #addButton; + + #tabsToAltText = null; + + #clearButton; + + #clearDescription; + + #currentEditor; + + #description; + + #dialog; + + #drawCurves = null; + + #drawPlaceholder; + + #drawPath = null; + + #drawPathString = ""; + + #drawPoints = null; + + #drawSVG; + + #drawThickness; + + #errorBar; + + #extractedSignatureData = null; + + #imagePath = null; + + #imagePicker; + + #imagePickerLink; + + #imagePlaceholder; + + #imageSVG; + + #saveCheckbox; + + #saveContainer; + + #tabButtons; + + #addSignatureToolbarButton; + + #loadSignaturesPromise = null; + + #typeInput; + + #currentTab = null; + + #currentTabAC = null; + + #hasDescriptionChanged = false; + + #eventBus; + + #l10n; + + #overlayManager; + + #editDescriptionDialog; + + #signatureStorage; + + #uiManager = null; + + static #l10nDescription = null; + + constructor( + { + dialog, + panels, + typeButton, + typeInput, + drawButton, + drawPlaceholder, + drawSVG, + drawThickness, + imageButton, + imageSVG, + imagePlaceholder, + imagePicker, + imagePickerLink, + description, + clearButton, + cancelButton, + addButton, + errorCloseButton, + errorBar, + saveCheckbox, + saveContainer, + }, + editSignatureElements, + addSignatureToolbarButton, + overlayManager, + l10n, + signatureStorage, + eventBus + ) { + this.#addButton = addButton; + this.#clearButton = clearButton; + this.#clearDescription = description.lastElementChild; + this.#description = description.firstElementChild; + this.#dialog = dialog; + this.#drawSVG = drawSVG; + this.#drawPlaceholder = drawPlaceholder; + this.#drawThickness = drawThickness; + this.#errorBar = errorBar; + this.#imageSVG = imageSVG; + this.#imagePlaceholder = imagePlaceholder; + this.#imagePicker = imagePicker; + this.#imagePickerLink = imagePickerLink; + this.#overlayManager = overlayManager; + this.#saveCheckbox = saveCheckbox; + this.#saveContainer = saveContainer; + this.#addSignatureToolbarButton = addSignatureToolbarButton; + this.#typeInput = typeInput; + this.#l10n = l10n; + this.#signatureStorage = signatureStorage; + this.#eventBus = eventBus; + this.#editDescriptionDialog = new EditDescriptionDialog( + editSignatureElements, + overlayManager + ); + + SignatureManager.#l10nDescription ||= Object.freeze({ + signature: "pdfjs-editor-add-signature-description-default-when-drawing", + }); + + dialog.addEventListener("close", this.#close.bind(this)); + dialog.addEventListener("contextmenu", e => { + const { target } = e; + if (target !== this.#typeInput && target !== this.#description) { + e.preventDefault(); + } + }); + dialog.addEventListener("drop", e => { + stopEvent(e); + }); + cancelButton.addEventListener("click", this.#cancel.bind(this)); + addButton.addEventListener("click", this.#add.bind(this)); + clearButton.addEventListener( + "click", + () => { + this.#reportTelemetry({ + type: "signature", + action: "pdfjs.signature.clear", + data: { + type: this.#currentTab, + }, + }); + this.#initTab(null); + }, + { passive: true } + ); + description.addEventListener( + "input", + () => { + this.#clearDescription.disabled = description.value === ""; + }, + { passive: true } + ); + this.#clearDescription.addEventListener( + "click", + () => { + this.#description.value = ""; + this.#clearDescription.disabled = true; + }, + { passive: true } + ); + errorCloseButton.addEventListener( + "click", + () => { + errorBar.hidden = true; + }, + { passive: true } + ); + + this.#initTabButtons(typeButton, drawButton, imageButton, panels); + imagePicker.accept = SupportedImageMimeTypes.join(","); + + eventBus._on("storedsignatureschanged", this.#signaturesChanged.bind(this)); + + overlayManager.register(dialog); + } + + #initTabButtons(typeButton, drawButton, imageButton, panels) { + const buttons = (this.#tabButtons = new Map([ + ["type", typeButton], + ["draw", drawButton], + ["image", imageButton], + ])); + const tabCallback = e => { + for (const [name, button] of buttons) { + if (button === e.target) { + button.setAttribute("aria-selected", true); + button.setAttribute("tabindex", 0); + panels.setAttribute("data-selected", name); + this.#initTab(name); + } else { + button.setAttribute("aria-selected", false); + // Only the active tab is focusable: the others can be + // reached by keyboard navigation (left/right arrows). + button.setAttribute("tabindex", -1); + } + } + }; + + const buttonsArray = Array.from(buttons.values()); + for (let i = 0, ii = buttonsArray.length; i < ii; i++) { + const button = buttonsArray[i]; + button.addEventListener("click", tabCallback, { passive: true }); + button.addEventListener( + "keydown", + ({ key }) => { + if (key !== "ArrowLeft" && key !== "ArrowRight") { + return; + } + buttonsArray[i + (key === "ArrowLeft" ? -1 : 1)]?.focus(); + }, + { passive: true } + ); + } + } + + #resetCommon() { + this.#hasDescriptionChanged = false; + this.#description.value = ""; + if (this.#currentTab) { + this.#tabsToAltText.get(this.#currentTab).value = ""; + } + } + + #resetTab(name) { + switch (name) { + case "type": + this.#typeInput.value = ""; + break; + case "draw": + this.#drawCurves = null; + this.#drawPoints = null; + this.#drawPathString = ""; + this.#drawPath?.remove(); + this.#drawPath = null; + this.#drawPlaceholder.hidden = false; + this.#drawThickness.value = 1; + break; + case "image": + this.#imagePlaceholder.hidden = false; + this.#imagePath?.remove(); + this.#imagePath = null; + break; + } + } + + #initTab(name) { + if (name && this.#currentTab === name) { + return; + } + if (this.#currentTab) { + this.#tabsToAltText.get(this.#currentTab).value = this.#description.value; + } + if (name) { + this.#currentTab = name; + } + + this.#errorBar.hidden = true; + const reset = !name; + if (reset) { + this.#resetCommon(); + } else { + this.#description.value = this.#tabsToAltText.get(this.#currentTab).value; + } + this.#clearDescription.disabled = this.#description.value === ""; + this.#currentTabAC?.abort(); + this.#currentTabAC = new AbortController(); + switch (this.#currentTab) { + case "type": + this.#initTypeTab(reset); + break; + case "draw": + this.#initDrawTab(reset); + break; + case "image": + this.#initImageTab(reset); + break; + } + } + + #disableButtons(value) { + this.#clearButton.disabled = this.#addButton.disabled = !value; + if (value) { + this.#saveContainer.removeAttribute("disabled"); + } else { + this.#saveContainer.setAttribute("disabled", true); + } + } + + #initTypeTab(reset) { + if (reset) { + this.#resetTab("type"); + } + + this.#disableButtons(this.#typeInput.value); + + const { signal } = this.#currentTabAC; + const options = { passive: true, signal }; + this.#typeInput.addEventListener( + "input", + () => { + const { value } = this.#typeInput; + if (!this.#hasDescriptionChanged) { + this.#tabsToAltText.get("type").default = this.#description.value = + value; + this.#clearDescription.disabled = value === ""; + } + this.#disableButtons(value); + }, + options + ); + this.#description.addEventListener( + "input", + () => { + this.#hasDescriptionChanged = + this.#typeInput.value !== this.#description.value; + }, + options + ); + } + + #initDrawTab(reset) { + if (reset) { + this.#resetTab("draw"); + } + + this.#disableButtons(this.#drawPath); + + const { signal } = this.#currentTabAC; + const options = { signal }; + let currentPointerId = NaN; + const drawCallback = e => { + const { pointerId } = e; + if (!isNaN(currentPointerId) && currentPointerId !== pointerId) { + return; + } + currentPointerId = pointerId; + e.preventDefault(); + this.#drawSVG.setPointerCapture(pointerId); + + const { width: drawWidth, height: drawHeight } = + this.#drawSVG.getBoundingClientRect(); + let { offsetX, offsetY } = e; + offsetX = Math.round(offsetX); + offsetY = Math.round(offsetY); + if (e.target === this.#drawPlaceholder) { + this.#drawPlaceholder.hidden = true; + } + if (!this.#drawCurves) { + this.#drawCurves = { + width: drawWidth, + height: drawHeight, + thickness: parseInt(this.#drawThickness.value), + curves: [], + }; + this.#disableButtons(true); + + const svgFactory = new DOMSVGFactory(); + const path = (this.#drawPath = svgFactory.createElement("path")); + path.setAttribute("stroke-width", this.#drawThickness.value); + this.#drawSVG.append(path); + this.#drawSVG.addEventListener("pointerdown", drawCallback, options); + this.#drawPlaceholder.removeEventListener("pointerdown", drawCallback); + if (this.#description.value === "") { + this.#l10n + .get(SignatureManager.#l10nDescription.signature) + .then(description => { + this.#tabsToAltText.get("draw").default = description; + this.#description.value ||= description; + this.#clearDescription.disabled = this.#description.value === ""; + }); + } + } + + this.#drawPoints = [offsetX, offsetY]; + this.#drawCurves.curves.push({ points: this.#drawPoints }); + this.#drawPathString += `M ${offsetX} ${offsetY}`; + this.#drawPath.setAttribute("d", this.#drawPathString); + + const finishDrawAC = new AbortController(); + const listenerDrawOptions = { + signal: AbortSignal.any([signal, finishDrawAC.signal]), + }; + this.#drawSVG.addEventListener( + "contextmenu", + noContextMenu, + listenerDrawOptions + ); + this.#drawSVG.addEventListener( + "pointermove", + evt => { + evt.preventDefault(); + let { offsetX: x, offsetY: y } = evt; + x = Math.round(x); + y = Math.round(y); + const drawPoints = this.#drawPoints; + if ( + x < 0 || + y < 0 || + x > drawWidth || + y > drawHeight || + (x === drawPoints.at(-2) && y === drawPoints.at(-1)) + ) { + return; + } + if (drawPoints.length >= 4) { + const [x1, y1, x2, y2] = drawPoints.slice(-4); + this.#drawPathString += `C${(x1 + 5 * x2) / 6} ${(y1 + 5 * y2) / 6} ${(5 * x2 + x) / 6} ${(5 * y2 + y) / 6} ${(x2 + x) / 2} ${(y2 + y) / 2}`; + } else { + this.#drawPathString += `L${x} ${y}`; + } + drawPoints.push(x, y); + this.#drawPath.setAttribute("d", this.#drawPathString); + }, + listenerDrawOptions + ); + this.#drawSVG.addEventListener( + "pointerup", + evt => { + const { pointerId: pId } = evt; + if (!isNaN(currentPointerId) && currentPointerId !== pId) { + return; + } + currentPointerId = NaN; + evt.preventDefault(); + this.#drawSVG.releasePointerCapture(pId); + finishDrawAC.abort(); + if (this.#drawPoints.length === 2) { + this.#drawPathString += `L${this.#drawPoints[0]} ${this.#drawPoints[1]}`; + this.#drawPath.setAttribute("d", this.#drawPathString); + } + }, + listenerDrawOptions + ); + }; + if (this.#drawCurves) { + this.#drawSVG.addEventListener("pointerdown", drawCallback, options); + } else { + this.#drawPlaceholder.addEventListener( + "pointerdown", + drawCallback, + options + ); + } + this.#drawThickness.addEventListener( + "input", + () => { + const { value: thickness } = this.#drawThickness; + this.#drawThickness.setAttribute( + "data-l10n-args", + JSON.stringify({ thickness }) + ); + if (!this.#drawCurves) { + return; + } + this.#drawPath.setAttribute("stroke-width", thickness); + this.#drawCurves.thickness = thickness; + }, + options + ); + } + + #initImageTab(reset) { + if (reset) { + this.#resetTab("image"); + } + + this.#disableButtons(this.#imagePath); + + const { signal } = this.#currentTabAC; + const options = { signal }; + const passiveOptions = { passive: true, signal }; + this.#imagePickerLink.addEventListener( + "keydown", + e => { + const { key } = e; + if (key === "Enter" || key === " ") { + stopEvent(e); + this.#imagePicker.click(); + } + }, + options + ); + this.#imagePicker.addEventListener( + "click", + () => { + this.#dialog.classList.toggle("waiting", true); + }, + passiveOptions + ); + this.#imagePicker.addEventListener( + "change", + async () => { + const file = this.#imagePicker.files?.[0]; + if (!file || !SupportedImageMimeTypes.includes(file.type)) { + this.#errorBar.hidden = false; + this.#dialog.classList.toggle("waiting", false); + return; + } + await this.#extractSignature(file); + }, + passiveOptions + ); + this.#imagePicker.addEventListener( + "cancel", + () => { + this.#dialog.classList.toggle("waiting", false); + }, + passiveOptions + ); + this.#imagePlaceholder.addEventListener( + "dragover", + e => { + const { dataTransfer } = e; + for (const { type } of dataTransfer.items) { + if (!SupportedImageMimeTypes.includes(type)) { + continue; + } + dataTransfer.dropEffect = + dataTransfer.effectAllowed === "copy" ? "copy" : "move"; + stopEvent(e); + return; + } + dataTransfer.dropEffect = "none"; + }, + options + ); + this.#imagePlaceholder.addEventListener( + "drop", + e => { + const { + dataTransfer: { files }, + } = e; + if (!files?.length) { + return; + } + for (const file of files) { + if (SupportedImageMimeTypes.includes(file.type)) { + this.#extractSignature(file); + break; + } + } + stopEvent(e); + this.#dialog.classList.toggle("waiting", true); + }, + options + ); + } + + async #extractSignature(file) { + let data; + try { + data = await this.#uiManager.imageManager.getFromFile(file); + } catch (e) { + console.error("SignatureManager.#extractSignature.", e); + } + if (!data) { + this.#errorBar.hidden = false; + this.#dialog.classList.toggle("waiting", false); + return; + } + + const { outline } = (this.#extractedSignatureData = + this.#currentEditor.getFromImage(data.bitmap)); + + if (!outline) { + this.#dialog.classList.toggle("waiting", false); + return; + } + + this.#imagePlaceholder.hidden = true; + this.#disableButtons(true); + + const svgFactory = new DOMSVGFactory(); + const path = (this.#imagePath = svgFactory.createElement("path")); + this.#imageSVG.setAttribute("viewBox", outline.viewBox); + this.#imageSVG.setAttribute("preserveAspectRatio", "xMidYMid meet"); + this.#imageSVG.append(path); + path.setAttribute("d", outline.toSVGPath()); + this.#tabsToAltText.get("image").default = file.name; + if (this.#description.value === "") { + this.#description.value = file.name || ""; + this.#clearDescription.disabled = this.#description.value === ""; + } + + this.#dialog.classList.toggle("waiting", false); + } + + #getOutlineForType() { + return this.#currentEditor.getFromText( + this.#typeInput.value, + window.getComputedStyle(this.#typeInput) + ); + } + + #getOutlineForDraw() { + const { width, height } = this.#drawSVG.getBoundingClientRect(); + return this.#currentEditor.getDrawnSignature( + this.#drawCurves, + width, + height + ); + } + + #reportTelemetry(data) { + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "editing", + data, + }, + }); + } + + #addToolbarButton(signatureData, uuid, description) { + const { curves, areContours, thickness, width, height } = signatureData; + const maxDim = Math.max(width, height); + const outlineData = SignatureExtractor.processDrawnLines({ + lines: { + curves, + thickness, + width, + height, + }, + pageWidth: maxDim, + pageHeight: maxDim, + rotation: 0, + innerMargin: 0, + mustSmooth: false, + areContours, + }); + if (!outlineData) { + return; + } + + const { outline } = outlineData; + const svgFactory = new DOMSVGFactory(); + + const div = document.createElement("div"); + const button = document.createElement("button"); + + button.addEventListener("click", () => { + this.#eventBus.dispatch("switchannotationeditorparams", { + source: this, + type: AnnotationEditorParamsType.CREATE, + value: { + signatureData: { + lines: { + curves, + thickness, + width, + height, + }, + mustSmooth: false, + areContours, + description, + uuid, + heightInPage: DEFAULT_HEIGHT_IN_PAGE, + }, + }, + }); + }); + div.append(button); + div.classList.add("toolbarAddSignatureButtonContainer"); + + const svg = svgFactory.create(1, 1, true); + button.append(svg); + + const span = document.createElement("span"); + button.append(span); + + button.classList.add("toolbarAddSignatureButton"); + button.type = "button"; + button.title = span.textContent = description; + button.tabIndex = 0; + + const path = svgFactory.createElement("path"); + svg.append(path); + svg.setAttribute("viewBox", outline.viewBox); + svg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + if (areContours) { + path.classList.add("contours"); + } + path.setAttribute("d", outline.toSVGPath()); + + const deleteButton = document.createElement("button"); + div.append(deleteButton); + deleteButton.classList.add("toolbarButton", "deleteButton"); + deleteButton.setAttribute( + "data-l10n-id", + "pdfjs-editor-delete-signature-button" + ); + deleteButton.type = "button"; + deleteButton.tabIndex = 0; + deleteButton.addEventListener("click", async () => { + if (await this.#signatureStorage.delete(uuid)) { + div.remove(); + this.#reportTelemetry({ + type: "signature", + action: "pdfjs.signature.delete_saved", + data: { + savedCount: await this.#signatureStorage.size(), + }, + }); + } + }); + const deleteSpan = document.createElement("span"); + deleteButton.append(deleteSpan); + deleteSpan.setAttribute( + "data-l10n-id", + "pdfjs-editor-delete-signature-button-label" + ); + + this.#addSignatureToolbarButton.before(div); + } + + async #signaturesChanged() { + const parent = this.#addSignatureToolbarButton.parentElement; + while (parent.firstElementChild !== this.#addSignatureToolbarButton) { + parent.firstElementChild.remove(); + } + this.#loadSignaturesPromise = null; + await this.loadSignatures(/* reload = */ true); + } + + getSignature(params) { + return this.open(params); + } + + async loadSignatures(reload = false) { + if ( + !this.#addSignatureToolbarButton || + (!reload && this.#addSignatureToolbarButton.previousElementSibling) || + !this.#signatureStorage + ) { + return; + } + + if (!this.#loadSignaturesPromise) { + // The first call of loadSignatures() starts loading the signatures. + // The second one will wait until the signatures are loaded in the DOM. + this.#loadSignaturesPromise = this.#signatureStorage + .getAll() + .then(async signatures => [ + signatures, + await Promise.all( + Array.from(signatures.values(), ({ signatureData }) => + SignatureExtractor.decompressSignature(signatureData) + ) + ), + ]); + if (!reload) { + return; + } + } + const [signatures, signaturesData] = await this.#loadSignaturesPromise; + this.#loadSignaturesPromise = null; + + let i = 0; + for (const [uuid, { description }] of signatures) { + const data = signaturesData[i++]; + if (!data) { + continue; + } + data.curves = data.outlines.map(points => ({ points })); + delete data.outlines; + this.#addToolbarButton(data, uuid, description); + } + } + + async renderEditButton(editor) { + const button = document.createElement("button"); + button.classList.add("altText", "editDescription"); + button.tabIndex = 0; + button.title = editor.description; + const span = document.createElement("span"); + button.append(span); + span.setAttribute( + "data-l10n-id", + "pdfjs-editor-add-signature-edit-button-label" + ); + button.addEventListener( + "click", + () => { + this.#editDescriptionDialog.open(editor); + }, + { passive: true } + ); + return button; + } + + async open({ uiManager, editor }) { + this.#tabsToAltText ||= new Map( + this.#tabButtons.keys().map(name => [name, { value: "", default: "" }]) + ); + this.#uiManager = uiManager; + this.#currentEditor = editor; + this.#uiManager.removeEditListeners(); + + const isStorageFull = await this.#signatureStorage.isFull(); + this.#saveContainer.classList.toggle("fullStorage", isStorageFull); + this.#saveCheckbox.checked = !isStorageFull; + + await this.#overlayManager.open(this.#dialog); + + const tabType = this.#tabButtons.get("type"); + tabType.focus(); + tabType.click(); + } + + #cancel() { + this.#finish(); + } + + #finish() { + this.#overlayManager.closeIfActive(this.#dialog); + } + + #close() { + if (this.#currentEditor._drawId === null) { + this.#currentEditor.remove(); + } + this.#uiManager?.addEditListeners(); + this.#currentTabAC?.abort(); + this.#currentTabAC = null; + this.#uiManager = null; + this.#currentEditor = null; + + this.#resetCommon(); + for (const [name] of this.#tabButtons) { + this.#resetTab(name); + } + this.#disableButtons(false); + this.#currentTab = null; + this.#tabsToAltText = null; + } + + async #add() { + let data; + const type = this.#currentTab; + switch (type) { + case "type": + data = this.#getOutlineForType(); + break; + case "draw": + data = this.#getOutlineForDraw(); + break; + case "image": + data = this.#extractedSignatureData; + break; + } + let uuid = null; + const description = this.#description.value; + if (this.#saveCheckbox.checked) { + const { newCurves, areContours, thickness, width, height } = data; + const signatureData = await SignatureExtractor.compressSignature({ + outlines: newCurves, + areContours, + thickness, + width, + height, + }); + uuid = await this.#signatureStorage.create({ + description, + signatureData, + }); + if (uuid) { + this.#addToolbarButton( + { + curves: newCurves.map(points => ({ points })), + areContours, + thickness, + width, + height, + }, + uuid, + description + ); + } else { + console.warn("SignatureManager.add: cannot save the signature."); + } + } + + const altText = this.#tabsToAltText.get(type); + this.#reportTelemetry({ + type: "signature", + action: "pdfjs.signature.created", + data: { + type, + saved: !!uuid, + savedCount: await this.#signatureStorage.size(), + descriptionChanged: description !== altText.default, + }, + }); + + this.#currentEditor.addSignature( + data, + DEFAULT_HEIGHT_IN_PAGE, + this.#description.value, + uuid + ); + + this.#finish(); + } + + destroy() { + this.#uiManager = null; + this.#finish(); + } +} + +class EditDescriptionDialog { + #currentEditor; + + #previousDescription; + + #description; + + #dialog; + + #overlayManager; + + #signatureSVG; + + #uiManager; + + constructor( + { dialog, description, cancelButton, updateButton, editSignatureView }, + overlayManager + ) { + const descriptionInput = (this.#description = + description.firstElementChild); + this.#signatureSVG = editSignatureView; + this.#dialog = dialog; + this.#overlayManager = overlayManager; + + dialog.addEventListener("close", this.#close.bind(this)); + dialog.addEventListener("contextmenu", e => { + if (e.target !== this.#description) { + e.preventDefault(); + } + }); + cancelButton.addEventListener("click", this.#cancel.bind(this)); + updateButton.addEventListener("click", this.#update.bind(this)); + + const clearDescription = description.lastElementChild; + clearDescription.addEventListener("click", () => { + descriptionInput.value = ""; + clearDescription.disabled = true; + updateButton.disabled = this.#previousDescription === ""; + }); + descriptionInput.addEventListener( + "input", + () => { + const { value } = descriptionInput; + clearDescription.disabled = value === ""; + updateButton.disabled = value === this.#previousDescription; + editSignatureView.setAttribute("aria-label", value); + }, + { passive: true } + ); + + overlayManager.register(dialog); + } + + async open(editor) { + this.#uiManager = editor._uiManager; + this.#currentEditor = editor; + this.#previousDescription = this.#description.value = editor.description; + this.#description.dispatchEvent(new Event("input")); + this.#uiManager.removeEditListeners(); + const { areContours, outline } = editor.getSignaturePreview(); + const svgFactory = new DOMSVGFactory(); + const path = svgFactory.createElement("path"); + this.#signatureSVG.append(path); + this.#signatureSVG.setAttribute("viewBox", outline.viewBox); + path.setAttribute("d", outline.toSVGPath()); + if (areContours) { + path.classList.add("contours"); + } + + await this.#overlayManager.open(this.#dialog); + } + + async #update() { + // The description has been changed because the button isn't disabled. + this.#currentEditor._reportTelemetry({ + action: "pdfjs.signature.edit_description", + data: { + hasBeenChanged: true, + }, + }); + this.#currentEditor.description = this.#description.value; + this.#finish(); + } + + #cancel() { + this.#currentEditor._reportTelemetry({ + action: "pdfjs.signature.edit_description", + data: { + hasBeenChanged: false, + }, + }); + this.#finish(); + } + + #finish() { + this.#overlayManager.closeIfActive(this.#dialog); + } + + #close() { + this.#uiManager?.addEditListeners(); + this.#uiManager = null; + this.#currentEditor = null; + this.#signatureSVG.firstElementChild.remove(); + } +} + +export { SignatureManager }; diff --git a/web/struct_tree_layer_builder.js b/web/struct_tree_layer_builder.js index ead85bbf12507..dbc9d7ee48100 100644 --- a/web/struct_tree_layer_builder.js +++ b/web/struct_tree_layer_builder.js @@ -13,6 +13,8 @@ * limitations under the License. */ +/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */ + import { removeNullCharacters } from "./ui_utils.js"; const PDF_ROLE_TO_HTML_ROLE = { @@ -73,6 +75,12 @@ const PDF_ROLE_TO_HTML_ROLE = { const HEADING_PATTERN = /^H(\d+)$/; +/** + * @typedef {Object} StructTreeLayerBuilderOptions + * @property {PDFPageProxy} pdfPage + * @property {Object} rawDims + */ + class StructTreeLayerBuilder { #promise; @@ -86,11 +94,17 @@ class StructTreeLayerBuilder { #elementsToAddToTextLayer = null; + /** + * @param {StructTreeLayerBuilderOptions} options + */ constructor(pdfPage, rawDims) { this.#promise = pdfPage.getStructTree(); this.#rawDims = rawDims; } + /** + * @returns {Promise} + */ async render() { if (this.#treePromise) { return this.#treePromise; @@ -112,8 +126,14 @@ class StructTreeLayerBuilder { } async getAriaAttributes(annotationId) { - await this.render(); - return this.#elementAttributes.get(annotationId); + try { + await this.render(); + return this.#elementAttributes.get(annotationId); + } catch { + // If the structTree cannot be fetched, parsed, and/or rendered, + // ensure that e.g. the AnnotationLayer won't break completely. + } + return null; } hide() { @@ -184,7 +204,7 @@ class StructTreeLayerBuilder { img.setAttribute("aria-label", removeNullCharacters(alt)); const { pageHeight, pageX, pageY } = this.#rawDims; - const calc = "calc(var(--scale-factor)*"; + const calc = "calc(var(--total-scale-factor) *"; const { style } = img; style.width = `${calc}${bbox[2] - bbox[0]}px)`; style.height = `${calc}${bbox[3] - bbox[1]}px)`; diff --git a/web/stubs-geckoview.js b/web/stubs-geckoview.js index 23b4ebb6af285..eabba37d208cd 100644 --- a/web/stubs-geckoview.js +++ b/web/stubs-geckoview.js @@ -27,6 +27,7 @@ const PDFPresentationMode = null; const PDFSidebar = null; const PDFThumbnailViewer = null; const SecondaryToolbar = null; +const SignatureManager = null; export { AltTextManager, @@ -43,4 +44,5 @@ export { PDFSidebar, PDFThumbnailViewer, SecondaryToolbar, + SignatureManager, }; diff --git a/web/text_highlighter.js b/web/text_highlighter.js index ac682dba15e7a..72d883f4627af 100644 --- a/web/text_highlighter.js +++ b/web/text_highlighter.js @@ -193,8 +193,15 @@ class TextHighlighter { span.className = `${className} appended`; span.append(node); div.append(span); - return className.includes("selected") ? span.offsetLeft : 0; + + if (className.includes("selected")) { + const { left } = span.getClientRects()[0]; + const parentLeft = div.getBoundingClientRect().left; + return left - parentLeft; + } + return 0; } + div.append(node); return 0; } diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 1d8e2440a507b..30816eef0c238 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -20,7 +20,7 @@ // eslint-disable-next-line max-len /** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ -import { normalizeUnicode, TextLayer } from "pdfjs-lib"; +import { normalizeUnicode, stopEvent, TextLayer } from "pdfjs-lib"; import { removeNullCharacters } from "./ui_utils.js"; /** @@ -29,9 +29,16 @@ import { removeNullCharacters } from "./ui_utils.js"; * @property {TextHighlighter} [highlighter] - Optional object that will handle * highlighting text from the find controller. * @property {TextAccessibilityManager} [accessibilityManager] + * @property {boolean} [enablePermissions] * @property {function} [onAppend] */ +/** + * @typedef {Object} TextLayerBuilderRenderOptions + * @property {PageViewport} viewport + * @property {Object} [textContentParams] + */ + /** * The text layer builder provides text selection functionality for the PDF. * It does this by creating overlay divs over the PDF's text. These divs @@ -50,6 +57,9 @@ class TextLayerBuilder { static #selectionChangeAbortController = null; + /** + * @param {TextLayerBuilderOptions} options + */ constructor({ pdfPage, highlighter = null, @@ -70,10 +80,10 @@ class TextLayerBuilder { /** * Renders the text layer. - * @param {PageViewport} viewport - * @param {Object} [textContentParams] + * @param {TextLayerBuilderRenderOptions} options + * @returns {Promise} */ - async render(viewport, textContentParams = null) { + async render({ viewport, textContentParams = null }) { if (this.#renderingDone && this.#textLayer) { this.#textLayer.update({ viewport, @@ -162,8 +172,7 @@ class TextLayerBuilder { removeNullCharacters(normalizeUnicode(selection.toString())) ); } - event.preventDefault(); - event.stopPropagation(); + stopEvent(event); }); TextLayerBuilder.#textLayers.set(div, end); diff --git a/web/toggle_button.css b/web/toggle_button.css index 18642e2729c22..426f528ff65dc 100644 --- a/web/toggle_button.css +++ b/web/toggle_button.css @@ -3,72 +3,72 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ .toggle-button { - --button-background-color: #f0f0f4; - --button-background-color-hover: #e0e0e6; - --button-background-color-active: #cfcfd8; + --button-background-color: color-mix(in srgb, currentColor 7%, transparent); + --button-background-color-hover: color-mix( + in srgb, + currentColor 14%, + transparent + ); + --button-background-color-active: color-mix( + in srgb, + currentColor 21%, + transparent + ); --color-accent-primary: #0060df; --color-accent-primary-hover: #0250bb; --color-accent-primary-active: #054096; - --border-interactive-color: #8f8f9d; --border-radius-circle: 9999px; --border-width: 1px; --size-item-small: 16px; --size-item-large: 32px; --color-canvas: white; + --background-color-canvas: var(--color-canvas); + --border-color-interactive: #8f8f9d; + --border-color-interactive-hover: var(--border-color-interactive); + --border-color-interactive-active: var(--border-color-interactive); @media (prefers-color-scheme: dark) { - --button-background-color: color-mix(in srgb, currentColor 7%, transparent); - --button-background-color-hover: color-mix( - in srgb, - currentColor 14%, - transparent - ); - --button-background-color-active: color-mix( - in srgb, - currentColor 21%, - transparent - ); --color-accent-primary: #0df; --color-accent-primary-hover: #80ebff; --color-accent-primary-active: #aaf2ff; - --border-interactive-color: #bfbfc9; --color-canvas: #1c1b22; + --border-color-interactive: #f9f9fa; } @media (forced-colors: active) { --color-accent-primary: ButtonText; --color-accent-primary-hover: SelectedItem; --color-accent-primary-active: SelectedItem; - --border-interactive-color: ButtonText; --button-background-color: ButtonFace; - --border-interactive-color-hover: SelectedItem; - --border-interactive-color-active: SelectedItem; - --border-interactive-color-disabled: GrayText; + --border-color-interactive: ButtonText; + --border-color-interactive-hover: SelectedItem; + --border-color-interactive-active: ButtonText; --color-canvas: ButtonText; + --background-color-canvas: Canvas; } +} - /* +/* The original file is located at: - https://hg.mozilla.org/mozilla-central/file/aded201f11ec90b8e11c59d1e399960785771fbd/toolkit/content/widgets/moz-toggle/moz-toggle.css + https://hg.mozilla.org/mozilla-central/file/168f6caa3b160c12413f9e9ad9df1db49c6b4cdf/toolkit/content/widgets/moz-toggle/moz-toggle.css The original file is licensed under the Mozilla Public License, v. 2.0. This file is a modified version of the original file. - In order to have a better reading of the code, the .toggle-button selector - has been removed from the original file and we put everything under a single - .toggle-button selector. - - TODO: check from times to times if the original file has been updated (and + The pseudo-selector :host has been replaced by .toggle-button. + + TODO: check from times to times if the original file has been updated (and in such a case don't forget to change the revision in the above link). - */ +*/ +.toggle-button { --toggle-background-color: var(--button-background-color); --toggle-background-color-hover: var(--button-background-color-hover); --toggle-background-color-active: var(--button-background-color-active); --toggle-background-color-pressed: var(--color-accent-primary); --toggle-background-color-pressed-hover: var(--color-accent-primary-hover); --toggle-background-color-pressed-active: var(--color-accent-primary-active); - --toggle-border-color: var(--border-interactive-color); + --toggle-border-color: var(--border-color-interactive); --toggle-border-color-hover: var(--toggle-border-color); --toggle-border-color-active: var(--toggle-border-color); --toggle-border-radius: var(--border-radius-circle); @@ -78,7 +78,7 @@ --toggle-dot-background-color: var(--toggle-border-color); --toggle-dot-background-color-hover: var(--toggle-dot-background-color); --toggle-dot-background-color-active: var(--toggle-dot-background-color); - --toggle-dot-background-color-on-pressed: var(--color-canvas); + --toggle-dot-background-color-on-pressed: var(--background-color-canvas); --toggle-dot-margin: 1px; --toggle-dot-height: calc( var(--toggle-height) - 2 * var(--toggle-dot-margin) - 2 * @@ -88,17 +88,16 @@ --toggle-dot-transform-x: calc( var(--toggle-width) - 4 * var(--toggle-dot-margin) - var(--toggle-dot-width) ); + --input-width: var(--toggle-width); appearance: none; padding: 0; - margin: 0; border: var(--toggle-border-width) solid var(--toggle-border-color); height: var(--toggle-height); width: var(--toggle-width); border-radius: var(--toggle-border-radius); - background: var(--toggle-background-color); + background-color: var(--toggle-background-color); box-sizing: border-box; - flex-shrink: 0; &:focus-visible { outline: var(--focus-outline); @@ -106,30 +105,15 @@ } &:enabled:hover { - background: var(--toggle-background-color-hover); + background-color: var(--toggle-background-color-hover); border-color: var(--toggle-border-color); } - &:enabled:active { - background: var(--toggle-background-color-active); + &:enabled:hover:active { + background-color: var(--toggle-background-color-active); border-color: var(--toggle-border-color); } - &[aria-pressed="true"] { - background: var(--toggle-background-color-pressed); - border-color: transparent; - } - - &[aria-pressed="true"]:enabled:hover { - background: var(--toggle-background-color-pressed-hover); - border-color: transparent; - } - - &[aria-pressed="true"]:enabled:active { - background: var(--toggle-background-color-pressed-active); - border-color: transparent; - } - &::before { display: block; content: ""; @@ -140,83 +124,95 @@ border-radius: var(--toggle-border-radius); translate: 0; } +} + +.toggle-button[aria-pressed="true"] { + background-color: var(--toggle-background-color-pressed); + border-color: transparent; + + &:enabled:hover { + background-color: var(--toggle-background-color-pressed-hover); + border-color: transparent; + } + + &:enabled:hover:active { + background-color: var(--toggle-background-color-pressed-active); + border-color: transparent; + } - &[aria-pressed="true"]::before { + &::before { translate: var(--toggle-dot-transform-x); background-color: var(--toggle-dot-background-color-on-pressed); } - &[aria-pressed="true"]:enabled:hover::before, - &[aria-pressed="true"]:enabled:active::before { + &:enabled:hover::before, + &:enabled:hover:active::before { background-color: var(--toggle-dot-background-color-on-pressed); } - /*#if MOZCENTRAL*/ - /*&[aria-pressed="true"]:-moz-locale-dir(rtl)::before,*/ - /*#endif*/ - &[aria-pressed="true"]:dir(rtl)::before { + &:-moz-locale-dir(rtl)::before, + &:dir(rtl)::before { translate: calc(-1 * var(--toggle-dot-transform-x)); } +} - @media (prefers-reduced-motion: no-preference) { - &::before { - transition: translate 100ms; - } +@media (prefers-reduced-motion: no-preference) { + .toggle-button::before { + transition: translate 100ms; } +} - @media (prefers-contrast) { - &:enabled:hover { - border-color: var(--toggle-border-color-hover); - } +@media (prefers-contrast) { + .toggle-button:enabled:hover { + border-color: var(--toggle-border-color-hover); + } - &:enabled:active { - border-color: var(--toggle-border-color-active); - } + .toggle-button:enabled:hover:active { + border-color: var(--toggle-border-color-active); + } - &[aria-pressed="true"]:enabled { - border-color: var(--toggle-border-color); - position: relative; - } + .toggle-button[aria-pressed="true"]:enabled { + border-color: var(--toggle-border-color); + position: relative; - &[aria-pressed="true"]:enabled:hover, - &[aria-pressed="true"]:enabled:hover:active { + &:hover { border-color: var(--toggle-border-color-hover); - } - &[aria-pressed="true"]:enabled:active { - background-color: var(--toggle-dot-background-color-active); - border-color: var(--toggle-dot-background-color-hover); + &:active { + background-color: var(--toggle-dot-background-color-active); + border-color: var(--toggle-dot-background-color-hover); + } } + } - &:hover::before, - &:active::before { - background-color: var(--toggle-dot-background-color-hover); - } + .toggle-button:enabled:hover::before, + .toggle-button:enabled:hover:active::before { + background-color: var(--toggle-dot-background-color-hover); } +} - @media (forced-colors) { +@media (forced-colors) { + .toggle-button { --toggle-dot-background-color: var(--color-accent-primary); --toggle-dot-background-color-hover: var(--color-accent-primary-hover); --toggle-dot-background-color-active: var(--color-accent-primary-active); --toggle-dot-background-color-on-pressed: var(--button-background-color); - --toggle-background-color-disabled: var(--button-background-color-disabled); - --toggle-border-color-hover: var(--border-interactive-color-hover); - --toggle-border-color-active: var(--border-interactive-color-active); - --toggle-border-color-disabled: var(--border-interactive-color-disabled); - - &[aria-pressed="true"]:enabled::after { - border: 1px solid var(--button-background-color); - content: ""; - position: absolute; - height: var(--toggle-height); - width: var(--toggle-width); - display: block; - border-radius: var(--toggle-border-radius); - inset: -2px; - } + --toggle-border-color-hover: var(--border-color-interactive-hover); + --toggle-border-color-active: var(--border-color-interactive-active); + } - &[aria-pressed="true"]:enabled:active::after { - border-color: var(--toggle-border-color-active); - } + .toggle-button[aria-pressed="true"]:enabled::after { + border: 1px solid var(--button-background-color); + content: ""; + position: absolute; + height: var(--toggle-height); + width: var(--toggle-width); + display: block; + border-radius: var(--toggle-border-radius); + inset: -2px; + } + + .toggle-button[aria-pressed="true"]:enabled:hover:active::after { + border-color: var(--toggle-border-color-active); } } diff --git a/web/toolbar.js b/web/toolbar.js index fa0d589acbe28..5945cd409165a 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -44,6 +44,8 @@ import { */ class Toolbar { + #colorPicker = null; + #opts; /** @@ -117,6 +119,18 @@ class Toolbar { data: { action: "pdfjs.image.icon_click" }, }, }, + { + element: options.editorSignatureButton, + eventName: "switchannotationeditormode", + eventDetails: { + get mode() { + const { classList } = options.editorSignatureButton; + return classList.contains("toggled") + ? AnnotationEditorType.NONE + : AnnotationEditorType.SIGNATURE; + }, + }, + }, ]; // Bind the event listeners for click and various other actions. @@ -139,12 +153,6 @@ class Toolbar { document.documentElement.setAttribute("data-toolbar-density", name); } - #setAnnotationEditorUIManager(uiManager, parentContainer) { - const colorPicker = new ColorPicker({ uiManager }); - uiManager.setMainHighlightColorPicker(colorPicker); - parentContainer.append(colorPicker.renderMainDropdown()); - } - setPageNumber(pageNumber, pageLabel) { this.pageNumber = pageNumber; this.pageLabel = pageLabel; @@ -164,6 +172,7 @@ class Toolbar { } reset() { + this.#colorPicker = null; this.pageNumber = 0; this.pageLabel = null; this.hasPageLabels = false; @@ -255,17 +264,15 @@ class Toolbar { eventBus._on("toolbardensity", this.#updateToolbarDensity.bind(this)); if (editorHighlightColorPicker) { - eventBus._on( - "annotationeditoruimanager", - ({ uiManager }) => { - this.#setAnnotationEditorUIManager( - uiManager, - editorHighlightColorPicker - ); - }, - // Once the color picker has been added, we don't want to add it again. - { once: true } - ); + eventBus._on("annotationeditoruimanager", ({ uiManager }) => { + const cp = (this.#colorPicker = new ColorPicker({ uiManager })); + uiManager.setMainHighlightColorPicker(cp); + editorHighlightColorPicker.append(cp.renderMainDropdown()); + }); + + eventBus._on("mainhighlightcolorpickerupdatecolor", ({ value }) => { + this.#colorPicker?.updateColor(value); + }); } } @@ -279,6 +286,8 @@ class Toolbar { editorInkParamsToolbar, editorStampButton, editorStampParamsToolbar, + editorSignatureButton, + editorSignatureParamsToolbar, } = this.#opts; toggleExpandedBtn( @@ -301,12 +310,18 @@ class Toolbar { mode === AnnotationEditorType.STAMP, editorStampParamsToolbar ); + toggleExpandedBtn( + editorSignatureButton, + mode === AnnotationEditorType.SIGNATURE, + editorSignatureParamsToolbar + ); const isDisable = mode === AnnotationEditorType.DISABLE; editorFreeTextButton.disabled = isDisable; editorHighlightButton.disabled = isDisable; editorInkButton.disabled = isDisable; editorStampButton.disabled = isDisable; + editorSignatureButton.disabled = isDisable; } #updateUIState(resetNumPages = false) { diff --git a/web/ui_utils.js b/web/ui_utils.js index 07dc55c7a7221..dd2f250eae2f1 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -554,10 +554,11 @@ function getVisibleElements({ continue; } - const hiddenHeight = - Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom); - const hiddenWidth = - Math.max(0, left - currentWidth) + Math.max(0, viewRight - right); + const minY = Math.max(0, top - currentHeight); + const minX = Math.max(0, left - currentWidth); + + const hiddenHeight = minY + Math.max(0, viewBottom - bottom); + const hiddenWidth = minX + Math.max(0, viewRight - right); const fractionHeight = (viewHeight - hiddenHeight) / viewHeight, fractionWidth = (viewWidth - hiddenWidth) / viewWidth; @@ -567,6 +568,18 @@ function getVisibleElements({ id: view.id, x: currentWidth, y: currentHeight, + visibleArea: + // We only specify which part of the page is visible when it's not + // the full page, as there is no point in handling a partial page + // rendering otherwise. + percent === 100 + ? null + : { + minX, + minY, + maxX: Math.min(viewRight, right) - currentWidth, + maxY: Math.min(viewBottom, bottom) - currentHeight, + }, view, percent, widthPercent: (fractionWidth * 100) | 0, diff --git a/web/viewer-geckoview.html b/web/viewer-geckoview.html index e80f528fd9016..907e2c3946f14 100644 --- a/web/viewer-geckoview.html +++ b/web/viewer-geckoview.html @@ -59,6 +59,9 @@ "fluent-dom": "../node_modules/@fluent/dom/esm/index.js", "cached-iterable": "../node_modules/cached-iterable/src/index.mjs", + "display-cmap_reader_factory": "../src/display/cmap_reader_factory.js", + "display-standard_fontdata_factory": "../src/display/standard_fontdata_factory.js", + "display-wasm_factory": "../src/display/wasm_factory.js", "display-fetch_stream": "../src/display/fetch_stream.js", "display-network": "../src/display/network.js", "display-node_stream": "../src/display/stubs.js", @@ -82,6 +85,7 @@ "web-preferences": "./genericcom.js", "web-print_service": "./pdf_print_service.js", "web-secondary_toolbar": "./stubs-geckoview.js", + "web-signature_manager": "./stubs-geckoview.js", "web-toolbar": "./toolbar-geckoview.js" } } diff --git a/web/viewer-geckoview.js b/web/viewer-geckoview.js index 9db5a0c1398b0..2b9fd1ace93fb 100644 --- a/web/viewer-geckoview.js +++ b/web/viewer-geckoview.js @@ -77,7 +77,7 @@ if ( } export { + PDFViewerApplication, AppConstants as PDFViewerApplicationConstants, AppOptions as PDFViewerApplicationOptions, - PDFViewerApplication, }; diff --git a/web/viewer.css b/web/viewer.css index 4b2b25ac9be0b..2560e46f62e99 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -51,11 +51,11 @@ --toolbar-border-color: rgb(184 184 184); --toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color); --toolbar-border-bottom: none; - --toolbarSidebar-box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 - rgb(0 0 0 / 0.25), + --toolbarSidebar-box-shadow: + inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25), 0 1px 0 rgb(0 0 0 / 0.15), 0 0 1px rgb(0 0 0 / 0.1); --toolbarSidebar-border-bottom: none; - --button-hover-color: rgb(221 222 223); + --button-hover-color: color-mix(in srgb, currentColor 17%, transparent); --toggled-btn-color: rgb(0 0 0); --toggled-btn-bg-color: rgb(0 0 0 / 0.3); --toggled-hover-active-btn-color: rgb(0 0 0 / 0.4); @@ -76,7 +76,6 @@ --doorhanger-bg-color: rgb(255 255 255); --doorhanger-border-color: rgb(12 12 13 / 0.2); --doorhanger-hover-color: rgb(12 12 13); - --doorhanger-hover-bg-color: rgb(237 237 237); --doorhanger-separator-color: rgb(222 222 222); --dialog-button-border: none; --dialog-button-bg-color: rgb(12 12 13 / 0.1); @@ -89,6 +88,7 @@ --toolbarButton-editorHighlight-icon: url(images/toolbarButton-editorHighlight.svg); --toolbarButton-editorInk-icon: url(images/toolbarButton-editorInk.svg); --toolbarButton-editorStamp-icon: url(images/toolbarButton-editorStamp.svg); + --toolbarButton-editorSignature-icon: url(images/toolbarButton-editorSignature.svg); --toolbarButton-menuArrow-icon: url(images/toolbarButton-menuArrow.svg); --toolbarButton-sidebarToggle-icon: url(images/toolbarButton-sidebarToggle.svg); --toolbarButton-secondaryToolbarToggle-icon: url(images/toolbarButton-secondaryToolbarToggle.svg); @@ -155,7 +155,6 @@ --sidebar-toolbar-bg-color: rgb(50 50 52); --toolbar-bg-color: rgb(56 56 61); --toolbar-border-color: rgb(12 12 13); - --button-hover-color: rgb(102 102 103); --toggled-btn-color: rgb(255 255 255); --toggled-btn-bg-color: rgb(0 0 0 / 0.3); --toggled-hover-active-btn-color: rgb(0 0 0 / 0.4); @@ -171,10 +170,9 @@ --treeitem-selected-bg-color: rgb(255 255 255 / 0.25); --thumbnail-hover-color: rgb(255 255 255 / 0.1); --thumbnail-selected-color: rgb(255 255 255 / 0.2); - --doorhanger-bg-color: rgb(74 74 79); + --doorhanger-bg-color: #42414d; --doorhanger-border-color: rgb(39 39 43); --doorhanger-hover-color: rgb(249 249 250); - --doorhanger-hover-bg-color: rgb(93 94 98); --doorhanger-separator-color: rgb(92 92 97); --dialog-button-bg-color: rgb(92 92 97); --dialog-button-hover-bg-color: rgb(115 115 115); @@ -184,7 +182,6 @@ @media screen and (forced-colors: active) { :root { --button-hover-color: Highlight; - --doorhanger-hover-bg-color: Highlight; --toolbar-icon-opacity: 1; --toolbar-icon-bg-color: ButtonText; --toolbar-icon-hover-bg-color: ButtonFace; @@ -572,6 +569,10 @@ body { mask-image: var(--toolbarButton-editorStamp-icon); } +#editorSignatureButton::before { + mask-image: var(--toolbarButton-editorSignature-icon); +} + #printButton::before { mask-image: var(--toolbarButton-print-icon); } @@ -634,14 +635,14 @@ body { .toggleButton { display: inline; - &:is(:hover, :has(> input:focus-visible)) { + &:has(> input:checked) { color: var(--toggled-btn-color); - background-color: var(--button-hover-color); + background-color: var(--toggled-btn-bg-color); } - &:has(> input:checked) { + &:is(:hover, :has(> input:focus-visible)) { color: var(--toggled-btn-color); - background-color: var(--toggled-btn-bg-color); + background-color: var(--button-hover-color); } & > input { @@ -996,6 +997,7 @@ dialog :link { font: message-box; flex: none; position: relative; + padding: 0; > span { display: inline-block; @@ -1070,7 +1072,6 @@ dialog :link { } &:is(:hover, :focus-visible) { - background-color: var(--doorhanger-hover-bg-color); color: var(--doorhanger-hover-color); } @@ -1118,7 +1119,7 @@ dialog :link { z-index: 30000; cursor: default; - #editorStampAddImage::before { + :is(#editorStampAddImage, #editorSignatureAddSignature)::before { mask-image: var(--editorParams-stampAddImage-icon); } @@ -1129,9 +1130,13 @@ dialog :link { font-style: normal; font-weight: 400; line-height: 150%; - color: var(--main-color); width: fit-content; inset-inline-start: 0; + color: var(--main-color); + } + + button:is(:hover, :focus-visible) .editorParamsLabel { + color: var(--doorhanger-hover-color); } .editorParamsToolbarContainer { diff --git a/web/viewer.html b/web/viewer.html index 36fec03150e9b..fd4d051c77ded 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -62,6 +62,9 @@ "fluent-dom": "../node_modules/@fluent/dom/esm/index.js", "cached-iterable": "../node_modules/cached-iterable/src/index.mjs", + "display-cmap_reader_factory": "../src/display/cmap_reader_factory.js", + "display-standard_fontdata_factory": "../src/display/standard_fontdata_factory.js", + "display-wasm_factory": "../src/display/wasm_factory.js", "display-fetch_stream": "../src/display/fetch_stream.js", "display-network": "../src/display/network.js", "display-node_stream": "../src/display/stubs.js", @@ -85,6 +88,7 @@ "web-preferences": "./genericcom.js", "web-print_service": "./pdf_print_service.js", "web-secondary_toolbar": "./secondary_toolbar.js", + "web-signature_manager": "./signature_manager.js", "web-toolbar": "./toolbar.js" } } @@ -298,7 +302,7 @@
- +
@@ -667,6 +671,107 @@ + + + + This modal allows the user to create a signature to add to a PDF document. The user can edit the name (which also serves as the alt text), and optionally save the signature for repeated use. + +
+
+ Add a signature +
+
+ + + +
+
+
+ +
+
+ + Draw your signature +
+
+ + +
+
+
+
+ +
+ Drag a file here to upload + + +
+
+
+
+
+ + + + + +
+ +
+
+ + + + You’ve reached the limit of 5 saved signatures. Remove one to save more. +
+
+ +
+ + +
+
+
+
+ + +
+
+ Edit description +
+
+
+ + + + + +
+ +
+
+ + +
+
+
+
@@ -686,6 +791,20 @@
+ +
diff --git a/web/viewer.js b/web/viewer.js index 7bd314fa1cc52..5590a591625c8 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -68,6 +68,10 @@ function getViewerConfiguration() { editorStampParamsToolbar: document.getElementById( "editorStampParamsToolbar" ), + editorSignatureButton: document.getElementById("editorSignatureButton"), + editorSignatureParamsToolbar: document.getElementById( + "editorSignatureParamsToolbar" + ), download: document.getElementById("downloadButton"), }, secondaryToolbar: { @@ -210,6 +214,36 @@ function getViewerConfiguration() { ), closeButton: document.getElementById("altTextSettingsCloseButton"), }, + addSignatureDialog: { + dialog: document.getElementById("addSignatureDialog"), + panels: document.getElementById("addSignatureActionContainer"), + typeButton: document.getElementById("addSignatureTypeButton"), + typeInput: document.getElementById("addSignatureTypeInput"), + drawButton: document.getElementById("addSignatureDrawButton"), + drawSVG: document.getElementById("addSignatureDraw"), + drawPlaceholder: document.getElementById("addSignatureDrawPlaceholder"), + drawThickness: document.getElementById("addSignatureDrawThickness"), + imageButton: document.getElementById("addSignatureImageButton"), + imageSVG: document.getElementById("addSignatureImage"), + imagePlaceholder: document.getElementById("addSignatureImagePlaceholder"), + imagePicker: document.getElementById("addSignatureFilePicker"), + imagePickerLink: document.getElementById("addSignatureImageBrowse"), + description: document.getElementById("addSignatureDescription"), + clearButton: document.getElementById("clearSignatureButton"), + saveContainer: document.getElementById("addSignatureSaveContainer"), + saveCheckbox: document.getElementById("addSignatureSaveCheckbox"), + errorBar: document.getElementById("addSignatureError"), + errorCloseButton: document.getElementById("addSignatureErrorCloseButton"), + cancelButton: document.getElementById("addSignatureCancelButton"), + addButton: document.getElementById("addSignatureAddButton"), + }, + editSignatureDialog: { + dialog: document.getElementById("editSignatureDescriptionDialog"), + description: document.getElementById("editSignatureDescription"), + editSignatureView: document.getElementById("editSignatureView"), + cancelButton: document.getElementById("editSignatureCancelButton"), + updateButton: document.getElementById("editSignatureUpdateButton"), + }, annotationEditorParams: { editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"), editorFreeTextColor: document.getElementById("editorFreeTextColor"), @@ -217,12 +251,21 @@ function getViewerConfiguration() { editorInkThickness: document.getElementById("editorInkThickness"), editorInkOpacity: document.getElementById("editorInkOpacity"), editorStampAddImage: document.getElementById("editorStampAddImage"), + editorSignatureAddSignature: document.getElementById( + "editorSignatureAddSignature" + ), editorFreeHighlightThickness: document.getElementById( "editorFreeHighlightThickness" ), editorHighlightShowAll: document.getElementById("editorHighlightShowAll"), }, printContainer: document.getElementById("printContainer"), + editorUndoBar: { + container: document.getElementById("editorUndoBar"), + message: document.getElementById("editorUndoBarMessage"), + undoButton: document.getElementById("editorUndoBarUndoButton"), + closeButton: document.getElementById("editorUndoBarCloseButton"), + }, }; } @@ -269,7 +312,7 @@ if ( } export { + PDFViewerApplication, AppConstants as PDFViewerApplicationConstants, AppOptions as PDFViewerApplicationOptions, - PDFViewerApplication, }; diff --git a/web/xfa_layer_builder.js b/web/xfa_layer_builder.js index 25a521d5f3bb9..95c540b93acec 100644 --- a/web/xfa_layer_builder.js +++ b/web/xfa_layer_builder.js @@ -30,6 +30,12 @@ import { XfaLayer } from "pdfjs-lib"; * @property {Object} [xfaHtml] */ +/** + * @typedef {Object} XfaLayerBuilderRenderOptions + * @property {PageViewport} viewport + * @property {string} [intent] - The default value is "display". + */ + class XfaLayerBuilder { /** * @param {XfaLayerBuilderOptions} options @@ -50,13 +56,12 @@ class XfaLayerBuilder { } /** - * @param {PageViewport} viewport - * @param {string} intent (default value is 'display') + * @param {XfaLayerBuilderRenderOptions} viewport * @returns {Promise} A promise that is resolved when rendering * of the XFA layer is complete. The first rendering will return an object * with a `textDivs` property that can be used with the TextHighlighter. */ - async render(viewport, intent = "display") { + async render({ viewport, intent = "display" }) { if (intent === "print") { const parameters = { viewport: viewport.clone({ dontFlip: true }),