Compare commits

..

No commits in common. "master" and "v3.0.279" have entirely different histories.

780 changed files with 82532 additions and 99788 deletions

View File

@ -1,6 +1,6 @@
root = true root = true
[*.{js,jsm,mjs,json,html,css,pdf.link}] [*.{js,jsm,json,html,css,pdf.link}]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
indent_size = 2 indent_size = 2

View File

@ -3,10 +3,12 @@ l10n/
docs/ docs/
node_modules/ node_modules/
external/bcmaps/ external/bcmaps/
external/webL10n/
external/builder/fixtures/ external/builder/fixtures/
external/builder/fixtures_esprima/ external/builder/fixtures_esprima/
external/quickjs/ external/quickjs/
src/shared/cffStandardStrings.js
src/shared/fonts_utils.js
test/tmp/ test/tmp/
test/pdfs/ test/pdfs/
web/locale/
*~/ *~/

View File

@ -22,27 +22,20 @@
"browser": true, "browser": true,
"es2022": true, "es2022": true,
"worker": true, "worker": true,
"amd": true,
}, },
"globals": { "globals": {
"PDFJSDev": "readonly", "PDFJSDev": false,
"__non_webpack_import__": "readonly", "exports": false,
"SystemJS": false,
}, },
"rules": { "rules": {
// Plugins // Plugins
"import/export": "error",
"import/exports-last": "error",
"import/extensions": ["error", "always", { "ignorePackages": true, }], "import/extensions": ["error", "always", { "ignorePackages": true, }],
"import/first": "error",
"import/named": "error",
"import/no-cycle": "error",
"import/no-empty-named-blocks": "error",
"import/no-commonjs": "error",
"import/no-mutable-exports": "error",
"import/no-self-import": "error",
"import/no-unresolved": ["error", { "import/no-unresolved": ["error", {
"ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web", "fluent-bundle", "fluent-dom"], "ignore": ["pdfjs", "pdfjs-lib", "pdfjs-web", "pdfjs-fitCurve"]
}], }],
"mozilla/avoid-removeChild": "error", "mozilla/avoid-removeChild": "error",
"mozilla/use-includes-instead-of-indexOf": "error", "mozilla/use-includes-instead-of-indexOf": "error",
@ -54,31 +47,20 @@
"unicorn/no-abusive-eslint-disable": "error", "unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-push-push": "error", "unicorn/no-array-push-push": "error",
"unicorn/no-instanceof-array": "error", "unicorn/no-instanceof-array": "error",
"unicorn/no-invalid-remove-event-listener": "error",
"unicorn/no-new-buffer": "error", "unicorn/no-new-buffer": "error",
"unicorn/no-typeof-undefined": ["error", {
"checkGlobalVariables": false,
}],
"unicorn/no-useless-promise-resolve-reject": "error",
"unicorn/no-useless-spread": "error", "unicorn/no-useless-spread": "error",
"unicorn/prefer-array-find": "error", "unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat": "error", "unicorn/prefer-array-flat": "error",
"unicorn/prefer-array-flat-map": "error", "unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error", "unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-at": "error", "unicorn/prefer-at": "error",
"unicorn/prefer-date-now": "error", "unicorn/prefer-date-now": "error",
"unicorn/prefer-dom-node-append": "error", "unicorn/prefer-dom-node-append": "error",
"unicorn/prefer-dom-node-remove": "error", "unicorn/prefer-dom-node-remove": "error",
"unicorn/prefer-logical-operator-over-ternary": "error", "unicorn/prefer-logical-operator-over-ternary": "error",
"unicorn/prefer-modern-dom-apis": "error", "unicorn/prefer-modern-dom-apis": "error",
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-optional-catch-binding": "error",
"unicorn/prefer-regexp-test": "error", "unicorn/prefer-regexp-test": "error",
"unicorn/prefer-string-replace-all": "error",
"unicorn/prefer-string-starts-ends-with": "error", "unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-ternary": ["error", "only-single-line"],
// Possible errors // Possible errors
"for-direction": "error", "for-direction": "error",
@ -140,12 +122,10 @@
"no-implied-eval": "error", "no-implied-eval": "error",
"no-iterator": "error", "no-iterator": "error",
"no-lone-blocks": "error", "no-lone-blocks": "error",
"no-lonely-if": "error",
"no-multi-str": "error", "no-multi-str": "error",
"no-new": "error",
"no-new-func": "error", "no-new-func": "error",
"no-new-symbol": "error",
"no-new-wrappers": "error", "no-new-wrappers": "error",
"no-new": "error",
"no-octal-escape": "error", "no-octal-escape": "error",
"no-octal": "error", "no-octal": "error",
"no-redeclare": "error", "no-redeclare": "error",
@ -178,7 +158,7 @@
"no-undef-init": "error", "no-undef-init": "error",
"no-undef": ["error", { "typeof": true, }], "no-undef": ["error", { "typeof": true, }],
"no-unused-vars": ["error", { "no-unused-vars": ["error", {
"vars": "all", "vars": "local",
"args": "none", "args": "none",
}], }],
"no-use-before-define": ["error", { "no-use-before-define": ["error", {
@ -243,9 +223,9 @@
}], }],
// ECMAScript 6 // ECMAScript 6
"arrow-body-style": ["error", "as-needed"],
"constructor-super": "error", "constructor-super": "error",
"no-class-assign": "error", "no-class-assign": "error",
"no-confusing-arrow": "error",
"no-const-assign": "error", "no-const-assign": "error",
"no-dupe-class-members": "error", "no-dupe-class-members": "error",
"no-duplicate-imports": "error", "no-duplicate-imports": "error",
@ -258,7 +238,6 @@
"avoidQuotes": true, "avoidQuotes": true,
}], }],
"prefer-const": "error", "prefer-const": "error",
"require-yield": "error",
"sort-imports": ["error", { "sort-imports": ["error", {
"ignoreCase": true, "ignoreCase": true,
}], }],

View File

@ -1,6 +0,0 @@
# Auto-format `.js` files with ESLint/Prettier
de36b2aabab2b7fd647d9591f959c4540129541d
# Auto-format `.css` files with Stylelint/Prettier
8aa2718d225ad701a5b8a2788b42d221f1e4327d
# Auto-format `.json` files with Prettier
29de9bdce6c9785574994fda0e51533d796a9bb4

2
.gitattributes vendored
View File

@ -4,7 +4,7 @@
*.css text eol=lf *.css text eol=lf
*.html text eol=lf *.html text eol=lf
*.md text eol=lf *.md text eol=lf
*.ftl text eol=lf *.properties text eol=lf
*.yml text eol=lf *.yml text eol=lf
*.json text eol=lf *.json text eol=lf
*.config text eol=lf *.config text eol=lf

1
.github/CODEOWNERS vendored
View File

