Compare commits

..

No commits in common. "master" and "v4.1.392" have entirely different histories.

758 changed files with 46503 additions and 95941 deletions

12
.eslintignore Normal file
View File

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

267
.eslintrc Normal file
View File

@ -0,0 +1,267 @@
{
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module",
},
"plugins": [
"import",
"json",
"mozilla",
"no-unsanitized",
"sort-exports",
"unicorn",
],
"extends": [
"plugin:json/recommended",
"plugin:prettier/recommended"
],
"env": {
"browser": true,
"es2022": true,
"worker": true,
},
"globals": {
"PDFJSDev": "readonly",
"__non_webpack_import__": "readonly",
},
"rules": {
// Plugins
"import/export": "error",
"import/exports-last": "error",
"import/extensions": ["error", "always", { "ignorePackages": true, }],
"import/first": "error",
"import/named": "error",
"import/no-cycle": "error",
"import/no-empty-named-blocks": "error",
"import/no-commonjs": "error",
"import/no-mutable-exports": "error",
"import/no-self-import": "error",
"import/no-unresolved": ["error", {
"ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web", "fluent-bundle", "fluent-dom"],
}],
"mozilla/avoid-removeChild": "error",
"mozilla/use-includes-instead-of-indexOf": "error",
"no-unsanitized/method": "error",
"no-unsanitized/property": "error",
"sort-exports/sort-exports": ["error", {
"ignoreCase": true,
}],
"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
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-cond-assign": ["error", "except-parens"],
"no-constant-condition": ["error", { "checkLoops": false, }],
"no-debugger": "error",
"no-dupe-args": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": ["error", { "allowEmptyCatch": true, }],
"no-empty-character-class": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-func-assign": "error",
"no-inner-declarations": ["error", "functions"],
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-obj-calls": "error",
"no-promise-executor-return": "error",
"no-regex-spaces": "error",
"no-setter-return": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }],
"no-unused-private-class-members": "error",
"use-isnan": ["error", { "enforceForIndexOf": true, }],
"valid-typeof": ["error", { "requireStringLiterals": true, }],
// Best Practices
"accessor-pairs": ["error", {
"setWithoutGet": true,
"enforceForClassMembers": true,
}],
"consistent-return": "error",
"curly": ["error", "all"],
"default-case-last": "error",
"dot-notation": "error",
"eqeqeq": ["error", "always"],
"grouped-accessor-pairs": ["error", "getBeforeSet"],
"no-alert": "error",
"no-caller": "error",
"no-else-return": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-fallthrough": "error",
"no-floating-decimal": "error",
"no-global-assign": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-symbol": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-octal": "error",
"no-redeclare": "error",
"no-return-await": "error",
"no-self-assign": "error",
"no-self-compare": "error",
"no-throw-literal": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-useless-call": "error",
"no-useless-catch": "error",
"no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error",
"prefer-promise-reject-errors": "error",
"prefer-spread": "error",
"wrap-iife": ["error", "any"],
"yoda": ["error", "never", {
"exceptRange": true,
}],
// Strict Mode
"strict": ["off", "global"],
// Variables
"no-delete-var": "error",
"no-label-var": "error",
"no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-undef-init": "error",
"no-undef": ["error", { "typeof": true, }],
"no-unused-vars": ["error", {
"vars": "all",
"args": "none",
}],
"no-use-before-define": ["error", {
"functions": false,
"classes": false,
"variables": false,
}],
// Stylistic Issues
"lines-between-class-members": ["error", "always"],
"max-len": ["error", {
"code": 1000,
"comments": 80,
"ignoreUrls": true
}],
"new-cap": ["error", { "newIsCap": true, "capIsNew": false, }],
"no-array-constructor": "error",
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0, "maxBOF": 1, }],
"no-nested-ternary": "error",
"no-new-object": "error",
"no-restricted-syntax": ["error",
{
"selector": "BinaryExpression[operator='instanceof'][right.name='Object']",
"message": "Use `typeof` rather than `instanceof Object`.",
},
{
"selector": "CallExpression[callee.name='assert'][arguments.length!=2]",
"message": "`assert()` must always be invoked with two arguments.",
},
{
"selector": "CallExpression[callee.name='isCmd'][arguments.length<2]",
"message": "Use `instanceof Cmd` rather than `isCmd()` with one argument.",
},
{
"selector": "CallExpression[callee.name='isDict'][arguments.length<2]",
"message": "Use `instanceof Dict` rather than `isDict()` with one argument.",
},
{
"selector": "CallExpression[callee.name='isName'][arguments.length<2]",
"message": "Use `instanceof Name` rather than `isName()` with one argument.",
},
{
"selector": "NewExpression[callee.name='Cmd']",
"message": "Use `Cmd.get()` rather than `new Cmd()`.",
},
{
"selector": "NewExpression[callee.name='Name']",
"message": "Use `Name.get()` rather than `new Name()`.",
},
{
"selector": "NewExpression[callee.name='Ref']",
"message": "Use `Ref.get()` rather than `new Ref()`.",
},
],
"no-unneeded-ternary": "error",
"operator-assignment": "error",
"prefer-exponentiation-operator": "error",
"spaced-comment": ["error", "always", {
"block": {
"balanced": true,
}
}],
// ECMAScript 6
"arrow-body-style": ["error", "as-needed"],
"constructor-super": "error",
"no-class-assign": "error",
"no-const-assign": "error",
"no-dupe-class-members": "error",
"no-duplicate-imports": "error",
"no-this-before-super": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-var": "error",
"object-shorthand": ["error", "always", {
"avoidQuotes": true,
}],
"prefer-const": "error",
"require-yield": "error",
"sort-imports": ["error", {
"ignoreCase": true,
}],
"template-curly-spacing": ["error", "never"],
},
}

17
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,17 @@
Attach (recommended) or Link to PDF file here:
Configuration:
- Web browser and its version:
- Operating system and its version:
- PDF.js version:
- Is a browser extension:
Steps to reproduce the problem:
1.
2.
What is the expected behavior? (add screenshot)
What went wrong? (add screenshot)
Link to a viewer (if hosted on a site other than mozilla.github.io/pdf.js or as Firefox/Chrome extension):

View File

