Compare commits

..

No commits in common. "master" and "v2.5.207" have entirely different histories.

1309 changed files with 106623 additions and 326705 deletions

View File

@ -1,6 +1,6 @@
root = true root = true
[*.{js,jsm,mjs,json,html,css,pdf.link}] [*.{js,jsm,html,css}]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
indent_size = 2 indent_size = 2
@ -9,12 +9,9 @@ insert_final_newline = true
max_line_length = 80 max_line_length = 80
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.{json,pdf.link}]
max_line_length = off
[*.md] [*.md]
max_line_length = off max_line_length = 0
trim_trailing_whitespace = false trim_trailing_whitespace = false
[COMMIT_EDITMSG] [COMMIT_EDITMSG]
max_line_length = off max_line_length = 0

View File

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

106
.eslintrc
View File

@ -1,84 +1,43 @@
{ {
"parserOptions": { "parserOptions": {
"ecmaVersion": 2022, "ecmaVersion": 2020,
"sourceType": "module", "sourceType": "module",
}, },
"plugins": [ "plugins": [
"import", "import",
"json",
"mozilla", "mozilla",
"no-unsanitized", "no-unsanitized",
"sort-exports",
"unicorn", "unicorn",
], ],
"extends": [ "extends": [
"plugin:json/recommended",
"plugin:prettier/recommended" "plugin:prettier/recommended"
], ],
"env": { "env": {
"browser": true, "browser": true,
"es2022": true, "es2020": 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/no-unresolved": "error",
"import/named": "error",
"import/no-cycle": "error",
"import/no-empty-named-blocks": "error",
"import/no-commonjs": "error",
"import/no-mutable-exports": "error",
"import/no-self-import": "error",
"import/no-unresolved": ["error", {
"ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web", "fluent-bundle", "fluent-dom"],
}],
"mozilla/avoid-removeChild": "error", "mozilla/avoid-removeChild": "error",
"mozilla/use-includes-instead-of-indexOf": "error", "mozilla/use-includes-instead-of-indexOf": "error",
"no-unsanitized/method": "error", "no-unsanitized/method": "error",
"no-unsanitized/property": "error", "no-unsanitized/property": "error",
"sort-exports/sort-exports": ["error", { "unicorn/no-array-instanceof": "error",
"ignoreCase": true, "unicorn/prefer-starts-ends-with": "error",
}],
"unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-push-push": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-invalid-remove-event-listener": "error",
"unicorn/no-new-buffer": "error",
"unicorn/no-typeof-undefined": ["error", {
"checkGlobalVariables": false,
}],
"unicorn/no-useless-promise-resolve-reject": "error",
"unicorn/no-useless-spread": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-at": "error",
"unicorn/prefer-date-now": "error",
"unicorn/prefer-dom-node-append": "error",
"unicorn/prefer-dom-node-remove": "error",
"unicorn/prefer-logical-operator-over-ternary": "error",
"unicorn/prefer-modern-dom-apis": "error",
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-optional-catch-binding": "error",
"unicorn/prefer-regexp-test": "error",
"unicorn/prefer-string-replace-all": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-ternary": ["error", "only-single-line"],
// Possible errors // Possible errors
"for-direction": "error", "for-direction": "error",
@ -86,32 +45,25 @@
"no-async-promise-executor": "error", "no-async-promise-executor": "error",
"no-cond-assign": ["error", "except-parens"], "no-cond-assign": ["error", "except-parens"],
"no-constant-condition": ["error", { "checkLoops": false, }], "no-constant-condition": ["error", { "checkLoops": false, }],
"no-debugger": "error",
"no-dupe-args": "error", "no-dupe-args": "error",
"no-dupe-else-if": "error", "no-dupe-else-if": "error",
"no-dupe-keys": "error", "no-dupe-keys": "error",
"no-duplicate-case": "error", "no-duplicate-case": "error",
"no-empty": ["error", { "allowEmptyCatch": true, }], "no-empty": ["error", { "allowEmptyCatch": true, }],
"no-empty-character-class": "error",
"no-ex-assign": "error", "no-ex-assign": "error",
"no-extra-boolean-cast": "error", "no-extra-boolean-cast": "error",
"no-func-assign": "error", "no-func-assign": "error",
"no-inner-declarations": ["error", "functions"], "no-inner-declarations": ["error", "functions"],
"no-invalid-regexp": "error", "no-invalid-regexp": "error",
"no-irregular-whitespace": "error", "no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-obj-calls": "error", "no-obj-calls": "error",
"no-promise-executor-return": "error",
"no-regex-spaces": "error", "no-regex-spaces": "error",
"no-setter-return": "error", "no-setter-return": "error",
"no-sparse-arrays": "error", "no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-unexpected-multiline": "error", "no-unexpected-multiline": "error",
"no-unreachable": "error", "no-unreachable": "error",
"no-unsafe-finally": "error", "no-unsafe-finally": "error",
"no-unsafe-negation": "error", "no-unsafe-negation": "error",
"no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }],
"no-unused-private-class-members": "error",
"use-isnan": ["error", { "enforceForIndexOf": true, }], "use-isnan": ["error", { "enforceForIndexOf": true, }],
"valid-typeof": ["error", { "requireStringLiterals": true, }], "valid-typeof": ["error", { "requireStringLiterals": true, }],
@ -126,7 +78,6 @@
"dot-notation": "error", "dot-notation": "error",
"eqeqeq": ["error", "always"], "eqeqeq": ["error", "always"],
"grouped-accessor-pairs": ["error", "getBeforeSet"], "grouped-accessor-pairs": ["error", "getBeforeSet"],
"no-alert": "error",
"no-caller": "error", "no-caller": "error",
"no-else-return": "error", "no-else-return": "error",
"no-empty-pattern": "error", "no-empty-pattern": "error",
@ -140,12 +91,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",
@ -158,10 +107,8 @@
"no-useless-call": "error", "no-useless-call": "error",
"no-useless-catch": "error", "no-useless-catch": "error",
"no-useless-concat": "error", "no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error", "no-useless-return": "error",
"prefer-promise-reject-errors": "error", "prefer-promise-reject-errors": "error",
"prefer-spread": "error",
"wrap-iife": ["error", "any"], "wrap-iife": ["error", "any"],
"yoda": ["error", "never", { "yoda": ["error", "never", {
"exceptRange": true, "exceptRange": true,
@ -171,6 +118,7 @@
"strict": ["off", "global"], "strict": ["off", "global"],
// Variables // Variables
"no-catch-shadow": "error",
"no-delete-var": "error", "no-delete-var": "error",
"no-label-var": "error", "no-label-var": "error",
"no-shadow": "error", "no-shadow": "error",
@ -178,14 +126,12 @@
"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", {
"functions": false, // Node.js and CommonJS
"classes": false, "no-buffer-constructor": "error",
"variables": false,
}],
// Stylistic Issues // Stylistic Issues
"lines-between-class-members": ["error", "always"], "lines-between-class-members": ["error", "always"],
@ -200,26 +146,10 @@
"no-nested-ternary": "error", "no-nested-ternary": "error",
"no-new-object": "error", "no-new-object": "error",
"no-restricted-syntax": ["error", "no-restricted-syntax": ["error",
{
"selector": "BinaryExpression[operator='instanceof'][right.name='Object']",
"message": "Use `typeof` rather than `instanceof Object`.",
},
{ {
"selector": "CallExpression[callee.name='assert'][arguments.length!=2]", "selector": "CallExpression[callee.name='assert'][arguments.length!=2]",
"message": "`assert()` must always be invoked with two arguments.", "message": "`assert()` must always be invoked with two arguments.",
}, },
{
"selector": "CallExpression[callee.name='isCmd'][arguments.length<2]",
"message": "Use `instanceof Cmd` rather than `isCmd()` with one argument.",
},
{
"selector": "CallExpression[callee.name='isDict'][arguments.length<2]",
"message": "Use `instanceof Dict` rather than `isDict()` with one argument.",
},
{
"selector": "CallExpression[callee.name='isName'][arguments.length<2]",
"message": "Use `instanceof Name` rather than `isName()` with one argument.",
},
{ {
"selector": "NewExpression[callee.name='Cmd']", "selector": "NewExpression[callee.name='Cmd']",
"message": "Use `Cmd.get()` rather than `new Cmd()`.", "message": "Use `Cmd.get()` rather than `new Cmd()`.",
@ -234,7 +164,6 @@
}, },
], ],
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
"operator-assignment": "error",
"prefer-exponentiation-operator": "error", "prefer-exponentiation-operator": "error",
"spaced-comment": ["error", "always", { "spaced-comment": ["error", "always", {
"block": { "block": {
@ -243,9 +172,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",
@ -253,12 +182,11 @@
"no-useless-computed-key": "error", "no-useless-computed-key": "error",
"no-useless-constructor": "error", "no-useless-constructor": "error",
"no-useless-rename": "error", "no-useless-rename": "error",
"no-var": "error", "no-var": "off",
"object-shorthand": ["error", "always", { "object-shorthand": ["error", "always", {
"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

@ -1,8 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
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

@ -1,37 +0,0 @@
name: CI
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, lts/*, latest]
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 external tests
run: gulp externaltest
- name: Run CLI unit tests
run: gulp unittestcli

View File

@ -1,35 +0,0 @@
name: CodeQL
on: [push, pull_request]
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
security-events: write
strategy:
fail-fast: false
matrix:
language: [javascript]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality
- name: Autobuild CodeQL
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v3

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

@ -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

@ -1,25 +0,0 @@
{
"plugins": [
"stylelint-prettier"
],
"rules": {
"prettier/prettier": true,
"alpha-value-notation": "number",
"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, {
ignore: ["custom-properties"]
}],
"selector-pseudo-element-colon-notation": "double",
"shorthand-property-no-redundant-values": true,
},
}

10
.travis.yml Normal file
View File

@ -0,0 +1,10 @@
language: node_js
node_js:
- lts/*
cache:
directories:
- node_modules
install:
- npm install -g npm@latest
- npm install -g gulp-cli
- npm install

View File

@ -1,8 +1,8 @@
# PDF.js [![Build Status](https://github.com/mozilla/pdf.js/workflows/CI/badge.svg?branch=master)](https://github.com/mozilla/pdf.js/actions?query=workflow%3ACI+branch%3Amaster) # PDF.js [![Build Status](https://travis-ci.org/mozilla/pdf.js.svg?branch=master)](https://travis-ci.org/mozilla/pdf.js)
[PDF.js](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5. [PDF.js](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5.
PDF.js is community-driven and supported by Mozilla. Our goal is to PDF.js is community-driven and supported by Mozilla Labs. Our goal is to
create a general-purpose, web standards-based platform for parsing and create a general-purpose, web standards-based platform for parsing and
rendering PDFs. rendering PDFs.
@ -14,7 +14,7 @@ get involved, visit:
+ [Issue Reporting Guide](https://github.com/mozilla/pdf.js/blob/master/.github/CONTRIBUTING.md) + [Issue Reporting Guide](https://github.com/mozilla/pdf.js/blob/master/.github/CONTRIBUTING.md)
+ [Code Contribution Guide](https://github.com/mozilla/pdf.js/wiki/Contributing) + [Code Contribution Guide](https://github.com/mozilla/pdf.js/wiki/Contributing)
+ [Frequently Asked Questions](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions) + [Frequently Asked Questions](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions)
+ [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?direction=desc&labels=good-beginner-bug&page=1&sort=created&state=open) + [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?direction=desc&labels=5-good-beginner-bug&page=1&sort=created&state=open)
+ [Projects](https://github.com/mozilla/pdf.js/projects) + [Projects](https://github.com/mozilla/pdf.js/projects)
Feel free to stop by our [Matrix room](https://chat.mozilla.org/#/room/#pdfjs:mozilla.org) for questions or guidance. Feel free to stop by our [Matrix room](https://chat.mozilla.org/#/room/#pdfjs:mozilla.org) for questions or guidance.
@ -23,12 +23,12 @@ 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 e.g. `async`/`await`, `Promise`, and `ReadableStream`.
+ Modern browsers: https://mozilla.github.io/pdf.js/web/viewer.html + Modern browsers: https://mozilla.github.io/pdf.js/web/viewer.html
+ Older browsers: https://mozilla.github.io/pdf.js/legacy/web/viewer.html + Older browsers: https://mozilla.github.io/pdf.js/es5/web/viewer.html
### Browser Extensions ### Browser Extensions
@ -53,19 +53,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 +70,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 an ES6 compatible browser; refer to [Building PDF.js](https://github.com/mozilla/pdf.js/blob/master/README.md#building-pdfjs) for usage with older browsers.
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:
@ -88,11 +83,7 @@ viewer, run:
$ gulp generic $ gulp generic
If you need to support older browsers, run: This will generate `pdf.js` and `pdf.worker.js` in the `build/generic/build/` directory.
$ gulp generic-legacy
This will generate `pdf.js` and `pdf.worker.js` in the `build/generic/build/` directory (respectively `build/generic-legacy/build/`).
Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will
be loaded by `pdf.js`. The PDF.js files are large and should be minified for production. be loaded by `pdf.js`. The PDF.js files are large and should be minified for production.
@ -144,3 +135,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

@ -44,24 +44,14 @@ Each PDF page has its own viewport which defines the size in pixels(72DPI) and i
```js ```js
var scale = 1.5; var scale = 1.5;
var viewport = page.getViewport({ scale: scale, }); var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
var canvas = document.getElementById('the-canvas'); var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = Math.floor(viewport.width * outputScale); canvas.width = viewport.width;
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
var transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
var renderContext = { var renderContext = {
canvasContext: context, canvasContext: context,
transform: transform,
viewport: viewport viewport: viewport
}; };
page.render(renderContext); page.render(renderContext);
@ -81,19 +71,19 @@ var scaledViewport = page.getViewport({ scale: scale, });
### Hello World with document load error handling ### Hello World with document load error handling
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 page 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

@ -37,22 +37,25 @@ Before downloading PDF.js please take a moment to understand the different layer
## Download ## Download
Please refer to [this wiki page](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support) for information about supported browsers.
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<h3>Prebuilt (modern browsers)</h3> <h3>Prebuilt</h3>
<p> <p>
Includes the generic build of PDF.js and the viewer. Includes the generic build of PDF.js and the viewer.
</p> </p>
<a type="button" class="btn btn-primary" href="https://github.com/mozilla/pdf.js/releases/download/vSTABLE_VERSION/pdfjs-STABLE_VERSION-dist.zip">Stable (vSTABLE_VERSION)</a> <span class="GROUP_CLASS">
<a type="button" class="btn btn-warning HIDDEN_CLASS" href="https://github.com/mozilla/pdf.js/releases/download/vBETA_VERSION/pdfjs-BETA_VERSION-dist.zip">Beta (vBETA_VERSION)</a>
</span>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h3>Prebuilt (older browsers)</h3> <h3>Prebuilt (ES5-compatible)</h3>
<p> <p>
Includes the generic build of PDF.js and the viewer. Includes the generic build of PDF.js and the viewer.
</p> </p>
<a type="button" class="btn btn-primary" href="https://github.com/mozilla/pdf.js/releases/download/vSTABLE_VERSION/pdfjs-STABLE_VERSION-legacy-dist.zip">Stable (vSTABLE_VERSION)</a> <span class="GROUP_CLASS">
<a type="button" class="btn btn-primary" href="https://github.com/mozilla/pdf.js/releases/download/vSTABLE_VERSION/pdfjs-STABLE_VERSION-dist.zip">Stable (vSTABLE_VERSION)</a>
<a type="button" class="btn btn-warning HIDDEN_CLASS" href="https://github.com/mozilla/pdf.js/releases/download/vBETA_VERSION/pdfjs-BETA_VERSION-es5-dist.zip">Beta (vBETA_VERSION)</a>
</span>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h3>Source</h3> <h3>Source</h3>
@ -108,8 +111,9 @@ 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
├── test/ - unit, font, reference, and integration tests │   └── worker_loader.js - used for developer builds to load worker files
├── test/ - unit, font and reference tests
├── web/ - viewer layer ├── web/ - viewer layer
├── LICENSE ├── LICENSE
├── README.md ├── README.md

View File

@ -3,9 +3,22 @@
"../.eslintrc" "../.eslintrc"
], ],
"parserOptions": {
"ecmaVersion": 6,
},
"env": {
"es6": false,
},
"globals": { "globals": {
"pdfjsImageDecoders": false, "pdfjsImageDecoders": false,
"pdfjsLib": false, "pdfjsLib": false,
"pdfjsViewer": false, "pdfjsViewer": false,
"Uint8Array": false,
}, },
"rules": {
"object-shorthand": ["error", "never"]
}
} }

View File

@ -0,0 +1,11 @@
## Overview
Example to demonstrate PDF.js library usage for rendering files with AcroForms.
## Getting started
Build and install PDF.js using `gulp dist-install` and run `gulp server` to
start a web server. You can then work with the example at
http://localhost:8888/examples/acroforms/acroforms.html.
Refer to `acroforms.js` for the source code of the example.

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!--
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.
-->
<html dir="ltr">
<head>
<meta charset="utf-8">
<title>AcroForms example</title>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.page {
position: relative;
}
</style>
</head>
<body>
<div id="pageContainer"></div>
<script src="acroforms.js"></script>
</body>
</html>

View File

@ -0,0 +1,56 @@
/* 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";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
var DEFAULT_URL = "../../test/pdfs/f1040.pdf";
var DEFAULT_SCALE = 1.0;
var container = document.getElementById("pageContainer");
var eventBus = new pdfjsViewer.EventBus();
// Fetch the PDF document from the URL using promises.
var loadingTask = pdfjsLib.getDocument(DEFAULT_URL);
loadingTask.promise.then(function (doc) {
// Use a promise to fetch and render the next page.
var promise = Promise.resolve();
for (var i = 1; i <= doc.numPages; i++) {
promise = promise.then(
function (pageNum) {
return doc.getPage(pageNum).then(function (pdfPage) {
// Create the page view.
var pdfPageView = new pdfjsViewer.PDFPageView({
container: container,
id: pageNum,
scale: DEFAULT_SCALE,
defaultViewport: pdfPage.getViewport({ scale: DEFAULT_SCALE }),
eventBus: eventBus,
annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(),
renderInteractiveForms: true,
});
// Associate the actual page with the view and draw it.
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
}.bind(null, i)
);
}
});

1
examples/browserify/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -0,0 +1,26 @@
## Overview
Example to demonstrate PDF.js library usage with Browserify.
## Getting started
Build project and install the example dependencies:
$ gulp dist-install
$ cd examples/browserify
$ npm install
To build Browserify bundles, run `gulp build`. If you are running
a web server, you can observe the build results at
http://localhost:8888/examples/browserify/index.html
See main.js, worker.js and gulpfile.js files. Please notice that PDF.js
packaging requires packaging of the main application and PDF.js worker code,
and the `workerSrc` path shall be set to the latter file. The pdf.worker.js file
shall be excluded from the main bundle.
Alternatives to the gulp commands (without compression) are:
$ mkdir -p ../../build/browserify
$ node_modules/.bin/browserify main.js -u ./node_modules/pdfjs-dist/build/pdf.worker.js -o ../../build/browserify/main.bundle.js
$ node_modules/.bin/browserify worker.js -o ../../build/browserify/pdf.worker.bundle.js

View File

@ -0,0 +1,40 @@
var gulp = require("gulp");
var browserify = require("browserify");
var streamify = require("gulp-streamify");
var rename = require("gulp-rename");
var uglify = require("gulp-uglify");
var source = require("vinyl-source-stream");
var OUTPUT_PATH = "../../build/browserify";
var TMP_FILE_PREFIX = "../../build/browserify_";
gulp.task("build-bundle", function () {
return browserify("main.js", { output: TMP_FILE_PREFIX + "main.tmp" })
.ignore(require.resolve("pdfjs-dist/build/pdf.worker")) // Reducing size
.bundle()
.pipe(source(TMP_FILE_PREFIX + "main.tmp"))
.pipe(streamify(uglify()))
.pipe(rename("main.bundle.js"))
.pipe(gulp.dest(OUTPUT_PATH));
});
gulp.task("build-worker", function () {
// We can create our own viewer (see worker.js) or use already defined one.
var workerSrc = require.resolve("pdfjs-dist/build/pdf.worker.entry");
return browserify(workerSrc, { output: TMP_FILE_PREFIX + "worker.tmp" })
.bundle()
.pipe(source(TMP_FILE_PREFIX + "worker.tmp"))
.pipe(
streamify(
uglify({
compress: {
sequences: false, // Chrome has issue with the generated code if true
},
})
)
)
.pipe(rename("pdf.worker.bundle.js"))
.pipe(gulp.dest(OUTPUT_PATH));
});
gulp.task("build", gulp.series("build-bundle", "build-worker"));

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>browserify example</title>
<script src="../../build/browserify/main.bundle.js"></script>
</head>
<body>
<canvas id="theCanvas"></canvas>
</body>
</html>

View File

@ -0,0 +1,35 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Hello world example for browserify.
var pdfjsLib = require("pdfjs-dist");
var pdfPath = "../learning/helloworld.pdf";
// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../build/browserify/pdf.worker.bundle.js";
// Loading a document.
var 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.
var viewport = pdfPage.getViewport({ scale: 1.0 });
var canvas = document.getElementById("theCanvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
var ctx = canvas.getContext("2d");
var renderTask = pdfPage.render({
canvasContext: ctx,
viewport: viewport,
});
return renderTask.promise;
});
})
.catch(function (reason) {
console.error("Error: " + reason);
});

View File

@ -0,0 +1,16 @@
{
"name": "browserify-pdf.js-example",
"version": "0.1.0",
"devDependencies": {
"browserify": "^13.0.0",
"gulp": "^3.9.1",
"gulp-rename": "^1.2.2",
"gulp-streamify": "^1.0.2",
"gulp-uglify": "^1.5.3",
"pdfjs-dist": "../../node_modules/pdfjs-dist",
"vinyl-source-stream": "^1.1.0"
},
"scripts": {
"build": "gulp build"
}
}

View File

@ -0,0 +1,9 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Hello world example for browserify: worker bundle.
(typeof window !== "undefined"
? window
: {}
).pdfjsWorker = require("pdfjs-dist/build/pdf.worker");

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

@ -0,0 +1,64 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps.
//
var CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
var CMAP_PACKED = true;
var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
var PAGE_TO_VIEW = 1;
var SCALE = 1.0;
var container = document.getElementById("pageContainer");
var eventBus = new pdfjsViewer.EventBus();
// Loading document.
var loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
});
loadingTask.promise.then(function (pdfDocument) {
// Document loaded, retrieving the page.
return pdfDocument.getPage(PAGE_TO_VIEW).then(function (pdfPage) {
// Creating the page view with default parameters.
var pdfPageView = new pdfjsViewer.PDFPageView({
container: container,
id: PAGE_TO_VIEW,
scale: SCALE,
defaultViewport: pdfPage.getViewport({ scale: SCALE }),
eventBus: eventBus,
// We can enable text/annotations layers, if needed
textLayerFactory: new pdfjsViewer.DefaultTextLayerFactory(),
annotationLayerFactory: new pdfjsViewer.DefaultAnnotationLayerFactory(),
});
// Associates the actual page with the view, and drawing it
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
});

View File

@ -1,63 +0,0 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
// Some PDFs need external cmaps.
//
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
const PAGE_TO_VIEW = 1;
const SCALE = 1.0;
const ENABLE_XFA = true;
const container = document.getElementById("pageContainer");
const eventBus = new pdfjsViewer.EventBus();
// Loading document.
const loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA,
});
const pdfDocument = await loadingTask.promise;
// Document loaded, retrieving the page.
const pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW);
// Creating the page view with default parameters.
const pdfPageView = new pdfjsViewer.PDFPageView({
container,
id: PAGE_TO_VIEW,
scale: SCALE,
defaultViewport: pdfPage.getViewport({ scale: SCALE }),
eventBus,
});
// Associate the actual page with the view, and draw it.
pdfPageView.setPdfPage(pdfPage);
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

@ -0,0 +1,80 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps.
//
var CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
var CMAP_PACKED = true;
var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
var SEARCH_FOR = ""; // try 'Mozilla';
var container = document.getElementById("viewerContainer");
var eventBus = new pdfjsViewer.EventBus();
// (Optionally) enable hyperlinks within PDF files.
var pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus: eventBus,
});
// (Optionally) enable find controller.
var pdfFindController = new pdfjsViewer.PDFFindController({
eventBus: eventBus,
linkService: pdfLinkService,
});
var pdfViewer = new pdfjsViewer.PDFViewer({
container: container,
eventBus: eventBus,
linkService: pdfLinkService,
findController: pdfFindController,
});
pdfLinkService.setViewer(pdfViewer);
eventBus.on("pagesinit", function () {
// We can use pdfViewer now, e.g. let's change default scale.
pdfViewer.currentScaleValue = "page-width";
// We can try searching for things.
if (SEARCH_FOR) {
pdfFindController.executeCommand("find", { query: SEARCH_FOR });
}
});
// Loading document.
var loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
});
loadingTask.promise.then(function (pdfDocument) {
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfViewer.setDocument(pdfDocument);
pdfLinkService.setDocument(pdfDocument, null);
});

View File

@ -1,97 +0,0 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
// Some PDFs need external cmaps.
//
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
// To test the AcroForm and/or scripting functionality, try e.g. this file:
// "../../test/pdfs/160F-2019.pdf"
const ENABLE_XFA = true;
const SEARCH_FOR = ""; // try "Mozilla";
const SANDBOX_BUNDLE_SRC = new URL(
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
window.location
);
const container = document.getElementById("viewerContainer");
const eventBus = new pdfjsViewer.EventBus();
// (Optionally) enable hyperlinks within PDF files.
const pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus,
});
// (Optionally) enable find controller.
const pdfFindController = new pdfjsViewer.PDFFindController({
eventBus,
linkService: pdfLinkService,
});
// (Optionally) enable scripting support.
const pdfScriptingManager = new pdfjsViewer.PDFScriptingManager({
eventBus,
sandboxBundleSrc: SANDBOX_BUNDLE_SRC,
});
const pdfViewer = new pdfjsViewer.PDFViewer({
container,
eventBus,
linkService: pdfLinkService,
findController: pdfFindController,
scriptingManager: pdfScriptingManager,
});
pdfLinkService.setViewer(pdfViewer);
pdfScriptingManager.setViewer(pdfViewer);
eventBus.on("pagesinit", function () {
// We can use pdfViewer now, e.g. let's change default scale.
pdfViewer.currentScaleValue = "page-width";
// We can try searching for things.
if (SEARCH_FOR) {
eventBus.dispatch("find", { type: "", query: SEARCH_FOR });
}
});
// Loading document.
const loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA,
});
const pdfDocument = await loadingTask.promise;
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfViewer.setDocument(pdfDocument);
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

@ -0,0 +1,80 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps.
//
var CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
var CMAP_PACKED = true;
var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
var SEARCH_FOR = ""; // try 'Mozilla';
var container = document.getElementById("viewerContainer");
var eventBus = new pdfjsViewer.EventBus();
// (Optionally) enable hyperlinks within PDF files.
var pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus: eventBus,
});
// (Optionally) enable find controller.
var pdfFindController = new pdfjsViewer.PDFFindController({
eventBus: eventBus,
linkService: pdfLinkService,
});
var pdfSinglePageViewer = new pdfjsViewer.PDFSinglePageViewer({
container: container,
eventBus: eventBus,
linkService: pdfLinkService,
findController: pdfFindController,
});
pdfLinkService.setViewer(pdfSinglePageViewer);
eventBus.on("pagesinit", function () {
// We can use pdfSinglePageViewer now, e.g. let's change default scale.
pdfSinglePageViewer.currentScaleValue = "page-width";
// We can try searching for things.
if (SEARCH_FOR) {
pdfFindController.executeCommand("find", { query: SEARCH_FOR });
}
});
// Loading document.
var loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
});
loadingTask.promise.then(function (pdfDocument) {
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfSinglePageViewer.setDocument(pdfDocument);
pdfLinkService.setDocument(pdfDocument, null);
});

View File

@ -1,97 +0,0 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
// Some PDFs need external cmaps.
//
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
// To test the AcroForm and/or scripting functionality, try e.g. this file:
// "../../test/pdfs/160F-2019.pdf"
const ENABLE_XFA = true;
const SEARCH_FOR = ""; // try "Mozilla";
const SANDBOX_BUNDLE_SRC = new URL(
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
window.location
);
const container = document.getElementById("viewerContainer");
const eventBus = new pdfjsViewer.EventBus();
// (Optionally) enable hyperlinks within PDF files.
const pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus,
});
// (Optionally) enable find controller.
const pdfFindController = new pdfjsViewer.PDFFindController({
eventBus,
linkService: pdfLinkService,
});
// (Optionally) enable scripting support.
const pdfScriptingManager = new pdfjsViewer.PDFScriptingManager({
eventBus,
sandboxBundleSrc: SANDBOX_BUNDLE_SRC,
});
const pdfSinglePageViewer = new pdfjsViewer.PDFSinglePageViewer({
container,
eventBus,
linkService: pdfLinkService,
findController: pdfFindController,
scriptingManager: pdfScriptingManager,
});
pdfLinkService.setViewer(pdfSinglePageViewer);
pdfScriptingManager.setViewer(pdfSinglePageViewer);
eventBus.on("pagesinit", function () {
// We can use pdfSinglePageViewer now, e.g. let's change default scale.
pdfSinglePageViewer.currentScaleValue = "page-width";
// We can try searching for things.
if (SEARCH_FOR) {
eventBus.dispatch("find", { type: "", query: SEARCH_FOR });
}
});
// Loading document.
const loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
enableXfa: ENABLE_XFA,
});
const pdfDocument = await loadingTask.promise;
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfSinglePageViewer.setDocument(pdfDocument);
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

@ -13,42 +13,64 @@
* limitations under the License. * limitations under the License.
*/ */
"use strict";
if (!pdfjsImageDecoders.JpegImage) { if (!pdfjsImageDecoders.JpegImage) {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using `gulp dist-install`"); alert("Please build the pdfjs-dist library using `gulp dist-install`");
} }
const JPEG_IMAGE = "fish.jpg"; var JPEG_IMAGE = "fish.jpg";
const jpegCanvas = document.getElementById("jpegCanvas"); var jpegCanvas = document.getElementById("jpegCanvas");
const jpegCtx = jpegCanvas.getContext("2d"); var jpegCtx = jpegCanvas.getContext("2d");
// Load the image data, and convert it to a Uint8Array. // Load the image data, and convert it to a Uint8Array.
// //
const response = await fetch(JPEG_IMAGE); var nonBinaryRequest = false;
if (!response.ok) { var request = new XMLHttpRequest();
throw new Error(response.statusText); request.open("GET", JPEG_IMAGE, false);
try {
request.responseType = "arraybuffer";
nonBinaryRequest = request.responseType !== "arraybuffer";
} catch (e) {
nonBinaryRequest = true;
}
if (nonBinaryRequest && request.overrideMimeType) {
request.overrideMimeType("text/plain; charset=x-user-defined");
}
request.send(null);
var typedArrayImage;
if (nonBinaryRequest) {
var str = request.responseText,
length = str.length;
var bytes = new Uint8Array(length);
for (var i = 0; i < length; ++i) {
bytes[i] = str.charCodeAt(i) & 0xff;
}
typedArrayImage = bytes;
} else {
typedArrayImage = new Uint8Array(request.response);
} }
const typedArrayImage = new Uint8Array(await response.arrayBuffer());
// Parse the image data using `JpegImage`. // Parse the image data using `JpegImage`.
// //
const jpegImage = new pdfjsImageDecoders.JpegImage(); var jpegImage = new pdfjsImageDecoders.JpegImage();
jpegImage.parse(typedArrayImage); jpegImage.parse(typedArrayImage);
const width = jpegImage.width, var width = jpegImage.width,
height = jpegImage.height; height = jpegImage.height;
const jpegData = jpegImage.getData({ var jpegData = jpegImage.getData({
width, width: width,
height, height: height,
forceRGB: true, forceRGB: true,
}); });
// Render the JPEG image on a <canvas>. // Render the JPEG image on a <canvas>.
// //
const imageData = jpegCtx.createImageData(width, height); var imageData = jpegCtx.createImageData(width, height);
const imageBytes = imageData.data; var imageBytes = imageData.data;
for (let j = 0, k = 0, jj = width * height * 4; j < jj; ) { for (var j = 0, k = 0, jj = width * height * 4; j < jj; ) {
imageBytes[j++] = jpegData[k++]; imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++]; imageBytes[j++] = jpegData[k++];
imageBytes[j++] = jpegData[k++]; imageBytes[j++] = jpegData[k++];

View File

@ -10,59 +10,51 @@
<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.
// //
const url = './helloworld.pdf'; var url = './helloworld.pdf';
// //
// 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); var loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise; loadingTask.promise.then(function(pdf) {
// //
// Fetch the first page // Fetch the first page
// //
const page = await pdf.getPage(1); pdf.getPage(1).then(function(page) {
const scale = 1.5; var scale = 1.5;
const viewport = page.getViewport({ scale }); var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
// //
// Prepare canvas using PDF page dimensions // Prepare canvas using PDF page dimensions
// //
const canvas = document.getElementById("the-canvas"); var canvas = document.getElementById('the-canvas');
const context = canvas.getContext("2d"); var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvas.width = Math.floor(viewport.width * outputScale); //
canvas.height = Math.floor(viewport.height * outputScale); // Render PDF page into canvas context
canvas.style.width = Math.floor(viewport.width) + "px"; //
canvas.style.height = Math.floor(viewport.height) + "px"; var renderContext = {
canvasContext: context,
const transform = outputScale !== 1 viewport: viewport,
? [outputScale, 0, 0, outputScale, 0, 0] };
: null; page.render(renderContext);
});
// });
// Render PDF page into canvas context
//
const renderContext = {
canvasContext: context,
transform,
viewport,
};
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,39 +35,31 @@
// 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, });
var pdf = await loadingTask.promise; loadingTask.promise.then(function(pdf) {
// Fetch the first page. // Fetch the first page.
var page = await pdf.getPage(1); pdf.getPage(1).then(function(page) {
var scale = 1.5; var scale = 1.5;
var viewport = page.getViewport({ scale: scale, }); var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
// Prepare canvas using PDF page dimensions. // Prepare canvas using PDF page dimensions.
var canvas = document.getElementById('the-canvas'); var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvas.width = Math.floor(viewport.width * outputScale); // Render PDF page into canvas context.
canvas.height = Math.floor(viewport.height * outputScale); var renderContext = {
canvas.style.width = Math.floor(viewport.width) + "px"; canvasContext: context,
canvas.style.height = Math.floor(viewport.height) + "px"; viewport: viewport,
};
var transform = outputScale !== 1 page.render(renderContext);
? [outputScale, 0, 0, outputScale, 0, 0] });
: null; });
// Render PDF page into canvas context.
var renderContext = {
canvasContext: context,
transform,
viewport,
};
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,
@ -53,22 +53,12 @@
// Using promise to fetch the page // Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) { pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({ scale: scale, }); var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens. canvas.height = viewport.height;
var outputScale = window.devicePixelRatio || 1; canvas.width = viewport.width;
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
var transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
// Render PDF page into canvas context // Render PDF page into canvas context
var renderContext = { var renderContext = {
canvasContext: ctx, canvasContext: ctx,
transform: transform,
viewport: viewport, viewport: viewport,
}; };
var renderTask = page.render(renderContext); var renderTask = page.render(renderContext);
@ -90,7 +80,7 @@
/** /**
* If another page rendering in progress, waits until the rendering is * If another page rendering in progress, waits until the rendering is
* finished. Otherwise, executes rendering immediately. * finised. Otherwise, executes rendering immediately.
*/ */
function queueRenderPage(num) { function queueRenderPage(num) {
if (pageRendering) { if (pageRendering) {
@ -128,11 +118,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_) {
document.getElementById('page_count').textContent = pdfDoc.numPages; pdfDoc = pdfDoc_;
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

@ -26,12 +26,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 +44,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 +71,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 +81,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,11 +110,10 @@ 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;
background-position: left, right; background-position: left, right;
background-size: 0.2rem, 0.2rem; background-size: 0.2rem, 0.2rem;
@ -139,7 +138,7 @@ footer {
} }
.toolbarButton[disabled] { .toolbarButton[disabled] {
opacity: 0.3; opacity: .3;
} }
.hidden { .hidden {
@ -153,7 +152,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 {
@ -164,7 +166,7 @@ canvas {
.pdfViewer .page .loadingIcon { .pdfViewer .page .loadingIcon {
width: 2.9rem; width: 2.9rem;
height: 2.9rem; height: 2.9rem;
background: url("images/spinner.png") no-repeat left top / 38rem; background: url("images/spinner.png") no-repeat left top / 38rem ;
border: medium none; border: medium none;
animation: 1s steps(10, end) 0s normal none infinite moveDefault; animation: 1s steps(10, end) 0s normal none infinite moveDefault;
display: block; display: block;
@ -184,59 +186,73 @@ 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: .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);
margin-top: 5rem;
} }
#loadingBar .progress { #loadingBar .progress {
position: absolute; position: absolute;
left: 0; left: 0;
width: 100%; width: 0;
transform: scaleX(var(--progressBar-percent));
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: width 200ms;
} }
@keyframes progressIndeterminate { @keyframes progressIndeterminate {
0% { 0% { left: 0; }
transform: translateX(0%); 50% { left: 100%; }
} 100% { left: 100%; }
50% {
transform: translateX(100%);
}
100% {
transform: translateX(100%);
}
} }
#loadingBar.indeterminate .progress { #loadingBar .progress.indeterminate {
transform: none; background-color: rgba(153, 153, 153, 1);
background-color: rgb(153 153 153 / 1);
transition: none; transition: none;
} }
#loadingBar.indeterminate .progress .glimmer { #loadingBar .indeterminate .glimmer {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
height: 100%; height: 100%;
width: 5rem; width: 5rem;
background-image: linear-gradient( background-image: linear-gradient(to right,
to right, rgba(153, 153, 153, 1) 0%,
rgb(153 153 153 / 1) 0%, rgba(255, 255, 255, 1) 50%,
rgb(255 255 255 / 1) 50%, rgba(153, 153, 153, 1) 100%);
rgb(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,27 +13,28 @@
* 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
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. var USE_ONLY_CSS_ZOOM = true;
const TEXT_LAYER_MODE = 0; // DISABLE var TEXT_LAYER_MODE = 0; // DISABLE
const MAX_IMAGE_SIZE = 1024 * 1024; var MAX_IMAGE_SIZE = 1024 * 1024;
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/"; var CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true; var 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"; var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
const DEFAULT_SCALE_DELTA = 1.1; var DEFAULT_SCALE_DELTA = 1.1;
const MIN_SCALE = 0.25; var MIN_SCALE = 0.25;
const MAX_SCALE = 10.0; var MAX_SCALE = 10.0;
const DEFAULT_SCALE_VALUE = "auto"; var DEFAULT_SCALE_VALUE = "auto";
const PDFViewerApplication = { var PDFViewerApplication = {
pdfLoadingTask: null, pdfLoadingTask: null,
pdfDocument: null, pdfDocument: null,
pdfViewer: null, pdfViewer: null,
@ -46,7 +47,7 @@ const PDFViewerApplication = {
* @returns {Promise} - Returns the promise, which is resolved when document * @returns {Promise} - Returns the promise, which is resolved when document
* is opened. * is opened.
*/ */
open(params) { open: function (params) {
if (this.pdfLoadingTask) { if (this.pdfLoadingTask) {
// We need to destroy already opened document // We need to destroy already opened document
return this.close().then( return this.close().then(
@ -57,13 +58,13 @@ const PDFViewerApplication = {
); );
} }
const url = params.url; var url = params.url;
const self = this; var self = this;
this.setTitleUsingUrl(url); this.setTitleUsingUrl(url);
// Loading document. // Loading document.
const loadingTask = pdfjsLib.getDocument({ var loadingTask = pdfjsLib.getDocument({
url, url: url,
maxImageSize: MAX_IMAGE_SIZE, maxImageSize: MAX_IMAGE_SIZE,
cMapUrl: CMAP_URL, cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
@ -80,24 +81,46 @@ const PDFViewerApplication = {
self.pdfDocument = pdfDocument; self.pdfDocument = pdfDocument;
self.pdfViewer.setDocument(pdfDocument); self.pdfViewer.setDocument(pdfDocument);
self.pdfLinkService.setDocument(pdfDocument); self.pdfLinkService.setDocument(pdfDocument);
self.pdfHistory.initialize({ self.pdfHistory.initialize({ fingerprint: pdfDocument.fingerprint });
fingerprint: pdfDocument.fingerprints[0],
});
self.loadingBar.hide(); self.loadingBar.hide();
self.setTitleUsingMetadata(pdfDocument); self.setTitleUsingMetadata(pdfDocument);
}, },
function (reason) { function (exception) {
let key = "pdfjs-loading-error"; var message = exception && exception.message;
if (reason instanceof pdfjsLib.InvalidPDFException) { var l10n = self.l10n;
key = "pdfjs-invalid-file-error"; var 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: message });
}); });
self.loadingBar.hide(); self.loadingBar.hide();
} }
@ -109,12 +132,15 @@ const PDFViewerApplication = {
* @returns {Promise} - Returns the promise, which is resolved when all * @returns {Promise} - Returns the promise, which is resolved when all
* destruction is completed. * destruction is completed.
*/ */
close() { close: function () {
var errorWrapper = document.getElementById("errorWrapper");
errorWrapper.setAttribute("hidden", "true");
if (!this.pdfLoadingTask) { if (!this.pdfLoadingTask) {
return Promise.resolve(); return Promise.resolve();
} }
const promise = this.pdfLoadingTask.destroy(); var promise = this.pdfLoadingTask.destroy();
this.pdfLoadingTask = null; this.pdfLoadingTask = null;
if (this.pdfDocument) { if (this.pdfDocument) {
@ -132,30 +158,27 @@ const PDFViewerApplication = {
}, },
get loadingBar() { get loadingBar() {
const bar = document.getElementById("loadingBar"); var 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) {
this.url = url; this.url = url;
let title = pdfjsLib.getFilenameFromUrl(url) || url; var 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
} }
this.setTitle(title); this.setTitle(title);
}, },
setTitleUsingMetadata(pdfDocument) { setTitleUsingMetadata: function (pdfDocument) {
const self = this; var self = this;
pdfDocument.getMetadata().then(function (data) { pdfDocument.getMetadata().then(function (data) {
const info = data.info, var info = data.info,
metadata = data.metadata; metadata = data.metadata;
self.documentInfo = info; self.documentInfo = info;
self.metadata = metadata; self.metadata = metadata;
@ -163,7 +186,7 @@ const PDFViewerApplication = {
// Provides some basic debug information // Provides some basic debug information
console.log( console.log(
"PDF " + "PDF " +
pdfDocument.fingerprints[0] + pdfDocument.fingerprint +
" [" + " [" +
info.PDFFormatVersion + info.PDFFormatVersion +
" " + " " +
@ -176,9 +199,9 @@ const PDFViewerApplication = {
")" ")"
); );
let pdfTitle; var pdfTitle;
if (metadata && metadata.has("dc:title")) { if (metadata && metadata.has("dc:title")) {
const title = metadata.get("dc:title"); var title = metadata.get("dc:title");
// Ghostscript sometimes returns 'Untitled', so prevent setting the // Ghostscript sometimes returns 'Untitled', so prevent setting the
// title to 'Untitled. // title to 'Untitled.
if (title !== "Untitled") { if (title !== "Untitled") {
@ -202,29 +225,83 @@ const PDFViewerApplication = {
}, },
error: function pdfViewError(message, moreInfo) { error: function pdfViewError(message, moreInfo) {
const moreInfoText = [ var l10n = this.l10n;
`PDF.js v${pdfjsLib.version || "?"} (build: ${pdfjsLib.build || "?"})`, var moreInfoText = [
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")}`); var errorWrapper = document.getElementById("errorWrapper");
errorWrapper.removeAttribute("hidden");
var errorMessage = document.getElementById("errorMessage");
errorMessage.textContent = message;
var closeButton = document.getElementById("errorClose");
closeButton.onclick = function () {
errorWrapper.setAttribute("hidden", "true");
};
var errorMoreInfo = document.getElementById("errorMoreInfo");
var moreInfoButton = document.getElementById("errorShowMore");
var lessInfoButton = document.getElementById("errorShowLess");
moreInfoButton.onclick = function () {
errorMoreInfo.removeAttribute("hidden");
moreInfoButton.setAttribute("hidden", "true");
lessInfoButton.removeAttribute("hidden");
errorMoreInfo.style.height = errorMoreInfo.scrollHeight + "px";
};
lessInfoButton.onclick = function () {
errorMoreInfo.setAttribute("hidden", "true");
moreInfoButton.removeAttribute("hidden");
lessInfoButton.setAttribute("hidden", "true");
};
moreInfoButton.removeAttribute("hidden");
lessInfoButton.setAttribute("hidden", "true");
Promise.all(moreInfoText).then(function (parts) {
errorMoreInfo.value = parts.join("\n");
});
}, },
progress: function pdfViewProgress(level) { progress: function pdfViewProgress(level) {
const percent = Math.round(level * 100); var percent = Math.round(level * 100);
// Updating the bar if value increases. // Updating the bar if value increases.
if (percent > this.loadingBar.percent || isNaN(percent)) { if (percent > this.loadingBar.percent || isNaN(percent)) {
this.loadingBar.percent = percent; this.loadingBar.percent = percent;
@ -244,7 +321,7 @@ const PDFViewerApplication = {
}, },
zoomIn: function pdfViewZoomIn(ticks) { zoomIn: function pdfViewZoomIn(ticks) {
let newScale = this.pdfViewer.currentScale; var newScale = this.pdfViewer.currentScale;
do { do {
newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2); newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.ceil(newScale * 10) / 10; newScale = Math.ceil(newScale * 10) / 10;
@ -254,7 +331,7 @@ const PDFViewerApplication = {
}, },
zoomOut: function pdfViewZoomOut(ticks) { zoomOut: function pdfViewZoomOut(ticks) {
let newScale = this.pdfViewer.currentScale; var newScale = this.pdfViewer.currentScale;
do { do {
newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2); newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.floor(newScale * 10) / 10; newScale = Math.floor(newScale * 10) / 10;
@ -264,31 +341,31 @@ const PDFViewerApplication = {
}, },
initUI: function pdfViewInitUI() { initUI: function pdfViewInitUI() {
const eventBus = new pdfjsViewer.EventBus(); var eventBus = new pdfjsViewer.EventBus();
this.eventBus = eventBus; this.eventBus = eventBus;
const linkService = new pdfjsViewer.PDFLinkService({ var linkService = new pdfjsViewer.PDFLinkService({
eventBus, eventBus: eventBus,
}); });
this.pdfLinkService = linkService; this.pdfLinkService = linkService;
this.l10n = new pdfjsViewer.GenericL10n(); this.l10n = pdfjsViewer.NullL10n;
const container = document.getElementById("viewerContainer"); var container = document.getElementById("viewerContainer");
const pdfViewer = new pdfjsViewer.PDFViewer({ var pdfViewer = new pdfjsViewer.PDFViewer({
container, container: container,
eventBus, eventBus: eventBus,
linkService, 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;
linkService.setViewer(pdfViewer); linkService.setViewer(pdfViewer);
this.pdfHistory = new pdfjsViewer.PDFHistory({ this.pdfHistory = new pdfjsViewer.PDFHistory({
eventBus, eventBus: eventBus,
linkService, linkService: linkService,
}); });
linkService.setHistory(this.pdfHistory); linkService.setHistory(this.pdfHistory);
@ -335,8 +412,8 @@ const PDFViewerApplication = {
eventBus.on( eventBus.on(
"pagechanging", "pagechanging",
function (evt) { function (evt) {
const page = evt.pageNumber; var page = evt.pageNumber;
const numPages = PDFViewerApplication.pagesCount; var numPages = PDFViewerApplication.pagesCount;
document.getElementById("pageNumber").value = page; document.getElementById("pageNumber").value = page;
document.getElementById("previous").disabled = page <= 1; document.getElementById("previous").disabled = page <= 1;
@ -347,8 +424,6 @@ const PDFViewerApplication = {
}, },
}; };
window.PDFViewerApplication = PDFViewerApplication;
document.addEventListener( document.addEventListener(
"DOMContentLoaded", "DOMContentLoaded",
function () { function () {
@ -357,14 +432,18 @@ document.addEventListener(
true true
); );
// The offsetParent is not set until the PDF.js iframe or object is visible; (function animationStartedClosure() {
// waiting for first animation. // The offsetParent is not set until the PDF.js iframe or object is visible.
const animationStarted = new Promise(function (resolve) { // Waiting for first animation.
window.requestAnimationFrame(resolve); PDFViewerApplication.animationStartedPromise = new Promise(function (
}); resolve
) {
window.requestAnimationFrame(resolve);
});
})();
// We need to delay opening until all HTML is loaded. // We need to delay opening until all HTML is loaded.
animationStarted.then(function () { PDFViewerApplication.animationStartedPromise.then(function () {
PDFViewerApplication.open({ PDFViewerApplication.open({
url: DEFAULT_URL, url: DEFAULT_URL,
}); });

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

@ -0,0 +1,278 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function xmlEncode(s) {
var 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;
}
var 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: function (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) {
var suffix = ":" + name;
for (var fullName in this.attributes) {
if (fullName.slice(-suffix.length) === suffix) {
return this.attributes[fullName];
}
}
}
return null;
},
setAttribute: function DOMElement_setAttribute(name, value) {
value = value || "";
value = xmlEncode(value);
this.attributes[name] = value;
},
setAttributeNS: function DOMElement_setAttributeNS(NS, name, value) {
this.setAttribute(name, value);
},
appendChild: function DOMElement_appendChild(element) {
var childNodes = this.childNodes;
if (!childNodes.includes(element)) {
childNodes.push(element);
}
},
hasChildNodes: function DOMElement_hasChildNodes() {
return this.childNodes.length !== 0;
},
cloneNode: function DOMElement_cloneNode() {
var 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() {
var buf = [];
var serializer = this.getSerializer();
var 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() {
var 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) {
var 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).
var value;
while (true) {
value = this._childSerializer && this._childSerializer.getNext();
if (value !== null) {
return value;
}
var 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: function (NS, element) {
var elObject = new DOMElement(element);
return elObject;
},
createElement: function (element) {
return this.createElementNS("", element);
},
getElementsByTagName: function (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;
var 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

@ -3,26 +3,27 @@
// //
// Basic node example that prints document metadata and text content. // Basic node example that prints document metadata and text content.
// Requires single file built version of PDF.js -- please run
// `gulp singlefile` before running the example.
// //
// 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"; var pdfjsLib = require("pdfjs-dist/es5/build/pdf.js");
// Loading file from file system into typed array // Loading file from file system into typed array
const pdfPath = var pdfPath = process.argv[2] || "../../web/compressed.tracemonkey-pldi-09.pdf";
process.argv[2] || "../../web/compressed.tracemonkey-pldi-09.pdf";
// 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); var loadingTask = pdfjsLib.getDocument(pdfPath);
loadingTask.promise loadingTask.promise
.then(function (doc) { .then(function (doc) {
const numPages = doc.numPages; var numPages = doc.numPages;
console.log("# Document Loaded"); console.log("# Document Loaded");
console.log("Number of Pages: " + numPages); console.log("Number of Pages: " + numPages);
console.log(); console.log();
let lastPromise; // will be used to chain promises var lastPromise; // will be used to chain promises
lastPromise = doc.getMetadata().then(function (data) { lastPromise = doc.getMetadata().then(function (data) {
console.log("# Metadata Is Loaded"); console.log("# Metadata Is Loaded");
console.log("## Info"); console.log("## Info");
@ -35,10 +36,10 @@ loadingTask.promise
} }
}); });
const loadPage = function (pageNum) { var loadPage = function (pageNum) {
return doc.getPage(pageNum).then(function (page) { return doc.getPage(pageNum).then(function (page) {
console.log("# Page " + pageNum); console.log("# Page " + pageNum);
const viewport = page.getViewport({ scale: 1.0 }); var viewport = page.getViewport({ scale: 1.0 });
console.log("Size: " + viewport.width + "x" + viewport.height); console.log("Size: " + viewport.width + "x" + viewport.height);
console.log(); console.log();
return page return page
@ -46,13 +47,11 @@ loadingTask.promise
.then(function (content) { .then(function (content) {
// Content contains lots of information about the text layout and // Content contains lots of information about the text layout and
// styles, but we need only strings at the moment // styles, but we need only strings at the moment
const strings = content.items.map(function (item) { var strings = content.items.map(function (item) {
return item.str; return item.str;
}); });
console.log("## Text Content"); console.log("## Text Content");
console.log(strings.join(" ")); console.log(strings.join(" "));
// Release page resources.
page.cleanup();
}) })
.then(function () { .then(function () {
console.log(); console.log();
@ -61,7 +60,7 @@ loadingTask.promise
}; };
// Loading of the first page will wait on metadata and subsequent loadings // Loading of the first page will wait on metadata and subsequent loadings
// will wait on the previous pages. // will wait on the previous pages.
for (let i = 1; i <= numPages; i++) { for (var i = 1; i <= numPages; i++) {
lastPromise = lastPromise.then(loadPage.bind(null, i)); lastPromise = lastPromise.then(loadPage.bind(null, i));
} }
return lastPromise; return lastPromise;

View File

@ -0,0 +1,98 @@
/* 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.
*/
var Canvas = require("canvas");
var assert = require("assert").strict;
var fs = require("fs");
function NodeCanvasFactory() {}
NodeCanvasFactory.prototype = {
create: function NodeCanvasFactory_create(width, height) {
assert(width > 0 && height > 0, "Invalid canvas size");
var canvas = Canvas.createCanvas(width, height);
var context = canvas.getContext("2d");
return {
canvas: canvas,
context: context,
};
},
reset: function NodeCanvasFactory_reset(canvasAndContext, width, height) {
assert(canvasAndContext.canvas, "Canvas is not specified");
assert(width > 0 && height > 0, "Invalid canvas size");
canvasAndContext.canvas.width = width;
canvasAndContext.canvas.height = height;
},
destroy: function NodeCanvasFactory_destroy(canvasAndContext) {
assert(canvasAndContext.canvas, "Canvas is not specified");
// Zeroing the width and height cause Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
canvasAndContext.canvas.width = 0;
canvasAndContext.canvas.height = 0;
canvasAndContext.canvas = null;
canvasAndContext.context = null;
},
};
var pdfjsLib = require("pdfjs-dist/es5/build/pdf.js");
// Relative path of the PDF file.
var pdfURL = "../../../web/compressed.tracemonkey-pldi-09.pdf";
// Read the PDF file into a typed array so PDF.js can load it.
var rawData = new Uint8Array(fs.readFileSync(pdfURL));
// Load the PDF file.
var loadingTask = pdfjsLib.getDocument(rawData);
loadingTask.promise
.then(function (pdfDocument) {
console.log("# PDF document loaded.");
// Get the first page.
pdfDocument.getPage(1).then(function (page) {
// Render the page on a Node canvas with 100% scale.
var viewport = page.getViewport({ scale: 1.0 });
var canvasFactory = new NodeCanvasFactory();
var canvasAndContext = canvasFactory.create(
viewport.width,
viewport.height
);
var renderContext = {
canvasContext: canvasAndContext.context,
viewport: viewport,
canvasFactory: canvasFactory,
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
// Convert the canvas to an image buffer.
var image = canvasAndContext.canvas.toBuffer();
fs.writeFile("output.png", image, function (error) {
if (error) {
console.error("Error: " + error);
} else {
console.log(
"Finished converting first page of PDF file to a PNG image."
);
}
});
});
});
})
.catch(function (reason) {
console.log(reason);
});

View File

@ -1,106 +0,0 @@
/* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { strict as assert } from "assert";
import Canvas from "canvas";
import fs from "fs";
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
class NodeCanvasFactory {
create(width, height) {
assert(width > 0 && height > 0, "Invalid canvas size");
const canvas = Canvas.createCanvas(width, height);
const context = canvas.getContext("2d");
return {
canvas,
context,
};
}
reset(canvasAndContext, width, height) {
assert(canvasAndContext.canvas, "Canvas is not specified");
assert(width > 0 && height > 0, "Invalid canvas size");
canvasAndContext.canvas.width = width;
canvasAndContext.canvas.height = height;
}
destroy(canvasAndContext) {
assert(canvasAndContext.canvas, "Canvas is not specified");
// Zeroing the width and height cause Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
canvasAndContext.canvas.width = 0;
canvasAndContext.canvas.height = 0;
canvasAndContext.canvas = null;
canvasAndContext.context = null;
}
}
// Some PDFs need external cmaps.
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
// Where the standard fonts are located.
const STANDARD_FONT_DATA_URL =
"../../../node_modules/pdfjs-dist/standard_fonts/";
const canvasFactory = new NodeCanvasFactory();
// 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));
// Load the PDF file.
const loadingTask = getDocument({
data,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL,
canvasFactory,
});
try {
const pdfDocument = await loadingTask.promise;
console.log("# PDF document loaded.");
// Get the first page.
const page = await pdfDocument.getPage(1);
// Render the page on a Node canvas with 100% scale.
const viewport = page.getViewport({ scale: 1.0 });
const canvasAndContext = canvasFactory.create(
viewport.width,
viewport.height
);
const renderContext = {
canvasContext: canvasAndContext.context,
viewport,
};
const renderTask = page.render(renderContext);
await renderTask.promise;
// Convert the canvas to an image buffer.
const image = canvasAndContext.canvas.toBuffer();
fs.writeFile("output.png", image, function (error) {
if (error) {
console.error("Error: " + error);
} else {
console.log("Finished converting first page of PDF file to a PNG image.");
}
});
// Release page resources.
page.cleanup();
} catch (reason) {
console.log(reason);
}

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

@ -0,0 +1,135 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
//
// Node tool to dump SVG output into a file.
//
var fs = require("fs");
var util = require("util");
var path = require("path");
var 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.
var pdfjsLib = require("pdfjs-dist/es5/build/pdf.js");
// Loading file from file system into typed array
var pdfPath = process.argv[2] || "../../web/compressed.tracemonkey-pldi-09.pdf";
var data = new Uint8Array(fs.readFileSync(pdfPath));
var 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) {
var 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 () {
var 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) {
var readableSvgStream = new ReadableSVGStream({
svgElement: svgElement,
});
var 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 promises to load document, pages and misc data instead of
// callback.
var loadingTask = pdfjsLib.getDocument({
data: data,
fontExtraProperties: true,
});
loadingTask.promise
.then(function (doc) {
var numPages = doc.numPages;
console.log("# Document Loaded");
console.log("Number of Pages: " + numPages);
console.log();
var lastPromise = Promise.resolve(); // will be used to chain promises
var loadPage = function (pageNum) {
return doc.getPage(pageNum).then(function (page) {
console.log("# Page " + pageNum);
var viewport = page.getViewport({ scale: 1.0 });
console.log("Size: " + viewport.width + "x" + viewport.height);
console.log();
return page.getOperatorList().then(function (opList) {
var svgGfx = new pdfjsLib.SVGGraphics(page.commonObjs, page.objs);
svgGfx.embedFonts = true;
return svgGfx.getSVG(opList, viewport).then(function (svg) {
return writeSvgToFile(svg, getFilePathForPage(pageNum)).then(
function () {
console.log("Page: " + pageNum);
},
function (err) {
console.log("Error: " + err);
}
);
});
});
});
};
for (var i = 1; i <= numPages; i++) {
lastPromise = lastPromise.then(loadPage.bind(null, i));
}
return lastPromise;
})
.then(
function () {
console.log("# End of Document");
},
function (err) {
console.error("Error: " + err);
}
);

View File

@ -0,0 +1,8 @@
## PDF.js using SVG
This is a project for implementing alternate backend for PDF.js using Scalable Vector Graphics. This is still a WIP.
Take a look at [proposal](https://docs.google.com/document/d/1k4nPx1RrHbxXi94kSdvW5ay8KMkjwLmBEiCNupyzlwk/pub) for this project.
## Getting started
Take a look at src/display/svg.js to see the SVG rendering code.

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!--
Copyright 2014 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.
-->
<html dir="ltr" mozdisallowselectionprint>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<title>PDF.js SVG viewer using built components</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
#viewerContainer {
overflow: auto;
position: absolute;
width: 100%;
height: 100%;
}
</style>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
</head>
<body tabindex="1">
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<script src="viewer.js"></script>
</body>
</html>

View File

@ -0,0 +1,69 @@
/* Copyright 2014 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 (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
}
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
// Some PDFs need external cmaps.
//
var CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
var CMAP_PACKED = true;
var DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
var container = document.getElementById("viewerContainer");
var eventBus = new pdfjsViewer.EventBus();
// (Optionally) enable hyperlinks within PDF files.
var pdfLinkService = new pdfjsViewer.PDFLinkService({
eventBus: eventBus,
});
var pdfViewer = new pdfjsViewer.PDFViewer({
container: container,
eventBus: eventBus,
linkService: pdfLinkService,
renderer: "svg",
textLayerMode: 0,
});
pdfLinkService.setViewer(pdfViewer);
eventBus.on("pagesinit", function () {
// We can use pdfViewer now, e.g. let's change default scale.
pdfViewer.currentScaleValue = "page-width";
});
// Loading document.
var loadingTask = pdfjsLib.getDocument({
url: DEFAULT_URL,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
});
loadingTask.promise.then(function (pdfDocument) {
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfViewer.setDocument(pdfDocument);
pdfLinkService.setDocument(pdfDocument, null);
});

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

@ -13,17 +13,17 @@
* limitations under the License. * limitations under the License.
*/ */
const PDF_PATH = "../../web/compressed.tracemonkey-pldi-09.pdf"; var PDF_PATH = "../../web/compressed.tracemonkey-pldi-09.pdf";
const PAGE_NUMBER = 1; var PAGE_NUMBER = 1;
const PAGE_SCALE = 1.5; var PAGE_SCALE = 1.5;
const SVG_NS = "http://www.w3.org/2000/svg"; var 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)
const svg = document.createElementNS(SVG_NS, "svg:svg"); var svg = document.createElementNS(SVG_NS, "svg:svg");
svg.setAttribute("width", viewport.width + "px"); svg.setAttribute("width", viewport.width + "px");
svg.setAttribute("height", viewport.height + "px"); svg.setAttribute("height", viewport.height + "px");
// items are transformed to have 1px font size // items are transformed to have 1px font size
@ -33,39 +33,42 @@ function buildSVG(viewport, textContent) {
textContent.items.forEach(function (textItem) { textContent.items.forEach(function (textItem) {
// we have to take in account viewport transform, which includes scale, // we have to take in account viewport transform, which includes scale,
// rotation and Y-axis flip, and not forgetting to flip text. // rotation and Y-axis flip, and not forgetting to flip text.
const tx = pdfjsLib.Util.transform( var tx = pdfjsLib.Util.transform(
pdfjsLib.Util.transform(viewport.transform, textItem.transform), pdfjsLib.Util.transform(viewport.transform, textItem.transform),
[1, 0, 0, -1, 0, 0] [1, 0, 0, -1, 0, 0]
); );
const style = textContent.styles[textItem.fontName]; var style = textContent.styles[textItem.fontName];
// adding text element // adding text element
const text = document.createElementNS(SVG_NS, "svg:text"); var text = document.createElementNS(SVG_NS, "svg:text");
text.setAttribute("transform", "matrix(" + tx.join(" ") + ")"); text.setAttribute("transform", "matrix(" + tx.join(" ") + ")");
text.setAttribute("font-family", style.fontFamily); text.setAttribute("font-family", style.fontFamily);
text.textContent = textItem.str; text.textContent = textItem.str;
svg.append(text); svg.appendChild(text);
}); });
return svg; return svg;
} }
async function pageLoaded() { function pageLoaded() {
// Loading document and page text content // Loading document and page text content
const loadingTask = pdfjsLib.getDocument({ url: PDF_PATH }); var loadingTask = pdfjsLib.getDocument({ url: PDF_PATH });
const pdfDocument = await loadingTask.promise; loadingTask.promise.then(function (pdfDocument) {
const page = await pdfDocument.getPage(PAGE_NUMBER); pdfDocument.getPage(PAGE_NUMBER).then(function (page) {
const viewport = page.getViewport({ scale: PAGE_SCALE }); var viewport = page.getViewport({ scale: PAGE_SCALE });
const textContent = await page.getTextContent(); page.getTextContent().then(function (textContent) {
// building SVG and adding that to the DOM // building SVG and adding that to the DOM
const svg = buildSVG(viewport, textContent); var svg = buildSVG(viewport, textContent);
document.getElementById("pageContainer").append(svg); document.getElementById("pageContainer").appendChild(svg);
// Release page resources. });
page.cleanup(); });
});
} }
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
if (typeof pdfjsLib === "undefined") { if (typeof pdfjsLib === "undefined") {
// eslint-disable-next-line no-alert alert(
alert("Please build the pdfjs-dist library using\n `gulp dist-install`"); "Built version of PDF.js was not found.\n" +
"Please run `gulp dist-install`."
);
return; return;
} }
pageLoaded(); pageLoaded();

View File

@ -18,16 +18,10 @@ Refer to the `main.js` and `webpack.config.js` files for the source code.
Note that PDF.js packaging requires packaging of the main application and Note that PDF.js packaging requires packaging of the main application and
the worker code, and the `workerSrc` path shall be set to the latter file. the worker code, and the `workerSrc` path shall be set to the latter file.
### Minification
If you are configuring Webpack to output a minified build, please note that you
*must* configure the minifier to keep original class/function names intact;
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` which is the zero-configuration method for Webpack users:
importing `pdfjs-dist/webpack.mjs` which is the zero-configuration method for
Webpack users. Installing `worker-loader` is no longer necessary.
import * as pdfjsLib from 'pdfjs-dist/webpack.mjs'; import 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.
var pdfjsLib = require("pdfjs-dist");
var pdfPath = "../learning/helloworld.pdf";
// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../build/webpack/pdf.worker.bundle.js";
// Loading a document.
var 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.
var viewport = pdfPage.getViewport({ scale: 1.0 });
var canvas = document.getElementById("theCanvas");
canvas.width = viewport.width;
canvas.height = viewport.height;
var ctx = canvas.getContext("2d");
var renderTask = pdfPage.render({
canvasContext: ctx,
viewport: 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": "4.21.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^3.1.2",
"pdfjs-dist": "../../node_modules/pdfjs-dist" "pdfjs-dist": "../../node_modules/pdfjs-dist"
} }
} }

View File

@ -1,13 +1,11 @@
/* eslint-disable import/no-commonjs */ var webpack = require("webpack"); // eslint-disable-line no-unused-vars
var path = require("path");
const webpack = require("webpack"); // eslint-disable-line no-unused-vars
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: {

9
extensions/.eslintrc Normal file
View File

@ -0,0 +1,9 @@
{
"extends": [
../.eslintrc
],
"rules": {
"no-restricted-globals": "off",
},
}

View File

@ -17,6 +17,6 @@
"rules": { "rules": {
"mozilla/import-globals": "error", "mozilla/import-globals": "error",
"no-var": "off", "object-shorthand": "off",
}, },
} }

View File

@ -22,7 +22,11 @@ function getViewerURL(pdf_url) {
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url); return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url);
} }
document.addEventListener("animationstart", onAnimationStart, true); if (CSS.supports("animation", "0s")) {
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") {
@ -124,15 +128,11 @@ function updateEmbedElement(elem) {
var parentNode = elem.parentNode; var parentNode = elem.parentNode;
var nextSibling = elem.nextSibling; var nextSibling = elem.nextSibling;
if (parentNode) { if (parentNode) {
elem.remove(); parentNode.removeChild(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) because nextSibling may be null.
// eslint-disable-next-line unicorn/prefer-modern-dom-apis
parentNode.insertBefore(elem, nextSibling); parentNode.insertBefore(elem, nextSibling);
} }
} }
@ -150,7 +150,7 @@ function updateObjectElement(elem) {
// attribute reloads the content (provided that the type was correctly set). // attribute reloads the content (provided that the type was correctly set).
// - When <object type=text/html data="chrome-extension://..."> is used // - When <object type=text/html data="chrome-extension://..."> is used
// (tested with a data-URL, data:text/html,<object...>, the extension's // (tested with a data-URL, data:text/html,<object...>, the extension's
// origin allowlist is not set up, so the viewer can't load the PDF file. // origin whitelist is not set up, so the viewer can't load the PDF file.
// - The content of the <object> tag may be affected by <param> tags. // - The content of the <object> tag may be affected by <param> tags.
// //
// To make sure that our solution works for all cases, we will insert a frame // To make sure that our solution works for all cases, we will insert a frame
@ -160,7 +160,7 @@ function updateObjectElement(elem) {
if (!iframe || !iframe.__inserted_by_pdfjs) { if (!iframe || !iframe.__inserted_by_pdfjs) {
iframe = createFullSizeIframe(); iframe = createFullSizeIframe();
elem.textContent = ""; elem.textContent = "";
elem.append(iframe); elem.appendChild(iframe);
iframe.__inserted_by_pdfjs = true; iframe.__inserted_by_pdfjs = true;
} }
iframe.src = getEmbeddedViewerURL(elem.data); iframe.src = getEmbeddedViewerURL(elem.data);

View File

@ -1,13 +1,12 @@
/** /**
* Detect creation of <embed> and <object> tags. * Detect creation of <embed> and <object> tags.
*/ */
@keyframes pdfjs-detected-object-or-embed { @-webkit-keyframes pdfjs-detected-object-or-embed { from {} }
from { @keyframes pdfjs-detected-object-or-embed { from {} }
/* empty */ object, embed {
} -webkit-animation-delay: 0s !important;
} -webkit-animation-name: pdfjs-detected-object-or-embed !important;
object, -webkit-animation-play-state: running !important;
embed {
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

@ -40,14 +40,14 @@ limitations under the License.
*/ */
function parseExtensionURL(url) { function parseExtensionURL(url) {
url = url.substring(CRX_BASE_URL.length); url = url.substring(CRX_BASE_URL.length);
// Find the (url-encoded) colon and verify that the scheme is allowed. // Find the (url-encoded) colon and verify that the scheme is whitelisted.
var schemeIndex = url.search(/:|%3A/i); var schemeIndex = url.search(/:|%3A/i);
if (schemeIndex === -1) { if (schemeIndex === -1) {
return undefined; return undefined;
} }
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);
} }
@ -95,8 +95,8 @@ limitations under the License.
url: CRX_BASE_URL + "*:*", url: CRX_BASE_URL + "*:*",
}, },
function (tabsFromLastSession) { function (tabsFromLastSession) {
for (const { id } of tabsFromLastSession) { for (var i = 0; i < tabsFromLastSession.length; ++i) {
chrome.tabs.reload(id); chrome.tabs.reload(tabsFromLastSession[i].id);
} }
} }
); );

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://*/*",
"run_at": "document_start", "https://*/*",
"all_frames": true, "ftp://*/*",
"css": ["contentstyle.css"], "file://*/*"
"js": ["contentscript.js"] ],
} "run_at": "document_start",
], "all_frames": true,
"css": ["contentstyle.css"],
"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": [
"file_filters": ["filesystem:*.pdf"] "filesystem:*.pdf"
} ]
], }],
"storage": { "storage": {
"managed_schema": "preferences_schema.json" "managed_schema": "preferences_schema.json"
}, },

View File

@ -109,10 +109,17 @@ limitations under the License.
} }
// Migration code for https://github.com/mozilla/pdf.js/pull/9479. // Migration code for https://github.com/mozilla/pdf.js/pull/9479.
if (typeof items.disableTextLayer === "boolean") { if (typeof items.disableTextLayer === "boolean") {
var textLayerMode = 1;
if (items.disableTextLayer) { if (items.disableTextLayer) {
textLayerMode = 0;
} else if (items.enhanceTextSelection) {
textLayerMode = 2;
}
if (textLayerMode !== 1) {
// Overwrite if computed textLayerMode is not the default value (1).
storageSync.set( storageSync.set(
{ {
textLayerMode: 0, textLayerMode: textLayerMode,
}, },
function () { function () {
if (!chrome.runtime.lastError) { if (!chrome.runtime.lastError) {

View File

@ -43,19 +43,6 @@ body {
</div> </div>
</template> </template>
<template id="viewerCssTheme-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Use system theme</option>
<option value="1">Light theme</option>
<option value="2">Dark theme</option>
</select>
</label>
</div>
</template>
<template id="viewOnLoad-template"> <template id="viewOnLoad-template">
<div class="settings-row"> <div class="settings-row">
<label> <label>
@ -126,6 +113,7 @@ body {
<select> <select>
<option value="0">Disable text selection</option> <option value="0">Disable text selection</option>
<option value="1">Enable text selection</option> <option value="1">Enable text selection</option>
<option value="2">Enable enhanced mode (experimental)</option>
</select> </select>
</label> </label>
</div> </div>
@ -152,7 +140,6 @@ body {
<span></span> <span></span>
<select> <select>
<option value="-1">Default</option> <option value="-1">Default</option>
<option value="3">Page scrolling</option>
<option value="0">Vertical scrolling</option> <option value="0">Vertical scrolling</option>
<option value="1">Horizontal scrolling</option> <option value="1">Horizontal scrolling</option>
<option value="2">Wrapped scrolling</option> <option value="2">Wrapped scrolling</option>

View File

@ -155,7 +155,7 @@ function renderBooleanPref(shortDescription, description, prefName) {
storageArea.set(pref); storageArea.set(pref);
}; };
wrapper.querySelector("span").textContent = shortDescription; wrapper.querySelector("span").textContent = shortDescription;
document.getElementById("settings-boxes").append(wrapper); document.getElementById("settings-boxes").appendChild(wrapper);
function renderPreference(value) { function renderPreference(value) {
checkbox.checked = value; checkbox.checked = value;
@ -172,7 +172,7 @@ function renderEnumPref(shortDescription, prefName) {
storageArea.set(pref); storageArea.set(pref);
}; };
wrapper.querySelector("span").textContent = shortDescription; wrapper.querySelector("span").textContent = shortDescription;
document.getElementById("settings-boxes").append(wrapper); document.getElementById("settings-boxes").appendChild(wrapper);
function renderPreference(value) { function renderPreference(value) {
select.value = value; select.value = value;
@ -189,7 +189,7 @@ function renderDefaultZoomValue(shortDescription) {
}); });
}; };
wrapper.querySelector("span").textContent = shortDescription; wrapper.querySelector("span").textContent = shortDescription;
document.getElementById("settings-boxes").append(wrapper); document.getElementById("settings-boxes").appendChild(wrapper);
function renderPreference(value) { function renderPreference(value) {
value = value || "auto"; value = value || "auto";

View File

@ -28,7 +28,7 @@ limitations under the License.
if (url) { if (url) {
url = url[1]; url = url[1];
chrome.pageAction.setPopup({ chrome.pageAction.setPopup({
tabId, tabId: tabId,
popup: "/pageAction/popup.html?file=" + encodeURIComponent(url), popup: "/pageAction/popup.html?file=" + encodeURIComponent(url),
}); });
chrome.pageAction.show(tabId); chrome.pageAction.show(tabId);

View File

@ -78,9 +78,9 @@ limitations under the License.
if (windowId) { if (windowId) {
chrome.tabs.create( chrome.tabs.create(
{ {
windowId, windowId: windowId,
active: true, active: true,
url, url: url,
}, },
function () { function () {
openViewer(windowId, fileEntries); openViewer(windowId, fileEntries);
@ -91,7 +91,7 @@ limitations under the License.
{ {
type: "normal", type: "normal",
focused: true, focused: true,
url, url: url,
}, },
function (chromeWindow) { function (chromeWindow) {
openViewer(chromeWindow.id, fileEntries); openViewer(chromeWindow.id, fileEntries);

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;
} }
/** /**
@ -62,7 +54,8 @@ function isPdfDownloadable(details) {
* @returns {undefined|{name: string, value: string}} The header, if found. * @returns {undefined|{name: string, value: string}} The header, if found.
*/ */
function getHeaderFromHeaders(headers, headerName) { function getHeaderFromHeaders(headers, headerName) {
for (const header of headers) { for (var i = 0; i < headers.length; ++i) {
var header = headers[i];
if (header.name.toLowerCase() === headerName) { if (header.name.toLowerCase() === headerName) {
return header; return header;
} }
@ -162,7 +155,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"]
@ -237,14 +242,21 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
chrome.tabs.create({ chrome.tabs.create({
windowId: sender.tab.windowId, windowId: sender.tab.windowId,
index: sender.tab.index + 1, index: sender.tab.index + 1,
url, url: url,
openerTabId: sender.tab.id, openerTabId: sender.tab.id,
}); });
} else { } else {
chrome.tabs.update(sender.tab.id, { chrome.tabs.update(sender.tab.id, {
url, url: url,
}); });
} }
} }
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

@ -1,13 +1,6 @@
{ {
"type": "object", "type": "object",
"properties": { "properties": {
"viewerCssTheme": {
"title": "Theme",
"description": "The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.",
"type": "integer",
"enum": [0, 1, 2],
"default": 2
},
"showPreviousViewOnLoad": { "showPreviousViewOnLoad": {
"description": "DEPRECATED. Set viewOnLoad to 1 to disable showing the last page/position on load.", "description": "DEPRECATED. Set viewOnLoad to 1 to disable showing the last page/position on load.",
"type": "boolean", "type": "boolean",
@ -17,15 +10,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 +28,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,45 +42,28 @@
"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
}, },
"enableWebGL": {
"title": "Enable WebGL",
"description": "Whether to enable WebGL.",
"type": "boolean",
"default": false
},
"pdfBugEnabled": { "pdfBugEnabled": {
"title": "Enable debugging tools", "title": "Enable debugging tools",
"description": "Whether to enable debugging tools.", "description": "Whether to enable debugging tools.",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enableScripting": {
"title": "Enable active content (JavaScript) in PDFs",
"type": "boolean",
"description": "Whether to allow execution of active content (JavaScript) by PDF files.",
"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).",
@ -111,18 +91,37 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enhanceTextSelection": {
"description": "DEPRECATED. Set textLayerMode to 2 to use the enhanced text selection layer by default.",
"type": "boolean",
"default": false
},
"textLayerMode": { "textLayerMode": {
"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.\n 2 = (Experimental) Enabled, with enhanced text selection.",
"type": "integer", "type": "integer",
"enum": [0, 1], "enum": [
0,
1,
2
],
"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": {
@ -140,24 +139,22 @@
"description": "Whether to prevent the extension from reporting the extension and browser version to the extension developers.", "description": "Whether to prevent the extension from reporting the extension and browser version to the extension developers.",
"default": false "default": false
}, },
"annotationMode": { "renderer": {
"type": "integer", "type": "string",
"enum": [0, 1, 2, 3], "enum": [
"default": 2 "canvas",
"svg"
],
"default": "canvas"
}, },
"annotationEditorMode": { "renderInteractiveForms": {
"type": "integer", "type": "boolean",
"enum": [-1, 0, 3, 15], "default": false
"default": 0
}, },
"enablePermissions": { "enablePermissions": {
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enableXfa": {
"type": "boolean",
"default": true
},
"historyUpdateUrl": { "historyUpdateUrl": {
"type": "boolean", "type": "boolean",
"default": false "default": false
@ -170,38 +167,33 @@
}, },
"enablePrintAutoRotate": { "enablePrintAutoRotate": {
"title": "Automatically rotate printed pages", "title": "Automatically rotate printed pages",
"description": "When enabled, landscape pages are rotated when printed.", "description": "When enabled, pages whose orientation differ from the first page are rotated when printed.",
"type": "boolean", "type": "boolean",
"default": true "default": false
}, },
"scrollModeOnLoad": { "scrollModeOnLoad": {
"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 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
],
"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": {
"description": "When enabled, the pdf rendering will use the high contrast mode colors",
"type": "boolean",
"default": false
},
"pageColorsBackground": {
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
"type": "string",
"default": "Canvas"
},
"pageColorsForeground": {
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
"type": "string",
"default": "CanvasText"
} }
} }
} }

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"],
}; };
chrome.webRequest.onSendHeaders.addListener( function registerListener(extraInfoSpec) {
function (details) { extraInfoSpecWithHeaders = extraInfoSpec;
g_requestHeaders[details.requestId] = details.requestHeaders; // May throw if the given extraInfoSpec is unsupported.
}, chrome.webRequest.onSendHeaders.addListener(
requestFilter, function (details) {
["requestHeaders", "extraHeaders"] g_requestHeaders[details.requestId] = details.requestHeaders;
); },
requestFilter,
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);
@ -112,9 +123,9 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
{ {
urls: [data.requestUrl], urls: [data.requestUrl],
types: ["xmlhttprequest"], types: ["xmlhttprequest"],
tabId, 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.
@ -137,7 +148,7 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
{ {
urls: ["https://*/*"], urls: ["https://*/*"],
types: ["xmlhttprequest"], types: ["xmlhttprequest"],
tabId, tabId: tabId,
}, },
["blocking", "responseHeaders"] ["blocking", "responseHeaders"]
); );

View File

@ -70,20 +70,29 @@ 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;
fetch(LOG_URL, { if (window.Request && "mode" in Request.prototype) {
method: "POST", // fetch is supported in extensions since Chrome 42 (though the above
headers: new Headers({ // feature-detection method detects Chrome 43+).
"Deduplication-Id": deduplication_id, // Unlike XMLHttpRequest, fetch omits credentials such as cookies in the
"Extension-Version": extension_version, // requests, which guarantees that the server cannot track the client
}), // via HTTP cookies.
// Set mode=cors so that the above custom headers are included in the fetch(LOG_URL, {
// request. method: "POST",
mode: "cors", headers: new Headers({
// Omits credentials such as cookies in the requests, which guarantees "Deduplication-Id": deduplication_id,
// that the server cannot track the client via HTTP cookies. "Extension-Version": extension_version,
credentials: "omit", }),
cache: "no-store", // Set mode=cors so that the above custom headers are included in the
}); // request.
mode: "cors",
});
return;
}
var x = new XMLHttpRequest();
x.open("POST", LOG_URL);
x.setRequestHeader("Deduplication-Id", deduplication_id);
x.setRequestHeader("Extension-Version", extension_version);
x.send();
}); });
} }
@ -101,7 +110,8 @@ limitations under the License.
id = ""; id = "";
var buf = new Uint8Array(5); var buf = new Uint8Array(5);
crypto.getRandomValues(buf); crypto.getRandomValues(buf);
for (const c of buf) { for (var i = 0; i < buf.length; ++i) {
var c = buf[i];
id += (c >>> 4).toString(16) + (c & 0xf).toString(16); id += (c >>> 4).toString(16) + (c & 0xf).toString(16);
} }
localStorage.telemetryDeduplicationId = id; localStorage.telemetryDeduplicationId = id;

View File

@ -6,11 +6,17 @@
"plugin:mozilla/recommended", "plugin:mozilla/recommended",
], ],
"parserOptions": {
"sourceType": "script",
},
"plugins": [ "plugins": [
"mozilla" "mozilla"
], ],
"rules": { "rules": {
// Items different from the mozilla/recommended configuration.
// 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

@ -0,0 +1,5 @@
This is the PDF.js project output, https://github.com/mozilla/pdf.js
Current extension version is: PDFJSSCRIPT_VERSION
Taken from upstream commit: PDFJSSCRIPT_COMMIT

View File

@ -1,4 +1,4 @@
/* Copyright 2021 Mozilla Foundation /* Copyright 2018 Mozilla Foundation
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,8 +13,10 @@
* limitations under the License. * limitations under the License.
*/ */
.xfaLink { "use strict";
opacity: 0.2;
background: rgb(255 255 0); var EXPORTED_SYMBOLS = ["PdfJsDefaultPreferences"];
box-shadow: 0 2px 10px rgb(255 255 0);
} var PdfJsDefaultPreferences = Object.freeze(
PDFJSDev.json("$ROOT/build/default_preferences.json")
);

View File

@ -0,0 +1,131 @@
"use strict";
// Small subset of the webL10n API by Fabien Cazenave for PDF.js extension.
(function (window) {
var gLanguage = "";
var gExternalLocalizerServices = null;
var gReadyState = "loading";
// fetch an l10n objects
function getL10nData(key) {
var response = gExternalLocalizerServices.getStrings(key);
var data = JSON.parse(response);
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);

6
external/.eslintrc vendored
View File

@ -6,4 +6,10 @@
"env": { "env": {
"node": true, "node": true,
}, },
"rules": {
"mozilla/use-includes-instead-of-indexOf": "off",
"object-shorthand": "off",
"no-restricted-globals": "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,8 +1,8 @@
import fs from "fs"; "use strict";
import path from "path";
import vm from "vm";
const AllWhitespaceRegexp = /^\s+$/g; var fs = require("fs"),
path = require("path"),
vm = require("vm");
/** /**
* A simple preprocessor that is based on the Firefox preprocessor * A simple preprocessor that is based on the Firefox preprocessor
@ -33,55 +33,22 @@ const AllWhitespaceRegexp = /^\s+$/g;
* //#endif * //#endif
*/ */
function preprocess(inFilename, outFilename, defines) { function preprocess(inFilename, outFilename, defines) {
let lineNumber = 0;
function loc() {
return fs.realpathSync(inFilename) + ":" + lineNumber;
}
function expandCssImports(content, baseUrl) {
return content.replaceAll(
/^\s*@import\s+url\(([^)]+)\);\s*$/gm,
function (all, url) {
if (defines.GECKOVIEW) {
switch (url) {
case "annotation_editor_layer_builder.css":
return "";
}
}
const file = path.join(path.dirname(baseUrl), url);
const imported = fs.readFileSync(file, "utf8").toString();
return expandCssImports(imported, file);
}
);
}
// TODO make this really read line by line. // TODO make this really read line by line.
let content = fs.readFileSync(inFilename, "utf8").toString(); var lines = fs.readFileSync(inFilename).toString().split("\n");
// Handle CSS-imports first, when necessary. var totalLines = lines.length;
if (/\.css$/i.test(inFilename)) { var out = "";
content = expandCssImports(content, inFilename); var i = 0;
}
const lines = content.split("\n"),
totalLines = lines.length;
const out = [];
let i = 0;
function readLine() { function readLine() {
if (i < totalLines) { if (i < totalLines) {
return lines[i++]; return lines[i++];
} }
return null; return null;
} }
const writeLine = var writeLine =
typeof outFilename === "function" typeof outFilename === "function"
? outFilename ? outFilename
: function (line) { : function (line) {
if (!line || AllWhitespaceRegexp.test(line)) { out += line + "\n";
const prevLine = out.at(-1);
if (!prevLine || AllWhitespaceRegexp.test(prevLine)) {
return; // Avoid adding consecutive blank lines.
}
}
out.push(line);
}; };
function evaluateCondition(code) { function evaluateCondition(code) {
if (!code || !code.trim()) { if (!code || !code.trim()) {
@ -103,10 +70,10 @@ function preprocess(inFilename, outFilename, defines) {
} }
} }
function include(file) { function include(file) {
const realPath = fs.realpathSync(inFilename); var realPath = fs.realpathSync(inFilename);
const dir = path.dirname(realPath); var dir = path.dirname(realPath);
try { try {
let fullpath; var fullpath;
if (file.indexOf("$ROOT/") === 0) { if (file.indexOf("$ROOT/") === 0) {
fullpath = path.join( fullpath = path.join(
__dirname, __dirname,
@ -125,7 +92,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];
@ -136,26 +103,28 @@ function preprocess(inFilename, outFilename, defines) {
} }
// not inside if or else (process lines) // not inside if or else (process lines)
const STATE_NONE = 0; var STATE_NONE = 0;
// inside if, condition false (ignore until #else or #endif) // inside if, condition false (ignore until #else or #endif)
const STATE_IF_FALSE = 1; var STATE_IF_FALSE = 1;
// inside else, #if was false, so #else is true (process lines until #endif) // inside else, #if was false, so #else is true (process lines until #endif)
const STATE_ELSE_TRUE = 2; var STATE_ELSE_TRUE = 2;
// inside if, condition true (process lines until #else or #endif) // inside if, condition true (process lines until #else or #endif)
const STATE_IF_TRUE = 3; var STATE_IF_TRUE = 3;
// inside else or elif, #if/#elif was true, so following #else or #elif is // inside else or elif, #if/#elif was true, so following #else or #elif is
// false (ignore lines until #endif) // false (ignore lines until #endif)
const STATE_ELSE_FALSE = 4; var STATE_ELSE_FALSE = 4;
let line;
let state = STATE_NONE;
const stack = [];
const control =
/^(?:\/\/|\s*\/\*|<!--)\s*#(if|elif|else|endif|expand|include|error)\b(?:\s+(.*?)(?:\*\/|-->)?$)?/;
var line;
var state = STATE_NONE;
var stack = [];
var control = /^(?:\/\/|<!--)\s*#(if|elif|else|endif|expand|include|error)\b(?:\s+(.*?)(?:-->)?$)?/;
var lineNumber = 0;
var loc = function () {
return fs.realpathSync(inFilename) + ":" + lineNumber;
};
while ((line = readLine()) !== null) { while ((line = readLine()) !== null) {
++lineNumber; ++lineNumber;
const m = control.exec(line); var m = control.exec(line);
if (m) { if (m) {
switch (m[1]) { switch (m[1]) {
case "if": case "if":
@ -204,19 +173,16 @@ function preprocess(inFilename, outFilename, defines) {
} }
break; break;
} }
} else if (state === STATE_NONE) { } else {
writeLine(line); if (state === STATE_NONE) {
} else if ( writeLine(line);
(state === STATE_IF_TRUE || state === STATE_ELSE_TRUE) && } else if (
!stack.includes(STATE_IF_FALSE) && (state === STATE_IF_TRUE || state === STATE_ELSE_TRUE) &&
!stack.includes(STATE_ELSE_FALSE) !stack.includes(STATE_IF_FALSE) &&
) { !stack.includes(STATE_ELSE_FALSE)
writeLine( ) {
line writeLine(line.replace(/^\/\/|^<!--|-->$/g, " "));
.replaceAll(/^\/\/|^<!--/g, " ") }
.replaceAll(/(^\s*)\/\*/g, "$1 ")
.replaceAll(/\*\/$|-->$/g, "")
);
} }
} }
if (state !== STATE_NONE || stack.length !== 0) { if (state !== STATE_NONE || stack.length !== 0) {
@ -225,8 +191,99 @@ function preprocess(inFilename, outFilename, defines) {
); );
} }
if (typeof outFilename !== "function") { if (typeof outFilename !== "function") {
fs.writeFileSync(outFilename, out.join("\n")); fs.writeFileSync(outFilename, out);
} }
} }
exports.preprocess = preprocess;
export { preprocess }; function preprocessCSS(mode, source, destination) {
function hasPrefixedMozcentral(line) {
return /(^|\W)-(ms|o|webkit)-\w/.test(line);
}
function expandImports(content, baseUrl) {
return content.replace(/^\s*@import\s+url\(([^\)]+)\);\s*$/gm, function (
all,
url
) {
var file = path.join(path.dirname(baseUrl), url);
var imported = fs.readFileSync(file, "utf8").toString();
return expandImports(imported, file);
});
}
function removePrefixed(content, hasPrefixedFilter) {
var lines = content.split(/\r?\n/g);
var i = 0;
while (i < lines.length) {
var line = lines[i];
if (!hasPrefixedFilter(line)) {
i++;
continue;
}
if (/\{\s*$/.test(line)) {
var bracketLevel = 1;
var j = i + 1;
while (j < lines.length && bracketLevel > 0) {
var checkBracket = /([{}])\s*$/.exec(lines[j]);
if (checkBracket) {
if (checkBracket[1] === "{") {
bracketLevel++;
} else if (!lines[j].includes("{")) {
bracketLevel--;
}
}
j++;
}
lines.splice(i, j - i);
} else if (/[};]\s*$/.test(line)) {
lines.splice(i, 1);
} else {
// multiline? skipping until next directive or bracket
do {
lines.splice(i, 1);
} while (
i < lines.length &&
!/\}\s*$/.test(lines[i]) &&
!lines[i].includes(":")
);
if (i < lines.length && /\S\s*}\s*$/.test(lines[i])) {
lines[i] = lines[i].substring(lines[i].indexOf("}"));
}
}
// collapse whitespaces
while (lines[i] === "" && lines[i - 1] === "") {
lines.splice(i, 1);
}
}
return lines.join("\n");
}
if (!mode) {
throw new Error("Invalid CSS preprocessor mode");
}
var content = fs.readFileSync(source, "utf8").toString();
content = expandImports(content, source);
if (mode === "mozcentral") {
content = removePrefixed(content, hasPrefixedMozcentral);
}
fs.writeFileSync(destination, content);
}
exports.preprocessCSS = preprocessCSS;
/**
* Merge two defines arrays. Values in the second param will override values in
* the first.
*/
function merge(defaults, defines) {
var ret = {};
for (var key in defaults) {
ret[key] = defaults[key];
}
for (key in defines) {
ret[key] = defines[key];
}
return ret;
}
exports.merge = merge;

View File

@ -1,5 +0,0 @@
/* Comment here... */
div {
margin: 0;
padding: 0;
}

View File

@ -1,7 +0,0 @@
/* Comment here... */
/*#if TRUE*/
/*div {*/
/* margin: 0;*/
/*padding: 0;*/
/*}*/
/*#endif*/

View File

@ -1,3 +0,0 @@
div {
margin: 0;
}

View File

@ -1,14 +0,0 @@
/*#if TRUE*/
div {
margin: 0;
/*#if FALSE*/
padding: 0;
/*#endif*/
}
/*#endif*/
/*#if FALSE*/
p {
margin: 0;
}
/*#endif*/

View File

@ -1,10 +1,10 @@
function test() { function test() {
"test"; "test";
"1"; "1";
"2"; "2";
"3"; "3";
if ("test") { if ("test") {
"5"; "5";
} }
"4"; "4";
} }

View File

@ -1,17 +1,17 @@
function f1() { function f1() {
"1"; "1";
"2"; "2";
} }
function f2() { function f2() {
"1"; "1";
"2"; "2";
} }
function f3() { function f3() {
if ("1") { if ("1") {
"1"; "1";
} }
"2"; "2";
if ("3") { if ("3") {
"4"; "4";
} }
} }

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,21 +1,13 @@
function f1() {} function f1() {
}
function f2() { function f2() {
return 1; return 1;
} }
function f3() { function f3() {
var i = 0; var i = 0;
throw "test"; throw "test";
} }
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;

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