@ -1 +0,0 @@
l10n/en-US/*.ftl @mozilla/pdfjs-l10n

View File

@ -4,5 +4,3 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
labels:
- "dependencies"

View File

@ -1,27 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# See https://github.com/mozilla-l10n/moz-fluent-linter/blob/main/src/fluent_linter/config.yml
# for details
---
ID01:
enabled: true
exclusions:
messages: []
files: []
ID02:
enabled: true
min_length: 9
exclusions:
messages: []
files: []
CO01:
enabled: true
brands:
- Firefox
- Mozilla
exclusions:
files: []
messages: []

View File

@ -1 +0,0 @@
moz-fluent-linter==0.4.*

View File

@ -8,30 +8,20 @@ jobs:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, lts/*, latest]
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js 16 LTS
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: '16'
- name: Install Gulp - name: Install Gulp
run: npm install -g gulp-cli run: npm install -g gulp-cli
- name: Install other dependencies - name: Install other dependencies
run: npm install run: npm install -f
- name: Run external tests - name: Run tests
run: gulp externaltest run: gulp ci-test
- name: Run CLI unit tests
run: gulp unittestcli

View File

@ -18,18 +18,16 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: security-and-quality queries: security-and-quality
- name: Autobuild CodeQL - name: Autobuild CodeQL
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL analysis - name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v2

View File

@ -1,42 +0,0 @@
name: Lint Fluent Reference Files
on:
push:
paths:
- 'l10n/en-US/**.ftl'
- '.github/fluent_linter_config.yml'
- '.github/workflows/fluent_linter.yml'
branches:
- master
pull_request:
paths:
- 'l10n/en-US/**.ftl'
- '.github/fluent_linter_config.yml'
- '.github/workflows/fluent_linter.yml'
branches:
- master
workflow_dispatch:
permissions:
contents: read
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Use Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install Fluent dependencies
run: |
pip install -r .github/requirements.txt
- name: Lint Fluent reference files
run: |
moz-fluent-lint ./l10n/en-US --config .github/fluent_linter_config.yml

View File

@ -1,64 +0,0 @@
name: Font tests
on:
push:
paths:
- 'gulpfile.mjs'
- 'src/**'
- 'test/test.mjs'
- 'test/font/**'
- '.github/workflows/font_tests.yml'
branches:
- master
pull_request:
paths:
- 'gulpfile.mjs'
- 'src/**'
- 'test/test.mjs'
- 'test/font/**'
- '.github/workflows/font_tests.yml'
branches:
- master
workflow_dispatch:
permissions:
contents: read
jobs:
test:
name: Test
strategy:
fail-fast: false
matrix:
node-version: [lts/*]
os: [windows-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Gulp
run: npm install -g gulp-cli
- name: Install other dependencies
run: npm install
- name: Use Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install Fonttools
run: pip install fonttools
- name: Run font tests
run: gulp fonttest --headless

View File

@ -1,37 +0,0 @@
name: Lint
on: [push, pull_request]
permissions:
contents: read
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [lts/*]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Gulp
run: npm install -g gulp-cli
- name: Install other dependencies
run: npm install
- name: Run lint
run: gulp lint
- name: Run lint-chromium
run: gulp lint-chromium

View File

@ -1,72 +0,0 @@
name: Publish website
on:
push:
branches:
- master
permissions:
contents: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [lts/*]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Gulp
run: npm install -g gulp-cli
- name: Install other dependencies
run: npm install
- name: Build the website
run: gulp web
- name: Archive the website
shell: sh
run: |
chmod -c -R +rX "$INPUT_PATH" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
tar \
--dereference --hard-dereference \
--directory "$INPUT_PATH" \
-cvf "$RUNNER_TEMP/website.tar" \
--exclude=.git \
--exclude=.github \
.
env:
INPUT_PATH: build/gh-pages
- name: Upload the website
uses: actions/upload-artifact@v4
with:
name: github-pages
path: ${{ runner.temp }}/website.tar
retention-days: 1
if-no-files-found: error
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
permissions:
pages: write # Required to deploy to GitHub Pages.
id-token: write # Required to verify that the deployment originates from this workflow.
steps:
- name: Deploy the website
uses: actions/deploy-pages@v4

View File

@ -1,34 +0,0 @@
name: Types tests
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [lts/*]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Gulp
run: npm install -g gulp-cli
- name: Install other dependencies
run: npm install
- name: Run types tests
run: gulp typestest

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "test/ttx/fonttools-code"]
path = test/ttx/fonttools-code
url = https://github.com/behdad/fonttools.git

View File

@ -1,12 +0,0 @@
build/
l10n/
docs/
node_modules/
external/bcmaps/
external/builder/fixtures/
external/builder/fixtures_esprima/
external/quickjs/
test/tmp/
test/pdfs/
web/locale/
*~/

View File

@ -5,14 +5,5 @@
"semi": true, "semi": true,
"tabWidth": 2, "tabWidth": 2,
"trailingComma": "es5", "trailingComma": "es5",
"useTabs": false, "useTabs": false
"overrides": [
{
files: ["tsconfig.json"],
options: {
parser: "json",
},
},
]
} }

View File

@ -3,10 +3,12 @@ l10n/
docs/ docs/
node_modules/ node_modules/
external/bcmaps/ external/bcmaps/
external/webL10n/
external/builder/fixtures/ external/builder/fixtures/
external/builder/fixtures_esprima/ external/builder/fixtures_esprima/
external/quickjs/ external/quickjs/
src/shared/cffStandardStrings.js
src/shared/fonts_utils.js
test/tmp/ test/tmp/
test/pdfs/ test/pdfs/
web/locale/
*~/ *~/

View File

@ -3,23 +3,16 @@
"stylelint-prettier" "stylelint-prettier"
], ],
"rules": { "extends": [
"prettier/prettier": true, "stylelint-prettier/recommended"
],
"alpha-value-notation": "number",
"rules": {
"block-no-empty": true, "block-no-empty": true,
"color-function-notation": "modern",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"declaration-block-no-duplicate-properties": true,
"declaration-block-no-redundant-longhand-properties": true,
"declaration-property-value-disallowed-list": {
"float": ["inline-start", "inline-end"]
},
"length-zero-no-unit": [true, { "length-zero-no-unit": [true, {
ignore: ["custom-properties"] ignore: ["custom-properties"]
}], }],
"selector-pseudo-element-colon-notation": "double",
"shorthand-property-no-redundant-values": true, "shorthand-property-no-redundant-values": true,
}, },
} }

View File

@ -23,8 +23,9 @@ Feel free to stop by our [Matrix room](https://chat.mozilla.org/#/room/#pdfjs:mo
### Online demo ### Online demo
Please note that the "Modern browsers" version assumes native support for the Please note that the "Modern browsers" version assumes native support for
latest JavaScript features; please also see [this wiki page](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support). features such as optional chaining, nullish coalescing,
and private `class` fields/methods; please also see [this wiki page](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support).
+ Modern browsers: https://mozilla.github.io/pdf.js/web/viewer.html + Modern browsers: https://mozilla.github.io/pdf.js/web/viewer.html
@ -38,8 +39,6 @@ PDF.js is built into version 19+ of Firefox.
#### Chrome #### Chrome
+ The official extension for Chrome can be installed from the [Chrome Web Store](https://chrome.google.com/webstore/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm).
*This extension is maintained by [@Rob--W](https://github.com/Rob--W).*
+ Build Your Own - Get the code as explained below and issue `gulp chromium`. Then open + Build Your Own - Get the code as explained below and issue `gulp chromium`. Then open
Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the
directory `build/chromium`. directory `build/chromium`.
@ -53,19 +52,14 @@ To get a local copy of the current code, clone it using git:
Next, install Node.js via the [official package](https://nodejs.org) or via Next, install Node.js via the [official package](https://nodejs.org) or via
[nvm](https://github.com/creationix/nvm). You need to install the gulp package [nvm](https://github.com/creationix/nvm). You need to install the gulp package
globally (see also [gulp's getting started](https://github.com/gulpjs/gulp/tree/master/docs/getting-started)): globally (see also [gulp's getting started](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md#getting-started)):
$ npm install -g gulp-cli@^2.3.0 $ npm install -g gulp-cli
If you prefer to not install `gulp-cli` globally, you have to prefix all the `gulp` commands with `npx` (for example, `npx gulp server` instead of `gulp server`).
If everything worked out, install all dependencies for PDF.js: If everything worked out, install all dependencies for PDF.js:
$ npm install $ npm install
> [!NOTE]
> On MacOS M1/M2 you may see some `node-gyp`-related errors when running `npm install`. This is because one of our dependencies, `"canvas"`, does not provide pre-built binaries for this platform and instead `npm` will try to build it from source. Please make sure to first install the necessary native dependencies using `brew`: https://github.com/Automattic/node-canvas#compiling.
Finally, you need to start a local web server as some browsers do not allow opening Finally, you need to start a local web server as some browsers do not allow opening
PDF files using a `file://` URL. Run: PDF files using a `file://` URL. Run:
@ -75,7 +69,7 @@ and then you can open:
+ http://localhost:8888/web/viewer.html + http://localhost:8888/web/viewer.html
Please keep in mind that this assumes the latest version of Mozilla Firefox; refer to [Building PDF.js](https://github.com/mozilla/pdf.js/blob/master/README.md#building-pdfjs) for non-development usage of the PDF.js library. Please keep in mind that this requires a modern and fully up-to-date browser; refer to [Building PDF.js](https://github.com/mozilla/pdf.js/blob/master/README.md#building-pdfjs) for non-development usage of the PDF.js library.
It is also possible to view all test PDF files on the right side by opening: It is also possible to view all test PDF files on the right side by opening:
@ -144,3 +138,7 @@ Talk to us on Matrix:
File an issue: File an issue:
+ https://github.com/mozilla/pdf.js/issues/new + https://github.com/mozilla/pdf.js/issues/new
Follow us on Twitter: @pdfjs
+ https://twitter.com/pdfjs

View File

@ -83,17 +83,17 @@ var scaledViewport = page.getViewport({ scale: scale, });
The example demonstrates how promises can be used to handle errors during loading. The example demonstrates how promises can be used to handle errors during loading.
It also demonstrates how to wait until a page is loaded and rendered. It also demonstrates how to wait until a page is loaded and rendered.
<script async src="//jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/9engc9mw/embed/js,html,css,result/"></script>
### Hello World using base64 encoded PDF ### Hello World using base64 encoded PDF
The PDF.js can accept any decoded base64 data as an array. The PDF.js can accept any decoded base64 data as an array.
<script async src="//jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/cq0asLqz/embed/js,html,css,result/"></script>
### Previous/Next example ### Previous/Next example
The same canvas cannot be used to perform to draw two pages at the same time -- The same canvas cannot be used to perform to draw two pages at the same time --
the example demonstrates how to wait on previous operation to be complete. the example demonstrates how to wait on previous operation to be complete.
<script async src="//jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/wagvs9Lf/embed/js,html,css,result/"></script>

View File

@ -108,7 +108,8 @@ Note that we only mention the most relevant files and folders.
│   ├── display/ - display layer │   ├── display/ - display layer
│   ├── shared/ - shared code between the core and display layers │   ├── shared/ - shared code between the core and display layers
│   ├── interfaces.js - interface definitions for the core/display layers │   ├── interfaces.js - interface definitions for the core/display layers
│   └── pdf.*.js - wrapper files for bundling │   ├── pdf.*.js - wrapper files for bundling
│   └── worker_loader.js - used for developer builds to load worker files
├── test/ - unit, font, reference, and integration tests ├── test/ - unit, font, reference, and integration tests
├── web/ - viewer layer ├── web/ - viewer layer
├── LICENSE ├── LICENSE

View File

@ -31,13 +31,13 @@ limitations under the License.
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css"> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<div id="pageContainer" class="pdfViewer singlePageView"></div> <div id="pageContainer" class="pdfViewer singlePageView"></div>
<script src="pageviewer.mjs" type="module"></script> <script src="pageviewer.js"></script>
</body> </body>
</html> </html>

View File

@ -13,6 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
"use strict";
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) { if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`"); alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
// The workerSrc property shall be specified. // The workerSrc property shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs"; "../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps. // Some PDFs need external cmaps.
// //
@ -45,11 +47,10 @@ const loadingTask = pdfjsLib.getDocument({
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA, enableXfa: ENABLE_XFA,
}); });
(async function () {
const pdfDocument = await loadingTask.promise; const pdfDocument = await loadingTask.promise;
// Document loaded, retrieving the page. // Document loaded, retrieving the page.
const pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW); const pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW);
// Creating the page view with default parameters. // Creating the page view with default parameters.
const pdfPageView = new pdfjsViewer.PDFPageView({ const pdfPageView = new pdfjsViewer.PDFPageView({
container, container,
@ -57,7 +58,17 @@ const pdfPageView = new pdfjsViewer.PDFPageView({
scale: SCALE, scale: SCALE,
defaultViewport: pdfPage.getViewport({ scale: SCALE }), defaultViewport: pdfPage.getViewport({ scale: SCALE }),
eventBus, eventBus,
// We can enable text/annotation/xfa/struct-layers, as needed.
textLayerFactory: !pdfDocument.isPureXfa
? new pdfjsViewer.DefaultTextLayerFactory()
: null,
annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(),
xfaLayerFactory: pdfDocument.isPureXfa
? new pdfjsViewer.DefaultXfaLayerFactory()
: null,
structTreeLayerFactory: new pdfjsViewer.DefaultStructTreeLayerFactory(),
}); });
// Associate the actual page with the view, and draw it. // Associate the actual page with the view, and draw it.
pdfPageView.setPdfPage(pdfPage); pdfPageView.setPdfPage(pdfPage);
pdfPageView.draw(); return pdfPageView.draw();
})();

View File

@ -37,8 +37,8 @@ limitations under the License.
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css"> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
@ -46,6 +46,6 @@ limitations under the License.
<div id="viewer" class="pdfViewer"></div> <div id="viewer" class="pdfViewer"></div>
</div> </div>
<script src="simpleviewer.mjs" type="module"></script> <script src="simpleviewer.js"></script>
</body> </body>
</html> </html>

View File

@ -13,6 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
"use strict";
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) { if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`"); alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
// The workerSrc property shall be specified. // The workerSrc property shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs"; "../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps. // Some PDFs need external cmaps.
// //
@ -35,10 +37,7 @@ const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
const ENABLE_XFA = true; const ENABLE_XFA = true;
const SEARCH_FOR = ""; // try "Mozilla"; const SEARCH_FOR = ""; // try "Mozilla";
const SANDBOX_BUNDLE_SRC = new URL( const SANDBOX_BUNDLE_SRC = "../../node_modules/pdfjs-dist/build/pdf.sandbox.js";
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
window.location
);
const container = document.getElementById("viewerContainer"); const container = document.getElementById("viewerContainer");
@ -88,10 +87,11 @@ const loadingTask = pdfjsLib.getDocument({
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA, enableXfa: ENABLE_XFA,
}); });
(async function () {
const pdfDocument = await loadingTask.promise; const pdfDocument = await loadingTask.promise;
// Document loaded, specifying document for the viewer and // Document loaded, specifying document for the viewer and
// the (optional) linkService. // the (optional) linkService.
pdfViewer.setDocument(pdfDocument); pdfViewer.setDocument(pdfDocument);
pdfLinkService.setDocument(pdfDocument, null); pdfLinkService.setDocument(pdfDocument, null);
})();

View File

@ -37,8 +37,8 @@ limitations under the License.
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css"> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
@ -46,6 +46,6 @@ limitations under the License.
<div id="viewer" class="pdfViewer"></div> <div id="viewer" class="pdfViewer"></div>
</div> </div>
<script src="singlepageviewer.mjs" type="module"></script> <script src="singlepageviewer.js"></script>
</body> </body>
</html> </html>

View File

@ -13,6 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
"use strict";
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) { if (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`"); alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
// The workerSrc property shall be specified. // The workerSrc property shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs"; "../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps. // Some PDFs need external cmaps.
// //
@ -35,10 +37,7 @@ const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
const ENABLE_XFA = true; const ENABLE_XFA = true;
const SEARCH_FOR = ""; // try "Mozilla"; const SEARCH_FOR = ""; // try "Mozilla";
const SANDBOX_BUNDLE_SRC = new URL( const SANDBOX_BUNDLE_SRC = "../../node_modules/pdfjs-dist/build/pdf.sandbox.js";
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
window.location
);
const container = document.getElementById("viewerContainer"); const container = document.getElementById("viewerContainer");
@ -88,10 +87,10 @@ const loadingTask = pdfjsLib.getDocument({
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA, enableXfa: ENABLE_XFA,
}); });
loadingTask.promise.then(function (pdfDocument) {
const pdfDocument = await loadingTask.promise;
// Document loaded, specifying document for the viewer and // Document loaded, specifying document for the viewer and
// the (optional) linkService. // the (optional) linkService.
pdfSinglePageViewer.setDocument(pdfDocument); pdfSinglePageViewer.setDocument(pdfDocument);
pdfLinkService.setDocument(pdfDocument, null); pdfLinkService.setDocument(pdfDocument, null);
});

View File

@ -29,12 +29,12 @@ limitations under the License.
} }
</style> </style>
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.js"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<canvas id="jpegCanvas" width="0" height="0"></canvas> <canvas id="jpegCanvas" width="0" height="0"></canvas>
<script src="jpeg_viewer.mjs" type="module"></script> <script src="jpeg_viewer.js"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,63 @@
/* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
if (!pdfjsImageDecoders.JpegImage) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using `gulp dist-install`");
}
const JPEG_IMAGE = "fish.jpg";
const jpegCanvas = document.getElementById("jpegCanvas");
const jpegCtx = jpegCanvas.getContext("2d");
(async function () {
// Load the image data, and convert it to a Uint8Array.
//
const response = await fetch(JPEG_IMAGE);
if (!response.ok) {
throw new Error(response.statusText);
}
const typedArrayImage = new Uint8Array(await response.arrayBuffer());
// Parse the image data using `JpegImage`.
//
const jpegImage = new pdfjsImageDecoders.JpegImage();
jpegImage.parse(typedArrayImage);
const width = jpegImage.width,
height = jpegImage.height;
const jpegData = jpegImage.getData({
width,
height,
forceRGB: true,
});
// Render the JPEG image on a <canvas>.
//
const imageData = jpegCtx.createImageData(width, height);
const imageBytes = imageData.data;
for (let j = 0, k = 0, jj = width * height * 4; j < jj; ) {
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = 255;
}
jpegCanvas.width = width;
jpegCanvas.height = height;
jpegCtx.putImageData(imageData, 0, 0);
})();

View File

@ -1,59 +0,0 @@
/* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
if (!pdfjsImageDecoders.JpegImage) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using `gulp dist-install`");
}
const JPEG_IMAGE = "fish.jpg";
const jpegCanvas = document.getElementById("jpegCanvas");
const jpegCtx = jpegCanvas.getContext("2d");
// Load the image data, and convert it to a Uint8Array.
//
const response = await fetch(JPEG_IMAGE);
if (!response.ok) {
throw new Error(response.statusText);
}
const typedArrayImage = new Uint8Array(await response.arrayBuffer());
// Parse the image data using `JpegImage`.
//
const jpegImage = new pdfjsImageDecoders.JpegImage();
jpegImage.parse(typedArrayImage);
const width = jpegImage.width,
height = jpegImage.height;
const jpegData = jpegImage.getData({
width,
height,
forceRGB: true,
});
// Render the JPEG image on a <canvas>.
//
const imageData = jpegCtx.createImageData(width, height);
const imageBytes = imageData.data;
for (let j = 0, k = 0, jj = width * height * 4; j < jj; ) {
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++];
imageBytes[j++] = 255;
}
jpegCanvas.width = width;
jpegCanvas.height = height;
jpegCtx.putImageData(imageData, 0, 0);

View File

@ -10,9 +10,9 @@
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas> <canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script id="script" type="module"> <script id="script">
// //
// If absolute URL from the remote server is provided, configure the CORS // If absolute URL from the remote server is provided, configure the CORS
// header on that server. // header on that server.
@ -23,12 +23,13 @@
// The workerSrc property shall be specified. // The workerSrc property shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs'; '../../node_modules/pdfjs-dist/build/pdf.worker.js';
// //
// Asynchronous download PDF // Asynchronous download PDF
// //
const loadingTask = pdfjsLib.getDocument(url); const loadingTask = pdfjsLib.getDocument(url);
(async () => {
const pdf = await loadingTask.promise; const pdf = await loadingTask.promise;
// //
// Fetch the first page // Fetch the first page
@ -63,6 +64,7 @@
viewport, viewport,
}; };
page.render(renderContext); page.render(renderContext);
})();
</script> </script>
<hr> <hr>

View File

@ -10,9 +10,9 @@
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas> <canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script id="script" type="module"> <script id="script">
// atob() is used to convert base64 encoded PDF to binary-like data. // atob() is used to convert base64 encoded PDF to binary-like data.
// (See also https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/ // (See also https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/
// Base64_encoding_and_decoding.) // Base64_encoding_and_decoding.)
@ -35,11 +35,12 @@
// The workerSrc property shall be specified. // The workerSrc property shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs'; '../../node_modules/pdfjs-dist/build/pdf.worker.js';
// Opening PDF by passing its binary data as a string. It is still preferable // Opening PDF by passing its binary data as a string. It is still preferable
// to use Uint8Array, but string or array-like structure will work too. // to use Uint8Array, but string or array-like structure will work too.
var loadingTask = pdfjsLib.getDocument({ data: pdfData, }); var loadingTask = pdfjsLib.getDocument({ data: pdfData, });
(async function() {
var pdf = await loadingTask.promise; var pdf = await loadingTask.promise;
// Fetch the first page. // Fetch the first page.
var page = await pdf.getPage(1); var page = await pdf.getPage(1);
@ -68,6 +69,7 @@
viewport, viewport,
}; };
page.render(renderContext); page.render(renderContext);
})();
</script> </script>
<hr> <hr>

View File

@ -19,9 +19,9 @@
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas> <canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
</div> </div>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script id="script" type="module"> <script id="script">
// //
// If absolute URL from the remote server is provided, configure the CORS // If absolute URL from the remote server is provided, configure the CORS
// header on that server. // header on that server.
@ -34,7 +34,7 @@
// shall be specified. // shall be specified.
// //
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs'; '../../node_modules/pdfjs-dist/build/pdf.worker.js';
var pdfDoc = null, var pdfDoc = null,
pageNum = 1, pageNum = 1,
@ -128,11 +128,13 @@
* Asynchronously downloads PDF. * Asynchronously downloads PDF.
*/ */
var loadingTask = pdfjsLib.getDocument(url); var loadingTask = pdfjsLib.getDocument(url);
pdfDoc = await loadingTask.promise; loadingTask.promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages; document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering // Initial/first page rendering
renderPage(pageNum); renderPage(pageNum);
});
</script> </script>
</body> </body>

View File