@ -1,81 +0,0 @@
name: Bug Report
description: Report a bug in PDF.js
title: "[Bug]: "
body:
- type: textarea
attributes:
label: Attach (recommended) or Link to PDF file
description: Without this information the issue may be closed without comment
placeholder: Please place only the PDF file in this field
validations:
required: true
- type: markdown
attributes: { value: "---" }
- type: input
attributes:
label: Web browser and its version
description: Please ensure that it's supported, refer to [the FAQ](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support)
validations:
required: true
- type: input
attributes:
label: Operating system and its version
validations:
required: true
- type: input
attributes:
label: PDF.js version
description: Please find official releases [here](https://github.com/mozilla/pdf.js/releases)
validations:
required: true
- type: dropdown
attributes:
label: Is the bug present in the latest PDF.js version?
description: Please check the [online demo](https://github.com/mozilla/pdf.js#online-demo)
options: ["Yes", "No"]
default: 0
validations:
required: true
- type: dropdown
attributes:
label: Is a browser extension
options: ["Yes", "No"]
default: 1
validations:
required: true
- type: markdown
attributes: { value: "---" }
- type: textarea
attributes:
label: Steps to reproduce the problem
placeholder: "1.\n2."
validations:
required: true
- type: textarea
attributes:
label: What is the expected behavior?
description: Also add a screenshot
validations:
required: true
- type: textarea
attributes:
label: What went wrong?
description: Also add a screenshot
validations:
required: true
- type: input
attributes:
label: Link to a viewer
description: Needed if hosted on a site other than mozilla.github.io/pdf.js or as Firefox/Chrome extension
- type: textarea
attributes:
label: Additional context
description: Do you have anything to add that doesn't fit in the issue template?

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Need help?
url: https://github.com/mozilla/pdf.js/discussions
about: If you need help on how to use PDF.js, please open a discussion where other community members can reply

View File

@ -1,23 +0,0 @@
name: Feature request
description: Propose a new feature or enhancement for PDF.js
title: "[Feature]: "
body:
- type: dropdown
attributes:
label: Is the feature relevant to the Firefox PDF Viewer?
options: ["Yes", "No"]
default: 1
validations:
required: true
- type: textarea
attributes:
label: Feature description
description: What new feature would you like PDF.js to have? Why would it be useful? What are the current workarounds?
validations:
required: true
- type: textarea
attributes:
label: Other PDF viewers
description: Do other PDF viewers implement similar functionality? Add descriptions, links, and/or screenshots.

13
.github/SECURITY.md vendored
View File

@ -1,13 +0,0 @@
# Security policy
Mozilla takes the security of our software seriously. If you believe you have found a security vulnerability in PDF.js, please report it to us as described below.
## Reporting security vulnerabilities
**Please don't report security vulnerabilities through public GitHub issues.**
Instead, please report security vulnerabilities in [Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=PDF%20Viewer&groups=firefox-core-security) and make sure that the checkbox in the "Security" section is checked so the required access controls are automatically configured:
![Security checkbox](https://github.com/mozilla/pdf.js/blob/master/.github/security.png)
The Mozilla security team will process the bug as described in [Mozilla's security bugs policy](https://www.mozilla.org/en-US/about/governance/policies/security-group/bugs).

View File

@ -25,5 +25,3 @@ CO01:
exclusions:
files: []
messages: []
VC:
disabled: false

BIN
.github/security.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -11,21 +11,21 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [20, 22, 24]
node-version: [18, lts/*, latest]
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
run: npm install
- name: Run external tests
run: npx gulp externaltest

View File

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

View File

@ -25,12 +25,12 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Use Python 3.14
uses: actions/setup-python@v6
- name: Use Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.14'
python-version: '3.12'
cache: 'pip'
- name: Install Fluent dependencies

View File

@ -36,22 +36,22 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
run: npm install
- name: Use Python 3.14
uses: actions/setup-python@v6
- name: Use Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.14'
python-version: '3.12'
cache: 'pip'
- name: Install Fonttools

View File

@ -15,17 +15,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
run: npm install
- name: Run lint
run: npx gulp lint

View File

@ -1,37 +0,0 @@
name: Publish release
on:
release:
types: [published]
permissions:
contents: read
id-token: write
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [lts/*]
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build the `pdfjs-dist` library
run: npx gulp dist
- name: Publish the `pdfjs-dist` library to NPM
run: npm publish ./build/dist

View File

@ -17,17 +17,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
run: npm install
- name: Build the website
run: npx gulp web
@ -49,7 +49,7 @@ jobs:
INPUT_PATH: build/gh-pages
- name: Upload the website
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: github-pages
path: ${{ runner.temp }}/website.tar

View File

@ -15,17 +15,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
run: npm install
- name: Run types tests
run: npx gulp typestest

7
.gitpod.Dockerfile vendored Normal file
View File

@ -0,0 +1,7 @@
FROM gitpod/workspace-full-vnc
USER gitpod
RUN sudo apt-get update && \
sudo apt-get install -yq firefox && \
sudo rm -rf /var/lib/apt/lists/*

13
.gitpod.yml Normal file
View File

@ -0,0 +1,13 @@
image:
file: .gitpod.Dockerfile
tasks:
- command: |
gp await-port 8888 && gp preview $(gp url 8888)/web/viewer.html && echo '[{"name": "Firefox","path": "/usr/bin/firefox"}]' | jq '.' > test/resources/browser_manifests/browser_manifest.json
- init: npm install -g gulp-cli && npm install
command: gulp server
ports:
- port: 8888
onOpen: ignore
- port: 6080
onOpen: ignore

View File

@ -4,11 +4,8 @@ docs/
node_modules/
external/bcmaps/
external/builder/fixtures/
external/builder/fixtures_babel/
external/openjpeg/
external/qcms/
external/builder/fixtures_esprima/
external/quickjs/
test/stats/results/
test/tmp/
test/pdfs/
web/locale/

View File

@ -9,17 +9,10 @@
"overrides": [
{
"files": ["tsconfig.json", ".prettierrc"],
"options": {
"parser": "json"
}
files: ["tsconfig.json"],
options: {
parser: "json",
},
},
{
"files": ["**/*.html"],
"options": {
"parser": "html",
"printWidth": 160
}
}
]
}

View File

@ -1,9 +0,0 @@
{
"chrome": {
"skipDownload": false
},
"firefox": {
"skipDownload": false,
"version": "nightly"
}
}

View File

@ -4,9 +4,8 @@ docs/
node_modules/
external/bcmaps/
external/builder/fixtures/
external/builder/fixtures_babel/
external/builder/fixtures_esprima/
external/quickjs/
test/stats/results/
test/tmp/
test/pdfs/
web/locale/

View File

@ -1,35 +0,0 @@
export default {
rules: {
valid: true,
custom: [
(reporter, $, ast, { filename }) => {
reporter.name = "no-svg-fill-context-fill";
const svg = $.find("svg");
const fill = svg.attr("fill");
if (fill === "context-fill") {
reporter.error(
"Fill attribute on svg element must not be set to 'context-fill'",
svg[0],
ast
);
}
},
],
},
ignore: [
"build/**",
"l10n/**",
"docs/**",
"node_modules/**",
"external/bcmaps/**",
"external/builder/fixtures/**",
"external/builder/fixtures_babel/**",
"external/quickjs/**",
"test/tmp/**",
"test/pdfs/**",
"web/locale/**",
"*~/**",
],
};

View File

@ -8,21 +8,16 @@ Andreas Gal <gal@mozilla.com>
Artur Adib <aadib@mozilla.com>
Brendan Dahl <bdahl@mozilla.com>
Bill Walker <bwalker@mozilla.com>
Calixte Denizet <calixte@mozilla.com>
Chris G Jones <cjones@mozilla.com>
David Quintana <gigaherz@gmail.com>
Emily Wachowiak <ewachowiak@mozilla.com>
Felix Kälberer <@fkaelberer>
Jakob Miland <saebekassebil@gmail.com>
Jonas Jenwald <jonas.jenwald@gmail.com>
Julian Viereck
Justin D'Arcangelo <justindarc@gmail.com>
Kalervo Kujala
Marco Castelluccio <mcastelluccio@mozilla.com>
Marie-Lilas Onanga Ozavino <monangaozavino@mozilla.com>
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
Ophir Lojkine <@lovasoa>
Ryan Casey <rcasey@mozilla.com>
Rob Wu <rob@robwu.nl>
Shaon Barman <shaon.barman@gmail.com>
Sehyun Park <premed055515@gmail.com>

View File

@ -1,4 +1,4 @@
# PDF.js [![CI](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml/badge.svg?query=branch%3Amaster)](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml?query=branch%3Amaster)
# PDF.js [![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](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5.
@ -14,7 +14,7 @@ get involved, visit:
+ [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)
+ [Frequently Asked Questions](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions)
+ [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?q=is%3Aissue%20state%3Aopen%20label%3Agood-beginner-bug)
+ [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?direction=desc&labels=good-beginner-bug&page=1&sort=created&state=open)
+ [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.
@ -40,7 +40,7 @@ PDF.js is built into version 19+ of Firefox.
+ The official extension for Chrome can be installed from the [Chrome Web Store](https://chrome.google.com/webstore/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm).
*This extension is maintained by [@Rob--W](https://github.com/Rob--W).*
+ Build Your Own - Get the code as explained below and issue `npx gulp chromium`. Then open
+ Build Your Own - Get the code as explained below and issue `gulp chromium`. Then open
Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the
directory `build/chromium`.
@ -52,15 +52,24 @@ To get a local copy of the current code, clone it using git:
$ cd pdf.js
Next, install Node.js via the [official package](https://nodejs.org) or via
[nvm](https://github.com/creationix/nvm). If everything worked out, install
all dependencies for PDF.js:
[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)):
$ npm install -g gulp-cli@^2.3.0
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:
$ npm install
> [!NOTE]
> On MacOS M1/M2 you may see some `node-gyp`-related errors when running `npm install`. This is because one of our dependencies, `"canvas"`, does not provide pre-built binaries for this platform and instead `npm` will try to build it from source. Please make sure to first install the necessary native dependencies using `brew`: https://github.com/Automattic/node-canvas#compiling.
Finally, you need to start a local web server as some browsers do not allow opening
PDF files using a `file://` URL. Run:
$ npx gulp server
$ gulp server
and then you can open:
@ -77,11 +86,11 @@ It is also possible to view all test PDF files on the right side by opening:
In order to bundle all `src/` files into two production scripts and build the generic
viewer, run:
$ npx gulp generic
$ gulp generic
If you need to support older browsers, run:
$ npx gulp generic-legacy
$ 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
@ -90,7 +99,7 @@ be loaded by `pdf.js`. The PDF.js files are large and should be minified for pro
## Using PDF.js in a web application
To use PDF.js in a web application you can choose to use a pre-built version of the library
or to build it from source. We supply pre-built versions for usage with NPM under
or to build it from source. We supply pre-built versions for usage with NPM and Bower under
the `pdfjs-dist` name. For more information and examples please refer to the
[wiki page](https://github.com/mozilla/pdf.js/wiki/Setup-pdf.js-in-a-website) on this subject.
@ -107,7 +116,7 @@ You can play with the PDF.js API directly from your browser using the live demos
+ [Interactive examples](https://mozilla.github.io/pdf.js/examples/index.html#interactive-examples)
More examples can be found in the [examples folder](https://github.com/mozilla/pdf.js/tree/master/examples/). Some of them are using the pdfjs-dist package, which can be built and installed in this repo directory via `npx gulp dist-install` command.
More examples can be found in the [examples folder](https://github.com/mozilla/pdf.js/tree/master/examples/). Some of them are using the pdfjs-dist package, which can be built and installed in this repo directory via `gulp dist-install` command.
For an introduction to the PDF.js code, check out the presentation by our
contributor Julian Viereck:
@ -134,4 +143,4 @@ Talk to us on Matrix:
File an issue:
+ https://github.com/mozilla/pdf.js/issues/new/choose
+ https://github.com/mozilla/pdf.js/issues/new

20
docs/config.json Normal file
View File

@ -0,0 +1,20 @@
{
"locals": {
"url": "http://localhost:8080",
"name": "PDF.js Documentation",
"description": ""
},
"require": {
"typogr": "typogr"
},
"jade": {
"pretty": true
},
"markdown": {
"smartLists": true,
"smartypants": true
},
"plugins": [
"./plugins/wintersmith-makerelative.coffee"
]
}

View File

@ -1,11 +1,8 @@
---
title: API
layout: layout.njk
slug: api
template: layout.jade
---
# API
The generated API documentation, from the inline comments in [api.js](https://github.com/mozilla/pdf.js/blob/master/src/display/api.js), is available below.
<iframe src="draft/index.html" title="PDF.js API documentation"></iframe>
We're currently working on <a href="draft/index.html">better API docs</a>, but the API is well documented in [api.js](https://github.com/mozilla/pdf.js/blob/master/src/display/api.js).

View File

@ -1,100 +0,0 @@
/*!
Theme: a11y-light
Author: @ericwbailey
Maintainer: @ericwbailey
Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css
Original source: https://github.com/highlightjs/highlight.js/blob/main/src/styles/a11y-light.css
*/
.hljs {
background: #fefefe;
color: #545454;
}
/* Comment */
.hljs-comment,
.hljs-quote {
color: #696969;
}
/* Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
color: #d91e18;
}
/* Orange */
.hljs-number,
.hljs-built_in,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
color: #aa5d00;
}
/* Yellow */
.hljs-attribute {
color: #aa5d00;
}
/* Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
color: #008000;
}
/* Blue */
.hljs-title,
.hljs-section {
color: #007faa;
}
/* Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #7928a1;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
@media screen and (-ms-high-contrast: active) {
.hljs-addition,
.hljs-attribute,
.hljs-built_in,
.hljs-bullet,
.hljs-comment,
.hljs-link,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-params,
.hljs-string,
.hljs-symbol,
.hljs-type,
.hljs-quote {
color: highlight;
}
.hljs-keyword,
.hljs-selector-tag {
font-weight: bold;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,40 +1,119 @@
header {
background-color: #f8f8f8;
border-bottom: 1px solid #e5e7e8;
.navbar-brand {
padding: 0;
img {
height: 42px;
}
}
body {
}
main {
margin: 50px 0;
.description {
font-size: 20px;
}
pre {
background-color: #f5f5f5;
border: 1px solid #cccccc;
border-radius: 4px;
padding: 10px;
}
iframe {
border: none;
height: calc(0.55 * 100vh);
width: 100%;
}
.starter-template {
padding: 0 15px;
}
.navbar-brand {
padding: 4px 15px;
}
.navbar-brand img {
height: 42px;
}
.navbar {
border-color: #e5e7e8;
}
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus {
background-color: #fff;
border: 1px solid #e5e7e8;
border-width: 0 1px;
position: relative;
top: 1px;
}
footer {
border-top: 1px solid #e5e5e5;
color: #777777;
padding: 40px 0;
padding-top: 40px;
padding-bottom: 40px;
margin-top: 100px;
color: #777;
text-align: center;
border-top: 1px solid #E5E5E5;
}
/* code styling */
code {
font-family: 'Anonymous Pro', monospace;
font-size: 0.85em;
color: #000;
}
pre code {
display: block;
line-height: 1.1;
}
p code {
padding: 0.1em 0.3em 0.2em;
border-radius: 0.3em;
position: relative;
top: -0.15em;
background: #444;
color: #fff;
white-space: nowrap;
}
/* syntax hl stuff */
code.lang-markdown {
color: #424242;
}
code.lang-markdown .header,
code.lang-markdown .strong {
font-weight: bold;
}
code.lang-markdown .emphasis {
font-style: italic;
}
code.lang-markdown .horizontal_rule,
code.lang-markdown .link_label,
code.lang-markdown .code,
code.lang-markdown .header,
code.lang-markdown .link_url {
color: #555;
}
code.lang-markdown .blockquote,
code.lang-markdown .bullet {
color: #bbb;
}
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
.tomorrow-comment, pre .comment, pre .title {
color: #8e908c;
}
.tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
color: #c82829;
}
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
color: #f5871f;
}
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
color: #eab700;
}
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
color: #718c00;
}
.tomorrow-aqua, pre .css .hexcolor {
color: #3e999f;
}
.tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
color: #4271ae;
}
.tomorrow-purple, pre .keyword, pre .javascript .function {
color: #8959a8;
}

View File

@ -1,7 +1,6 @@
---
title: Examples
layout: layout.njk
slug: examples
template: layout.jade
---
## Hello World Walkthrough
@ -84,17 +83,17 @@ var scaledViewport = page.getViewport({ scale: scale, });
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.
<script async src="https://jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script>
<script async src="//jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script>
### Hello World using base64 encoded PDF
The PDF.js can accept any decoded base64 data as an array.
<script async src="https://jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script>
<script async src="//jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script>
### Previous/Next example
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.
<script async src="https://jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script>
<script async src="//jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script>

View File

@ -1,7 +1,6 @@
---
title: Getting Started
layout: layout.njk
slug: getting_started
template: layout.jade
---
# Getting Started
@ -77,50 +76,51 @@ Note that we only mention the most relevant files and folders.
### Prebuilt
```plaintext
```
├── build/
├── pdf.mjs - display layer
├── pdf.mjs.map - display layer's source map
├── pdf.worker.mjs - core layer
└── pdf.worker.mjs.map - core layer's source map
   ├── pdf.js - display layer
   ├── pdf.js.map - display layer's source map
   ├── pdf.worker.js - core layer
   └── pdf.worker.js.map - core layer's source map
├── web/
│ ├── cmaps/ - character maps (required by core)
│ ├── compressed.tracemonkey-pldi-09.pdf - PDF file for testing purposes
│ ├── debugger.js - helpful debugging features
│ ├── images/ - images for the viewer and annotation icons
│ ├── locale/ - translation files
│ ├── viewer.css - viewer style sheet
│ ├── viewer.html - viewer layout
│ ├── viewer.mjs - viewer layer
│ └── viewer.mjs.map - viewer layer's source map
│ ├── viewer.js - viewer layer
│ └── viewer.js.map - viewer layer's source map
└── LICENSE
```
### Source
```plaintext
```
├── docs/ - website source code
├── examples/ - simple usage examples
├── extensions/ - browser extension source code
├── external/ - third party code
├── l10n/ - translation files
├── src/
├── core/ - core layer
├── display/ - display layer
├── shared/ - shared code between the core and display layers
├── interfaces.js - interface definitions for the core/display layers
└── pdf.*.js - wrapper files for bundling
   ├── core/ - core layer
   ├── display/ - display layer
   ├── shared/ - shared code between the core and display layers
   ├── interfaces.js - interface definitions for the core/display layers
   └── pdf.*.js - wrapper files for bundling
├── test/ - unit, font, reference, and integration tests
├── web/ - viewer layer
├── LICENSE
├── README.md
├── gulpfile.mjs - build scripts/logic
├── gulpfile.js - build scripts/logic
├── package-lock.json - pinned dependency versions
└── package.json - package definition and dependencies
```
## Trying the Viewer
With the prebuilt or source version, open `web/viewer.html` in a browser and the test pdf should load. Note: the worker is not enabled for file:// urls, so use a server. If you're using the source build and have node, you can run `npx gulp server`.
With the prebuilt or source version, open `web/viewer.html` in a browser and the test pdf should load. Note: the worker is not enabled for file:// urls, so use a server. If you're using the source build and have node, you can run `gulp server`.
## More Information

View File

@ -1,13 +1,14 @@
---
title: Home
layout: layout.njk
slug: home
title: PDF.js
template: layout.jade
---
<h1 class="text-center">PDF.js</h1>
<p class="text-center description">A general-purpose, web standards-based platform for parsing and rendering PDFs.</p>
<p class="text-center">
<a type="button" class="btn btn-outline-dark" href="/getting_started/#download">Download</a>
<a type="button" class="btn btn-outline-dark" href="https://github.com/mozilla/pdf.js#online-demo">Demo</a>
<a type="button" class="btn btn-outline-dark" href="https://github.com/mozilla/pdf.js">GitHub Project</a>
<p class="text-center" style="font-size: 20px">A general-purpose, web standards-based platform for parsing and rendering PDFs.
</p>
<p class="text-center">
<a type="button" class="btn btn-lg btn-default" href="getting_started/#download">Download</a>
<a type="button" class="btn btn-lg btn-default" href="https://github.com/mozilla/pdf.js#online-demo">Demo</a>
<a type="button" class="btn btn-lg btn-default" href="https://github.com/mozilla/pdf.js">GitHub Project</a>
</p>

File diff suppressed because one or more lines are too long

4
docs/contents/js/jquery-2.1.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
module.exports = (env, callback) ->
count = (string, substr) ->
num = pos = 0
return 1/0 unless substr.length
num++ while pos = 1 + string.indexOf(substr, pos)
num
env.helpers.makeRelative = (source, dest) ->
return dest unless dest.indexOf("/") == 0
depth = count(source, '/') # 1 being /
ret = ""
ret += "../" while depth = depth - 1
ret + dest.substring(1)
callback()

52
docs/templates/layout.jade vendored Normal file
View File

@ -0,0 +1,52 @@
- makeRelative = env.helpers.makeRelative
doctype html
html(lang='en')
head
meta(charset='utf-8')
meta(name='viewport', content='width=device-width, initial-scale=1.0')
meta(name='description', content='A general-purpose, web standards-based platform for parsing and rendering PDFs.')
meta(name='author', content='')
link(rel='shortcut icon', href=makeRelative(page.url, '/images/favicon.ico'))
title=page.title
// Bootstrap core CSS
link(href=makeRelative(page.url, '/css/bootstrap.min.css'), rel='stylesheet')
// Custom styles for this template
link(href=makeRelative(page.url, '/css/main.css'), rel='stylesheet')
body
header.navbar.navbar-default.navbar-static-top
.container
.navbar-header
button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand(href='#')
img(src=makeRelative(page.url, '/images/logo.svg'))
.collapse.navbar-collapse
ul.nav.navbar-nav
li(class=(page.url === '/' ? 'active' : ''))
a(href=makeRelative(page.url, '/')) Home
li(class=(page.url === '/getting_started/' ? 'active' : ''))
a(href=makeRelative(page.url, '/getting_started/')) Getting Started
li(class=(page.url === '/examples/' ? 'active' : ''))
a(href=makeRelative(page.url, '/examples/')) Examples
li
a(href='https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions') FAQ
li(class=(page.url === '/api/' ? 'active' : ''))
a(href=makeRelative(page.url, '/api/')) API
.container
.starter-template
section.content!= typogr(page.html).typogrify()
.container
footer
p &copy;Mozilla and individual contributors
:markdown-it
PDF.js is licensed under [Apache](https://github.com/mozilla/pdf.js/blob/master/LICENSE),
documentation is licensed under [CC BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/)
// Bootstrap core JavaScript
script(src=makeRelative(page.url, '/js/jquery-2.1.0.min.js'))
script(src=makeRelative(page.url, '/js/bootstrap.min.js'))

View File

@ -1,56 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ sitename }} - {{ title }}</title>
<meta charset="utf-8">
<meta name="description" content="{{ description }}">
<meta name="viewport" content="device-width, initial-scale=1.0">
<script src="/js/jquery-3.7.1.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<link rel="shortcut icon" href="/images/favicon.ico">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/a11y-light.css">
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<div class="navbar-brand"><img src="/images/logo.svg"></div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item {{ 'active' if slug == 'home' else '' }}">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item {{ 'active' if slug == 'getting_started' else '' }}">
<a class="nav-link" href="/getting_started">Getting started</a>
</li>
<li class="nav-item {{ 'active' if slug == 'examples' else '' }}">
<a class="nav-link" href="/examples">Examples</a>
</li>
<li class="nav-item {{ 'active' if slug == 'api' else '' }}">
<a class="nav-link" href="/api">API</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions">FAQ</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main class="container">
{{ contents | safe }}
</main>
<footer>
<p>&copy; Mozilla and individual contributors</p>
<p>
PDF.js is licensed under <a href="https://github.com/mozilla/pdf.js/blob/master/LICENSE">Apache 2.0</a>,
documentation is licensed under <a href="https://creativecommons.org/licenses/by-sa/2.5">CC BY-SA 2.5</a>.
</p>
</footer>
</body>
</html>

View File

@ -1,473 +0,0 @@
import globals from "globals";
import import_ from "eslint-plugin-import";
import jasmine from "eslint-plugin-jasmine";
import json from "eslint-plugin-json";
import noUnsanitized from "eslint-plugin-no-unsanitized";
import perfectionist from "eslint-plugin-perfectionist";
import prettierRecommended from "eslint-plugin-prettier/recommended";
import unicorn from "eslint-plugin-unicorn";
const jsFiles = folder => {
const prefix = folder === "." ? "" : folder + "/";
return [prefix + "**/*.js", prefix + "**/*.jsm", prefix + "**/*.mjs"];
};
// Include all files referenced in extensions/chromium/background.js
const chromiumExtensionServiceWorkerFiles = [
"extensions/chromium/extension-router.js",
"extensions/chromium/options/migration.js",
"extensions/chromium/pdfHandler.js",
"extensions/chromium/preserve-referer.js",
"extensions/chromium/suppress-update.js",
"extensions/chromium/telemetry.js",
];
export default [
{
ignores: [
"**/build/",
"**/l10n/",
"**/docs/",
"**/node_modules/",
"external/bcmaps/",
"external/builder/fixtures/",
"external/builder/fixtures_babel/",
"external/openjpeg/",
"external/qcms/",
"external/quickjs/",
"test/stats/results/",
"test/tmp/",
"test/pdfs/",
"web/locale/",
"web/wasm/",
"**/*~/",
],
},
/* ======================================================================== *\
Base configuration
\* ======================================================================== */
prettierRecommended,
{
files: ["**/*.json"],
...json.configs.recommended,
},
{
files: jsFiles("."),
ignores: chromiumExtensionServiceWorkerFiles,
languageOptions: {
globals: globals.browser,
},
},
{
files: jsFiles("."),
plugins: {
import: import_.flatConfigs.recommended.plugins.import,
json,
"no-unsanitized": noUnsanitized,
perfectionist,
unicorn,
},
languageOptions: {
globals: {
...globals.worker,
PDFJSDev: "readonly",
__raw_import__: "readonly",
},
ecmaVersion: 2025,
sourceType: "module",
},
rules: {
"import/export": "error",
"import/exports-last": "error",
"import/extensions": ["error", "always", { ignorePackages: true }],
"import/first": "error",
"import/named": "error",
"import/no-cycle": "error",
"import/no-empty-named-blocks": "error",
"import/no-commonjs": "error",
"import/no-mutable-exports": "error",
"import/no-restricted-paths": [
"error",
{
zones: [
{
target: "./web",
from: "./src",
},
],
},
],
"import/no-self-import": "error",
"import/no-unresolved": [
"error",
{
ignore: [
"display",
"pdfjs",
"pdfjs-lib",
"pdfjs-web",
"web",
"fluent-bundle",
"fluent-dom",
// See https://github.com/firebase/firebase-admin-node/discussions/1359.
"eslint-plugin-perfectionist",
],
},
],
"no-unsanitized/method": "error",
"no-unsanitized/property": "error",
"perfectionist/sort-exports": "error",
"perfectionist/sort-named-exports": "error",
"unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-reduce": ["error", { allowSimpleOperations: true }],
"unicorn/no-console-spaces": "error",
"unicorn/no-instanceof-builtins": "error",
"unicorn/no-invalid-remove-event-listener": "error",
"unicorn/no-new-buffer": "error",
"unicorn/no-single-promise-in-promise-methods": "error",
"unicorn/no-typeof-undefined": ["error", { checkGlobalVariables: false }],
"unicorn/no-unnecessary-array-flat-depth": "error",
"unicorn/no-unnecessary-array-splice-count": "error",
"unicorn/no-unnecessary-slice-end": "error",
"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-import-meta-properties": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-logical-operator-over-ternary": "error",
"unicorn/prefer-modern-dom-apis": "error",
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-optional-catch-binding": "error",
"unicorn/prefer-regexp-test": "error",
"unicorn/prefer-single-call": "error",
"unicorn/prefer-string-replace-all": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-ternary": ["error", "only-single-line"],
"unicorn/throw-new-error": "error",
// Possible errors
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-cond-assign": ["error", "except-parens"],
"no-constant-condition": ["error", { checkLoops: false }],
"no-debugger": "error",
"no-dupe-args": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": ["error", { allowEmptyCatch: true }],
"no-empty-character-class": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-func-assign": "error",
"no-inner-declarations": ["error", "functions"],
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-obj-calls": "error",
"no-promise-executor-return": "error",
"no-regex-spaces": "error",
"no-setter-return": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": [
"error",
{ disallowArithmeticOperators: true },
],
"no-unused-private-class-members": "error",
"use-isnan": ["error", { enforceForIndexOf: true }],
"valid-typeof": ["error", { requireStringLiterals: true }],
// Best Practices
"accessor-pairs": [
"error",
{ setWithoutGet: true, enforceForClassMembers: true },
],
"consistent-return": "error",
curly: ["error", "all"],
"default-case-last": "error",
"dot-notation": "error",
eqeqeq: ["error", "always"],
"grouped-accessor-pairs": ["error", "getBeforeSet"],
"no-alert": "error",
"no-caller": "error",
"no-else-return": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-fallthrough": "error",
"no-floating-decimal": "error",
"no-global-assign": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-symbol": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-octal": "error",
"no-redeclare": "error",
"no-return-await": "error",
"no-self-assign": "error",
"no-self-compare": "error",
"no-throw-literal": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-useless-call": "error",
"no-useless-catch": "error",
"no-useless-concat": "error",
"no-useless-escape": "error",
"no-useless-return": "error",
"prefer-promise-reject-errors": "error",
"prefer-spread": "error",
"wrap-iife": ["error", "any"],
yoda: ["error", "never", { exceptRange: true }],
// Strict Mode
strict: ["off", "global"],
// Variables
"no-delete-var": "error",
"no-label-var": "error",
"no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-undef-init": "error",
"no-undef": ["error", { typeof: true }],
"no-unused-vars": ["error", { vars: "all", args: "none" }],
"no-use-before-define": [
"error",
{ functions: false, classes: false, variables: false },
],
// Stylistic Issues
"lines-between-class-members": ["error", "always"],
"max-len": ["error", { code: 1000, comments: 80, ignoreUrls: true }],
"new-cap": ["error", { newIsCap: true, capIsNew: false }],
"no-array-constructor": "error",
"no-multiple-empty-lines": ["error", { max: 1, maxEOF: 0, maxBOF: 1 }],
"no-nested-ternary": "error",
"no-new-object": "error",
"no-restricted-syntax": [
"error",
{
selector:
"BinaryExpression[operator='instanceof'][right.name='Object']",
message: "Use `typeof` rather than `instanceof Object`.",
},
{
selector: "CallExpression[callee.name='assert'][arguments.length!=2]",
message: "`assert()` must always be invoked with two arguments.",
},
{
selector: "CallExpression[callee.name='isCmd'][arguments.length<2]",
message:
"Use `instanceof Cmd` rather than `isCmd()` with one argument.",
},
{
selector: "CallExpression[callee.name='isDict'][arguments.length<2]",
message:
"Use `instanceof Dict` rather than `isDict()` with one argument.",
},
{
selector: "CallExpression[callee.name='isName'][arguments.length<2]",
message:
"Use `instanceof Name` rather than `isName()` with one argument.",
},
{
selector: "NewExpression[callee.name='Cmd']",
message: "Use `Cmd.get()` rather than `new Cmd()`.",
},
{
selector: "NewExpression[callee.name='Name']",
message: "Use `Name.get()` rather than `new Name()`.",
},
{
selector: "NewExpression[callee.name='ObjectLoader']",
message:
"Use `ObjectLoader.load()` rather than `new ObjectLoader()`.",
},
{
selector: "NewExpression[callee.name='Ref']",
message: "Use `Ref.get()` rather than `new Ref()`.",
},
{
selector: "ExportNamedDeclaration[declaration]",
message:
"Separate the declaration and the export statement, using `export { ... }`.",
},
{
selector: "ExportDefaultDeclaration:has(> :declaration)",
message:
"Separate the declaration and the export statement, using `export default <variable name>`.",
},
],
"no-unneeded-ternary": "error",
"operator-assignment": "error",
"prefer-exponentiation-operator": "error",
"spaced-comment": ["error", "always", { block: { balanced: true } }],
// ECMAScript 6
"arrow-body-style": ["error", "as-needed"],
"constructor-super": "error",
"no-class-assign": "error",
"no-const-assign": "error",
"no-dupe-class-members": "error",
"no-duplicate-imports": "error",
"no-this-before-super": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-var": "error",
"object-shorthand": ["error", "always", { avoidQuotes: true }],
"prefer-const": "error",
"require-yield": "error",
"sort-imports": ["error", { ignoreCase: true }],
"template-curly-spacing": ["error", "never"],
},
},
{
files: jsFiles("src"),
rules: {
"no-console": "error",
},
},
/* ======================================================================== *\
Test-specific rules
\* ======================================================================== */
{
files: jsFiles("test"),
plugins: { jasmine },
languageOptions: {
globals: {
...globals.node,
...globals.jasmine,
},
},
rules: {
...jasmine.configs.recommended.rules,
"jasmine/new-line-before-expect": "off",
"jasmine/new-line-between-declarations": "off",
"jasmine/no-focused-tests": "error",
"jasmine/no-pending-tests": "off",
"jasmine/no-spec-dupes": ["error", "branch"],
"jasmine/no-suite-dupes": ["error", "branch"],
"jasmine/prefer-jasmine-matcher": "off",
"jasmine/prefer-toHaveBeenCalledWith": "off",
},
},
{
files: jsFiles("test/unit"),
rules: {
"import/no-unresolved": ["error", { ignore: ["pdfjs/"] }],
"no-console": ["error", { allow: ["warn", "error"] }],
},
},
{
files: jsFiles("test/integration"),
rules: {
"no-console": ["error", { allow: ["warn", "error"] }],
"no-restricted-syntax": [
"error",
{
selector: "CallExpression[callee.name='waitForTimeout']",
message:
"`waitForTimeout` can cause intermittent failures and should not be used (see issue #17656 for replacements).",
},
],
},
},
/* ======================================================================== *\
External libraries
\* ======================================================================== */
{
files: jsFiles("external"),
languageOptions: { globals: globals.node },
},
/* ======================================================================== *\
Examples
\* ======================================================================== */
{
files: jsFiles("examples"),
languageOptions: {
globals: {
pdfjsImageDecoders: false,
pdfjsLib: false,
pdfjsViewer: false,
},
},
},
{
files: [...jsFiles("examples/node"), ...jsFiles("examples/webpack")],
languageOptions: { globals: globals.node },
},
/* ======================================================================== *\
Chromium extension
\* ======================================================================== */
{
files: jsFiles("extensions/chromium"),
languageOptions: {
globals: globals.webextensions,
sourceType: "script",
},
rules: {
"no-var": "off",
},
},
{
files: chromiumExtensionServiceWorkerFiles,
languageOptions: {
globals: globals.serviceworker,
sourceType: "script",
},
},
/* ======================================================================== *\
Other
\* ======================================================================== */
{
files: ["gulpfile.mjs"],
languageOptions: { globals: globals.node },
},
];

11
examples/.eslintrc Normal file
View File

@ -0,0 +1,11 @@
{
"extends": [
"../.eslintrc"
],
"globals": {
"pdfjsImageDecoders": false,
"pdfjsLib": false,
"pdfjsViewer": false,
},
}

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<!--
Copyright 2014 Mozilla Foundation
@ -15,29 +15,29 @@ 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 page viewer using built components</title>
<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 page viewer using built components</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<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/web/pdf_viewer.mjs" type="module"></script>
</head>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head>
<body tabindex="1">
<div id="pageContainer" class="pdfViewer singlePageView"></div>
<body tabindex="1">
<div id="pageContainer" class="pdfViewer singlePageView"></div>
<script src="pageviewer.mjs" type="module"></script>
</body>
<script src="pageviewer.mjs" type="module"></script>
</body>
</html>

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<!--
Copyright 2014 Mozilla Foundation
@ -15,37 +15,37 @@ 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 viewer using built components</title>
<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 viewer using built components</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
#viewerContainer {
overflow: auto;
position: absolute;
width: 100%;
height: 100%;
}
</style>
<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" />
<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/web/pdf_viewer.mjs" type="module"></script>
</head>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head>
<body tabindex="1">
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<body tabindex="1">
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<script src="simpleviewer.mjs" type="module"></script>
</body>
<script src="simpleviewer.mjs" type="module"></script>
</body>
</html>

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<!--
Copyright 2014 Mozilla Foundation
@ -15,37 +15,37 @@ 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 Single Page Viewer using built components</title>
<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 Single Page Viewer using built components</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
#viewerContainer {
overflow: auto;
position: absolute;
width: 100%;
height: 100%;
}
</style>
<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" />
<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/web/pdf_viewer.mjs" type="module"></script>
</head>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head>
<body tabindex="1">
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<body tabindex="1">
<div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
<script src="singlepageviewer.mjs" type="module"></script>
</body>
<script src="singlepageviewer.mjs" type="module"></script>
</body>
</html>

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<!--
Copyright 2018 Mozilla Foundation
@ -15,26 +15,26 @@ 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 standalone JpegImage parser</title>
<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 standalone JpegImage parser</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script>
</head>
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script>
</head>
<body tabindex="1">
<canvas id="jpegCanvas" width="0" height="0"></canvas>
<body tabindex="1">
<canvas id="jpegCanvas" width="0" height="0"></canvas>
<script src="jpeg_viewer.mjs" type="module"></script>
</body>
<script src="jpeg_viewer.mjs" type="module"></script>
</body>
</html>

View File

@ -1,71 +1,76 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>'Hello, world!' example</title>
</head>
<body>
<h1>'Hello, world!' example</h1>
<head>
<meta charset="UTF-8">
<title>'Hello, world!' example</title>
</head>
<body>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr"></canvas>
<h1>'Hello, world!' example</h1>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
<script id="script" type="module">
//
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
//
const url = "./helloworld.pdf";
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
//
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
<script id="script" type="module">
//
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
//
const url = './helloworld.pdf';
//
// Asynchronous download PDF
//
const loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise;
//
// Fetch the first page
//
const page = await pdf.getPage(1);
const scale = 1.5;
const viewport = page.getViewport({ scale });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
//
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
//
// Prepare canvas using PDF page dimensions
//
const canvas = document.getElementById("the-canvas");
const context = canvas.getContext("2d");
//
// Asynchronous download PDF
//
const loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise;
//
// Fetch the first page
//
const page = await pdf.getPage(1);
const scale = 1.5;
const viewport = page.getViewport({ scale });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
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";
//
// Prepare canvas using PDF page dimensions
//
const canvas = document.getElementById("the-canvas");
const context = canvas.getContext("2d");
const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
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";
//
// Render PDF page into canvas context
//
const renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
</script>
const transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
<hr />
<h2>JavaScript code:</h2>
<pre id="code"></pre>
<script>
document.getElementById("code").textContent = document.getElementById("script").text;
</script>
</body>
//
// Render PDF page into canvas context
//
const renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
</script>
<hr>
<h2>JavaScript code:</h2>
<pre id="code"></pre>
<script>
document.getElementById('code').textContent =
document.getElementById('script').text;
</script>
</body>
</html>

View File

@ -1,77 +1,81 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>'Hello, world!' base64 example</title>
</head>
<body>
<h1>'Hello, world!' example</h1>
<head>
<meta charset="UTF-8">
<title>'Hello, world!' base64 example</title>
</head>
<body>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr"></canvas>
<h1>'Hello, world!' example</h1>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
<script id="script" type="module">
// atob() is used to convert base64 encoded PDF to binary-like data.
// (See also https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/
// Base64_encoding_and_decoding.)
var pdfData = atob(
"JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog" +
"IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv" +
"TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K" +
"Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg" +
"L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+" +
"PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u" +
"dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq" +
"Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU" +
"CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu" +
"ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g" +
"CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw" +
"MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v" +
"dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G"
);
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
//
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
<script id="script" type="module">
// atob() is used to convert base64 encoded PDF to binary-like data.
// (See also https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/
// Base64_encoding_and_decoding.)
var pdfData = atob(
'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' +
'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' +
'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' +
'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' +
'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' +
'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' +
'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' +
'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' +
'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' +
'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' +
'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' +
'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' +
'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G');
// 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.
var loadingTask = pdfjsLib.getDocument({ data: pdfData });
var pdf = await loadingTask.promise;
// Fetch the first page.
var page = await pdf.getPage(1);
var scale = 1.5;
var viewport = page.getViewport({ scale: scale });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
//
// The workerSrc property shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
// Prepare canvas using PDF page dimensions.
var canvas = document.getElementById("the-canvas");
var context = canvas.getContext("2d");
// 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.
var loadingTask = pdfjsLib.getDocument({ data: pdfData, });
var pdf = await loadingTask.promise;
// Fetch the first page.
var page = await pdf.getPage(1);
var scale = 1.5;
var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
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";
// Prepare canvas using PDF page dimensions.
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
var transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
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";
// Render PDF page into canvas context.
var renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
</script>
var transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
<hr />
<h2>JavaScript code:</h2>
<pre id="code"></pre>
<script>
document.getElementById("code").textContent = document.getElementById("script").text;
</script>
</body>
// Render PDF page into canvas context.
var renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
</script>
<hr>
<h2>JavaScript code:</h2>
<pre id="code"></pre>
<script>
document.getElementById('code').textContent =
document.getElementById('script').text;
</script>
</body>
</html>

View File

@ -1,134 +1,139 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Previous/Next example</title>
</head>
<body>
<h1>'Previous/Next' example</h1>
<head>
<meta charset="UTF-8">
<title>Previous/Next example</title>
</head>
<body>
<div>
<button id="prev" type="button">Previous</button>
<button id="next" type="button">Next</button>
&nbsp; &nbsp;
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
</div>
<h1>'Previous/Next' example</h1>
<div>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr"></canvas>
</div>
<div>
<button id="prev">Previous</button>
<button id="next">Next</button>
&nbsp; &nbsp;
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
</div>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<div>
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
</div>
<script id="script" type="module">
//
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
//
var url = "../../web/compressed.tracemonkey-pldi-09.pdf";
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
//
// In cases when the pdf.worker.js is located at the different folder than the
// PDF.js's one, or the PDF.js is executed via eval(), the workerSrc property
// shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
<script id="script" type="module">
//
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
//
var url = '../../web/compressed.tracemonkey-pldi-09.pdf';
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById("the-canvas"),
ctx = canvas.getContext("2d");
//
// In cases when the pdf.worker.js is located at the different folder than the
// PDF.js's one, or the PDF.js is executed via eval(), the workerSrc property
// shall be specified.
//
pdfjsLib.GlobalWorkerOptions.workerSrc =
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function (page) {
var viewport = page.getViewport({ scale: scale });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
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";
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
var transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
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";
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
transform: transform,
viewport: viewport,
};
var renderTask = page.render(renderContext);
var transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
// Wait for rendering to finish
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
transform: transform,
viewport: viewport,
};
var renderTask = page.render(renderContext);
// Update page counters
document.getElementById("page_num").textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finished. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
// Wait for rendering to finish
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
}
});
});
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById("prev").addEventListener("click", onPrevPage);
// Update page counters
document.getElementById('page_num').textContent = num;
}
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById("next").addEventListener("click", onNextPage);
/**
* If another page rendering in progress, waits until the rendering is
* finished. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Asynchronously downloads PDF.
*/
var loadingTask = pdfjsLib.getDocument(url);
pdfDoc = await loadingTask.promise;
document.getElementById("page_count").textContent = pdfDoc.numPages;
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
// Initial/first page rendering
renderPage(pageNum);
</script>
</body>
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
/**
* Asynchronously downloads PDF.
*/
var loadingTask = pdfjsLib.getDocument(url);
pdfDoc = await loadingTask.promise;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
</script>
</body>
</html>

