[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: wojtekmaj\nopen_collective: react-pdf-wojtekmaj\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.yml",
    "content": "name: 🐛 Bug report\ndescription: Something does not work the way we promised\nlabels:\n  - bug\nbody:\n  - type: checkboxes\n    attributes:\n      label: Before you start - checklist\n      options:\n        - label: I followed instructions in documentation written for my React-PDF version\n          required: true\n        - label: I have checked if this bug is not already reported\n          required: true\n        - label: I have checked if an issue is not listed in [Known issues](https://github.com/wojtekmaj/react-pdf/wiki/Known-issues)\n          required: true\n        - label: If I have a problem with PDF rendering, I checked if my PDF renders properly in [PDF.js demo](https://mozilla.github.io/pdf.js/web/viewer.html)\n  - type: textarea\n    attributes:\n      label: Description\n      description: Short description of the bug you encountered.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Steps to reproduce\n      description: |\n        Steps to reproduce the behavior or, if applicable, a minimal code snippet to reproduce the behavior.\n\n        Example:\n          1. Go to '…'\n          2. Click on '…'\n          3. Scroll down to '…'\n          4. See error\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected behavior\n      description: What is the expected behavior?\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Actual behavior\n      description: What is the actual behavior?\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Additional information\n      description: If applicable, add screenshots (preferably with browser console open) and files you have an issue with to help explain your problem.\n  - type: textarea\n    attributes:\n      label: Environment\n      description: |\n        Example:\n          - **Browser (if applicable)**: Chrome 96, Firefox 94\n          - **React-PDF version**: 9.0.0\n          - **React version**: 18.2.0\n          - **Bundler name and version (if applicable)**: Vite 5.2.0\n      value: |\n          - **Browser (if applicable)**:\n          - **React-PDF version**:\n          - **React version**:\n          - **Bundler name and version (if applicable)**:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.yml",
    "content": "name: 🚀 Feature request\ndescription: I have a great idea for this project\nlabels:\n  - enhancement\nbody:\n  - type: checkboxes\n    attributes:\n      label: Before you start - checklist\n      options:\n        - label: I understand that React-PDF does not aim to be a fully-fledged PDF viewer and is only a tool to make one\n          required: true\n        - label: I have checked if this feature request is not already reported\n          required: true\n  - type: textarea\n    attributes:\n      label: Description\n      description: |\n        Describe what the problem is.\n\n        Example: _I'd like to add a feature that […]_\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Proposed solution\n      description: |\n        Describe the solution you'd like.\n\n        Example:\n          - Add a `foo` flag that, when toggled, enables the feature.\n  - type: textarea\n    attributes:\n      label: Alternatives\n      description: Describe alternative solutions or features you've considered, if any.\n  - type: textarea\n    attributes:\n      label: Additional information\n      description: If applicable, add screenshots (preferably with browser console open) and files you have an issue with to help explain your problem.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: 🤔 Support question\n    url: https://stackoverflow.com/questions/tagged/react-pdf\n    about: This is a bug tracker, not a support forum. For usage questions, please use Discussions (see menu) or Stack Overflow (\"Open\" button) where there is a lot more people ready to help you out. Thanks!\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: ['*']\n  pull_request:\n    branches: [main]\n\nenv:\n  HUSKY: 0\n\njobs:\n  lint:\n    name: Static code analysis\n    runs-on: ubuntu-24.04-arm\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Setup Biome\n        uses: biomejs/setup-biome@v2\n\n      - name: Run tests\n        run: biome lint\n\n  typescript:\n    name: Type checking (React ${{ matrix.react }})\n    runs-on: ubuntu-24.04-arm\n    strategy:\n      matrix:\n        react: [18, 19]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Cache Yarn cache\n        uses: actions/cache@v5\n        env:\n          cache-name: yarn-cache\n        with:\n          path: ~/.yarn/berry/cache\n          key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-${{ env.cache-name }}\n\n      - name: Use Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: '24'\n\n      - name: Install Corepack\n        run: npm install -g corepack\n\n      - name: Install dependencies\n        run: yarn --immutable\n        env:\n          PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true\n\n      - name: Override React version\n        if: ${{ matrix.react == 18 }}\n        run: |\n          npm pkg set resolutions.'@types/react'='npm:^18.0.0'\n          npm pkg set resolutions.'@types/react-dom'='npm:^18.0.0'\n          yarn config set enableImmutableInstalls false\n          yarn up react@^18.0.0 react-dom@^18.0.0\n\n      - name: Build package\n        run: yarn build\n\n      - name: Run type checking (React ${{ matrix.react }})\n        run: yarn tsc\n\n  format:\n    name: Formatting\n    runs-on: ubuntu-24.04-arm\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Setup Biome\n        uses: biomejs/setup-biome@v2\n\n      - name: Run formatting\n        run: biome format\n\n  unit:\n    name: Unit tests (React ${{ matrix.react }})\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        react: [18, 19]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Cache Yarn cache\n        uses: actions/cache@v5\n        env:\n          cache-name: yarn-cache\n        with:\n          path: ~/.yarn/berry/cache\n          key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-${{ env.cache-name }}\n\n      - name: Cache ~/.cache/ms-playwright\n        id: playwright-cache\n        uses: actions/cache@v5\n        env:\n          cache-name: playwright-cache\n        with:\n          path: ~/.cache/ms-playwright\n          key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}\n\n      - name: Use Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: '24'\n\n      - name: Install Corepack\n        run: npm install -g corepack\n\n      - name: Install dependencies\n        run: yarn --immutable\n\n      - name: Override React version\n        if: ${{ matrix.react == 18 }}\n        run: |\n          npm pkg set resolutions.'@types/react'='npm:^18.0.0'\n          npm pkg set resolutions.'@types/react-dom'='npm:^18.0.0'\n          yarn config set enableImmutableInstalls false\n          yarn up react@^18.0.0 react-dom@^18.0.0\n\n      - name: Install Playwright browsers\n        if: steps.playwright-cache.outputs.cache-hit != 'true'\n        run: yarn workspace react-pdf playwright install chromium-headless-shell\n\n      - name: Run unit tests (React ${{ matrix.react }})\n        run: yarn unit\n"
  },
  {
    "path": ".github/workflows/close-stale-issues.yml",
    "content": "name: Close stale issues\n\non:\n  schedule:\n    - cron: '0 0 * * 1' # Every Monday\n  workflow_dispatch:\n\njobs:\n  close-issues:\n    name: Close stale issues\n    runs-on: ubuntu-24.04-arm\n\n    steps:\n      - name: Close stale issues\n        uses: actions/stale@v8\n        with:\n          days-before-issue-stale: 90\n          days-before-issue-close: 14\n          stale-issue-label: 'stale'\n          stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this issue will be closed in 14 days.'\n          close-issue-message: 'This issue was closed because it has been stalled for 14 days with no activity.'\n          exempt-issue-labels: 'fresh'\n          remove-issue-stale-when-updated: true\n          days-before-pr-stale: -1\n          days-before-pr-close: -1\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types: [published]\n\nenv:\n  HUSKY: 0\n\npermissions:\n  id-token: write\n\njobs:\n  publish:\n    name: Publish\n    runs-on: ubuntu-24.04-arm\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Cache Yarn cache\n        uses: actions/cache@v5\n        env:\n          cache-name: yarn-cache\n        with:\n          path: ~/.yarn/berry/cache\n          key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-${{ env.cache-name }}\n\n      - name: Use Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: '24'\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Install Corepack\n        run: npm install -g corepack\n\n      - name: Install dependencies\n        run: yarn --immutable\n\n      - name: Publish with latest tag\n        if: github.event.release.prerelease == false\n        run: yarn npm publish --tag latest\n        working-directory: packages/react-pdf\n\n      - name: Publish with next tag\n        if: github.event.release.prerelease == true\n        run: yarn npm publish --tag next\n        working-directory: packages/react-pdf\n"
  },
  {
    "path": ".gitignore",
    "content": "# OS\n.DS_Store\n\n# Cache\n.cache\n.playwright\n.tmp\n*.tsbuildinfo\n.eslintcache\n\n# Yarn\n.pnp.*\n**/.yarn/*\n!**/.yarn/patches\n!**/.yarn/plugins\n!**/.yarn/releases\n!**/.yarn/sdks\n!**/.yarn/versions\n\n# Project-generated directories and files\n__screenshots__\ncoverage\ndist\nnode_modules\nplaywright-report\ntest-results\npackage.tgz\n\n# Logs\nnpm-debug.log\nyarn-error.log\n\n# .env files\n**/.env\n**/.env.*\n!**/.env.example\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "yarn format --staged --no-errors-on-unmatched --write\n"
  },
  {
    "path": ".mailmap",
    "content": "Niklas Närhinen <niklas@narhinen.net>\nWojciech Maj <kontakt@wojtekmaj.pl>\nWojciech Maj <kontakt@wojtekmaj.pl> <wojciech.maj@motorolasolutions.com>\nWojciech Maj <kontakt@wojtekmaj.pl> <wojciech.maj@ocado.com>\nWojciech Maj <kontakt@wojtekmaj.pl> <w.maj@intive.com>\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"biomejs.biome\"],\n  \"unwantedRecommendations\": [\"dbaeumer.jshint\", \"dbaeumer.vscode-eslint\", \"esbenp.prettier-vscode\"]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.defaultFormatter\": \"biomejs.biome\",\n  \"editor.formatOnSave\": true,\n  \"search.exclude\": {\n    \"**/.yarn\": true\n  }\n}\n"
  },
  {
    "path": ".yarn/plugins/@yarnpkg/plugin-nolyfill.cjs",
    "content": "/* eslint-disable */\n//prettier-ignore\nmodule.exports = {\nname: \"@yarnpkg/plugin-nolyfill\",\nfactory: function (require) {\n\"use strict\";var plugin=(()=>{var p=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var n=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var l=(t=>typeof require<\"u\"?require:typeof Proxy<\"u\"?new Proxy(t,{get:(r,e)=>(typeof require<\"u\"?require:r)[e]}):t)(function(t){if(typeof require<\"u\")return require.apply(this,arguments);throw new Error('Dynamic require of \"'+t+'\" is not supported')});var c=(t,r)=>{for(var e in r)p(t,e,{get:r[e],enumerable:!0})},g=(t,r,e,s)=>{if(r&&typeof r==\"object\"||typeof r==\"function\")for(let a of n(r))!y.call(t,a)&&a!==e&&p(t,a,{get:()=>r[a],enumerable:!(s=i(r,a))||s.enumerable});return t};var f=t=>g(p({},\"__esModule\",{value:!0}),t);var m={};c(m,{default:()=>h});var o=l(\"@yarnpkg/core\"),d=[\"abab\",\"array-buffer-byte-length\",\"array-includes\",\"array.from\",\"array.of\",\"array.prototype.at\",\"array.prototype.every\",\"array.prototype.find\",\"array.prototype.findlast\",\"array.prototype.findlastindex\",\"array.prototype.flat\",\"array.prototype.flatmap\",\"array.prototype.flatmap\",\"array.prototype.foreach\",\"array.prototype.reduce\",\"array.prototype.toreversed\",\"array.prototype.tosorted\",\"arraybuffer.prototype.slice\",\"assert\",\"asynciterator.prototype\",\"available-typed-arrays\",\"deep-equal\",\"deep-equal-json\",\"define-properties\",\"es-aggregate-error\",\"es-iterator-helpers\",\"es-set-tostringtag\",\"es6-object-assign\",\"function-bind\",\"function.prototype.name\",\"get-symbol-description\",\"globalthis\",\"gopd\",\"harmony-reflect\",\"has\",\"has-property-descriptors\",\"has-proto\",\"has-symbols\",\"has-tostringtag\",\"hasown\",\"internal-slot\",\"is-arguments\",\"is-array-buffer\",\"is-core-module\",\"is-date-object\",\"is-generator-function\",\"is-nan\",\"is-regex\",\"is-shared-array-buffer\",\"is-string\",\"is-symbol\",\"is-typed-array\",\"is-weakref\",\"isarray\",\"iterator.prototype\",\"json-stable-stringify\",\"jsonify\",\"object-is\",\"object-keys\",\"object.assign\",\"object.entries\",\"object.fromentries\",\"object.getownpropertydescriptors\",\"object.groupby\",\"object.hasown\",\"object.values\",\"promise.allsettled\",\"promise.any\",\"reflect.getprototypeof\",\"reflect.ownkeys\",\"regexp.prototype.flags\",\"safe-array-concat\",\"safe-regex-test\",\"set-function-length\",\"side-channel\",\"string.prototype.at\",\"string.prototype.codepointat\",\"string.prototype.includes\",\"string.prototype.matchall\",\"string.prototype.padend\",\"string.prototype.padstart\",\"string.prototype.repeat\",\"string.prototype.replaceall\",\"string.prototype.split\",\"string.prototype.startswith\",\"string.prototype.trim\",\"string.prototype.trimend\",\"string.prototype.trimleft\",\"string.prototype.trimright\",\"string.prototype.trimstart\",\"typed-array-buffer\",\"typed-array-byte-length\",\"typed-array-byte-offset\",\"typed-array-length\",\"typedarray\",\"unbox-primitive\",\"util.promisify\",\"which-boxed-primitive\",\"which-typed-array\"],u=new Map(d.map(t=>[o.structUtils.makeIdent(null,t).identHash,o.structUtils.makeIdent(\"nolyfill\",t)])),b={hooks:{reduceDependency:async t=>{let r=u.get(t.identHash);if(r){let e=o.structUtils.makeDescriptor(r,\"latest\"),s=o.structUtils.makeRange({protocol:\"npm:\",source:null,selector:o.structUtils.stringifyDescriptor(e),params:null});return o.structUtils.makeDescriptor(t,s)}return t}}},h=b;return f(m);})();\nreturn plugin;\n}\n};\n"
  },
  {
    "path": ".yarnrc.yml",
    "content": "enableScripts: false\n\nlogFilters:\n  - code: YN0076\n    level: discard\n\nnodeLinker: node-modules\n\nplugins:\n  - checksum: 9b6f8a34bda80f025c0b223fa80836f5e931cf5c8dd83e10ccfa9e677856cf1508b063d027060f74e3ce66ee1c8a936542e85db18a30584f9b88a50379b3f514\n    path: .yarn/plugins/@yarnpkg/plugin-nolyfill.cjs\n    spec: \"https://raw.githubusercontent.com/wojtekmaj/yarn-plugin-nolyfill/v1.0.1/bundles/@yarnpkg/plugin-nolyfill.js\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017–2026 Wojciech Maj\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "__mocks__/_failing_page.ts",
    "content": "import type { PDFPageProxy } from 'pdfjs-dist';\n\nexport default {\n  cleanup: () => {\n    return true;\n  },\n  commonObjs: {\n    get: () => {\n      // Intentionally empty\n    },\n  },\n  getAnnotations: () => new Promise((_resolve, reject) => reject(new Error())),\n  getOperatorList: () => new Promise((_resolve, reject) => reject(new Error())),\n  getStructTree: () => new Promise<void>((_resolve, reject) => reject(new Error())),\n  getTextContent: () => new Promise((_resolve, reject) => reject(new Error())),\n  getViewport: () => ({\n    width: 600,\n    height: 800,\n    rotation: 0,\n  }),\n  render: () => ({\n    promise: new Promise((_resolve, reject) => reject(new Error())),\n    cancel: () => {\n      // Intentionally empty\n    },\n  }),\n} as unknown as PDFPageProxy;\n"
  },
  {
    "path": "__mocks__/_failing_pdf.ts",
    "content": "import type { PDFDocumentProxy } from 'pdfjs-dist';\n\nexport default {\n  _pdfInfo: {\n    fingerprint: 'a62067476e69734bb8eb60122615dfbf',\n    numPages: 4,\n  },\n  getDestination: () => new Promise((_resolve, reject) => reject(new Error())),\n  getOutline: () => new Promise((_resolve, reject) => reject(new Error())),\n  getPage: () => new Promise((_resolve, reject) => reject(new Error())),\n  numPages: 4,\n} as unknown as PDFDocumentProxy;\n"
  },
  {
    "path": "__mocks__/_silently_failing_pdf.ts",
    "content": "import { RenderingCancelledException } from 'pdfjs-dist';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\nexport default {\n  _pdfInfo: {\n    fingerprint: 'a62067476e69734bb8eb60122615dfbf',\n    numPages: 4,\n  },\n  getDestination: () =>\n    new Promise((_resolve, reject) => reject(new RenderingCancelledException('Cancelled'))),\n  getOutline: () =>\n    new Promise((_resolve, reject) => reject(new RenderingCancelledException('Cancelled'))),\n  getPage: () =>\n    new Promise((_resolve, reject) => reject(new RenderingCancelledException('Cancelled'))),\n  numPages: 4,\n} as unknown as PDFDocumentProxy;\n"
  },
  {
    "path": "biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.2.2/schema.json\",\n  \"files\": {\n    \"includes\": [\n      \"**\",\n      \"!**/.yarn\",\n      \"!**/coverage\",\n      \"!**/dist\",\n      \"!**/.pnp.cjs\",\n      \"!**/.pnp.loader.mjs\"\n    ]\n  },\n  \"assist\": {\n    \"actions\": {\n      \"source\": {\n        \"organizeImports\": {\n          \"level\": \"on\",\n          \"options\": {\n            \"groups\": [\n              { \"type\": false, \"source\": \":NODE:\" },\n              { \"type\": false, \"source\": [\"vitest\", \"vitest/**\", \"@vitest/**\", \"vitest-*\"] },\n              { \"type\": false, \"source\": [\"react\", \"react-dom\", \"react-dom/**\", \"react-native\"] },\n              { \"type\": false, \"source\": [\":PACKAGE:\"] },\n              \":BLANK_LINE:\",\n              {\n                \"type\": false,\n                \"source\": [\n                  \":PATH:\",\n                  \"!**/hooks/*\",\n                  \"!**/use*.js\",\n                  \"!**/shared/*\",\n                  \"!**/utils/*\",\n                  \"!**/__mocks__/*\",\n                  \"!**/test-utils.js\"\n                ]\n              },\n              \":BLANK_LINE:\",\n              { \"type\": false, \"source\": [\"**/hooks/*\", \"**/use*.js\"] },\n              \":BLANK_LINE:\",\n              { \"type\": false, \"source\": [\"**/shared/*\", \"**/utils/*\"] },\n              \":BLANK_LINE:\",\n              { \"type\": false, \"source\": \"**/__mocks__/*\" },\n              \":BLANK_LINE:\",\n              { \"type\": false, \"source\": \"**/test-utils.js\" },\n              \":BLANK_LINE:\",\n              \":NODE:\",\n              \":PACKAGE:\",\n              \":PATH:\"\n            ]\n          }\n        }\n      }\n    }\n  },\n  \"formatter\": {\n    \"lineWidth\": 100,\n    \"indentStyle\": \"space\"\n  },\n  \"linter\": {\n    \"rules\": {\n      \"complexity\": {\n        \"noUselessSwitchCase\": \"off\"\n      },\n      \"correctness\": {\n        \"noUnusedImports\": \"warn\",\n        \"noUnusedVariables\": {\n          \"level\": \"warn\",\n          \"options\": {\n            \"ignoreRestSiblings\": true\n          }\n        }\n      },\n      \"suspicious\": {\n        \"noConsole\": \"warn\"\n      }\n    }\n  },\n  \"css\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\"\n    }\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\"\n    }\n  },\n  \"overrides\": [\n    {\n      \"includes\": [\"**/vite.config.ts\"],\n      \"linter\": {\n        \"rules\": {\n          \"suspicious\": {\n            \"noConsole\": \"off\"\n          }\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-pdf-monorepo\",\n  \"version\": \"1.0.0\",\n  \"description\": \"react-pdf monorepo\",\n  \"type\": \"module\",\n  \"workspaces\": [\n    \"packages/*\",\n    \"test\"\n  ],\n  \"scripts\": {\n    \"build\": \"yarn workspace react-pdf build\",\n    \"dev\": \"yarn workspace react-pdf watch & yarn workspace test dev\",\n    \"format\": \"yarn workspaces foreach --all run format\",\n    \"lint\": \"yarn workspaces foreach --all run lint\",\n    \"postinstall\": \"husky\",\n    \"test\": \"yarn workspaces foreach --all run test\",\n    \"tsc\": \"yarn workspaces foreach --all run tsc\",\n    \"unit\": \"yarn workspaces foreach --all run unit\"\n  },\n  \"devDependencies\": {\n    \"husky\": \"^9.0.0\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "packages/react-pdf/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017–2026 Wojciech Maj\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/react-pdf/README.md",
    "content": "[![npm](https://img.shields.io/npm/v/react-pdf.svg)](https://www.npmjs.com/package/react-pdf) ![downloads](https://img.shields.io/npm/dt/react-pdf.svg) [![CI](https://github.com/wojtekmaj/react-pdf/actions/workflows/ci.yml/badge.svg)](https://github.com/wojtekmaj/react-pdf/actions)\n\n# React-PDF\n\nDisplay PDFs in your React app as easily as if they were images.\n\n## Lost?\n\nThis package is used to _display_ existing PDFs. If you wish to _create_ PDFs using React, you may be looking for [@react-pdf/renderer](https://www.npmjs.com/package/@react-pdf/renderer).\n\n## tl;dr\n\n- Install by executing `npm install react-pdf` or `yarn add react-pdf`.\n- Import by adding `import { Document } from 'react-pdf'`.\n- Use by adding `<Document file=\"...\" />`. `file` can be a URL, base64 content, Uint8Array, and more.\n- Put `<Page />` components inside `<Document />` to render pages.\n- Import stylesheets for [annotations](#support-for-annotations) and [text layer](#support-for-text-layer) if applicable.\n\n## Demo\n\nA minimal demo page can be found in `sample` directory.\n\n[Online demo](https://projects.wojtekmaj.pl/react-pdf/) is also available!\n\n## Before you continue\n\nReact-PDF is under constant development. This documentation is written for React-PDF 10.x branch. If you want to see documentation for other versions of React-PDF, use dropdown on top of GitHub page to switch to an appropriate tag. Here are quick links to the newest docs from each branch:\n\n- [v9.x](https://github.com/wojtekmaj/react-pdf/blob/v9.x/packages/react-pdf/README.md)\n- [v8.x](https://github.com/wojtekmaj/react-pdf/blob/v8.x/packages/react-pdf/README.md)\n- [v7.x](https://github.com/wojtekmaj/react-pdf/blob/v7.x/packages/react-pdf/README.md)\n- [v6.x](https://github.com/wojtekmaj/react-pdf/blob/v6.x/README.md)\n- [v5.x](https://github.com/wojtekmaj/react-pdf/blob/v5.x/README.md)\n\n## Getting started\n\n### Compatibility\n\n#### Browser support\n\nReact-PDF supports the latest versions of all major modern browsers.\n\nBrowser compatibility for React-PDF primarily depends on PDF.js support. For details, refer to the [PDF.js documentation](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support).\n\nYou may extend the list of supported browsers by providing additional polyfills (e.g. `Array.prototype.at`, `Promise.allSettled` or `Promise.withResolvers`) and configuring your bundler to transpile `pdfjs-dist`.\n\n#### React\n\nTo use the latest version of React-PDF, your project needs to use React 16.8 or later.\n\n#### Preact\n\nReact-PDF may be used with Preact.\n\n### Installation\n\nAdd React-PDF to your project by executing `npm install react-pdf` or `yarn add react-pdf`.\n\n#### Next.js\n\nIf you use Next.js prior to v15 (v15.0.0-canary.53, specifically), you may need to add the following to your `next.config.js`:\n\n```diff\nmodule.exports = {\n+ swcMinify: false,\n}\n```\n\n### Configure PDF.js worker\n\nFor React-PDF to work, PDF.js worker needs to be provided. You have several options.\n\n#### Import worker (recommended)\n\nFor most cases, the following example will work:\n\n```ts\nimport { pdfjs } from 'react-pdf';\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n```\n\n> [!WARNING]\n> The `workerSrc` must be set in the **same module** where you use React-PDF components (e.g., `<Document>`, `<Page>`). Setting it in a separate file like `main.tsx` and then importing React-PDF in another component may cause the default value to overwrite your custom setting due to module execution order. Always configure the worker in the file where you render the PDF components.\n\n> [!NOTE]\n> In Next.js, make sure to skip SSR when importing the module you're using this code in. Here's how to do this in [Pages Router](https://nextjs.org/docs/pages/guides/lazy-loading#with-no-ssr) and [App Router](https://nextjs.org/docs/app/guides/lazy-loading#skipping-ssr).\n\n> [!NOTE]\n> pnpm requires an `.npmrc` file with `public-hoist-pattern[]=pdfjs-dist` for this to work.\n\n<details>\n<summary>See more examples</summary>\n\n##### Parcel 2\n\nFor Parcel 2, you need to use a slightly different code:\n\n```diff\n pdfjs.GlobalWorkerOptions.workerSrc = new URL(\n-  'pdfjs-dist/build/pdf.worker.min.mjs',\n+  'npm:pdfjs-dist/build/pdf.worker.min.mjs',\n   import.meta.url,\n ).toString();\n```\n\n</details>\n\n#### Copy worker to public directory\n\nYou will have to make sure on your own that `pdf.worker.mjs` file from `pdfjs-dist/build` is copied to your project's output folder.\n\nFor example, you could use a custom script like:\n\n```ts\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nconst pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\nconst pdfWorkerPath = path.join(pdfjsDistPath, 'build', 'pdf.worker.mjs');\n\nfs.cpSync(pdfWorkerPath, './dist/pdf.worker.mjs', { recursive: true });\n```\n\n#### Use external CDN\n\n```ts\nimport { pdfjs } from 'react-pdf';\n\npdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;\n```\n\n> [!WARNING]\n> The `workerSrc` must be set in the **same module** where you use React-PDF components (e.g., `<Document>`, `<Page>`). Setting it in a separate file like `main.tsx` and then importing React-PDF in another component may cause the default value to overwrite your custom setting due to module execution order. Always configure the worker in the file where you render the PDF components.\n\n### Usage\n\nHere's an example of basic usage:\n\n```tsx\nimport { useState } from 'react';\nimport { Document, Page } from 'react-pdf';\n\nfunction MyApp() {\n  const [numPages, setNumPages] = useState<number>();\n  const [pageNumber, setPageNumber] = useState<number>(1);\n\n  function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {\n    setNumPages(numPages);\n  }\n\n  return (\n    <div>\n      <Document file=\"somefile.pdf\" onLoadSuccess={onDocumentLoadSuccess}>\n        <Page pageNumber={pageNumber} />\n      </Document>\n      <p>\n        Page {pageNumber} of {numPages}\n      </p>\n    </div>\n  );\n}\n```\n\nCheck the [sample directory](https://github.com/wojtekmaj/react-pdf/tree/main/sample) in this repository for a full working example. For more examples and more advanced use cases, check [Recipes](https://github.com/wojtekmaj/react-pdf/wiki/Recipes) in [React-PDF Wiki](https://github.com/wojtekmaj/react-pdf/wiki/).\n\n### Support for annotations\n\nIf you want to use annotations (e.g. links) in PDFs rendered by React-PDF, then you would need to include stylesheet necessary for annotations to be correctly displayed like so:\n\n```ts\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\n```\n\n### Support for text layer\n\nIf you want to use text layer in PDFs rendered by React-PDF, then you would need to include stylesheet necessary for text layer to be correctly displayed like so:\n\n```ts\nimport 'react-pdf/dist/Page/TextLayer.css';\n```\n\n### Support for non-latin characters\n\nIf you want to ensure that PDFs with non-latin characters will render perfectly, or you have encountered the following warning:\n\n```\nWarning: The CMap \"baseUrl\" parameter must be specified, ensure that the \"cMapUrl\" and \"cMapPacked\" API parameters are provided.\n```\n\nthen you would also need to include cMaps in your build and tell React-PDF where they are.\n\n#### Copying cMaps\n\nFirst, you need to copy cMaps from `pdfjs-dist` (React-PDF's dependency - it should be in your `node_modules` if you have React-PDF installed). cMaps are located in `pdfjs-dist/cmaps`.\n\n##### Vite\n\nAdd [`vite-plugin-static-copy`](https://www.npmjs.com/package/vite-plugin-static-copy) by executing `npm install vite-plugin-static-copy --save-dev` or `yarn add vite-plugin-static-copy --dev` and add the following to your Vite config:\n\n```diff\n+import path from 'node:path';\n+import { createRequire } from 'node:module';\n\n-import { defineConfig } from 'vite';\n+import { defineConfig, normalizePath } from 'vite';\n+import { viteStaticCopy } from 'vite-plugin-static-copy';\n\n+const require = createRequire(import.meta.url);\n+\n+const pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\n+const cMapsDir = normalizePath(path.join(pdfjsDistPath, 'cmaps'));\n\nexport default defineConfig({\n  plugins: [\n+   viteStaticCopy({\n+     targets: [\n+       {\n+         src: cMapsDir,\n+         dest: '',\n+       },\n+     ],\n+   }),\n  ]\n});\n```\n\n##### Webpack\n\nAdd [`copy-webpack-plugin`](https://www.npmjs.com/package/copy-webpack-plugin) by executing `npm install copy-webpack-plugin --save-dev` or `yarn add copy-webpack-plugin --dev` and add the following to your Webpack config:\n\n```diff\n+import path from 'node:path';\n+import CopyWebpackPlugin from 'copy-webpack-plugin';\n\n+const pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\n+const cMapsDir = path.join(pdfjsDistPath, 'cmaps');\n\nmodule.exports = {\n  plugins: [\n+   new CopyWebpackPlugin({\n+     patterns: [\n+       {\n+         from: cMapsDir,\n+         to: 'cmaps/'\n+       },\n+     ],\n+   }),\n  ],\n};\n```\n\n##### Other tools\n\nIf you use other bundlers, you will have to make sure on your own that cMaps are copied to your project's output folder.\n\nFor example, you could use a custom script like:\n\n```ts\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nconst pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\nconst cMapsDir = path.join(pdfjsDistPath, 'cmaps');\n\nfs.cpSync(cMapsDir, 'dist/cmaps/', { recursive: true });\n```\n\n#### Setting up React-PDF\n\nNow that you have cMaps in your build, pass required options to Document component by using `options` prop, like so:\n\n```ts\n// Outside of React component\nconst options = {\n  cMapUrl: '/cmaps/',\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n> [!NOTE]\n> Make sure to define `options` object outside of your React component or use `useMemo` if you can't.\n\nAlternatively, you could use cMaps from external CDN:\n\n```tsx\n// Outside of React component\nimport { pdfjs } from 'react-pdf';\n\nconst options = {\n  cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n### Support for JPEG 2000\n\nIf you want to ensure that JPEG 2000 images in PDFs will render, or you have encountered the following warning:\n\n```\nWarning: Unable to decode image \"img_p0_1\": \"JpxError: OpenJPEG failed to initialize\".\n```\n\nthen you would also need to include wasm directory in your build and tell React-PDF where it is.\n\n#### Copying wasm directory\n\nFirst, you need to copy wasm from `pdfjs-dist` (React-PDF's dependency - it should be in your `node_modules` if you have React-PDF installed). cMaps are located in `pdfjs-dist/wasm`.\n\n##### Vite\n\nAdd [`vite-plugin-static-copy`](https://www.npmjs.com/package/vite-plugin-static-copy) by executing `npm install vite-plugin-static-copy --save-dev` or `yarn add vite-plugin-static-copy --dev` and add the following to your Vite config:\n\n```diff\n+import path from 'node:path';\n+import { createRequire } from 'node:module';\n\n-import { defineConfig } from 'vite';\n+import { defineConfig, normalizePath } from 'vite';\n+import { viteStaticCopy } from 'vite-plugin-static-copy';\n\n+const require = createRequire(import.meta.url);\n+\n+const pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\n+const wasmDir = normalizePath(path.join(pdfjsDistPath, 'wasm'));\n\nexport default defineConfig({\n  plugins: [\n+   viteStaticCopy({\n+     targets: [\n+       {\n+         src: wasmDir,\n+         dest: '',\n+       },\n+     ],\n+   }),\n  ]\n});\n```\n\n##### Webpack\n\nAdd [`copy-webpack-plugin`](https://www.npmjs.com/package/copy-webpack-plugin) by executing `npm install copy-webpack-plugin --save-dev` or `yarn add copy-webpack-plugin --dev` and add the following to your Webpack config:\n\n```diff\n+import path from 'node:path';\n+import CopyWebpackPlugin from 'copy-webpack-plugin';\n\n+const pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\n+const wasmDir = path.join(pdfjsDistPath, 'wasm');\n\nmodule.exports = {\n  plugins: [\n+   new CopyWebpackPlugin({\n+     patterns: [\n+       {\n+         from: wasmDir,\n+         to: 'wasm/'\n+       },\n+     ],\n+   }),\n  ],\n};\n```\n\n##### Other tools\n\nIf you use other bundlers, you will have to make sure on your own that wasm directory is copied to your project's output folder.\n\nFor example, you could use a custom script like:\n\n```ts\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nconst pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\nconst wasmDir = path.join(pdfjsDistPath, 'wasm');\n\nfs.cpSync(wasmDir, 'dist/wasm/', { recursive: true });\n```\n\n#### Setting up React-PDF\n\nNow that you have wasm directory in your build, pass required options to Document component by using `options` prop, like so:\n\n```ts\n// Outside of React component\nconst options = {\n  wasmUrl: '/wasm/',\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n> [!NOTE]\n> Make sure to define `options` object outside of your React component or use `useMemo` if you can't.\n\nAlternatively, you could use wasm directory from external CDN:\n\n```tsx\n// Outside of React component\nimport { pdfjs } from 'react-pdf';\n\nconst options = {\n  wasmUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/wasm/`,\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n### Support for standard fonts\n\nIf you want to support PDFs using standard fonts (deprecated in PDF 1.5, but still around), or you have encountered the following warning:\n\n```\nThe standard font \"baseUrl\" parameter must be specified, ensure that the \"standardFontDataUrl\" API parameter is provided.\n```\n\nthen you would also need to include standard fonts in your build and tell React-PDF where they are.\n\n#### Copying fonts\n\nFirst, you need to copy standard fonts from `pdfjs-dist` (React-PDF's dependency - it should be in your `node_modules` if you have React-PDF installed). Standard fonts are located in `pdfjs-dist/standard_fonts`.\n\n##### Vite\n\nAdd [`vite-plugin-static-copy`](https://www.npmjs.com/package/vite-plugin-static-copy) by executing `npm install vite-plugin-static-copy --save-dev` or `yarn add vite-plugin-static-copy --dev` and add the following to your Vite config:\n\n```diff\n+import path from 'node:path';\n+import { createRequire } from 'node:module';\n\n-import { defineConfig } from 'vite';\n+import { defineConfig, normalizePath } from 'vite';\n+import { viteStaticCopy } from 'vite-plugin-static-copy';\n\n+const require = createRequire(import.meta.url);\n+const standardFontsDir = normalizePath(\n+  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts')\n+);\n\nexport default defineConfig({\n  plugins: [\n+   viteStaticCopy({\n+     targets: [\n+       {\n+         src: standardFontsDir,\n+         dest: '',\n+       },\n+     ],\n+   }),\n  ]\n});\n```\n\n##### Webpack\n\nAdd [`copy-webpack-plugin`](https://www.npmjs.com/package/copy-webpack-plugin) by executing `npm install copy-webpack-plugin --save-dev` or `yarn add copy-webpack-plugin --dev` and add the following to your Webpack config:\n\n```diff\n+import path from 'node:path';\n+import CopyWebpackPlugin from 'copy-webpack-plugin';\n\n+const standardFontsDir = path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts');\n\nmodule.exports = {\n  plugins: [\n+   new CopyWebpackPlugin({\n+     patterns: [\n+       {\n+         from: standardFontsDir,\n+         to: 'standard_fonts/'\n+       },\n+     ],\n+   }),\n  ],\n};\n```\n\n##### Other tools\n\nIf you use other bundlers, you will have to make sure on your own that standard fonts are copied to your project's output folder.\n\nFor example, you could use a custom script like:\n\n```ts\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nconst pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));\nconst standardFontsDir = path.join(pdfjsDistPath, 'standard_fonts');\n\nfs.cpSync(standardFontsDir, 'dist/standard_fonts/', { recursive: true });\n```\n\n#### Setting up React-PDF\n\nNow that you have standard fonts in your build, pass required options to Document component by using `options` prop, like so:\n\n```tsx\n// Outside of React component\nconst options = {\n  standardFontDataUrl: '/standard_fonts/',\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n> [!NOTE]\n> Make sure to define `options` object outside of your React component or use `useMemo` if you can't.\n\nAlternatively, you could use standard fonts from external CDN:\n\n```tsx\n// Outside of React component\nimport { pdfjs } from 'react-pdf';\n\nconst options = {\n  standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/standard_fonts/`,\n};\n\n// Inside of React component\n<Document options={options} />;\n```\n\n## User guide\n\n### Document\n\nLoads a document passed using `file` prop.\n\n#### Props\n\n| Prop name          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Default value                                         | Example values                                                                                                                                                                                                                                                               |\n| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| className          | Class name(s) that will be added to rendered element along with the default `react-pdf__Document`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | n/a                                                   | <ul><li>String:<br />`\"custom-class-name-1 custom-class-name-2\"`</li><li>Array of strings:<br />`[\"custom-class-name-1\", \"custom-class-name-2\"]`</li></ul>                                                                                                                   |\n| error              | What the component should display in case of an error.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `\"Failed to load PDF file.\"`                          | <ul><li>String:<br />`\"An error occurred!\"`</li><li>React element:<br />`<p>An error occurred!</p>`</li><li>Function:<br />`this.renderError`</li></ul>                                                                                                                      |\n| externalLinkRel    | Link rel for links rendered in annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | `\"noopener noreferrer nofollow\"`                      | One of valid [values for `rel` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-rel).<ul><li>`\"noopener\"`</li><li>`\"noreferrer\"`</li><li>`\"nofollow\"`</li><li>`\"noopener noreferrer\"`</li></ul>                                                   |\n| externalLinkTarget | Link target for external links rendered in annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | unset, which means that default behavior will be used | One of valid [values for `target` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target).<ul><li>`\"_self\"`</li><li>`\"_blank\"`</li><li>`\"_parent\"`</li><li>`\"_top\"`</li></ul>                                                                    |\n| file               | What PDF should be displayed.<br />Its value can be an URL, a file (imported using `import … from …` or from file input form element), or an object with parameters (`url` - URL; `data` - data, preferably Uint8Array; `range` - PDFDataRangeTransport.<br />**Warning**: Since equality check (`===`) is used to determine if `file` object has changed, it must be memoized by setting it in component's state, `useMemo` or other similar technique.                                                                                                                                                                                     | n/a                                                   | <ul><li>URL:<br />`\"https://example.com/sample.pdf\"`</li><li>File:<br />`import importedPdf from '../static/sample.pdf'` and then<br />`sample`</li><li>Parameter object:<br />`{ url: 'https://example.com/sample.pdf' }`</ul>                                              |\n| imageResourcesPath | The path used to prefix the src attributes of annotation SVGs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | n/a (pdf.js will fallback to an empty string)         | `\"/public/images/\"`                                                                                                                                                                                                                                                          |\n| inputRef           | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Document>` component.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | n/a                                                   | <ul><li>Function:<br />`(ref) => { this.myDocument = ref; }`</li><li>Ref created using `createRef`:<br />`this.ref = createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `useRef`:<br />`const ref = useRef();`<br />…<br />`inputRef={ref}`</li></ul> |\n| loading            | What the component should display while loading.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `\"Loading PDF…\"`                                      | <ul><li>String:<br />`\"Please wait!\"`</li><li>React element:<br />`<p>Please wait!</p>`</li><li>Function:<br />`this.renderLoader`</li></ul>                                                                                                                               |\n| noData             | What the component should display in case of no data.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `\"No PDF file specified.\"`                            | <ul><li>String:<br />`\"Please select a file.\"`</li><li>React element:<br />`<p>Please select a file.</p>`</li><li>Function:<br />`this.renderNoData`</li></ul>                                                                                                               |\n| onItemClick        | Function called when an outline item or a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to.                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | n/a                                                   | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')`                                                                                                                                                                                |\n| onLoadError        | Function called in case of an error while loading a document.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | n/a                                                   | `(error) => alert('Error while loading document! ' + error.message)`                                                                                                                                                                                                         |\n| onLoadProgress     | Function called, potentially multiple times, as the loading progresses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | n/a                                                   | `({ loaded, total }) => alert('Loading a document: ' + (loaded / total) * 100 + '%')`                                                                                                                                                                                        |\n| onLoadSuccess      | Function called when the document is successfully loaded.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | n/a                                                   | `(pdf) => alert('Loaded a file with ' + pdf.numPages + ' pages!')`                                                                                                                                                                                                           |\n| onPassword         | Function called when a password-protected PDF is loaded.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Function that prompts the user for password.          | `(callback) => callback('s3cr3t_p4ssw0rd')`                                                                                                                                                                                                                                  |\n| onSourceError      | Function called in case of an error while retrieving document source from `file` prop.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | n/a                                                   | `(error) => alert('Error while retrieving document source! ' + error.message)`                                                                                                                                                                                               |\n| onSourceSuccess    | Function called when document source is successfully retrieved from `file` prop.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | n/a                                                   | `() => alert('Document source retrieved!')`                                                                                                                                                                                                                                  |\n| options            | An object in which additional parameters to be passed to PDF.js can be defined. Most notably:<ul><li>`cMapUrl`;</li><li>`httpHeaders` - custom request headers, e.g. for authorization);</li><li>`withCredentials` - a boolean to indicate whether or not to include cookies in the request (defaults to `false`)</li></ul>For a full list of possible parameters, check [PDF.js documentation on DocumentInitParameters](https://mozilla.github.io/pdf.js/api/draft/module-pdfjsLib.html#~DocumentInitParameters).<br /><br />**Note**: Make sure to define options object outside of your React component or use `useMemo` if you can't. | n/a                                                   | `{ cMapUrl: '/cmaps/' }`                                                                                                                                                                                                                                                     |\n| renderMode         | Rendering mode of the document. Can be `\"canvas\"`, `\"custom\"` or `\"none\"`. If set to `\"custom\"`, `customRenderer` must also be provided.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | `\"canvas\"`                                            | `\"custom\"`                                                                                                                                                                                                                                                                   |\n| rotate             | Rotation of the document in degrees. If provided, will change rotation globally, even for the pages which were given `rotate` prop of their own. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left.                                                                                                                                                                                                                                                                                                                                                                                                              | n/a                                                   | `90`                                                                                                                                                                                                                                                                         |\n| scale              | Document scale.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | `1`                                                   | `0.5`                                                                                                                                                                                                                                                                        |\n\n### Page\n\nDisplays a page. Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function, however some advanced functions like rendering annotations and linking between pages inside a document may not be working correctly.\n\n#### Props\n\n| Prop name                      | Description                                                                                                                                                                                                                                                                                      | Default value                                       | Example values                                                                                                                                                                                                                                                             |\n| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| canvasBackground               | Canvas background color. Any valid `canvas.fillStyle` can be used.                                                                                                                                                                                                                               | n/a                                                 | `\"transparent\"`                                                                                                                                                                                                                                                            |\n| canvasRef                      | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to `<canvas>` rendered by `<Canvas>` component.                                                                                                                                                  | n/a                                                 | <ul><li>Function:<br />`(ref) => { this.myCanvas = ref; }`</li><li>Ref created using `createRef`:<br />`this.ref = createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `useRef`:<br />`const ref = useRef();`<br />…<br />`inputRef={ref}`</li></ul> |\n| className                      | Class name(s) that will be added to rendered element along with the default `react-pdf__Page`.                                                                                                                                                                                                   | n/a                                                 | <ul><li>String:<br />`\"custom-class-name-1 custom-class-name-2\"`</li><li>Array of strings:<br />`[\"custom-class-name-1\", \"custom-class-name-2\"]`</li></ul>                                                                                                                 |\n| customRenderer                 | Function that customizes how a page is rendered. You must set `renderMode` to `\"custom\"` to use this prop.                                                                                                                                                                                       | n/a                                                 | `MyCustomRenderer`                                                                                                                                                                                                                                                         |\n| customTextRenderer             | Function that customizes how a text layer is rendered.                                                                                                                                                                                                                                           | n/a                                                 | ``({ str, itemIndex }) => str.replace(/ipsum/g, value => `<mark>${value}</mark>`)``                                                                                                                                                                                        |\n| devicePixelRatio               | The ratio between physical pixels and device-independent pixels (DIPs) on the current device.                                                                                                                                                                                                    | `window.devicePixelRatio`                           | `1`                                                                                                                                                                                                                                                                        |\n| error                          | What the component should display in case of an error.                                                                                                                                                                                                                                           | `\"Failed to load the page.\"`                        | <ul><li>String:<br />`\"An error occurred!\"`</li><li>React element:<br />`<p>An error occurred!</p>`</li><li>Function:<br />`this.renderError`</li></ul>                                                                                                                    |\n| filterAnnotations              | Function to filter annotations before they are rendered.                                                                                                                                                                                                                                         | n/a                                                 | `({ annotations }) => annotations.filter(annotation => annotation.subtype === 'Text')` |\n| height                         | Page height. If neither `height` nor `width` are defined, page will be rendered at the size defined in PDF. If you define `width` and `height` at the same time, `height` will be ignored. If you define `height` and `scale` at the same time, the height will be multiplied by a given factor. | Page's default height                               | `300`                                                                                                                                                                                                                                                                      |\n| imageResourcesPath             | The path used to prefix the src attributes of annotation SVGs.                                                                                                                                                                                                                                   | n/a (pdf.js will fallback to an empty string)       | `\"/public/images/\"`                                                                                                                                                                                                                                                        |\n| inputRef                       | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Page>` component.                                                                                                                                                  | n/a                                                 | <ul><li>Function:<br />`(ref) => { this.myPage = ref; }`</li><li>Ref created using `createRef`:<br />`this.ref = createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `useRef`:<br />`const ref = useRef();`<br />…<br />`inputRef={ref}`</li></ul>   |\n| loading                        | What the component should display while loading.                                                                                                                                                                                                                                                 | `\"Loading page…\"`                                   | <ul><li>String:<br />`\"Please wait!\"`</li><li>React element:<br />`<p>Please wait!</p>`</li><li>Function:<br />`this.renderLoader`</li></ul>                                                                                                                               |\n| noData                         | What the component should display in case of no data.                                                                                                                                                                                                                                            | `\"No page specified.\"`                              | <ul><li>String:<br />`\"Please select a page.\"`</li><li>React element:<br />`<p>Please select a page.</p>`</li><li>Function:<br />`this.renderNoData`</li></ul>                                                                                                             |\n| onGetAnnotationsError          | Function called in case of an error while loading annotations.                                                                                                                                                                                                                                   | n/a                                                 | `(error) => alert('Error while loading annotations! ' + error.message)`                                                                                                                                                                                                    |\n| onGetAnnotationsSuccess        | Function called when annotations are successfully loaded.                                                                                                                                                                                                                                        | n/a                                                 | `(annotations) => alert('Now displaying ' + annotations.length + ' annotations!')`                                                                                                                                                                                         |\n| onGetStructTreeError           | Function called in case of an error while loading structure tree.                                                                                                                                                                                                                                | n/a                                                 | `(error) => alert('Error while loading structure tree! ' + error.message)`                                                                                                                                                                                                 |\n| onGetStructTreeSuccess         | Function called when structure tree is successfully loaded.                                                                                                                                                                                                                                      | n/a                                                 | `(structTree) => alert(JSON.stringify(structTree))`                                                                                                                                                                                                                        |\n| onGetTextError                 | Function called in case of an error while loading text layer items.                                                                                                                                                                                                                              | n/a                                                 | `(error) => alert('Error while loading text layer items! ' + error.message)`                                                                                                                                                                                               |\n| onGetTextSuccess               | Function called when text layer items are successfully loaded.                                                                                                                                                                                                                                   | n/a                                                 | `({ items, styles }) => alert('Now displaying ' + items.length + ' text layer items!')`                                                                                                                                                                                    |\n| onLoadError                    | Function called in case of an error while loading the page.                                                                                                                                                                                                                                      | n/a                                                 | `(error) => alert('Error while loading page! ' + error.message)`                                                                                                                                                                                                           |\n| onLoadSuccess                  | Function called when the page is successfully loaded.                                                                                                                                                                                                                                            | n/a                                                 | `(page) => alert('Now displaying a page number ' + page.pageNumber + '!')`                                                                                                                                                                                                 |\n| onRenderAnnotationLayerError   | Function called in case of an error while rendering the annotation layer.                                                                                                                                                                                                                        | n/a                                                 | `(error) => alert('Error while loading annotation layer! ' + error.message)`                                                                                                                                                                                               |\n| onRenderAnnotationLayerSuccess | Function called when annotations are successfully rendered on the screen.                                                                                                                                                                                                                        | n/a                                                 | `() => alert('Rendered the annotation layer!')`                                                                                                                                                                                                                            |\n| onRenderError                  | Function called in case of an error while rendering the page.                                                                                                                                                                                                                                    | n/a                                                 | `(error) => alert('Error while loading page! ' + error.message)`                                                                                                                                                                                                           |\n| onRenderSuccess                | Function called when the page is successfully rendered on the screen.                                                                                                                                                                                                                            | n/a                                                 | `() => alert('Rendered the page!')`                                                                                                                                                                                                                                        |\n| onRenderTextLayerError         | Function called in case of an error while rendering the text layer.                                                                                                                                                                                                                              | n/a                                                 | `(error) => alert('Error while rendering text layer! ' + error.message)`                                                                                                                                                                                                   |\n| onRenderTextLayerSuccess       | Function called when the text layer is successfully rendered on the screen.                                                                                                                                                                                                                      | n/a                                                 | `() => alert('Rendered the text layer!')`                                                                                                                                                                                                                                  |\n| pageColors                     | Colors used to render the page. If not provided, the default colors from PDF will be used.                                                                                                                                                                                                       | n/a                                                 | `{ background: 'black', foreground: '#ffff00' }`                                                                                                                                                                                                                         |\n| pageIndex                      | Which page from PDF file should be displayed, by page index. Ignored if `pageNumber` prop is provided.                                                                                                                                                                                           | `0`                                                 | `1`                                                                                                                                                                                                                                                                        |\n| pageNumber                     | Which page from PDF file should be displayed, by page number. If provided, `pageIndex` prop will be ignored.                                                                                                                                                                                     | `1`                                                 | `2`                                                                                                                                                                                                                                                                        |\n| pdf                            | pdf object obtained from `<Document />`'s `onLoadSuccess` callback function.                                                                                                                                                                                                                     | (automatically obtained from parent `<Document />`) | `pdf`                                                                                                                                                                                                                                                                      |\n| renderAnnotationLayer          | Whether annotations (e.g. links) should be rendered.                                                                                                                                                                                                                                             | `true`                                              | `false`                                                                                                                                                                                                                                                                    |\n| renderForms                    | Whether forms should be rendered. `renderAnnotationLayer` prop must be set to `true`.                                                                                                                                                                                                            | `false`                                             | `true`                                                                                                                                                                                                                                                                     |\n| renderMode                     | Rendering mode of the document. Can be `\"canvas\"`, `\"custom\"` or `\"none\"`. If set to `\"custom\"`, `customRenderer` must also be provided.                                                                                                                                                         | `\"canvas\"`                                          | `\"custom\"`                                                                                                                                                                                                                                                                 |\n| renderTextLayer                | Whether a text layer should be rendered.                                                                                                                                                                                                                                                         | `true`                                              | `false`                                                                                                                                                                                                                                                                    |\n| rotate                         | Rotation of the page in degrees. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left.                                                                                                                                                                                  | Page's default setting, usually `0`                 | `90`                                                                                                                                                                                                                                                                       |\n| scale                          | Page scale.                                                                                                                                                                                                                                                                                      | `1`                                                 | `0.5`                                                                                                                                                                                                                                                                      |\n| width                          | Page width. If neither `height` nor `width` are defined, page will be rendered at the size defined in PDF. If you define `width` and `height` at the same time, `height` will be ignored. If you define `width` and `scale` at the same time, the width will be multiplied by a given factor.    | Page's default width                                | `300`                                                                                                                                                                                                                                                                      |\n\n### Outline\n\nDisplays an outline (table of contents). Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function.\n\n#### Props\n\n| Prop name     | Description                                                                                                                                        | Default value | Example values                                                                                                                                                                                                                                                              |\n| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| className     | Class name(s) that will be added to rendered element along with the default `react-pdf__Outline`.                                                  | n/a           | <ul><li>String:<br />`\"custom-class-name-1 custom-class-name-2\"`</li><li>Array of strings:<br />`[\"custom-class-name-1\", \"custom-class-name-2\"]`</li></ul>                                                                                                                  |\n| inputRef      | A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Outline>` component. | n/a           | <ul><li>Function:<br />`(ref) => { this.myOutline = ref; }`</li><li>Ref created using `createRef`:<br />`this.ref = createRef();`<br />…<br />`inputRef={this.ref}`</li><li>Ref created using `useRef`:<br />`const ref = useRef();`<br />…<br />`inputRef={ref}`</li></ul> |\n| onItemClick   | Function called when an outline item has been clicked. Usually, you would like to use this callback to move the user wherever they requested to.   | n/a           | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')`                                                                                                                                                                               |\n| onLoadError   | Function called in case of an error while retrieving the outline.                                                                                  | n/a           | `(error) => alert('Error while retrieving the outline! ' + error.message)`                                                                                                                                                                                                  |\n| onLoadSuccess | Function called when the outline is successfully retrieved.                                                                                        | n/a           | `(outline) => alert('The outline has been successfully retrieved.')`                                                                                                                                                                                                        |\n\n### Thumbnail\n\nDisplays a thumbnail of a page. Does not render the annotation layer or the text layer. Does not register itself as a link target, so the user will not be scrolled to a Thumbnail component when clicked on an internal link (e.g. in Table of Contents). When clicked, attempts to navigate to the page clicked (similarly to a link in Outline). Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function.\n\n#### Props\n\nProps are the same as in `<Page />` component, but certain annotation layer and text layer-related props are not available:\n\n- customTextRenderer\n- onGetAnnotationsError\n- onGetAnnotationsSuccess\n- onGetTextError\n- onGetTextSuccess\n- onRenderAnnotationLayerError\n- onRenderAnnotationLayerSuccess\n- onRenderTextLayerError\n- onRenderTextLayerSuccess\n- renderAnnotationLayer\n- renderForms\n- renderTextLayer\n\nOn top of that, additional props are available:\n\n| Prop name   | Description                                                                                                                                  | Default value | Example values                                                                                                                                             |\n| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| className   | Class name(s) that will be added to rendered element along with the default `react-pdf__Thumbnail`.                                          | n/a           | <ul><li>String:<br />`\"custom-class-name-1 custom-class-name-2\"`</li><li>Array of strings:<br />`[\"custom-class-name-1\", \"custom-class-name-2\"]`</li></ul> |\n| onItemClick | Function called when a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to. | n/a           | `({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')`                                                              |\n\n## Useful links\n\n- [React-PDF Wiki](https://github.com/wojtekmaj/react-pdf/wiki/)\n\n## License\n\nThe MIT License.\n\n## Author\n\n<table>\n  <tr>\n    <td >\n      <img src=\"https://avatars.githubusercontent.com/u/5426427?v=4&s=128\" width=\"64\" height=\"64\" alt=\"Wojciech Maj\">\n    </td>\n    <td>\n      <a href=\"https://github.com/wojtekmaj\">Wojciech Maj</a>\n    </td>\n  </tr>\n</table>\n\n## Thank you\n\nThis project wouldn't be possible without the awesome work of [Niklas Närhinen](https://github.com/nnarhinen) who created its original version and without Mozilla, author of [pdf.js](http://mozilla.github.io/pdf.js). Thank you!\n\n### Sponsors\n\nThank you to all our sponsors! [Become a sponsor](https://opencollective.com/react-pdf-wojtekmaj#sponsor) and get your image on our README on GitHub.\n\n<a href=\"https://opencollective.com/react-pdf-wojtekmaj#sponsors\" target=\"_blank\"><img src=\"https://opencollective.com/react-pdf-wojtekmaj/sponsors.svg?width=890\"></a>\n\n### Backers\n\nThank you to all our backers! [Become a backer](https://opencollective.com/react-pdf-wojtekmaj#backer) and get your image on our README on GitHub.\n\n<a href=\"https://opencollective.com/react-pdf-wojtekmaj#backers\" target=\"_blank\"><img src=\"https://opencollective.com/react-pdf-wojtekmaj/backers.svg?width=890\"></a>\n\n### Top Contributors\n\nThank you to all our contributors that helped on this project!\n\n![Top Contributors](https://opencollective.com/react-pdf/contributors.svg?width=890&button=false)\n"
  },
  {
    "path": "packages/react-pdf/package.json",
    "content": "{\n  \"name\": \"react-pdf\",\n  \"version\": \"10.4.1\",\n  \"description\": \"Display PDFs in your React app as easily as if they were images.\",\n  \"type\": \"module\",\n  \"sideEffects\": [\n    \"*.css\"\n  ],\n  \"main\": \"./dist/index.js\",\n  \"source\": \"./src/index.ts\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": \"./dist/index.js\",\n    \"./*\": \"./*\"\n  },\n  \"scripts\": {\n    \"build\": \"yarn build-js && yarn copy-styles\",\n    \"build-js\": \"tsc --project tsconfig.build.json\",\n    \"clean\": \"node -e \\\"fs.rmSync('./dist', { recursive: true, force: true })\\\"\",\n    \"copy-styles\": \"cpy 'src/**/*.css' dist\",\n    \"format\": \"biome format\",\n    \"lint\": \"biome lint\",\n    \"prepack\": \"yarn clean && yarn build\",\n    \"test\": \"yarn lint && yarn tsc && yarn format && yarn unit\",\n    \"tsc\": \"tsc\",\n    \"unit\": \"vitest\",\n    \"watch\": \"yarn build-js --watch & node --eval \\\"fs.watch('src', () => child_process.exec('yarn copy-styles'))\\\"\"\n  },\n  \"keywords\": [\n    \"pdf\",\n    \"pdf-viewer\",\n    \"react\"\n  ],\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"clsx\": \"^2.0.0\",\n    \"dequal\": \"^2.0.3\",\n    \"make-cancellable-promise\": \"^2.0.0\",\n    \"make-event-props\": \"^2.0.0\",\n    \"merge-refs\": \"^2.0.0\",\n    \"pdfjs-dist\": \"5.4.296\",\n    \"tiny-invariant\": \"^1.0.0\",\n    \"warning\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"2.2.2\",\n    \"@types/node\": \"*\",\n    \"@types/react\": \"^19.2.0\",\n    \"@types/react-dom\": \"^19.2.0\",\n    \"@types/warning\": \"^3.0.0\",\n    \"@vitest/browser-playwright\": \"^4.0.1\",\n    \"cpy-cli\": \"^5.0.0\",\n    \"playwright\": \"^1.55.1\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"typescript\": \"^5.9.2\",\n    \"vitest\": \"^4.0.1\",\n    \"vitest-browser-react\": \"^2.0.0\"\n  },\n  \"peerDependencies\": {\n    \"@types/react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react-dom\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/react\": {\n      \"optional\": true\n    }\n  },\n  \"publishConfig\": {\n    \"access\": \"public\",\n    \"provenance\": true\n  },\n  \"files\": [\n    \"dist/**/*\",\n    \"src/**/*\",\n    \"!**/*.spec.ts\",\n    \"!**/*.spec.tsx\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/wojtekmaj/react-pdf.git\",\n    \"directory\": \"packages/react-pdf\"\n  },\n  \"funding\": \"https://github.com/wojtekmaj/react-pdf?sponsor=1\"\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Document.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { page, userEvent } from 'vitest/browser';\nimport { render } from 'vitest-browser-react';\nimport { createRef } from 'react';\n\nimport Document from './Document.js';\nimport DocumentContext from './DocumentContext.js';\nimport { pdfjs } from './index.test.js';\nimport Page from './Page.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type LinkService from './LinkService.js';\nimport type { ScrollPageIntoViewArgs } from './shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst pdfFile2 = await loadPDF('../../__mocks__/_pdf2.pdf');\n\nconst OK = Symbol('OK');\n\nfunction ChildInternal({\n  renderMode,\n  rotate,\n  scale,\n}: {\n  renderMode?: string | null;\n  rotate?: number | null;\n  scale?: number | null;\n}) {\n  return (\n    <div data-testid=\"child\" data-rendermode={renderMode} data-rotate={rotate} data-scale={scale} />\n  );\n}\n\nfunction Child(props: React.ComponentProps<typeof ChildInternal>) {\n  return (\n    <DocumentContext.Consumer>\n      {(context) => <ChildInternal {...context} {...props} />}\n    </DocumentContext.Consumer>\n  );\n}\n\nasync function waitForAsync() {\n  await new Promise((resolve) => {\n    setTimeout(resolve, 0);\n  });\n}\n\ndescribe('Document', () => {\n  // Object with basic loaded PDF information that shall match after successful loading\n  const desiredLoadedPdf: Partial<PDFDocumentProxy> = {};\n  const desiredLoadedPdf2: Partial<PDFDocumentProxy> = {};\n\n  beforeAll(async () => {\n    const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n    desiredLoadedPdf._pdfInfo = pdf._pdfInfo;\n\n    const pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;\n    desiredLoadedPdf2._pdfInfo = pdf2._pdfInfo;\n  });\n\n  describe('loading', () => {\n    it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via data URI properly', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.dataURI}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(2);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n    });\n\n    it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via data URI properly (param object)', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={{ url: pdfFile.dataURI }}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(2);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n    });\n\n    it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via ArrayBuffer properly', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.arrayBuffer}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(2);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n    });\n\n    it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via Blob properly', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.blob}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(2);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n    });\n\n    it('loads a file and calls onSourceSuccess and onLoadSuccess callbacks via File properly', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.file}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(2);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n    });\n\n    it('fails to load a file and calls onSourceError given invalid file source', async () => {\n      const { func: onSourceError, promise: onSourceErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      // @ts-expect-error-next-line\n      await render(<Document file={() => null} onSourceError={onSourceError} />);\n\n      expect.assertions(1);\n\n      const [error] = await onSourceErrorPromise;\n\n      expect(error).toMatchObject(expect.any(Error));\n\n      restoreConsole();\n    });\n\n    it('replaces a file properly', async () => {\n      const { func: onSourceSuccess, promise: onSourceSuccessPromise } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await render(\n        <Document\n          file={pdfFile.file}\n          onLoadSuccess={onLoadSuccess}\n          onSourceSuccess={onSourceSuccess}\n        />,\n      );\n\n      expect.assertions(4);\n\n      await expect(onSourceSuccessPromise).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPdf]);\n\n      const { func: onSourceSuccess2, promise: onSourceSuccessPromise2 } = makeAsyncCallback(OK);\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(\n        <Document\n          file={pdfFile2.file}\n          onLoadSuccess={onLoadSuccess2}\n          onSourceSuccess={onSourceSuccess2}\n        />,\n      );\n\n      await expect(onSourceSuccessPromise2).resolves.toBe(OK);\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedPdf2]);\n    });\n  });\n\n  describe('rendering', () => {\n    it('applies className to its wrapper when given a string', async () => {\n      const className = 'testClassName';\n\n      const { container } = await render(<Document className={className} />);\n\n      const wrapper = container.querySelector('.react-pdf__Document');\n\n      expect(wrapper).toHaveClass(className);\n    });\n\n    it('passes container element to inputRef properly', async () => {\n      const inputRef = createRef<HTMLDivElement>();\n\n      await render(<Document inputRef={inputRef} />);\n\n      expect(inputRef.current).toBeInstanceOf(HTMLDivElement);\n    });\n\n    it('renders \"No PDF file specified.\" when given nothing', async () => {\n      const { container } = await render(<Document />);\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('No PDF file specified.');\n    });\n\n    it('renders custom no data message when given nothing and noData prop is given', async () => {\n      const { container } = await render(<Document noData=\"Nothing here\" />);\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n    });\n\n    it('renders custom no data message when given nothing and noData prop is given as a function', async () => {\n      const { container } = await render(<Document noData={() => 'Nothing here'} />);\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n    });\n\n    it('renders \"Loading PDF…\" when loading a file', async () => {\n      const { container } = await render(<Document file={pdfFile.file} />);\n\n      const loading = container.querySelector('.react-pdf__message');\n\n      expect(loading).toBeInTheDocument();\n      await expect.element(page.getByText('Loading PDF…')).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a file and loading prop is given', async () => {\n      const { container } = await render(<Document file={pdfFile.file} loading=\"Loading\" />);\n\n      const loading = container.querySelector('.react-pdf__message');\n\n      expect(loading).toBeInTheDocument();\n      await expect.element(page.getByText('Loading')).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a file and loading prop is given as a function', async () => {\n      const { container } = await render(\n        <Document file={pdfFile.file} loading={() => 'Loading'} />,\n      );\n\n      const loading = container.querySelector('.react-pdf__message');\n\n      expect(loading).toBeInTheDocument();\n      await expect.element(page.getByText('Loading')).toBeInTheDocument();\n    });\n\n    it('renders \"Failed to load PDF file.\" when failed to load a document', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n      const failingPdf = 'data:application/pdf;base64,abcdef';\n\n      muteConsole();\n\n      const { container } = await render(<Document file={failingPdf} onLoadError={onLoadError} />);\n\n      expect.assertions(2);\n\n      await onLoadErrorPromise;\n\n      await waitForAsync();\n\n      const error = container.querySelector('.react-pdf__message');\n\n      expect(error).toBeInTheDocument();\n      await expect.element(page.getByText('Failed to load PDF file.')).toBeInTheDocument();\n\n      restoreConsole();\n    });\n\n    it('renders custom error message when failed to load a document and error prop is given', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n      const failingPdf = 'data:application/pdf;base64,abcdef';\n\n      muteConsole();\n\n      const { container } = await render(\n        <Document error=\"Error\" file={failingPdf} onLoadError={onLoadError} />,\n      );\n\n      expect.assertions(2);\n\n      await onLoadErrorPromise;\n\n      await waitForAsync();\n\n      const error = container.querySelector('.react-pdf__message');\n\n      expect(error).toBeInTheDocument();\n\n      await expect.element(page.getByText('Error', { exact: true })).toBeInTheDocument();\n\n      restoreConsole();\n    });\n\n    it('renders custom error message when failed to load a document and error prop is given as a function', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n      const failingPdf = 'data:application/pdf;base64,abcdef';\n\n      muteConsole();\n\n      const { container } = await render(\n        <Document error=\"Error\" file={failingPdf} onLoadError={onLoadError} />,\n      );\n\n      expect.assertions(2);\n\n      await onLoadErrorPromise;\n\n      await waitForAsync();\n\n      const error = container.querySelector('.react-pdf__message');\n\n      expect(error).toBeInTheDocument();\n\n      await expect.element(page.getByText('Error', { exact: true })).toBeInTheDocument();\n\n      restoreConsole();\n    });\n\n    it('passes renderMode prop to its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.file}\n          loading=\"Loading\"\n          onLoadSuccess={onLoadSuccess}\n          renderMode=\"custom\"\n        >\n          <Child />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.rendermode).toBe('custom');\n    });\n\n    it('passes rotate prop to its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document file={pdfFile.file} loading=\"Loading\" onLoadSuccess={onLoadSuccess} rotate={90}>\n          <Child />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.rotate).toBe('90');\n    });\n\n    it('passes scale prop to its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document file={pdfFile.file} loading=\"Loading\" onLoadSuccess={onLoadSuccess} scale={1.5}>\n          <Child />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.scale).toBe('1.5');\n    });\n\n    it('does not overwrite renderMode prop in its children when given renderMode prop to both Document and its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document\n          file={pdfFile.file}\n          loading=\"Loading\"\n          onLoadSuccess={onLoadSuccess}\n          renderMode=\"canvas\"\n        >\n          <Child renderMode=\"custom\" />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.rendermode).toBe('custom');\n    });\n\n    it('does not overwrite rotate prop in its children when given rotate prop to both Document and its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document file={pdfFile.file} loading=\"Loading\" onLoadSuccess={onLoadSuccess} rotate={90}>\n          <Child rotate={180} />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.rotate).toBe('180');\n    });\n\n    it('does not overwrite scale prop in its children when given scale prop to both Document and its children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document file={pdfFile.file} loading=\"Loading\" onLoadSuccess={onLoadSuccess} scale={1.5}>\n          <Child scale={2} />\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByTestId('child').element();\n\n      expect(child.dataset.scale).toBe('2');\n    });\n\n    it('supports function as children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Document file={pdfFile.file} loading=\"Loading\" onLoadSuccess={onLoadSuccess}>\n          {({ pdf }) => <p>{`This PDF has ${pdf.numPages} pages`}</p>}\n        </Document>,\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByText('This PDF has 4 pages');\n\n      expect(child).toBeInTheDocument();\n    });\n  });\n\n  describe('viewer', () => {\n    it('calls onItemClick if defined', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const onItemClick = vi.fn();\n      const instance = createRef<{\n        linkService: React.RefObject<LinkService>;\n        pages: React.RefObject<HTMLDivElement[]>;\n        viewer: React.RefObject<{ scrollPageIntoView: (args: ScrollPageIntoViewArgs) => void }>;\n      }>();\n\n      await render(\n        <Document\n          file={pdfFile.file}\n          onItemClick={onItemClick}\n          onLoadSuccess={onLoadSuccess}\n          ref={instance}\n        />,\n      );\n\n      if (!instance.current) {\n        throw new Error('Document ref is not set');\n      }\n\n      if (!instance.current.viewer.current) {\n        throw new Error('Viewer ref is not set');\n      }\n\n      expect.assertions(2);\n\n      await onLoadSuccessPromise;\n\n      const dest: number[] = [];\n      const pageIndex = 5;\n      const pageNumber = 6;\n\n      // Simulate clicking on an outline item\n      instance.current.viewer.current.scrollPageIntoView({ dest, pageIndex, pageNumber });\n\n      expect(onItemClick).toHaveBeenCalledTimes(1);\n      expect(onItemClick).toHaveBeenCalledWith({ dest, pageIndex, pageNumber });\n    });\n\n    it('attempts to find a page and scroll it into view if onItemClick is not given', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const instance = createRef<{\n        linkService: React.RefObject<LinkService>;\n        // biome-ignore lint/suspicious/noExplicitAny: Intentional use to simplify the test\n        pages: React.RefObject<any[]>;\n        viewer: React.RefObject<{ scrollPageIntoView: (args: ScrollPageIntoViewArgs) => void }>;\n      }>();\n\n      await render(<Document file={pdfFile.file} onLoadSuccess={onLoadSuccess} ref={instance} />);\n\n      if (!instance.current) {\n        throw new Error('Document ref is not set');\n      }\n\n      if (!instance.current.pages.current) {\n        throw new Error('Pages ref is not set');\n      }\n\n      if (!instance.current.viewer.current) {\n        throw new Error('Viewer ref is not set');\n      }\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const scrollIntoView = vi.fn();\n\n      const dest: number[] = [];\n      const pageIndex = 5;\n      const pageNumber = 6;\n\n      // Register fake page in Document viewer\n      instance.current.pages.current[pageIndex] = { scrollIntoView };\n\n      // Simulate clicking on an outline item\n      instance.current.viewer.current.scrollPageIntoView({ dest, pageIndex, pageNumber });\n\n      expect(scrollIntoView).toHaveBeenCalledTimes(1);\n    });\n  });\n\n  describe('linkService', () => {\n    it.each`\n      externalLinkTarget | target\n      ${null}            | ${''}\n      ${'_self'}         | ${'_self'}\n      ${'_blank'}        | ${'_blank'}\n      ${'_parent'}       | ${'_parent'}\n      ${'_top'}          | ${'_top'}\n    `(\n      'returns externalLinkTarget = $target given externalLinkTarget prop = $externalLinkTarget',\n      async ({ externalLinkTarget, target }) => {\n        const {\n          func: onRenderAnnotationLayerSuccess,\n          promise: onRenderAnnotationLayerSuccessPromise,\n        } = makeAsyncCallback();\n\n        const { container } = await render(\n          <Document externalLinkTarget={externalLinkTarget} file={pdfFile.file}>\n            <Page\n              onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}\n              renderMode=\"none\"\n              pageNumber={1}\n            />\n          </Document>,\n        );\n\n        expect.assertions(1);\n\n        await onRenderAnnotationLayerSuccessPromise;\n\n        const link = container.querySelector('a') as HTMLAnchorElement;\n\n        expect(link.target).toBe(target);\n      },\n    );\n\n    it.each`\n      externalLinkRel | rel\n      ${null}         | ${'noopener noreferrer nofollow'}\n      ${'noopener'}   | ${'noopener'}\n      ${'noreferrer'} | ${'noreferrer'}\n      ${'nofollow'}   | ${'nofollow'}\n    `(\n      'returns externalLinkRel = $rel given externalLinkRel prop = $externalLinkRel',\n      async ({ externalLinkRel, rel }) => {\n        const {\n          func: onRenderAnnotationLayerSuccess,\n          promise: onRenderAnnotationLayerSuccessPromise,\n        } = makeAsyncCallback();\n\n        const { container } = await render(\n          <Document externalLinkRel={externalLinkRel} file={pdfFile.file}>\n            <Page\n              onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}\n              renderMode=\"none\"\n              pageNumber={1}\n            />\n          </Document>,\n        );\n\n        expect.assertions(1);\n\n        await onRenderAnnotationLayerSuccessPromise;\n\n        const link = container.querySelector('a') as HTMLAnchorElement;\n\n        expect(link.rel).toBe(rel);\n      },\n    );\n  });\n\n  it('calls onClick callback when clicked a document (sample of mouse events family)', async () => {\n    const onClick = vi.fn();\n\n    const { container } = await render(<Document onClick={onClick} />);\n\n    const document = container.querySelector('.react-pdf__Document') as HTMLDivElement;\n    await userEvent.click(document);\n\n    expect(onClick).toHaveBeenCalled();\n  });\n\n  function triggerTouchStart(element: HTMLElement) {\n    element.dispatchEvent(new TouchEvent('touchstart', { bubbles: true, cancelable: true }));\n  }\n\n  it('calls onTouchStart callback when touched a document (sample of touch events family)', async () => {\n    const onTouchStart = vi.fn();\n\n    const { container } = await render(<Document onTouchStart={onTouchStart} />);\n\n    const document = container.querySelector('.react-pdf__Document') as HTMLDivElement;\n    triggerTouchStart(document);\n\n    expect(onTouchStart).toHaveBeenCalled();\n  });\n\n  it('does not warn if file prop was memoized', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const file = { url: pdfFile.dataURI };\n\n    const { rerender } = await render(<Document file={file} />);\n\n    await rerender(<Document file={file} />);\n\n    expect(spy).not.toHaveBeenCalled();\n\n    vi.mocked(globalThis.console.error).mockReset();\n  });\n\n  it('warns if file prop was not memoized', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const { rerender } = await render(<Document file={{ url: pdfFile.dataURI }} />);\n\n    await rerender(<Document file={{ url: pdfFile.dataURI }} />);\n\n    expect(spy).toHaveBeenCalledTimes(1);\n\n    vi.mocked(globalThis.console.error).mockReset();\n  });\n\n  it('does not warn if file prop was not memoized, but was changed', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const { rerender } = await render(<Document file={{ url: pdfFile.dataURI }} />);\n\n    await rerender(<Document file={{ url: pdfFile2.dataURI }} />);\n\n    expect(spy).not.toHaveBeenCalled();\n\n    vi.mocked(globalThis.console.error).mockRestore();\n  });\n\n  it('does not warn if options prop was memoized', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const options = {};\n\n    const { rerender } = await render(<Document file={pdfFile.blob} options={options} />);\n\n    await rerender(<Document file={pdfFile.blob} options={options} />);\n\n    expect(spy).not.toHaveBeenCalled();\n\n    vi.mocked(globalThis.console.error).mockRestore();\n  });\n\n  it('warns if options prop was not memoized', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const { rerender } = await render(<Document file={pdfFile.blob} options={{}} />);\n\n    await rerender(<Document file={pdfFile.blob} options={{}} />);\n\n    expect(spy).toHaveBeenCalledTimes(1);\n\n    vi.mocked(globalThis.console.error).mockRestore();\n  });\n\n  it('does not warn if options prop was not memoized, but was changed', async () => {\n    const spy = vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n      // Intentionally empty\n    });\n\n    const { rerender } = await render(<Document file={pdfFile.blob} options={{}} />);\n\n    await rerender(<Document file={pdfFile.blob} options={{ maxImageSize: 100 }} />);\n\n    expect(spy).not.toHaveBeenCalled();\n\n    vi.mocked(globalThis.console.error).mockRestore();\n  });\n\n  it('does not throw an error on unmount', async () => {\n    const { func: onLoadProgress, promise: onLoadProgressPromise } = makeAsyncCallback();\n\n    const { unmount } = await render(<Document file={pdfFile} onLoadProgress={onLoadProgress} />);\n\n    await onLoadProgressPromise;\n\n    expect(unmount).not.toThrowError();\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Document.tsx",
    "content": "'use client';\n\nimport { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';\nimport clsx from 'clsx';\nimport { dequal } from 'dequal';\nimport makeCancellable from 'make-cancellable-promise';\nimport makeEventProps from 'make-event-props';\nimport * as pdfjs from 'pdfjs-dist';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport DocumentContext from './DocumentContext.js';\nimport LinkService from './LinkService.js';\nimport Message from './Message.js';\nimport PasswordResponses from './PasswordResponses.js';\n\nimport useResolver from './shared/hooks/useResolver.js';\n\nimport {\n  cancelRunningTask,\n  dataURItoByteString,\n  displayCORSWarning,\n  isArrayBuffer,\n  isBlob,\n  isBrowser,\n  isDataURI,\n  loadFromFile,\n} from './shared/utils.js';\n\nimport type { EventProps } from 'make-event-props';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { DocumentInitParameters } from 'pdfjs-dist/types/src/display/api.js';\nimport type {\n  ClassName,\n  DocumentCallback,\n  DocumentContextType,\n  DocumentRenderProps,\n  ExternalLinkRel,\n  ExternalLinkTarget,\n  File,\n  ImageResourcesPath,\n  NodeOrRenderer,\n  OnDocumentLoadError,\n  OnDocumentLoadProgress,\n  OnDocumentLoadSuccess,\n  OnError,\n  OnItemClickArgs,\n  OnPasswordCallback,\n  Options,\n  PasswordResponse,\n  RenderMode,\n  ScrollPageIntoViewArgs,\n  Source,\n} from './shared/types.js';\n\nconst { PDFDataRangeTransport } = pdfjs;\n\ntype OnItemClick = (args: OnItemClickArgs) => void;\n\ntype OnPassword = (callback: OnPasswordCallback, reason: PasswordResponse) => void;\n\ntype OnSourceError = OnError;\n\ntype OnSourceSuccess = () => void;\n\nexport type DocumentProps = {\n  children?: React.ReactNode | ((props: DocumentRenderProps) => React.ReactNode);\n  /**\n   * Class name(s) that will be added to rendered element along with the default `react-pdf__Document`.\n   *\n   * @example 'custom-class-name-1 custom-class-name-2'\n   * @example ['custom-class-name-1', 'custom-class-name-2']\n   */\n  className?: ClassName;\n  /**\n   * What the component should display in case of an error.\n   *\n   * @default 'Failed to load PDF file.'\n   * @example 'An error occurred!'\n   * @example <p>An error occurred!</p>\n   * @example {this.renderError}\n   */\n  error?: NodeOrRenderer;\n  /**\n   * Link rel for links rendered in annotations.\n   *\n   * @default 'noopener noreferrer nofollow'\n   */\n  externalLinkRel?: ExternalLinkRel;\n  /**\n   * Link target for external links rendered in annotations.\n   */\n  externalLinkTarget?: ExternalLinkTarget;\n  /**\n   * What PDF should be displayed.\n   *\n   * Its value can be an URL, a file (imported using `import … from …` or from file input form element), or an object with parameters (`url` - URL; `data` - data, preferably Uint8Array; `range` - PDFDataRangeTransport.\n   *\n   * **Warning**: Since equality check (`===`) is used to determine if `file` object has changed, it must be memoized by setting it in component's state, `useMemo` or other similar technique.\n   *\n   * @example 'https://example.com/sample.pdf'\n   * @example importedPdf\n   * @example { url: 'https://example.com/sample.pdf' }\n   */\n  file?: File;\n  /**\n   * The path used to prefix the src attributes of annotation SVGs.\n   *\n   * @default ''\n   * @example '/public/images/'\n   */\n  imageResourcesPath?: ImageResourcesPath;\n  /**\n   * A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Document>` component.\n   *\n   * @example (ref) => { this.myDocument = ref; }\n   * @example this.ref\n   * @example ref\n   */\n  inputRef?: React.Ref<HTMLDivElement | null>;\n  /**\n   * What the component should display while loading.\n   *\n   * @default 'Loading PDF…'\n   * @example 'Please wait!'\n   * @example <p>Please wait!</p>\n   * @example {this.renderLoader}\n   */\n  loading?: NodeOrRenderer;\n  /**\n   * What the component should display in case of no data.\n   *\n   * @default 'No PDF file specified.'\n   * @example 'Please select a file.'\n   * @example <p>Please select a file.</p>\n   * @example {this.renderNoData}\n   */\n  noData?: NodeOrRenderer;\n  /**\n   * Function called when an outline item or a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to.\n   *\n   * @example ({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')\n   */\n  onItemClick?: OnItemClick;\n  /**\n   * Function called in case of an error while loading a document.\n   *\n   * @example (error) => alert('Error while loading document! ' + error.message)\n   */\n  onLoadError?: OnDocumentLoadError;\n  /**\n   * Function called, potentially multiple times, as the loading progresses.\n   *\n   * @example ({ loaded, total }) => alert('Loading a document: ' + (loaded / total) * 100 + '%')\n   */\n  onLoadProgress?: OnDocumentLoadProgress;\n  /**\n   * Function called when the document is successfully loaded.\n   *\n   * @example (pdf) => alert('Loaded a file with ' + pdf.numPages + ' pages!')\n   */\n  onLoadSuccess?: OnDocumentLoadSuccess;\n  /**\n   * Function called when a password-protected PDF is loaded.\n   *\n   * @example (callback) => callback('s3cr3t_p4ssw0rd')\n   */\n  onPassword?: OnPassword;\n  /**\n   * Function called in case of an error while retrieving document source from `file` prop.\n   *\n   * @example (error) => alert('Error while retrieving document source! ' + error.message)\n   */\n  onSourceError?: OnSourceError;\n  /**\n   * Function called when document source is successfully retrieved from `file` prop.\n   *\n   * @example () => alert('Document source retrieved!')\n   */\n  onSourceSuccess?: OnSourceSuccess;\n  /**\n   * An object in which additional parameters to be passed to PDF.js can be defined. Most notably:\n   * - `cMapUrl`;\n   * - `httpHeaders` - custom request headers, e.g. for authorization);\n   * - `wasmUrl`;\n   * - `withCredentials` - a boolean to indicate whether or not to include cookies in the request (defaults to `false`)\n   *\n   * For a full list of possible parameters, check [PDF.js documentation on DocumentInitParameters](https://mozilla.github.io/pdf.js/api/draft/module-pdfjsLib.html#~DocumentInitParameters).\n   *\n   * **Note**: Make sure to define options object outside of your React component or use `useMemo` if you can't.\n   *\n   * @example { cMapUrl: '/cmaps/', wasmUrl: '/wasm/' }\n   */\n  options?: Options;\n  /**\n   * Rendering mode of the document. Can be `\"canvas\"`, `\"custom\"` or `\"none\"``. If set to `\"custom\"`, `customRenderer` must also be provided.\n   *\n   * @default 'canvas'\n   * @example 'custom'\n   */\n  renderMode?: RenderMode;\n  /**\n   * Rotation of the document in degrees. If provided, will change rotation globally, even for the pages which were given `rotate` prop of their own. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left.\n   *\n   * @example 90\n   */\n  rotate?: number | null;\n  /**\n   * Document scale.\n   *\n   * @default 1\n   * @example 0.5\n   */\n  scale?: number;\n} & EventProps<DocumentCallback | false | undefined>;\n\nconst defaultOnPassword: OnPassword = (callback, reason) => {\n  switch (reason) {\n    case PasswordResponses.NEED_PASSWORD: {\n      const password = prompt('Enter the password to open this PDF file.');\n      callback(password);\n      break;\n    }\n    case PasswordResponses.INCORRECT_PASSWORD: {\n      const password = prompt('Invalid password. Please try again.');\n      callback(password);\n      break;\n    }\n    default:\n  }\n};\n\nfunction isParameterObject(file: File): file is Source {\n  return (\n    typeof file === 'object' &&\n    file !== null &&\n    ('data' in file || 'range' in file || 'url' in file)\n  );\n}\n\n/**\n * Loads a document passed using `file` prop.\n */\nconst Document: React.ForwardRefExoticComponent<\n  DocumentProps &\n    React.RefAttributes<{\n      linkService: React.RefObject<LinkService>;\n      pages: React.RefObject<HTMLDivElement[]>;\n      viewer: React.RefObject<{ scrollPageIntoView: (args: ScrollPageIntoViewArgs) => void }>;\n    }>\n> = forwardRef(function Document(\n  {\n    children,\n    className,\n    error = 'Failed to load PDF file.',\n    externalLinkRel,\n    externalLinkTarget,\n    file,\n    inputRef,\n    imageResourcesPath,\n    loading = 'Loading PDF…',\n    noData = 'No PDF file specified.',\n    onItemClick,\n    onLoadError: onLoadErrorProps,\n    onLoadProgress,\n    onLoadSuccess: onLoadSuccessProps,\n    onPassword = defaultOnPassword,\n    onSourceError: onSourceErrorProps,\n    onSourceSuccess: onSourceSuccessProps,\n    options,\n    renderMode,\n    rotate,\n    scale,\n    ...otherProps\n  },\n  ref,\n) {\n  const [sourceState, sourceDispatch] = useResolver<Source | null>();\n  const { value: source, error: sourceError } = sourceState;\n  const [pdfState, pdfDispatch] = useResolver<PDFDocumentProxy>();\n  const { value: pdf, error: pdfError } = pdfState;\n\n  const linkService = useRef(new LinkService());\n\n  const pages = useRef<HTMLDivElement[]>([]);\n\n  const prevFile = useRef<File | undefined>(undefined);\n  const prevOptions = useRef<Options | undefined>(undefined);\n\n  if (file && file !== prevFile.current && isParameterObject(file)) {\n    warning(\n      !dequal(file, prevFile.current),\n      `File prop passed to <Document /> changed, but it's equal to previous one. This might result in unnecessary reloads. Consider memoizing the value passed to \"file\" prop.`,\n    );\n\n    prevFile.current = file;\n  }\n\n  // Detect non-memoized changes in options prop\n  if (options && options !== prevOptions.current) {\n    warning(\n      !dequal(options, prevOptions.current),\n      `Options prop passed to <Document /> changed, but it's equal to previous one. This might result in unnecessary reloads. Consider memoizing the value passed to \"options\" prop.`,\n    );\n\n    prevOptions.current = options;\n  }\n\n  const viewer = useRef({\n    // Handling jumping to internal links target\n    scrollPageIntoView: (args: ScrollPageIntoViewArgs) => {\n      const { dest, pageNumber, pageIndex = pageNumber - 1 } = args;\n\n      // First, check if custom handling of onItemClick was provided\n      if (onItemClick) {\n        onItemClick({ dest, pageIndex, pageNumber });\n        return;\n      }\n\n      // If not, try to look for target page within the <Document>.\n      const page = pages.current[pageIndex];\n\n      if (page) {\n        // Scroll to the page automatically\n        page.scrollIntoView();\n        return;\n      }\n\n      warning(\n        false,\n        `An internal link leading to page ${pageNumber} was clicked, but neither <Document> was provided with onItemClick nor it was able to find the page within itself. Either provide onItemClick to <Document> and handle navigating by yourself or ensure that all pages are rendered within <Document>.`,\n      );\n    },\n  });\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      linkService,\n      pages,\n      viewer,\n    }),\n    [],\n  );\n\n  /**\n   * Called when a document source is resolved correctly\n   */\n  function onSourceSuccess() {\n    if (onSourceSuccessProps) {\n      onSourceSuccessProps();\n    }\n  }\n\n  /**\n   * Called when a document source failed to be resolved correctly\n   */\n  function onSourceError() {\n    if (!sourceError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, sourceError.toString());\n\n    if (onSourceErrorProps) {\n      onSourceErrorProps(sourceError);\n    }\n  }\n\n  function resetSource() {\n    sourceDispatch({ type: 'RESET' });\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: See https://github.com/biomejs/biome/issues/3080\n  useEffect(resetSource, [file, sourceDispatch]);\n\n  const findDocumentSource = useCallback(async (): Promise<Source | null> => {\n    if (!file) {\n      return null;\n    }\n\n    // File is a string\n    if (typeof file === 'string') {\n      if (isDataURI(file)) {\n        const fileByteString = dataURItoByteString(file);\n        return { data: fileByteString };\n      }\n\n      displayCORSWarning();\n      return { url: file };\n    }\n\n    // File is PDFDataRangeTransport\n    if (file instanceof PDFDataRangeTransport) {\n      return { range: file };\n    }\n\n    // File is an ArrayBuffer\n    if (isArrayBuffer(file)) {\n      return { data: file };\n    }\n\n    /**\n     * The cases below are browser-only.\n     * If you're running on a non-browser environment, these cases will be of no use.\n     */\n    if (isBrowser) {\n      // File is a Blob\n      if (isBlob(file)) {\n        const data = await loadFromFile(file);\n\n        return { data };\n      }\n    }\n\n    // At this point, file must be an object\n    invariant(\n      typeof file === 'object',\n      'Invalid parameter in file, need either Uint8Array, string or a parameter object',\n    );\n\n    invariant(\n      isParameterObject(file),\n      'Invalid parameter object: need either .data, .range or .url',\n    );\n\n    // File .url is a string\n    if ('url' in file && typeof file.url === 'string') {\n      if (isDataURI(file.url)) {\n        const { url, ...otherParams } = file;\n        const fileByteString = dataURItoByteString(url);\n        return { data: fileByteString, ...otherParams };\n      }\n\n      displayCORSWarning();\n    }\n\n    return file;\n  }, [file]);\n\n  useEffect(() => {\n    const cancellable = makeCancellable(findDocumentSource());\n\n    cancellable.promise\n      .then((nextSource) => {\n        sourceDispatch({ type: 'RESOLVE', value: nextSource });\n      })\n      .catch((error) => {\n        sourceDispatch({ type: 'REJECT', error });\n      });\n\n    return () => {\n      cancelRunningTask(cancellable);\n    };\n  }, [findDocumentSource, sourceDispatch]);\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (typeof source === 'undefined') {\n      return;\n    }\n\n    if (source === false) {\n      onSourceError();\n      return;\n    }\n\n    onSourceSuccess();\n  }, [source]);\n\n  /**\n   * Called when a document is read successfully\n   */\n  function onLoadSuccess() {\n    if (!pdf) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    if (onLoadSuccessProps) {\n      onLoadSuccessProps(pdf);\n    }\n\n    pages.current = new Array(pdf.numPages);\n    linkService.current.setDocument(pdf);\n  }\n\n  /**\n   * Called when a document failed to read successfully\n   */\n  function onLoadError() {\n    if (!pdfError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, pdfError.toString());\n\n    if (onLoadErrorProps) {\n      onLoadErrorProps(pdfError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on source change\n  useEffect(\n    function resetDocument() {\n      pdfDispatch({ type: 'RESET' });\n    },\n    [pdfDispatch, source],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(\n    function loadDocument() {\n      if (!source) {\n        return;\n      }\n\n      const documentInitParams: DocumentInitParameters = options\n        ? { ...source, ...options }\n        : source;\n\n      const destroyable = pdfjs.getDocument(documentInitParams);\n      if (onLoadProgress) {\n        destroyable.onProgress = onLoadProgress;\n      }\n      if (onPassword) {\n        destroyable.onPassword = onPassword;\n      }\n      const loadingTask = destroyable;\n\n      loadingTask.promise\n        .then((nextPdf) => {\n          if (loadingTask.destroyed) {\n            return;\n          }\n\n          pdfDispatch({ type: 'RESOLVE', value: nextPdf });\n        })\n        .catch((error) => {\n          if (loadingTask.destroyed) {\n            return;\n          }\n\n          pdfDispatch({ type: 'REJECT', error });\n        });\n\n      return () => {\n        loadingTask.destroy();\n      };\n    },\n    [options, pdfDispatch, source],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (typeof pdf === 'undefined') {\n      return;\n    }\n\n    if (pdf === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [pdf]);\n\n  useEffect(\n    function setupLinkService() {\n      linkService.current.setViewer(viewer.current);\n      linkService.current.setExternalLinkRel(externalLinkRel);\n      linkService.current.setExternalLinkTarget(externalLinkTarget);\n    },\n    [externalLinkRel, externalLinkTarget],\n  );\n\n  const registerPage = useCallback((pageIndex: number, ref: HTMLDivElement) => {\n    pages.current[pageIndex] = ref;\n  }, []);\n\n  const unregisterPage = useCallback((pageIndex: number) => {\n    delete pages.current[pageIndex];\n  }, []);\n\n  const childContext = useMemo(\n    () => ({\n      imageResourcesPath,\n      linkService: linkService.current,\n      onItemClick,\n      pdf,\n      registerPage,\n      renderMode,\n      rotate,\n      scale,\n      unregisterPage,\n    }),\n    [imageResourcesPath, onItemClick, pdf, registerPage, renderMode, rotate, scale, unregisterPage],\n  );\n\n  const eventProps = useMemo(\n    () => makeEventProps(otherProps, () => pdf),\n    // biome-ignore lint/correctness/useExhaustiveDependencies: FIXME\n    [otherProps, pdf],\n  );\n\n  function renderChildren() {\n    function isFulfilledContext(context: DocumentContextType): context is DocumentRenderProps {\n      return Boolean(context?.pdf);\n    }\n\n    if (!isFulfilledContext(childContext)) {\n      // Impossible, but TypeScript doesn't know that\n      throw new Error('pdf is undefined');\n    }\n\n    const resolvedChildren = typeof children === 'function' ? children(childContext) : children;\n\n    return (\n      <DocumentContext.Provider value={childContext}>{resolvedChildren}</DocumentContext.Provider>\n    );\n  }\n\n  function renderContent() {\n    if (!file) {\n      return <Message type=\"no-data\">{typeof noData === 'function' ? noData() : noData}</Message>;\n    }\n\n    if (pdf === undefined || pdf === null) {\n      return (\n        <Message type=\"loading\">{typeof loading === 'function' ? loading() : loading}</Message>\n      );\n    }\n\n    if (pdf === false) {\n      return <Message type=\"error\">{typeof error === 'function' ? error() : error}</Message>;\n    }\n\n    return renderChildren();\n  }\n\n  return (\n    <div\n      className={clsx('react-pdf__Document', className)}\n      // Assertion is needed for React 18 compatibility\n      ref={inputRef as React.Ref<HTMLDivElement>}\n      {...eventProps}\n    >\n      {renderContent()}\n    </div>\n  );\n});\n\nexport default Document;\n"
  },
  {
    "path": "packages/react-pdf/src/DocumentContext.tsx",
    "content": "'use client';\n\nimport { createContext } from 'react';\n\nimport type { DocumentContextType } from './shared/types.js';\n\nconst documentContext: React.Context<DocumentContextType> =\n  createContext<DocumentContextType>(null);\n\nexport default documentContext;\n"
  },
  {
    "path": "packages/react-pdf/src/LinkService.ts",
    "content": "/* Copyright 2015 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport invariant from 'tiny-invariant';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { IPDFLinkService } from 'pdfjs-dist/types/web/interfaces.js';\nimport type {\n  Dest,\n  ExternalLinkRel,\n  ExternalLinkTarget,\n  ResolvedDest,\n  ScrollPageIntoViewArgs,\n} from './shared/types.js';\n\nconst DEFAULT_LINK_REL = 'noopener noreferrer nofollow';\n\ntype PDFViewer = {\n  currentPageNumber?: number;\n  scrollPageIntoView: (args: ScrollPageIntoViewArgs) => void;\n};\n\nexport default class LinkService implements IPDFLinkService {\n  externalLinkEnabled: boolean;\n  externalLinkRel?: ExternalLinkRel;\n  externalLinkTarget?: ExternalLinkTarget;\n  isInPresentationMode: boolean;\n  pdfDocument?: PDFDocumentProxy | null;\n  pdfViewer?: PDFViewer | null;\n\n  constructor() {\n    this.externalLinkEnabled = true;\n    this.externalLinkRel = undefined;\n    this.externalLinkTarget = undefined;\n    this.isInPresentationMode = false;\n    this.pdfDocument = undefined;\n    this.pdfViewer = undefined;\n  }\n\n  setDocument(pdfDocument: PDFDocumentProxy): void {\n    this.pdfDocument = pdfDocument;\n  }\n\n  setViewer(pdfViewer: PDFViewer): void {\n    this.pdfViewer = pdfViewer;\n  }\n\n  setExternalLinkRel(externalLinkRel?: ExternalLinkRel): void {\n    this.externalLinkRel = externalLinkRel;\n  }\n\n  setExternalLinkTarget(externalLinkTarget?: ExternalLinkTarget): void {\n    this.externalLinkTarget = externalLinkTarget;\n  }\n\n  setHash(): void {\n    // Intentionally empty\n  }\n\n  setHistory(): void {\n    // Intentionally empty\n  }\n\n  get pagesCount(): number {\n    return this.pdfDocument ? this.pdfDocument.numPages : 0;\n  }\n\n  get page(): number {\n    invariant(this.pdfViewer, 'PDF viewer is not initialized.');\n\n    return this.pdfViewer.currentPageNumber || 0;\n  }\n\n  set page(value: number) {\n    invariant(this.pdfViewer, 'PDF viewer is not initialized.');\n\n    this.pdfViewer.currentPageNumber = value;\n  }\n\n  get rotation(): number {\n    return 0;\n  }\n\n  set rotation(_value) {\n    // Intentionally empty\n  }\n\n  addLinkAttributes(link: HTMLAnchorElement, url: string, newWindow: boolean): void {\n    link.href = url;\n    link.rel = this.externalLinkRel || DEFAULT_LINK_REL;\n    link.target = newWindow ? '_blank' : this.externalLinkTarget || '';\n  }\n\n  goToDestination(dest: Dest): Promise<void> {\n    return new Promise<ResolvedDest | null>((resolve) => {\n      invariant(this.pdfDocument, 'PDF document not loaded.');\n\n      invariant(dest, 'Destination is not specified.');\n\n      if (typeof dest === 'string') {\n        this.pdfDocument.getDestination(dest).then(resolve);\n      } else if (Array.isArray(dest)) {\n        resolve(dest);\n      } else {\n        dest.then(resolve);\n      }\n    }).then((explicitDest) => {\n      invariant(Array.isArray(explicitDest), `\"${explicitDest}\" is not a valid destination array.`);\n\n      const destRef = explicitDest[0];\n\n      new Promise<number>((resolve) => {\n        invariant(this.pdfDocument, 'PDF document not loaded.');\n\n        if (destRef instanceof Object) {\n          this.pdfDocument\n            .getPageIndex(destRef)\n            .then((pageIndex) => {\n              resolve(pageIndex);\n            })\n            .catch(() => {\n              invariant(false, `\"${destRef}\" is not a valid page reference.`);\n            });\n        } else if (typeof destRef === 'number') {\n          resolve(destRef);\n        } else {\n          invariant(false, `\"${destRef}\" is not a valid destination reference.`);\n        }\n      }).then((pageIndex) => {\n        const pageNumber = pageIndex + 1;\n\n        invariant(this.pdfViewer, 'PDF viewer is not initialized.');\n\n        invariant(\n          pageNumber >= 1 && pageNumber <= this.pagesCount,\n          `\"${pageNumber}\" is not a valid page number.`,\n        );\n\n        this.pdfViewer.scrollPageIntoView({\n          dest: explicitDest,\n          pageIndex,\n          pageNumber,\n        });\n      });\n    });\n  }\n\n  goToPage(pageNumber: number): void {\n    const pageIndex = pageNumber - 1;\n\n    invariant(this.pdfViewer, 'PDF viewer is not initialized.');\n\n    invariant(\n      pageNumber >= 1 && pageNumber <= this.pagesCount,\n      `\"${pageNumber}\" is not a valid page number.`,\n    );\n\n    this.pdfViewer.scrollPageIntoView({\n      pageIndex,\n      pageNumber,\n    });\n  }\n\n  goToXY(): void {\n    // Intentionally empty\n  }\n\n  cachePageRef(): void {\n    // Intentionally empty\n  }\n\n  getDestinationHash(): string {\n    return '#';\n  }\n\n  getAnchorUrl(): string {\n    return '#';\n  }\n\n  executeNamedAction(): void {\n    // Intentionally empty\n  }\n\n  executeSetOCGState(): void {\n    // Intentionally empty\n  }\n\n  isPageVisible(): boolean {\n    return true;\n  }\n\n  isPageCached(): boolean {\n    return true;\n  }\n\n  navigateTo(dest: Dest): void {\n    this.goToDestination(dest);\n  }\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Message.tsx",
    "content": "type MessageProps = {\n  children?: React.ReactNode;\n  type: 'error' | 'loading' | 'no-data';\n};\n\nexport default function Message({ children, type }: MessageProps): React.ReactElement {\n  return <div className={`react-pdf__message react-pdf__message--${type}`}>{children}</div>;\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Outline.spec.tsx",
    "content": "import { beforeAll, describe, expect, it } from 'vitest';\nimport { page } from 'vitest/browser';\nimport { render } from 'vitest-browser-react';\nimport { createRef } from 'react';\n\nimport DocumentContext from './DocumentContext.js';\nimport { pdfjs } from './index.test.js';\nimport Outline from './Outline.js';\n\nimport failingPdf from '../../../__mocks__/_failing_pdf.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { DocumentContextType } from './shared/types.js';\n\ntype PDFOutline = Awaited<ReturnType<PDFDocumentProxy['getOutline']>>;\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst pdfFile2 = await loadPDF('../../__mocks__/_pdf2.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<DocumentContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <DocumentContext.Provider value={context as DocumentContextType}>\n      {children}\n    </DocumentContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<DocumentContextType> = context,\n    ) =>\n      await rerender(\n        <DocumentContext.Provider value={nextContext as DocumentContextType}>\n          {nextChildren}\n        </DocumentContext.Provider>,\n      ),\n  };\n}\n\ndescribe('Outline', () => {\n  // Loaded PDF file\n  let pdf: PDFDocumentProxy;\n  let pdf2: PDFDocumentProxy;\n\n  // Object with basic loaded outline information that shall match after successful loading\n  let desiredLoadedOutline: PDFOutline;\n  let desiredLoadedOutline2: PDFOutline;\n\n  beforeAll(async () => {\n    pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n    pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;\n\n    desiredLoadedOutline = await pdf.getOutline();\n    desiredLoadedOutline2 = await pdf2.getOutline();\n  });\n\n  describe('loading', () => {\n    it('loads an outline and calls onLoadSuccess callback properly when placed inside Document', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Outline onLoadSuccess={onLoadSuccess} />, { pdf });\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedOutline]);\n    });\n\n    it('loads an outline and calls onLoadSuccess callback properly when pdf prop is passed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(<Outline onLoadSuccess={onLoadSuccess} pdf={pdf} />);\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedOutline]);\n    });\n\n    it('calls onLoadError when failed to load an outline', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<Outline onLoadError={onLoadError} />, { pdf: failingPdf });\n\n      expect.assertions(1);\n\n      await expect(onLoadErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('replaces an outline properly when pdf is changed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(<Outline onLoadSuccess={onLoadSuccess} />, {\n        pdf,\n      });\n\n      expect.assertions(2);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedOutline]);\n\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<Outline onLoadSuccess={onLoadSuccess2} />, { pdf: pdf2 });\n\n      // It would have been .toMatchObject if not for the fact _pdf2.pdf has no outline\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedOutline2]);\n    });\n\n    it('throws an error when placed outside Document without pdf prop passed', async () => {\n      muteConsole();\n\n      await expect(render(<Outline />)).rejects.toThrowError(\n        'Invariant failed: Attempted to load an outline, but no document was specified. Wrap <Outline /> in a <Document /> or pass explicit `pdf` prop.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('applies className to its wrapper when given a string', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const className = 'testClassName';\n\n      const { container } = await renderWithContext(\n        <Outline className={className} onLoadSuccess={onLoadSuccess} />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const wrapper = container.querySelector('.react-pdf__Outline');\n\n      expect(wrapper).toHaveClass(className);\n    });\n\n    it('passes container element to inputRef properly', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const inputRef = createRef<HTMLDivElement>();\n\n      await renderWithContext(<Outline inputRef={inputRef} onLoadSuccess={onLoadSuccess} />, {\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      expect(inputRef.current).toBeInstanceOf(HTMLDivElement);\n    });\n\n    it('renders OutlineItem components properly', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Outline onLoadSuccess={onLoadSuccess} />, { pdf });\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const items = page.getByRole('listitem');\n\n      expect(items).toHaveLength(5);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Outline.tsx",
    "content": "'use client';\n\nimport { useEffect, useMemo } from 'react';\nimport clsx from 'clsx';\nimport makeCancellable from 'make-cancellable-promise';\nimport makeEventProps from 'make-event-props';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport OutlineContext from './OutlineContext.js';\nimport OutlineItem from './OutlineItem.js';\n\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\nimport useResolver from './shared/hooks/useResolver.js';\n\nimport { cancelRunningTask } from './shared/utils.js';\n\nimport type { EventProps } from 'make-event-props';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { ClassName, OnItemClickArgs } from './shared/types.js';\n\ntype PDFOutline = Awaited<ReturnType<PDFDocumentProxy['getOutline']>>;\n\nexport type OutlineProps = {\n  /**\n   * Class name(s) that will be added to rendered element along with the default `react-pdf__Outline`.\n   *\n   * @example 'custom-class-name-1 custom-class-name-2'\n   * @example ['custom-class-name-1', 'custom-class-name-2']\n   */\n  className?: ClassName;\n  /**\n   * A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Outline>` component.\n   *\n   * @example (ref) => { this.myOutline = ref; }\n   * @example this.ref\n   * @example ref\n   */\n  inputRef?: React.Ref<HTMLDivElement>;\n  /**\n   * Function called when an outline item has been clicked. Usually, you would like to use this callback to move the user wherever they requested to.\n   *\n   * @example ({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')\n   */\n  onItemClick?: (props: OnItemClickArgs) => void;\n  /**\n   * Function called in case of an error while retrieving the outline.\n   *\n   * @example (error) => alert('Error while retrieving the outline! ' + error.message)\n   */\n  onLoadError?: (error: Error) => void;\n  /**\n   * Function called when the outline is successfully retrieved.\n   *\n   * @example (outline) => alert('The outline has been successfully retrieved.')\n   */\n  onLoadSuccess?: (outline: PDFOutline | null) => void;\n  pdf?: PDFDocumentProxy | false;\n} & EventProps<PDFOutline | null | false | undefined>;\n\n/**\n * Displays an outline (table of contents).\n *\n * Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function.\n */\nexport default function Outline(props: OutlineProps): React.ReactElement | null {\n  const documentContext = useDocumentContext();\n\n  const mergedProps = { ...documentContext, ...props };\n  const {\n    className,\n    inputRef,\n    onItemClick,\n    onLoadError: onLoadErrorProps,\n    onLoadSuccess: onLoadSuccessProps,\n    pdf,\n    ...otherProps\n  } = mergedProps;\n\n  invariant(\n    pdf,\n    'Attempted to load an outline, but no document was specified. Wrap <Outline /> in a <Document /> or pass explicit `pdf` prop.',\n  );\n\n  const [outlineState, outlineDispatch] = useResolver<PDFOutline | null>();\n  const { value: outline, error: outlineError } = outlineState;\n\n  /**\n   * Called when an outline is read successfully\n   */\n  function onLoadSuccess() {\n    if (typeof outline === 'undefined' || outline === false) {\n      return;\n    }\n\n    if (onLoadSuccessProps) {\n      onLoadSuccessProps(outline);\n    }\n  }\n\n  /**\n   * Called when an outline failed to read successfully\n   */\n  function onLoadError() {\n    if (!outlineError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, outlineError.toString());\n\n    if (onLoadErrorProps) {\n      onLoadErrorProps(outlineError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on pdf change\n  useEffect(\n    function resetOutline() {\n      outlineDispatch({ type: 'RESET' });\n    },\n    [outlineDispatch, pdf],\n  );\n\n  useEffect(\n    function loadOutline() {\n      const cancellable = makeCancellable(pdf.getOutline());\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then((nextOutline) => {\n          outlineDispatch({ type: 'RESOLVE', value: nextOutline });\n        })\n        .catch((error) => {\n          outlineDispatch({ type: 'REJECT', error });\n        });\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [outlineDispatch, pdf],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (outline === undefined) {\n      return;\n    }\n\n    if (outline === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [outline]);\n\n  const childContext = useMemo(\n    () => ({\n      onItemClick,\n    }),\n    [onItemClick],\n  );\n\n  const eventProps = useMemo(\n    () => makeEventProps(otherProps, () => outline),\n    // biome-ignore lint/correctness/useExhaustiveDependencies: FIXME\n    [otherProps, outline],\n  );\n\n  if (!outline) {\n    return null;\n  }\n\n  function renderOutline() {\n    if (!outline) {\n      return null;\n    }\n\n    return (\n      <ul>\n        {outline.map((item, itemIndex) => (\n          <OutlineItem\n            key={typeof item.dest === 'string' ? item.dest : itemIndex}\n            item={item}\n            pdf={pdf}\n          />\n        ))}\n      </ul>\n    );\n  }\n\n  return (\n    <div className={clsx('react-pdf__Outline', className)} ref={inputRef} {...eventProps}>\n      <OutlineContext.Provider value={childContext}>{renderOutline()}</OutlineContext.Provider>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/OutlineContext.tsx",
    "content": "'use client';\n\nimport { createContext } from 'react';\n\nimport type { OutlineContextType } from './shared/types.js';\n\nconst outlineContext: React.Context<OutlineContextType> = createContext<OutlineContextType>(null);\n\nexport default outlineContext;\n"
  },
  {
    "path": "packages/react-pdf/src/OutlineItem.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { page, userEvent } from 'vitest/browser';\nimport { render } from 'vitest-browser-react';\n\nimport DocumentContext from './DocumentContext.js';\nimport { pdfjs } from './index.test.js';\nimport OutlineContext from './OutlineContext.js';\nimport OutlineItem from './OutlineItem.js';\n\nimport { loadPDF, makeAsyncCallback } from '../../../test-utils.js';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { DocumentContextType, OutlineContextType } from './shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\n\ntype PDFOutline = Awaited<ReturnType<PDFDocumentProxy['getOutline']>>;\ntype PDFOutlineItem = PDFOutline[number];\n\nasync function renderWithContext(\n  children: React.ReactNode,\n  documentContext: Partial<DocumentContextType>,\n  outlineContext: Partial<OutlineContextType>,\n) {\n  const { rerender, ...otherResult } = await render(\n    <DocumentContext.Provider value={documentContext as DocumentContextType}>\n      <OutlineContext.Provider value={outlineContext as OutlineContextType}>\n        {children}\n      </OutlineContext.Provider>\n    </DocumentContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextDocumentContext: Partial<DocumentContextType> = documentContext,\n      nextOutlineContext: Partial<OutlineContextType> = outlineContext,\n    ) =>\n      await rerender(\n        <DocumentContext.Provider value={nextDocumentContext as DocumentContextType}>\n          <OutlineContext.Provider value={nextOutlineContext as OutlineContextType}>\n            {nextChildren}\n          </OutlineContext.Provider>\n        </DocumentContext.Provider>,\n      ),\n  };\n}\n\ndescribe('OutlineItem', () => {\n  // Loaded PDF file\n  let pdf: PDFDocumentProxy;\n\n  // Object with basic loaded outline item information\n  let outlineItem: PDFOutlineItem;\n\n  beforeAll(async () => {\n    pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    const outlineItems = await pdf.getOutline();\n    [outlineItem] = outlineItems as [PDFOutlineItem];\n  });\n\n  describe('rendering', () => {\n    it('renders an item properly', async () => {\n      const onItemClick = vi.fn();\n\n      await renderWithContext(<OutlineItem item={outlineItem} />, { pdf }, { onItemClick });\n\n      const item = page.getByRole('listitem').first();\n\n      expect(item).toHaveTextContent(outlineItem.title);\n    });\n\n    it(\"renders item's subitems properly\", async () => {\n      const onItemClick = vi.fn();\n\n      await renderWithContext(<OutlineItem item={outlineItem} />, { pdf }, { onItemClick });\n\n      const item = page.getByRole('listitem').first();\n      const subitems = item.getByRole('listitem');\n\n      expect(subitems).toHaveLength(outlineItem.items.length);\n    });\n\n    it('calls onItemClick with proper arguments when clicked a link', async () => {\n      const { func: onItemClick, promise: onItemClickPromise } = makeAsyncCallback();\n\n      await renderWithContext(<OutlineItem item={outlineItem} />, { pdf }, { onItemClick });\n\n      const item = page.getByRole('listitem').first();\n      const link = item.getByRole('link').first();\n      await userEvent.click(link);\n\n      await onItemClickPromise;\n\n      expect(onItemClick).toHaveBeenCalled();\n    });\n\n    it('calls onItemClick with proper arguments multiple times when clicked a link multiple times', async () => {\n      const { func: onItemClick, promise: onItemClickPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <OutlineItem item={outlineItem} />,\n        { pdf },\n        { onItemClick },\n      );\n\n      const item = page.getByRole('listitem').first();\n      const link = item.getByRole('link').first();\n      await userEvent.click(link);\n\n      await onItemClickPromise;\n\n      expect(onItemClick).toHaveBeenCalledTimes(1);\n\n      const { func: onItemClick2, promise: onItemClickPromise2 } = makeAsyncCallback();\n\n      await rerender(<OutlineItem item={outlineItem} />, { pdf }, { onItemClick: onItemClick2 });\n\n      await userEvent.click(link);\n\n      await onItemClickPromise2;\n\n      expect(onItemClick2).toHaveBeenCalledTimes(1);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/OutlineItem.tsx",
    "content": "import invariant from 'tiny-invariant';\n\nimport Ref from './Ref.js';\n\nimport useCachedValue from './shared/hooks/useCachedValue.js';\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\nimport useOutlineContext from './shared/hooks/useOutlineContext.js';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport type { RefProxy } from 'pdfjs-dist/types/src/display/api.js';\n\ntype PDFOutline = Awaited<ReturnType<PDFDocumentProxy['getOutline']>>;\n\ntype PDFOutlineItem = PDFOutline[number];\n\ntype OutlineItemProps = {\n  item: PDFOutlineItem;\n  pdf?: PDFDocumentProxy | false;\n};\n\nexport default function OutlineItem(props: OutlineItemProps): React.ReactElement {\n  const documentContext = useDocumentContext();\n\n  const outlineContext = useOutlineContext();\n\n  invariant(outlineContext, 'Unable to find Outline context.');\n\n  const mergedProps = { ...documentContext, ...outlineContext, ...props };\n  const { item, linkService, onItemClick, pdf, ...otherProps } = mergedProps;\n\n  invariant(\n    pdf,\n    'Attempted to load an outline, but no document was specified. Wrap <Outline /> in a <Document /> or pass explicit `pdf` prop.',\n  );\n\n  const getDestination = useCachedValue(() => {\n    if (typeof item.dest === 'string') {\n      return pdf.getDestination(item.dest);\n    }\n\n    return item.dest;\n  });\n\n  const getPageIndex = useCachedValue(async () => {\n    const destination = await getDestination();\n\n    if (!destination) {\n      throw new Error('Destination not found.');\n    }\n\n    const [ref] = destination as [RefProxy];\n\n    return pdf.getPageIndex(new Ref(ref));\n  });\n\n  const getPageNumber = useCachedValue(async () => {\n    const pageIndex = await getPageIndex();\n\n    return pageIndex + 1;\n  });\n\n  function onClick(event: React.MouseEvent<HTMLAnchorElement>) {\n    event.preventDefault();\n\n    invariant(\n      onItemClick || linkService,\n      'Either onItemClick callback or linkService must be defined in order to navigate to an outline item.',\n    );\n\n    if (onItemClick) {\n      Promise.all([getDestination(), getPageIndex(), getPageNumber()]).then(\n        ([dest, pageIndex, pageNumber]) => {\n          onItemClick({\n            dest,\n            pageIndex,\n            pageNumber,\n          });\n        },\n      );\n    } else if (linkService) {\n      linkService.goToDestination(item.dest);\n    }\n  }\n\n  function renderSubitems() {\n    if (!item.items || !item.items.length) {\n      return null;\n    }\n\n    const { items: subitems } = item;\n\n    return (\n      <ul>\n        {subitems.map((subitem, subitemIndex) => (\n          <OutlineItem\n            key={typeof subitem.dest === 'string' ? subitem.dest : subitemIndex}\n            item={subitem}\n            pdf={pdf}\n            {...otherProps}\n          />\n        ))}\n      </ul>\n    );\n  }\n\n  return (\n    <li>\n      {/* biome-ignore lint/a11y/useValidAnchor: We can't provide real href here */}\n      <a href=\"#\" onClick={onClick}>\n        {item.title}\n      </a>\n      {renderSubitems()}\n    </li>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page/AnnotationLayer.css",
    "content": "/* Copyright 2014 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n:root {\n  --react-pdf-annotation-layer: 1;\n  --annotation-unfocused-field-background: url(\"data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>\");\n  --input-focus-border-color: Highlight;\n  --input-focus-outline: 1px solid Canvas;\n  --input-unfocused-border-color: transparent;\n  --input-disabled-border-color: transparent;\n  --input-hover-border-color: black;\n  --link-outline: none;\n}\n\n@media screen and (forced-colors: active) {\n  :root {\n    --input-focus-border-color: CanvasText;\n    --input-unfocused-border-color: ActiveText;\n    --input-disabled-border-color: GrayText;\n    --input-hover-border-color: Highlight;\n    --link-outline: 1.5px solid LinkText;\n  }\n  .annotationLayer .textWidgetAnnotation :is(input, textarea):required,\n  .annotationLayer .choiceWidgetAnnotation select:required,\n  .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required {\n    outline: 1.5px solid selectedItem;\n  }\n\n  .annotationLayer .linkAnnotation:hover {\n    backdrop-filter: invert(100%);\n  }\n}\n\n.annotationLayer {\n  position: absolute;\n  top: 0;\n  left: 0;\n  pointer-events: none;\n  transform-origin: 0 0;\n  z-index: 3;\n}\n\n.annotationLayer[data-main-rotation='90'] .norotate {\n  transform: rotate(270deg) translateX(-100%);\n}\n.annotationLayer[data-main-rotation='180'] .norotate {\n  transform: rotate(180deg) translate(-100%, -100%);\n}\n.annotationLayer[data-main-rotation='270'] .norotate {\n  transform: rotate(90deg) translateY(-100%);\n}\n\n.annotationLayer canvas {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n}\n\n.annotationLayer section {\n  position: absolute;\n  text-align: initial;\n  pointer-events: auto;\n  box-sizing: border-box;\n  margin: 0;\n  transform-origin: 0 0;\n}\n\n.annotationLayer .linkAnnotation {\n  outline: var(--link-outline);\n}\n\n.textLayer.selecting ~ .annotationLayer section {\n  pointer-events: none;\n}\n\n.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a {\n  position: absolute;\n  font-size: 1em;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\n.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a:hover {\n  opacity: 0.2;\n  background: rgba(255, 255, 0, 1);\n  box-shadow: 0 2px 10px rgba(255, 255, 0, 1);\n}\n\n.annotationLayer .textAnnotation img {\n  position: absolute;\n  cursor: pointer;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n}\n\n.annotationLayer .textWidgetAnnotation :is(input, textarea),\n.annotationLayer .choiceWidgetAnnotation select,\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input {\n  background-image: var(--annotation-unfocused-field-background);\n  border: 2px solid var(--input-unfocused-border-color);\n  box-sizing: border-box;\n  font: calc(9px * var(--total-scale-factor)) sans-serif;\n  height: 100%;\n  margin: 0;\n  vertical-align: top;\n  width: 100%;\n}\n\n.annotationLayer .textWidgetAnnotation :is(input, textarea):required,\n.annotationLayer .choiceWidgetAnnotation select:required,\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required {\n  outline: 1.5px solid red;\n}\n\n.annotationLayer .choiceWidgetAnnotation select option {\n  padding: 0;\n}\n\n.annotationLayer .buttonWidgetAnnotation.radioButton input {\n  border-radius: 50%;\n}\n\n.annotationLayer .textWidgetAnnotation textarea {\n  resize: none;\n}\n\n.annotationLayer .textWidgetAnnotation :is(input, textarea)[disabled],\n.annotationLayer .choiceWidgetAnnotation select[disabled],\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input[disabled] {\n  background: none;\n  border: 2px solid var(--input-disabled-border-color);\n  cursor: not-allowed;\n}\n\n.annotationLayer .textWidgetAnnotation :is(input, textarea):hover,\n.annotationLayer .choiceWidgetAnnotation select:hover,\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:hover {\n  border: 2px solid var(--input-hover-border-color);\n}\n.annotationLayer .textWidgetAnnotation :is(input, textarea):hover,\n.annotationLayer .choiceWidgetAnnotation select:hover,\n.annotationLayer .buttonWidgetAnnotation.checkBox input:hover {\n  border-radius: 2px;\n}\n\n.annotationLayer .textWidgetAnnotation :is(input, textarea):focus,\n.annotationLayer .choiceWidgetAnnotation select:focus {\n  background: none;\n  border: 2px solid var(--input-focus-border-color);\n  border-radius: 2px;\n  outline: var(--input-focus-outline);\n}\n\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) :focus {\n  background-image: none;\n  background-color: transparent;\n}\n\n.annotationLayer .buttonWidgetAnnotation.checkBox :focus {\n  border: 2px solid var(--input-focus-border-color);\n  border-radius: 2px;\n  outline: var(--input-focus-outline);\n}\n\n.annotationLayer .buttonWidgetAnnotation.radioButton :focus {\n  border: 2px solid var(--input-focus-border-color);\n  outline: var(--input-focus-outline);\n}\n\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after,\n.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before {\n  background-color: CanvasText;\n  content: '';\n  display: block;\n  position: absolute;\n}\n\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after {\n  height: 80%;\n  left: 45%;\n  width: 1px;\n}\n\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before {\n  transform: rotate(45deg);\n}\n\n.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after {\n  transform: rotate(-45deg);\n}\n\n.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before {\n  border-radius: 50%;\n  height: 50%;\n  left: 30%;\n  top: 20%;\n  width: 50%;\n}\n\n.annotationLayer .textWidgetAnnotation input.comb {\n  font-family: monospace;\n  padding-left: 2px;\n  padding-right: 0;\n}\n\n.annotationLayer .textWidgetAnnotation input.comb:focus {\n  /*\n   * Letter spacing is placed on the right side of each character. Hence, the\n   * letter spacing of the last character may be placed outside the visible\n   * area, causing horizontal scrolling. We avoid this by extending the width\n   * when the element has focus and revert this when it loses focus.\n   */\n  width: 103%;\n}\n\n.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input {\n  appearance: none;\n}\n\n.annotationLayer .popupTriggerArea {\n  height: 100%;\n  width: 100%;\n}\n\n.annotationLayer .fileAttachmentAnnotation .popupTriggerArea {\n  position: absolute;\n}\n\n.annotationLayer .popupWrapper {\n  position: absolute;\n  font-size: calc(9px * var(--total-scale-factor));\n  width: 100%;\n  min-width: calc(180px * var(--total-scale-factor));\n  pointer-events: none;\n}\n\n.annotationLayer .popup {\n  position: absolute;\n  max-width: calc(180px * var(--total-scale-factor));\n  background-color: rgba(255, 255, 153, 1);\n  box-shadow: 0 calc(2px * var(--total-scale-factor)) calc(5px * var(--total-scale-factor))\n    rgba(136, 136, 136, 1);\n  border-radius: calc(2px * var(--total-scale-factor));\n  padding: calc(6px * var(--total-scale-factor));\n  margin-left: calc(5px * var(--total-scale-factor));\n  cursor: pointer;\n  font: message-box;\n  white-space: normal;\n  word-wrap: break-word;\n  pointer-events: auto;\n}\n\n.annotationLayer .popup > * {\n  font-size: calc(9px * var(--total-scale-factor));\n}\n\n.annotationLayer .popup h1 {\n  display: inline-block;\n}\n\n.annotationLayer .popupDate {\n  display: inline-block;\n  margin-left: calc(5px * var(--total-scale-factor));\n}\n\n.annotationLayer .popupContent {\n  border-top: 1px solid rgba(51, 51, 51, 1);\n  margin-top: calc(2px * var(--total-scale-factor));\n  padding-top: calc(2px * var(--total-scale-factor));\n}\n\n.annotationLayer .richText > * {\n  white-space: pre-wrap;\n  font-size: calc(9px * var(--total-scale-factor));\n}\n\n.annotationLayer .highlightAnnotation,\n.annotationLayer .underlineAnnotation,\n.annotationLayer .squigglyAnnotation,\n.annotationLayer .strikeoutAnnotation,\n.annotationLayer .freeTextAnnotation,\n.annotationLayer .lineAnnotation svg line,\n.annotationLayer .squareAnnotation svg rect,\n.annotationLayer .circleAnnotation svg ellipse,\n.annotationLayer .polylineAnnotation svg polyline,\n.annotationLayer .polygonAnnotation svg polygon,\n.annotationLayer .caretAnnotation,\n.annotationLayer .inkAnnotation svg polyline,\n.annotationLayer .stampAnnotation,\n.annotationLayer .fileAttachmentAnnotation {\n  cursor: pointer;\n}\n\n.annotationLayer section svg {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n}\n\n.annotationLayer .annotationTextContent {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  opacity: 0;\n  color: transparent;\n  user-select: none;\n  pointer-events: none;\n}\n\n.annotationLayer .annotationTextContent span {\n  width: 100%;\n  display: inline-block;\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page/AnnotationLayer.spec.tsx",
    "content": "import { beforeAll, describe, expect, it } from 'vitest';\nimport { render } from 'vitest-browser-react';\n\nimport DocumentContext from '../DocumentContext.js';\nimport { pdfjs } from '../index.test.js';\nimport LinkService from '../LinkService.js';\nimport PageContext from '../PageContext.js';\nimport AnnotationLayer from './AnnotationLayer.js';\n\nimport failingPage from '../../../../__mocks__/_failing_page.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../../test-utils.js';\n\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\nimport type { RenderResult } from 'vitest-browser-react';\nimport type { Annotations, DocumentContextType, PageContextType } from '../shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst annotatedPdfFile = await loadPDF('../../__mocks__/_pdf3.pdf');\n\nasync function renderWithContext(\n  children: React.ReactNode,\n  documentContext: Partial<DocumentContextType>,\n  pageContext: Partial<PageContextType>,\n) {\n  const { rerender, ...otherResult } = await render(\n    <DocumentContext.Provider value={documentContext as DocumentContextType}>\n      <PageContext.Provider value={pageContext as PageContextType}>{children}</PageContext.Provider>\n    </DocumentContext.Provider>,\n  );\n\n  const customRerender = async (\n    nextChildren: React.ReactNode,\n    nextDocumentContext: Partial<DocumentContextType> = documentContext,\n    nextPageContext: Partial<PageContextType> = pageContext,\n  ) =>\n    await rerender(\n      <DocumentContext.Provider value={nextDocumentContext as DocumentContextType}>\n        <PageContext.Provider value={nextPageContext as PageContextType}>\n          {nextChildren}\n        </PageContext.Provider>\n      </DocumentContext.Provider>,\n    );\n\n  return {\n    ...otherResult,\n    rerender: customRerender,\n  } as RenderResult & { rerender: typeof customRerender };\n}\n\ndescribe('AnnotationLayer', () => {\n  const linkService = new LinkService();\n\n  // Loaded PDF file\n  let pdf: PDFDocumentProxy;\n\n  // Loaded page\n  let page: PDFPageProxy;\n  let page2: PDFPageProxy;\n\n  // Loaded page text items\n  let desiredAnnotations: Annotations;\n  let desiredAnnotations2: Annotations;\n\n  beforeAll(async () => {\n    pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    page = await pdf.getPage(1);\n    desiredAnnotations = await page.getAnnotations();\n\n    page2 = await pdf.getPage(2);\n    desiredAnnotations2 = await page2.getAnnotations();\n  });\n\n  describe('loading', () => {\n    it('loads annotations and calls onGetAnnotationsSuccess callback properly', async () => {\n      const { func: onGetAnnotationsSuccess, promise: onGetAnnotationsSuccessPromise } =\n        makeAsyncCallback();\n\n      await renderWithContext(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onGetAnnotationsSuccess,\n          page,\n        },\n      );\n\n      expect.assertions(1);\n\n      await expect(onGetAnnotationsSuccessPromise).resolves.toMatchObject([desiredAnnotations]);\n    });\n\n    it('calls onGetAnnotationsError when failed to load annotations', async () => {\n      const { func: onGetAnnotationsError, promise: onGetAnnotationsErrorPromise } =\n        makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onGetAnnotationsError,\n          page: failingPage,\n        },\n      );\n\n      expect.assertions(1);\n\n      await expect(onGetAnnotationsErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('replaces annotations properly when page is changed', async () => {\n      const { func: onGetAnnotationsSuccess, promise: onGetAnnotationsSuccessPromise } =\n        makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onGetAnnotationsSuccess,\n          page,\n        },\n      );\n\n      expect.assertions(2);\n\n      await expect(onGetAnnotationsSuccessPromise).resolves.toMatchObject([desiredAnnotations]);\n\n      const { func: onGetAnnotationsSuccess2, promise: onGetAnnotationsSuccessPromise2 } =\n        makeAsyncCallback();\n\n      await rerender(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onGetAnnotationsSuccess: onGetAnnotationsSuccess2,\n          page: page2,\n        },\n      );\n\n      await expect(onGetAnnotationsSuccessPromise2).resolves.toMatchObject([desiredAnnotations2]);\n    });\n\n    it('throws an error when placed outside Page', async () => {\n      muteConsole();\n\n      await expect(render(<AnnotationLayer />)).rejects.toThrowError(\n        'Invariant failed: Unable to find Page context.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('renders annotations properly', async () => {\n      const {\n        func: onRenderAnnotationLayerSuccess,\n        promise: onRenderAnnotationLayerSuccessPromise,\n      } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onRenderAnnotationLayerSuccess,\n          page,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onRenderAnnotationLayerSuccessPromise;\n\n      const wrapper = container.firstElementChild as HTMLDivElement;\n      const annotationItems = Array.from(wrapper.children);\n\n      expect(annotationItems).toHaveLength(desiredAnnotations.length);\n    });\n\n    it.each`\n      externalLinkTarget | target\n      ${null}            | ${''}\n      ${'_self'}         | ${'_self'}\n      ${'_blank'}        | ${'_blank'}\n      ${'_parent'}       | ${'_parent'}\n      ${'_top'}          | ${'_top'}\n    `(\n      'renders all links with target $target given externalLinkTarget = $externalLinkTarget',\n      async ({ externalLinkTarget, target }) => {\n        const {\n          func: onRenderAnnotationLayerSuccess,\n          promise: onRenderAnnotationLayerSuccessPromise,\n        } = makeAsyncCallback();\n        const customLinkService = new LinkService();\n        if (externalLinkTarget) {\n          customLinkService.setExternalLinkTarget(externalLinkTarget);\n        }\n\n        const { container } = await renderWithContext(\n          <AnnotationLayer />,\n          {\n            linkService: customLinkService,\n            pdf,\n          },\n          {\n            onRenderAnnotationLayerSuccess,\n            page,\n          },\n        );\n\n        expect.assertions(desiredAnnotations.length);\n\n        await onRenderAnnotationLayerSuccessPromise;\n\n        const wrapper = container.firstElementChild as HTMLDivElement;\n        const annotationItems = Array.from(wrapper.children);\n        const annotationLinkItems = annotationItems\n          .map((item) => item.firstChild as HTMLElement)\n          .filter((item) => item.tagName === 'A');\n\n        for (const link of annotationLinkItems) {\n          expect(link).toHaveAttribute('target', target);\n        }\n      },\n    );\n\n    it.each`\n      externalLinkRel | rel\n      ${null}         | ${'noopener noreferrer nofollow'}\n      ${'noopener'}   | ${'noopener'}\n    `(\n      'renders all links with rel $rel given externalLinkRel = $externalLinkRel',\n      async ({ externalLinkRel, rel }) => {\n        const {\n          func: onRenderAnnotationLayerSuccess,\n          promise: onRenderAnnotationLayerSuccessPromise,\n        } = makeAsyncCallback();\n        const customLinkService = new LinkService();\n        if (externalLinkRel) {\n          customLinkService.setExternalLinkRel(externalLinkRel);\n        }\n\n        const { container } = await renderWithContext(\n          <AnnotationLayer />,\n          {\n            linkService: customLinkService,\n            pdf,\n          },\n          {\n            onRenderAnnotationLayerSuccess,\n            page,\n          },\n        );\n\n        expect.assertions(desiredAnnotations.length);\n\n        await onRenderAnnotationLayerSuccessPromise;\n\n        const wrapper = container.firstElementChild as HTMLDivElement;\n        const annotationItems = Array.from(wrapper.children);\n        const annotationLinkItems = annotationItems\n          .map((item) => item.firstChild as HTMLElement)\n          .filter((item) => item.tagName === 'A');\n\n        for (const link of annotationLinkItems) {\n          expect(link).toHaveAttribute('rel', rel);\n        }\n      },\n    );\n\n    it('renders annotations with the default imageResourcesPath given no imageResourcesPath', async () => {\n      const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;\n      const annotatedPage = await pdf.getPage(1);\n\n      const {\n        func: onRenderAnnotationLayerSuccess,\n        promise: onRenderAnnotationLayerSuccessPromise,\n      } = makeAsyncCallback();\n      const imageResourcesPath = '';\n      const desiredImageTagRegExp = new RegExp(\n        `<img[^>]+src=\"${imageResourcesPath}annotation-note.svg\"`,\n      );\n\n      const { container } = await renderWithContext(\n        <AnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n        {\n          onRenderAnnotationLayerSuccess,\n          page: annotatedPage,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onRenderAnnotationLayerSuccessPromise;\n\n      const stringifiedAnnotationLayerNode = container.outerHTML;\n\n      expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);\n    });\n\n    it('renders annotations with the specified imageResourcesPath given imageResourcesPath', async () => {\n      const pdf = await pdfjs.getDocument({ data: annotatedPdfFile.arrayBuffer }).promise;\n      const annotatedPage = await pdf.getPage(1);\n\n      const {\n        func: onRenderAnnotationLayerSuccess,\n        promise: onRenderAnnotationLayerSuccessPromise,\n      } = makeAsyncCallback();\n      const imageResourcesPath = '/public/images/';\n      const desiredImageTagRegExp = new RegExp(\n        `<img[^>]+src=\"${imageResourcesPath}annotation-note.svg\"`,\n      );\n\n      const { container } = await renderWithContext(\n        <AnnotationLayer />,\n        {\n          imageResourcesPath,\n          linkService,\n          pdf,\n        },\n        {\n          onRenderAnnotationLayerSuccess,\n          page: annotatedPage,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onRenderAnnotationLayerSuccessPromise;\n\n      const stringifiedAnnotationLayerNode = container.outerHTML;\n\n      expect(stringifiedAnnotationLayerNode).toMatch(desiredImageTagRegExp);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Page/AnnotationLayer.tsx",
    "content": "'use client';\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport clsx from 'clsx';\nimport makeCancellable from 'make-cancellable-promise';\nimport * as pdfjs from 'pdfjs-dist';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport useDocumentContext from '../shared/hooks/useDocumentContext.js';\nimport usePageContext from '../shared/hooks/usePageContext.js';\nimport useResolver from '../shared/hooks/useResolver.js';\n\nimport { cancelRunningTask } from '../shared/utils.js';\n\nimport type { AnnotationLayerParameters } from 'pdfjs-dist/types/src/display/annotation_layer.js';\nimport type { Annotations } from '../shared/types.js';\n\nexport default function AnnotationLayer(): React.ReactElement {\n  const documentContext = useDocumentContext();\n  const pageContext = usePageContext();\n\n  invariant(pageContext, 'Unable to find Page context.');\n\n  const mergedProps = { ...documentContext, ...pageContext };\n  const {\n    filterAnnotations,\n    imageResourcesPath,\n    linkService,\n    onGetAnnotationsError: onGetAnnotationsErrorProps,\n    onGetAnnotationsSuccess: onGetAnnotationsSuccessProps,\n    onRenderAnnotationLayerError: onRenderAnnotationLayerErrorProps,\n    onRenderAnnotationLayerSuccess: onRenderAnnotationLayerSuccessProps,\n    page,\n    pdf,\n    renderForms,\n    rotate,\n    scale = 1,\n  } = mergedProps;\n\n  invariant(\n    pdf,\n    'Attempted to load page annotations, but no document was specified. Wrap <Page /> in a <Document /> or pass explicit `pdf` prop.',\n  );\n  invariant(page, 'Attempted to load page annotations, but no page was specified.');\n  invariant(linkService, 'Attempted to load page annotations, but no linkService was specified.');\n\n  const [annotationsState, annotationsDispatch] = useResolver<Annotations>();\n  const { value: annotations, error: annotationsError } = annotationsState;\n  const layerElement = useRef<HTMLDivElement>(null);\n\n  warning(\n    Number.parseInt(\n      window.getComputedStyle(document.body).getPropertyValue('--react-pdf-annotation-layer'),\n      10,\n    ) === 1,\n    'AnnotationLayer styles not found. Read more: https://github.com/wojtekmaj/react-pdf#support-for-annotations',\n  );\n\n  function onLoadSuccess() {\n    if (!annotations) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    if (onGetAnnotationsSuccessProps) {\n      onGetAnnotationsSuccessProps(annotations);\n    }\n  }\n\n  function onLoadError() {\n    if (!annotationsError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, annotationsError.toString());\n\n    if (onGetAnnotationsErrorProps) {\n      onGetAnnotationsErrorProps(annotationsError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on page change\n  useEffect(\n    function resetAnnotations() {\n      annotationsDispatch({ type: 'RESET' });\n    },\n    [annotationsDispatch, page],\n  );\n\n  useEffect(\n    function loadAnnotations() {\n      if (!page) {\n        return;\n      }\n\n      const cancellable = makeCancellable(page.getAnnotations());\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then((nextAnnotations) => {\n          annotationsDispatch({ type: 'RESOLVE', value: nextAnnotations });\n        })\n        .catch((error) => {\n          annotationsDispatch({ type: 'REJECT', error });\n        });\n\n      return () => {\n        cancelRunningTask(runningTask);\n      };\n    },\n    [annotationsDispatch, page],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (annotations === undefined) {\n      return;\n    }\n\n    if (annotations === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [annotations]);\n\n  function onRenderSuccess() {\n    if (onRenderAnnotationLayerSuccessProps) {\n      onRenderAnnotationLayerSuccessProps();\n    }\n  }\n\n  function onRenderError(error: unknown) {\n    warning(false, `${error}`);\n\n    if (onRenderAnnotationLayerErrorProps) {\n      onRenderAnnotationLayerErrorProps(error);\n    }\n  }\n\n  const viewport = useMemo(\n    () => page.getViewport({ scale, rotation: rotate }),\n    [page, rotate, scale],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(\n    function renderAnnotationLayer() {\n      if (!pdf || !page || !linkService || !annotations) {\n        return;\n      }\n\n      const { current: layer } = layerElement;\n\n      if (!layer) {\n        return;\n      }\n\n      const clonedViewport = viewport.clone({ dontFlip: true });\n\n      const annotationLayerParameters = {\n        accessibilityManager: null, // TODO: Implement this\n        annotationCanvasMap: null, // TODO: Implement this\n        annotationEditorUIManager: null, // TODO: Implement this\n        annotationStorage: pdf.annotationStorage,\n        commentManager: null, // TODO: Implement this\n        div: layer,\n        l10n: null, // TODO: Implement this\n        linkService,\n        page,\n        structTreeLayer: null, // TODO: Implement this\n        viewport: clonedViewport,\n      };\n\n      const renderParameters: AnnotationLayerParameters = {\n        annotations: filterAnnotations ? filterAnnotations({ annotations }) : annotations,\n        annotationStorage: pdf.annotationStorage,\n        div: layer,\n        imageResourcesPath,\n        linkService,\n        page,\n        renderForms,\n        viewport: clonedViewport,\n      };\n\n      layer.innerHTML = '';\n\n      try {\n        new pdfjs.AnnotationLayer(annotationLayerParameters).render(renderParameters);\n\n        // Intentional immediate callback\n        onRenderSuccess();\n      } catch (error) {\n        onRenderError(error);\n      }\n\n      return () => {\n        // TODO: Cancel running task?\n      };\n    },\n    [\n      annotations,\n      filterAnnotations,\n      imageResourcesPath,\n      linkService,\n      page,\n      pdf,\n      renderForms,\n      viewport,\n    ],\n  );\n\n  return (\n    <div className={clsx('react-pdf__Page__annotations', 'annotationLayer')} ref={layerElement} />\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page/Canvas.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { render } from 'vitest-browser-react';\n\nimport { pdfjs } from '../index.test.js';\nimport PageContext from '../PageContext.js';\nimport Canvas from './Canvas.js';\n\nimport failingPage from '../../../../__mocks__/_failing_page.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../../test-utils.js';\n\nimport type { PDFPageProxy } from 'pdfjs-dist';\nimport type { PageContextType } from '../shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<PageContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <PageContext.Provider value={context as PageContextType}>{children}</PageContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<PageContextType> = context,\n    ) =>\n      await rerender(\n        <PageContext.Provider value={nextContext as PageContextType}>\n          {nextChildren}\n        </PageContext.Provider>,\n      ),\n  };\n}\n\ndescribe('Canvas', () => {\n  // Loaded page\n  let page: PDFPageProxy;\n  let pageWithRendererMocked: PDFPageProxy;\n\n  beforeAll(async () => {\n    const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    page = await pdf.getPage(1);\n\n    pageWithRendererMocked = Object.assign(page, {\n      render: () => ({\n        promise: new Promise<void>((resolve) => resolve()),\n        cancel: () => {\n          // Intentionally empty\n        },\n      }),\n    });\n  });\n\n  describe('loading', () => {\n    it('renders a page and calls onRenderSuccess callback properly', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<Canvas />, {\n        onRenderSuccess,\n        page: pageWithRendererMocked,\n        scale: 1,\n      });\n\n      expect.assertions(1);\n\n      await expect(onRenderSuccessPromise).resolves.toMatchObject([{}]);\n\n      restoreConsole();\n    });\n\n    it('calls onRenderError when failed to render canvas', async () => {\n      const { func: onRenderError, promise: onRenderErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<Canvas />, {\n        onRenderError,\n        page: failingPage,\n        scale: 1,\n      });\n\n      expect.assertions(1);\n\n      await expect(onRenderErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('passes canvas element to canvasRef properly', async () => {\n      const canvasRef = vi.fn();\n\n      await renderWithContext(<Canvas canvasRef={canvasRef} />, {\n        page: pageWithRendererMocked,\n        scale: 1,\n      });\n\n      expect(canvasRef).toHaveBeenCalled();\n      expect(canvasRef).toHaveBeenCalledWith(expect.any(HTMLElement));\n    });\n\n    it('does not request structure tree to be rendered when renderTextLayer = false', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(<Canvas />, {\n        onRenderSuccess,\n        page: pageWithRendererMocked,\n        renderTextLayer: false,\n      });\n\n      await onRenderSuccessPromise;\n\n      const structTree = container.querySelector('.react-pdf__Page__structTree');\n\n      expect(structTree).not.toBeInTheDocument();\n    });\n\n    it('renders StructTree when given renderTextLayer = true', async () => {\n      const { func: onGetStructTreeSuccess, promise: onGetStructTreeSuccessPromise } =\n        makeAsyncCallback();\n\n      const { container } = await renderWithContext(<Canvas />, {\n        onGetStructTreeSuccess,\n        page: pageWithRendererMocked,\n        renderTextLayer: true,\n      });\n\n      expect.assertions(1);\n\n      await onGetStructTreeSuccessPromise;\n\n      const canvas = container.querySelector('canvas') as HTMLCanvasElement;\n\n      expect(canvas.children.length).toBeGreaterThan(0);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Page/Canvas.tsx",
    "content": "'use client';\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport mergeRefs from 'merge-refs';\nimport * as pdfjs from 'pdfjs-dist';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport StructTree from '../StructTree.js';\n\nimport usePageContext from '../shared/hooks/usePageContext.js';\n\nimport {\n  cancelRunningTask,\n  getDevicePixelRatio,\n  isAbortException,\n  makePageCallback,\n} from '../shared/utils.js';\n\nimport type { RenderParameters } from 'pdfjs-dist/types/src/display/api.js';\n\nconst ANNOTATION_MODE = pdfjs.AnnotationMode;\n\ntype CanvasProps = {\n  canvasRef?: React.Ref<HTMLCanvasElement>;\n};\n\nexport default function Canvas(props: CanvasProps): React.ReactElement {\n  const pageContext = usePageContext();\n\n  invariant(pageContext, 'Unable to find Page context.');\n\n  const mergedProps = { ...pageContext, ...props };\n  const {\n    _className,\n    canvasBackground,\n    devicePixelRatio = getDevicePixelRatio(),\n    onRenderError: onRenderErrorProps,\n    onRenderSuccess: onRenderSuccessProps,\n    page,\n    renderForms,\n    renderTextLayer,\n    pageColors,\n    rotate,\n    scale,\n  } = mergedProps;\n  const { canvasRef } = props;\n\n  invariant(page, 'Attempted to render page canvas, but no page was specified.');\n\n  const canvasElement = useRef<HTMLCanvasElement>(null);\n\n  /**\n   * Called when a page is rendered successfully.\n   */\n  function onRenderSuccess() {\n    if (!page) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    if (onRenderSuccessProps) {\n      onRenderSuccessProps(makePageCallback(page, scale));\n    }\n  }\n\n  /**\n   * Called when a page fails to render.\n   */\n  function onRenderError(error: Error) {\n    if (isAbortException(error)) {\n      return;\n    }\n\n    warning(false, error.toString());\n\n    if (onRenderErrorProps) {\n      onRenderErrorProps(error);\n    }\n  }\n\n  const renderViewport = useMemo(\n    () => page.getViewport({ scale: scale * devicePixelRatio, rotation: rotate }),\n    [devicePixelRatio, page, rotate, scale],\n  );\n\n  const viewport = useMemo(\n    () => page.getViewport({ scale, rotation: rotate }),\n    [page, rotate, scale],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(\n    function drawPageOnCanvas() {\n      if (!page) {\n        return;\n      }\n\n      // Ensures the canvas will be re-rendered from scratch. Otherwise all form data will stay.\n      page.cleanup();\n\n      const { current: canvas } = canvasElement;\n\n      if (!canvas) {\n        return;\n      }\n\n      canvas.width = renderViewport.width;\n      canvas.height = renderViewport.height;\n\n      canvas.style.width = `${Math.floor(viewport.width)}px`;\n      canvas.style.height = `${Math.floor(viewport.height)}px`;\n      canvas.style.visibility = 'hidden';\n\n      const renderContext: RenderParameters = {\n        annotationMode: renderForms ? ANNOTATION_MODE.ENABLE_FORMS : ANNOTATION_MODE.ENABLE,\n        canvas,\n        canvasContext: canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D,\n        pageColors,\n        viewport: renderViewport,\n      };\n      if (canvasBackground) {\n        renderContext.background = canvasBackground;\n      }\n\n      const cancellable = page.render(renderContext);\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then(() => {\n          canvas.style.visibility = '';\n\n          onRenderSuccess();\n        })\n        .catch(onRenderError);\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [canvasBackground, page, pageColors, renderForms, renderViewport, viewport],\n  );\n\n  const cleanup = useCallback(() => {\n    const { current: canvas } = canvasElement;\n\n    /**\n     * Zeroing the width and height cause most browsers to release graphics\n     * resources immediately, which can greatly reduce memory consumption.\n     */\n    if (canvas) {\n      canvas.width = 0;\n      canvas.height = 0;\n    }\n  }, []);\n\n  useEffect(() => cleanup, [cleanup]);\n\n  return (\n    <canvas\n      className={`${_className}__canvas`}\n      dir=\"ltr\"\n      ref={mergeRefs(canvasRef, canvasElement)}\n      style={{\n        display: 'block',\n        userSelect: 'none',\n      }}\n    >\n      {renderTextLayer ? <StructTree /> : null}\n    </canvas>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page/TextLayer.css",
    "content": "/* Copyright 2014 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n:root {\n  --react-pdf-text-layer: 1;\n  --highlight-bg-color: rgba(180, 0, 170, 1);\n  --highlight-selected-bg-color: rgba(0, 100, 0, 1);\n}\n\n@media screen and (forced-colors: active) {\n  :root {\n    --highlight-bg-color: Highlight;\n    --highlight-selected-bg-color: ButtonText;\n  }\n}\n\n[data-main-rotation='90'] {\n  transform: rotate(90deg) translateY(-100%);\n}\n[data-main-rotation='180'] {\n  transform: rotate(180deg) translate(-100%, -100%);\n}\n[data-main-rotation='270'] {\n  transform: rotate(270deg) translateX(-100%);\n}\n\n.textLayer {\n  position: absolute;\n  text-align: initial;\n  inset: 0;\n  overflow: hidden;\n  line-height: 1;\n  text-size-adjust: none;\n  forced-color-adjust: none;\n  transform-origin: 0 0;\n  z-index: 2;\n}\n\n.textLayer :is(span, br) {\n  color: transparent;\n  position: absolute;\n  white-space: pre;\n  cursor: text;\n  margin: 0;\n  transform-origin: 0 0;\n}\n\n/* Only necessary in Google Chrome, see issue 14205, and most unfortunately\n * the problem doesn't show up in \"text\" reference tests. */\n.textLayer span.markedContent {\n  top: 0;\n  height: 0;\n}\n\n.textLayer .highlight {\n  margin: -1px;\n  padding: 1px;\n  background-color: var(--highlight-bg-color);\n  border-radius: 4px;\n}\n\n.textLayer .highlight.appended {\n  position: initial;\n}\n\n.textLayer .highlight.begin {\n  border-radius: 4px 0 0 4px;\n}\n\n.textLayer .highlight.end {\n  border-radius: 0 4px 4px 0;\n}\n\n.textLayer .highlight.middle {\n  border-radius: 0;\n}\n\n.textLayer .highlight.selected {\n  background-color: var(--highlight-selected-bg-color);\n}\n\n/* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */\n.textLayer br::selection {\n  background: transparent;\n}\n\n.textLayer .endOfContent {\n  display: block;\n  position: absolute;\n  inset: 100% 0 0;\n  z-index: -1;\n  cursor: default;\n  user-select: none;\n}\n\n.textLayer.selecting .endOfContent {\n  top: 0;\n}\n\n.hiddenCanvasElement {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  height: 0;\n  display: none;\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page/TextLayer.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { render } from 'vitest-browser-react';\n\nimport { pdfjs } from '../index.test.js';\nimport PageContext from '../PageContext.js';\nimport TextLayer from './TextLayer.js';\n\nimport failingPage from '../../../../__mocks__/_failing_page.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../../test-utils.js';\n\nimport type { PDFPageProxy } from 'pdfjs-dist';\nimport type { TextContent } from 'pdfjs-dist/types/src/display/api.js';\nimport type { CustomTextRenderer, PageContextType } from '../shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst untaggedPdfFile = await loadPDF('../../__mocks__/_untagged.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<PageContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <PageContext.Provider value={context as PageContextType}>{children}</PageContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<PageContextType> = context,\n    ) =>\n      await rerender(\n        <PageContext.Provider value={nextContext as PageContextType}>\n          {nextChildren}\n        </PageContext.Provider>,\n      ),\n  };\n}\n\nfunction getTextItems(container: HTMLElement) {\n  const wrapper = container.firstElementChild as HTMLDivElement;\n\n  return wrapper.querySelectorAll('[role=\"presentation\"]');\n}\n\ndescribe('TextLayer', () => {\n  // Loaded page\n  let page: PDFPageProxy;\n  let page2: PDFPageProxy;\n\n  // Loaded page text items\n  let desiredTextItems: TextContent['items'];\n  let desiredTextItems2: TextContent['items'];\n\n  beforeAll(async () => {\n    const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    page = await pdf.getPage(1);\n    const textContent = await page.getTextContent();\n    desiredTextItems = textContent.items;\n\n    page2 = await pdf.getPage(2);\n    const textContent2 = await page2.getTextContent();\n    desiredTextItems2 = textContent2.items;\n  });\n\n  describe('loading', () => {\n    it('loads text content and calls onGetTextSuccess callback properly', async () => {\n      const { func: onGetTextSuccess, promise: onGetTextSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<TextLayer />, {\n        onGetTextSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await expect(onGetTextSuccessPromise).resolves.toMatchObject([{ items: desiredTextItems }]);\n    });\n\n    it('calls onGetTextError when failed to load text content', async () => {\n      const { func: onGetTextError, promise: onGetTextErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<TextLayer />, {\n        onGetTextError,\n        page: failingPage,\n      });\n\n      expect.assertions(1);\n\n      await expect(onGetTextErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('replaces text content properly', async () => {\n      const { func: onGetTextSuccess, promise: onGetTextSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(<TextLayer />, {\n        onGetTextSuccess,\n        page,\n      });\n\n      expect.assertions(2);\n\n      await expect(onGetTextSuccessPromise).resolves.toMatchObject([\n        {\n          items: desiredTextItems,\n        },\n      ]);\n\n      const { func: onGetTextSuccess2, promise: onGetTextSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<TextLayer />, {\n        onGetTextSuccess: onGetTextSuccess2,\n        page: page2,\n      });\n\n      await expect(onGetTextSuccessPromise2).resolves.toMatchObject([\n        {\n          items: desiredTextItems2,\n        },\n      ]);\n    });\n\n    it('throws an error when placed outside Page', async () => {\n      muteConsole();\n\n      await expect(render(<TextLayer />)).rejects.toThrowError(\n        'Invariant failed: Unable to find Page context.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('renders text content properly', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const textItems = getTextItems(container);\n\n      expect(textItems).toHaveLength(desiredTextItems.length);\n    });\n\n    it('renders text content properly given customTextRenderer', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer = vi.fn();\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const textItems = getTextItems(container);\n\n      expect(textItems).toHaveLength(desiredTextItems.length);\n    });\n\n    it('maps textContent items to actual TextLayer children properly', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const { container, rerender } = await renderWithContext(<TextLayer />, {\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const textItems = getTextItems(container);\n\n      const { func: onRenderTextLayerSuccess2, promise: onRenderTextLayerSuccessPromise2 } =\n        makeAsyncCallback();\n\n      const customTextRenderer: CustomTextRenderer = ({ str }: { str: string }) => str;\n\n      await rerender(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess: onRenderTextLayerSuccess2,\n        page,\n      });\n\n      await onRenderTextLayerSuccessPromise2;\n\n      const textItems2 = getTextItems(container);\n\n      expect(textItems).toEqual(textItems2);\n    });\n\n    it('calls customTextRenderer with necessary arguments', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer = vi.fn();\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(3);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const textItems = getTextItems(container);\n\n      expect(textItems).toHaveLength(desiredTextItems.length);\n\n      expect(customTextRenderer).toHaveBeenCalledTimes(desiredTextItems.length);\n      expect(customTextRenderer).toHaveBeenCalledWith(\n        expect.objectContaining({\n          str: expect.any(String),\n          itemIndex: expect.any(Number),\n        }),\n      );\n    });\n\n    it('renders text content properly given customTextRenderer', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer = () => 'Test value';\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await onRenderTextLayerSuccessPromise;\n\n      expect(container).toHaveTextContent('Test value');\n    });\n\n    it('renders text content properly given customTextRenderer and untagged document', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer = () => 'Test value';\n\n      const untaggedDoc = await pdfjs.getDocument({ data: untaggedPdfFile.arrayBuffer }).promise;\n      const untaggedPage = await untaggedDoc.getPage(1);\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page: untaggedPage,\n      });\n\n      expect.assertions(1);\n\n      await onRenderTextLayerSuccessPromise;\n\n      expect(container).toHaveTextContent('Test value');\n    });\n\n    it('renders HTML formatting from customTextRenderer output', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer: CustomTextRenderer = ({ str }: { str: string }) =>\n        str.replace(/ipsum/g, '<mark>ipsum</mark>');\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(2);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const highlightedText = container.querySelectorAll('mark');\n\n      expect(highlightedText.length).toBeGreaterThan(0);\n      expect(highlightedText[0]).toHaveTextContent('ipsum');\n    });\n\n    it('does not render blocked tags from customTextRenderer output', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const windowWithBlockedTagFlag = window as typeof window & {\n        __reactPdfBlockedTagExecuted?: boolean;\n      };\n      windowWithBlockedTagFlag.__reactPdfBlockedTagExecuted = false;\n\n      const customTextRenderer: CustomTextRenderer = () =>\n        '<script>window.__reactPdfBlockedTagExecuted = true</script><mark>safe</mark>';\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(3);\n\n      await onRenderTextLayerSuccessPromise;\n\n      expect(container.querySelector('script')).not.toBeInTheDocument();\n      expect(windowWithBlockedTagFlag.__reactPdfBlockedTagExecuted).toBe(false);\n      expect(container.querySelector('mark')).toHaveTextContent('safe');\n\n      delete windowWithBlockedTagFlag.__reactPdfBlockedTagExecuted;\n    });\n\n    it('does not execute scripts from customTextRenderer output', async () => {\n      const { func: onRenderTextLayerSuccess, promise: onRenderTextLayerSuccessPromise } =\n        makeAsyncCallback();\n\n      const customTextRenderer: CustomTextRenderer = () =>\n        'javascript:/*--></title></style></textarea></script></xmp><details open ontoggle=alert(1)>x</details>';\n\n      const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => {\n        // Intentionally empty\n      });\n\n      const { container } = await renderWithContext(<TextLayer />, {\n        customTextRenderer,\n        onRenderTextLayerSuccess,\n        page,\n      });\n\n      expect.assertions(2);\n\n      await onRenderTextLayerSuccessPromise;\n\n      const detailsElement = container.querySelector('details');\n      detailsElement?.dispatchEvent(new Event('toggle', { bubbles: true }));\n\n      expect(detailsElement).not.toHaveAttribute('ontoggle');\n      expect(alertSpy).not.toHaveBeenCalled();\n\n      alertSpy.mockRestore();\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Page/TextLayer.tsx",
    "content": "'use client';\n\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport clsx from 'clsx';\nimport makeCancellable from 'make-cancellable-promise';\nimport * as pdfjs from 'pdfjs-dist';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport usePageContext from '../shared/hooks/usePageContext.js';\nimport useResolver from '../shared/hooks/useResolver.js';\n\nimport { cancelRunningTask, isAbortException } from '../shared/utils.js';\n\nimport type { TextContent, TextItem, TextMarkedContent } from 'pdfjs-dist/types/src/display/api.js';\n\nfunction isTextItem(item: TextItem | TextMarkedContent): item is TextItem {\n  return 'str' in item;\n}\n\nconst BLOCKED_CUSTOM_TEXT_TAGS = new Set([\n  'base',\n  'embed',\n  'iframe',\n  'link',\n  'meta',\n  'object',\n  'script',\n  'style',\n  'template',\n]);\n\nconst URL_ATTRIBUTES = new Set(['action', 'formaction', 'href', 'poster', 'src', 'xlink:href']);\n\nfunction isDangerousUrl(value: string): boolean {\n  let normalizedValue = '';\n\n  for (const char of value) {\n    const charCode = char.charCodeAt(0);\n\n    if (charCode <= 32 || charCode === 127) {\n      continue;\n    }\n\n    normalizedValue += char.toLowerCase();\n  }\n\n  return normalizedValue.startsWith('javascript:') || normalizedValue.startsWith('vbscript:');\n}\n\nfunction isElementNode(node: Node): node is Element {\n  return node.nodeType === Node.ELEMENT_NODE;\n}\n\nfunction isHtmlElement(node: Node): node is HTMLElement {\n  return isElementNode(node) && node instanceof HTMLElement;\n}\n\nfunction isBlockedCustomTextElement(node: Node): boolean {\n  return isHtmlElement(node) && BLOCKED_CUSTOM_TEXT_TAGS.has(node.tagName.toLowerCase());\n}\n\nfunction sanitizeCustomHtmlElement(element: HTMLElement): HTMLElement {\n  const sanitizedElement = document.createElement(element.tagName.toLowerCase());\n\n  Array.from(element.attributes).forEach((attribute) => {\n    const attributeName = attribute.name.toLowerCase();\n\n    if (attributeName.startsWith('on') || attributeName === 'srcdoc') {\n      return;\n    }\n\n    if (URL_ATTRIBUTES.has(attributeName) && isDangerousUrl(attribute.value)) {\n      return;\n    }\n\n    sanitizedElement.setAttribute(attribute.name, attribute.value);\n  });\n\n  Array.from(element.childNodes).forEach((child) => {\n    sanitizedElement.append(sanitizeCustomTextNode(child));\n  });\n\n  return sanitizedElement;\n}\n\nfunction sanitizeCustomTextNode(node: ChildNode): Node {\n  if (isHtmlElement(node) && !isBlockedCustomTextElement(node)) {\n    return sanitizeCustomHtmlElement(node);\n  }\n\n  return document.createTextNode(node.textContent ?? '');\n}\n\nfunction renderSafeCustomText(target: Element, content: string): void {\n  const template = document.createElement('template');\n  template.innerHTML = content;\n\n  const sanitizedFragment = document.createDocumentFragment();\n\n  Array.from(template.content.childNodes).forEach((child) => {\n    sanitizedFragment.append(sanitizeCustomTextNode(child));\n  });\n\n  target.replaceChildren(sanitizedFragment);\n}\n\nexport default function TextLayer(): React.ReactElement {\n  const pageContext = usePageContext();\n\n  invariant(pageContext, 'Unable to find Page context.');\n\n  const {\n    customTextRenderer,\n    onGetTextError,\n    onGetTextSuccess,\n    onRenderTextLayerError,\n    onRenderTextLayerSuccess,\n    page,\n    pageIndex,\n    pageNumber,\n    rotate,\n    scale,\n  } = pageContext;\n\n  invariant(page, 'Attempted to load page text content, but no page was specified.');\n\n  const [textContentState, textContentDispatch] = useResolver<TextContent>();\n  const { value: textContent, error: textContentError } = textContentState;\n  const layerElement = useRef<HTMLDivElement>(null);\n\n  warning(\n    Number.parseInt(\n      window.getComputedStyle(document.body).getPropertyValue('--react-pdf-text-layer'),\n      10,\n    ) === 1,\n    'TextLayer styles not found. Read more: https://github.com/wojtekmaj/react-pdf#support-for-text-layer',\n  );\n\n  /**\n   * Called when a page text content is read successfully\n   */\n  function onLoadSuccess() {\n    if (!textContent) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    if (onGetTextSuccess) {\n      onGetTextSuccess(textContent);\n    }\n  }\n\n  /**\n   * Called when a page text content failed to read successfully\n   */\n  function onLoadError() {\n    if (!textContentError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, textContentError.toString());\n\n    if (onGetTextError) {\n      onGetTextError(textContentError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on page change\n  useEffect(\n    function resetTextContent() {\n      textContentDispatch({ type: 'RESET' });\n    },\n    [page, textContentDispatch],\n  );\n\n  useEffect(\n    function loadTextContent() {\n      if (!page) {\n        return;\n      }\n\n      const cancellable = makeCancellable(page.getTextContent());\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then((nextTextContent) => {\n          textContentDispatch({ type: 'RESOLVE', value: nextTextContent });\n        })\n        .catch((error) => {\n          textContentDispatch({ type: 'REJECT', error });\n        });\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [page, textContentDispatch],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (textContent === undefined) {\n      return;\n    }\n\n    if (textContent === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [textContent]);\n\n  /**\n   * Called when a text layer is rendered successfully\n   */\n  const onRenderSuccess = useCallback(() => {\n    if (onRenderTextLayerSuccess) {\n      onRenderTextLayerSuccess();\n    }\n  }, [onRenderTextLayerSuccess]);\n\n  /**\n   * Called when a text layer failed to render successfully\n   */\n  const onRenderError = useCallback(\n    (error: Error) => {\n      if (isAbortException(error)) {\n        return;\n      }\n\n      warning(false, error.toString());\n\n      if (onRenderTextLayerError) {\n        onRenderTextLayerError(error);\n      }\n    },\n    [onRenderTextLayerError],\n  );\n\n  function onMouseDown() {\n    const layer = layerElement.current;\n\n    if (!layer) {\n      return;\n    }\n\n    layer.classList.add('selecting');\n  }\n\n  function onMouseUp() {\n    const layer = layerElement.current;\n\n    if (!layer) {\n      return;\n    }\n\n    layer.classList.remove('selecting');\n  }\n\n  const viewport = useMemo(\n    () => page.getViewport({ scale, rotation: rotate }),\n    [page, rotate, scale],\n  );\n\n  useLayoutEffect(\n    function renderTextLayer() {\n      if (!page || !textContent) {\n        return;\n      }\n\n      const { current: layer } = layerElement;\n\n      if (!layer) {\n        return;\n      }\n\n      layer.innerHTML = '';\n\n      const textContentSource = page.streamTextContent({ includeMarkedContent: true });\n\n      const parameters = {\n        container: layer,\n        textContentSource,\n        viewport,\n      };\n\n      const cancellable = new pdfjs.TextLayer(parameters);\n      const runningTask = cancellable;\n\n      cancellable\n        .render()\n        .then(() => {\n          const end = document.createElement('div');\n          end.className = 'endOfContent';\n          layer.append(end);\n\n          const layerChildren = layer.querySelectorAll('[role=\"presentation\"]');\n\n          if (customTextRenderer) {\n            let index = 0;\n            textContent.items.forEach((item, itemIndex) => {\n              if (!isTextItem(item)) {\n                return;\n              }\n\n              const child = layerChildren[index];\n\n              if (!child) {\n                return;\n              }\n\n              const content = customTextRenderer({\n                pageIndex,\n                pageNumber,\n                itemIndex,\n                ...item,\n              });\n\n              renderSafeCustomText(child, content);\n              index += item.str && item.hasEOL ? 2 : 1;\n            });\n          }\n\n          // Intentional immediate callback\n          onRenderSuccess();\n        })\n        .catch(onRenderError);\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [\n      customTextRenderer,\n      onRenderError,\n      onRenderSuccess,\n      page,\n      pageIndex,\n      pageNumber,\n      textContent,\n      viewport,\n    ],\n  );\n\n  return (\n    // biome-ignore lint/a11y/noStaticElementInteractions: False positive caused by non interactive wrapper listening for bubbling events\n    <div\n      className={clsx('react-pdf__Page__textContent', 'textLayer')}\n      onMouseUp={onMouseUp}\n      onMouseDown={onMouseDown}\n      ref={layerElement}\n    />\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Page.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { page, userEvent } from 'vitest/browser';\nimport { render } from 'vitest-browser-react';\nimport { createRef } from 'react';\n\nimport DocumentContext from './DocumentContext.js';\nimport { pdfjs } from './index.test.js';\nimport LinkService from './LinkService.js';\nimport Page from './Page.js';\n\nimport failingPdf from '../../../__mocks__/_failing_pdf.js';\nimport silentlyFailingPdf from '../../../__mocks__/_silently_failing_pdf.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';\n\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\nimport type { DocumentContextType, PageCallback } from './shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst pdfFile2 = await loadPDF('../../__mocks__/_pdf2.pdf');\nconst pdfFile4 = await loadPDF('../../__mocks__/_pdf4.pdf');\nconst pdfFile5 = await loadPDF('../../__mocks__/_pdf5.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<DocumentContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <DocumentContext.Provider value={context as DocumentContextType}>\n      {children}\n    </DocumentContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<DocumentContextType> = context,\n    ) =>\n      await rerender(\n        <DocumentContext.Provider value={nextContext as DocumentContextType}>\n          {nextChildren}\n        </DocumentContext.Provider>,\n      ),\n  };\n}\n\ndescribe('Page', () => {\n  const linkService = new LinkService();\n\n  // Loaded PDF file\n  let pdf: PDFDocumentProxy;\n  let pdf2: PDFDocumentProxy;\n  let pdf4: PDFDocumentProxy;\n  let pdf5: PDFDocumentProxy;\n\n  // Object with basic loaded page information that shall match after successful loading\n  const desiredLoadedPage: Partial<PDFPageProxy> = {};\n  const desiredLoadedPage2: Partial<PDFPageProxy> = {};\n  const desiredLoadedPage3: Partial<PDFPageProxy> = {};\n\n  // Callbacks used in registerPage and unregisterPage callbacks\n  let registerPageArguments: [number, HTMLDivElement];\n  let unregisterPageArguments: [number];\n\n  beforeAll(async () => {\n    pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    const page = await pdf.getPage(1);\n    desiredLoadedPage._pageIndex = page._pageIndex;\n    desiredLoadedPage._pageInfo = page._pageInfo;\n\n    const page2 = await pdf.getPage(2);\n    desiredLoadedPage2._pageIndex = page2._pageIndex;\n    desiredLoadedPage2._pageInfo = page2._pageInfo;\n\n    pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;\n\n    const page3 = await pdf2.getPage(1);\n    desiredLoadedPage3._pageIndex = page3._pageIndex;\n    desiredLoadedPage3._pageInfo = page3._pageInfo;\n\n    registerPageArguments = [page._pageIndex, expect.any(HTMLDivElement)];\n    unregisterPageArguments = [page._pageIndex];\n\n    pdf4 = await pdfjs.getDocument({ data: pdfFile4.arrayBuffer }).promise;\n\n    pdf5 = await pdfjs.getDocument({ data: pdfFile5.arrayBuffer }).promise;\n  });\n\n  describe('loading', () => {\n    it('loads a page and calls onLoadSuccess callback properly when placed inside Document', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPage]);\n    });\n\n    it('loads a page and calls onLoadSuccess callback properly when pdf prop is passed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(\n        <Page\n          onLoadSuccess={onLoadSuccess}\n          pageIndex={0}\n          pdf={pdf}\n          renderAnnotationLayer={false}\n        />,\n      );\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPage]);\n    });\n\n    it('returns all desired parameters in onLoadSuccess callback', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(5);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page.width).toBeDefined();\n      expect(page.height).toBeDefined();\n      expect(page.originalWidth).toBeDefined();\n      expect(page.originalHeight).toBeDefined();\n      // Example of a method that got stripped away in the past\n      expect(page.getTextContent).toBeInstanceOf(Function);\n    });\n\n    it('calls onLoadError when failed to load a page', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<Page onLoadError={onLoadError} pageIndex={0} />, {\n        linkService,\n        pdf: failingPdf,\n      });\n\n      expect.assertions(1);\n\n      await expect(onLoadErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('loads page when given pageIndex', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedPage);\n    });\n\n    it('loads page when given pageNumber', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageNumber={1} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedPage);\n    });\n\n    it('loads page of a given number when given conflicting pageNumber and pageIndex', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={1} pageNumber={1} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedPage);\n    });\n\n    it('calls registerPage when loaded a page', async () => {\n      const { func: registerPage, promise: registerPagePromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page pageIndex={0} />, {\n        linkService,\n        pdf,\n        registerPage,\n      });\n\n      expect.assertions(1);\n\n      await expect(registerPagePromise).resolves.toMatchObject(registerPageArguments);\n    });\n\n    it('calls unregisterPage on unmount', async () => {\n      const { func: unregisterPage, promise: nuregisterPagePromise } = makeAsyncCallback();\n\n      const { unmount } = await renderWithContext(<Page pageIndex={0} />, {\n        linkService,\n        pdf,\n        unregisterPage,\n      });\n\n      await unmount();\n\n      expect.assertions(1);\n\n      await expect(nuregisterPagePromise).resolves.toMatchObject(unregisterPageArguments);\n    });\n\n    it('replaces a page properly when pdf is changed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(2);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPage]);\n\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<Page onLoadSuccess={onLoadSuccess2} pageIndex={0} />, {\n        linkService,\n        pdf: pdf2,\n      });\n\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedPage3]);\n    });\n\n    it('replaces a page properly when pageNumber is changed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(2);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedPage]);\n\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<Page onLoadSuccess={onLoadSuccess2} pageIndex={1} />, {\n        linkService,\n        pdf,\n      });\n\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedPage2]);\n    });\n\n    it('throws an error when placed outside Document without pdf prop passed', async () => {\n      muteConsole();\n\n      await expect(render(<Page pageIndex={0} />)).rejects.toThrowError(\n        'Invariant failed: Attempted to load a page, but no document was specified. Wrap <Page /> in a <Document /> or pass explicit `pdf` prop.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('applies className to its wrapper when given a string', async () => {\n      const className = 'testClassName';\n\n      const { container } = await renderWithContext(<Page className={className} pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      const wrapper = container.querySelector('.react-pdf__Page');\n\n      expect(wrapper).toHaveClass(className);\n    });\n\n    it('passes container element to inputRef properly', async () => {\n      const inputRef = createRef<HTMLDivElement>();\n\n      await renderWithContext(<Page inputRef={inputRef} pageIndex={1} />, {\n        linkService,\n        pdf: silentlyFailingPdf,\n      });\n\n      expect(inputRef.current).toBeInstanceOf(HTMLDivElement);\n    });\n\n    it('passes canvas element to Canvas properly', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const canvasRef = createRef<HTMLCanvasElement>();\n\n      const { container } = await renderWithContext(\n        <Page canvasRef={canvasRef} onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas');\n\n      expect(canvasRef.current).toBe(pageCanvas);\n    });\n\n    it('renders \"No page specified.\" when given neither pageIndex nor pageNumber', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Page />, {\n        linkService,\n        pdf,\n      });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('No page specified.');\n\n      restoreConsole();\n    });\n\n    it('renders custom no data message when given nothing and noData is given', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Page noData=\"Nothing here\" />, {\n        linkService,\n        pdf,\n      });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n\n      restoreConsole();\n    });\n\n    it('renders custom no data message when given nothing and noData is given as a function', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Page noData={() => 'Nothing here'} />, {\n        linkService,\n        pdf,\n      });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n\n      restoreConsole();\n    });\n\n    it('renders \"Loading page…\" when loading a page', () => {\n      renderWithContext(<Page pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      const loading = page.getByText('Loading page…');\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a page and loading prop is given', () => {\n      renderWithContext(<Page loading=\"Loading\" pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      const loading = page.getByText('Loading', { exact: true });\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a page and loading prop is given as a function', () => {\n      renderWithContext(<Page loading={() => 'Loading'} pageIndex={0} />, {\n        linkService,\n        pdf,\n      });\n\n      const loading = page.getByText('Loading', { exact: true });\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('ignores pageIndex when given pageIndex and pageNumber', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={1} pageNumber={1} />, {\n        linkService,\n        pdf,\n      });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedPage);\n    });\n\n    it('requests page to be rendered with default rotation when given nothing', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n\n      const { container } = await renderWithContext(\n        <Page onRenderSuccess={onRenderSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      const [page] = await onRenderSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas') as HTMLCanvasElement;\n\n      const { width, height } = window.getComputedStyle(pageCanvas);\n\n      const viewport = page.getViewport({ scale: 1 });\n\n      // Expect the canvas layer not to be rotated\n      expect(Number.parseInt(width, 10)).toBe(Math.floor(viewport.width));\n      expect(Number.parseInt(height, 10)).toBe(Math.floor(viewport.height));\n    });\n\n    it('requests page to be rendered with given rotation when given rotate prop', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n      const rotate = 90;\n\n      const { container } = await renderWithContext(\n        <Page onRenderSuccess={onRenderSuccess} pageIndex={0} rotate={rotate} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      const [page] = await onRenderSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas') as HTMLCanvasElement;\n\n      const { width, height } = window.getComputedStyle(pageCanvas);\n\n      const viewport = page.getViewport({ scale: 1, rotation: rotate });\n\n      // Expect the canvas layer to be rotated\n      expect(Number.parseInt(width, 10)).toBe(Math.floor(viewport.width));\n      expect(Number.parseInt(height, 10)).toBe(Math.floor(viewport.height));\n    });\n\n    it('requests page to be rendered in canvas mode by default', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas');\n\n      expect(pageCanvas).toBeInTheDocument();\n    });\n\n    it('requests page not to be rendered when given renderMode = \"none\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderMode=\"none\" />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas');\n\n      expect(pageCanvas).not.toBeInTheDocument();\n    });\n\n    it('requests page to be rendered in canvas mode when given renderMode = \"canvas\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderMode=\"canvas\" />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Page__canvas');\n\n      expect(pageCanvas).toBeInTheDocument();\n    });\n\n    it('requests page to be rendered in custom mode when given renderMode = \"custom\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      function CustomRenderer() {\n        return <div className=\"custom-renderer\" />;\n      }\n\n      const { container } = await renderWithContext(\n        <Page\n          customRenderer={CustomRenderer}\n          onLoadSuccess={onLoadSuccess}\n          pageIndex={0}\n          renderMode=\"custom\"\n        />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const customRenderer = container.querySelector('.custom-renderer');\n\n      expect(customRenderer).toBeInTheDocument();\n    });\n\n    it('requests text content to be rendered by default', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const textLayer = container.querySelector('.react-pdf__Page__textContent');\n\n      expect(textLayer).toBeInTheDocument();\n    });\n\n    it('requests text content to be rendered when given renderTextLayer = true', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderTextLayer />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const textLayer = container.querySelector('.react-pdf__Page__textContent');\n\n      expect(textLayer).toBeInTheDocument();\n    });\n\n    it('does not request text content to be rendered when given renderTextLayer = false', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderTextLayer={false} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const textLayer = container.querySelector('.react-pdf__Page__textContent');\n\n      expect(textLayer).not.toBeInTheDocument();\n    });\n\n    it('renders TextLayer when given renderMode = \"canvas\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderMode=\"canvas\" renderTextLayer />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const textLayer = container.querySelector('.react-pdf__Page__textContent');\n\n      expect(textLayer).toBeInTheDocument();\n    });\n\n    it('renders TextLayer when given renderMode = \"custom\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      function CustomRenderer() {\n        return <div className=\"custom-renderer\" />;\n      }\n\n      const { container } = await renderWithContext(\n        <Page\n          customRenderer={CustomRenderer}\n          onLoadSuccess={onLoadSuccess}\n          pageIndex={0}\n          renderMode=\"custom\"\n          renderTextLayer\n        />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const textLayer = container.querySelector('.react-pdf__Page__textContent');\n\n      expect(textLayer).toBeInTheDocument();\n    });\n\n    it('requests annotations to be rendered by default', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const annotationLayer = container.querySelector('.react-pdf__Page__annotations');\n\n      expect(annotationLayer).toBeInTheDocument();\n    });\n\n    it('requests annotations to be rendered when given renderAnnotationLayer = true', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderAnnotationLayer />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const annotationLayer = container.querySelector('.react-pdf__Page__annotations');\n\n      expect(annotationLayer).toBeInTheDocument();\n    });\n\n    it('does not request annotations to be rendered when given renderAnnotationLayer = false', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0} renderAnnotationLayer={false} />,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const annotationLayer = container.querySelector('.react-pdf__Page__annotations');\n\n      expect(annotationLayer).not.toBeInTheDocument();\n    });\n\n    it('supports function as children', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(\n        <Page onLoadSuccess={onLoadSuccess} pageIndex={0}>\n          {({ page }) => <p>{`Page ${page.pageNumber}`}</p>}\n        </Page>,\n        {\n          linkService,\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const child = page.getByText('Page 1');\n\n      expect(child).toBeInTheDocument();\n    });\n  });\n\n  it('requests page to be rendered without forms by default', async () => {\n    const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =\n      makeAsyncCallback();\n\n    const { container } = await renderWithContext(\n      <Page\n        onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}\n        pageIndex={0}\n        renderMode=\"none\"\n      />,\n      {\n        linkService,\n        pdf: pdf4,\n      },\n    );\n\n    expect.assertions(1);\n\n    await onRenderAnnotationLayerSuccessPromise;\n\n    const textWidgetAnnotation = container.querySelector('.textWidgetAnnotation');\n\n    expect(textWidgetAnnotation).toBeFalsy();\n  });\n\n  it('requests page to be rendered with forms given renderForms = true', async () => {\n    const { func: onRenderAnnotationLayerSuccess, promise: onRenderAnnotationLayerSuccessPromise } =\n      makeAsyncCallback();\n\n    const { container } = await renderWithContext(\n      <Page\n        onRenderAnnotationLayerSuccess={onRenderAnnotationLayerSuccess}\n        pageIndex={0}\n        renderForms\n        renderMode=\"none\"\n      />,\n      {\n        linkService,\n        pdf: pdf4,\n      },\n    );\n\n    expect.assertions(1);\n\n    await onRenderAnnotationLayerSuccessPromise;\n\n    const textWidgetAnnotation = container.querySelector('.textWidgetAnnotation');\n\n    expect(textWidgetAnnotation).toBeTruthy();\n  });\n\n  it('requests page to be rendered at its original size given nothing', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n\n    await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n      linkService,\n      pdf,\n    });\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(page.originalWidth);\n  });\n\n  it('requests page to be rendered with a proper scale when given scale', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const scale = 1.5;\n\n    await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} />, {\n      linkService,\n      pdf,\n    });\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(page.originalWidth * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given width', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n\n    await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} width={width} />, {\n      linkService,\n      pdf,\n    });\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(width);\n  });\n\n  it('requests page to be rendered with a proper scale when given width and scale (multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Page onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} width={width} />,\n      {\n        linkService,\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toBeCloseTo(width * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given height', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const height = 850;\n\n    await renderWithContext(<Page height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n      linkService,\n      pdf,\n    });\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.height).toEqual(height);\n  });\n\n  it('requests page to be rendered with a proper scale when given height and scale (multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const height = 850;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Page height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} />,\n      {\n        linkService,\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.height).toBeCloseTo(height * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given width and height (ignores height)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const height = 100;\n\n    await renderWithContext(\n      <Page height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} width={width} />,\n      {\n        linkService,\n        pdf,\n      },\n    );\n\n    expect.assertions(2);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(width);\n    // Expect proportions to be correct even though invalid height was provided\n    expect(page.height).toEqual(page.originalHeight * (page.width / page.originalWidth));\n  });\n\n  it('requests page to be rendered with a proper scale when given width, height and scale (ignores height, multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const height = 100;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Page\n        height={height}\n        onLoadSuccess={onLoadSuccess}\n        pageIndex={0}\n        scale={scale}\n        width={width}\n      />,\n      {\n        linkService,\n        pdf,\n      },\n    );\n\n    expect.assertions(2);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toBeCloseTo(width * scale);\n    // Expect proportions to be correct even though invalid height was provided\n    expect(page.height).toEqual(page.originalHeight * (page.width / page.originalWidth));\n  });\n\n  it('calls onClick callback when clicked a page (sample of mouse events family)', async () => {\n    const onClick = vi.fn();\n\n    const { container } = await renderWithContext(<Page onClick={onClick} />, {\n      linkService,\n      pdf,\n    });\n\n    const page = container.querySelector('.react-pdf__Page') as HTMLDivElement;\n    await userEvent.click(page);\n\n    expect(onClick).toHaveBeenCalled();\n  });\n\n  function triggerTouchStart(element: HTMLElement) {\n    element.dispatchEvent(new TouchEvent('touchstart', { bubbles: true, cancelable: true }));\n  }\n\n  it('calls onTouchStart callback when touched a page (sample of touch events family)', async () => {\n    const onTouchStart = vi.fn();\n\n    const { container } = await renderWithContext(<Page onTouchStart={onTouchStart} />, {\n      linkService,\n      pdf,\n    });\n\n    const page = container.querySelector('.react-pdf__Page') as HTMLDivElement;\n    triggerTouchStart(page);\n\n    expect(onTouchStart).toHaveBeenCalled();\n  });\n\n  it('handles rotated page dimensions correctly in onLoadPage callback', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n\n    const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } =\n      makeAsyncCallback<[PageCallback]>();\n\n    await renderWithContext(<Page onLoadSuccess={onLoadSuccess} pageIndex={0} />, {\n      linkService,\n      pdf: pdf5,\n    });\n\n    await renderWithContext(<Page onLoadSuccess={onLoadSuccess2} pageIndex={1} />, {\n      linkService,\n      pdf: pdf5,\n    });\n\n    const [page1] = await onLoadSuccessPromise;\n    const [page2] = await onLoadSuccessPromise2;\n\n    expect(page1.width).toEqual(page2.height);\n    expect(page1.height).toEqual(page2.width);\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Page.tsx",
    "content": "'use client';\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport clsx from 'clsx';\nimport makeCancellable from 'make-cancellable-promise';\nimport makeEventProps from 'make-event-props';\nimport mergeRefs from 'merge-refs';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport Message from './Message.js';\nimport AnnotationLayer from './Page/AnnotationLayer.js';\nimport Canvas from './Page/Canvas.js';\nimport TextLayer from './Page/TextLayer.js';\nimport PageContext from './PageContext.js';\n\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\nimport useResolver from './shared/hooks/useResolver.js';\n\nimport { cancelRunningTask, isProvided, makePageCallback } from './shared/utils.js';\n\nimport type { EventProps } from 'make-event-props';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\nimport type {\n  ClassName,\n  CustomRenderer,\n  CustomTextRenderer,\n  FilterAnnotations,\n  NodeOrRenderer,\n  OnGetAnnotationsError,\n  OnGetAnnotationsSuccess,\n  OnGetStructTreeError,\n  OnGetStructTreeSuccess,\n  OnGetTextError,\n  OnGetTextSuccess,\n  OnPageLoadError,\n  OnPageLoadSuccess,\n  OnRenderAnnotationLayerError,\n  OnRenderAnnotationLayerSuccess,\n  OnRenderError,\n  OnRenderSuccess,\n  OnRenderTextLayerError,\n  OnRenderTextLayerSuccess,\n  PageCallback,\n  PageColors,\n  PageContextType,\n  PageRenderProps,\n  RenderMode,\n} from './shared/types.js';\n\nconst defaultScale = 1;\n\nexport type PageProps = {\n  _className?: string;\n  _enableRegisterUnregisterPage?: boolean;\n  /**\n   * Canvas background color. Any valid `canvas.fillStyle` can be used.\n   *\n   * @example 'transparent'\n   */\n  canvasBackground?: string;\n  /**\n   * A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to `<canvas>` rendered by `<PageCanvas>` component.\n   *\n   * @example (ref) => { this.myCanvas = ref; }\n   * @example this.ref\n   * @example ref\n   */\n  canvasRef?: React.Ref<HTMLCanvasElement>;\n  children?: React.ReactNode | ((props: PageRenderProps) => React.ReactNode);\n  /**\n   * Class name(s) that will be added to rendered element along with the default `react-pdf__Page`.\n   *\n   * @example 'custom-class-name-1 custom-class-name-2'\n   * @example ['custom-class-name-1', 'custom-class-name-2']\n   */\n  className?: ClassName;\n  /**\n   * Function that customizes how a page is rendered. You must set `renderMode` to `\"custom\"` to use this prop.\n   *\n   * @example MyCustomRenderer\n   */\n  customRenderer?: CustomRenderer;\n  /**\n   * Function that customizes how a text layer is rendered.\n   *\n   * @example ({ str, itemIndex }) => str.replace(/ipsum/g, value => `<mark>${value}</mark>`)\n   */\n  customTextRenderer?: CustomTextRenderer;\n  /**\n   * The ratio between physical pixels and device-independent pixels (DIPs) on the current device.\n   *\n   * @default window.devicePixelRatio\n   * @example 1\n   */\n  devicePixelRatio?: number;\n  /**\n   * What the component should display in case of an error.\n   *\n   * @default 'Failed to load the page.'\n   * @example 'An error occurred!'\n   * @example <p>An error occurred!</p>\n   * @example this.renderError\n   */\n  error?: NodeOrRenderer;\n  /**\n   * Function to filter annotations before they are rendered.\n   *\n   * @example ({ annotations }) => annotations.filter(annotation => annotation.subtype === 'Text')\n   */\n  filterAnnotations?: FilterAnnotations;\n  /**\n   * Page height. If neither `height` nor `width` are defined, page will be rendered at the size defined in PDF. If you define `width` and `height` at the same time, `height` will be ignored. If you define `height` and `scale` at the same time, the height will be multiplied by a given factor.\n   *\n   * @example 300\n   */\n  height?: number;\n  /**\n   * The path used to prefix the src attributes of annotation SVGs.\n   *\n   * @default ''\n   * @example '/public/images/'\n   */\n  imageResourcesPath?: string;\n  /**\n   * A prop that behaves like [ref](https://reactjs.org/docs/refs-and-the-dom.html), but it's passed to main `<div>` rendered by `<Page>` component.\n   *\n   * @example (ref) => { this.myPage = ref; }\n   * @example this.ref\n   * @example ref\n   */\n  inputRef?: React.Ref<HTMLDivElement | null>;\n  /**\n   * What the component should display while loading.\n   *\n   * @default 'Loading page…'\n   * @example 'Please wait!'\n   * @example <p>Please wait!</p>\n   * @example this.renderLoader\n   */\n  loading?: NodeOrRenderer;\n  /**\n   *  What the component should display in case of no data.\n   *\n   * @default 'No page specified.'\n   * @example 'Please select a page.'\n   * @example <p>Please select a page.</p>\n   * @example this.renderNoData\n   */\n  noData?: NodeOrRenderer;\n  /**\n   * Function called in case of an error while loading annotations.\n   *\n   * @example (error) => alert('Error while loading annotations! ' + error.message)\n   */\n  onGetAnnotationsError?: OnGetAnnotationsError;\n  /**\n   * Function called when annotations are successfully loaded.\n   *\n   * @example (annotations) => alert('Now displaying ' + annotations.length + ' annotations!')\n   */\n  onGetAnnotationsSuccess?: OnGetAnnotationsSuccess;\n  /**\n   * Function called in case of an error while loading structure tree.\n   *\n   * @example (error) => alert('Error while loading structure tree! ' + error.message)\n   */\n  onGetStructTreeError?: OnGetStructTreeError;\n  /**\n   * Function called when structure tree is successfully loaded.\n   *\n   * @example (structTree) => alert(JSON.stringify(structTree))\n   */\n  onGetStructTreeSuccess?: OnGetStructTreeSuccess;\n  /**\n   * Function called in case of an error while loading text layer items.\n   *\n   * @example (error) => alert('Error while loading text layer items! ' + error.message)\n   */\n  onGetTextError?: OnGetTextError;\n  /**\n   * Function called when text layer items are successfully loaded.\n   *\n   * @example ({ items, styles }) => alert('Now displaying ' + items.length + ' text layer items!')\n   */\n  onGetTextSuccess?: OnGetTextSuccess;\n  /**\n   * Function called in case of an error while loading the page.\n   *\n   * @example (error) => alert('Error while loading page! ' + error.message)\n   */\n  onLoadError?: OnPageLoadError;\n  /**\n   * Function called when the page is successfully loaded.\n   *\n   * @example (page) => alert('Now displaying a page number ' + page.pageNumber + '!')\n   */\n  onLoadSuccess?: OnPageLoadSuccess;\n  /**\n   * Function called in case of an error while rendering the annotation layer.\n   *\n   * @example (error) => alert('Error while rendering annotation layer! ' + error.message)\n   */\n  onRenderAnnotationLayerError?: OnRenderAnnotationLayerError;\n  /**\n   * Function called when annotations are successfully rendered on the screen.\n   *\n   * @example () => alert('Rendered the annotation layer!')\n   */\n  onRenderAnnotationLayerSuccess?: OnRenderAnnotationLayerSuccess;\n  /**\n   * Function called in case of an error while rendering the page.\n   *\n   * @example (error) => alert('Error while loading page! ' + error.message)\n   */\n  onRenderError?: OnRenderError;\n  /**\n   * Function called when the page is successfully rendered on the screen.\n   *\n   * @example () => alert('Rendered the page!')\n   */\n  onRenderSuccess?: OnRenderSuccess;\n  /**\n   * Function called in case of an error while rendering the text layer.\n   *\n   * @example (error) => alert('Error while rendering text layer! ' + error.message)\n   */\n  onRenderTextLayerError?: OnRenderTextLayerError;\n  /**\n   * Function called when the text layer is successfully rendered on the screen.\n   *\n   * @example () => alert('Rendered the text layer!')\n   */\n  onRenderTextLayerSuccess?: OnRenderTextLayerSuccess;\n  /**\n   * Colors used to render the page. If not provided, the default colors from PDF will be used.\n   *\n   * @example { background: 'transparent', foreground: '#ff0000' }\n   */\n  pageColors?: PageColors;\n  /**\n   * Which page from PDF file should be displayed, by page index. Ignored if `pageNumber` prop is provided.\n   *\n   * @default 0\n   * @example 1\n   */\n  pageIndex?: number;\n  /**\n   * Which page from PDF file should be displayed, by page number. If provided, `pageIndex` prop will be ignored.\n   *\n   * @default 1\n   * @example 2\n   */\n  pageNumber?: number;\n  /**\n   * pdf object obtained from `<Document />`'s `onLoadSuccess` callback function.\n   *\n   * @example pdf\n   */\n  pdf?: PDFDocumentProxy | false;\n  registerPage?: undefined;\n  /**\n   * Whether annotations (e.g. links) should be rendered.\n   *\n   * @default true\n   * @example false\n   */\n  renderAnnotationLayer?: boolean;\n  /**\n   * Whether forms should be rendered. `renderAnnotationLayer` prop must be set to `true`.\n   *\n   * @default false\n   * @example true\n   */\n  renderForms?: boolean;\n  /**\n   * Rendering mode of the document. Can be `\"canvas\"`, `\"custom\"` or `\"none\"`. If set to `\"custom\"`, `customRenderer` must also be provided.\n   *\n   * @default 'canvas'\n   * @example 'custom'\n   */\n  renderMode?: RenderMode;\n  /**\n   * Whether a text layer should be rendered.\n   *\n   * @default true\n   * @example false\n   */\n  renderTextLayer?: boolean;\n  /**\n   * Rotation of the page in degrees. `90` = rotated to the right, `180` = upside down, `270` = rotated to the left.\n   *\n   * @default 0\n   * @example 90\n   */\n  rotate?: number | null;\n  /**\n   * Page scale.\n   *\n   * @default 1\n   * @example 0.5\n   */\n  scale?: number;\n  unregisterPage?: undefined;\n  /**\n   * Page width. If neither `height` nor `width` are defined, page will be rendered at the size defined in PDF. If you define `width` and `height` at the same time, `height` will be ignored. If you define `width` and `scale` at the same time, the width will be multiplied by a given factor.\n   *\n   * @example 300\n   */\n  width?: number;\n} & EventProps<PageCallback | false | undefined>;\n\n/**\n * Displays a page.\n *\n * Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function, however some advanced functions like linking between pages inside a document may not be working correctly.\n */\nexport default function Page(props: PageProps): React.ReactElement {\n  const documentContext = useDocumentContext();\n\n  const mergedProps = { ...documentContext, ...props };\n  const {\n    _className = 'react-pdf__Page',\n    _enableRegisterUnregisterPage = true,\n    canvasBackground,\n    canvasRef,\n    children,\n    className,\n    customRenderer: CustomRenderer,\n    customTextRenderer,\n    devicePixelRatio,\n    error = 'Failed to load the page.',\n    filterAnnotations,\n    height,\n    inputRef,\n    loading = 'Loading page…',\n    noData = 'No page specified.',\n    onGetAnnotationsError: onGetAnnotationsErrorProps,\n    onGetAnnotationsSuccess: onGetAnnotationsSuccessProps,\n    onGetStructTreeError: onGetStructTreeErrorProps,\n    onGetStructTreeSuccess: onGetStructTreeSuccessProps,\n    onGetTextError: onGetTextErrorProps,\n    onGetTextSuccess: onGetTextSuccessProps,\n    onLoadError: onLoadErrorProps,\n    onLoadSuccess: onLoadSuccessProps,\n    onRenderAnnotationLayerError: onRenderAnnotationLayerErrorProps,\n    onRenderAnnotationLayerSuccess: onRenderAnnotationLayerSuccessProps,\n    onRenderError: onRenderErrorProps,\n    onRenderSuccess: onRenderSuccessProps,\n    onRenderTextLayerError: onRenderTextLayerErrorProps,\n    onRenderTextLayerSuccess: onRenderTextLayerSuccessProps,\n    pageColors,\n    pageIndex: pageIndexProps,\n    pageNumber: pageNumberProps,\n    pdf,\n    registerPage,\n    renderAnnotationLayer: renderAnnotationLayerProps = true,\n    renderForms = false,\n    renderMode = 'canvas',\n    renderTextLayer: renderTextLayerProps = true,\n    rotate: rotateProps,\n    scale: scaleProps = defaultScale,\n    unregisterPage,\n    width,\n    ...otherProps\n  } = mergedProps;\n\n  const [pageState, pageDispatch] = useResolver<PDFPageProxy>();\n  const { value: page, error: pageError } = pageState;\n  const pageElement = useRef<HTMLDivElement>(null);\n\n  invariant(\n    pdf,\n    'Attempted to load a page, but no document was specified. Wrap <Page /> in a <Document /> or pass explicit `pdf` prop.',\n  );\n\n  const pageIndex = isProvided(pageNumberProps) ? pageNumberProps - 1 : (pageIndexProps ?? null);\n\n  const pageNumber = pageNumberProps ?? (isProvided(pageIndexProps) ? pageIndexProps + 1 : null);\n\n  const rotate = rotateProps ?? (page ? page.rotate : null);\n\n  const scale = useMemo(() => {\n    if (!page) {\n      return null;\n    }\n\n    // Be default, we'll render page at 100% * scale width.\n    let pageScale = 1;\n\n    // Passing scale explicitly null would cause the page not to render\n    const scaleWithDefault = scaleProps ?? defaultScale;\n\n    // If width/height is defined, calculate the scale of the page so it could be of desired width.\n    if (width || height) {\n      const viewport = page.getViewport({ scale: 1, rotation: rotate as number });\n      if (width) {\n        pageScale = width / viewport.width;\n      } else if (height) {\n        pageScale = height / viewport.height;\n      }\n    }\n\n    return scaleWithDefault * pageScale;\n  }, [height, page, rotate, scaleProps, width]);\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on pdf change\n  useEffect(\n    function hook() {\n      return () => {\n        if (!isProvided(pageIndex)) {\n          // Impossible, but TypeScript doesn't know that\n          return;\n        }\n\n        if (_enableRegisterUnregisterPage && unregisterPage) {\n          unregisterPage(pageIndex);\n        }\n      };\n    },\n    [_enableRegisterUnregisterPage, pdf, pageIndex, unregisterPage],\n  );\n\n  /**\n   * Called when a page is loaded successfully\n   */\n  function onLoadSuccess() {\n    if (onLoadSuccessProps) {\n      if (!page || !scale) {\n        // Impossible, but TypeScript doesn't know that\n        return;\n      }\n\n      onLoadSuccessProps(makePageCallback(page, scale));\n    }\n\n    if (_enableRegisterUnregisterPage && registerPage) {\n      if (!isProvided(pageIndex) || !pageElement.current) {\n        // Impossible, but TypeScript doesn't know that\n        return;\n      }\n\n      registerPage(pageIndex, pageElement.current);\n    }\n  }\n\n  /**\n   * Called when a page failed to load\n   */\n  function onLoadError() {\n    if (!pageError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, pageError.toString());\n\n    if (onLoadErrorProps) {\n      onLoadErrorProps(pageError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on pdf and pageIndex change\n  useEffect(\n    function resetPage() {\n      pageDispatch({ type: 'RESET' });\n    },\n    [pageDispatch, pdf, pageIndex],\n  );\n\n  useEffect(\n    function loadPage() {\n      if (!pdf || !pageNumber) {\n        return;\n      }\n\n      const cancellable = makeCancellable(pdf.getPage(pageNumber));\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then((nextPage) => {\n          pageDispatch({ type: 'RESOLVE', value: nextPage });\n        })\n        .catch((error) => {\n          pageDispatch({ type: 'REJECT', error });\n        });\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [pageDispatch, pdf, pageNumber],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (page === undefined) {\n      return;\n    }\n\n    if (page === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [page, scale]);\n\n  const childContext = useMemo(\n    () =>\n      // Technically there cannot be page without pageIndex, pageNumber, rotate and scale, but TypeScript doesn't know that\n      isProvided(pageIndex) && pageNumber && isProvided(rotate) && isProvided(scale)\n        ? {\n            _className,\n            canvasBackground,\n            customTextRenderer,\n            devicePixelRatio,\n            filterAnnotations,\n            onGetAnnotationsError: onGetAnnotationsErrorProps,\n            onGetAnnotationsSuccess: onGetAnnotationsSuccessProps,\n            onGetStructTreeError: onGetStructTreeErrorProps,\n            onGetStructTreeSuccess: onGetStructTreeSuccessProps,\n            onGetTextError: onGetTextErrorProps,\n            onGetTextSuccess: onGetTextSuccessProps,\n            onRenderAnnotationLayerError: onRenderAnnotationLayerErrorProps,\n            onRenderAnnotationLayerSuccess: onRenderAnnotationLayerSuccessProps,\n            onRenderError: onRenderErrorProps,\n            onRenderSuccess: onRenderSuccessProps,\n            onRenderTextLayerError: onRenderTextLayerErrorProps,\n            onRenderTextLayerSuccess: onRenderTextLayerSuccessProps,\n            page,\n            pageColors,\n            pageIndex,\n            pageNumber,\n            renderForms,\n            renderTextLayer: renderTextLayerProps,\n            rotate,\n            scale,\n          }\n        : null,\n    [\n      _className,\n      canvasBackground,\n      customTextRenderer,\n      devicePixelRatio,\n      filterAnnotations,\n      onGetAnnotationsErrorProps,\n      onGetAnnotationsSuccessProps,\n      onGetStructTreeErrorProps,\n      onGetStructTreeSuccessProps,\n      onGetTextErrorProps,\n      onGetTextSuccessProps,\n      onRenderAnnotationLayerErrorProps,\n      onRenderAnnotationLayerSuccessProps,\n      onRenderErrorProps,\n      onRenderSuccessProps,\n      onRenderTextLayerErrorProps,\n      onRenderTextLayerSuccessProps,\n      page,\n      pageColors,\n      pageIndex,\n      pageNumber,\n      renderForms,\n      renderTextLayerProps,\n      rotate,\n      scale,\n    ],\n  );\n\n  const eventProps = useMemo(\n    () =>\n      makeEventProps(otherProps, () =>\n        page ? (scale ? makePageCallback(page, scale) : undefined) : page,\n      ),\n    // biome-ignore lint/correctness/useExhaustiveDependencies: FIXME\n    [otherProps, page, scale],\n  );\n\n  const pageKey = `${pageIndex}@${scale}/${rotate}`;\n\n  function renderMainLayer() {\n    switch (renderMode) {\n      case 'custom': {\n        invariant(\n          CustomRenderer,\n          `renderMode was set to \"custom\", but no customRenderer was passed.`,\n        );\n\n        return <CustomRenderer key={`${pageKey}_custom`} />;\n      }\n      case 'none':\n        return null;\n      case 'canvas':\n      default:\n        return <Canvas key={`${pageKey}_canvas`} canvasRef={canvasRef} />;\n    }\n  }\n\n  function renderTextLayer() {\n    if (!renderTextLayerProps) {\n      return null;\n    }\n\n    return <TextLayer key={`${pageKey}_text`} />;\n  }\n\n  function renderAnnotationLayer() {\n    if (!renderAnnotationLayerProps) {\n      return null;\n    }\n\n    return <AnnotationLayer key={`${pageKey}_annotations`} />;\n  }\n\n  function renderChildren() {\n    function isFulfilledContext(context: PageContextType): context is PageRenderProps {\n      return Boolean(context?.page);\n    }\n\n    if (!isFulfilledContext(childContext)) {\n      // Impossible, but TypeScript doesn't know that\n      throw new Error('page is undefined');\n    }\n\n    const resolvedChildren = typeof children === 'function' ? children(childContext) : children;\n\n    return (\n      <PageContext.Provider value={childContext}>\n        {renderMainLayer()}\n        {renderTextLayer()}\n        {renderAnnotationLayer()}\n        {resolvedChildren}\n      </PageContext.Provider>\n    );\n  }\n\n  function renderContent() {\n    if (!pageNumber) {\n      return <Message type=\"no-data\">{typeof noData === 'function' ? noData() : noData}</Message>;\n    }\n\n    if (pdf === null || page === undefined || page === null) {\n      return (\n        <Message type=\"loading\">{typeof loading === 'function' ? loading() : loading}</Message>\n      );\n    }\n\n    if (pdf === false || page === false) {\n      return <Message type=\"error\">{typeof error === 'function' ? error() : error}</Message>;\n    }\n\n    return renderChildren();\n  }\n\n  return (\n    <div\n      className={clsx(_className, className)}\n      data-page-number={pageNumber}\n      // Assertion is needed for React 18 compatibility\n      ref={mergeRefs(inputRef as React.Ref<HTMLDivElement>, pageElement)}\n      style={\n        {\n          '--scale-round-x': '1px',\n          '--scale-round-y': '1px',\n          '--scale-factor': '1',\n          '--user-unit': `${scale}`,\n          '--total-scale-factor': 'calc(var(--scale-factor) * var(--user-unit))',\n          backgroundColor: canvasBackground || 'white',\n          position: 'relative',\n          minWidth: 'min-content',\n          minHeight: 'min-content',\n        } as React.CSSProperties\n      }\n      {...eventProps}\n    >\n      {renderContent()}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/PageContext.tsx",
    "content": "'use client';\n\nimport { createContext } from 'react';\n\nimport type { PageContextType } from './shared/types.js';\n\nconst pageContext: React.Context<PageContextType> = createContext<PageContextType>(null);\n\nexport default pageContext;\n"
  },
  {
    "path": "packages/react-pdf/src/PasswordResponses.ts",
    "content": "// As defined in https://github.com/mozilla/pdf.js/blob/d9fac3459609a807be6506fb3441b5da4b154d14/src/shared/util.js#L371-L374\n\nconst PasswordResponses = {\n  NEED_PASSWORD: 1,\n  INCORRECT_PASSWORD: 2,\n} as const;\n\nexport default PasswordResponses;\n"
  },
  {
    "path": "packages/react-pdf/src/Ref.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\n\nimport Ref from './Ref.js';\n\ndescribe('Ref', () => {\n  it('returns proper reference for given num and gen', () => {\n    const num = 1;\n    const gen = 2;\n    const ref = new Ref({ num, gen });\n    expect(ref.toString()).toBe('1R2');\n  });\n\n  it('returns proper reference for given num and gen when gen = 0', () => {\n    const num = 1;\n    const gen = 0;\n    const ref = new Ref({ num, gen });\n    expect(ref.toString()).toBe('1R');\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Ref.ts",
    "content": "export default class Ref {\n  num: number;\n  gen: number;\n\n  constructor({ num, gen }: { num: number; gen: number }) {\n    this.num = num;\n    this.gen = gen;\n  }\n\n  toString(): string {\n    let str = `${this.num}R`;\n    if (this.gen !== 0) {\n      str += this.gen;\n    }\n    return str;\n  }\n}\n"
  },
  {
    "path": "packages/react-pdf/src/StructTree.spec.tsx",
    "content": "import { beforeAll, describe, expect, it } from 'vitest';\nimport { render } from 'vitest-browser-react';\n\nimport { pdfjs } from './index.test.js';\nimport PageContext from './PageContext.js';\nimport StructTree from './StructTree.js';\n\nimport failingPage from '../../../__mocks__/_failing_page.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';\n\nimport type { PDFPageProxy } from 'pdfjs-dist';\nimport type { StructTreeNode } from 'pdfjs-dist/types/src/display/api.js';\nimport type { PageContextType } from './shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<PageContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <PageContext.Provider value={context as PageContextType}>{children}</PageContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<PageContextType> = context,\n    ) =>\n      await rerender(\n        <PageContext.Provider value={nextContext as PageContextType}>\n          {nextChildren}\n        </PageContext.Provider>,\n      ),\n  };\n}\n\ndescribe('StructTree', () => {\n  // Loaded page\n  let page: PDFPageProxy;\n  let page2: PDFPageProxy;\n\n  // Loaded structure tree\n  let desiredStructTree: StructTreeNode;\n  let desiredStructTree2: StructTreeNode;\n\n  beforeAll(async () => {\n    const pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    page = await pdf.getPage(1);\n    desiredStructTree = await page.getStructTree();\n\n    page2 = await pdf.getPage(2);\n    desiredStructTree2 = await page2.getStructTree();\n  });\n\n  describe('loading', () => {\n    it('loads structure tree and calls onGetStructTreeSuccess callback properly', async () => {\n      const { func: onGetStructTreeSuccess, promise: onGetStructTreeSuccessPromise } =\n        makeAsyncCallback();\n\n      await renderWithContext(<StructTree />, {\n        onGetStructTreeSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await expect(onGetStructTreeSuccessPromise).resolves.toMatchObject([desiredStructTree]);\n    });\n\n    it('calls onGetStructTreeError when failed to load annotations', async () => {\n      const { func: onGetStructTreeError, promise: onGetStructTreeErrorPromise } =\n        makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<StructTree />, {\n        onGetStructTreeError,\n        page: failingPage,\n      });\n\n      expect.assertions(1);\n\n      await expect(onGetStructTreeErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('replaces structure tree properly when page is changed', async () => {\n      const { func: onGetStructTreeSuccess, promise: onGetStructTreeSuccessPromise } =\n        makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(<StructTree />, {\n        onGetStructTreeSuccess,\n        page,\n      });\n\n      expect.assertions(2);\n\n      await expect(onGetStructTreeSuccessPromise).resolves.toMatchObject([desiredStructTree]);\n\n      const { func: onGetStructTreeSuccess2, promise: onGetStructTreeSuccessPromise2 } =\n        makeAsyncCallback();\n\n      await rerender(<StructTree />, {\n        onGetStructTreeSuccess: onGetStructTreeSuccess2,\n        page: page2,\n      });\n\n      await expect(onGetStructTreeSuccessPromise2).resolves.toMatchObject([desiredStructTree2]);\n    });\n\n    it('throws an error when placed outside Page', async () => {\n      muteConsole();\n\n      await expect(render(<StructTree />)).rejects.toThrowError(\n        'Invariant failed: Unable to find Page context.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('renders structure tree properly', async () => {\n      const { func: onGetStructTreeSuccess, promise: onGetStructTreeSuccessPromise } =\n        makeAsyncCallback();\n\n      const { container } = await renderWithContext(<StructTree />, {\n        onGetStructTreeSuccess,\n        page,\n      });\n\n      expect.assertions(1);\n\n      await onGetStructTreeSuccessPromise;\n\n      const wrapper = container.firstElementChild as HTMLSpanElement;\n\n      expect(wrapper.outerHTML).toBe(\n        '<span class=\"react-pdf__Page__structTree structTree\"><span><span role=\"heading\" aria-level=\"1\" aria-owns=\"p3R_mc0\"></span><span aria-owns=\"p3R_mc1\"></span><span aria-owns=\"p3R_mc2\"></span><span role=\"figure\" aria-owns=\"p3R_mc12\"></span><span aria-owns=\"p3R_mc3\"></span><span aria-owns=\"p3R_mc4\"></span><span role=\"heading\" aria-level=\"2\" aria-owns=\"p3R_mc5\"></span><span aria-owns=\"p3R_mc6\"></span><span><span aria-owns=\"p3R_mc7\"></span><span role=\"link\"><span aria-owns=\"13R\"></span><span aria-owns=\"p3R_mc8\"></span></span><span aria-owns=\"p3R_mc9\"></span></span><span aria-owns=\"p3R_mc10\"></span><span aria-owns=\"p3R_mc11\"></span></span></span>',\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/StructTree.tsx",
    "content": "import { useEffect } from 'react';\nimport makeCancellable from 'make-cancellable-promise';\nimport invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport StructTreeItem from './StructTreeItem.js';\n\nimport usePageContext from './shared/hooks/usePageContext.js';\nimport useResolver from './shared/hooks/useResolver.js';\n\nimport { cancelRunningTask } from './shared/utils.js';\n\nimport type { StructTreeNodeWithExtraAttributes } from './shared/types.js';\n\nexport default function StructTree(): React.ReactElement | null {\n  const pageContext = usePageContext();\n\n  invariant(pageContext, 'Unable to find Page context.');\n\n  const {\n    onGetStructTreeError: onGetStructTreeErrorProps,\n    onGetStructTreeSuccess: onGetStructTreeSuccessProps,\n  } = pageContext;\n\n  const [structTreeState, structTreeDispatch] = useResolver<StructTreeNodeWithExtraAttributes>();\n  const { value: structTree, error: structTreeError } = structTreeState;\n\n  const { customTextRenderer, page } = pageContext;\n\n  function onLoadSuccess() {\n    if (!structTree) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    if (onGetStructTreeSuccessProps) {\n      onGetStructTreeSuccessProps(structTree);\n    }\n  }\n\n  function onLoadError() {\n    if (!structTreeError) {\n      // Impossible, but TypeScript doesn't know that\n      return;\n    }\n\n    warning(false, structTreeError.toString());\n\n    if (onGetStructTreeErrorProps) {\n      onGetStructTreeErrorProps(structTreeError);\n    }\n  }\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect intentionally triggered on page change\n  useEffect(\n    function resetStructTree() {\n      structTreeDispatch({ type: 'RESET' });\n    },\n    [structTreeDispatch, page],\n  );\n\n  useEffect(\n    function loadStructTree() {\n      if (customTextRenderer) {\n        // TODO: Document why this is necessary\n        return;\n      }\n\n      if (!page) {\n        return;\n      }\n\n      const cancellable = makeCancellable(page.getStructTree());\n      const runningTask = cancellable;\n\n      cancellable.promise\n        .then((nextStructTree) => {\n          structTreeDispatch({ type: 'RESOLVE', value: nextStructTree });\n        })\n        .catch((error) => {\n          structTreeDispatch({ type: 'REJECT', error });\n        });\n\n      return () => cancelRunningTask(runningTask);\n    },\n    [customTextRenderer, page, structTreeDispatch],\n  );\n\n  // biome-ignore lint/correctness/useExhaustiveDependencies: Omitted callbacks so they are not called every time they change\n  useEffect(() => {\n    if (structTree === undefined) {\n      return;\n    }\n\n    if (structTree === false) {\n      onLoadError();\n      return;\n    }\n\n    onLoadSuccess();\n  }, [structTree]);\n\n  if (!structTree) {\n    return null;\n  }\n\n  return <StructTreeItem className=\"react-pdf__Page__structTree structTree\" node={structTree} />;\n}\n"
  },
  {
    "path": "packages/react-pdf/src/StructTreeItem.tsx",
    "content": "import { useMemo } from 'react';\n\nimport {\n  getAttributes,\n  isStructTreeNode,\n  isStructTreeNodeWithOnlyContentChild,\n} from './shared/structTreeUtils.js';\n\nimport type { StructTreeContent } from 'pdfjs-dist/types/src/display/api.js';\nimport type { StructTreeNodeWithExtraAttributes } from './shared/types.js';\n\ntype StructTreeItemProps = {\n  className?: string;\n  node: StructTreeNodeWithExtraAttributes | StructTreeContent;\n};\n\nexport default function StructTreeItem({\n  className,\n  node,\n}: StructTreeItemProps): React.ReactElement {\n  const attributes = useMemo(() => getAttributes(node), [node]);\n\n  const children = useMemo(() => {\n    if (!isStructTreeNode(node)) {\n      return null;\n    }\n\n    if (isStructTreeNodeWithOnlyContentChild(node)) {\n      return null;\n    }\n\n    return node.children.map((child, index) => {\n      return (\n        // biome-ignore lint/suspicious/noArrayIndexKey: index is stable here\n        <StructTreeItem key={index} node={child} />\n      );\n    });\n  }, [node]);\n\n  return (\n    <span className={className} {...attributes}>\n      {children}\n    </span>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/Thumbnail.spec.tsx",
    "content": "import { beforeAll, describe, expect, it, vi } from 'vitest';\nimport { page, userEvent } from 'vitest/browser';\nimport { render } from 'vitest-browser-react';\nimport { createRef } from 'react';\n\nimport DocumentContext from './DocumentContext.js';\nimport { pdfjs } from './index.test.js';\nimport LinkService from './LinkService.js';\nimport Thumbnail from './Thumbnail.js';\n\nimport failingPdf from '../../../__mocks__/_failing_pdf.js';\nimport silentlyFailingPdf from '../../../__mocks__/_silently_failing_pdf.js';\n\nimport { loadPDF, makeAsyncCallback, muteConsole, restoreConsole } from '../../../test-utils.js';\n\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\nimport type { DocumentContextType, PageCallback } from './shared/types.js';\n\nconst pdfFile = await loadPDF('../../__mocks__/_pdf.pdf');\nconst pdfFile2 = await loadPDF('../../__mocks__/_pdf2.pdf');\n\nasync function renderWithContext(children: React.ReactNode, context: Partial<DocumentContextType>) {\n  const { rerender, ...otherResult } = await render(\n    <DocumentContext.Provider value={context as DocumentContextType}>\n      {children}\n    </DocumentContext.Provider>,\n  );\n\n  return {\n    ...otherResult,\n    rerender: async (\n      nextChildren: React.ReactNode,\n      nextContext: Partial<DocumentContextType> = context,\n    ) =>\n      await rerender(\n        <DocumentContext.Provider value={nextContext as DocumentContextType}>\n          {nextChildren}\n        </DocumentContext.Provider>,\n      ),\n  };\n}\n\ndescribe('Thumbnail', () => {\n  // Loaded PDF file\n  let pdf: PDFDocumentProxy;\n  let pdf2: PDFDocumentProxy;\n\n  // Object with basic loaded page information that shall match after successful loading\n  const desiredLoadedThumbnail: Partial<PDFPageProxy> = {};\n  const desiredLoadedThumbnail2: Partial<PDFPageProxy> = {};\n  const desiredLoadedThumbnail3: Partial<PDFPageProxy> = {};\n\n  const linkService = new LinkService();\n\n  beforeAll(async () => {\n    pdf = await pdfjs.getDocument({ data: pdfFile.arrayBuffer }).promise;\n\n    const page = await pdf.getPage(1);\n    desiredLoadedThumbnail._pageIndex = page._pageIndex;\n    desiredLoadedThumbnail._pageInfo = page._pageInfo;\n\n    const page2 = await pdf.getPage(2);\n    desiredLoadedThumbnail2._pageIndex = page2._pageIndex;\n    desiredLoadedThumbnail2._pageInfo = page2._pageInfo;\n\n    pdf2 = await pdfjs.getDocument({ data: pdfFile2.arrayBuffer }).promise;\n\n    const page3 = await pdf2.getPage(1);\n    desiredLoadedThumbnail3._pageIndex = page3._pageIndex;\n    desiredLoadedThumbnail3._pageInfo = page3._pageInfo;\n  });\n\n  describe('loading', () => {\n    it('loads a page and calls onLoadSuccess callback properly when placed inside Document', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />, { pdf });\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedThumbnail]);\n    });\n\n    it('loads a page and calls onLoadSuccess callback properly when pdf prop is passed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await render(<Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} pdf={pdf} />);\n\n      expect.assertions(1);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedThumbnail]);\n    });\n\n    it('returns all desired parameters in onLoadSuccess callback', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n\n      await renderWithContext(<Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />, { pdf });\n\n      expect.assertions(5);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page.width).toBeDefined();\n      expect(page.height).toBeDefined();\n      expect(page.originalWidth).toBeDefined();\n      expect(page.originalHeight).toBeDefined();\n      // Example of a method that got stripped away in the past\n      expect(page.getTextContent).toBeInstanceOf(Function);\n    });\n\n    it('calls onLoadError when failed to load a page', async () => {\n      const { func: onLoadError, promise: onLoadErrorPromise } = makeAsyncCallback();\n\n      muteConsole();\n\n      await renderWithContext(<Thumbnail onLoadError={onLoadError} pageIndex={0} />, {\n        pdf: failingPdf,\n      });\n\n      expect.assertions(1);\n\n      await expect(onLoadErrorPromise).resolves.toMatchObject([expect.any(Error)]);\n\n      restoreConsole();\n    });\n\n    it('loads page when given pageIndex', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />, { pdf });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedThumbnail);\n    });\n\n    it('loads page when given pageNumber', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(<Thumbnail onLoadSuccess={onLoadSuccess} pageNumber={1} />, { pdf });\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedThumbnail);\n    });\n\n    it('loads page of a given number when given conflicting pageNumber and pageIndex', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={1} pageNumber={1} />,\n        {\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedThumbnail);\n    });\n\n    it('replaces a page properly when pdf is changed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          pdf,\n        },\n      );\n\n      expect.assertions(2);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedThumbnail]);\n\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<Thumbnail onLoadSuccess={onLoadSuccess2} pageIndex={0} />, { pdf: pdf2 });\n\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedThumbnail3]);\n    });\n\n    it('replaces a page properly when pageNumber is changed', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { rerender } = await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        {\n          pdf,\n        },\n      );\n\n      expect.assertions(2);\n\n      await expect(onLoadSuccessPromise).resolves.toMatchObject([desiredLoadedThumbnail]);\n\n      const { func: onLoadSuccess2, promise: onLoadSuccessPromise2 } = makeAsyncCallback();\n\n      await rerender(<Thumbnail onLoadSuccess={onLoadSuccess2} pageIndex={1} />, { pdf });\n\n      await expect(onLoadSuccessPromise2).resolves.toMatchObject([desiredLoadedThumbnail2]);\n    });\n\n    it('throws an error when placed outside Document without pdf prop passed', async () => {\n      muteConsole();\n\n      await expect(render(<Thumbnail pageIndex={0} />)).rejects.toThrowError(\n        'Invariant failed: Attempted to load a thumbnail, but no document was specified. Wrap <Thumbnail /> in a <Document /> or pass explicit `pdf` prop.',\n      );\n\n      restoreConsole();\n    });\n  });\n\n  describe('rendering', () => {\n    it('applies className to its wrapper when given a string', async () => {\n      const className = 'testClassName';\n\n      const { container } = await renderWithContext(\n        <Thumbnail className={className} pageIndex={0} />,\n        {\n          pdf,\n        },\n      );\n\n      const wrapper = container.querySelector('.react-pdf__Thumbnail');\n\n      expect(wrapper).toHaveClass(className);\n    });\n\n    it('passes container element to inputRef properly', async () => {\n      const inputRef = createRef<HTMLDivElement>();\n\n      await renderWithContext(<Thumbnail inputRef={inputRef} pageIndex={1} />, {\n        pdf: silentlyFailingPdf,\n      });\n\n      expect(inputRef.current).toBeInstanceOf(HTMLDivElement);\n    });\n\n    it('passes canvas element to ThumbnailCanvas properly', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const canvasRef = createRef<HTMLCanvasElement>();\n\n      const { container } = await renderWithContext(\n        <Thumbnail canvasRef={canvasRef} onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Thumbnail__page__canvas');\n\n      expect(canvasRef.current).toBe(pageCanvas);\n    });\n\n    it('renders \"No page specified.\" when given neither pageIndex nor pageNumber', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Thumbnail />, { pdf });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('No page specified.');\n\n      restoreConsole();\n    });\n\n    it('renders custom no data message when given nothing and noData is given', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Thumbnail noData=\"Nothing here\" />, { pdf });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n\n      restoreConsole();\n    });\n\n    it('renders custom no data message when given nothing and noData is given as a function', async () => {\n      muteConsole();\n\n      const { container } = await renderWithContext(<Thumbnail noData={() => 'Nothing here'} />, {\n        pdf,\n      });\n\n      const noData = container.querySelector('.react-pdf__message');\n\n      expect(noData).toBeInTheDocument();\n      expect(noData).toHaveTextContent('Nothing here');\n\n      restoreConsole();\n    });\n\n    it('renders \"Loading page…\" when loading a page', () => {\n      renderWithContext(<Thumbnail pageIndex={0} />, { pdf });\n\n      const loading = page.getByText('Loading page…');\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a page and loading prop is given', () => {\n      renderWithContext(<Thumbnail loading=\"Loading\" pageIndex={0} />, { pdf });\n\n      const loading = page.getByText('Loading', { exact: true });\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('renders custom loading message when loading a page and loading prop is given as a function', () => {\n      renderWithContext(<Thumbnail loading={() => 'Loading'} pageIndex={0} />, { pdf });\n\n      const loading = page.getByText('Loading', { exact: true });\n\n      expect(loading).toBeInTheDocument();\n    });\n\n    it('ignores pageIndex when given pageIndex and pageNumber', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={1} pageNumber={1} />,\n        {\n          pdf,\n        },\n      );\n\n      expect.assertions(1);\n\n      const [page] = await onLoadSuccessPromise;\n\n      expect(page).toMatchObject(desiredLoadedThumbnail);\n    });\n\n    it('requests page to be rendered with default rotation when given nothing', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n\n      const { container } = await renderWithContext(\n        <Thumbnail onRenderSuccess={onRenderSuccess} pageIndex={0} />,\n        { pdf },\n      );\n\n      const [page] = await onRenderSuccessPromise;\n\n      const pageCanvas = container.querySelector(\n        '.react-pdf__Thumbnail__page__canvas',\n      ) as HTMLCanvasElement;\n\n      const { width, height } = window.getComputedStyle(pageCanvas);\n\n      const viewport = page.getViewport({ scale: 1 });\n\n      // Expect the canvas layer not to be rotated\n      expect(Number.parseInt(width, 10)).toBe(Math.floor(viewport.width));\n      expect(Number.parseInt(height, 10)).toBe(Math.floor(viewport.height));\n    });\n\n    it('requests page to be rendered with given rotation when given rotate prop', async () => {\n      const { func: onRenderSuccess, promise: onRenderSuccessPromise } =\n        makeAsyncCallback<[PageCallback]>();\n      const rotate = 90;\n\n      const { container } = await renderWithContext(\n        <Thumbnail onRenderSuccess={onRenderSuccess} pageIndex={0} rotate={rotate} />,\n        { pdf },\n      );\n\n      const [page] = await onRenderSuccessPromise;\n\n      const pageCanvas = container.querySelector(\n        '.react-pdf__Thumbnail__page__canvas',\n      ) as HTMLCanvasElement;\n\n      const { width, height } = window.getComputedStyle(pageCanvas);\n\n      const viewport = page.getViewport({ scale: 1, rotation: rotate });\n\n      // Expect the canvas layer to be rotated\n      expect(Number.parseInt(width, 10)).toBe(Math.floor(viewport.width));\n      expect(Number.parseInt(height, 10)).toBe(Math.floor(viewport.height));\n    });\n\n    it('requests page to be rendered in canvas mode by default', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Thumbnail__page__canvas');\n\n      expect(pageCanvas).toBeInTheDocument();\n    });\n\n    it('requests page not to be rendered when given renderMode = \"none\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} renderMode=\"none\" />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Thumbnail__page__canvas');\n\n      expect(pageCanvas).not.toBeInTheDocument();\n    });\n\n    it('requests page to be rendered in canvas mode when given renderMode = \"canvas\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      const { container } = await renderWithContext(\n        <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} renderMode=\"canvas\" />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const pageCanvas = container.querySelector('.react-pdf__Thumbnail__page__canvas');\n\n      expect(pageCanvas).toBeInTheDocument();\n    });\n\n    it('requests page to be rendered in custom mode when given renderMode = \"custom\"', async () => {\n      const { func: onLoadSuccess, promise: onLoadSuccessPromise } = makeAsyncCallback();\n\n      function CustomRenderer() {\n        return <div className=\"custom-renderer\" />;\n      }\n\n      const { container } = await renderWithContext(\n        <Thumbnail\n          customRenderer={CustomRenderer}\n          onLoadSuccess={onLoadSuccess}\n          pageIndex={0}\n          renderMode=\"custom\"\n        />,\n        { pdf },\n      );\n\n      expect.assertions(1);\n\n      await onLoadSuccessPromise;\n\n      const customRenderer = container.querySelector('.custom-renderer');\n\n      expect(customRenderer).toBeInTheDocument();\n    });\n  });\n\n  it('requests page to be rendered at its original size given nothing', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n\n    await renderWithContext(<Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} />, { pdf });\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(page.originalWidth);\n  });\n\n  it('requests page to be rendered with a proper scale when given scale', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(page.originalWidth * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given width', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n\n    await renderWithContext(\n      <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} width={width} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(width);\n  });\n\n  it('requests page to be rendered with a proper scale when given width and scale (multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Thumbnail onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} width={width} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toBeCloseTo(width * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given height', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const height = 850;\n\n    await renderWithContext(\n      <Thumbnail height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.height).toEqual(height);\n  });\n\n  it('requests page to be rendered with a proper scale when given height and scale (multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const height = 850;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Thumbnail height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} scale={scale} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(1);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.height).toBeCloseTo(height * scale);\n  });\n\n  it('requests page to be rendered with a proper scale when given width and height (ignores height)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const height = 100;\n\n    await renderWithContext(\n      <Thumbnail height={height} onLoadSuccess={onLoadSuccess} pageIndex={0} width={width} />,\n      {\n        pdf,\n      },\n    );\n\n    expect.assertions(2);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toEqual(width);\n    // Expect proportions to be correct even though invalid height was provided\n    expect(page.height).toEqual(page.originalHeight * (page.width / page.originalWidth));\n  });\n\n  it('requests page to be rendered with a proper scale when given width, height and scale (ignores height, multiplies)', async () => {\n    const { func: onLoadSuccess, promise: onLoadSuccessPromise } =\n      makeAsyncCallback<[PageCallback]>();\n    const width = 600;\n    const height = 100;\n    const scale = 1.5;\n\n    await renderWithContext(\n      <Thumbnail\n        height={height}\n        onLoadSuccess={onLoadSuccess}\n        pageIndex={0}\n        scale={scale}\n        width={width}\n      />,\n      { pdf },\n    );\n\n    expect.assertions(2);\n\n    const [page] = await onLoadSuccessPromise;\n\n    expect(page.width).toBeCloseTo(width * scale);\n    // Expect proportions to be correct even though invalid height was provided\n    expect(page.height).toEqual(page.originalHeight * (page.width / page.originalWidth));\n  });\n\n  it('calls onClick callback when clicked a page (sample of mouse events family)', async () => {\n    const onClick = vi.fn();\n\n    const { container } = await renderWithContext(<Thumbnail onClick={onClick} />, {\n      linkService,\n      pdf,\n    });\n\n    const page = container.querySelector('.react-pdf__Thumbnail__page') as HTMLDivElement;\n    await userEvent.click(page);\n\n    expect(onClick).toHaveBeenCalled();\n  });\n\n  function triggerTouchStart(element: HTMLElement) {\n    element.dispatchEvent(new TouchEvent('touchstart', { bubbles: true, cancelable: true }));\n  }\n\n  it('calls onTouchStart callback when touched a page (sample of touch events family)', async () => {\n    const onTouchStart = vi.fn();\n\n    const { container } = await renderWithContext(<Thumbnail onTouchStart={onTouchStart} />, {\n      linkService,\n      pdf,\n    });\n\n    const page = container.querySelector('.react-pdf__Thumbnail__page') as HTMLDivElement;\n    triggerTouchStart(page);\n\n    expect(onTouchStart).toHaveBeenCalled();\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/Thumbnail.tsx",
    "content": "'use client';\n\nimport clsx from 'clsx';\nimport invariant from 'tiny-invariant';\n\nimport Page from './Page.js';\n\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\n\nimport { isProvided } from './shared/utils.js';\n\nimport type { PageProps } from './Page.js';\nimport type { ClassName, OnItemClickArgs } from './shared/types.js';\n\nexport type ThumbnailProps = Omit<\n  PageProps,\n  | 'className'\n  | 'customTextRenderer'\n  | 'onGetAnnotationsError'\n  | 'onGetAnnotationsSuccess'\n  | 'onGetTextError'\n  | 'onGetTextSuccess'\n  | 'onRenderAnnotationLayerError'\n  | 'onRenderAnnotationLayerSuccess'\n  | 'onRenderTextLayerError'\n  | 'onRenderTextLayerSuccess'\n  | 'renderAnnotationLayer'\n  | 'renderForms'\n  | 'renderTextLayer'\n> & {\n  /**\n   * Class name(s) that will be added to rendered element along with the default `react-pdf__Thumbnail`.\n   *\n   * @example 'custom-class-name-1 custom-class-name-2'\n   * @example ['custom-class-name-1', 'custom-class-name-2']\n   */\n  className?: ClassName;\n  /**\n   * Function called when a thumbnail has been clicked. Usually, you would like to use this callback to move the user wherever they requested to.\n   *\n   * @example ({ dest, pageIndex, pageNumber }) => alert('Clicked an item from page ' + pageNumber + '!')\n   */\n  onItemClick?: (args: OnItemClickArgs) => void;\n};\n\n/**\n * Displays a thumbnail of a page. Does not render the annotation layer or the text layer. Does not register itself as a link target, so the user will not be scrolled to a Thumbnail component when clicked on an internal link (e.g. in Table of Contents). When clicked, attempts to navigate to the page clicked (similarly to a link in Outline).\n *\n * Should be placed inside `<Document />`. Alternatively, it can have `pdf` prop passed, which can be obtained from `<Document />`'s `onLoadSuccess` callback function.\n */\nexport default function Thumbnail(props: ThumbnailProps): React.ReactElement {\n  const documentContext = useDocumentContext();\n\n  const mergedProps = { ...documentContext, ...props };\n  const {\n    className,\n    linkService,\n    onItemClick,\n    pageIndex: pageIndexProps,\n    pageNumber: pageNumberProps,\n    pdf,\n  } = mergedProps;\n\n  invariant(\n    pdf,\n    'Attempted to load a thumbnail, but no document was specified. Wrap <Thumbnail /> in a <Document /> or pass explicit `pdf` prop.',\n  );\n\n  const pageIndex = isProvided(pageNumberProps) ? pageNumberProps - 1 : (pageIndexProps ?? null);\n\n  const pageNumber = pageNumberProps ?? (isProvided(pageIndexProps) ? pageIndexProps + 1 : null);\n\n  function onClick(event: React.MouseEvent<HTMLAnchorElement>) {\n    event.preventDefault();\n\n    if (!isProvided(pageIndex) || !pageNumber) {\n      return;\n    }\n\n    invariant(\n      onItemClick || linkService,\n      'Either onItemClick callback or linkService must be defined in order to navigate to an outline item.',\n    );\n\n    if (onItemClick) {\n      onItemClick({\n        pageIndex,\n        pageNumber,\n      });\n    } else if (linkService) {\n      linkService.goToPage(pageNumber);\n    }\n  }\n\n  const { className: classNameProps, onItemClick: onItemClickProps, ...pageProps } = props;\n\n  return (\n    <a\n      className={clsx('react-pdf__Thumbnail', className)}\n      href={pageNumber ? '#' : undefined}\n      onClick={onClick}\n    >\n      <Page\n        {...pageProps}\n        _className=\"react-pdf__Thumbnail__page\"\n        _enableRegisterUnregisterPage={false}\n        renderAnnotationLayer={false}\n        renderTextLayer={false}\n      />\n    </a>\n  );\n}\n"
  },
  {
    "path": "packages/react-pdf/src/index.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\n\nimport { Document, Outline, Page, pdfjs, Thumbnail } from './index.js';\n\ndescribe('default entry', () => {\n  describe('has pdfjs exported properly', () => {\n    it('has pdfjs.version exported properly', () => {\n      expect(typeof pdfjs.version).toBe('string');\n    });\n\n    it('has GlobalWorkerOptions exported properly', () => {\n      expect(typeof pdfjs.GlobalWorkerOptions).toBe('function');\n    });\n  });\n\n  it('has Document exported properly', () => {\n    expect(Document).toBeInstanceOf(Object);\n  });\n\n  it('has Outline exported properly', () => {\n    expect(Outline).toBeInstanceOf(Object);\n  });\n\n  it('has Page exported properly', () => {\n    expect(Page).toBeInstanceOf(Object);\n  });\n\n  it('has Thumbnail exported properly', () => {\n    expect(Thumbnail).toBeInstanceOf(Object);\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/index.test.ts",
    "content": "import * as pdfjs from 'pdfjs-dist';\n\nimport Document from './Document.js';\nimport Outline from './Outline.js';\nimport Page from './Page.js';\nimport Thumbnail from './Thumbnail.js';\n\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\nimport useOutlineContext from './shared/hooks/useOutlineContext.js';\nimport usePageContext from './shared/hooks/usePageContext.js';\n\nexport type { DocumentProps } from './Document.js';\nexport type { OutlineProps } from './Outline.js';\nexport type { PageProps } from './Page.js';\nexport type { ThumbnailProps } from './Thumbnail.js';\n\nimport './pdf.worker.entry.js';\n\nexport {\n  pdfjs,\n  Document,\n  Outline,\n  Page,\n  Thumbnail,\n  useDocumentContext,\n  useOutlineContext,\n  usePageContext,\n};\n"
  },
  {
    "path": "packages/react-pdf/src/index.ts",
    "content": "import * as pdfjs from 'pdfjs-dist';\n\nimport Document from './Document.js';\nimport Outline from './Outline.js';\nimport Page from './Page.js';\nimport PasswordResponses from './PasswordResponses.js';\nimport Thumbnail from './Thumbnail.js';\n\nimport useDocumentContext from './shared/hooks/useDocumentContext.js';\nimport useOutlineContext from './shared/hooks/useOutlineContext.js';\nimport usePageContext from './shared/hooks/usePageContext.js';\n\nimport type LinkService from './LinkService.js';\n\nexport type { DocumentProps } from './Document.js';\nexport type { OutlineProps } from './Outline.js';\nexport type { PageProps } from './Page.js';\nexport type {\n  PasswordResponses as PasswordResponsesType,\n  StructTreeNode,\n  TextContent,\n  TextItem,\n  TextMarkedContent,\n} from './shared/types.js';\nexport type { ThumbnailProps } from './Thumbnail.js';\nexport type { LinkService };\n\nimport { displayWorkerWarning } from './shared/utils.js';\n\ndisplayWorkerWarning();\n\npdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.mjs';\n\nexport {\n  pdfjs,\n  Document,\n  Outline,\n  Page,\n  Thumbnail,\n  useDocumentContext,\n  useOutlineContext,\n  usePageContext,\n  PasswordResponses,\n};\n"
  },
  {
    "path": "packages/react-pdf/src/pdf.worker.entry.ts",
    "content": "/**\n * PDF.js worker entry file.\n *\n * This file is identical to Mozilla's pdf.worker.entry.js, with one exception being placed inside\n * this bundle, not theirs.\n */\n\n(\n  (typeof window !== 'undefined' ? window : {}) as Window &\n    typeof globalThis & { pdfjsWorker: unknown }\n).pdfjsWorker =\n  // @ts-expect-error - pdfjs-dist does not ship with types\n  await import('pdfjs-dist/build/pdf.worker.mjs');\n\nexport {};\n"
  },
  {
    "path": "packages/react-pdf/src/shared/constants.ts",
    "content": "// From pdfjs-dist/lib/web/struct_tree_layer_builder.js\n\nexport const PDF_ROLE_TO_HTML_ROLE = {\n  // Document level structure types\n  Document: null, // There's a \"document\" role, but it doesn't make sense here.\n  DocumentFragment: null,\n  // Grouping level structure types\n  Part: 'group',\n  Sect: 'group', // XXX: There's a \"section\" role, but it's abstract.\n  Div: 'group',\n  Aside: 'note',\n  NonStruct: 'none',\n  // Block level structure types\n  P: null,\n  // H<n>,\n  H: 'heading',\n  Title: null,\n  FENote: 'note',\n  // Sub-block level structure type\n  Sub: 'group',\n  // General inline level structure types\n  Lbl: null,\n  Span: null,\n  Em: null,\n  Strong: null,\n  Link: 'link',\n  Annot: 'note',\n  Form: 'form',\n  // Ruby and Warichu structure types\n  Ruby: null,\n  RB: null,\n  RT: null,\n  RP: null,\n  Warichu: null,\n  WT: null,\n  WP: null,\n  // List standard structure types\n  L: 'list',\n  LI: 'listitem',\n  LBody: null,\n  // Table standard structure types\n  Table: 'table',\n  TR: 'row',\n  TH: 'columnheader',\n  TD: 'cell',\n  THead: 'columnheader',\n  TBody: null,\n  TFoot: null,\n  // Standard structure type Caption\n  Caption: null,\n  // Standard structure type Figure\n  Figure: 'figure',\n  // Standard structure type Formula\n  Formula: null,\n  // standard structure type Artifact\n  Artifact: null,\n};\n\nexport const HEADING_PATTERN: RegExp = /^H(\\d+)$/;\n"
  },
  {
    "path": "packages/react-pdf/src/shared/hooks/useCachedValue.ts",
    "content": "'use client';\n\nimport { useRef } from 'react';\n\nimport { isDefined } from '../utils.js';\n\nexport default function useCachedValue<T>(getter: () => T): () => T {\n  const ref = useRef<T | undefined>(undefined);\n\n  const currentValue = ref.current;\n\n  if (isDefined(currentValue)) {\n    return () => currentValue;\n  }\n\n  return () => {\n    const value = getter();\n\n    ref.current = value;\n\n    return value;\n  };\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/hooks/useDocumentContext.ts",
    "content": "import { useContext } from 'react';\n\nimport DocumentContext from '../../DocumentContext.js';\n\nimport type { DocumentContextType } from '../types.js';\n\nexport default function useDocumentContext(): DocumentContextType {\n  return useContext(DocumentContext);\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/hooks/useOutlineContext.ts",
    "content": "import { useContext } from 'react';\n\nimport OutlineContext from '../../OutlineContext.js';\n\nimport type { OutlineContextType } from '../types.js';\n\nexport default function useOutlineContext(): OutlineContextType {\n  return useContext(OutlineContext);\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/hooks/usePageContext.ts",
    "content": "import { useContext } from 'react';\n\nimport PageContext from '../../PageContext.js';\n\nimport type { PageContextType } from '../types.js';\n\nexport default function usePageContext(): PageContextType {\n  return useContext(PageContext);\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/hooks/useResolver.ts",
    "content": "import { useReducer } from 'react';\n\ntype State<T> =\n  | { value: T; error: undefined }\n  | { value: false; error: Error }\n  | { value: undefined; error: undefined };\n\ntype Action<T> =\n  | { type: 'RESOLVE'; value: T }\n  | { type: 'REJECT'; error: Error }\n  | { type: 'RESET' };\n\nfunction reducer<T>(state: State<T>, action: Action<T>): State<T> {\n  switch (action.type) {\n    case 'RESOLVE':\n      return { value: action.value, error: undefined };\n    case 'REJECT':\n      return { value: false, error: action.error };\n    case 'RESET':\n      return { value: undefined, error: undefined };\n    default:\n      return state;\n  }\n}\n\nexport default function useResolver<T>(): [State<T>, React.Dispatch<Action<T>>] {\n  return useReducer(reducer<T>, { value: undefined, error: undefined });\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/structTreeUtils.ts",
    "content": "import { HEADING_PATTERN, PDF_ROLE_TO_HTML_ROLE } from './constants.js';\n\nimport type { StructTreeContent, StructTreeNode } from 'pdfjs-dist/types/src/display/api.js';\nimport type { StructTreeNodeWithExtraAttributes } from './types.js';\n\ntype PdfRole = keyof typeof PDF_ROLE_TO_HTML_ROLE;\n\ntype Attributes = React.HTMLAttributes<HTMLElement>;\n\nexport function isPdfRole(role: string): role is PdfRole {\n  return role in PDF_ROLE_TO_HTML_ROLE;\n}\n\nexport function isStructTreeNode(node: StructTreeNode | StructTreeContent): node is StructTreeNode {\n  return 'children' in node;\n}\n\nexport function isStructTreeNodeWithOnlyContentChild(\n  node: StructTreeNode | StructTreeContent,\n): boolean {\n  if (!isStructTreeNode(node)) {\n    return false;\n  }\n\n  return node.children.length === 1 && 0 in node.children && 'id' in node.children[0];\n}\n\nexport function getRoleAttributes(node: StructTreeNode | StructTreeContent): Attributes {\n  const attributes: Attributes = {};\n\n  if (isStructTreeNode(node)) {\n    const { role } = node;\n\n    const matches = role.match(HEADING_PATTERN);\n\n    if (matches) {\n      attributes.role = 'heading';\n      attributes['aria-level'] = Number(matches[1]);\n    } else if (isPdfRole(role)) {\n      const htmlRole = PDF_ROLE_TO_HTML_ROLE[role];\n\n      if (htmlRole) {\n        attributes.role = htmlRole;\n      }\n    }\n  }\n\n  return attributes;\n}\n\nexport function getBaseAttributes(\n  node: StructTreeNodeWithExtraAttributes | StructTreeContent,\n): Attributes {\n  const attributes: Attributes = {};\n\n  if (isStructTreeNode(node)) {\n    if (node.alt !== undefined) {\n      attributes['aria-label'] = node.alt;\n    }\n\n    if (node.lang !== undefined) {\n      attributes.lang = node.lang;\n    }\n\n    if (isStructTreeNodeWithOnlyContentChild(node)) {\n      const [child] = node.children;\n\n      if (child) {\n        const childAttributes = getBaseAttributes(child);\n\n        return {\n          ...attributes,\n          ...childAttributes,\n        };\n      }\n    }\n  } else if ('id' in node) {\n    attributes['aria-owns'] = node.id;\n  }\n\n  return attributes;\n}\n\nexport function getAttributes(\n  node: StructTreeNodeWithExtraAttributes | StructTreeContent,\n): Attributes | null {\n  if (!node) {\n    return null;\n  }\n\n  return {\n    ...getRoleAttributes(node),\n    ...getBaseAttributes(node),\n  };\n}\n"
  },
  {
    "path": "packages/react-pdf/src/shared/types.ts",
    "content": "import type {\n  PasswordResponses,\n  PDFDataRangeTransport,\n  PDFDocumentProxy,\n  PDFPageProxy,\n} from 'pdfjs-dist';\nimport type { AnnotationLayerParameters } from 'pdfjs-dist/types/src/display/annotation_layer.js';\nimport type {\n  DocumentInitParameters,\n  RefProxy,\n  StructTreeNode,\n  TextContent,\n  TextItem,\n  TextMarkedContent,\n  TypedArray,\n} from 'pdfjs-dist/types/src/display/api.js';\nimport type LinkService from '../LinkService.js';\n\nexport type { PasswordResponses, StructTreeNode, TextContent, TextItem, TextMarkedContent };\n\ntype NullableObject<T extends object> = { [P in keyof T]: T[P] | null };\n\ntype KeyOfUnion<T> = T extends unknown ? keyof T : never;\n\n/* Primitive types */\nexport type Annotations = AnnotationLayerParameters['annotations'];\n\nexport type ClassName = string | null | undefined | (string | null | undefined)[];\n\nexport type ResolvedDest = (RefProxy | number)[];\n\nexport type Dest = Promise<ResolvedDest> | ResolvedDest | string | null;\n\nexport type ExternalLinkRel = string;\n\nexport type ExternalLinkTarget = '_self' | '_blank' | '_parent' | '_top';\n\nexport type FilterAnnotationsArgs = {\n  annotations: Annotations;\n};\n\nexport type ImageResourcesPath = string;\n\nexport type OnError = (error: Error) => void;\n\nexport type OnItemClickArgs = {\n  dest?: Dest;\n  pageIndex: number;\n  pageNumber: number;\n};\n\nexport type OnLoadProgressArgs = {\n  loaded: number;\n  total: number;\n};\n\nexport type RegisterPage = (pageIndex: number, ref: HTMLDivElement) => void;\n\nexport type RenderMode = 'canvas' | 'custom' | 'none';\n\nexport type ScrollPageIntoViewArgs = {\n  dest?: ResolvedDest;\n  pageIndex?: number;\n  pageNumber: number;\n};\n\ntype BinaryData = TypedArray | ArrayBuffer | number[] | string;\n\nexport type Source =\n  | { data: BinaryData | undefined }\n  | { range: PDFDataRangeTransport }\n  | { url: string };\n\nexport type UnregisterPage = (pageIndex: number) => void;\n\n/* Complex types */\nexport type CustomRenderer = React.FunctionComponent | React.ComponentClass;\n\nexport type CustomTextRenderer = (\n  props: { pageIndex: number; pageNumber: number; itemIndex: number } & TextItem,\n) => string;\n\nexport type DocumentCallback = PDFDocumentProxy;\n\nexport type File = string | ArrayBuffer | Blob | Source | null;\n\nexport type PageCallback = PDFPageProxy & {\n  width: number;\n  height: number;\n  originalWidth: number;\n  originalHeight: number;\n};\n\nexport type NodeOrRenderer = React.ReactNode | (() => React.ReactNode);\n\nexport type FilterAnnotations = (args: FilterAnnotationsArgs) => Annotations;\n\nexport type OnDocumentLoadError = OnError;\n\nexport type OnDocumentLoadProgress = (args: OnLoadProgressArgs) => void;\n\nexport type OnDocumentLoadSuccess = (document: DocumentCallback) => void;\n\nexport type OnGetAnnotationsError = OnError;\n\nexport type OnGetAnnotationsSuccess = (annotations: Annotations) => void;\n\nexport type OnGetStructTreeError = OnError;\n\nexport type OnGetStructTreeSuccess = (tree: StructTreeNode) => void;\n\nexport type OnGetTextError = OnError;\n\nexport type OnGetTextSuccess = (textContent: TextContent) => void;\n\nexport type OnPageLoadError = OnError;\n\nexport type OnPageLoadSuccess = (page: PageCallback) => void;\n\nexport type OnPasswordCallback = (password: string | null) => void;\n\nexport type OnRenderAnnotationLayerError = (error: unknown) => void;\n\nexport type OnRenderAnnotationLayerSuccess = () => void;\n\nexport type OnRenderError = OnError;\n\nexport type OnRenderSuccess = (page: PageCallback) => void;\n\nexport type OnRenderTextLayerError = OnError;\n\nexport type OnRenderTextLayerSuccess = () => void;\n\nexport type PasswordResponse = (typeof PasswordResponses)[keyof typeof PasswordResponses];\n\nexport type Options = NullableObject<Omit<DocumentInitParameters, KeyOfUnion<Source>>>;\n\nexport type PageColors = {\n  background: string;\n  foreground: string;\n};\n\n/* Context types */\nexport type DocumentContextType = {\n  imageResourcesPath?: ImageResourcesPath;\n  linkService: LinkService;\n  onItemClick?: (args: OnItemClickArgs) => void;\n  pdf?: PDFDocumentProxy | false;\n  registerPage: RegisterPage;\n  renderMode?: RenderMode;\n  rotate?: number | null;\n  unregisterPage: UnregisterPage;\n} | null;\n\nexport type PageContextType = {\n  _className?: string;\n  canvasBackground?: string;\n  customTextRenderer?: CustomTextRenderer;\n  devicePixelRatio?: number;\n  filterAnnotations?: FilterAnnotations;\n  onGetAnnotationsError?: OnGetAnnotationsError;\n  onGetAnnotationsSuccess?: OnGetAnnotationsSuccess;\n  onGetStructTreeError?: OnGetStructTreeError;\n  onGetStructTreeSuccess?: OnGetStructTreeSuccess;\n  onGetTextError?: OnGetTextError;\n  onGetTextSuccess?: OnGetTextSuccess;\n  onRenderAnnotationLayerError?: OnRenderAnnotationLayerError;\n  onRenderAnnotationLayerSuccess?: OnRenderAnnotationLayerSuccess;\n  onRenderError?: OnRenderError;\n  onRenderSuccess?: OnRenderSuccess;\n  onRenderTextLayerError?: OnRenderTextLayerError;\n  onRenderTextLayerSuccess?: OnRenderTextLayerSuccess;\n  page: PDFPageProxy | false | undefined;\n  pageColors?: PageColors;\n  pageIndex: number;\n  pageNumber: number;\n  renderForms: boolean;\n  renderTextLayer: boolean;\n  rotate: number;\n  scale: number;\n} | null;\n\nexport type OutlineContextType = {\n  onItemClick?: (args: OnItemClickArgs) => void;\n} | null;\n\nexport type StructTreeNodeWithExtraAttributes = StructTreeNode & {\n  alt?: string;\n  lang?: string;\n};\n\nexport type DocumentRenderProps = Omit<NonNullable<DocumentContextType>, 'pdf'> & {\n  pdf: PDFDocumentProxy;\n};\n\nexport type PageRenderProps = Omit<NonNullable<PageContextType>, 'page'> & {\n  page: PDFPageProxy;\n};\n"
  },
  {
    "path": "packages/react-pdf/src/shared/utils.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\n\nimport { dataURItoByteString, isDataURI } from './utils.js';\n\ndescribe('isDataURI()', () => {\n  it.each`\n    input                                            | expectedResult\n    ${'potato'}                                      | ${false}\n    ${'data:,Hello%2C%20world%21'}                   | ${true}\n    ${'data:text/plain;base64,SGVsbG8sIHdvcmxkIQ=='} | ${true}\n  `('returns $expectedResult given $input', ({ input, expectedResult }) => {\n    const result = isDataURI(input);\n\n    expect(result).toBe(expectedResult);\n  });\n});\n\ndescribe('dataURItoByteString()', () => {\n  it('throws given invalid data URI', () => {\n    expect(() => dataURItoByteString('potato')).toThrow();\n  });\n\n  it('returns a byte string given plain text data URI', () => {\n    const result = dataURItoByteString('data:,Hello%2C%20world%21');\n\n    expect(result).toBe('Hello, world!');\n  });\n\n  it('returns a byte string given base64 data URI', () => {\n    const result = dataURItoByteString('data:text/plain;base64,SGVsbG8sIHdvcmxkIQ==');\n\n    expect(result).toBe('Hello, world!');\n  });\n\n  it('returns a byte string given base64 PDF data URI', () => {\n    const result = dataURItoByteString(\n      'data:application/pdf;base64,JVBERi0xLg10cmFpbGVyPDwvUm9vdDw8L1BhZ2VzPDwvS2lkc1s8PC9NZWRpYUJveFswIDAgMyAzXT4+XT4+Pj4+Pg==',\n    );\n\n    expect(result).toBe('%PDF-1.\\rtrailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>');\n  });\n\n  it('returns a byte string given base64 PDF data URI with filename', () => {\n    const result = dataURItoByteString(\n      'data:application/pdf;filename=generated.pdf;base64,JVBERi0xLg10cmFpbGVyPDwvUm9vdDw8L1BhZ2VzPDwvS2lkc1s8PC9NZWRpYUJveFswIDAgMyAzXT4+XT4+Pj4+Pg==',\n    );\n\n    expect(result).toBe('%PDF-1.\\rtrailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>');\n  });\n});\n"
  },
  {
    "path": "packages/react-pdf/src/shared/utils.ts",
    "content": "import invariant from 'tiny-invariant';\nimport warning from 'warning';\n\nimport type { PDFPageProxy } from 'pdfjs-dist';\nimport type { PageCallback } from './types.js';\n\n/**\n * Checks if we're running in a browser environment.\n */\nexport const isBrowser: boolean = typeof window !== 'undefined';\n\n/**\n * Checks whether we're running from a local file system.\n */\nexport const isLocalFileSystem: boolean = isBrowser && window.location.protocol === 'file:';\n\n/**\n * Checks whether a variable is defined.\n *\n * @param {*} variable Variable to check\n */\nexport function isDefined<T>(variable: T | undefined): variable is T {\n  return typeof variable !== 'undefined';\n}\n\n/**\n * Checks whether a variable is defined and not null.\n *\n * @param {*} variable Variable to check\n */\nexport function isProvided<T>(variable: T | null | undefined): variable is T {\n  return isDefined(variable) && variable !== null;\n}\n\n/**\n * Checks whether a variable provided is a string.\n *\n * @param {*} variable Variable to check\n */\nexport function isString(variable: unknown): variable is string {\n  return typeof variable === 'string';\n}\n\n/**\n * Checks whether a variable provided is an ArrayBuffer.\n *\n * @param {*} variable Variable to check\n */\nexport function isArrayBuffer(variable: unknown): variable is ArrayBuffer {\n  return variable instanceof ArrayBuffer;\n}\n\n/**\n * Checks whether a variable provided is a Blob.\n *\n * @param {*} variable Variable to check\n */\nexport function isBlob(variable: unknown): variable is Blob {\n  invariant(isBrowser, 'isBlob can only be used in a browser environment');\n\n  return variable instanceof Blob;\n}\n\n/**\n * Checks whether a variable provided is a data URI.\n *\n * @param {*} variable String to check\n */\nexport function isDataURI(variable: unknown): variable is `data:${string}` {\n  return isString(variable) && /^data:/.test(variable);\n}\n\nexport function dataURItoByteString(dataURI: unknown): string {\n  invariant(isDataURI(dataURI), 'Invalid data URI.');\n\n  const [headersString = '', dataString = ''] = dataURI.split(',');\n  const headers = headersString.split(';');\n\n  if (headers.indexOf('base64') !== -1) {\n    return atob(dataString);\n  }\n\n  return unescape(dataString);\n}\n\nexport function getDevicePixelRatio(): number {\n  return (isBrowser && window.devicePixelRatio) || 1;\n}\n\nconst allowFileAccessFromFilesTip =\n  'On Chromium based browsers, you can use --allow-file-access-from-files flag for debugging purposes.';\n\nexport function displayCORSWarning(): void {\n  warning(\n    !isLocalFileSystem,\n    `Loading PDF as base64 strings/URLs may not work on protocols other than HTTP/HTTPS. ${allowFileAccessFromFilesTip}`,\n  );\n}\n\nexport function displayWorkerWarning(): void {\n  warning(\n    !isLocalFileSystem,\n    `Loading PDF.js worker may not work on protocols other than HTTP/HTTPS. ${allowFileAccessFromFilesTip}`,\n  );\n}\n\nexport function cancelRunningTask(runningTask?: { cancel?: () => void } | null): void {\n  if (runningTask?.cancel) runningTask.cancel();\n}\n\nexport function makePageCallback(page: PDFPageProxy, scale: number): PageCallback {\n  Object.defineProperty(page, 'width', {\n    get() {\n      return this.getViewport({ scale }).width;\n    },\n    configurable: true,\n  });\n  Object.defineProperty(page, 'height', {\n    get() {\n      return this.getViewport({ scale }).height;\n    },\n    configurable: true,\n  });\n  Object.defineProperty(page, 'originalWidth', {\n    get() {\n      return this.getViewport({ scale: 1 }).width;\n    },\n    configurable: true,\n  });\n  Object.defineProperty(page, 'originalHeight', {\n    get() {\n      return this.getViewport({ scale: 1 }).height;\n    },\n    configurable: true,\n  });\n  return page as PageCallback;\n}\n\nexport function isAbortException(error: Error): boolean {\n  return error.name === 'AbortException' || error.name === 'RenderingCancelledException';\n}\n\nexport function loadFromFile(file: Blob): Promise<ArrayBuffer> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n\n    reader.onload = () => {\n      if (!reader.result) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      resolve(reader.result as ArrayBuffer);\n    };\n\n    reader.onerror = (event) => {\n      if (!event.target) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      const { error } = event.target;\n\n      if (!error) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      switch (error.code) {\n        case error.NOT_FOUND_ERR:\n          return reject(new Error('Error while reading a file: File not found.'));\n        case error.SECURITY_ERR:\n          return reject(new Error('Error while reading a file: Security error.'));\n        case error.ABORT_ERR:\n          return reject(new Error('Error while reading a file: Aborted.'));\n        default:\n          return reject(new Error('Error while reading a file.'));\n      }\n    };\n\n    reader.readAsArrayBuffer(file);\n  });\n}\n"
  },
  {
    "path": "packages/react-pdf/tsconfig.build.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"src/**/*.spec.ts\", \"src/**/*.spec.tsx\"]\n}\n"
  },
  {
    "path": "packages/react-pdf/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"isolatedDeclarations\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"nodenext\",\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"outDir\": \"dist\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  },\n  \"exclude\": [\"dist\"]\n}\n"
  },
  {
    "path": "packages/react-pdf/vitest.config.ts",
    "content": "import { playwright } from '@vitest/browser-playwright';\nimport { configDefaults, defineConfig } from 'vitest/config';\n\nimport type { ViteUserConfig } from 'vitest/config';\n\nconst config: ViteUserConfig = defineConfig({\n  test: {\n    browser: {\n      enabled: true,\n      headless: true,\n      instances: [{ browser: 'chromium' }],\n      provider: playwright(),\n    },\n    exclude: [...configDefaults.exclude, 'src/index.test.ts'],\n    pool: 'forks',\n    setupFiles: 'vitest.setup.ts',\n    watch: false,\n  },\n});\n\nexport default config;\n"
  },
  {
    "path": "packages/react-pdf/vitest.setup.ts",
    "content": "document.body.style.setProperty('--react-pdf-annotation-layer', '1');\ndocument.body.style.setProperty('--react-pdf-text-layer', '1');\n"
  },
  {
    "path": "sample/next-app/.gitignore",
    "content": ".next\ndist\nnode_modules\n"
  },
  {
    "path": "sample/next-app/app/Sample.css",
    "content": "body {\n  margin: 0;\n  background-color: #525659;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Example input,\n.Example button {\n  font: inherit;\n}\n\n.Example header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Example header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Example__container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Example__container__load {\n  margin-top: 1em;\n  color: white;\n}\n\n.Example__container__document {\n  width: 100%;\n  max-width: calc(100% - 2em);\n  margin: 1em 0;\n}\n\n.Example__container__document .react-pdf__Document {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.Example__container__document .react-pdf__Page {\n  margin: 1em 0;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n}\n\n.Example__container__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n"
  },
  {
    "path": "sample/next-app/app/Sample.tsx",
    "content": "'use client';\n\nimport { useCallback, useId, useState } from 'react';\nimport { useResizeObserver } from '@wojtekmaj/react-hooks';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport './Sample.css';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nconst resizeObserverOptions = {};\n\nconst maxWidth = 800;\n\ntype PDFFile = string | File | null;\n\nexport default function Sample() {\n  const fileId = useId();\n  const [file, setFile] = useState<PDFFile>('./sample.pdf');\n  const [numPages, setNumPages] = useState<number>();\n  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);\n  const [containerWidth, setContainerWidth] = useState<number>();\n\n  const onResize = useCallback<ResizeObserverCallback>((entries) => {\n    const [entry] = entries;\n\n    if (entry) {\n      setContainerWidth(entry.contentRect.width);\n    }\n  }, []);\n\n  useResizeObserver(containerRef, resizeObserverOptions, onResize);\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    const nextFile = files?.[0];\n\n    if (nextFile) {\n      setFile(nextFile);\n    }\n  }\n\n  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {\n    setNumPages(nextNumPages);\n  }\n\n  return (\n    <div className=\"Example\">\n      <header>\n        <h1>react-pdf sample page</h1>\n      </header>\n      <div className=\"Example__container\">\n        <div className=\"Example__container__load\">\n          <label htmlFor={fileId}>Load from file:</label>{' '}\n          <input id={fileId} onChange={onFileChange} type=\"file\" />\n        </div>\n        <div className=\"Example__container__document\" ref={setContainerRef}>\n          <Document file={file} onLoadSuccess={onDocumentLoadSuccess} options={options}>\n            {Array.from(new Array(numPages), (_el, index) => (\n              <Page\n                key={`page_${index + 1}`}\n                pageNumber={index + 1}\n                width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}\n              />\n            ))}\n          </Document>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "sample/next-app/app/layout.tsx",
    "content": "export const metadata = {\n  title: 'react-pdf sample page',\n};\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en-US\">\n      <body>{children}</body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "sample/next-app/app/page.tsx",
    "content": "'use client';\n\nimport dynamic from 'next/dynamic';\n\nconst Sample = dynamic(() => import('./Sample'), {\n  ssr: false,\n});\n\nexport default function Page() {\n  return <Sample />;\n}\n"
  },
  {
    "path": "sample/next-app/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\nimport './.next/types/routes.d.ts';\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\n"
  },
  {
    "path": "sample/next-app/next.config.ts",
    "content": "import type { NextConfig } from 'next';\n\nconst nextConfig: NextConfig = {};\n\nexport default nextConfig;\n"
  },
  {
    "path": "sample/next-app/package.json",
    "content": "{\n  \"name\": \"react-pdf-sample-page-next\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A sample page for React-PDF.\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"next build\",\n    \"dev\": \"next dev\",\n    \"preview\": \"next preview\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@wojtekmaj/react-hooks\": \"^2.0.0\",\n    \"next\": \"^16.1.7\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"latest\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "sample/next-app/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"esModuleInterop\": true,\n    \"incremental\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"module\": \"esnext\",\n    \"moduleDetection\": \"force\",\n    \"moduleResolution\": \"bundler\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"plugins\": [{ \"name\": \"next\" }],\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  },\n  \"include\": [\".\", \".next/types/**/*.ts\", \".next/dev/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "sample/next-pages/.gitignore",
    "content": ".next\ndist\nnode_modules\n"
  },
  {
    "path": "sample/next-pages/empty-module.ts",
    "content": "export {};\n"
  },
  {
    "path": "sample/next-pages/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\nimport './.next/dev/types/routes.d.ts';\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.\n"
  },
  {
    "path": "sample/next-pages/next.config.ts",
    "content": "import type { NextConfig } from 'next';\n\nconst turbopackEnabled = process.env.TURBOPACK;\n\nconst nextConfig: NextConfig = {\n  experimental: turbopackEnabled\n    ? undefined\n    : {\n        /**\n         * Prevents \"ESM packages (pdfjs-dist/build/pdf.worker.min.mjs) need to be imported.\" error in Webpack builds\n         */\n        esmExternals: 'loose',\n      },\n};\n\nexport default nextConfig;\n"
  },
  {
    "path": "sample/next-pages/package.json",
    "content": "{\n  \"name\": \"react-pdf-sample-page-next\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A sample page for React-PDF.\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"next build\",\n    \"dev\": \"next dev\",\n    \"preview\": \"next preview\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@wojtekmaj/react-hooks\": \"^2.0.0\",\n    \"next\": \"^16.1.7\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"latest\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "sample/next-pages/pages/Sample.css",
    "content": "body {\n  margin: 0;\n  background-color: #525659;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Example input,\n.Example button {\n  font: inherit;\n}\n\n.Example header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Example header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Example__container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Example__container__load {\n  margin-top: 1em;\n  color: white;\n}\n\n.Example__container__document {\n  width: 100%;\n  max-width: calc(100% - 2em);\n  margin: 1em 0;\n}\n\n.Example__container__document .react-pdf__Document {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.Example__container__document .react-pdf__Page {\n  margin: 1em 0;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n}\n\n.Example__container__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n"
  },
  {
    "path": "sample/next-pages/pages/Sample.tsx",
    "content": "import { useCallback, useId, useState } from 'react';\nimport { useResizeObserver } from '@wojtekmaj/react-hooks';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nconst resizeObserverOptions = {};\n\nconst maxWidth = 800;\n\ntype PDFFile = string | File | null;\n\nexport default function Sample() {\n  const fileId = useId();\n  const [file, setFile] = useState<PDFFile>('./sample.pdf');\n  const [numPages, setNumPages] = useState<number>();\n  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);\n  const [containerWidth, setContainerWidth] = useState<number>();\n\n  const onResize = useCallback<ResizeObserverCallback>((entries) => {\n    const [entry] = entries;\n\n    if (entry) {\n      setContainerWidth(entry.contentRect.width);\n    }\n  }, []);\n\n  useResizeObserver(containerRef, resizeObserverOptions, onResize);\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    const nextFile = files?.[0];\n\n    if (nextFile) {\n      setFile(nextFile);\n    }\n  }\n\n  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {\n    setNumPages(nextNumPages);\n  }\n\n  return (\n    <div className=\"Example\">\n      <header>\n        <h1>react-pdf sample page</h1>\n      </header>\n      <div className=\"Example__container\">\n        <div className=\"Example__container__load\">\n          <label htmlFor={fileId}>Load from file:</label>{' '}\n          <input id={fileId} onChange={onFileChange} type=\"file\" />\n        </div>\n        <div className=\"Example__container__document\" ref={setContainerRef}>\n          <Document file={file} onLoadSuccess={onDocumentLoadSuccess} options={options}>\n            {Array.from(new Array(numPages), (_el, index) => (\n              <Page\n                key={`page_${index + 1}`}\n                pageNumber={index + 1}\n                width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}\n              />\n            ))}\n          </Document>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "sample/next-pages/pages/_app.tsx",
    "content": "import './Sample.css';\n\nimport type { AppProps } from 'next/app';\n\nexport default function App({ Component, pageProps }: AppProps) {\n  return <Component {...pageProps} />;\n}\n"
  },
  {
    "path": "sample/next-pages/pages/index.tsx",
    "content": "import dynamic from 'next/dynamic';\n\nconst Sample = dynamic(() => import('./Sample'), {\n  ssr: false,\n});\n\nexport default function Page() {\n  return <Sample />;\n}\n"
  },
  {
    "path": "sample/next-pages/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"esModuleInterop\": true,\n    \"incremental\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"module\": \"esnext\",\n    \"moduleDetection\": \"force\",\n    \"moduleResolution\": \"bundler\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"plugins\": [{ \"name\": \"next\" }],\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  },\n  \"include\": [\".\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "sample/parcel2/.gitignore",
    "content": ".parcel-cache\ndist\nnode_modules\n"
  },
  {
    "path": "sample/parcel2/Sample.css",
    "content": "body {\n  margin: 0;\n  background-color: #525659;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Example input,\n.Example button {\n  font: inherit;\n}\n\n.Example header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Example header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Example__container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Example__container__load {\n  margin-top: 1em;\n  color: white;\n}\n\n.Example__container__document {\n  width: 100%;\n  max-width: calc(100% - 2em);\n  margin: 1em 0;\n}\n\n.Example__container__document .react-pdf__Document {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.Example__container__document .react-pdf__Page {\n  margin: 1em 0;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n}\n\n.Example__container__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n"
  },
  {
    "path": "sample/parcel2/Sample.tsx",
    "content": "import { useCallback, useId, useState } from 'react';\nimport { useResizeObserver } from '@wojtekmaj/react-hooks';\nimport { pdfjs, Document, Page } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport './Sample.css';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\nconst pdfFile = new URL('./sample.pdf', import.meta.url).toString();\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'npm:pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nconst resizeObserverOptions = {};\n\nconst maxWidth = 800;\n\ntype PDFFile = string | File | null;\n\nexport default function Sample() {\n  const fileId = useId();\n  const [file, setFile] = useState<PDFFile>(pdfFile);\n  const [numPages, setNumPages] = useState<number>();\n  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);\n  const [containerWidth, setContainerWidth] = useState<number>();\n\n  const onResize = useCallback<ResizeObserverCallback>((entries) => {\n    const [entry] = entries;\n\n    if (entry) {\n      setContainerWidth(entry.contentRect.width);\n    }\n  }, []);\n\n  useResizeObserver(containerRef, resizeObserverOptions, onResize);\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    const nextFile = files?.[0];\n\n    if (nextFile) {\n      setFile(nextFile);\n    }\n  }\n\n  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {\n    setNumPages(nextNumPages);\n  }\n\n  return (\n    <div className=\"Example\">\n      <header>\n        <h1>react-pdf sample page</h1>\n      </header>\n      <div className=\"Example__container\">\n        <div className=\"Example__container__load\">\n          <label htmlFor={fileId}>Load from file:</label>{' '}\n          <input id={fileId} onChange={onFileChange} type=\"file\" />\n        </div>\n        <div className=\"Example__container__document\" ref={setContainerRef}>\n          <Document file={file} onLoadSuccess={onDocumentLoadSuccess} options={options}>\n            {Array.from(new Array(numPages), (_el, index) => (\n              <Page\n                key={`page_${index + 1}`}\n                pageNumber={index + 1}\n                width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}\n              />\n            ))}\n          </Document>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "sample/parcel2/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>react-pdf sample page</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "sample/parcel2/index.tsx",
    "content": "import { createRoot } from 'react-dom/client';\n\nimport Sample from './Sample';\n\nconst root = document.getElementById('root');\n\nif (!root) {\n  throw new Error('Could not find root element');\n}\n\ncreateRoot(root).render(<Sample />);\n"
  },
  {
    "path": "sample/parcel2/package.json",
    "content": "{\n  \"name\": \"react-pdf-sample-page-parcel2\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A sample page for React-PDF.\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"yarn copy && parcel build index.html --public-url ./\",\n    \"copy\": \"yarn copy-cmaps && yarn copy-standard-fonts\",\n    \"copy-cmaps\": \"node --experimental-strip-types ./scripts/copy-cmaps.ts\",\n    \"copy-standard-fonts\": \"node --experimental-strip-types ./scripts/copy-standard-fonts.ts\",\n    \"copy-wasm\": \"node --experimental-strip-types ./scripts/copy-wasm.ts\",\n    \"dev\": \"yarn copy && parcel index.html\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@wojtekmaj/react-hooks\": \"^2.0.0\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"latest\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"*\",\n    \"buffer\": \"^5.5.0\",\n    \"parcel\": \"^2.16.4\",\n    \"process\": \"^0.11.10\",\n    \"typescript\": \"^5.9.2\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "sample/parcel2/scripts/copy-cmaps.ts",
    "content": "import fs from 'node:fs';\nimport path from 'node:path';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nconst cMapsDir = path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps');\nconst targetDir = path.join('dist', 'cmaps');\n\nfs.cpSync(cMapsDir, targetDir, { recursive: true });\n"
  },
  {
    "path": "sample/parcel2/scripts/copy-standard-fonts.ts",
    "content": "import fs from 'node:fs';\nimport path from 'node:path';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nconst standardFontsDir = path.join(\n  path.dirname(require.resolve('pdfjs-dist/package.json')),\n  'standard_fonts',\n);\nconst targetDir = path.join('dist', 'standard_fonts');\n\nfs.cpSync(standardFontsDir, targetDir, { recursive: true });\n"
  },
  {
    "path": "sample/parcel2/scripts/copy-wasm.ts",
    "content": "import fs from 'node:fs';\nimport path from 'node:path';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\nconst wasmDir = path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'wasm');\nconst targetDir = path.join('dist', 'wasm');\n\nfs.cpSync(wasmDir, targetDir, { recursive: true });\n"
  },
  {
    "path": "sample/parcel2/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"preserve\",\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  }\n}\n"
  },
  {
    "path": "sample/vite/.gitignore",
    "content": "dist\nnode_modules\n"
  },
  {
    "path": "sample/vite/Sample.css",
    "content": "body {\n  margin: 0;\n  background-color: #525659;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Example input,\n.Example button {\n  font: inherit;\n}\n\n.Example header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Example header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Example__container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Example__container__load {\n  margin-top: 1em;\n  color: white;\n}\n\n.Example__container__document {\n  width: 100%;\n  max-width: calc(100% - 2em);\n  margin: 1em 0;\n}\n\n.Example__container__document .react-pdf__Document {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.Example__container__document .react-pdf__Page {\n  margin: 1em 0;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n}\n\n.Example__container__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n"
  },
  {
    "path": "sample/vite/Sample.tsx",
    "content": "import { useCallback, useId, useState } from 'react';\nimport { useResizeObserver } from '@wojtekmaj/react-hooks';\nimport { pdfjs, Document, Page } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport './Sample.css';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nconst resizeObserverOptions = {};\n\nconst maxWidth = 800;\n\ntype PDFFile = string | File | null;\n\nexport default function Sample() {\n  const fileId = useId();\n  const [file, setFile] = useState<PDFFile>('./sample.pdf');\n  const [numPages, setNumPages] = useState<number>();\n  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);\n  const [containerWidth, setContainerWidth] = useState<number>();\n\n  const onResize = useCallback<ResizeObserverCallback>((entries) => {\n    const [entry] = entries;\n\n    if (entry) {\n      setContainerWidth(entry.contentRect.width);\n    }\n  }, []);\n\n  useResizeObserver(containerRef, resizeObserverOptions, onResize);\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    const nextFile = files?.[0];\n\n    if (nextFile) {\n      setFile(nextFile);\n    }\n  }\n\n  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {\n    setNumPages(nextNumPages);\n  }\n\n  return (\n    <div className=\"Example\">\n      <header>\n        <h1>react-pdf sample page</h1>\n      </header>\n      <div className=\"Example__container\">\n        <div className=\"Example__container__load\">\n          <label htmlFor={fileId}>Load from file:</label>{' '}\n          <input id={fileId} onChange={onFileChange} type=\"file\" />\n        </div>\n        <div className=\"Example__container__document\" ref={setContainerRef}>\n          <Document file={file} onLoadSuccess={onDocumentLoadSuccess} options={options}>\n            {Array.from(new Array(numPages), (_el, index) => (\n              <Page\n                key={`page_${index + 1}`}\n                pageNumber={index + 1}\n                width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}\n              />\n            ))}\n          </Document>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "sample/vite/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>react-pdf sample page</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "sample/vite/index.tsx",
    "content": "import { createRoot } from 'react-dom/client';\n\nimport Sample from './Sample.js';\n\nconst root = document.getElementById('root');\n\nif (!root) {\n  throw new Error('Could not find root element');\n}\n\ncreateRoot(root).render(<Sample />);\n"
  },
  {
    "path": "sample/vite/package.json",
    "content": "{\n  \"name\": \"react-pdf-sample-page-vite4\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A sample page for React-PDF.\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"vite build\",\n    \"dev\": \"vite\",\n    \"preview\": \"vite preview\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@wojtekmaj/react-hooks\": \"^2.0.0\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"latest\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-react\": \"^4.6.0\",\n    \"vite\": \"^7.1.11\",\n    \"vite-plugin-static-copy\": \"^3.1.2\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "sample/vite/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"preserve\",\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  }\n}\n"
  },
  {
    "path": "sample/vite/vite.config.ts",
    "content": "import path from 'node:path';\nimport { createRequire } from 'node:module';\n\nimport { defineConfig, normalizePath } from 'vite';\nimport { viteStaticCopy } from 'vite-plugin-static-copy';\n\nconst require = createRequire(import.meta.url);\nconst cMapsDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps'),\n);\nconst standardFontsDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts'),\n);\nconst wasmDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'wasm'),\n);\n\nexport default defineConfig({\n  plugins: [\n    viteStaticCopy({\n      targets: [\n        { src: cMapsDir, dest: '' },\n        { src: standardFontsDir, dest: '' },\n        { src: wasmDir, dest: '' },\n      ],\n    }),\n  ],\n});\n"
  },
  {
    "path": "sample/webpack5/.babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-typescript\",\n    [\n      \"@babel/preset-env\",\n      {\n        \"modules\": false\n      }\n    ],\n    [\n      \"@babel/preset-react\",\n      {\n        \"runtime\": \"automatic\"\n      }\n    ]\n  ]\n}\n"
  },
  {
    "path": "sample/webpack5/.gitignore",
    "content": "dist\nnode_modules\n"
  },
  {
    "path": "sample/webpack5/Sample.css",
    "content": "body {\n  margin: 0;\n  background-color: #525659;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Example input,\n.Example button {\n  font: inherit;\n}\n\n.Example header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Example header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Example__container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Example__container__load {\n  margin-top: 1em;\n  color: white;\n}\n\n.Example__container__document {\n  width: 100%;\n  max-width: calc(100% - 2em);\n  margin: 1em 0;\n}\n\n.Example__container__document .react-pdf__Document {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.Example__container__document .react-pdf__Page {\n  margin: 1em 0;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n}\n\n.Example__container__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n"
  },
  {
    "path": "sample/webpack5/Sample.tsx",
    "content": "import { useCallback, useId, useState } from 'react';\nimport { useResizeObserver } from '@wojtekmaj/react-hooks';\nimport { pdfjs, Document, Page } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport './Sample.css';\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nconst resizeObserverOptions = {};\n\nconst maxWidth = 800;\n\ntype PDFFile = string | File | null;\n\nexport default function Sample() {\n  const fileId = useId();\n  const [file, setFile] = useState<PDFFile>('./sample.pdf');\n  const [numPages, setNumPages] = useState<number>();\n  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);\n  const [containerWidth, setContainerWidth] = useState<number>();\n\n  const onResize = useCallback<ResizeObserverCallback>((entries) => {\n    const [entry] = entries;\n\n    if (entry) {\n      setContainerWidth(entry.contentRect.width);\n    }\n  }, []);\n\n  useResizeObserver(containerRef, resizeObserverOptions, onResize);\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    const nextFile = files?.[0];\n\n    if (nextFile) {\n      setFile(nextFile);\n    }\n  }\n\n  function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {\n    setNumPages(nextNumPages);\n  }\n\n  return (\n    <div className=\"Example\">\n      <header>\n        <h1>react-pdf sample page</h1>\n      </header>\n      <div className=\"Example__container\">\n        <div className=\"Example__container__load\">\n          <label htmlFor={fileId}>Load from file:</label>{' '}\n          <input id={fileId} onChange={onFileChange} type=\"file\" />\n        </div>\n        <div className=\"Example__container__document\" ref={setContainerRef}>\n          <Document file={file} onLoadSuccess={onDocumentLoadSuccess} options={options}>\n            {Array.from(new Array(numPages), (_el, index) => (\n              <Page\n                key={`page_${index + 1}`}\n                pageNumber={index + 1}\n                width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}\n              />\n            ))}\n          </Document>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "sample/webpack5/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>react-pdf sample page</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "sample/webpack5/index.tsx",
    "content": "import { createRoot } from 'react-dom/client';\n\nimport Sample from './Sample';\n\nconst root = document.getElementById('root');\n\nif (!root) {\n  throw new Error('Could not find root element');\n}\n\ncreateRoot(root).render(<Sample />);\n"
  },
  {
    "path": "sample/webpack5/package.json",
    "content": "{\n  \"name\": \"react-pdf-sample-page-webpack5\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A sample page for React-PDF.\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"NODE_ENV=production webpack\",\n    \"dev\": \"NODE_ENV=development webpack serve\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@wojtekmaj/react-hooks\": \"^2.0.0\",\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"latest\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.27.4\",\n    \"@babel/preset-env\": \"^7.27.2\",\n    \"@babel/preset-react\": \"^7.27.1\",\n    \"@babel/preset-typescript\": \"^7.27.1\",\n    \"@types/node\": \"*\",\n    \"babel-loader\": \"^9.0.1\",\n    \"copy-webpack-plugin\": \"^14.0.0\",\n    \"css-loader\": \"^6.0.0\",\n    \"html-webpack-plugin\": \"^5.1.0\",\n    \"style-loader\": \"^3.0.0\",\n    \"ts-node\": \"^10.9.1\",\n    \"typescript\": \"^5.9.2\",\n    \"webpack\": \"^5.104.1\",\n    \"webpack-cli\": \"^5.0.0\",\n    \"webpack-dev-server\": \"^5.2.1\"\n  },\n  \"packageManager\": \"yarn@4.10.3\"\n}\n"
  },
  {
    "path": "sample/webpack5/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"esModuleInterop\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"preserve\",\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es2018\",\n    \"verbatimModuleSyntax\": true\n  },\n  \"ts-node\": {\n    \"compilerOptions\": {\n      \"module\": \"commonjs\",\n      \"verbatimModuleSyntax\": false\n    }\n  }\n}\n"
  },
  {
    "path": "sample/webpack5/webpack.config.ts",
    "content": "import webpack from 'webpack';\nimport path from 'node:path';\n\nimport CopyWebpackPlugin from 'copy-webpack-plugin';\nimport HtmlWebpackPlugin from 'html-webpack-plugin';\n\nimport type { Configuration } from 'webpack';\nimport 'webpack-dev-server';\n\nconst isProduction = process.env.NODE_ENV === 'production';\n\nconst cMapsDir = path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps');\nconst standardFontsDir = path.join(\n  path.dirname(require.resolve('pdfjs-dist/package.json')),\n  'standard_fonts',\n);\nconst wasmDir = path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'wasm');\n\nconst config = {\n  mode: isProduction ? 'production' : 'development',\n  /**\n   * Critical: prevents \"Uncaught TypeError: Object.defineProperty called on non-object\" error\n   */\n  devtool: 'cheap-source-map',\n  bail: isProduction,\n  context: path.join(__dirname),\n  entry: {\n    src: './index.tsx',\n  },\n  output: {\n    path: path.resolve(__dirname, 'dist'),\n  },\n  resolve: {\n    extensions: ['.js', '.jsx', '.ts', '.tsx'],\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.(j|t)sx?$/,\n        use: ['babel-loader'],\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader'],\n      },\n    ],\n  },\n  plugins: [\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: JSON.stringify(process.env.NODE_ENV),\n      },\n    }),\n    new HtmlWebpackPlugin({\n      template: 'index.html',\n    }),\n    new CopyWebpackPlugin({\n      patterns: [\n        { from: './sample.pdf' },\n        { from: cMapsDir, to: 'cmaps/' },\n        { from: standardFontsDir, to: 'standard_fonts/' },\n        { from: wasmDir, to: 'wasm/' },\n      ],\n    }),\n  ],\n  devServer: {\n    compress: true,\n    historyApiFallback: true, // respond to 404s with index.html\n    host: 'localhost',\n    hot: true, // enable HMR on the server\n    port: 3000,\n  },\n} satisfies Configuration;\n\nexport default config;\n"
  },
  {
    "path": "test/.gitignore",
    "content": "dist\nnode_modules\n"
  },
  {
    "path": "test/AnnotationOptions.tsx",
    "content": "import { useId } from 'react';\n\nimport type { ExternalLinkTarget } from './shared/types.js';\n\ntype AnnotationOptionsProps = {\n  externalLinkTarget: ExternalLinkTarget | undefined;\n  setExternalLinkTarget: (value: ExternalLinkTarget | undefined) => void;\n};\n\nexport default function AnnotationOptions({\n  externalLinkTarget,\n  setExternalLinkTarget,\n}: AnnotationOptionsProps) {\n  const targetUnsetId = useId();\n  const targetSelfId = useId();\n  const targetBlankId = useId();\n\n  function onExternalLinkTargetChange(event: React.ChangeEvent<HTMLInputElement>) {\n    const { value } = event.target;\n\n    if (value === 'undefined') {\n      setExternalLinkTarget(undefined);\n    } else {\n      setExternalLinkTarget(value as '_blank' | '_self');\n    }\n  }\n\n  return (\n    <fieldset>\n      <legend>Annotation options</legend>\n\n      <label htmlFor={targetUnsetId}>External link target</label>\n      <div>\n        <input\n          checked={externalLinkTarget === undefined}\n          id={targetUnsetId}\n          name=\"externalLinkTarget\"\n          onChange={onExternalLinkTargetChange}\n          type=\"radio\"\n          value=\"undefined\"\n        />\n        <label htmlFor={targetUnsetId}>Unset</label>\n      </div>\n      <div>\n        <input\n          checked={externalLinkTarget === '_self'}\n          id={targetSelfId}\n          name=\"externalLinkTarget\"\n          onChange={onExternalLinkTargetChange}\n          type=\"radio\"\n          value=\"_self\"\n        />\n        <label htmlFor={targetSelfId}>_self</label>\n      </div>\n      <div>\n        <input\n          checked={externalLinkTarget === '_blank'}\n          id={targetBlankId}\n          name=\"externalLinkTarget\"\n          onChange={onExternalLinkTargetChange}\n          type=\"radio\"\n          value=\"_blank\"\n        />\n        <label htmlFor={targetBlankId}>_blank</label>\n      </div>\n    </fieldset>\n  );\n}\n"
  },
  {
    "path": "test/CustomRenderer.tsx",
    "content": "import { useEffect, useMemo, useRef } from 'react';\nimport { usePageContext } from 'react-pdf';\nimport invariant from 'tiny-invariant';\n\nimport type { RenderParameters } from 'pdfjs-dist/types/src/display/api.js';\n\nexport default function CustomRenderer() {\n  const pageContext = usePageContext();\n\n  invariant(pageContext, 'Unable to find Page context.');\n\n  const { _className, page, rotate, scale } = pageContext;\n\n  invariant(page, 'Attempted to render page canvas, but no page was specified.');\n\n  const canvasElement = useRef<HTMLCanvasElement>(null);\n\n  const viewport = useMemo(\n    () => page.getViewport({ scale, rotation: rotate }),\n    [page, rotate, scale],\n  );\n\n  useEffect(\n    function drawPageOnCanvas() {\n      if (!page) {\n        return;\n      }\n\n      const { current: canvas } = canvasElement;\n\n      if (!canvas) {\n        return;\n      }\n\n      const renderContext: RenderParameters = {\n        canvas,\n        canvasContext: canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D,\n        viewport,\n      };\n\n      const cancellable = page.render(renderContext);\n      const runningTask = cancellable;\n\n      cancellable.promise.catch(() => {\n        // Intentionally empty\n      });\n\n      return () => {\n        runningTask.cancel();\n      };\n    },\n    [page, viewport],\n  );\n\n  return (\n    <canvas\n      className={`${_className}__canvas`}\n      height={viewport.height}\n      ref={canvasElement}\n      width={viewport.width}\n    />\n  );\n}\n"
  },
  {
    "path": "test/LayerOptions.tsx",
    "content": "import { useId } from 'react';\n\ntype LayerOptionsProps = {\n  renderAnnotationLayer: boolean;\n  renderForms: boolean;\n  renderTextLayer: boolean;\n  useCustomTextRenderer: boolean;\n  setRenderAnnotationLayer: (value: boolean) => void;\n  setRenderForms: (value: boolean) => void;\n  setRenderTextLayer: (value: boolean) => void;\n  setUseCustomTextRenderer: (value: boolean) => void;\n};\n\nexport default function LayerOptions({\n  renderAnnotationLayer,\n  renderForms,\n  renderTextLayer,\n  useCustomTextRenderer,\n  setRenderAnnotationLayer,\n  setRenderForms,\n  setRenderTextLayer,\n  setUseCustomTextRenderer,\n}: LayerOptionsProps) {\n  const renderTextLayerId = useId();\n  const useCustomTextRendererId = useId();\n  const renderAnnotationLayerId = useId();\n  const renderFormsId = useId();\n\n  function onRenderAnnotationLayerChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setRenderAnnotationLayer(event.target.checked);\n  }\n\n  function onRenderFormsChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setRenderForms(event.target.checked);\n  }\n\n  function onRenderTextLayersChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setRenderTextLayer(event.target.checked);\n  }\n\n  function onUseCustomTextRendererChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setUseCustomTextRenderer(event.target.checked);\n  }\n\n  return (\n    <fieldset>\n      <legend>Layer options</legend>\n\n      <div>\n        <input\n          checked={renderTextLayer}\n          id={renderTextLayerId}\n          onChange={onRenderTextLayersChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={renderTextLayerId}>Render text layer</label>\n      </div>\n\n      <div>\n        <input\n          checked={renderTextLayer ? useCustomTextRenderer : false}\n          disabled={!renderTextLayer}\n          id={useCustomTextRendererId}\n          onChange={onUseCustomTextRendererChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={useCustomTextRendererId}>Use custom text renderer</label>\n      </div>\n\n      <div>\n        <input\n          checked={renderAnnotationLayer}\n          id={renderAnnotationLayerId}\n          onChange={onRenderAnnotationLayerChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={renderAnnotationLayerId}>Render annotation layer</label>\n      </div>\n\n      <div>\n        <input\n          checked={renderAnnotationLayer ? renderForms : false}\n          disabled={!renderAnnotationLayer}\n          id={renderFormsId}\n          onChange={onRenderFormsChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={renderFormsId}>Render forms</label>\n      </div>\n    </fieldset>\n  );\n}\n"
  },
  {
    "path": "test/LoadingOptions.tsx",
    "content": "import { useId, useRef } from 'react';\nimport { flushSync } from 'react-dom';\n\nimport samplePDF from './test.pdf';\n\nimport type { File } from './shared/types.js';\n\ntype LoadingOptionsProps = {\n  file: File | null;\n  setFile: (value: File | null) => void;\n  setRender: (value: boolean) => void;\n};\n\nexport default function LoadingOptions({ file, setFile, setRender }: LoadingOptionsProps) {\n  const url = useRef<HTMLInputElement>(null);\n  const fetchAndPass = useRef<HTMLInputElement>(null);\n  const fileId = useId();\n  const urlId = useId();\n  const fetchAndPassId = useId();\n\n  function onFileChange(event: React.ChangeEvent<HTMLInputElement>): void {\n    const { files } = event.target;\n\n    if (files?.[0]) {\n      setFile(files[0]);\n    } else {\n      setFile(null);\n    }\n  }\n\n  function onURLChange(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = url.current;\n    const { value: nextUrl } = input as HTMLInputElement;\n\n    setFile(nextUrl);\n  }\n\n  function onRequestChange(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = fetchAndPass.current;\n    const { value: nextFetchAndPass } = input as HTMLInputElement;\n\n    fetch(nextFetchAndPass)\n      .then((response) => response.blob())\n      .then(setFile);\n  }\n\n  function onUseImported() {\n    setFile(samplePDF);\n  }\n\n  function onImportAndUnmount() {\n    setFile(samplePDF);\n\n    flushSync(() => setRender(false));\n\n    flushSync(() => setRender(true));\n  }\n\n  function unloadFile() {\n    setFile(null);\n  }\n\n  return (\n    <fieldset>\n      <legend>Load file</legend>\n\n      <label htmlFor={fileId}>Load from file:</label>\n      <input id={fileId} onChange={onFileChange} type=\"file\" />\n\n      <form onSubmit={onURLChange}>\n        <label htmlFor={urlId}>Load from URL:</label>\n        <input id={urlId} type=\"text\" />\n        <button type=\"submit\">Apply</button>\n      </form>\n\n      <form onSubmit={onRequestChange}>\n        <label htmlFor={fetchAndPassId}>Fetch and pass:</label>\n        <input id={fetchAndPassId} type=\"text\" />\n        <button type=\"submit\">Apply</button>\n      </form>\n\n      <div>\n        <button onClick={onUseImported} type=\"button\">\n          Use imported file\n        </button>\n      </div>\n\n      <div>\n        <button onClick={onImportAndUnmount} type=\"button\">\n          Import, unmount and mount\n        </button>\n      </div>\n\n      <div>\n        <button disabled={file === null} onClick={unloadFile} type=\"button\">\n          Unload file\n        </button>\n      </div>\n    </fieldset>\n  );\n}\n"
  },
  {
    "path": "test/PassingOptions.tsx",
    "content": "import { isDataURI } from './shared/utils.js';\nimport { useId } from 'react';\n\nimport type { File, PassMethod } from './shared/types.js';\n\ntype PassingOptionsProps = {\n  file: File | null;\n  passMethod: PassMethod | undefined;\n  setPassMethod: (value: PassMethod | undefined) => void;\n};\n\nexport default function PassingOptions({ file, passMethod, setPassMethod }: PassingOptionsProps) {\n  const passNormalId = useId();\n  const passObjectId = useId();\n  const passStringId = useId();\n  const passBlobId = useId();\n\n  const sourceType = (() => {\n    if (file === null) {\n      return 'null';\n    }\n\n    if (typeof file === 'string') {\n      if (isDataURI(file)) {\n        return 'data URI';\n      }\n\n      return 'string';\n    }\n\n    if (typeof file === 'object') {\n      return file.constructor.name;\n    }\n\n    return typeof file;\n  })();\n\n  function onPassMethodChange(event: React.ChangeEvent<HTMLInputElement>) {\n    const nextPassMethod = event.target.value;\n\n    if (nextPassMethod === 'undefined') {\n      setPassMethod(undefined);\n    } else {\n      setPassMethod(nextPassMethod as PassMethod);\n    }\n  }\n\n  return (\n    <fieldset>\n      <legend>Passing options</legend>\n\n      <div>\n        <input\n          checked={passMethod === undefined}\n          id={passNormalId}\n          name=\"passMethod\"\n          onChange={onPassMethodChange}\n          type=\"radio\"\n          value=\"undefined\"\n        />\n        <label htmlFor={passNormalId}>Pass as is ({sourceType})</label>\n      </div>\n      <div>\n        <input\n          checked={passMethod === 'object'}\n          id={passObjectId}\n          name=\"passMethod\"\n          onChange={onPassMethodChange}\n          type=\"radio\"\n          value=\"object\"\n        />\n        <label htmlFor={passObjectId}>Pass as a parameter object</label>\n      </div>\n      <div>\n        <input\n          checked={passMethod === 'string'}\n          id={passStringId}\n          name=\"passMethod\"\n          onChange={onPassMethodChange}\n          type=\"radio\"\n          value=\"string\"\n        />\n        <label htmlFor={passStringId}>Pass as a string/data URI</label>\n      </div>\n    </fieldset>\n  );\n}\n"
  },
  {
    "path": "test/Test.css",
    "content": "body {\n  margin: 0;\n  font-family: 'Segoe UI', Tahoma, sans-serif;\n}\n\n.Test header {\n  background-color: #323639;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  padding: 20px;\n  color: white;\n}\n\n.Test header h1 {\n  font-size: inherit;\n  margin: 0;\n}\n\n.Test__container {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  align-items: flex-start;\n  margin: 10px 0;\n  padding: 10px;\n}\n\n.Test__container > * > * {\n  margin: 10px;\n}\n\n.Test__container__options {\n  display: flex;\n  flex-basis: 400px;\n  flex-grow: 1;\n  flex-wrap: wrap;\n  margin: 0;\n}\n\n.Test__container__options input,\n.Test__container__options button {\n  font: inherit;\n}\n\n.Test__container__options fieldset {\n  border: 1px solid black;\n  flex-grow: 1;\n  position: relative;\n  top: -10px;\n}\n\n.Test__container__options fieldset legend {\n  font-weight: 600;\n}\n\n.Test__container__options fieldset legend + * {\n  margin-top: 0 !important;\n}\n\n.Test__container__options fieldset label {\n  font-weight: 600;\n  display: block;\n}\n\n.Test__container__options fieldset label:not(:first-of-type) {\n  margin-top: 1em;\n}\n\n.Test__container__options fieldset input[type='checkbox'] + label,\n.Test__container__options fieldset input[type='radio'] + label {\n  font-weight: normal;\n  display: inline-block;\n  margin: 0;\n}\n\n.Test__container__options fieldset form:not(:first-child),\n.Test__container__options fieldset div:not(:first-child) {\n  margin-top: 1em;\n}\n\n.Test__container__options fieldset form:not(:last-child),\n.Test__container__options fieldset div:not(:last-child) {\n  margin-bottom: 1em;\n}\n\n.Test__container__content {\n  display: flex;\n  max-width: 100%;\n  flex-basis: 420px;\n  flex-direction: column;\n  flex-grow: 100;\n  align-items: stretch;\n}\n\n.Test__container__content__toc {\n  max-width: 100%;\n  max-height: 16vh;\n  margin-bottom: 20px;\n  border: 1px solid black;\n  overflow: auto;\n}\n\n.Test__container__content__toc .react-pdf__message {\n  padding: 20px;\n}\n\n.Test__container__content__document {\n  max-width: 100%;\n  max-height: 55vh;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  border: 1px solid black;\n  background-color: #525659;\n  overflow: auto;\n}\n\n.Test__container__content__document .react-pdf__Page {\n  max-width: calc(100% - 2em);\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);\n  margin: 1em;\n}\n\n.Test__container__content__document .react-pdf__Page__textContent mark {\n  opacity: 0.5;\n  background: #ff0;\n  box-shadow: 0px 2px 10px #ff0;\n  color: transparent;\n  white-space: pre;\n}\n\n.Test__container__content__document .react-pdf__message {\n  padding: 20px;\n  color: white;\n}\n\n.Test__container__content__controls {\n  max-width: 100%;\n  display: flex;\n  margin-top: 1em;\n  align-self: center;\n}\n\n.Test__container__content__controls span {\n  flex-grow: 1;\n  margin: 0 1em;\n  text-align: center;\n}\n\n.Test__container__content__controls button {\n  width: 80px;\n}\n\n.Test__container__content__thumbnails {\n  display: flex;\n  margin-top: 1em;\n  gap: 1em;\n  overflow: auto;\n}\n\n.Test__container__content__thumbnails .react-pdf__Thumbnail {\n  border: 1px solid black;\n}\n"
  },
  {
    "path": "test/Test.tsx",
    "content": "import { useCallback, useEffect, useState } from 'react';\nimport { Document, Outline, Page, pdfjs, Thumbnail } from 'react-pdf';\nimport 'react-pdf/dist/Page/AnnotationLayer.css';\nimport 'react-pdf/dist/Page/TextLayer.css';\n\nimport './Test.css';\n\nimport AnnotationOptions from './AnnotationOptions.js';\nimport CustomRenderer from './CustomRenderer.js';\nimport LayerOptions from './LayerOptions.js';\nimport LoadingOptions from './LoadingOptions.js';\nimport PassingOptions from './PassingOptions.js';\nimport ViewOptions from './ViewOptions.js';\n\nimport { isArrayBuffer, isBlob, isBrowser, loadFromFile } from './shared/utils.js';\n\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\nimport type { ExternalLinkTarget, File, PassMethod, RenderMode } from './shared/types.js';\n\nconst { PDFDataRangeTransport } = pdfjs;\n\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n  'pdfjs-dist/build/pdf.worker.min.mjs',\n  import.meta.url,\n).toString();\n\nconst options = {\n  cMapUrl: '/cmaps/',\n  standardFontDataUrl: '/standard_fonts/',\n  wasmUrl: '/wasm/',\n};\n\nexport function readAsDataURL(file: Blob): Promise<string> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n\n    reader.onload = () => {\n      if (!reader.result) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      resolve(reader.result as string);\n    };\n\n    reader.onerror = (event) => {\n      if (!event.target) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      const { error } = event.target;\n\n      if (!error) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      switch (error.code) {\n        case error.NOT_FOUND_ERR:\n          return reject(new Error('Error while reading a file: File not found.'));\n        case error.SECURITY_ERR:\n          return reject(new Error('Error while reading a file: Security error.'));\n        case error.ABORT_ERR:\n          return reject(new Error('Error while reading a file: Aborted.'));\n        default:\n          return reject(new Error('Error while reading a file.'));\n      }\n    };\n\n    reader.readAsDataURL(file);\n  });\n}\n\nexport default function Test() {\n  const [canvasBackground, setCanvasBackground] = useState<string>();\n  const [devicePixelRatio, setDevicePixelRatio] = useState<number>();\n  const [displayAll, setDisplayAll] = useState(false);\n  const [externalLinkTarget, setExternalLinkTarget] = useState<ExternalLinkTarget>();\n  const [file, setFile] = useState<File>(null);\n  const [fileForProps, setFileForProps] = useState<File>();\n  const [numPages, setNumPages] = useState<number>();\n  const [pageHeight, setPageHeight] = useState<number>();\n  const [pageNumber, setPageNumber] = useState<number>();\n  const [pageScale, setPageScale] = useState<number>();\n  const [pageWidth, setPageWidth] = useState<number>();\n  const [passMethod, setPassMethod] = useState<PassMethod>();\n  const [renderHighContrast, setRenderHighContrast] = useState(false);\n  const [render, setRender] = useState(true);\n  const [renderAnnotationLayer, setRenderAnnotationLayer] = useState(true);\n  const [renderForms, setRenderForms] = useState(true);\n  const [renderMode, setRenderMode] = useState<RenderMode | undefined>('canvas');\n  const [renderTextLayer, setRenderTextLayer] = useState(true);\n  const [useCustomTextRenderer, setUseCustomTextRenderer] = useState(true);\n  const [rotate, setRotate] = useState<number>();\n\n  const onDocumentLoadProgress = useCallback((progressData: { loaded: number; total: number }) => {\n    console.log('Loading a document: ' + (progressData.loaded / progressData.total) * 100 + '%');\n  }, []);\n\n  const onDocumentLoadSuccess = useCallback((document: PDFDocumentProxy) => {\n    console.log('Loaded a document', document);\n    const { numPages: nextNumPages } = document;\n    setNumPages(nextNumPages);\n    setPageNumber(1);\n  }, []);\n\n  const onDocumentLoadError = useCallback((error: Error) => {\n    console.error(error);\n  }, []);\n\n  const onPageRenderSuccess = useCallback(\n    (page: PDFPageProxy) => console.log('Rendered a page', page),\n    [],\n  );\n\n  const onPageClick = useCallback(\n    (event: React.MouseEvent<HTMLDivElement>, page: PDFPageProxy | false | undefined) =>\n      console.log('Clicked a page', { event, page }),\n    [],\n  );\n\n  const onItemClick = useCallback((args: { pageNumber: number }) => {\n    console.log('Clicked an item', args);\n    const { pageNumber: nextPageNumber } = args;\n    setPageNumber(nextPageNumber);\n  }, []);\n\n  const customTextRenderer = useCallback(\n    ({ str }: { str: string }) => str.replace(/ipsum/g, (value) => `<mark>${value}</mark>`),\n    [],\n  );\n\n  useEffect(() => {\n    (async () => {\n      const nextFileForProps = await (async () => {\n        if (!file) {\n          return null;\n        }\n\n        switch (passMethod) {\n          case 'string': {\n            if (typeof file === 'string') {\n              return file;\n            }\n\n            if (file instanceof File || file instanceof Blob) {\n              return readAsDataURL(file);\n            }\n\n            return file;\n          }\n          case 'object': {\n            // File is a string\n            if (typeof file === 'string') {\n              return { url: file };\n            }\n\n            // File is PDFDataRangeTransport\n            if (file instanceof PDFDataRangeTransport) {\n              return { range: file };\n            }\n\n            // File is an ArrayBuffer\n            if (isArrayBuffer(file)) {\n              return { data: file };\n            }\n\n            /**\n             * The cases below are browser-only.\n             * If you're running on a non-browser environment, these cases will be of no use.\n             */\n            if (isBrowser) {\n              // File is a Blob\n              if (isBlob(file)) {\n                return { data: await loadFromFile(file) };\n              }\n            }\n            return file;\n          }\n          default:\n            return file;\n        }\n      })();\n\n      setFileForProps(nextFileForProps);\n    })();\n  }, [file, passMethod]);\n\n  const changePage = useCallback(\n    (offset: number) => setPageNumber((prevPageNumber) => (prevPageNumber || 1) + offset),\n    [],\n  );\n\n  const previousPage = useCallback(() => changePage(-1), [changePage]);\n\n  const nextPage = useCallback(() => changePage(1), [changePage]);\n\n  const documentProps = {\n    externalLinkTarget,\n    file: fileForProps,\n    options,\n    rotate,\n  };\n\n  const pageProps = {\n    canvasBackground,\n    className: 'custom-classname-page',\n    customRenderer: CustomRenderer,\n    customTextRenderer: useCustomTextRenderer ? customTextRenderer : undefined,\n    devicePixelRatio,\n    height: pageHeight,\n    onClick: onPageClick,\n    onRenderSuccess: onPageRenderSuccess,\n    pageColors: renderHighContrast\n      ? {\n          background: 'black',\n          foreground: '#ffff00',\n        }\n      : undefined,\n    renderAnnotationLayer,\n    renderForms,\n    renderMode,\n    renderTextLayer,\n    scale: pageScale,\n    width: pageWidth,\n  };\n\n  return (\n    <div className=\"Test\">\n      <header>\n        <h1>react-pdf test page</h1>\n      </header>\n      <div className=\"Test__container\">\n        <aside className=\"Test__container__options\">\n          <LoadingOptions file={file} setFile={setFile} setRender={setRender} />\n          <PassingOptions file={file} passMethod={passMethod} setPassMethod={setPassMethod} />\n          <LayerOptions\n            renderAnnotationLayer={renderAnnotationLayer}\n            renderForms={renderForms}\n            renderTextLayer={renderTextLayer}\n            useCustomTextRenderer={useCustomTextRenderer}\n            setRenderAnnotationLayer={setRenderAnnotationLayer}\n            setRenderForms={setRenderForms}\n            setRenderTextLayer={setRenderTextLayer}\n            setUseCustomTextRenderer={setUseCustomTextRenderer}\n          />\n          <ViewOptions\n            canvasBackground={canvasBackground}\n            devicePixelRatio={devicePixelRatio}\n            displayAll={displayAll}\n            pageHeight={pageHeight}\n            pageScale={pageScale}\n            pageWidth={pageWidth}\n            renderHighContrast={renderHighContrast}\n            renderMode={renderMode}\n            rotate={rotate}\n            setCanvasBackground={setCanvasBackground}\n            setDevicePixelRatio={setDevicePixelRatio}\n            setDisplayAll={setDisplayAll}\n            setPageHeight={setPageHeight}\n            setPageScale={setPageScale}\n            setPageWidth={setPageWidth}\n            setRenderHighContrast={setRenderHighContrast}\n            setRenderMode={setRenderMode}\n            setRotate={setRotate}\n          />\n          <AnnotationOptions\n            externalLinkTarget={externalLinkTarget}\n            setExternalLinkTarget={setExternalLinkTarget}\n          />\n        </aside>\n        <main className=\"Test__container__content\">\n          <Document\n            {...documentProps}\n            className=\"custom-classname-document\"\n            onClick={(\n              event: React.MouseEvent<HTMLDivElement>,\n              pdf: PDFDocumentProxy | false | undefined,\n            ) => console.log('Clicked a document', { event, pdf })}\n            onItemClick={onItemClick}\n            onLoadError={onDocumentLoadError}\n            onLoadProgress={onDocumentLoadProgress}\n            onLoadSuccess={onDocumentLoadSuccess}\n            onSourceError={onDocumentLoadError}\n          >\n            <div className=\"Test__container__content__toc\">\n              {render ? <Outline className=\"custom-classname-outline\" /> : null}\n            </div>\n            <div className=\"Test__container__content__document\">\n              {render ? (\n                displayAll ? (\n                  Array.from(new Array(numPages), (_el, index) => (\n                    <Page\n                      key={`page_${index + 1}`}\n                      {...pageProps}\n                      inputRef={\n                        pageNumber === index + 1\n                          ? (ref: HTMLDivElement) => {\n                              ref?.scrollIntoView();\n                            }\n                          : null\n                      }\n                      pageNumber={index + 1}\n                    />\n                  ))\n                ) : (\n                  <Page {...pageProps} pageNumber={pageNumber || 1} />\n                )\n              ) : null}\n            </div>\n            {displayAll || (\n              <div className=\"Test__container__content__controls\">\n                <button disabled={(pageNumber || 0) <= 1} onClick={previousPage} type=\"button\">\n                  Previous\n                </button>\n                <span>{`Page ${pageNumber || (numPages ? 1 : '--')} of ${numPages || '--'}`}</span>\n                <button\n                  disabled={(pageNumber || 0) >= (numPages || 0)}\n                  onClick={nextPage}\n                  type=\"button\"\n                >\n                  Next\n                </button>\n              </div>\n            )}\n            <div className=\"Test__container__content__thumbnails\">\n              {Array.from(new Array(numPages), (_el, index) => (\n                <Thumbnail\n                  key={`thumbnail_${index + 1}`}\n                  className=\"custom-classname-thumbnail\"\n                  pageNumber={index + 1}\n                  width={100}\n                />\n              ))}\n            </div>\n          </Document>\n        </main>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "test/ViewOptions.tsx",
    "content": "import { useId, useRef } from 'react';\n\nimport type { RenderMode } from './shared/types.js';\n\ntype ViewOptionsProps = {\n  canvasBackground?: string;\n  devicePixelRatio?: number;\n  displayAll: boolean;\n  pageHeight?: number;\n  pageScale?: number;\n  pageWidth?: number;\n  renderHighContrast: boolean;\n  renderMode?: RenderMode;\n  rotate?: number;\n  setCanvasBackground: (value: string | undefined) => void;\n  setDevicePixelRatio: (value: number | undefined) => void;\n  setDisplayAll: (value: boolean) => void;\n  setPageHeight: (value: number | undefined) => void;\n  setPageScale: (value: number | undefined) => void;\n  setPageWidth: (value: number | undefined) => void;\n  setRenderHighContrast: (value: boolean) => void;\n  setRenderMode: (value: RenderMode | undefined) => void;\n  setRotate: React.Dispatch<React.SetStateAction<number | undefined>>;\n};\n\nexport default function ViewOptions({\n  canvasBackground,\n  devicePixelRatio,\n  displayAll,\n  pageHeight,\n  pageScale,\n  pageWidth,\n  renderHighContrast,\n  renderMode,\n  rotate,\n  setCanvasBackground,\n  setDevicePixelRatio,\n  setDisplayAll,\n  setPageHeight,\n  setPageScale,\n  setPageWidth,\n  setRenderHighContrast,\n  setRenderMode,\n  setRotate,\n}: ViewOptionsProps) {\n  const devicePixelRatioInput = useRef<HTMLInputElement>(null);\n  const pageHeightInput = useRef<HTMLInputElement>(null);\n  const pageWidthInput = useRef<HTMLInputElement>(null);\n  const pageScaleInput = useRef<HTMLInputElement>(null);\n\n  const canvasBackgroundId = useId();\n  const pageWidthId = useId();\n  const pageHeightId = useId();\n  const pageScaleId = useId();\n  const devicePixelRatioId = useId();\n  const renderCanvasId = useId();\n  const renderCustomId = useId();\n  const renderNoneId = useId();\n  const rotationId = useId();\n  const displayAllId = useId();\n  const renderHighContrastId = useId();\n\n  function onCanvasBackgroundChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setCanvasBackground(event.target.value);\n  }\n\n  function onDevicePixelRatioChange(event: React.ChangeEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = devicePixelRatioInput.current;\n    const { valueAsNumber: devicePixelRatio } = input as HTMLInputElement;\n\n    setDevicePixelRatio(devicePixelRatio);\n  }\n\n  function onDisplayAllChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setDisplayAll(event.target.checked);\n  }\n\n  function onRenderHighContrastChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setRenderHighContrast(event.target.checked);\n  }\n\n  function onPageHeightChange(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = pageHeightInput.current;\n    const { valueAsNumber: nextHeight } = input as HTMLInputElement;\n\n    setPageHeight(nextHeight);\n  }\n\n  function onPageScaleChange(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = pageScaleInput.current;\n    const { valueAsNumber: nextScale } = input as HTMLInputElement;\n\n    setPageScale(nextScale);\n  }\n\n  function onPageWidthChange(event: React.FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n\n    const input = pageWidthInput.current;\n    const { valueAsNumber: nextWidth } = input as HTMLInputElement;\n\n    setPageWidth(nextWidth);\n  }\n\n  function onRenderModeChange(event: React.ChangeEvent<HTMLInputElement>) {\n    const { value } = event.target;\n\n    setRenderMode(value as RenderMode);\n  }\n\n  function changeRotation(by: number) {\n    setRotate((prevRotate) => ((prevRotate || 0) + by + 360) % 360);\n  }\n\n  function rotateLeft() {\n    changeRotation(-90);\n  }\n\n  function rotateRight() {\n    changeRotation(90);\n  }\n\n  function onChangeRotate(event: React.ChangeEvent<HTMLInputElement>) {\n    const { valueAsNumber: nextRotate } = event.target;\n\n    changeRotation(nextRotate - (rotate || 0));\n  }\n\n  function resetRotation() {\n    setRotate(undefined);\n  }\n\n  function resetHeight() {\n    setPageHeight(undefined);\n  }\n\n  function resetScale() {\n    setPageScale(undefined);\n  }\n\n  function resetWidth() {\n    setPageWidth(undefined);\n  }\n\n  function resetDevicePixelRatio() {\n    setDevicePixelRatio(undefined);\n  }\n\n  return (\n    <fieldset>\n      <legend>View options</legend>\n\n      <label htmlFor={canvasBackgroundId}>Canvas background:</label>\n      <input\n        defaultValue={canvasBackground || '#ffffff'}\n        id={canvasBackgroundId}\n        onChange={onCanvasBackgroundChange}\n        type=\"color\"\n      />\n\n      <form onSubmit={onPageWidthChange}>\n        <label htmlFor={pageWidthId}>Page width:</label>\n        &nbsp;\n        <input\n          defaultValue={pageWidth ? `${pageWidth}` : ''}\n          id={pageWidthId}\n          min={0}\n          name=\"pageWidth\"\n          ref={pageWidthInput}\n          type=\"number\"\n        />\n        &nbsp;\n        <button style={{ display: 'none' }} type=\"submit\">\n          Set width\n        </button>\n        <button disabled={pageWidth === null} onClick={resetWidth} type=\"button\">\n          Reset width\n        </button>\n      </form>\n\n      <form onSubmit={onPageHeightChange}>\n        <label htmlFor={pageHeightId}>Page height:</label>\n        &nbsp;\n        <input\n          defaultValue={pageHeight ? `${pageHeight}` : ''}\n          id={pageHeightId}\n          min={0}\n          name=\"pageHeight\"\n          ref={pageHeightInput}\n          type=\"number\"\n        />\n        &nbsp;\n        <button style={{ display: 'none' }} type=\"submit\">\n          Set height\n        </button>\n        <button disabled={pageHeight === null} onClick={resetHeight} type=\"button\">\n          Reset height\n        </button>\n      </form>\n\n      <form onSubmit={onPageScaleChange}>\n        <label htmlFor={pageScaleId}>Page scale:</label>\n        &nbsp;\n        <input\n          defaultValue={pageScale ? `${pageScale}` : ''}\n          id={pageScaleId}\n          max={2}\n          min={0}\n          name=\"pageScale\"\n          ref={pageScaleInput}\n          step=\"0.01\"\n          type=\"range\"\n        />\n        &nbsp;\n        <button style={{ display: 'none' }} type=\"submit\">\n          Set scale\n        </button>\n        <button disabled={pageScale === null} onClick={resetScale} type=\"button\">\n          Reset scale\n        </button>\n      </form>\n\n      <form onSubmit={onDevicePixelRatioChange}>\n        <label htmlFor={devicePixelRatioId}>Device pixel ratio:</label>\n        &nbsp;\n        <input\n          defaultValue={devicePixelRatio ? `${devicePixelRatio}` : ''}\n          id={devicePixelRatioId}\n          max={3}\n          min={1}\n          name=\"devicePixelRatio\"\n          ref={devicePixelRatioInput}\n          step={1}\n          type=\"number\"\n        />\n        &nbsp;\n        <button style={{ display: 'none' }} type=\"submit\">\n          Set device pixel ratio\n        </button>\n        <button disabled={devicePixelRatio === null} onClick={resetDevicePixelRatio} type=\"button\">\n          Reset device pixel ratio\n        </button>\n      </form>\n\n      <label htmlFor={renderCanvasId}>Render mode:</label>\n      <div>\n        <input\n          checked={!renderMode || renderMode === 'canvas'}\n          id={renderCanvasId}\n          name=\"renderMode\"\n          onChange={onRenderModeChange}\n          type=\"radio\"\n          value=\"canvas\"\n        />\n        <label htmlFor={renderCanvasId}>Canvas</label>\n      </div>\n      <div>\n        <input\n          checked={renderMode === 'custom'}\n          id={renderCustomId}\n          name=\"renderMode\"\n          onChange={onRenderModeChange}\n          type=\"radio\"\n          value=\"custom\"\n        />\n        <label htmlFor={renderCustomId}>Custom</label>\n      </div>\n      <div>\n        <input\n          checked={renderMode === 'none'}\n          id={renderNoneId}\n          name=\"renderMode\"\n          onChange={onRenderModeChange}\n          type=\"radio\"\n          value=\"none\"\n        />\n        <label htmlFor={renderNoneId}>None</label>\n      </div>\n\n      <div>\n        <label htmlFor={rotationId}>Rotation:</label>\n        <input\n          id={rotationId}\n          onChange={onChangeRotate}\n          step=\"90\"\n          style={{ width: '42px' }}\n          type=\"number\"\n          value={rotate !== null ? rotate : ''}\n        />\n        &nbsp;\n        <button onClick={rotateLeft} type=\"button\">\n          Rotate left\n        </button>\n        &nbsp;\n        <button onClick={rotateRight} type=\"button\">\n          Rotate right\n        </button>\n        &nbsp;\n        <button disabled={rotate === null} onClick={resetRotation} type=\"button\">\n          Reset rotation\n        </button>\n      </div>\n\n      <div>\n        <input\n          checked={renderHighContrast}\n          id={renderHighContrastId}\n          onChange={onRenderHighContrastChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={renderHighContrastId}>Use black/yellow page colors</label>\n      </div>\n\n      <div>\n        <input\n          checked={displayAll}\n          id={displayAllId}\n          onChange={onDisplayAllChange}\n          type=\"checkbox\"\n        />\n        <label htmlFor={displayAllId}>View all pages</label>\n      </div>\n    </fieldset>\n  );\n}\n"
  },
  {
    "path": "test/global.d.ts",
    "content": "declare module '*.pdf' {\n  const src: string;\n  export default src;\n}\n"
  },
  {
    "path": "test/index.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>react-pdf test page</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"./index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/index.tsx",
    "content": "import { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport Test from './Test.js';\n\nconst root = document.getElementById('root');\n\nif (!root) {\n  throw new Error('Could not find root element');\n}\n\ncreateRoot(root).render(\n  <StrictMode>\n    <Test />\n  </StrictMode>,\n);\n"
  },
  {
    "path": "test/package.json",
    "content": "{\n  \"name\": \"test\",\n  \"version\": \"4.0.0\",\n  \"description\": \"A test page for React-PDF.\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"vite build\",\n    \"dev\": \"vite\",\n    \"format\": \"biome format\",\n    \"lint\": \"biome lint\",\n    \"preview\": \"vite preview\",\n    \"test\": \"yarn lint && yarn tsc && yarn format\",\n    \"tsc\": \"tsc\"\n  },\n  \"author\": {\n    \"name\": \"Wojciech Maj\",\n    \"email\": \"kontakt@wojtekmaj.pl\"\n  },\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"react\": \"^19.2.0\",\n    \"react-dom\": \"^19.2.0\",\n    \"react-pdf\": \"workspace:packages/react-pdf\",\n    \"tiny-invariant\": \"^1.0.0\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"2.2.2\",\n    \"@types/react\": \"^19.2.0\",\n    \"@vitejs/plugin-react\": \"^4.6.0\",\n    \"typescript\": \"^5.9.2\",\n    \"vite\": \"^7.1.11\",\n    \"vite-plugin-static-copy\": \"^3.1.2\"\n  }\n}\n"
  },
  {
    "path": "test/shared/types.ts",
    "content": "import type { PDFDataRangeTransport } from 'pdfjs-dist';\nimport type { TypedArray } from 'pdfjs-dist/types/src/display/api.js';\n\ntype BinaryData = TypedArray | ArrayBuffer | number[] | string;\n\nexport type Source =\n  | { data: BinaryData | undefined }\n  | { range: PDFDataRangeTransport }\n  | { url: string };\n\nexport type ExternalLinkTarget = '_self' | '_blank' | '_parent' | '_top';\n\nexport type File = string | ArrayBuffer | Blob | Source | null;\n\nexport type PassMethod = 'normal' | 'object' | 'string';\n\nexport type RenderMode = 'canvas' | 'custom' | 'none';\n"
  },
  {
    "path": "test/shared/utils.ts",
    "content": "import invariant from 'tiny-invariant';\n\n/**\n * Checks if we're running in a browser environment.\n */\nexport const isBrowser = typeof window !== 'undefined';\n\n/**\n * Checks whether a variable provided is a string.\n *\n * @param {*} variable Variable to check\n */\nexport function isString(variable: unknown): variable is string {\n  return typeof variable === 'string';\n}\n\n/**\n * Checks whether a variable provided is an ArrayBuffer.\n *\n * @param {*} variable Variable to check\n */\nexport function isArrayBuffer(variable: unknown): variable is ArrayBuffer {\n  return variable instanceof ArrayBuffer;\n}\n\n/**\n * Checks whether a variable provided is a Blob.\n *\n * @param {*} variable Variable to check\n */\nexport function isBlob(variable: unknown): variable is Blob {\n  invariant(isBrowser, 'isBlob can only be used in a browser environment');\n\n  return variable instanceof Blob;\n}\n\n/**\n * Checks whether a variable provided is a data URI.\n *\n * @param {*} variable String to check\n */\nexport function isDataURI(variable: unknown): variable is `data:${string}` {\n  return isString(variable) && variable.startsWith('data:');\n}\n\nexport function loadFromFile(file: Blob): Promise<ArrayBuffer> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n\n    reader.onload = () => {\n      if (!reader.result) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      resolve(reader.result as ArrayBuffer);\n    };\n\n    reader.onerror = (event) => {\n      if (!event.target) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      const { error } = event.target;\n\n      if (!error) {\n        return reject(new Error('Error while reading a file.'));\n      }\n\n      switch (error.code) {\n        case error.NOT_FOUND_ERR:\n          return reject(new Error('Error while reading a file: File not found.'));\n        case error.SECURITY_ERR:\n          return reject(new Error('Error while reading a file: Security error.'));\n        case error.ABORT_ERR:\n          return reject(new Error('Error while reading a file: Aborted.'));\n        default:\n          return reject(new Error('Error while reading a file.'));\n      }\n    };\n\n    reader.readAsArrayBuffer(file);\n  });\n}\n"
  },
  {
    "path": "test/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"module\": \"preserve\",\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"esnext\",\n    \"verbatimModuleSyntax\": true\n  }\n}\n"
  },
  {
    "path": "test/vite.config.ts",
    "content": "import path from 'node:path';\nimport { createRequire } from 'node:module';\n\nimport { defineConfig, normalizePath } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport { viteStaticCopy } from 'vite-plugin-static-copy';\n\nconst require = createRequire(import.meta.url);\nconst cMapsDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps'),\n);\nconst standardFontsDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts'),\n);\nconst wasmDir = normalizePath(\n  path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'wasm'),\n);\n\nexport default defineConfig({\n  base: './',\n  plugins: [\n    react(),\n    viteStaticCopy({\n      targets: [\n        { src: cMapsDir, dest: '' },\n        { src: standardFontsDir, dest: '' },\n        { src: wasmDir, dest: '' },\n      ],\n    }),\n  ],\n  server: {\n    fs: {\n      allow: ['..'],\n    },\n  },\n});\n"
  },
  {
    "path": "test-utils.ts",
    "content": "import { vi } from 'vitest';\nimport { server } from 'vitest/browser';\n\nconst { readFile } = server.commands;\n\ntype Func<T extends unknown[]> = (...args: T) => void;\n\nfunction makeAsyncCallbackWithoutValue<T extends unknown[]>(): {\n  func: Func<T>;\n  promise: Promise<T>;\n} {\n  let promiseResolve: (args: T) => void;\n  const promise = new Promise<T>((resolve) => {\n    promiseResolve = resolve;\n  });\n  const func: Func<T> = vi.fn((...args: T) => promiseResolve(args));\n\n  return { func, promise };\n}\n\nfunction makeAsyncCallbackWithValue<T>(value: T): {\n  func: Func<never[]>;\n  promise: Promise<T>;\n} {\n  let promiseResolve: (arg: T) => void;\n  const promise = new Promise<T>((resolve) => {\n    promiseResolve = resolve;\n  });\n  const func: Func<never[]> = vi.fn(() => promiseResolve(value));\n\n  return { func, promise };\n}\n\nexport function makeAsyncCallback<T extends unknown[]>(): ReturnType<\n  typeof makeAsyncCallbackWithoutValue<T>\n>;\nexport function makeAsyncCallback<T>(value?: T): ReturnType<typeof makeAsyncCallbackWithValue<T>>;\nexport function makeAsyncCallback<T>(value?: T) {\n  if (value === undefined) {\n    return makeAsyncCallbackWithoutValue();\n  }\n  return makeAsyncCallbackWithValue<T>(value);\n}\n\nexport async function loadPDF(path: string): Promise<{\n  raw: string;\n  readonly arrayBuffer: ArrayBuffer;\n  readonly blob: Blob;\n  readonly data: Uint8Array<ArrayBuffer>;\n  readonly dataURI: string;\n  readonly file: File;\n}> {\n  const raw = await readFile(path, 'binary');\n\n  // Convert binary read as string to ArrayBuffer\n  const arrayBuffer = new Uint8Array(raw.length);\n  for (let i = 0; i < raw.length; i += 1) {\n    arrayBuffer[i] = raw.charCodeAt(i) & 0xff;\n  }\n\n  return {\n    raw,\n    get arrayBuffer() {\n      return arrayBuffer.buffer.slice(0);\n    },\n    get blob() {\n      return new Blob([arrayBuffer], { type: 'application/pdf' });\n    },\n    get data() {\n      return new Uint8Array(arrayBuffer);\n    },\n    get dataURI() {\n      return `data:application/pdf;base64,${btoa(raw)}`;\n    },\n    get file() {\n      return new File([arrayBuffer], 'test.pdf', { type: 'application/pdf' });\n    },\n  };\n}\n\nexport function muteConsole(): void {\n  vi.spyOn(globalThis.console, 'log').mockImplementation(() => {\n    // Intentionally empty\n  });\n  vi.spyOn(globalThis.console, 'error').mockImplementation(() => {\n    // Intentionally empty\n  });\n  vi.spyOn(globalThis.console, 'warn').mockImplementation(() => {\n    // Intentionally empty\n  });\n}\n\nexport function restoreConsole(): void {\n  vi.mocked(globalThis.console.log).mockRestore();\n  vi.mocked(globalThis.console.error).mockRestore();\n  vi.mocked(globalThis.console.warn).mockRestore();\n}\n"
  }
]