@ -13,6 +13,10 @@
* limitations under the License. * limitations under the License.
*/ */
:root {
--progressBar-percent: 0%;
}
* { * {
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -26,12 +30,12 @@ html {
} }
header { header {
background-color: rgb(244 244 244 / 1); background-color: rgba(244, 244, 244, 1);
} }
header h1 { header h1 {
border-bottom: 1px solid rgb(216 216 216 / 1); border-bottom: 1px solid rgba(216, 216, 216, 1);
color: rgb(133 133 133 / 1); color: rgba(133, 133, 133, 1);
font-size: 23px; font-size: 23px;
font-style: italic; font-style: italic;
font-weight: normal; font-weight: normal;
@ -44,7 +48,7 @@ header h1 {
body { body {
background: url(images/document_bg.png); background: url(images/document_bg.png);
color: rgb(255 255 255 / 1); color: rgba(255, 255, 255, 1);
font-family: sans-serif; font-family: sans-serif;
font-size: 10px; font-size: 10px;
height: 100%; height: 100%;
@ -71,7 +75,7 @@ footer {
left: 0; left: 0;
right: 0; right: 0;
z-index: 1; z-index: 1;
box-shadow: 0 -0.2rem 0.5rem rgb(50 50 50 / 0.75); box-shadow: 0 -0.2rem 0.5rem rgba(50, 50, 50, 0.75);
} }
.toolbarButton { .toolbarButton {
@ -81,7 +85,7 @@ footer {
border-width: 0; border-width: 0;
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: rgb(0 0 0 / 0); background-color: rgba(0, 0, 0, 0);
} }
.toolbarButton.pageUp { .toolbarButton.pageUp {
@ -110,9 +114,9 @@ footer {
left: 36%; left: 36%;
text-align: center; text-align: center;
border: 0; border: 0;
background-color: rgb(0 0 0 / 0); background-color: rgba(0, 0, 0, 0);
font-size: 1.2rem; font-size: 1.2rem;
color: rgb(255 255 255 / 1); color: rgba(255, 255, 255, 1);
background-image: url(images/div_line_left.png), background-image: url(images/div_line_left.png),
url(images/div_line_right.png); url(images/div_line_right.png);
background-repeat: no-repeat; background-repeat: no-repeat;
@ -153,7 +157,10 @@ footer {
position: absolute; position: absolute;
overflow: auto; overflow: auto;
width: 100%; width: 100%;
inset: 5rem 0 4rem; top: 5rem;
bottom: 4rem;
left: 0;
right: 0;
} }
canvas { canvas {
@ -184,14 +191,10 @@ canvas {
} }
#loadingBar { #loadingBar {
/* Define this variable here, and not in :root, to avoid reflowing the
entire viewer when updating progress (see issue 15958). */
--progressBar-percent: 0%;
position: relative; position: relative;
height: 0.6rem; height: 0.6rem;
background-color: rgb(51 51 51 / 1); background-color: rgba(51, 51, 51, 1);
border-bottom: 1px solid rgb(51 51 51 / 1); border-bottom: 1px solid rgba(51, 51, 51, 1);
} }
#loadingBar .progress { #loadingBar .progress {
@ -201,7 +204,7 @@ canvas {
transform: scaleX(var(--progressBar-percent)); transform: scaleX(var(--progressBar-percent));
transform-origin: 0 0; transform-origin: 0 0;
height: 100%; height: 100%;
background-color: rgb(221 221 221 / 1); background-color: rgba(221, 221, 221, 1);
overflow: hidden; overflow: hidden;
transition: transform 200ms; transition: transform 200ms;
} }
@ -220,7 +223,7 @@ canvas {
#loadingBar.indeterminate .progress { #loadingBar.indeterminate .progress {
transform: none; transform: none;
background-color: rgb(153 153 153 / 1); background-color: rgba(153, 153, 153, 1);
transition: none; transition: none;
} }
@ -232,11 +235,39 @@ canvas {
width: 5rem; width: 5rem;
background-image: linear-gradient( background-image: linear-gradient(
to right, to right,
rgb(153 153 153 / 1) 0%, rgba(153, 153, 153, 1) 0%,
rgb(255 255 255 / 1) 50%, rgba(255, 255, 255, 1) 50%,
rgb(153 153 153 / 1) 100% rgba(153, 153, 153, 1) 100%
); );
background-size: 100% 100%; background-size: 100% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
animation: progressIndeterminate 2s linear infinite; animation: progressIndeterminate 2s linear infinite;
} }
#errorWrapper {
background: none repeat scroll 0 0 rgba(255, 85, 85, 1);
color: rgba(255, 255, 255, 1);
left: 0;
position: absolute;
right: 0;
top: 3.2rem;
z-index: 1000;
padding: 0.3rem;
font-size: 0.8em;
}
#errorMessageLeft {
float: left;
}
#errorMessageRight {
float: right;
}
#errorMoreInfo {
background-color: rgba(255, 255, 255, 1);
color: rgba(0, 0, 0, 1);
padding: 0.3rem;
margin: 0.3rem;
width: 98%;
}

View File

@ -24,8 +24,8 @@ limitations under the License.
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css"> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<link rel="stylesheet" type="text/css" href="viewer.css"> <link rel="stylesheet" type="text/css" href="viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
</head> </head>
<body> <body>
@ -42,6 +42,25 @@ limitations under the License.
<div class="glimmer"></div> <div class="glimmer"></div>
</div> </div>
<div id="errorWrapper" hidden="true">
<div id="errorMessageLeft">
<span id="errorMessage"></span>
<button id="errorShowMore">
More Information
</button>
<button id="errorShowLess">
Less Information
</button>
</div>
<div id="errorMessageRight">
<button id="errorClose">
Close
</button>
</div>
<div class="clearBoth"></div>
<textarea id="errorMoreInfo" hidden="true" readonly="readonly"></textarea>
</div>
<footer> <footer>
<button class="toolbarButton pageUp" title="Previous Page" id="previous"></button> <button class="toolbarButton pageUp" title="Previous Page" id="previous"></button>
<button class="toolbarButton pageDown" title="Next Page" id="next"></button> <button class="toolbarButton pageDown" title="Next Page" id="next"></button>
@ -52,6 +71,6 @@ limitations under the License.
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn"></button> <button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn"></button>
</footer> </footer>
<script src="viewer.mjs" type="module"></script> <script src="viewer.js"></script>
</body> </body>
</html> </html>

View File