View File

@ -113,8 +113,8 @@ footer {
background-color: rgb(0 0 0 / 0);
font-size: 1.2rem;
color: rgb(255 255 255 / 1);
background-image:
url(images/div_line_left.png), url(images/div_line_right.png);
background-image: url(images/div_line_left.png),
url(images/div_line_right.png);
background-repeat: no-repeat;
background-position: left, right;
background-size: 0.2rem, 0.2rem;

View File

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<!--
Copyright 2016 Mozilla Foundation
@ -16,13 +16,13 @@ limitations under the License.
-->
<html dir="ltr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>PDF.js viewer</title>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css" />
<link rel="stylesheet" type="text/css" href="viewer.css" />
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_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/web/pdf_viewer.mjs" type="module"></script>
@ -43,15 +43,15 @@ limitations under the License.
</div>
<footer>
<button class="toolbarButton pageUp" title="Previous Page" id="previous" type="button"></button>
<button class="toolbarButton pageDown" title="Next Page" id="next" type="button"></button>
<button class="toolbarButton pageUp" title="Previous Page" id="previous"></button>
<button class="toolbarButton pageDown" title="Next Page" id="next"></button>
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" />
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1">
<button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut" type="button"></button>
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn" type="button"></button>
<button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut"></button>
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn"></button>
</footer>
<script src="viewer.mjs" type="module"></script>
<script src="viewer.mjs" type="module"></script>
</body>
</html>

View File

@ -91,10 +91,10 @@ const PDFViewerApplication = {
let key = "pdfjs-loading-error";
if (reason instanceof pdfjsLib.InvalidPDFException) {
key = "pdfjs-invalid-file-error";
} else if (reason instanceof pdfjsLib.ResponseException) {
key = reason.missing
? "pdfjs-missing-file-error"
: "pdfjs-unexpected-response-error";
} else if (reason instanceof pdfjsLib.MissingPDFException) {
key = "pdfjs-missing-file-error";
} else if (reason instanceof pdfjsLib.UnexpectedResponseException) {
key = "pdfjs-unexpected-response-error";
}
self.l10n.get(key).then(msg => {
self.error(msg, { message: reason?.message });

9
examples/node/.eslintrc Normal file
View File

@ -0,0 +1,9 @@
{
"extends": [
"../.eslintrc"
],
"env": {
"node": true,
},
}

View File

@ -9,7 +9,9 @@ Install the dependencies and build the PDF.js library:
$ npm install
$ gulp dist-install
Run the example to convert the first page of a PDF file to a PNG image:
Install the Node canvas library and run the example to convert the first page of a
PDF file to a PNG image:
$ npm install canvas
$ cd examples/node/pdf2png
$ node pdf2png.mjs
$ node pdf2png.js

View File

@ -13,9 +13,41 @@
* 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;
@ -24,6 +56,8 @@ const CMAP_PACKED = true;
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";
@ -35,6 +69,7 @@ const loadingTask = getDocument({
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL,
canvasFactory,
});
try {
@ -43,7 +78,6 @@ try {
// Get the first page.
const page = await pdfDocument.getPage(1);
// Render the page on a Node canvas with 100% scale.
const canvasFactory = pdfDocument.canvasFactory;
const viewport = page.getViewport({ scale: 1.0 });
const canvasAndContext = canvasFactory.create(
viewport.width,
@ -57,7 +91,7 @@ try {
const renderTask = page.render(renderContext);
await renderTask.promise;
// Convert the canvas to an image buffer.
const image = canvasAndContext.canvas.toBuffer("image/png");
const image = canvasAndContext.canvas.toBuffer();
fs.writeFile("output.png", image, function (error) {
if (error) {
console.error("Error: " + error);

View File

@ -1,13 +1,14 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<head>
<meta charset="UTF-8">
<title>Text-only PDF.js example</title>
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="pdf2svg.mjs" type="module"></script>
</head>
<body>
<p>Text-only PDF.js example</p>
<div id="pageContainer" style="display: inline-block; border: solid 1px black"></div>
</body>
</head>
<body>
<p>Text-only PDF.js example</p>
<div id="pageContainer" style="display: inline-block; border: solid 1px black;">
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
{
"extends": [
"../.eslintrc"
],
"env": {
"node": true,
},
}

View File

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

View File

@ -0,0 +1,22 @@
{
"extends": [
../../.eslintrc
],
"env": {
"webextensions": true
},
"plugins": [
"mozilla"
],
"parserOptions": {
"sourceType": "script"
},
"rules": {
"mozilla/import-globals": "error",
"no-var": "off",
},
}

View File

@ -16,16 +16,13 @@ limitations under the License.
"use strict";
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
function getViewerURL(pdf_url) {
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url);
}
document.addEventListener("animationstart", onAnimationStart, true);
if (document.contentType === "application/pdf") {
chrome.runtime.sendMessage({ action: "canRequestBody" }, maybeRenderPdfDoc);
}
function onAnimationStart(event) {
if (event.animationName === "pdfjs-detected-object-or-embed") {
@ -224,38 +221,3 @@ function getEmbeddedViewerURL(path) {
path = a.href;
return getViewerURL(path) + fragment;
}
function maybeRenderPdfDoc(isNotPOST) {
if (!isNotPOST) {
// The document was loaded through a POST request, but we cannot access the
// original response body, nor safely send a new request to fetch the PDF.
// Until #4483 is fixed, POST requests should be ignored.
return;
}
// Detected PDF that was not redirected by the declarativeNetRequest rules.
// Maybe because this was served without Content-Type and sniffed as PDF.
// Or because this is Chrome 127-, which does not support responseHeaders
// condition in declarativeNetRequest (DNR), and PDF requests are therefore
// not redirected via DNR.
// In any case, load the viewer.
console.log(`Detected PDF via document, opening viewer for ${document.URL}`);
// Ideally we would use logic consistent with the DNR logic, like this:
// location.href = getEmbeddedViewerURL(document.URL);
// ... unfortunately, this causes Chrome to crash until version 129, fixed by
// https://chromium.googlesource.com/chromium/src/+/8c42358b2cc549553d939efe7d36515d80563da7%5E%21/
// Work around this by replacing the body with an iframe of the viewer.
// Interestingly, Chrome's built-in PDF viewer uses a similar technique.
const shadowRoot = document.body.attachShadow({ mode: "closed" });
const iframe = document.createElement("iframe");
iframe.style.position = "absolute";
iframe.style.top = "0";
iframe.style.left = "0";
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "0 none";
iframe.src = getEmbeddedViewerURL(document.URL);
shadowRoot.append(iframe);
}

View File

@ -17,12 +17,13 @@ limitations under the License.
"use strict";
(function ExtensionRouterClosure() {
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
var CRX_BASE_URL = chrome.runtime.getURL("/");
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
var CRX_BASE_URL = chrome.extension.getURL("/");
var schemes = [
"http",
"https",
"ftp",
"file",
"chrome-extension",
"blob",
@ -46,7 +47,6 @@ limitations under the License.
}
var scheme = url.slice(0, schemeIndex).toLowerCase();
if (schemes.includes(scheme)) {
// NOTE: We cannot use the `updateUrlHash` function in this context.
url = url.split("#", 1)[0];
if (url.charAt(schemeIndex) === ":") {
url = encodeURIComponent(url);
@ -56,50 +56,73 @@ limitations under the License.
return undefined;
}
function resolveViewerURL(originalUrl) {
if (originalUrl.startsWith(CRX_BASE_URL)) {
// TODO(rob): Use declarativeWebRequest once declared URL-encoding is
// supported, see http://crbug.com/273589
// (or rewrite the query string parser in viewer.js to get it to
// recognize the non-URL-encoded PDF URL.)
chrome.webRequest.onBeforeRequest.addListener(
function (details) {
// This listener converts chrome-extension://.../http://...pdf to
// chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
var url = parseExtensionURL(originalUrl);
var url = parseExtensionURL(details.url);
if (url) {
url = VIEWER_URL + "?file=" + url;
var i = originalUrl.indexOf("#");
var i = details.url.indexOf("#");
if (i > 0) {
url += originalUrl.slice(i);
url += details.url.slice(i);
}
return url;
}
}
return undefined;
}
self.addEventListener("fetch", event => {
const req = event.request;
if (req.destination === "document") {
var url = resolveViewerURL(req.url);
if (url) {
console.log("Redirecting " + req.url + " to " + url);
event.respondWith(Response.redirect(url));
}
}
});
// Ctrl + F5 bypasses service worker. the pretty extension URLs will fail to
// resolve in that case. Catch this and redirect to destination.
chrome.webNavigation.onErrorOccurred.addListener(
details => {
if (details.frameId !== 0) {
// Not a top-level frame. Cannot easily navigate a specific child frame.
return;
}
const url = resolveViewerURL(details.url);
if (url) {
console.log(`Redirecting ${details.url} to ${url} (fallback)`);
chrome.tabs.update(details.tabId, { url });
console.log("Redirecting " + details.url + " to " + url);
return { redirectUrl: url };
}
return undefined;
},
{ url: [{ urlPrefix: CRX_BASE_URL }] }
{
types: ["main_frame", "sub_frame"],
urls: schemes.map(function (scheme) {
// Format: "chrome-extension://[EXTENSIONID]/<scheme>*"
return CRX_BASE_URL + scheme + "*";
}),
},
["blocking"]
);
// When session restore is used, viewer pages may be loaded before the
// webRequest event listener is attached (= page not found).
// Or the extension could have been crashed (OOM), leaving a sad tab behind.
// Reload these tabs.
chrome.tabs.query(
{
url: CRX_BASE_URL + "*:*",
},
function (tabsFromLastSession) {
for (const { id } of tabsFromLastSession) {
chrome.tabs.reload(id);
}
}
);
console.log("Set up extension URL router.");
Object.keys(localStorage).forEach(function (key) {
// The localStorage item is set upon unload by chromecom.js.
var parsedKey = /^unload-(\d+)-(true|false)-(.+)/.exec(key);
if (parsedKey) {
var timeStart = parseInt(parsedKey[1], 10);
var isHidden = parsedKey[2] === "true";
var url = parsedKey[3];
if (Date.now() - timeStart < 3000) {
// Is it a new item (younger than 3 seconds)? Assume that the extension
// just reloaded, so restore the tab (work-around for crbug.com/511670).
chrome.tabs.create({
url:
chrome.runtime.getURL("restoretab.html") +
"?" +
encodeURIComponent(url) +
"#" +
encodeURIComponent(localStorage.getItem(key)),
active: !isHidden,
});
}
localStorage.removeItem(key);
}
});
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,6 +1,6 @@
{
"minimum_chrome_version": "103",
"manifest_version": 3,
"minimum_chrome_version": "88",
"manifest_version": 2,
"name": "PDF Viewer",
"version": "PDFJSSCRIPT_VERSION",
"description": "Uses HTML5 to display PDF files directly in the browser.",
@ -10,52 +10,61 @@
"16": "icon16.png"
},
"permissions": [
"alarms",
"declarativeNetRequestWithHostAccess",
"fileBrowserHandler",
"webRequest",
"webRequestBlocking",
"<all_urls>",
"tabs",
"webNavigation",
"storage"
],
"host_permissions": ["<all_urls>"],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "file://*/*"],
"matches": ["http://*/*", "https://*/*", "ftp://*/*", "file://*/*"],
"run_at": "document_start",
"all_frames": true,
"css": ["contentstyle.css"],
"js": ["contentscript.js"]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"file_browser_handlers": [
{
"id": "open-as-pdf",
"default_title": "Open with PDF Viewer",
"file_filters": ["filesystem:*.pdf"]
}
],
"storage": {
"managed_schema": "preferences_schema.json"
},
"options_ui": {
"page": "options/options.html"
"page": "options/options.html",
"chrome_style": true
},
"options_page": "options/options.html",
"background": {
"service_worker": "background.js"
"page": "pdfHandler.html"
},
"page_action": {
"default_icon": {
"19": "icon19.png",
"38": "icon38.png"
},
"default_title": "Show PDF URL",
"default_popup": "pageActionPopup.html"
},
"incognito": "split",
"web_accessible_resources": [
{
"resources": [
"content/web/viewer.html",
"http:/*",
"https:/*",
"file:/*",
"chrome-extension:/*",
"blob:*",
"data:*",
"filesystem:/*",
"drive:*"
],
"matches": ["<all_urls>"],
"extension_ids": ["*"]
}
"content/web/viewer.html",
"http:/*",
"https:/*",
"ftp:/*",
"file:/*",
"chrome-extension:/*",
"blob:*",
"data:*",
"filesystem:/*",
"drive:*"
]
}

View File

@ -13,14 +13,10 @@ 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";
/* eslint strict: ["error", "function"] */
chrome.runtime.onInstalled.addListener(({ reason }) => {
if (reason !== "update") {
// We only need to run migration logic for extension updates, not for new
// installs or browser updates.
return;
}
(function () {
"use strict";
var storageLocal = chrome.storage.local;
var storageSync = chrome.storage.sync;
@ -41,12 +37,16 @@ chrome.runtime.onInstalled.addListener(({ reason }) => {
});
});
async function getStorageNames(callback) {
function getStorageNames(callback) {
var x = new XMLHttpRequest();
var schema_location = chrome.runtime.getManifest().storage.managed_schema;
var res = await fetch(chrome.runtime.getURL(schema_location));
var storageManifest = await res.json();
var storageKeys = Object.keys(storageManifest.properties);
callback(storageKeys);
x.open("get", chrome.runtime.getURL(schema_location));
x.onload = function () {
var storageKeys = Object.keys(x.response.properties);
callback(storageKeys);
};
x.responseType = "json";
x.send();
}
// Save |values| to storage.sync and delete the values with that key from
@ -150,4 +150,4 @@ chrome.runtime.onInstalled.addListener(({ reason }) => {
}
);
}
});
})();

View File

@ -15,171 +15,166 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<meta charset="utf-8" />
<title>PDF.js viewer options</title>
<style>
body {
min-width: 400px; /* a page at the settings page is at least 400px wide */
margin: 14px 17px; /* already added by default in Chrome 40.0.2212.0 */
}
.settings-row {
margin: 1em 0;
}
.checkbox label {
display: inline-flex;
align-items: center;
}
.checkbox label input {
flex-shrink: 0;
}
</style>
</head>
<body>
<div id="settings-boxes"></div>
<button id="reset-button" type="button">Restore default settings</button>
<head>
<meta charset="utf-8">
<title>PDF.js viewer options</title>
<style>
/* TODO: Remove as much custom CSS as possible - crbug.com/446511 */
body {
min-width: 400px; /* a page at the settings page is at least 400px wide */
margin: 14px 17px; /* already added by default in Chrome 40.0.2212.0 */
}
.settings-row {
margin: 0.65em 0;
}
</style>
</head>
<body>
<div id="settings-boxes"></div>
<button id="reset-button">Restore default settings</button>
<template id="checkbox-template">
<div class="settings-row checkbox">
<label>
<input type="checkbox" />
<span></span>
</label>
</div>
</template>
<template id="checkbox-template">
<!-- Chromium's style: //src/extensions/renderer/resources/extension.css -->
<div class="checkbox">
<label>
<input type="checkbox">
<span></span>
</label>
</div>
</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="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">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">Show previous position</option>
<option value="1">Show initial position</option>
</select>
</label>
</div>
</template>
<template id="viewOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">Show previous position</option>
<option value="1">Show initial position</option>
</select>
</label>
</div>
</template>
<template id="defaultZoomValue-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="auto" selected="selected">Automatic Zoom</option>
<option value="page-actual">Actual Size</option>
<option value="page-fit">Page Fit</option>
<option value="page-width">Page Width</option>
<option value="custom" class="custom-zoom" hidden></option>
<option value="50">50%</option>
<option value="75">75%</option>
<option value="100">100%</option>
<option value="125">125%</option>
<option value="150">150%</option>
<option value="200">200%</option>
<option value="300">300%</option>
<option value="400">400%</option>
</select>
</label>
</div>
</template>
<template id="defaultZoomValue-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="auto" selected="selected">Automatic Zoom</option>
<option value="page-actual">Actual Size</option>
<option value="page-fit">Page Fit</option>
<option value="page-width">Page Width</option>
<option value="custom" class="custom-zoom" hidden></option>
<option value="50">50%</option>
<option value="75">75%</option>
<option value="100">100%</option>
<option value="125">125%</option>
<option value="150">150%</option>
<option value="200">200%</option>
<option value="300">300%</option>
<option value="400">400%</option>
</select>
</label>
</div>
</template>
<template id="sidebarViewOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">Do not show sidebar</option>
<option value="1">Show thumbnails in sidebar</option>
<option value="2">Show document outline in sidebar</option>
<option value="3">Show attachments in sidebar</option>
</select>
</label>
</div>
</template>
<template id="sidebarViewOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">Do not show sidebar</option>
<option value="1">Show thumbnails in sidebar</option>
<option value="2">Show document outline in sidebar</option>
<option value="3">Show attachments in sidebar</option>
</select>
</label>
</div>
</template>
<template id="cursorToolOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Text selection tool</option>
<option value="1">Hand tool</option>
</select>
</label>
</div>
</template>
<template id="cursorToolOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Text selection tool</option>
<option value="1">Hand tool</option>
</select>
</label>
</div>
</template>
<template id="textLayerMode-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Disable text selection</option>
<option value="1">Enable text selection</option>
</select>
</label>
</div>
</template>
<template id="textLayerMode-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Disable text selection</option>
<option value="1">Enable text selection</option>
</select>
</label>
</div>
</template>
<template id="externalLinkTarget-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Default</option>
<option value="1">Current window/tab</option>
<option value="2">New window/tab</option>
<option value="3">Parent window/tab</option>
<option value="4">Top window/tab</option>
</select>
</label>
</div>
</template>
<template id="externalLinkTarget-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="0">Default</option>
<option value="1">Current window/tab</option>
<option value="2">New window/tab</option>
<option value="3">Parent window/tab</option>
<option value="4">Top window/tab</option>
</select>
</label>
</div>
</template>
<template id="scrollModeOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="3">Page scrolling</option>
<option value="0">Vertical scrolling</option>
<option value="1">Horizontal scrolling</option>
<option value="2">Wrapped scrolling</option>
</select>
</label>
</div>
</template>
<template id="scrollModeOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="3">Page scrolling</option>
<option value="0">Vertical scrolling</option>
<option value="1">Horizontal scrolling</option>
<option value="2">Wrapped scrolling</option>
</select>
</label>
</div>
</template>
<template id="spreadModeOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">No spreads</option>
<option value="1">Odd spreads</option>
<option value="2">Even spreads</option>
</select>
</label>
</div>
</template>
<template id="spreadModeOnLoad-template">
<div class="settings-row">
<label>
<span></span>
<select>
<option value="-1">Default</option>
<option value="0">No spreads</option>
<option value="1">Odd spreads</option>
<option value="2">Even spreads</option>
</select>
</label>
</div>
</template>
<script src="options.js"></script>
</body>
<script src="options.js"></script>
</body>
</html>

View File

@ -0,0 +1,45 @@
/*
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";
(function PageActionClosure() {
/**
* @param {number} tabId - ID of tab where the page action will be shown.
* @param {string} url - URL to be displayed in page action.
*/
function showPageAction(tabId, displayUrl) {
// rewriteUrlClosure in viewer.js ensures that the URL looks like
// chrome-extension://[extensionid]/http://example.com/file.pdf
var url = /^chrome-extension:\/\/[a-p]{32}\/([^#]+)/.exec(displayUrl);
if (url) {
url = url[1];
chrome.pageAction.setPopup({
tabId,
popup: "/pageAction/popup.html?file=" + encodeURIComponent(url),
});
chrome.pageAction.show(tabId);
} else {
console.log("Unable to get PDF url from " + displayUrl);
}
}
chrome.runtime.onMessage.addListener(function (message, sender) {
if (message === "showPageAction" && sender.tab) {
showPageAction(sender.tab.id, sender.tab.url);
}
});
})();

View File

@ -0,0 +1,44 @@
<!doctype html>
<!--
Copyright 2012 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
html {
/* maximum width of popup as defined in Chromium's source code as kMaxWidth
//src/chrome/browser/ui/views/extensions/extension_popup.cc
//src/chrome/browser/ui/gtk/extensions/extension_popup_gtk.cc
*/
width: 800px;
/* in case Chromium decides to lower the value of kMaxWidth */
max-width: 100%;
margin: 0;
padding: 0;
}
body {
box-sizing: border-box;
margin: 0;
padding: 5px;
width: 100%;
}
</style>
</head>
<body contentEditable="plaintext-only" spellcheck="false">
<script src="popup.js"></script>
</body>
</html>

View File

@ -0,0 +1,25 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var url = location.search.match(/[&?]file=([^&]+)/i);
if (url) {
url = decodeURIComponent(url[1]);
document.body.textContent = url;
// Set cursor to end of the content-editable section.
window.getSelection().selectAllChildren(document.body);
window.getSelection().collapseToEnd();
}

View File

@ -0,0 +1,102 @@
/*
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.
*/
/* eslint strict: ["error", "function"] */
/* import-globals-from pdfHandler.js */
(function () {
"use strict";
if (!chrome.fileBrowserHandler) {
// Not on Chromium OS, bail out
return;
}
chrome.fileBrowserHandler.onExecute.addListener(onExecuteFileBrowserHandler);
/**
* Invoked when "Open with PDF Viewer" is chosen in the File browser.
*
* @param {string} id File browser action ID as specified in
* manifest.json
* @param {Object} details Object of type FileHandlerExecuteEventDetails
*/
function onExecuteFileBrowserHandler(id, details) {
if (id !== "open-as-pdf") {
return;
}
var fileEntries = details.entries;
// "tab_id" is the currently documented format, but it is inconsistent with
// the other Chrome APIs that use "tabId" (http://crbug.com/179767)
var tabId = details.tab_id || details.tabId;
if (tabId > 0) {
chrome.tabs.get(tabId, function (tab) {
openViewer(tab && tab.windowId, fileEntries);
});
} else {
// Re-use existing window, if available.
chrome.windows.getLastFocused(function (chromeWindow) {
var windowId = chromeWindow && chromeWindow.id;
if (windowId) {
chrome.windows.update(windowId, { focused: true });
}
openViewer(windowId, fileEntries);
});
}
}
/**
* Open the PDF Viewer for the given list of PDF files.
*
* @param {number} windowId
* @param {Array} fileEntries List of Entry objects (HTML5 FileSystem API)
*/
function openViewer(windowId, fileEntries) {
if (!fileEntries.length) {
return;
}
var fileEntry = fileEntries.shift();
var url = fileEntry.toURL();
// Use drive: alias to get shorter (more human-readable) URLs.
url = url.replace(
/^filesystem:chrome-extension:\/\/[a-p]{32}\/external\//,
"drive:"
);
url = getViewerURL(url);
if (windowId) {
chrome.tabs.create(
{
windowId,
active: true,
url,
},
function () {
openViewer(windowId, fileEntries);
}
);
} else {
chrome.windows.create(
{
type: "normal",
focused: true,
url,
},
function (chromeWindow) {
openViewer(chromeWindow.id, fileEntries);
}
);
}
}
})();

View File

@ -0,0 +1,24 @@
<!doctype html>
<!--
Copyright 2012 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script src="options/migration.js"></script>
<script src="preserve-referer.js"></script>
<script src="pdfHandler.js"></script>
<script src="extension-router.js"></script>
<script src="pdfHandler-vcros.js"></script>
<script src="pageAction/background.js"></script>
<script src="suppress-update.js"></script>
<script src="telemetry.js"></script>

View File

@ -13,256 +13,11 @@ 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.
*/
/* globals canRequestBody */ // From preserve-referer.js
/* import-globals-from preserve-referer.js */
"use strict";
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
// Use in-memory storage to ensure that the DNR rules have been registered at
// least once per session. runtime.onInstalled would have been the most fitting
// event to ensure that, except there are cases where it does not fire when
// needed. E.g. in incognito mode: https://issues.chromium.org/issues/41029550
chrome.storage.session.get({ hasPdfRedirector: false }, async items => {
if (items?.hasPdfRedirector) {
return;
}
const rules = await chrome.declarativeNetRequest.getDynamicRules();
if (rules.length) {
// Dynamic rules persist across extension updates. We don't expect other
// dynamic rules, so just remove them all.
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: rules.map(r => r.id),
});
}
await registerPdfRedirectRule();
// Only set the flag in the end, so that we know for sure that all
// asynchronous initialization logic has run. If not, then we will run the
// logic again at the next background wakeup.
chrome.storage.session.set({ hasPdfRedirector: true });
});
/**
* Registers declarativeNetRequest rules to redirect PDF requests to the viewer.
* The caller should clear any previously existing dynamic DNR rules.
*
* The logic here is the declarative version of the runtime logic in the
* webRequest.onHeadersReceived implementation at
* https://github.com/mozilla/pdf.js/blob/0676ea19cf17023ec8c2d6ad69a859c345c01dc1/extensions/chromium/pdfHandler.js#L34-L152
*/
async function registerPdfRedirectRule() {
// "allow" means to ignore rules (from this extension) with lower priority.
const ACTION_IGNORE_OTHER_RULES = { type: "allow" };
// Redirect to viewer. The rule condition is expected to specify regexFilter
// that matches the full request URL.
const ACTION_REDIRECT_TO_VIEWER = {
type: "redirect",
redirect: {
// DNR does not support transformations such as encodeURIComponent on the
// match, so we just concatenate the URL as is without modifications.
// TODO: use "?file=\\0" when DNR supports transformations as proposed at
// https://github.com/w3c/webextensions/issues/636#issuecomment-2165978322
regexSubstitution: VIEWER_URL + "?DNR:\\0",
},
};
// Rules in order of priority (highest priority rule first).
// The required "id" fields will be auto-generated later.
const addRules = [
{
// Do not redirect for URLs containing pdfjs.action=download.
condition: {
urlFilter: "pdfjs.action=download",
resourceTypes: ["main_frame", "sub_frame"],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// Redirect local PDF files if isAllowedFileSchemeAccess is true. No-op
// otherwise and then handled by webNavigation.onBeforeNavigate below.
condition: {
regexFilter: "^file://.*\\.pdf$",
resourceTypes: ["main_frame", "sub_frame"],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Respect the Content-Disposition:attachment header in sub_frame. But:
// Display the PDF viewer regardless of the Content-Disposition header if
// the file is displayed in the main frame, since most often users want to
// view a PDF, and servers are often misconfigured.
condition: {
urlFilter: "*",
resourceTypes: ["sub_frame"], // Note: no main_frame, handled below.
responseHeaders: [
{
header: "content-disposition",
values: ["attachment*"],
},
],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// If the query string contains "=download", do not unconditionally force
// viewer to open the PDF, but first check whether the Content-Disposition
// header specifies an attachment. This allows sites like Google Drive to
// operate correctly (#6106).
condition: {
urlFilter: "=download",
resourceTypes: ["main_frame"], // No sub_frame, was handled before.
responseHeaders: [
{
header: "content-disposition",
values: ["attachment*"],
},
],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// Regular http(s) PDF requests.
condition: {
regexFilter: "^.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-type",
values: ["application/pdf", "application/pdf;*"],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Wrong MIME-type, but a PDF file according to the file name in the URL.
condition: {
regexFilter: "^.*\\.pdf\\b.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-type",
values: ["application/octet-stream", "application/octet-stream;*"],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Wrong MIME-type, but a PDF file according to Content-Disposition.
condition: {
regexFilter: "^.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-disposition",
values: ["*.pdf", '*.pdf"*', "*.pdf'*"],
},
],
// We only want to match by content-disposition if Content-Type is set
// to application/octet-stream. The responseHeaders condition is a
// logical OR instead of AND, so to simulate the AND condition we use
// the double negation of excludedResponseHeaders + excludedValues.
// This matches any request whose content-type header is set and not
// "application/octet-stream". It will also match if "content-type" is
// not set, but we are okay with that since the browser would usually
// try to sniff the MIME type in that case.
excludedResponseHeaders: [
{
header: "content-type",
excludedValues: [
"application/octet-stream",
"application/octet-stream;*",
],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
];
for (const [i, rule] of addRules.entries()) {
// id must be unique and at least 1, but i starts at 0. So add +1.
rule.id = i + 1;
rule.priority = addRules.length - i;
}
try {
// Note: condition.responseHeaders is only supported in Chrome 128+, but
// does not trigger errors in Chrome 123 - 127 as explained at:
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
// We need to detect this and avoid registering rules, because otherwise all
// requests are redirected to the viewer instead of just PDF requests,
// because Chrome accepts rules while ignoring the responseHeaders condition
// - also reported at https://crbug.com/347186592
if (!(await isHeaderConditionSupported())) {
throw new Error("DNR responseHeaders condition is not supported.");
}
await chrome.declarativeNetRequest.updateDynamicRules({ addRules });
} catch (e) {
// When we do not register DNR rules for any reason, fall back to catching
// PDF documents via maybeRenderPdfDoc in contentscript.js.
console.error("Failed to register rules to redirect PDF requests.");
console.error(e);
}
}
// For the source and explanation of this logic, see
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
async function isHeaderConditionSupported() {
const ruleId = 123456; // Some rule ID that is not already used elsewhere.
try {
// Throws synchronously if not supported.
await chrome.declarativeNetRequest.updateSessionRules({
addRules: [
{
id: ruleId,
condition: {
responseHeaders: [{ header: "whatever" }],
urlFilter: "|does_not_match_anything",
},
action: { type: "block" },
},
],
});
} catch {
return false; // responseHeaders condition not supported.
}
// Chrome may recognize the properties but have the implementation behind a
// flag. When the implementation is disabled, validation is skipped too.
try {
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [ruleId],
addRules: [
{
id: ruleId,
condition: {
responseHeaders: [],
urlFilter: "|does_not_match_anything",
},
action: { type: "block" },
},
],
});
return false; // Validation skipped = feature disabled.
} catch {
return true; // Validation worked = feature enabled.
} finally {
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [ruleId],
});
}
}
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
function getViewerURL(pdf_url) {
// |pdf_url| may contain a fragment such as "#page=2". That should be passed
@ -276,42 +31,174 @@ function getViewerURL(pdf_url) {
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url) + hash;
}
// If the user has not granted access to file:-URLs, then declarativeNetRequest
// will not catch the request. It is still visible through the webNavigation
// API though, and we can replace the tab with the viewer.
// The viewer will detect that it has no access to file:-URLs, and prompt the
// user to activate file permissions.
chrome.webNavigation.onBeforeNavigate.addListener(
function (details) {
// Note: pdfjs.action=download is not checked here because that code path
// is not reachable for local files through the viewer when we do not have
// file:-access.
if (details.frameId === 0) {
chrome.extension.isAllowedFileSchemeAccess(function (isAllowedAccess) {
if (isAllowedAccess) {
// Expected to be handled by DNR. Don't do anything.
return;
}
/**
* @param {Object} details First argument of the webRequest.onHeadersReceived
* event. The property "url" is read.
* @returns {boolean} True if the PDF file should be downloaded.
*/
function isPdfDownloadable(details) {
if (details.url.includes("pdfjs.action=download")) {
return true;
}
// Display the PDF viewer regardless of the Content-Disposition header if the
// file is displayed in the main frame, since most often users want to view
// a PDF, and servers are often misconfigured.
// If the query string contains "=download", do not unconditionally force the
// viewer to open the PDF, but first check whether the Content-Disposition
// header specifies an attachment. This allows sites like Google Drive to
// operate correctly (#6106).
if (details.type === "main_frame" && !details.url.includes("=download")) {
return false;
}
var cdHeader =
details.responseHeaders &&
getHeaderFromHeaders(details.responseHeaders, "content-disposition");
return cdHeader && /^attachment/i.test(cdHeader.value);
}
/**
* Get the header from the list of headers for a given name.
* @param {Array} headers responseHeaders of webRequest.onHeadersReceived
* @returns {undefined|{name: string, value: string}} The header, if found.
*/
function getHeaderFromHeaders(headers, headerName) {
for (const header of headers) {
if (header.name.toLowerCase() === headerName) {
return header;
}
}
return undefined;
}
/**
* Check if the request is a PDF file.
* @param {Object} details First argument of the webRequest.onHeadersReceived
* event. The properties "responseHeaders" and "url"
* are read.
* @returns {boolean} True if the resource is a PDF file.
*/
function isPdfFile(details) {
var header = getHeaderFromHeaders(details.responseHeaders, "content-type");
if (header) {
var headerValue = header.value.toLowerCase().split(";", 1)[0].trim();
if (headerValue === "application/pdf") {
return true;
}
if (headerValue === "application/octet-stream") {
if (details.url.toLowerCase().indexOf(".pdf") > 0) {
return true;
}
var cdHeader = getHeaderFromHeaders(
details.responseHeaders,
"content-disposition"
);
if (cdHeader && /\.pdf(["']|$)/i.test(cdHeader.value)) {
return true;
}
}
}
return false;
}
/**
* Takes a set of headers, and set "Content-Disposition: attachment".
* @param {Object} details First argument of the webRequest.onHeadersReceived
* event. The property "responseHeaders" is read and
* modified if needed.
* @returns {Object|undefined} The return value for the onHeadersReceived event.
* Object with key "responseHeaders" if the headers
* have been modified, undefined otherwise.
*/
function getHeadersWithContentDispositionAttachment(details) {
var headers = details.responseHeaders;
var cdHeader = getHeaderFromHeaders(headers, "content-disposition");
if (!cdHeader) {
cdHeader = { name: "Content-Disposition" };
headers.push(cdHeader);
}
if (!/^attachment/i.test(cdHeader.value)) {
cdHeader.value = "attachment" + cdHeader.value.replace(/^[^;]+/i, "");
return { responseHeaders: headers };
}
return undefined;
}
chrome.webRequest.onHeadersReceived.addListener(
function (details) {
if (details.method !== "GET") {
// Don't intercept POST requests until http://crbug.com/104058 is fixed.
return undefined;
}
if (!isPdfFile(details)) {
return undefined;
}
if (isPdfDownloadable(details)) {
// Force download by ensuring that Content-Disposition: attachment is set
return getHeadersWithContentDispositionAttachment(details);
}
var viewerUrl = getViewerURL(details.url);
// Implemented in preserve-referer.js
saveReferer(details);
return { redirectUrl: viewerUrl };
},
{
urls: ["<all_urls>"],
types: ["main_frame", "sub_frame"],
},
["blocking", "responseHeaders"]
);
chrome.webRequest.onBeforeRequest.addListener(
function (details) {
if (isPdfDownloadable(details)) {
return undefined;
}
var viewerUrl = getViewerURL(details.url);
return { redirectUrl: viewerUrl };
},
{
urls: ["file://*/*.pdf", "file://*/*.PDF"],
types: ["main_frame", "sub_frame"],
},
["blocking"]
);
chrome.extension.isAllowedFileSchemeAccess(function (isAllowedAccess) {
if (isAllowedAccess) {
return;
}
// If the user has not granted access to file:-URLs, then the webRequest API
// will not catch the request. It is still visible through the webNavigation
// API though, and we can replace the tab with the viewer.
// The viewer will detect that it has no access to file:-URLs, and prompt the
// user to activate file permissions.
chrome.webNavigation.onBeforeNavigate.addListener(
function (details) {
if (details.frameId === 0 && !isPdfDownloadable(details)) {
chrome.tabs.update(details.tabId, {
url: getViewerURL(details.url),
});
});
}
},
{
url: [
{
urlPrefix: "file://",
pathSuffix: ".pdf",
},
{
urlPrefix: "file://",
pathSuffix: ".PDF",
},
],
}
},
{
url: [
{
urlPrefix: "file://",
pathSuffix: ".pdf",
},
{
urlPrefix: "file://",
pathSuffix: ".PDF",
},
],
}
);
);
});
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message && message.action === "getParentOrigin") {
@ -358,11 +245,6 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
url,
});
}
return undefined;
}
if (message && message.action === "canRequestBody") {
sendResponse(canRequestBody(sender.tab.id, sender.frameId));
return undefined;
}
return undefined;
});

View File

@ -45,41 +45,7 @@
"type": "boolean",
"default": false
},
"enableHWA": {
"title": "Enable hardware acceleration",
"description": "Whether to enable hardware acceleration.",
"type": "boolean",
"default": true
},
"enableAltText": {
"type": "boolean",
"default": false
},
"enableGuessAltText": {
"type": "boolean",
"default": true
},
"enableAltTextModelDownload": {
"type": "boolean",
"default": true
},
"enableNewAltTextWhenAddingImage": {
"type": "boolean",
"default": true
},
"altTextLearnMoreUrl": {
"type": "string",
"default": ""
},
"commentLearnMoreUrl": {
"type": "string",
"default": ""
},
"enableSignatureEditor": {
"type": "boolean",
"default": false
},
"enableUpdatedAddImage": {
"enableML": {
"type": "boolean",
"default": false
},
@ -102,13 +68,21 @@
"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,yellow_HCM=#FFFFCC,green_HCM=#53FFBC,blue_HCM=#80EBFF,pink_HCM=#F6B8FF,red_HCM=#C50043"
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
},
"enableStampEditor": {
"type": "boolean",
"default": true
},
"disableRange": {
"title": "Disable range requests",
@ -176,10 +150,6 @@
"enum": [-1, 0, 3, 15],
"default": 0
},
"capCanvasAreaFactor": {
"type": "integer",
"default": 200
},
"enablePermissions": {
"type": "boolean",
"default": false
@ -232,21 +202,6 @@
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
"type": "string",
"default": "CanvasText"
},
"enableAutoLinking": {
"description": "Enable creation of hyperlinks from text that look like URLs.",
"type": "boolean",
"default": true
},
"enableComment": {
"description": "Enable creation of comment annotations.",
"type": "boolean",
"default": false
},
"enableOptimizedPartialRendering": {
"description": "Enable tracking of PDF operations to optimize partial rendering.",
"type": "boolean",
"default": false
}
}
}

View File

@ -13,14 +13,20 @@ 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-globals-from pdfHandler.js */
/* exported saveReferer */
"use strict";
/**
* This file is one part of the Referer persistency implementation. The other
* part resides in chromecom.js.
*
* This file collects Referer headers for every http(s) request, and temporarily
* stores the request headers in a dictionary, for REFERRER_IN_MEMORY_TIME ms.
* This file collects request headers for every http(s) request, and temporarily
* stores the request headers in a dictionary. Upon completion of the request
* (success or failure), the headers are discarded.
* pdfHandler.js will call saveReferer(details) when it is about to redirect to
* the viewer. Upon calling saveReferer, the Referer header is extracted from
* the request headers and saved.
*
* When the viewer is opened, it opens a port ("chromecom-referrer"). This port
* is used to set up the webRequest listeners that stick the Referer headers to
@ -30,64 +36,49 @@ limitations under the License.
* See setReferer in chromecom.js for more explanation of this logic.
*/
/* exported canRequestBody */ // Used in pdfHandler.js
// Remembers the request headers for every http(s) page request for the duration
// of the request.
var g_requestHeaders = {};
// g_referrers[tabId][frameId] = referrer of PDF frame.
var g_referrers = {};
var g_referrerTimers = {};
// The background script will eventually suspend after 30 seconds of inactivity.
// This can be delayed when extension events are firing. To prevent the data
// from being kept in memory for too long, cap the data duration to 5 minutes.
var REFERRER_IN_MEMORY_TIME = 300000;
// g_postRequests[tabId] = Set of frameId that were loaded via POST.
var g_postRequests = {};
var rIsReferer = /^referer$/i;
chrome.webRequest.onSendHeaders.addListener(
function saveReferer(details) {
const { tabId, frameId, requestHeaders, method } = details;
g_referrers[tabId] ??= {};
g_referrers[tabId][frameId] = requestHeaders.find(h =>
rIsReferer.test(h.name)
)?.value;
setCanRequestBody(tabId, frameId, method !== "GET");
forgetReferrerEventually(tabId);
},
{ urls: ["*://*/*"], types: ["main_frame", "sub_frame"] },
["requestHeaders", "extraHeaders"]
);
function forgetReferrerEventually(tabId) {
if (g_referrerTimers[tabId]) {
clearTimeout(g_referrerTimers[tabId]);
(function () {
var requestFilter = {
urls: ["*://*/*"],
types: ["main_frame", "sub_frame"],
};
chrome.webRequest.onSendHeaders.addListener(
function (details) {
g_requestHeaders[details.requestId] = details.requestHeaders;
},
requestFilter,
["requestHeaders", "extraHeaders"]
);
chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
function forgetHeaders(details) {
delete g_requestHeaders[details.requestId];
}
g_referrerTimers[tabId] = setTimeout(() => {
delete g_referrers[tabId];
delete g_referrerTimers[tabId];
delete g_postRequests[tabId];
}, REFERRER_IN_MEMORY_TIME);
}
})();
// Keeps track of whether a document in tabId + frameId is loaded through a
// POST form submission. Although this logic has nothing to do with referrer
// tracking, it is still here to enable re-use of the webRequest listener above.
function setCanRequestBody(tabId, frameId, isPOST) {
if (isPOST) {
g_postRequests[tabId] ??= new Set();
g_postRequests[tabId].add(frameId);
} else {
g_postRequests[tabId]?.delete(frameId);
/**
* @param {object} details - onHeadersReceived event data.
*/
function saveReferer(details) {
var referer =
g_requestHeaders[details.requestId] &&
getHeaderFromHeaders(g_requestHeaders[details.requestId], "referer");
referer = (referer && referer.value) || "";
if (!g_referrers[details.tabId]) {
g_referrers[details.tabId] = {};
}
g_referrers[details.tabId][details.frameId] = referer;
}
function canRequestBody(tabId, frameId) {
// Returns true unless the frame is known to be loaded through a POST request.
// If the background suspends, the information may be lost. This is acceptable
// because the information is only potentially needed shortly after document
// load, by contentscript.js.
return !g_postRequests[tabId]?.has(frameId);
}
chrome.tabs.onRemoved.addListener(function (tabId) {
delete g_referrers[tabId];
});
// This method binds a webRequest event handler which adds the Referer header
// to matching PDF resource requests (only if the Referer is non-empty). The
@ -96,13 +87,15 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
if (port.name !== "chromecom-referrer") {
return;
}
// Note: sender.frameId is only set in Chrome 41+.
if (!("frameId" in port.sender)) {
port.disconnect();
return;
}
var tabId = port.sender.tab.id;
var frameId = port.sender.frameId;
var dnrRequestId;
// If the PDF is viewed for the first time, then the referer will be set here.
// Note: g_referrers could be empty if the background script was suspended by
// the browser. In that case, chromecom.js may send us the referer (below).
var referer = (g_referrers[tabId] && g_referrers[tabId][frameId]) || "";
port.onMessage.addListener(function (data) {
// If the viewer was opened directly (without opening a PDF URL first), then
@ -111,49 +104,80 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
if (data.referer) {
referer = data.referer;
}
dnrRequestId = data.dnrRequestId;
setStickyReferrer(dnrRequestId, tabId, data.requestUrl, referer, () => {
// Acknowledge the message, and include the latest referer for this frame.
port.postMessage(referer);
});
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
if (referer) {
// Only add a blocking request handler if the referer has to be rewritten.
chrome.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeaders,
{
urls: [data.requestUrl],
types: ["xmlhttprequest"],
tabId,
},
["blocking", "requestHeaders", "extraHeaders"]
);
}
// Acknowledge the message, and include the latest referer for this frame.
port.postMessage(referer);
});
// The port is only disconnected when the other end reloads.
port.onDisconnect.addListener(function () {
unsetStickyReferrer(dnrRequestId);
if (g_referrers[tabId]) {
delete g_referrers[tabId][frameId];
}
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
chrome.webRequest.onHeadersReceived.removeListener(exposeOnHeadersReceived);
});
});
function setStickyReferrer(dnrRequestId, tabId, url, referer, callback) {
if (!referer) {
unsetStickyReferrer(dnrRequestId);
callback();
return;
}
const rule = {
id: dnrRequestId,
condition: {
urlFilter: `|${url}|`,
// The viewer and background are presumed to have the same origin:
initiatorDomains: [location.hostname], // = chrome.runtime.id.
resourceTypes: ["xmlhttprequest"],
tabIds: [tabId],
// Expose some response headers for fetch API calls from PDF.js;
// This is a work-around for https://crbug.com/784528
chrome.webRequest.onHeadersReceived.addListener(
exposeOnHeadersReceived,
{
urls: ["https://*/*"],
types: ["xmlhttprequest"],
tabId,
},
action: {
type: "modifyHeaders",
requestHeaders: [{ operation: "set", header: "referer", value: referer }],
},
};
chrome.declarativeNetRequest.updateSessionRules(
{ removeRuleIds: [dnrRequestId], addRules: [rule] },
callback
["blocking", "responseHeaders"]
);
}
function unsetStickyReferrer(dnrRequestId) {
if (dnrRequestId) {
chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [dnrRequestId],
});
function onBeforeSendHeaders(details) {
if (details.frameId !== frameId) {
return undefined;
}
var headers = details.requestHeaders;
var refererHeader = getHeaderFromHeaders(headers, "referer");
if (!refererHeader) {
refererHeader = { name: "Referer" };
headers.push(refererHeader);
} else if (
refererHeader.value &&
refererHeader.value.lastIndexOf("chrome-extension:", 0) !== 0
) {
// Sanity check. If the referer is set, and the value is not the URL of
// this extension, then the request was not initiated by this extension.
return undefined;
}
refererHeader.value = referer;
return { requestHeaders: headers };
}
}
function exposeOnHeadersReceived(details) {
if (details.frameId !== frameId) {
return undefined;
}
var headers = details.responseHeaders;
var aceh = getHeaderFromHeaders(headers, "access-control-expose-headers");
// List of headers that PDF.js uses in src/display/network_utils.js
var acehValue =
"accept-ranges,content-encoding,content-length,content-disposition";
if (aceh) {
aceh.value += "," + acehValue;
} else {
aceh = { name: "Access-Control-Expose-Headers", value: acehValue };
headers.push(aceh);
}
return { responseHeaders: headers };
}
});

View File

@ -1,5 +1,6 @@
/*
Copyright 2024 Mozilla Foundation
<!doctype html>
<!--
Copyright 2015 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -12,15 +13,5 @@ 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";
importScripts(
"options/migration.js",
"preserve-referer.js",
"pdfHandler.js",
"extension-router.js",
"suppress-update.js",
"telemetry.js"
);
-->
<script src="restoretab.js"></script>

View File

@ -0,0 +1,31 @@
/*
Copyright 2015 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* This is part of the work-around for crbug.com/511670.
* - chromecom.js sets the URL and history state upon unload.
* - extension-router.js retrieves the saved state and opens restoretab.html
* - restoretab.html (this script) restores the URL and history state.
*/
"use strict";
var url = decodeURIComponent(location.search.slice(1));
var historyState = decodeURIComponent(location.hash.slice(1));
historyState = historyState === "undefined" ? null : JSON.parse(historyState);
history.replaceState(historyState, null, url);
location.reload();

View File

@ -20,10 +20,7 @@ limitations under the License.
// viewer is not displaying any PDF files. Otherwise the tabs would close, which
// is quite disruptive (crbug.com/511670).
chrome.runtime.onUpdateAvailable.addListener(function () {
chrome.tabs.query({ url: chrome.runtime.getURL("*") }, tabs => {
if (tabs?.length) {
return;
}
if (chrome.extension.getViews({ type: "tab" }).length === 0) {
chrome.runtime.reload();
});
}
});

View File

@ -42,35 +42,8 @@ limitations under the License.
return;
}
// The localStorage API is unavailable in service workers. We store data in
// chrome.storage.local and use this "localStorage" object to enable
// synchronous access in the logic.
const localStorage = {
telemetryLastTime: 0,
telemetryDeduplicationId: "",
telemetryLastVersion: "",
};
chrome.alarms.onAlarm.addListener(alarm => {
if (alarm.name === "maybeSendPing") {
maybeSendPing();
}
});
chrome.storage.session.get({ didPingCheck: false }, async items => {
if (items?.didPingCheck) {
return;
}
maybeSendPing();
await chrome.alarms.clear("maybeSendPing");
await chrome.alarms.create("maybeSendPing", { periodInMinutes: 60 });
chrome.storage.session.set({ didPingCheck: true });
});
function updateLocalStorage(key, value) {
localStorage[key] = value;
// Note: We mirror the data in localStorage because the following is async.
chrome.storage.local.set({ [key]: value });
}
maybeSendPing();
setInterval(maybeSendPing, 36e5);
function maybeSendPing() {
getLoggingPref(function (didOptOut) {
@ -88,20 +61,12 @@ limitations under the License.
// send more pings.
return;
}
doSendPing();
});
}
function doSendPing() {
chrome.storage.local.get(localStorage, items => {
Object.assign(localStorage, items);
var lastTime = parseInt(localStorage.telemetryLastTime) || 0;
var wasUpdated = didUpdateSinceLastCheck();
if (!wasUpdated && Date.now() - lastTime < MINIMUM_TIME_BETWEEN_PING) {
return;
}
updateLocalStorage("telemetryLastTime", Date.now());
localStorage.telemetryLastTime = Date.now();
var deduplication_id = getDeduplicationId(wasUpdated);
var extension_version = chrome.runtime.getManifest().version;
@ -139,7 +104,7 @@ limitations under the License.
for (const c of buf) {
id += (c >>> 4).toString(16) + (c & 0xf).toString(16);
}
updateLocalStorage("telemetryDeduplicationId", id);
localStorage.telemetryDeduplicationId = id;
}
return id;
}
@ -154,7 +119,7 @@ limitations under the License.
if (!chromeVersion || localStorage.telemetryLastVersion === chromeVersion) {
return false;
}
updateLocalStorage("telemetryLastVersion", chromeVersion);
localStorage.telemetryLastVersion = chromeVersion;
return true;
}

View File

@ -0,0 +1,22 @@
{
// Note: The root .eslintrc file will define the base rules,
// but mozilla/recommended will override them for the rules it sets. Finally,
// the rules in this file will take precedence.
"extends": [
"plugin:mozilla/recommended",
],
"plugins": [
"mozilla"
],
"rules": {
// Other rules mozilla/recommended hasn't enabled yet.
"no-shadow": "error",
"arrow-body-style": ["error", "as-needed"],
"arrow-parens": ["error", "always"],
"constructor-super": "error",
"no-confusing-arrow": "error",
"no-useless-constructor": "error",
},
}

View File

@ -1,4 +1,4 @@
/* Copyright 2024 Mozilla Foundation
/* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,16 +13,6 @@
* limitations under the License.
*/
import { InkDrawOutline } from "./inkdraw.js";
class ContourDrawOutline extends InkDrawOutline {
toSVGPath() {
let path = super.toSVGPath();
if (!path.endsWith("Z")) {
path += "Z";
}
return path;
}
}
export { ContourDrawOutline };
export const PdfJsDefaultPreferences = Object.freeze(
PDFJSDev.eval("DEFAULT_PREFERENCES")
);

9
external/.eslintrc vendored Normal file
View File

@ -0,0 +1,9 @@
{
"extends": [
../.eslintrc
],
"env": {
"node": true,
},
}

View File

@ -1,7 +1,10 @@
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;
@ -37,6 +40,18 @@ function handlePreprocessorAction(ctx, actionName, args, path) {
return result;
}
break;
case "json":
if (!t.isStringLiteral(arg)) {
throw new Error("Path to JSON is not provided");
}
let jsonPath = arg.value;
if (jsonPath.startsWith(ROOT_PREFIX)) {
jsonPath = joinPaths(
ctx.rootPath,
jsonPath.substring(ROOT_PREFIX.length)
);
}
return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
}
throw new Error("Unsupported action");
} catch (e) {
@ -47,25 +62,6 @@ function handlePreprocessorAction(ctx, actionName, args, path) {
}
function babelPluginPDFJSPreprocessor(babel, ctx) {
function removeUnusedFunctions(path) {
let removed;
do {
removed = false;
path.scope.crawl();
for (const name in path.scope.bindings) {
const binding = path.scope.bindings[name];
if (!binding.referenced) {
const { path: bindingPath } = binding;
if (bindingPath.isFunctionDeclaration()) {
bindingPath.remove();
removed = true;
}
}
}
// If we removed some functions, there might be new unused ones
} while (removed);
}
return {
name: "babel-plugin-pdfjs-preprocessor",
manipulateOptions({ parserOpts }) {
@ -172,27 +168,23 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
path.replaceWith(t.inherits(t.valueToNode(result), path.node));
}
if (t.isIdentifier(node.callee, { name: "__raw_import__" })) {
if (t.isIdentifier(node.callee, { name: "__non_webpack_import__" })) {
if (node.arguments.length !== 1) {
throw new Error("Invalid `__raw_import__` usage.");
throw new Error("Invalid `__non_webpack_import__` usage.");
}
// Replace it with a standard `import`-call and attempt to ensure that
// various bundlers will leave it alone; this *must* include Webpack.
// 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",
},
{
type: "CommentBlock",
value: "@vite-ignore",
},
];
path.replaceWith(t.importExpression(source));
}
},
"BlockStatement|StaticBlock": {
BlockStatement: {
// Visit node in post-order so that recursive flattening
// of blocks works correctly.
exit(path) {
@ -223,10 +215,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
}
subExpressionIndex++;
}
if (node.type === "StaticBlock" && node.body.length === 0) {
path.remove();
}
},
},
Function: {
@ -244,50 +232,8 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
// Function body ends with return without arg -- removing it.
body.pop();
}
removeUnusedFunctions(path);
},
},
ClassMethod: {
exit(path) {
const {
node,
parentPath: { parent: classNode },
} = path;
if (
// Remove empty constructors. We only do this for
// base classes, as the default constructor of derived
// classes is not empty (and an empty constructor
// must throw at runtime when constructed).
node.kind === "constructor" &&
node.body.body.length === 0 &&
node.params.every(p => p.type === "Identifier") &&
!classNode.superClass
) {
path.remove();
}
},
},
Program: {
exit(path) {
if (path.node.sourceType === "module") {
removeUnusedFunctions(path);
}
},
},
MemberExpression(path) {
// The Emscripten Compiler (emcc) generates code that allows the caller
// to provide the Wasm module (thorugh Module.instantiateWasm), with
// a fallback in case .instantiateWasm is not provided.
// We always define instantiateWasm, so we can hard-code the check
// and let our dead code elimination logic remove the unused fallback.
if (
path.parentPath.isIfStatement({ test: path.node }) &&
path.matchesPattern("Module.instantiateWasm")
) {
path.replaceWith(t.booleanLiteral(true));
}
},
},
};
}