@ -13,19 +13,21 @@
* limitations under the License. * limitations under the License.
*/ */
"use strict";
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) { if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`"); alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
} }
const MAX_CANVAS_PIXELS = 0; // CSS-only zooming. const USE_ONLY_CSS_ZOOM = true;
const TEXT_LAYER_MODE = 0; // DISABLE const TEXT_LAYER_MODE = 0; // DISABLE
const MAX_IMAGE_SIZE = 1024 * 1024; const MAX_IMAGE_SIZE = 1024 * 1024;
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/"; const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true; const CMAP_PACKED = true;
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs"; "../../node_modules/pdfjs-dist/build/pdf.worker.js";
const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf"; const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
const DEFAULT_SCALE_DELTA = 1.1; const DEFAULT_SCALE_DELTA = 1.1;
@ -87,17 +89,41 @@ const PDFViewerApplication = {
self.loadingBar.hide(); self.loadingBar.hide();
self.setTitleUsingMetadata(pdfDocument); self.setTitleUsingMetadata(pdfDocument);
}, },
function (reason) { function (exception) {
let key = "pdfjs-loading-error"; const message = exception && exception.message;
if (reason instanceof pdfjsLib.InvalidPDFException) { const l10n = self.l10n;
key = "pdfjs-invalid-file-error"; let loadingErrorMessage;
} else if (reason instanceof pdfjsLib.MissingPDFException) {
key = "pdfjs-missing-file-error"; if (exception instanceof pdfjsLib.InvalidPDFException) {
} else if (reason instanceof pdfjsLib.UnexpectedResponseException) { // change error message also for other builds
key = "pdfjs-unexpected-response-error"; loadingErrorMessage = l10n.get(
"invalid_file_error",
null,
"Invalid or corrupted PDF file."
);
} else if (exception instanceof pdfjsLib.MissingPDFException) {
// special message for missing PDFs
loadingErrorMessage = l10n.get(
"missing_file_error",
null,
"Missing PDF file."
);
} else if (exception instanceof pdfjsLib.UnexpectedResponseException) {
loadingErrorMessage = l10n.get(
"unexpected_response_error",
null,
"Unexpected server response."
);
} else {
loadingErrorMessage = l10n.get(
"loading_error",
null,
"An error occurred while loading the PDF."
);
} }
self.l10n.get(key).then(msg => {
self.error(msg, { message: reason?.message }); loadingErrorMessage.then(function (msg) {
self.error(msg, { message });
}); });
self.loadingBar.hide(); self.loadingBar.hide();
} }
@ -110,6 +136,9 @@ const PDFViewerApplication = {
* destruction is completed. * destruction is completed.
*/ */
close() { close() {
const errorWrapper = document.getElementById("errorWrapper");
errorWrapper.hidden = true;
if (!this.pdfLoadingTask) { if (!this.pdfLoadingTask) {
return Promise.resolve(); return Promise.resolve();
} }
@ -132,12 +161,9 @@ const PDFViewerApplication = {
}, },
get loadingBar() { get loadingBar() {
const bar = document.getElementById("loadingBar"); const bar = new pdfjsViewer.ProgressBar("loadingBar");
return pdfjsLib.shadow(
this, return pdfjsLib.shadow(this, "loadingBar", bar);
"loadingBar",
new pdfjsViewer.ProgressBar(bar)
);
}, },
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) { setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
@ -145,7 +171,7 @@ const PDFViewerApplication = {
let title = pdfjsLib.getFilenameFromUrl(url) || url; let title = pdfjsLib.getFilenameFromUrl(url) || url;
try { try {
title = decodeURIComponent(title); title = decodeURIComponent(title);
} catch { } catch (e) {
// decodeURIComponent may throw URIError, // decodeURIComponent may throw URIError,
// fall back to using the unprocessed url in that case // fall back to using the unprocessed url in that case
} }
@ -202,25 +228,79 @@ const PDFViewerApplication = {
}, },
error: function pdfViewError(message, moreInfo) { error: function pdfViewError(message, moreInfo) {
const l10n = this.l10n;
const moreInfoText = [ const moreInfoText = [
`PDF.js v${pdfjsLib.version || "?"} (build: ${pdfjsLib.build || "?"})`, l10n.get(
"error_version_info",
{ version: pdfjsLib.version || "?", build: pdfjsLib.build || "?" },
"PDF.js v{{version}} (build: {{build}})"
),
]; ];
if (moreInfo) {
moreInfoText.push(`Message: ${moreInfo.message}`);
if (moreInfo) {
moreInfoText.push(
l10n.get(
"error_message",
{ message: moreInfo.message },
"Message: {{message}}"
)
);
if (moreInfo.stack) { if (moreInfo.stack) {
moreInfoText.push(`Stack: ${moreInfo.stack}`); moreInfoText.push(
l10n.get("error_stack", { stack: moreInfo.stack }, "Stack: {{stack}}")
);
} else { } else {
if (moreInfo.filename) { if (moreInfo.filename) {
moreInfoText.push(`File: ${moreInfo.filename}`); moreInfoText.push(
l10n.get(
"error_file",
{ file: moreInfo.filename },
"File: {{file}}"
)
);
} }
if (moreInfo.lineNumber) { if (moreInfo.lineNumber) {
moreInfoText.push(`Line: ${moreInfo.lineNumber}`); moreInfoText.push(
l10n.get(
"error_line",
{ line: moreInfo.lineNumber },
"Line: {{line}}"
)
);
} }
} }
} }
console.error(`${message}\n\n${moreInfoText.join("\n")}`); const errorWrapper = document.getElementById("errorWrapper");
errorWrapper.hidden = false;
const errorMessage = document.getElementById("errorMessage");
errorMessage.textContent = message;
const closeButton = document.getElementById("errorClose");
closeButton.onclick = function () {
errorWrapper.hidden = true;
};
const errorMoreInfo = document.getElementById("errorMoreInfo");
const moreInfoButton = document.getElementById("errorShowMore");
const lessInfoButton = document.getElementById("errorShowLess");
moreInfoButton.onclick = function () {
errorMoreInfo.hidden = false;
moreInfoButton.hidden = true;
lessInfoButton.hidden = false;
errorMoreInfo.style.height = errorMoreInfo.scrollHeight + "px";
};
lessInfoButton.onclick = function () {
errorMoreInfo.hidden = true;
moreInfoButton.hidden = false;
lessInfoButton.hidden = true;
};
moreInfoButton.hidden = false;
lessInfoButton.hidden = true;
Promise.all(moreInfoText).then(function (parts) {
errorMoreInfo.value = parts.join("\n");
});
}, },
progress: function pdfViewProgress(level) { progress: function pdfViewProgress(level) {
@ -272,7 +352,7 @@ const PDFViewerApplication = {
}); });
this.pdfLinkService = linkService; this.pdfLinkService = linkService;
this.l10n = new pdfjsViewer.GenericL10n(); this.l10n = pdfjsViewer.NullL10n;
const container = document.getElementById("viewerContainer"); const container = document.getElementById("viewerContainer");
const pdfViewer = new pdfjsViewer.PDFViewer({ const pdfViewer = new pdfjsViewer.PDFViewer({
@ -280,7 +360,7 @@ const PDFViewerApplication = {
eventBus, eventBus,
linkService, linkService,
l10n: this.l10n, l10n: this.l10n,
maxCanvasPixels: MAX_CANVAS_PIXELS, useOnlyCssZoom: USE_ONLY_CSS_ZOOM,
textLayerMode: TEXT_LAYER_MODE, textLayerMode: TEXT_LAYER_MODE,
}); });
this.pdfViewer = pdfViewer; this.pdfViewer = pdfViewer;

285
examples/node/domstubs.js Normal file
View File

@ -0,0 +1,285 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function xmlEncode(s) {
let i = 0,
ch;
s = String(s);
while (
i < s.length &&
(ch = s[i]) !== "&" &&
ch !== "<" &&
ch !== '"' &&
ch !== "\n" &&
ch !== "\r" &&
ch !== "\t"
) {
i++;
}
if (i >= s.length) {
return s;
}
let buf = s.substring(0, i);
while (i < s.length) {
ch = s[i++];
switch (ch) {
case "&":
buf += "&amp;";
break;
case "<":
buf += "&lt;";
break;
case '"':
buf += "&quot;";
break;
case "\n":
buf += "&#xA;";
break;
case "\r":
buf += "&#xD;";
break;
case "\t":
buf += "&#x9;";
break;
default:
buf += ch;
break;
}
}
return buf;
}
function DOMElement(name) {
this.nodeName = name;
this.childNodes = [];
this.attributes = {};
this.textContent = "";
if (name === "style") {
this.sheet = {
cssRules: [],
insertRule(rule) {
this.cssRules.push(rule);
},
};
}
}
DOMElement.prototype = {
getAttribute: function DOMElement_getAttribute(name) {
if (name in this.attributes) {
return this.attributes[name];
}
return null;
},
getAttributeNS: function DOMElement_getAttributeNS(NS, name) {
// Fast path
if (name in this.attributes) {
return this.attributes[name];
}
// Slow path - used by test/unit/display_svg_spec.js
// Assuming that there is only one matching attribute for a given name,
// across all namespaces.
if (NS) {
const suffix = ":" + name;
for (const fullName in this.attributes) {
if (fullName.slice(-suffix.length) === suffix) {
return this.attributes[fullName];
}
}
}
return null;
},
setAttribute: function DOMElement_setAttribute(name, value) {
this.attributes[name] = value || "";
},
setAttributeNS: function DOMElement_setAttributeNS(NS, name, value) {
this.setAttribute(name, value);
},
append: function DOMElement_append(...elements) {
const childNodes = this.childNodes;
for (const element of elements) {
if (!childNodes.includes(element)) {
childNodes.push(element);
}
}
},
appendChild: function DOMElement_appendChild(element) {
const childNodes = this.childNodes;
if (!childNodes.includes(element)) {
childNodes.push(element);
}
},
hasChildNodes: function DOMElement_hasChildNodes() {
return this.childNodes.length !== 0;
},
cloneNode: function DOMElement_cloneNode() {
const newNode = new DOMElement(this.nodeName);
newNode.childNodes = this.childNodes;
newNode.attributes = this.attributes;
newNode.textContent = this.textContent;
return newNode;
},
// This method is offered for convenience. It is recommended to directly use
// getSerializer because that allows you to process the chunks as they come
// instead of requiring the whole image to fit in memory.
toString: function DOMElement_toString() {
const buf = [];
const serializer = this.getSerializer();
let chunk;
while ((chunk = serializer.getNext()) !== null) {
buf.push(chunk);
}
return buf.join("");
},
getSerializer: function DOMElement_getSerializer() {
return new DOMElementSerializer(this);
},
};
function DOMElementSerializer(node) {
this._node = node;
this._state = 0;
this._loopIndex = 0;
this._attributeKeys = null;
this._childSerializer = null;
}
DOMElementSerializer.prototype = {
/**
* Yields the next chunk in the serialization of the element.
*
* @returns {string|null} null if the element has fully been serialized.
*/
getNext: function DOMElementSerializer_getNext() {
const node = this._node;
switch (this._state) {
case 0: // Start opening tag.
++this._state;
return "<" + node.nodeName;
case 1: // Add SVG namespace if this is the root element.
++this._state;
if (node.nodeName === "svg:svg") {
return (
' xmlns:xlink="http://www.w3.org/1999/xlink"' +
' xmlns:svg="http://www.w3.org/2000/svg"'
);
}
/* falls through */
case 2: // Initialize variables for looping over attributes.
++this._state;
this._loopIndex = 0;
this._attributeKeys = Object.keys(node.attributes);
/* falls through */
case 3: // Serialize any attributes and end opening tag.
if (this._loopIndex < this._attributeKeys.length) {
const name = this._attributeKeys[this._loopIndex++];
return " " + name + '="' + xmlEncode(node.attributes[name]) + '"';
}
++this._state;
return ">";
case 4: // Serialize textContent for tspan/style elements.
if (node.nodeName === "svg:tspan" || node.nodeName === "svg:style") {
this._state = 6;
return xmlEncode(node.textContent);
}
++this._state;
this._loopIndex = 0;
/* falls through */
case 5: // Serialize child nodes (only for non-tspan/style elements).
while (true) {
const value =
this._childSerializer && this._childSerializer.getNext();
if (value !== null) {
return value;
}
const nextChild = node.childNodes[this._loopIndex++];
if (nextChild) {
this._childSerializer = new DOMElementSerializer(nextChild);
} else {
this._childSerializer = null;
++this._state;
break;
}
}
/* falls through */
case 6: // Ending tag.
++this._state;
return "</" + node.nodeName + ">";
case 7: // Done.
return null;
default:
throw new Error("Unexpected serialization state: " + this._state);
}
},
};
const document = {
childNodes: [],
get currentScript() {
return { src: "" };
},
get documentElement() {
return this;
},
createElementNS(NS, element) {
const elObject = new DOMElement(element);
return elObject;
},
createElement(element) {
return this.createElementNS("", element);
},
getElementsByTagName(element) {
if (element === "head") {
return [this.head || (this.head = new DOMElement("head"))];
}
return [];
},
};
function Image() {
this._src = null;
this.onload = null;
}
Image.prototype = {
get src() {
return this._src;
},
set src(value) {
this._src = value;
if (this.onload) {
this.onload();
}
},
};
exports.document = document;
exports.Image = Image;
const exported_symbols = Object.keys(exports);
exports.setStubs = function (namespace) {
exported_symbols.forEach(function (key) {
console.assert(!(key in namespace), "property should not be set: " + key);
namespace[key] = exports[key];
});
};
exports.unsetStubs = function (namespace) {
exported_symbols.forEach(function (key) {
console.assert(key in namespace, "property should be set: " + key);
delete namespace[key];
});
};

View File

@ -6,7 +6,7 @@
// //
// Run `gulp dist-install` to generate 'pdfjs-dist' npm package files. // Run `gulp dist-install` to generate 'pdfjs-dist' npm package files.
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
// Loading file from file system into typed array // Loading file from file system into typed array
const pdfPath = const pdfPath =
@ -14,7 +14,7 @@ const pdfPath =
// Will be using promises to load document, pages and misc data instead of // Will be using promises to load document, pages and misc data instead of
// callback. // callback.
const loadingTask = getDocument(pdfPath); const loadingTask = pdfjsLib.getDocument(pdfPath);
loadingTask.promise loadingTask.promise
.then(function (doc) { .then(function (doc) {
const numPages = doc.numPages; const numPages = doc.numPages;

View File

@ -13,13 +13,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { strict as assert } from "assert"; const Canvas = require("canvas");
import Canvas from "canvas"; const assert = require("assert").strict;
import fs from "fs"; const fs = require("fs");
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
class NodeCanvasFactory { function NodeCanvasFactory() {}
create(width, height) { NodeCanvasFactory.prototype = {
create: function NodeCanvasFactory_create(width, height) {
assert(width > 0 && height > 0, "Invalid canvas size"); assert(width > 0 && height > 0, "Invalid canvas size");
const canvas = Canvas.createCanvas(width, height); const canvas = Canvas.createCanvas(width, height);
const context = canvas.getContext("2d"); const context = canvas.getContext("2d");
@ -27,16 +27,16 @@ class NodeCanvasFactory {
canvas, canvas,
context, context,
}; };
} },
reset(canvasAndContext, width, height) { reset: function NodeCanvasFactory_reset(canvasAndContext, width, height) {
assert(canvasAndContext.canvas, "Canvas is not specified"); assert(canvasAndContext.canvas, "Canvas is not specified");
assert(width > 0 && height > 0, "Invalid canvas size"); assert(width > 0 && height > 0, "Invalid canvas size");
canvasAndContext.canvas.width = width; canvasAndContext.canvas.width = width;
canvasAndContext.canvas.height = height; canvasAndContext.canvas.height = height;
} },
destroy(canvasAndContext) { destroy: function NodeCanvasFactory_destroy(canvasAndContext) {
assert(canvasAndContext.canvas, "Canvas is not specified"); assert(canvasAndContext.canvas, "Canvas is not specified");
// Zeroing the width and height cause Firefox to release graphics // Zeroing the width and height cause Firefox to release graphics
@ -45,8 +45,10 @@ class NodeCanvasFactory {
canvasAndContext.canvas.height = 0; canvasAndContext.canvas.height = 0;
canvasAndContext.canvas = null; canvasAndContext.canvas = null;
canvasAndContext.context = null; canvasAndContext.context = null;
} },
} };
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
// Some PDFs need external cmaps. // Some PDFs need external cmaps.
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/"; const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
@ -56,22 +58,20 @@ const CMAP_PACKED = true;
const STANDARD_FONT_DATA_URL = const STANDARD_FONT_DATA_URL =
"../../../node_modules/pdfjs-dist/standard_fonts/"; "../../../node_modules/pdfjs-dist/standard_fonts/";
const canvasFactory = new NodeCanvasFactory();
// Loading file from file system into typed array. // Loading file from file system into typed array.
const pdfPath = const pdfPath =
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf"; process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
const data = new Uint8Array(fs.readFileSync(pdfPath)); const data = new Uint8Array(fs.readFileSync(pdfPath));
// Load the PDF file. // Load the PDF file.
const loadingTask = getDocument({ const loadingTask = pdfjsLib.getDocument({
data, data,
cMapUrl: CMAP_URL, cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL, standardFontDataUrl: STANDARD_FONT_DATA_URL,
canvasFactory,
}); });
(async function () {
try { try {
const pdfDocument = await loadingTask.promise; const pdfDocument = await loadingTask.promise;
console.log("# PDF document loaded."); console.log("# PDF document loaded.");
@ -79,6 +79,7 @@ try {
const page = await pdfDocument.getPage(1); const page = await pdfDocument.getPage(1);
// Render the page on a Node canvas with 100% scale. // Render the page on a Node canvas with 100% scale.
const viewport = page.getViewport({ scale: 1.0 }); const viewport = page.getViewport({ scale: 1.0 });
const canvasFactory = new NodeCanvasFactory();
const canvasAndContext = canvasFactory.create( const canvasAndContext = canvasFactory.create(
viewport.width, viewport.width,
viewport.height viewport.height
@ -86,6 +87,7 @@ try {
const renderContext = { const renderContext = {
canvasContext: canvasAndContext.context, canvasContext: canvasAndContext.context,
viewport, viewport,
canvasFactory,
}; };
const renderTask = page.render(renderContext); const renderTask = page.render(renderContext);
@ -96,7 +98,9 @@ try {
if (error) { if (error) {
console.error("Error: " + error); console.error("Error: " + error);
} else { } else {
console.log("Finished converting first page of PDF file to a PNG image."); console.log(
"Finished converting first page of PDF file to a PNG image."
);
} }
}); });
// Release page resources. // Release page resources.
@ -104,3 +108,4 @@ try {
} catch (reason) { } catch (reason) {
console.log(reason); console.log(reason);
} }
})();

128
examples/node/pdf2svg.js Normal file
View File

@ -0,0 +1,128 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
//
// Node tool to dump SVG output into a file.
//
const fs = require("fs");
const util = require("util");
const path = require("path");
const stream = require("stream");
// HACK few hacks to let PDF.js be loaded not as a module in global space.
require("./domstubs.js").setStubs(global);
// Run `gulp dist-install` to generate 'pdfjs-dist' npm package files.
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
// Some PDFs need external cmaps.
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
// Loading file from file system into typed array
const pdfPath =
process.argv[2] || "../../web/compressed.tracemonkey-pldi-09.pdf";
const data = new Uint8Array(fs.readFileSync(pdfPath));
const outputDirectory = "./svgdump";
try {
// Note: This creates a directory only one level deep. If you want to create
// multiple subdirectories on the fly, use the mkdirp module from npm.
fs.mkdirSync(outputDirectory);
} catch (e) {
if (e.code !== "EEXIST") {
throw e;
}
}
// Dumps svg outputs to a folder called svgdump
function getFilePathForPage(pageNum) {
const name = path.basename(pdfPath, path.extname(pdfPath));
return path.join(outputDirectory, `${name}-${pageNum}.svg`);
}
/**
* A readable stream which offers a stream representing the serialization of a
* given DOM element (as defined by domstubs.js).
*
* @param {object} options
* @param {DOMElement} options.svgElement The element to serialize
*/
function ReadableSVGStream(options) {
if (!(this instanceof ReadableSVGStream)) {
return new ReadableSVGStream(options);
}
stream.Readable.call(this, options);
this.serializer = options.svgElement.getSerializer();
}
util.inherits(ReadableSVGStream, stream.Readable);
// Implements https://nodejs.org/api/stream.html#stream_readable_read_size_1
ReadableSVGStream.prototype._read = function () {
let chunk;
while ((chunk = this.serializer.getNext()) !== null) {
if (!this.push(chunk)) {
return;
}
}
this.push(null);
};
// Streams the SVG element to the given file path.
function writeSvgToFile(svgElement, filePath) {
let readableSvgStream = new ReadableSVGStream({
svgElement,
});
const writableStream = fs.createWriteStream(filePath);
return new Promise(function (resolve, reject) {
readableSvgStream.once("error", reject);
writableStream.once("error", reject);
writableStream.once("finish", resolve);
readableSvgStream.pipe(writableStream);
}).catch(function (err) {
readableSvgStream = null; // Explicitly null because of v8 bug 6512.
writableStream.end();
throw err;
});
}
// Will be using async/await to load document, pages and misc data.
const loadingTask = pdfjsLib.getDocument({
data,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
fontExtraProperties: true,
});
(async function () {
const doc = await loadingTask.promise;
const numPages = doc.numPages;
console.log("# Document Loaded");
console.log(`Number of Pages: ${numPages}`);
console.log();
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
try {
const page = await doc.getPage(pageNum);
console.log(`# Page ${pageNum}`);
const viewport = page.getViewport({ scale: 1.0 });
console.log(`Size: ${viewport.width}x${viewport.height}`);
console.log();
const opList = await page.getOperatorList();
const svgGfx = new pdfjsLib.SVGGraphics(
page.commonObjs,
page.objs,
/* forceDataSchema = */ true
);
svgGfx.embedFonts = true;
const svg = await svgGfx.getSVG(opList, viewport);
await writeSvgToFile(svg, getFilePathForPage(pageNum));
// Release page resources.
page.cleanup();
} catch (err) {
console.log(`Error: ${err}`);
}
}
console.log("# End of Document");
})();

View File

@ -3,8 +3,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Text-only PDF.js example</title> <title>Text-only PDF.js example</title>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="pdf2svg.mjs" type="module"></script> <script src="pdf2svg.js"></script>
</head> </head>
<body> <body>
<p>Text-only PDF.js example</p> <p>Text-only PDF.js example</p>

View File

@ -19,7 +19,7 @@ const PAGE_SCALE = 1.5;
const SVG_NS = "http://www.w3.org/2000/svg"; const SVG_NS = "http://www.w3.org/2000/svg";
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs"; "../../node_modules/pdfjs-dist/build/pdf.worker.js";
function buildSVG(viewport, textContent) { function buildSVG(viewport, textContent) {
// Building SVG with size of the viewport (for simplicity) // Building SVG with size of the viewport (for simplicity)

View File

@ -27,7 +27,9 @@ otherwise the build is not guaranteed to work correctly.
## Worker loading ## Worker loading
If you are getting the `Setting up fake worker` warning, make sure you are If you are getting the `Setting up fake worker` warning, make sure you are
importing `pdfjs-dist/webpack.mjs` which is the zero-configuration method for importing `pdfjs-dist/webpack` which is the zero-configuration method for
Webpack users. Installing `worker-loader` is no longer necessary. Webpack users. Installing `worker-loader` is no longer necessary.
import * as pdfjsLib from 'pdfjs-dist/webpack.mjs'; import * as pdfjsLib from 'pdfjs-dist/webpack';
For a full working example refer to [this repository](https://github.com/yurydelendik/pdfjs-react).

35
examples/webpack/main.js Normal file
View File

@ -0,0 +1,35 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Hello world example for webpack.
const pdfjsLib = require("pdfjs-dist");
const pdfPath = "../learning/helloworld.pdf";
// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../build/webpack/pdf.worker.bundle.js";
// Loading a document.
const loadingTask = pdfjsLib.getDocument(pdfPath);
loadingTask.promise
.then(function (pdfDocument) {
// Request a first page
return pdfDocument.getPage(1).then(function (pdfPage) {
// Display page on the existing canvas with 100% scale.
const viewport = pdfPage.getViewport({ scale: 1.0 });
const canvas = document.getElementById("theCanvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext("2d");
const renderTask = pdfPage.render({
canvasContext: ctx,
viewport,
});
return renderTask.promise;
});
})
.catch(function (reason) {
console.error("Error: " + reason);
});

View File

@ -1,29 +0,0 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Hello world example for webpack.
import * as pdfjsLib from "pdfjs-dist";
const pdfPath = "../learning/helloworld.pdf";
// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../build/webpack/pdf.worker.bundle.js";
// Loading a document.
const loadingTask = pdfjsLib.getDocument(pdfPath);
const pdfDocument = await loadingTask.promise;
// Request a first page
const pdfPage = await pdfDocument.getPage(1);
// Display page on the existing canvas with 100% scale.
const viewport = pdfPage.getViewport({ scale: 1.0 });
const canvas = document.getElementById("theCanvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext("2d");
const renderTask = pdfPage.render({
canvasContext: ctx,
viewport,
});
await renderTask.promise;

View File

@ -1,12 +1,12 @@
{ {
"name": "webpack-pdf.js-example", "name": "webpack-pdf.js-example",
"version": "0.2.0", "version": "0.1.0",
"scripts": { "scripts": {
"build": "webpack" "build": "webpack"
}, },
"devDependencies": { "devDependencies": {
"webpack": "^5.89.0", "webpack": "^5.11.1",
"webpack-cli": "^5.1.4", "webpack-cli": "^4.3.1",
"pdfjs-dist": "../../node_modules/pdfjs-dist" "pdfjs-dist": "../../node_modules/pdfjs-dist"
} }
} }

View File

@ -1,13 +1,11 @@
/* eslint-disable import/no-commonjs */
const webpack = require("webpack"); // eslint-disable-line no-unused-vars const webpack = require("webpack"); // eslint-disable-line no-unused-vars
const path = require("path"); const path = require("path");
module.exports = { module.exports = {
context: __dirname, context: __dirname,
entry: { entry: {
main: "./main.mjs", main: "./main.js",
"pdf.worker": "pdfjs-dist/build/pdf.worker.mjs", "pdf.worker": "pdfjs-dist/build/pdf.worker.entry",
}, },
mode: "none", mode: "none",
output: { output: {

View File

@ -22,7 +22,11 @@ function getViewerURL(pdf_url) {
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url); return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url);
} }
if (CSS.supports("animation", "0s")) {
document.addEventListener("animationstart", onAnimationStart, true); document.addEventListener("animationstart", onAnimationStart, true);
} else {
document.addEventListener("webkitAnimationStart", onAnimationStart, true);
}
function onAnimationStart(event) { function onAnimationStart(event) {
if (event.animationName === "pdfjs-detected-object-or-embed") { if (event.animationName === "pdfjs-detected-object-or-embed") {
@ -128,12 +132,8 @@ function updateEmbedElement(elem) {
} }
elem.type = "text/html"; elem.type = "text/html";
elem.src = getEmbeddedViewerURL(elem.src); elem.src = getEmbeddedViewerURL(elem.src);
if (parentNode) { if (parentNode) {
// Suppress linter warning: insertBefore is preferable to nextSibling.before(elem);
// nextSibling.before(elem) because nextSibling may be null.
// eslint-disable-next-line unicorn/prefer-modern-dom-apis
parentNode.insertBefore(elem, nextSibling);
} }
} }

View File

@ -1,6 +1,11 @@
/** /**
* Detect creation of <embed> and <object> tags. * Detect creation of <embed> and <object> tags.
*/ */
@-webkit-keyframes pdfjs-detected-object-or-embed {
from {
/* empty */
}
}
@keyframes pdfjs-detected-object-or-embed { @keyframes pdfjs-detected-object-or-embed {
from { from {
/* empty */ /* empty */
@ -8,6 +13,9 @@
} }
object, object,
embed { embed {
-webkit-animation-delay: 0s !important;
-webkit-animation-name: pdfjs-detected-object-or-embed !important;
-webkit-animation-play-state: running !important;
animation-delay: 0s !important; animation-delay: 0s !important;
animation-name: pdfjs-detected-object-or-embed !important; animation-name: pdfjs-detected-object-or-embed !important;
animation-play-state: running !important; animation-play-state: running !important;

View File

@ -47,7 +47,7 @@ limitations under the License.
} }
var scheme = url.slice(0, schemeIndex).toLowerCase(); var scheme = url.slice(0, schemeIndex).toLowerCase();
if (schemes.includes(scheme)) { if (schemes.includes(scheme)) {
url = url.split("#", 1)[0]; url = url.split("#")[0];
if (url.charAt(schemeIndex) === ":") { if (url.charAt(schemeIndex) === ":") {
url = encodeURIComponent(url); url = encodeURIComponent(url);
} }

View File

@ -1,5 +1,4 @@
{ {
"minimum_chrome_version": "88",
"manifest_version": 2, "manifest_version": 2,
"name": "PDF Viewer", "name": "PDF Viewer",
"version": "PDFJSSCRIPT_VERSION", "version": "PDFJSSCRIPT_VERSION",
@ -11,30 +10,32 @@
}, },
"permissions": [ "permissions": [
"fileBrowserHandler", "fileBrowserHandler",
"webRequest", "webRequest", "webRequestBlocking",
"webRequestBlocking",
"<all_urls>", "<all_urls>",
"tabs", "tabs",
"webNavigation", "webNavigation",
"storage" "storage"
], ],
"content_scripts": [ "content_scripts": [{
{ "matches": [
"matches": ["http://*/*", "https://*/*", "ftp://*/*", "file://*/*"], "http://*/*",
"https://*/*",
"ftp://*/*",
"file://*/*"
],
"run_at": "document_start", "run_at": "document_start",
"all_frames": true, "all_frames": true,
"css": ["contentstyle.css"], "css": ["contentstyle.css"],
"js": ["contentscript.js"] "js": ["contentscript.js"]
} }],
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"file_browser_handlers": [ "file_browser_handlers": [{
{
"id": "open-as-pdf", "id": "open-as-pdf",
"default_title": "Open with PDF Viewer", "default_title": "Open with PDF Viewer",
"file_filters": ["filesystem:*.pdf"] "file_filters": [
} "filesystem:*.pdf"
], ]
}],
"storage": { "storage": {
"managed_schema": "preferences_schema.json" "managed_schema": "preferences_schema.json"
}, },

View File

@ -20,15 +20,7 @@ limitations under the License.
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html"); var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
function getViewerURL(pdf_url) { function getViewerURL(pdf_url) {
// |pdf_url| may contain a fragment such as "#page=2". That should be passed return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url);
// as a fragment to the viewer, not encoded in pdf_url.
var hash = "";
var i = pdf_url.indexOf("#");
if (i > 0) {
hash = pdf_url.slice(i);
pdf_url = pdf_url.slice(0, i);
}
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url) + hash;
} }
/** /**
@ -162,7 +154,19 @@ chrome.webRequest.onBeforeRequest.addListener(
return { redirectUrl: viewerUrl }; return { redirectUrl: viewerUrl };
}, },
{ {
urls: ["file://*/*.pdf", "file://*/*.PDF"], urls: [
"file://*/*.pdf",
"file://*/*.PDF",
...// Duck-typing: MediaError.prototype.message was added in Chrome 59.
(MediaError.prototype.hasOwnProperty("message")
? []
: [
// Note: Chrome 59 has disabled ftp resource loading by default:
// https://www.chromestatus.com/feature/5709390967472128
"ftp://*/*.pdf",
"ftp://*/*.PDF",
]),
],
types: ["main_frame", "sub_frame"], types: ["main_frame", "sub_frame"],
}, },
["blocking"] ["blocking"]
@ -248,3 +252,10 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
} }
return undefined; return undefined;
}); });
// Remove keys from storage that were once part of the deleted feature-detect.js
chrome.storage.local.remove([
"featureDetectLastUA",
"webRequestRedirectUrl",
"extensionSupportsFTP",
]);

View File

@ -5,7 +5,11 @@
"title": "Theme", "title": "Theme",
"description": "The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.", "description": "The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.",
"type": "integer", "type": "integer",
"enum": [0, 1, 2], "enum": [
0,
1,
2
],
"default": 2 "default": 2
}, },
"showPreviousViewOnLoad": { "showPreviousViewOnLoad": {
@ -17,15 +21,13 @@
"title": "View position on load", "title": "View position on load",
"description": "The position in the document upon load.\n -1 = Default (uses OpenAction if available, otherwise equal to `viewOnLoad = 0`).\n 0 = The last viewed page/position.\n 1 = The initial page/position.", "description": "The position in the document upon load.\n -1 = Default (uses OpenAction if available, otherwise equal to `viewOnLoad = 0`).\n 0 = The last viewed page/position.\n 1 = The initial page/position.",
"type": "integer", "type": "integer",
"enum": [-1, 0, 1], "enum": [
-1,
0,
1
],
"default": 0 "default": 0
}, },
"defaultZoomDelay": {
"title": "Default zoom delay",
"description": "Delay (in ms) to wait before redrawing the canvas.",
"type": "integer",
"default": 400
},
"defaultZoomValue": { "defaultZoomValue": {
"title": "Default zoom level", "title": "Default zoom level",
"description": "Default zoom level of the viewer. Accepted values: 'auto', 'page-actual', 'page-width', 'page-height', 'page-fit', or a zoom level in percents.", "description": "Default zoom level of the viewer. Accepted values: 'auto', 'page-actual', 'page-width', 'page-height', 'page-fit', or a zoom level in percents.",
@ -37,7 +39,13 @@
"title": "Sidebar state on load", "title": "Sidebar state on load",
"description": "Controls the state of the sidebar upon load.\n -1 = Default (uses PageMode if available, otherwise the last position if available/enabled).\n 0 = Do not show sidebar.\n 1 = Show thumbnails in sidebar.\n 2 = Show document outline in sidebar.\n 3 = Show attachments in sidebar.", "description": "Controls the state of the sidebar upon load.\n -1 = Default (uses PageMode if available, otherwise the last position if available/enabled).\n 0 = Do not show sidebar.\n 1 = Show thumbnails in sidebar.\n 2 = Show document outline in sidebar.\n 3 = Show attachments in sidebar.",
"type": "integer", "type": "integer",
"enum": [-1, 0, 1, 2, 3], "enum": [
-1,
0,
1,
2,
3
],
"default": -1 "default": -1
}, },
"enableHandToolOnLoad": { "enableHandToolOnLoad": {
@ -45,15 +53,14 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enableML": {
"type": "boolean",
"default": false
},
"cursorToolOnLoad": { "cursorToolOnLoad": {
"title": "Cursor tool on load", "title": "Cursor tool on load",
"description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.", "description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.",
"type": "integer", "type": "integer",
"enum": [0, 1], "enum": [
0,
1
],
"default": 0 "default": 0
}, },
"pdfBugEnabled": { "pdfBugEnabled": {
@ -68,22 +75,6 @@
"description": "Whether to allow execution of active content (JavaScript) by PDF files.", "description": "Whether to allow execution of active content (JavaScript) by PDF files.",
"default": false "default": false
}, },
"enableHighlightEditor": {
"type": "boolean",
"default": false
},
"enableHighlightFloatingButton": {
"type": "boolean",
"default": false
},
"highlightEditorColors": {
"type": "string",
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
},
"enableStampEditor": {
"type": "boolean",
"default": true
},
"disableRange": { "disableRange": {
"title": "Disable range requests", "title": "Disable range requests",
"description": "Whether to disable range requests (not recommended).", "description": "Whether to disable range requests (not recommended).",
@ -115,14 +106,27 @@
"title": "Text layer mode", "title": "Text layer mode",
"description": "Controls if the text layer is enabled, and the selection mode that is used.\n 0 = Disabled.\n 1 = Enabled.", "description": "Controls if the text layer is enabled, and the selection mode that is used.\n 0 = Disabled.\n 1 = Enabled.",
"type": "integer", "type": "integer",
"enum": [0, 1], "enum": [
0,
1
],
"default": 1 "default": 1
}, },
"useOnlyCssZoom": {
"type": "boolean",
"default": false
},
"externalLinkTarget": { "externalLinkTarget": {
"title": "External links target window", "title": "External links target window",
"description": "Controls how external links will be opened.\n 0 = default.\n 1 = replaces current window.\n 2 = new window/tab.\n 3 = parent.\n 4 = in top window.", "description": "Controls how external links will be opened.\n 0 = default.\n 1 = replaces current window.\n 2 = new window/tab.\n 3 = parent.\n 4 = in top window.",
"type": "integer", "type": "integer",
"enum": [0, 1, 2, 3, 4], "enum": [
0,
1,
2,
3,
4
],
"default": 0 "default": 0
}, },
"disablePageLabels": { "disablePageLabels": {
@ -142,12 +146,22 @@
}, },
"annotationMode": { "annotationMode": {
"type": "integer", "type": "integer",
"enum": [0, 1, 2, 3], "enum": [
0,
1,
2,
3
],
"default": 2 "default": 2
}, },
"annotationEditorMode": { "annotationEditorMode": {
"type": "integer", "type": "integer",
"enum": [-1, 0, 3, 15], "enum": [
-1,
0,
3,
15
],
"default": 0 "default": 0
}, },
"enablePermissions": { "enablePermissions": {
@ -178,14 +192,25 @@
"title": "Scroll mode on load", "title": "Scroll mode on load",
"description": "Controls how the viewer scrolls upon load.\n -1 = Default (uses the last position if available/enabled).\n 3 = Page scrolling.\n 0 = Vertical scrolling.\n 1 = Horizontal scrolling.\n 2 = Wrapped scrolling.", "description": "Controls how the viewer scrolls upon load.\n -1 = Default (uses the last position if available/enabled).\n 3 = Page scrolling.\n 0 = Vertical scrolling.\n 1 = Horizontal scrolling.\n 2 = Wrapped scrolling.",
"type": "integer", "type": "integer",
"enum": [-1, 0, 1, 2, 3], "enum": [
-1,
0,
1,
2,
3
],
"default": -1 "default": -1
}, },
"spreadModeOnLoad": { "spreadModeOnLoad": {
"title": "Spread mode on load", "title": "Spread mode on load",
"description": "Whether the viewer should join pages into spreads upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = No spreads.\n 1 = Odd spreads.\n 2 = Even spreads.", "description": "Whether the viewer should join pages into spreads upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = No spreads.\n 1 = Odd spreads.\n 2 = Even spreads.",
"type": "integer", "type": "integer",
"enum": [-1, 0, 1, 2], "enum": [
-1,
0,
1,
2
],
"default": -1 "default": -1
}, },
"forcePageColors": { "forcePageColors": {

View File

@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/* import-globals-from pdfHandler.js */ /* import-globals-from pdfHandler.js */
/* exported saveReferer */
"use strict"; "use strict";
/** /**
@ -42,18 +41,30 @@ var g_requestHeaders = {};
// g_referrers[tabId][frameId] = referrer of PDF frame. // g_referrers[tabId][frameId] = referrer of PDF frame.
var g_referrers = {}; var g_referrers = {};
var extraInfoSpecWithHeaders; // = ['requestHeaders', 'extraHeaders']
(function () { (function () {
var requestFilter = { var requestFilter = {
urls: ["*://*/*"], urls: ["*://*/*"],
types: ["main_frame", "sub_frame"], types: ["main_frame", "sub_frame"],
}; };
function registerListener(extraInfoSpec) {
extraInfoSpecWithHeaders = extraInfoSpec;
// May throw if the given extraInfoSpec is unsupported.
chrome.webRequest.onSendHeaders.addListener( chrome.webRequest.onSendHeaders.addListener(
function (details) { function (details) {
g_requestHeaders[details.requestId] = details.requestHeaders; g_requestHeaders[details.requestId] = details.requestHeaders;
}, },
requestFilter, requestFilter,
["requestHeaders", "extraHeaders"] extraInfoSpec
); );
}
try {
registerListener(["requestHeaders", "extraHeaders"]);
} catch (e) {
// "extraHeaders" is not supported in Chrome 71 and earlier.
registerListener(["requestHeaders"]);
}
chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter); chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter); chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter); chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
@ -114,7 +125,7 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
types: ["xmlhttprequest"], types: ["xmlhttprequest"],
tabId, tabId,
}, },
["blocking", "requestHeaders", "extraHeaders"] ["blocking", ...extraInfoSpecWithHeaders]
); );
} }
// Acknowledge the message, and include the latest referer for this frame. // Acknowledge the message, and include the latest referer for this frame.

View File

@ -70,6 +70,12 @@ limitations under the License.
var deduplication_id = getDeduplicationId(wasUpdated); var deduplication_id = getDeduplicationId(wasUpdated);
var extension_version = chrome.runtime.getManifest().version; var extension_version = chrome.runtime.getManifest().version;
if (window.Request && "mode" in Request.prototype) {
// fetch is supported in extensions since Chrome 42 (though the above
// feature-detection method detects Chrome 43+).
// Unlike XMLHttpRequest, fetch omits credentials such as cookies in the
// requests, which guarantees that the server cannot track the client
// via HTTP cookies.
fetch(LOG_URL, { fetch(LOG_URL, {
method: "POST", method: "POST",
headers: new Headers({ headers: new Headers({
@ -79,11 +85,14 @@ limitations under the License.
// Set mode=cors so that the above custom headers are included in the // Set mode=cors so that the above custom headers are included in the
// request. // request.
mode: "cors", mode: "cors",
// Omits credentials such as cookies in the requests, which guarantees
// that the server cannot track the client via HTTP cookies.
credentials: "omit",
cache: "no-store",
}); });
return;
}
var x = new XMLHttpRequest();
x.open("POST", LOG_URL);
x.setRequestHeader("Deduplication-Id", deduplication_id);
x.setRequestHeader("Extension-Version", extension_version);
x.send();
}); });
} }