View File

@ -151,7 +151,7 @@ function preprocess(inFilename, outFilename, defines) {
let state = STATE_NONE;
const stack = [];
const control =
/^(?:\/\/|\s*\/\*|\s*<!--)\s*#(if|elif|else|endif|expand|include|error)\b(?:\s+(.*?)(?:\*\/|-->)?$)?/;
/^(?:\/\/|\s*\/\*|<!--)\s*#(if|elif|else|endif|expand|include|error)\b(?:\s+(.*?)(?:\*\/|-->)?$)?/;
while ((line = readLine()) !== null) {
++lineNumber;
@ -213,7 +213,7 @@ function preprocess(inFilename, outFilename, defines) {
) {
writeLine(
line
.replaceAll(/^\/\/|^\s*<!--/g, " ")
.replaceAll(/^\/\/|^<!--/g, " ")
.replaceAll(/(^\s*)\/\*/g, "$1 ")
.replaceAll(/\*\/$|-->$/g, "")
);

View File

@ -1,23 +0,0 @@
class A {
constructor() {
console.log("Hi!");
}
}
class B {
constructor(x = console.log("Hi!")) {}
}
class C {
constructor({
x
}) {}
}
class D {}
class E extends A {
constructor() {}
}
class F {
constructor() {
var a = 0;
}
}
class G {}

View File

@ -1,37 +0,0 @@
class A {
constructor() {
console.log("Hi!");
}
}
class B {
constructor(x = console.log("Hi!")) {}
}
class C {
constructor({ x }) {}
}
class D {
constructor(x, y, z) {}
}
class E extends A {
constructor() {}
}
class F {
constructor() {
if (PDFJSDev.test('TRUE')) {
var a = 0;
}
}
}
class G {
constructor() {
if (PDFJSDev.test('FALSE')) {
var a = 0;
}
}
}

View File

@ -1,8 +0,0 @@
class A {
static {
foo();
}
static {
var a = 0;
}
}

View File

@ -1,20 +0,0 @@
class A {
static {}
static {
{ foo() }
}
static {
{;}
}
static {
if (PDFJSDev.test('TRUE')) {
var a = 0;
}
}
static {
if (PDFJSDev.test('FALSE')) {
var a = 1;
}
}
}

View File

@ -1,5 +0,0 @@
function usedByUsed() {}
function used() {
usedByUsed();
}
used();

View File

@ -1,14 +0,0 @@
function usedByUsed() {}
function usedByUnused() {}
function usedByRemovedCode() {}
function used() {
usedByUsed();
return;
usedByRemovedCode();
}
function unused() {
usedByUnused();
}
used();

View File

@ -8,4 +8,3 @@ function test() {
}
"4";
}
test();

View File

@ -17,4 +17,3 @@ function test() {
"4";
}
}
test();

View File

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

View File

@ -6,7 +6,6 @@ function f1() {
"2";
/* tail */
}
f1();
function f2() {
// head
@ -15,7 +14,6 @@ function f2() {
"2";
// tail
}
f2();
function f3() {
if ("1") { // begin block
@ -26,4 +24,3 @@ function f3() {
"4";
}
}
f3();

View File

@ -1,18 +1,14 @@
function f1() {}
f1();
function f2() {
return 1;
}
f2();
function f3() {
var i = 0;
throw "test";
}
f3();
function f4() {
var i = 0;
}
f4();
var obj = {
method1() {},
method2() {}

View File

@ -2,20 +2,17 @@ function f1() {
return;
var i = 0;
}
f1();
function f2() {
return 1;
var i = 0;
}
f2();
function f3() {
var i = 0;
throw "test";
var j = 0;
}
f3();
function f4() {
var i = 0;
@ -25,7 +22,6 @@ function f4() {
throw "test";
var j = 0;
}
f4();
var obj = {
method1() { return; var i = 0; },

View File

@ -10,6 +10,9 @@ var g = {
},
j: 2
};
var h = {
test: "test"
};
var i = '0';
var j = {
i: 1

View File

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

View File

@ -0,0 +1 @@
{ "test": "test" }

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