View File

@ -6,11 +6,18 @@
"plugin:mozilla/recommended", "plugin:mozilla/recommended",
], ],
"parserOptions": {
"sourceType": "script",
},
"plugins": [ "plugins": [
"mozilla" "mozilla"
], ],
"rules": { "rules": {
// Items different from the mozilla/recommended configuration.
"no-var": "off",
// Other rules mozilla/recommended hasn't enabled yet. // Other rules mozilla/recommended hasn't enabled yet.
"no-shadow": "error", "no-shadow": "error",
"arrow-body-style": ["error", "as-needed"], "arrow-body-style": ["error", "as-needed"],

View File

@ -13,6 +13,10 @@
* limitations under the License. * limitations under the License.
*/ */
export const PdfJsDefaultPreferences = Object.freeze( "use strict";
var EXPORTED_SYMBOLS = ["PdfJsDefaultPreferences"];
var PdfJsDefaultPreferences = Object.freeze(
PDFJSDev.eval("DEFAULT_PREFERENCES") PDFJSDev.eval("DEFAULT_PREFERENCES")
); );

View File

@ -0,0 +1,133 @@
"use strict";
// Small subset of the webL10n API by Fabien Cazenave for PDF.js extension.
(function (window) {
let gL10nData = null;
let gLanguage = "";
let gExternalLocalizerServices = null;
let gReadyState = "loading";
// fetch an l10n objects
function getL10nData(key) {
gL10nData ||= gExternalLocalizerServices.getStrings();
const data = gL10nData?.[key];
if (!data) {
console.warn("[l10n] #" + key + " missing for [" + gLanguage + "]");
}
return data;
}
// replace {{arguments}} with their values
function substArguments(text, args) {
if (!args) {
return text;
}
return text.replace(/\{\{\s*(\w+)\s*\}\}/g, function (all, name) {
return name in args ? args[name] : "{{" + name + "}}";
});
}
// translate a string
function translateString(key, args, fallback) {
var i = key.lastIndexOf(".");
var name, property;
if (i >= 0) {
name = key.substring(0, i);
property = key.substring(i + 1);
} else {
name = key;
property = "textContent";
}
var data = getL10nData(name);
var value = (data && data[property]) || fallback;
if (!value) {
return "{{" + key + "}}";
}
return substArguments(value, args);
}
// translate an HTML element
function translateElement(element) {
if (!element || !element.dataset) {
return;
}
// get the related l10n object
var key = element.dataset.l10nId;
var data = getL10nData(key);
if (!data) {
return;
}
// get arguments (if any)
// TODO: more flexible parser?
var args;
if (element.dataset.l10nArgs) {
try {
args = JSON.parse(element.dataset.l10nArgs);
} catch (e) {
console.warn("[l10n] could not parse arguments for #" + key + "");
}
}
// translate element
// TODO: security check?
for (var k in data) {
element[k] = substArguments(data[k], args);
}
}
// translate an HTML subtree
function translateFragment(element) {
element = element || document.querySelector("html");
// check all translatable children (= w/ a `data-l10n-id' attribute)
var children = element.querySelectorAll("*[data-l10n-id]");
var elementCount = children.length;
for (var i = 0; i < elementCount; i++) {
translateElement(children[i]);
}
// translate element itself if necessary
if (element.dataset.l10nId) {
translateElement(element);
}
}
// Public API
document.mozL10n = {
// get a localized string
get: translateString,
// get the document language
getLanguage() {
return gLanguage;
},
// get the direction (ltr|rtl) of the current language
getDirection() {
// http://www.w3.org/International/questions/qa-scripts
// Arabic, Hebrew, Farsi, Pashto, Urdu
var rtlList = ["ar", "he", "fa", "ps", "ur"];
// use the short language code for "full" codes like 'ar-sa' (issue 5440)
var shortCode = gLanguage.split("-")[0];
return rtlList.includes(shortCode) ? "rtl" : "ltr";
},
getReadyState() {
return gReadyState;
},
setExternalLocalizerServices(externalLocalizerServices) {
gExternalLocalizerServices = externalLocalizerServices;
gLanguage = gExternalLocalizerServices.getLocale();
gReadyState = "complete";
},
// translate an element or document fragment
translate: translateFragment,
};
})(this);

4
external/.eslintrc vendored
View File

@ -6,4 +6,8 @@
"env": { "env": {
"node": true, "node": true,
}, },
"rules": {
"unicorn/prefer-at": "off",
},
} }

View File

@ -1,253 +0,0 @@
import { types as t, transformSync } from "@babel/core";
import fs from "fs";
import { join as joinPaths } from "path";
import vm from "vm";
const PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
const ROOT_PREFIX = "$ROOT/";
function isPDFJSPreprocessor(obj) {
return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME;
}
function evalWithDefines(code, defines) {
if (!code || !code.trim()) {
throw new Error("No JavaScript expression given");
}
return vm.runInNewContext(code, defines, { displayErrors: false });
}
function handlePreprocessorAction(ctx, actionName, args, path) {
try {
const arg = args[0];
switch (actionName) {
case "test":
if (!t.isStringLiteral(arg)) {
throw new Error("No code for testing is given");
}
return !!evalWithDefines(arg.value, ctx.defines);
case "eval":
if (!t.isStringLiteral(arg)) {
throw new Error("No code for eval is given");
}
const result = evalWithDefines(arg.value, ctx.defines);
if (
typeof result === "boolean" ||
typeof result === "string" ||
typeof result === "number" ||
typeof result === "object"
) {
return result;
}
break;
case "json":
if (!t.isStringLiteral(arg)) {
throw new Error("Path to JSON is not provided");
}
let jsonPath = arg.value;
if (jsonPath.startsWith(ROOT_PREFIX)) {
jsonPath = joinPaths(
ctx.rootPath,
jsonPath.substring(ROOT_PREFIX.length)
);
}
return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
}
throw new Error("Unsupported action");
} catch (e) {
throw path.buildCodeFrameError(
"Could not process " +
PDFJS_PREPROCESSOR_NAME +
"." +
actionName +
": " +
e.message
);
}
}
function babelPluginPDFJSPreprocessor(babel, ctx) {
return {
name: "babel-plugin-pdfjs-preprocessor",
manipulateOptions({ parserOpts }) {
parserOpts.attachComment = false;
},
visitor: {
"ExportNamedDeclaration|ImportDeclaration": ({ node }) => {
if (node.source && ctx.map?.[node.source.value]) {
node.source.value = ctx.map[node.source.value];
}
},
"IfStatement|ConditionalExpression": {
exit(path) {
const { node } = path;
if (t.isBooleanLiteral(node.test)) {
// if (true) stmt1; => stmt1
// if (false) stmt1; else stmt2; => stmt2
if (node.test.value === true) {
path.replaceWith(node.consequent);
} else if (node.alternate) {
path.replaceWith(node.alternate);
} else {
path.remove(node);
}
}
},
},
UnaryExpression: {
exit(path) {
const { node } = path;
if (
node.operator === "typeof" &&
isPDFJSPreprocessor(node.argument)
) {
// typeof PDFJSDev => 'object'
path.replaceWith(t.stringLiteral("object"));
return;
}
if (node.operator === "!" && t.isBooleanLiteral(node.argument)) {
// !true => false, !false => true
path.replaceWith(t.booleanLiteral(!node.argument.value));
}
},
},
LogicalExpression: {
exit(path) {
const { node } = path;
if (!t.isBooleanLiteral(node.left)) {
return;
}
switch (node.operator) {
case "&&":
// true && expr => expr
// false && expr => false
path.replaceWith(
node.left.value === true ? node.right : node.left
);
break;
case "||":
// true || expr => true
// false || expr => expr
path.replaceWith(
node.left.value === true ? node.left : node.right
);
break;
}
},
},
BinaryExpression: {
exit(path) {
const { node } = path;
switch (node.operator) {
case "==":
case "===":
case "!=":
case "!==":
if (t.isLiteral(node.left) && t.isLiteral(node.right)) {
// folding == and != check that can be statically evaluated
const { confident, value } = path.evaluate();
if (confident) {
path.replaceWith(t.booleanLiteral(value));
}
}
}
},
},
CallExpression(path) {
const { node } = path;
if (
t.isMemberExpression(node.callee) &&
isPDFJSPreprocessor(node.callee.object) &&
t.isIdentifier(node.callee.property) &&
!node.callee.computed
) {
// PDFJSDev.xxxx(arg1, arg2, ...) => transform
const action = node.callee.property.name;
const result = handlePreprocessorAction(
ctx,
action,
node.arguments,
path
);
path.replaceWith(t.inherits(t.valueToNode(result), path.node));
}
if (t.isIdentifier(node.callee, { name: "__non_webpack_import__" })) {
if (node.arguments.length !== 1) {
throw new Error("Invalid `__non_webpack_import__` usage.");
}
// Replace it with a standard `import`-call and
// ensure that Webpack will leave it alone.
const source = node.arguments[0];
source.leadingComments = [
{
type: "CommentBlock",
value: "webpackIgnore: true",
},
];
path.replaceWith(t.importExpression(source));
}
},
BlockStatement: {
// Visit node in post-order so that recursive flattening
// of blocks works correctly.
exit(path) {
const { node } = path;
let subExpressionIndex = 0;
while (subExpressionIndex < node.body.length) {
switch (node.body[subExpressionIndex].type) {
case "EmptyStatement":
// Removing empty statements from the blocks.
node.body.splice(subExpressionIndex, 1);
continue;
case "BlockStatement":
// Block statements inside a block are flattened
// into the parent one.
const subChildren = node.body[subExpressionIndex].body;
node.body.splice(subExpressionIndex, 1, ...subChildren);
subExpressionIndex += Math.max(subChildren.length - 1, 0);
continue;
case "ReturnStatement":
case "ThrowStatement":
// Removing dead code after return or throw.
node.body.splice(
subExpressionIndex + 1,
node.body.length - subExpressionIndex - 1
);
break;
}
subExpressionIndex++;
}
},
},
Function: {
exit(path) {
if (!t.isBlockStatement(path.node.body)) {
// Arrow function with expression body
return;
}
const { body } = path.node.body;
if (
body.length > 0 &&
t.isReturnStatement(body.at(-1), { argument: null })
) {
// Function body ends with return without arg -- removing it.
body.pop();
}
},
},
},
};
}
function preprocessPDFJSCode(ctx, content) {
return transformSync(content, {
configFile: false,
plugins: [[babelPluginPDFJSPreprocessor, ctx]],
}).code;
}
export { babelPluginPDFJSPreprocessor, preprocessPDFJSCode };

View File

@ -1,6 +1,8 @@
import fs from "fs"; "use strict";
import path from "path";
import vm from "vm"; const fs = require("fs"),
path = require("path"),
vm = require("vm");
const AllWhitespaceRegexp = /^\s+$/g; const AllWhitespaceRegexp = /^\s+$/g;
@ -39,15 +41,9 @@ function preprocess(inFilename, outFilename, defines) {
} }
function expandCssImports(content, baseUrl) { function expandCssImports(content, baseUrl) {
return content.replaceAll( return content.replace(
/^\s*@import\s+url\(([^)]+)\);\s*$/gm, /^\s*@import\s+url\(([^)]+)\);\s*$/gm,
function (all, url) { function (all, url) {
if (defines.GECKOVIEW) {
switch (url) {
case "annotation_editor_layer_builder.css":
return "";
}
}
const file = path.join(path.dirname(baseUrl), url); const file = path.join(path.dirname(baseUrl), url);
const imported = fs.readFileSync(file, "utf8").toString(); const imported = fs.readFileSync(file, "utf8").toString();
return expandCssImports(imported, file); return expandCssImports(imported, file);
@ -76,7 +72,7 @@ function preprocess(inFilename, outFilename, defines) {
? outFilename ? outFilename
: function (line) { : function (line) {
if (!line || AllWhitespaceRegexp.test(line)) { if (!line || AllWhitespaceRegexp.test(line)) {
const prevLine = out.at(-1); const prevLine = out[out.length - 1];
if (!prevLine || AllWhitespaceRegexp.test(prevLine)) { if (!prevLine || AllWhitespaceRegexp.test(prevLine)) {
return; // Avoid adding consecutive blank lines. return; // Avoid adding consecutive blank lines.
} }
@ -125,7 +121,7 @@ function preprocess(inFilename, outFilename, defines) {
} }
} }
function expand(line) { function expand(line) {
line = line.replaceAll(/__[\w]+__/g, function (variable) { line = line.replace(/__[\w]+__/g, function (variable) {
variable = variable.substring(2, variable.length - 2); variable = variable.substring(2, variable.length - 2);
if (variable in defines) { if (variable in defines) {
return defines[variable]; return defines[variable];
@ -204,7 +200,8 @@ function preprocess(inFilename, outFilename, defines) {
} }
break; break;
} }
} else if (state === STATE_NONE) { } else {
if (state === STATE_NONE) {
writeLine(line); writeLine(line);
} else if ( } else if (
(state === STATE_IF_TRUE || state === STATE_ELSE_TRUE) && (state === STATE_IF_TRUE || state === STATE_ELSE_TRUE) &&
@ -213,12 +210,13 @@ function preprocess(inFilename, outFilename, defines) {
) { ) {
writeLine( writeLine(
line line
.replaceAll(/^\/\/|^<!--/g, " ") .replace(/^\/\/|^<!--/g, " ")
.replaceAll(/(^\s*)\/\*/g, "$1 ") .replace(/(^\s*)\/\*/g, "$1 ")
.replaceAll(/\*\/$|-->$/g, "") .replace(/\*\/$|-->$/g, "")
); );
} }
} }
}
if (state !== STATE_NONE || stack.length !== 0) { if (state !== STATE_NONE || stack.length !== 0) {
throw new Error( throw new Error(
"Missing #endif in preprocessor for " + fs.realpathSync(inFilename) "Missing #endif in preprocessor for " + fs.realpathSync(inFilename)
@ -228,5 +226,20 @@ function preprocess(inFilename, outFilename, defines) {
fs.writeFileSync(outFilename, out.join("\n")); fs.writeFileSync(outFilename, out.join("\n"));
} }
} }
exports.preprocess = preprocess;
export { preprocess }; /**
* Merge two defines arrays. Values in the second param will override values in
* the first.
*/
function merge(defaults, defines) {
const ret = Object.create(null);
for (const key in defaults) {
ret[key] = defaults[key];
}
for (const key in defines) {
ret[key] = defines[key];
}
return ret;
}
exports.merge = merge;

View File

@ -10,6 +10,6 @@ var i = true;
var j = false; var j = false;
var k = false; var k = false;
var l = true; var l = true;
var m = false; var m = '1' === true;
var n = false; var n = false;
var o = true; var o = true;

View File

@ -1,4 +1,5 @@
function f1() {} function f1() {
}
function f2() { function f2() {
return 1; return 1;
} }
@ -9,13 +10,4 @@ function f3() {
function f4() { function f4() {
var i = 0; var i = 0;
} }
var obj = {
method1() {},
method2() {}
};
class C {
method1() {}
method2() {}
}
var arrow1 = () => {};
var arrow2 = () => {};

View File

@ -23,15 +23,3 @@ function f4() {
var j = 0; var j = 0;
} }
var obj = {
method1() { return; var i = 0; },
method2() { return; },
};
class C {
method1() { return; var i = 0; }
method2() { return; }
}
var arrow1 = () => { return; var i = 0; };
var arrow2 = () => { return; };

View File

@ -3,19 +3,11 @@ var b = true;
var c = true; var c = true;
var d = false; var d = false;
var e = true; var e = true;
var f = "text"; var f = 'text';
var g = { var g = {
obj: { "obj": { "i": 1 },
i: 1 "j": 2
},
j: 2
};
var h = {
test: "test"
}; };
var h = { 'test': 'test' };
var i = '0'; var i = '0';
var j = { var j = { "i": 1 };
i: 1
};
var k = false;
var l = true;

View File

@ -8,5 +8,3 @@ var g = PDFJSDev.eval('OBJ');
var h = PDFJSDev.json('$ROOT/external/builder/fixtures_esprima/evals.json'); var h = PDFJSDev.json('$ROOT/external/builder/fixtures_esprima/evals.json');
var i = typeof PDFJSDev === 'undefined' ? PDFJSDev.eval('FALSE') : '0'; var i = typeof PDFJSDev === 'undefined' ? PDFJSDev.eval('FALSE') : '0';
var j = typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('OBJ.obj') : '0'; var j = typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('OBJ.obj') : '0';
var k = !PDFJSDev.test('TRUE');
var l = !PDFJSDev.test('FALSE');

View File

@ -1 +1 @@
{ "test": "test" } { 'test': 'test' }

View File

@ -7,12 +7,11 @@ if ('test') {
{ {
"1"; "1";
} }
;
{ {
"2"; "2";
} }
;
if ('1') { if ('1') {
"1"; "1";
} }
function f1() {
"1";
}

View File

@ -23,12 +23,3 @@ if (true && false) {
if (true && false || '1') { if (true && false || '1') {
"1"; "1";
} }
function f1() {
if (true) {
"1";
}
if (false) {
"2";
}
}

View File

@ -1,4 +1,7 @@
import { Test } from "import-name"; import { Test } from 'import-name';
import { Test2 } from './non-alias'; import { Test2 } from './non-alias';
export { Test3 } from "import-name"; export {
await import( /*webpackIgnore: true*/"./non-alias"); Test3
} from 'import-name';
var Imp = require('import-name');
var Imp2 = require('./non-alias');

View File

@ -1,4 +1,5 @@
import { Test } from 'import-alias'; import { Test } from 'import-alias';
import { Test2 } from './non-alias'; import { Test2 } from './non-alias';
export { Test3 } from 'import-alias'; export { Test3 } from 'import-alias';
await __non_webpack_import__("./non-alias"); var Imp = require('import-alias');
var Imp2 = require('./non-alias');

349
external/builder/preprocessor2.js vendored Normal file
View File

@ -0,0 +1,349 @@
"use strict";
const acorn = require("acorn");
const escodegen = require("@javascript-obfuscator/escodegen");
const vm = require("vm");
const fs = require("fs");
const path = require("path");
const PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
const ROOT_PREFIX = "$ROOT/";
const ACORN_ECMA_VERSION = 2022;
function isLiteral(obj, value) {
return obj.type === "Literal" && obj.value === value;
}
function isPDFJSPreprocessor(obj) {
return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME;
}
function evalWithDefines(code, defines, loc) {
if (!code || !code.trim()) {
throw new Error("No JavaScript expression given");
}
return vm.runInNewContext(code, defines, { displayErrors: false });
}
function handlePreprocessorAction(ctx, actionName, args, loc) {
try {
let arg;
switch (actionName) {
case "test":
arg = args[0];
if (!arg || arg.type !== "Literal" || typeof arg.value !== "string") {
throw new Error("No code for testing is given");
}
const isTrue = !!evalWithDefines(arg.value, ctx.defines);
return { type: "Literal", value: isTrue, loc };
case "eval":
arg = args[0];
if (!arg || arg.type !== "Literal" || typeof arg.value !== "string") {
throw new Error("No code for eval is given");
}
const result = evalWithDefines(arg.value, ctx.defines);
if (
typeof result === "boolean" ||
typeof result === "string" ||
typeof result === "number"
) {
return { type: "Literal", value: result, loc };
}
if (typeof result === "object") {
const parsedObj = acorn.parse("(" + JSON.stringify(result) + ")", {
ecmaVersion: ACORN_ECMA_VERSION,
});
parsedObj.body[0].expression.loc = loc;
return parsedObj.body[0].expression;
}
break;
case "json":
arg = args[0];
if (!arg || arg.type !== "Literal" || typeof arg.value !== "string") {
throw new Error("Path to JSON is not provided");
}
let jsonPath = arg.value;
if (jsonPath.indexOf(ROOT_PREFIX) === 0) {
jsonPath = path.join(
ctx.rootPath,
jsonPath.substring(ROOT_PREFIX.length)
);
}
const jsonContent = fs.readFileSync(jsonPath).toString();
const parsedJSON = acorn.parse("(" + jsonContent + ")", {
ecmaVersion: ACORN_ECMA_VERSION,
});
parsedJSON.body[0].expression.loc = loc;
return parsedJSON.body[0].expression;
}
throw new Error("Unsupported action");
} catch (e) {
throw new Error(
"Could not process " +
PDFJS_PREPROCESSOR_NAME +
"." +
actionName +
" at " +
JSON.stringify(loc) +
"\n" +
e.name +
": " +
e.message
);
}
}
function postprocessNode(ctx, node) {
switch (node.type) {
case "ExportNamedDeclaration":
case "ImportDeclaration":
if (
node.source &&
node.source.type === "Literal" &&
ctx.map &&
ctx.map[node.source.value]
) {
const newValue = ctx.map[node.source.value];
node.source.value = node.source.raw = newValue;
}
break;
case "IfStatement":
if (isLiteral(node.test, true)) {
// if (true) stmt1; => stmt1
return node.consequent;
} else if (isLiteral(node.test, false)) {
// if (false) stmt1; else stmt2; => stmt2
return node.alternate || { type: "EmptyStatement", loc: node.loc };
}
break;
case "ConditionalExpression":
if (isLiteral(node.test, true)) {
// true ? stmt1 : stmt2 => stmt1
return node.consequent;
} else if (isLiteral(node.test, false)) {
// false ? stmt1 : stmt2 => stmt2
return node.alternate;
}
break;
case "UnaryExpression":
if (node.operator === "typeof" && isPDFJSPreprocessor(node.argument)) {
// typeof PDFJSDev => 'object'
return { type: "Literal", value: "object", loc: node.loc };
}
if (
node.operator === "!" &&
node.argument.type === "Literal" &&
typeof node.argument.value === "boolean"
) {
// !true => false, !false => true
return { type: "Literal", value: !node.argument.value, loc: node.loc };
}
break;
case "LogicalExpression":
switch (node.operator) {
case "&&":
if (isLiteral(node.left, true)) {
return node.right;
}
if (isLiteral(node.left, false)) {
return node.left;
}
break;
case "||":
if (isLiteral(node.left, true)) {
return node.left;
}
if (isLiteral(node.left, false)) {
return node.right;
}
break;
}
break;
case "BinaryExpression":
switch (node.operator) {
case "==":
case "===":
case "!=":
case "!==":
if (
node.left.type === "Literal" &&
node.right.type === "Literal" &&
typeof node.left.value === typeof node.right.value
) {
// folding two literals == and != check
switch (typeof node.left.value) {
case "string":
case "boolean":
case "number":
const equal = node.left.value === node.right.value;
return {
type: "Literal",
value: (node.operator[0] === "=") === equal,
loc: node.loc,
};
}
}
break;
}
break;
case "CallExpression":
if (
node.callee.type === "MemberExpression" &&
isPDFJSPreprocessor(node.callee.object) &&
node.callee.property.type === "Identifier"
) {
// PDFJSDev.xxxx(arg1, arg2, ...) => transform
const action = node.callee.property.name;
return handlePreprocessorAction(ctx, action, node.arguments, node.loc);
}
// require('string')
if (
node.callee.type === "Identifier" &&
node.callee.name === "require" &&
node.arguments.length === 1 &&
node.arguments[0].type === "Literal" &&
ctx.map &&
ctx.map[node.arguments[0].value]
) {
const requireName = node.arguments[0];
requireName.value = requireName.raw = ctx.map[requireName.value];
}
break;
case "BlockStatement":
let subExpressionIndex = 0;
while (subExpressionIndex < node.body.length) {
switch (node.body[subExpressionIndex].type) {
case "EmptyStatement":
// Removing empty statements from the blocks.
node.body.splice(subExpressionIndex, 1);
continue;
case "BlockStatement":
// Block statements inside a block are moved to the parent one.
const subChildren = node.body[subExpressionIndex].body;
Array.prototype.splice.apply(node.body, [
subExpressionIndex,
1,
...subChildren,
]);
subExpressionIndex += Math.max(subChildren.length - 1, 0);
continue;
case "ReturnStatement":
case "ThrowStatement":
// Removing dead code after return or throw.
node.body.splice(
subExpressionIndex + 1,
node.body.length - subExpressionIndex - 1
);
break;
}
subExpressionIndex++;
}
break;
case "FunctionDeclaration":
case "FunctionExpression":
const block = node.body;
if (
block.body.length > 0 &&
block.body[block.body.length - 1].type === "ReturnStatement" &&
!block.body[block.body.length - 1].argument
) {
// Function body ends with return without arg -- removing it.
block.body.pop();
}
break;
}
return node;
}
function fixComments(ctx, node) {
if (!ctx.saveComments) {
return;
}
// Fixes double comments in the escodegen output.
delete node.trailingComments;
// Removes ESLint and other service comments.
if (node.leadingComments) {
const CopyrightRegExp = /\bcopyright\b/i;
const BlockCommentRegExp = /^\s*(globals|eslint|falls through)\b/;
const LineCommentRegExp = /^\s*eslint\b/;
let i = 0;
while (i < node.leadingComments.length) {
const type = node.leadingComments[i].type;
const value = node.leadingComments[i].value;
if (ctx.saveComments === "copyright") {
// Remove all comments, except Copyright notices and License headers.
if (!(type === "Block" && CopyrightRegExp.test(value))) {
node.leadingComments.splice(i, 1);
continue;
}
} else if (
(type === "Block" && BlockCommentRegExp.test(value)) ||
(type === "Line" && LineCommentRegExp.test(value))
) {
node.leadingComments.splice(i, 1);
continue;
}
i++;
}
}
}
function traverseTree(ctx, node) {
// generic node processing
for (const i in node) {
const child = node[i];
if (typeof child === "object" && child !== null && child.type) {
const result = traverseTree(ctx, child);
if (result !== child) {
node[i] = result;
}
} else if (Array.isArray(child)) {
child.forEach(function (childItem, index) {
if (
typeof childItem === "object" &&
childItem !== null &&
childItem.type
) {
const result = traverseTree(ctx, childItem);
if (result !== childItem) {
child[index] = result;
}
}
});
}
}
node = postprocessNode(ctx, node) || node;
fixComments(ctx, node);
return node;
}
function preprocessPDFJSCode(ctx, code) {
const format = ctx.format || {
indent: {
style: " ",
},
};
const parseOptions = {
ecmaVersion: ACORN_ECMA_VERSION,
locations: true,
sourceFile: ctx.sourceFile,
sourceType: "module",
};
const codegenOptions = {
format,
parse(input) {
return acorn.parse(input, { ecmaVersion: ACORN_ECMA_VERSION });
},
sourceMap: ctx.sourceMap,
sourceMapWithCode: ctx.sourceMap,
};
const syntax = acorn.parse(code, parseOptions);
traverseTree(ctx, syntax);
return escodegen.generate(syntax, codegenOptions);
}
exports.preprocessPDFJSCode = preprocessPDFJSCode;

View File

@ -1,9 +1,8 @@
import * as builder from "./builder.mjs"; "use strict";
import { fileURLToPath } from "url";
import fs from "fs";
import path from "path";
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const builder = require("./builder.js");
const fs = require("fs");
const path = require("path");
let errors = 0; let errors = 0;
@ -22,7 +21,7 @@ files.forEach(function (expectationFilename) {
.readFileSync(expectationFilename) .readFileSync(expectationFilename)
.toString() .toString()
.trim() .trim()
.replaceAll("__filename", fs.realpathSync(inFilename)); .replace(/__filename/g, fs.realpathSync(inFilename));
const outLines = []; const outLines = [];
const outFilename = function (line) { const outFilename = function (line) {
@ -37,17 +36,11 @@ files.forEach(function (expectationFilename) {
builder.preprocess(inFilename, outFilename, defines); builder.preprocess(inFilename, outFilename, defines);
out = outLines.join("\n").trim(); out = outLines.join("\n").trim();
} catch (e) { } catch (e) {
out = ("Error: " + e.message).replaceAll(/^/gm, "//"); out = ("Error: " + e.message).replace(/^/gm, "//");
} }
if (out !== expectation) { if (out !== expectation) {
errors++; errors++;
// Allow regenerating the expected output using
// OVERWRITE=true node ./external/builder/test-fixtures.mjs
if (process.env.OVERWRITE) {
fs.writeFileSync(expectationFilename, out + "\n");
}
console.log("Assertion failed for " + inFilename); console.log("Assertion failed for " + inFilename);
console.log("--------------------------------------------------"); console.log("--------------------------------------------------");
console.log("EXPECTED:"); console.log("EXPECTED:");

View File

@ -1,9 +1,8 @@
import { fileURLToPath } from "url"; "use strict";
import fs from "fs";
import path from "path";
import { preprocessPDFJSCode } from "./babel-plugin-pdfjs-preprocessor.mjs";
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const p2 = require("./preprocessor2.js");
const fs = require("fs");
const path = require("path");
let errors = 0; let errors = 0;
@ -22,7 +21,7 @@ files.forEach(function (expectationFilename) {
.readFileSync(expectationFilename) .readFileSync(expectationFilename)
.toString() .toString()
.trim() .trim()
.replaceAll("__filename", fs.realpathSync(inFilename)); .replace(/__filename/g, fs.realpathSync(inFilename));
const input = fs.readFileSync(inFilename).toString(); const input = fs.readFileSync(inFilename).toString();
const defines = { const defines = {
@ -41,19 +40,13 @@ files.forEach(function (expectationFilename) {
}; };
let out; let out;
try { try {
out = preprocessPDFJSCode(ctx, input); out = p2.preprocessPDFJSCode(ctx, input);
} catch (e) { } catch (e) {
out = ("Error: " + e.message).replaceAll(/^/gm, "//"); out = ("Error: " + e.message).replace(/^/gm, "//");
} }
if (out !== expectation) { if (out !== expectation) {
errors++; errors++;
// Allow regenerating the expected output using
// OVERWRITE=true node ./external/builder/test-fixtures_esprima.mjs
if (process.env.OVERWRITE) {
fs.writeFileSync(expectationFilename, out + "\n");
}
console.log("Assertion failed for " + inFilename); console.log("Assertion failed for " + inFilename);
console.log("--------------------------------------------------"); console.log("--------------------------------------------------");
console.log("EXPECTED:"); console.log("EXPECTED:");

View File

@ -13,10 +13,10 @@
* limitations under the License. * limitations under the License.
*/ */
import fs from "fs"; const fs = require("fs");
import { optimizeCMap } from "./optimize.mjs"; const path = require("path");
import { parseAdobeCMap } from "./parse.mjs"; const parseAdobeCMap = require("./parse.js").parseAdobeCMap;
import path from "path"; const optimizeCMap = require("./optimize.js").optimizeCMap;
function compressCmap(srcPath, destPath, verify) { function compressCmap(srcPath, destPath, verify) {
const content = fs.readFileSync(srcPath).toString(); const content = fs.readFileSync(srcPath).toString();
@ -215,7 +215,7 @@ function parseCMap(binaryData) {
}, },
readHexSigned(size) { readHexSigned(size) {
const num = this.readHexNumber(size); const num = this.readHexNumber(size);
const sign = fromHexDigit(num.at(-1)) & 1 ? 15 : 0; const sign = fromHexDigit(num[num.length - 1]) & 1 ? 15 : 0;
let c = 0; let c = 0;
let result = ""; let result = "";
for (const digit of num) { for (const digit of num) {
@ -469,7 +469,7 @@ function incHex(a) {
return s; return s;
} }
function compressCmaps(src, dest, verify) { exports.compressCmaps = function (src, dest, verify) {
const files = fs.readdirSync(src).filter(function (fn) { const files = fs.readdirSync(src).filter(function (fn) {
return !fn.includes("."); // skipping files with the extension return !fn.includes("."); // skipping files with the extension
}); });
@ -489,6 +489,4 @@ function compressCmaps(src, dest, verify) {
"%" "%"
); );
}); });
} };
export { compressCmaps };

View File

@ -13,7 +13,7 @@
* limitations under the License. * limitations under the License.
*/ */
function optimizeCMap(data) { exports.optimizeCMap = function (data) {
let i = 1; let i = 1;
while (i < data.body.length) { while (i < data.body.length) {
if (data.body[i - 1].type === data.body[i].type) { if (data.body[i - 1].type === data.body[i].type) {
@ -151,7 +151,10 @@ function optimizeCMap(data) {
const maxDistance = 100, const maxDistance = 100,
minItems = 10, minItems = 10,
itemsPerBucket = 50; itemsPerBucket = 50;
if (subitems.length > minItems && codes.at(-1) - codes[0] > maxDistance) { if (
subitems.length > minItems &&
codes[codes.length - 1] - codes[0] > maxDistance
) {
const gapsCount = Math.max(2, (subitems.length / itemsPerBucket) | 0); const gapsCount = Math.max(2, (subitems.length / itemsPerBucket) | 0);
const gaps = []; const gaps = [];
for (let q = 0; q < gapsCount; q++) { for (let q = 0; q < gapsCount; q++) {
@ -206,7 +209,7 @@ function optimizeCMap(data) {
} }
i++; i++;
} }
} };
function incHex(a) { function incHex(a) {
let c = 1, let c = 1,
@ -223,5 +226,3 @@ function incHex(a) {
} }
return s; return s;
} }
export { optimizeCMap };

View File

@ -13,13 +13,13 @@
* limitations under the License. * limitations under the License.
*/ */
function parseAdobeCMap(content) { exports.parseAdobeCMap = function (content) {
let m = /(\bbegincmap\b[\s\S]*?)\bendcmap\b/.exec(content); let m = /(\bbegincmap\b[\s\S]*?)\bendcmap\b/.exec(content);
if (!m) { if (!m) {
throw new Error("cmap was not found"); throw new Error("cmap was not found");
} }
const body = m[1].replaceAll(/\r\n?/g, "\n"); const body = m[1].replace(/\r\n?/g, "\n");
const result = { const result = {
type: 1, type: 1,
wmode: 0, wmode: 0,
@ -100,6 +100,4 @@ function parseAdobeCMap(content) {
} }
return result; return result;
} };
export { parseAdobeCMap };

View File

@ -7,8 +7,8 @@ parsing and rendering PDFs.
This is a pre-built version of the PDF.js source code. It is automatically This is a pre-built version of the PDF.js source code. It is automatically
generated by the build scripts. generated by the build scripts.
For usage with older browsers/environments, without native support for the For usage with older browsers or environments, without support for modern
latest JavaScript features, please see the `legacy/` folder. features such as optional chaining, nullish coalescing,
Please see [this wiki page](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support) for information about supported browsers/environments. and private `class` fields/methods; please see the `legacy/` folder.
See https://github.com/mozilla/pdf.js for learning and contributing. See https://github.com/mozilla/pdf.js for learning and contributing.

View File

@ -1 +0,0 @@
export * from "../../types/web/pdf_viewer.component.js";

View File

@ -0,0 +1 @@
export * from "../../types/web/pdf_viewer.component";

7
external/dist/lib/README.md vendored Normal file
View File

@ -0,0 +1,7 @@
It's *very highly* recommended to *not* use these files, but rather use the
pre-built library as found in e.g. the `/build`, `/web`, and `/image_decoders`
folders in the root of this repository.
Please note that the "lib" build target exists mostly to enable unit-testing in
Node.js/GitHub, and that you'll need to handle e.g. any necessary polyfills
and/or Node.js dependencies yourself if using the files in this folder.

View File

@ -1 +0,0 @@
export * from "../types/web/pdf_viewer.component.js";

1
external/dist/web/pdf_viewer.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "../types/web/pdf_viewer.component";

View File

@ -12,15 +12,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* eslint-disable import/no-unresolved */
import { GlobalWorkerOptions } from "./build/pdf.mjs"; "use strict";
const pdfjs = require("./build/pdf.js");
if (typeof window !== "undefined" && "Worker" in window) { if (typeof window !== "undefined" && "Worker" in window) {
GlobalWorkerOptions.workerPort = new Worker( pdfjs.GlobalWorkerOptions.workerPort = new Worker(
new URL("./build/pdf.worker.mjs", import.meta.url), new URL("./build/pdf.worker.js", import.meta.url)
{ type: "module" }
); );
} }
export * from "./build/pdf.mjs"; module.exports = pdfjs;

151
external/importL10n/locales.js vendored Normal file
View File

@ -0,0 +1,151 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
const fs = require("fs");
const https = require("https");
const path = require("path");
// Fetches all languages that have an *active* translation in mozilla-central.
// This is used in gulpfile.js for the `importl10n` command.
const DEFAULT_LOCALE = "en-US";
const EXCLUDE_LANG_CODES = ["ca-valencia", "ja-JP-mac"];
function normalizeText(s) {
return s.replace(/\r\n?/g, "\n").replace(/\uFEFF/g, "");
}
function downloadLanguageCodes() {
console.log("Downloading language codes...\n");
const ALL_LOCALES =
"https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/locales/all-locales";
return new Promise(function (resolve) {
https.get(ALL_LOCALES, function (response) {
if (response.statusCode === 200) {
let content = "";
response.setEncoding("utf8");
response.on("data", function (chunk) {
content += chunk;
});
response.on("end", function () {
content = content.trim(); // Remove any leading/trailing white-space.
const langCodes = normalizeText(content).split("\n");
// Remove all locales that we don't want to download below.
for (const langCode of [DEFAULT_LOCALE, ...EXCLUDE_LANG_CODES]) {
const i = langCodes.indexOf(langCode);
if (i > -1) {
langCodes.splice(i, 1);
}
}
resolve(langCodes);
});
} else {
resolve([]);
}
});
});
}
function downloadLanguageFiles(root, langCode) {
console.log("Downloading " + langCode + "...");
// Constants for constructing the URLs. Translations are taken from the
// Nightly channel as those are the most recent ones.
const MOZ_CENTRAL_ROOT = "https://hg.mozilla.org/l10n-central/";
const MOZ_CENTRAL_PDFJS_DIR = "/raw-file/default/browser/pdfviewer/";
// Defines which files to download for each language.
const files = ["viewer.properties"];
let downloadsLeft = files.length;
const outputDir = path.join(root, langCode);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}
return new Promise(function (resolve) {
// Download the necessary files for this language.
files.forEach(function (fileName) {
const outputPath = path.join(outputDir, fileName);
const url =
MOZ_CENTRAL_ROOT + langCode + MOZ_CENTRAL_PDFJS_DIR + fileName;
https.get(url, function (response) {
// Not all files exist for each language. Files without translations
// have been removed (https://bugzilla.mozilla.org/show_bug.cgi?id=1443175).
if (response.statusCode === 200) {
let content = "";
response.setEncoding("utf8");
response.on("data", function (chunk) {
content += chunk;
});
response.on("end", function () {
fs.writeFileSync(outputPath, normalizeText(content), "utf8");
if (--downloadsLeft === 0) {
resolve();
}
});
} else {
if (--downloadsLeft === 0) {
resolve();
}
}
});
});
});
}
async function downloadL10n(root, callback) {
const langCodes = await downloadLanguageCodes();
for (const langCode of langCodes) {
if (!langCode) {
continue;
}
await downloadLanguageFiles(root, langCode);
}
const removeCodes = [];
for (const entry of fs.readdirSync(root)) {
const dirPath = path.join(root, entry),
stat = fs.lstatSync(dirPath);
if (
stat.isDirectory() &&
entry !== DEFAULT_LOCALE &&
!langCodes.includes(entry)
) {
removeCodes.push(entry);
}
}
if (removeCodes.length) {
console.log(
"\nConsider removing the following unmaintained locales:\n" +
removeCodes.join(", ") +
"\n"
);
}
if (callback) {
callback();
}
}
exports.downloadL10n = downloadL10n;

View File

@ -1,115 +0,0 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from "fs";
import path from "path";
// Fetches all languages that have an *active* translation in mozilla-central.
// This is used in gulpfile.js for the `importl10n` command.
const DEFAULT_LOCALE = "en-US";
const EXCLUDE_LANG_CODES = new Set(["ca-valencia", "ja-JP-mac"]);
function normalizeText(s) {
return s.replaceAll(/\r\n?/g, "\n").replaceAll("\uFEFF", "");
}
async function downloadLanguageCodes() {
console.log("Downloading language codes...\n");
const ALL_LOCALES =
"https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/locales/all-locales";
const response = await fetch(ALL_LOCALES);
if (!response.ok) {
throw new Error(response.statusText);
}
const content = await response.text();
// Remove any leading/trailing white-space.
const langCodes = normalizeText(content.trim()).split("\n");
// Remove all locales that we don't want to download below.
return langCodes.filter(
langCode => langCode !== DEFAULT_LOCALE && !EXCLUDE_LANG_CODES.has(langCode)
);
}
async function downloadLanguageFiles(root, langCode) {
console.log(`Downloading ${langCode}...`);
// Constants for constructing the URLs. Translations are taken from the
// Nightly channel as those are the most recent ones.
const MOZ_CENTRAL_ROOT = "https://hg.mozilla.org/l10n-central/";
const MOZ_CENTRAL_PDFJS_DIR = "/raw-file/default/toolkit/toolkit/pdfviewer/";
// Defines which files to download for each language.
const files = ["viewer.ftl"];
const outputDir = path.join(root, langCode);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}
// Download the necessary files for this language.
for (const fileName of files) {
const outputPath = path.join(outputDir, fileName);
const url = MOZ_CENTRAL_ROOT + langCode + MOZ_CENTRAL_PDFJS_DIR + fileName;
const response = await fetch(url);
if (!response.ok) {
// Not all files exist for each language. Files without translations
// have been removed (https://bugzilla.mozilla.org/show_bug.cgi?id=1443175).
continue;
}
const content = await response.text();
fs.writeFileSync(outputPath, normalizeText(content), "utf8");
}
}
async function downloadL10n(root) {
const langCodes = await downloadLanguageCodes();
for (const langCode of langCodes) {
if (!langCode) {
continue;
}
await downloadLanguageFiles(root, langCode);
}
const removeCodes = [];
for (const entry of fs.readdirSync(root)) {
const dirPath = path.join(root, entry),
stat = fs.lstatSync(dirPath);
if (
stat.isDirectory() &&
entry !== DEFAULT_LOCALE &&
!langCodes.includes(entry)
) {
removeCodes.push(entry);
}
}
if (removeCodes.length) {
console.log(
"\nConsider removing the following unmaintained locales:\n" +
removeCodes.join(", ") +
"\n"
);
}
}
export { downloadL10n };

File diff suppressed because one or more lines are too long

BIN
external/standard_fonts/FoxitSans.pfb vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

152
external/systemjs/plugin-babel-cached.js vendored Normal file
View File

@ -0,0 +1,152 @@
/* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-var */
var babel = require("plugin-babel");
var cacheExpiration = 60 /* min */ * 60 * 1000;
var dbVersion = 1;
var dbName = "babelcache";
var dbCacheTable = "translated";
var dbPromise;
function getDb() {
if (!dbPromise) {
dbPromise = new Promise(function (resolve, reject) {
var request = indexedDB.open(dbName, dbVersion);
request.onupgradeneeded = function () {
var db = request.result;
db.createObjectStore(dbCacheTable, { keyPath: "address" });
};
request.onsuccess = function () {
var db = request.result;
resolve(db);
};
request.onerror = function () {
console.warn("getDb: " + request.error);
reject(request.error);
};
});
}
return dbPromise;
}
function storeCache(address, hashCode, translated, format, sourceMap) {
return getDb().then(function (db) {
var tx = db.transaction(dbCacheTable, "readwrite");
var store = tx.objectStore(dbCacheTable);
store.put({
address,
hashCode,
translated,
expires: Date.now() + cacheExpiration,
format,
sourceMap,
});
return new Promise(function (resolve, reject) {
tx.oncomplete = function () {
resolve();
};
tx.onerror = function () {
resolve();
};
});
});
}
function loadCache(address, hashCode) {
return getDb().then(function (db) {
var tx = db.transaction(dbCacheTable, "readonly");
var store = tx.objectStore(dbCacheTable);
var getAddress = store.get(address);
return new Promise(function (resolve, reject) {
tx.oncomplete = function () {
var found = getAddress.result;
var isValid =
found && found.hashCode === hashCode && Date.now() < found.expires;
resolve(
isValid
? {
translated: found.translated,
format: found.format,
sourceMap: found.sourceMap,
}
: null
);
};
tx.onerror = function () {
resolve(null);
};
});
});
}
var encoder = new TextEncoder("utf-8");
function sha256(str) {
var buffer = encoder.encode(str);
return crypto.subtle.digest("SHA-256", buffer).then(function (hash) {
var data = new Int32Array(hash);
return (
data[0].toString(36) +
"-" +
data[1].toString(36) +
"-" +
data[2].toString(36) +
"-" +
data[3].toString(36)
);
});
}
exports.translate = function (load, opt) {
var savedHashCode, babelTranslateError;
return sha256(load.source)
.then(function (hashCode) {
savedHashCode = hashCode;
return loadCache(load.address, hashCode);
})
.then(
function (cache) {
if (cache) {
load.metadata.format = cache.format;
return cache.translated;
}
return babel.translate.call(this, load, opt).then(
function (translated) {
return storeCache(
load.address,
savedHashCode,
translated,
load.metadata.format,
load.metadata.sourceMap
).then(function () {
return translated;
});
},
function (reason) {
throw (babelTranslateError = reason);
}
);
}.bind(this)
)
.catch(
function (reason) {
if (babelTranslateError) {
throw babelTranslateError;
}
return babel.translate.call(this, load, opt);
}.bind(this)
);
};

3
external/webL10n/README.md vendored Normal file
View File

@ -0,0 +1,3 @@
The source code for the library can be found at
https://github.com/fabi1cazenave/webL10n

1029
external/webL10n/l10n.js vendored Normal file

File diff suppressed because it is too large Load Diff

43
external/webpack/pdfjsdev-loader.js vendored Normal file
View File

@ -0,0 +1,43 @@
/* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
const preprocessor2 = require("../builder/preprocessor2.js");
const path = require("path");
module.exports = function (source) {
// Options must be specified, ignoring request if not.
if (!this.query || typeof this.query !== "object") {
return source;
}
this.cacheable();
const filePath = this.resourcePath;
const context = this.rootContext;
const sourcePath = path.relative(context, filePath).split(path.sep).join("/");
const ctx = Object.create(this.query);
ctx.sourceMap = true;
ctx.sourceFile = sourcePath;
const callback = this.callback;
const sourceAndMap = preprocessor2.preprocessPDFJSCode(ctx, source);
const map = sourceAndMap.map.toJSON();
// escodegen does not embed source -- setting map's sourcesContent.
map.sourcesContent = [source];
callback(null, sourceAndMap.code, map);
return undefined;
};

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More