Compare commits
No commits in common. "master" and "v3.10.111" have entirely different histories.
@ -3,11 +3,12 @@ l10n/
|
||||
docs/
|
||||
node_modules/
|
||||
external/bcmaps/
|
||||
external/webL10n/
|
||||
external/builder/fixtures/
|
||||
external/builder/fixtures_babel/
|
||||
external/builder/fixtures_esprima/
|
||||
external/quickjs/
|
||||
test/stats/results/
|
||||
src/shared/cffStandardStrings.js
|
||||
src/shared/fonts_utils.js
|
||||
test/tmp/
|
||||
test/pdfs/
|
||||
web/locale/
|
||||
*~/
|
||||
265
.eslintrc
Normal file
265
.eslintrc
Normal file
@ -0,0 +1,265 @@
|
||||
{
|
||||
"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,
|
||||
"amd": true,
|
||||
},
|
||||
|
||||
"globals": {
|
||||
"PDFJSDev": false,
|
||||
"exports": false,
|
||||
},
|
||||
|
||||
"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-mutable-exports": "error",
|
||||
"import/no-self-import": "error",
|
||||
"import/no-unresolved": ["error", {
|
||||
"ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web"]
|
||||
}],
|
||||
"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-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-new": "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
|
||||
"constructor-super": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-confusing-arrow": "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",
|
||||
"sort-imports": ["error", {
|
||||
"ignoreCase": true,
|
||||
}],
|
||||
"template-curly-spacing": ["error", "never"],
|
||||
},
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
# Auto-format `.js` files with ESLint/Prettier
|
||||
de36b2aabab2b7fd647d9591f959c4540129541d
|
||||
# Auto-format `.css` files with Stylelint/Prettier
|
||||
8aa2718d225ad701a5b8a2788b42d221f1e4327d
|
||||
# Auto-format `.json` files with Prettier
|
||||
29de9bdce6c9785574994fda0e51533d796a9bb4
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -4,7 +4,7 @@
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.md text eol=lf
|
||||
*.ftl text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.json text eol=lf
|
||||
*.config text eol=lf
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1 +1 @@
|
||||
l10n/en-US/*.ftl @mozilla/pdfjs-l10n
|
||||
l10n/en-US/*.properties @mozilla/pdfjs-l10n
|
||||
|
||||
17
.github/ISSUE_TEMPLATE.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE.md
vendored
Normal 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):
|
||||
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -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?
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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
|
||||
23
.github/ISSUE_TEMPLATE/feature.yml
vendored
23
.github/ISSUE_TEMPLATE/feature.yml
vendored
@ -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
13
.github/SECURITY.md
vendored
@ -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:
|
||||
|
||||

|
||||
|
||||
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).
|
||||
27
.github/fluent_linter_config.yml
vendored
27
.github/fluent_linter_config.yml
vendored
@ -1,27 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# See https://github.com/mozilla-l10n/moz-fluent-linter/blob/main/src/fluent_linter/config.yml
|
||||
# for details
|
||||
|
||||
---
|
||||
ID01:
|
||||
enabled: true
|
||||
exclusions:
|
||||
messages: []
|
||||
files: []
|
||||
ID02:
|
||||
enabled: true
|
||||
min_length: 9
|
||||
exclusions:
|
||||
messages: []
|
||||
files: []
|
||||
CO01:
|
||||
enabled: true
|
||||
brands:
|
||||
- Firefox
|
||||
- Mozilla
|
||||
exclusions:
|
||||
files: []
|
||||
messages: []
|
||||
1
.github/requirements.txt
vendored
1
.github/requirements.txt
vendored
@ -1 +0,0 @@
|
||||
moz-fluent-linter==0.4.*
|
||||
BIN
.github/security.png
vendored
BIN
.github/security.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@ -8,27 +8,22 @@ jobs:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [20, 22, 23]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Use Node.js 18 LTS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Gulp
|
||||
run: npm install -g gulp-cli
|
||||
|
||||
- name: Run external tests
|
||||
run: npx gulp externaltest
|
||||
- name: Install other dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run CLI unit tests
|
||||
run: npx gulp unittestcli
|
||||
- name: Run tests
|
||||
run: gulp ci-test
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@ -18,18 +18,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: security-and-quality
|
||||
|
||||
- name: Autobuild CodeQL
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
42
.github/workflows/fluent_linter.yml
vendored
42
.github/workflows/fluent_linter.yml
vendored
@ -1,42 +0,0 @@
|
||||
name: Lint Fluent Reference Files
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'l10n/en-US/**.ftl'
|
||||
- '.github/fluent_linter_config.yml'
|
||||
- '.github/workflows/fluent_linter.yml'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- 'l10n/en-US/**.ftl'
|
||||
- '.github/fluent_linter_config.yml'
|
||||
- '.github/workflows/fluent_linter.yml'
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Fluent dependencies
|
||||
run: |
|
||||
pip install -r .github/requirements.txt
|
||||
|
||||
- name: Lint Fluent reference files
|
||||
run: |
|
||||
moz-fluent-lint ./l10n/en-US --config .github/fluent_linter_config.yml
|
||||
61
.github/workflows/font_tests.yml
vendored
61
.github/workflows/font_tests.yml
vendored
@ -1,61 +0,0 @@
|
||||
name: Font tests
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'gulpfile.mjs'
|
||||
- 'src/**'
|
||||
- 'test/test.mjs'
|
||||
- 'test/font/**'
|
||||
- '.github/workflows/font_tests.yml'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- 'gulpfile.mjs'
|
||||
- 'src/**'
|
||||
- 'test/test.mjs'
|
||||
- 'test/font/**'
|
||||
- '.github/workflows/font_tests.yml'
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Use Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Fonttools
|
||||
run: pip install fonttools
|
||||
|
||||
- name: Run font tests
|
||||
run: npx gulp fonttest --headless
|
||||
34
.github/workflows/lint.yml
vendored
34
.github/workflows/lint.yml
vendored
@ -1,34 +0,0 @@
|
||||
name: Lint
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run lint
|
||||
run: npx gulp lint
|
||||
|
||||
- name: Run lint-chromium
|
||||
run: npx gulp lint-chromium
|
||||
39
.github/workflows/publish_release.yml
vendored
39
.github/workflows/publish_release.yml
vendored
@ -1,39 +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@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
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 --provenance
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
25
.github/workflows/publish_website.yml
vendored
25
.github/workflows/publish_website.yml
vendored
@ -11,26 +11,25 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Use Node.js 18 LTS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Gulp
|
||||
run: npm install -g gulp-cli
|
||||
|
||||
- name: Install other dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build the website
|
||||
run: npx gulp web
|
||||
run: gulp web
|
||||
|
||||
- name: Archive the website
|
||||
shell: sh
|
||||
@ -49,7 +48,7 @@ jobs:
|
||||
INPUT_PATH: build/gh-pages
|
||||
|
||||
- name: Upload the website
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: github-pages
|
||||
path: ${{ runner.temp }}/website.tar
|
||||
@ -66,4 +65,4 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Deploy the website
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v2
|
||||
|
||||
31
.github/workflows/types_tests.yml
vendored
31
.github/workflows/types_tests.yml
vendored
@ -1,31 +0,0 @@
|
||||
name: Types tests
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run types tests
|
||||
run: npx gulp typestest
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "test/ttx/fonttools-code"]
|
||||
path = test/ttx/fonttools-code
|
||||
url = https://github.com/behdad/fonttools.git
|
||||
7
.gitpod.Dockerfile
vendored
Normal file
7
.gitpod.Dockerfile
vendored
Normal 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
13
.gitpod.yml
Normal 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
|
||||
11
.prettierrc
11
.prettierrc
@ -5,14 +5,5 @@
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false,
|
||||
|
||||
"overrides": [
|
||||
{
|
||||
files: ["tsconfig.json"],
|
||||
options: {
|
||||
parser: "json",
|
||||
},
|
||||
},
|
||||
]
|
||||
"useTabs": false
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"chrome": {
|
||||
"skipDownload": false
|
||||
},
|
||||
"firefox": {
|
||||
"skipDownload": false,
|
||||
"version": "nightly"
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,12 @@ l10n/
|
||||
docs/
|
||||
node_modules/
|
||||
external/bcmaps/
|
||||
external/webL10n/
|
||||
external/builder/fixtures/
|
||||
external/builder/fixtures_babel/
|
||||
external/builder/fixtures_esprima/
|
||||
external/quickjs/
|
||||
test/stats/results/
|
||||
src/shared/cffStandardStrings.js
|
||||
src/shared/fonts_utils.js
|
||||
test/tmp/
|
||||
test/pdfs/
|
||||
web/locale/
|
||||
*~/
|
||||
|
||||
@ -6,12 +6,7 @@
|
||||
"rules": {
|
||||
"prettier/prettier": true,
|
||||
|
||||
"alpha-value-notation": "number",
|
||||
"block-no-empty": true,
|
||||
"color-function-notation": "modern",
|
||||
"color-hex-length": "short",
|
||||
"color-no-invalid-hex": true,
|
||||
"declaration-block-no-duplicate-properties": true,
|
||||
"declaration-block-no-redundant-longhand-properties": true,
|
||||
"declaration-property-value-disallowed-list": {
|
||||
"float": ["inline-start", "inline-end"]
|
||||
|
||||
@ -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/**",
|
||||
"*~/**",
|
||||
],
|
||||
};
|
||||
5
AUTHORS
5
AUTHORS
@ -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>
|
||||
|
||||
24
README.md
24
README.md
@ -1,4 +1,4 @@
|
||||
# PDF.js [](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
# PDF.js [](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.
|
||||
|
||||
@ -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,19 @@ 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
|
||||
|
||||
If everything worked out, install all dependencies for PDF.js:
|
||||
|
||||
$ npm install
|
||||
|
||||
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 +81,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 +94,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 +111,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 +138,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
20
docs/config.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@ -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).
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
10
docs/contents/css/bootstrap.min.css
vendored
10
docs/contents/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -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;
|
||||
}
|
||||
|
||||
@ -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/js,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/js,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/js,html,css,result/"></script>
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
11
docs/contents/js/bootstrap.min.js
vendored
11
docs/contents/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
4
docs/contents/js/jquery-2.1.0.min.js
vendored
Normal file
4
docs/contents/js/jquery-2.1.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
docs/contents/js/jquery-3.7.1.min.js
vendored
2
docs/contents/js/jquery-3.7.1.min.js
vendored
File diff suppressed because one or more lines are too long
15
docs/plugins/wintersmith-makerelative.coffee
Normal file
15
docs/plugins/wintersmith-makerelative.coffee
Normal 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
52
docs/templates/layout.jade
vendored
Normal 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 ©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'))
|
||||
56
docs/templates/layout.njk
vendored
56
docs/templates/layout.njk
vendored
@ -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>© 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>
|
||||
@ -1,452 +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/quickjs/",
|
||||
"external/openjpeg/",
|
||||
"test/stats/results/",
|
||||
"test/tmp/",
|
||||
"test/pdfs/",
|
||||
"web/locale/",
|
||||
"web/wasm/",
|
||||
"**/*~/",
|
||||
],
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Base configuration
|
||||
\* ======================================================================== */
|
||||
|
||||
prettierRecommended,
|
||||
{
|
||||
files: ["**/*.json"],
|
||||
...json.configs.recommended,
|
||||
},
|
||||
{
|
||||
files: jsFiles("."),
|
||||
ignores: chromiumExtensionServiceWorkerFiles,
|
||||
languageOptions: {
|
||||
globals: globals.browser,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("."),
|
||||
|
||||
plugins: {
|
||||
import: import_.flatConfigs.recommended.plugins.import,
|
||||
json,
|
||||
"no-unsanitized": noUnsanitized,
|
||||
perfectionist,
|
||||
unicorn,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.worker,
|
||||
PDFJSDev: "readonly",
|
||||
__non_webpack_import__: "readonly",
|
||||
},
|
||||
|
||||
ecmaVersion: 2025,
|
||||
sourceType: "module",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"import/export": "error",
|
||||
"import/exports-last": "error",
|
||||
"import/extensions": ["error", "always", { ignorePackages: true }],
|
||||
"import/first": "error",
|
||||
"import/named": "error",
|
||||
"import/no-cycle": "error",
|
||||
"import/no-empty-named-blocks": "error",
|
||||
"import/no-commonjs": "error",
|
||||
"import/no-mutable-exports": "error",
|
||||
"import/no-restricted-paths": [
|
||||
"error",
|
||||
{
|
||||
zones: [
|
||||
{
|
||||
target: "./web",
|
||||
from: "./src",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"import/no-self-import": "error",
|
||||
"import/no-unresolved": [
|
||||
"error",
|
||||
{
|
||||
ignore: [
|
||||
"display",
|
||||
"pdfjs",
|
||||
"pdfjs-lib",
|
||||
"pdfjs-web",
|
||||
"web",
|
||||
"fluent-bundle",
|
||||
"fluent-dom",
|
||||
// See https://github.com/firebase/firebase-admin-node/discussions/1359.
|
||||
"eslint-plugin-perfectionist",
|
||||
],
|
||||
},
|
||||
],
|
||||
"no-unsanitized/method": "error",
|
||||
"no-unsanitized/property": "error",
|
||||
"perfectionist/sort-exports": "error",
|
||||
"perfectionist/sort-named-exports": "error",
|
||||
"unicorn/no-abusive-eslint-disable": "error",
|
||||
"unicorn/no-array-push-push": "error",
|
||||
"unicorn/no-console-spaces": "error",
|
||||
"unicorn/no-instanceof-builtins": "error",
|
||||
"unicorn/no-invalid-remove-event-listener": "error",
|
||||
"unicorn/no-new-buffer": "error",
|
||||
"unicorn/no-single-promise-in-promise-methods": "error",
|
||||
"unicorn/no-typeof-undefined": ["error", { checkGlobalVariables: false }],
|
||||
"unicorn/no-useless-promise-resolve-reject": "error",
|
||||
"unicorn/no-useless-spread": "error",
|
||||
"unicorn/prefer-array-find": "error",
|
||||
"unicorn/prefer-array-flat": "error",
|
||||
"unicorn/prefer-array-flat-map": "error",
|
||||
"unicorn/prefer-array-index-of": "error",
|
||||
"unicorn/prefer-array-some": "error",
|
||||
"unicorn/prefer-at": "error",
|
||||
"unicorn/prefer-date-now": "error",
|
||||
"unicorn/prefer-dom-node-append": "error",
|
||||
"unicorn/prefer-dom-node-remove": "error",
|
||||
"unicorn/prefer-includes": "error",
|
||||
"unicorn/prefer-logical-operator-over-ternary": "error",
|
||||
"unicorn/prefer-modern-dom-apis": "error",
|
||||
"unicorn/prefer-modern-math-apis": "error",
|
||||
"unicorn/prefer-negative-index": "error",
|
||||
"unicorn/prefer-optional-catch-binding": "error",
|
||||
"unicorn/prefer-regexp-test": "error",
|
||||
"unicorn/prefer-string-replace-all": "error",
|
||||
"unicorn/prefer-string-starts-ends-with": "error",
|
||||
"unicorn/prefer-ternary": ["error", "only-single-line"],
|
||||
"unicorn/throw-new-error": "error",
|
||||
|
||||
// Possible errors
|
||||
"for-direction": "error",
|
||||
"getter-return": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-cond-assign": ["error", "except-parens"],
|
||||
"no-constant-condition": ["error", { checkLoops: false }],
|
||||
"no-debugger": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-else-if": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-empty": ["error", { allowEmptyCatch: true }],
|
||||
"no-empty-character-class": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-loss-of-precision": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-promise-executor-return": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-setter-return": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unsafe-negation": "error",
|
||||
"no-unsafe-optional-chaining": [
|
||||
"error",
|
||||
{ disallowArithmeticOperators: true },
|
||||
],
|
||||
"no-unused-private-class-members": "error",
|
||||
"use-isnan": ["error", { enforceForIndexOf: true }],
|
||||
"valid-typeof": ["error", { requireStringLiterals: true }],
|
||||
|
||||
// Best Practices
|
||||
"accessor-pairs": [
|
||||
"error",
|
||||
{ setWithoutGet: true, enforceForClassMembers: true },
|
||||
],
|
||||
"consistent-return": "error",
|
||||
curly: ["error", "all"],
|
||||
"default-case-last": "error",
|
||||
"dot-notation": "error",
|
||||
eqeqeq: ["error", "always"],
|
||||
"grouped-accessor-pairs": ["error", "getBeforeSet"],
|
||||
"no-alert": "error",
|
||||
"no-caller": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty-pattern": "error",
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-global-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-iterator": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-str": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-symbol": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-octal": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-return-await": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-catch": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-escape": "error",
|
||||
"no-useless-return": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"prefer-spread": "error",
|
||||
"wrap-iife": ["error", "any"],
|
||||
yoda: ["error", "never", { exceptRange: true }],
|
||||
|
||||
// Strict Mode
|
||||
strict: ["off", "global"],
|
||||
|
||||
// Variables
|
||||
"no-delete-var": "error",
|
||||
"no-label-var": "error",
|
||||
"no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undef": ["error", { typeof: true }],
|
||||
"no-unused-vars": ["error", { vars: "all", args: "none" }],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{ functions: false, classes: false, variables: false },
|
||||
],
|
||||
|
||||
// Stylistic Issues
|
||||
"lines-between-class-members": ["error", "always"],
|
||||
"max-len": ["error", { code: 1000, comments: 80, ignoreUrls: true }],
|
||||
"new-cap": ["error", { newIsCap: true, capIsNew: false }],
|
||||
"no-array-constructor": "error",
|
||||
"no-multiple-empty-lines": ["error", { max: 1, maxEOF: 0, maxBOF: 1 }],
|
||||
"no-nested-ternary": "error",
|
||||
"no-new-object": "error",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"BinaryExpression[operator='instanceof'][right.name='Object']",
|
||||
message: "Use `typeof` rather than `instanceof Object`.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='assert'][arguments.length!=2]",
|
||||
message: "`assert()` must always be invoked with two arguments.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isCmd'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Cmd` rather than `isCmd()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isDict'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Dict` rather than `isDict()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isName'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Name` rather than `isName()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Cmd']",
|
||||
message: "Use `Cmd.get()` rather than `new Cmd()`.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Name']",
|
||||
message: "Use `Name.get()` rather than `new Name()`.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Ref']",
|
||||
message: "Use `Ref.get()` rather than `new Ref()`.",
|
||||
},
|
||||
],
|
||||
"no-unneeded-ternary": "error",
|
||||
"operator-assignment": "error",
|
||||
"prefer-exponentiation-operator": "error",
|
||||
"spaced-comment": ["error", "always", { block: { balanced: true } }],
|
||||
|
||||
// ECMAScript 6
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"constructor-super": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-rename": "error",
|
||||
"no-var": "error",
|
||||
"object-shorthand": ["error", "always", { avoidQuotes: true }],
|
||||
"prefer-const": "error",
|
||||
"require-yield": "error",
|
||||
"sort-imports": ["error", { ignoreCase: true }],
|
||||
"template-curly-spacing": ["error", "never"],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("src"),
|
||||
rules: {
|
||||
"no-console": "error",
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Test-specific rules
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("test"),
|
||||
|
||||
plugins: { jasmine },
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jasmine,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...jasmine.configs.recommended.rules,
|
||||
"jasmine/new-line-before-expect": "off",
|
||||
"jasmine/new-line-between-declarations": "off",
|
||||
"jasmine/no-focused-tests": "error",
|
||||
"jasmine/no-pending-tests": "off",
|
||||
"jasmine/no-spec-dupes": ["error", "branch"],
|
||||
"jasmine/no-suite-dupes": ["error", "branch"],
|
||||
"jasmine/prefer-jasmine-matcher": "off",
|
||||
"jasmine/prefer-toHaveBeenCalledWith": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("test/unit"),
|
||||
rules: {
|
||||
"import/no-unresolved": ["error", { ignore: ["pdfjs/"] }],
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("test/integration"),
|
||||
rules: {
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.name='waitForTimeout']",
|
||||
message:
|
||||
"`waitForTimeout` can cause intermittent failures and should not be used (see issue #17656 for replacements).",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
External libraries
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("external"),
|
||||
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Examples
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("examples"),
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
pdfjsImageDecoders: false,
|
||||
pdfjsLib: false,
|
||||
pdfjsViewer: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [...jsFiles("examples/node"), ...jsFiles("examples/webpack")],
|
||||
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Chromium extension
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("extensions/chromium"),
|
||||
|
||||
languageOptions: {
|
||||
globals: globals.webextensions,
|
||||
sourceType: "script",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"no-var": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: chromiumExtensionServiceWorkerFiles,
|
||||
|
||||
languageOptions: {
|
||||
globals: globals.serviceworker,
|
||||
sourceType: "script",
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Other
|
||||
\* ======================================================================== */
|
||||
{
|
||||
files: ["gulpfile.mjs"],
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
];
|
||||
11
examples/.eslintrc
Normal file
11
examples/.eslintrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"globals": {
|
||||
"pdfjsImageDecoders": false,
|
||||
"pdfjsLib": false,
|
||||
"pdfjsViewer": false,
|
||||
},
|
||||
}
|
||||
@ -31,13 +31,13 @@ limitations under the License.
|
||||
|
||||
<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>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
|
||||
</head>
|
||||
|
||||
<body tabindex="1">
|
||||
<div id="pageContainer" class="pdfViewer singlePageView"></div>
|
||||
|
||||
<script src="pageviewer.mjs" type="module"></script>
|
||||
<script src="pageviewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
|
||||
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFPageView) {
|
||||
// The workerSrc property shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
//
|
||||
@ -45,19 +47,20 @@ const loadingTask = pdfjsLib.getDocument({
|
||||
cMapPacked: CMAP_PACKED,
|
||||
enableXfa: ENABLE_XFA,
|
||||
});
|
||||
(async function () {
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Document loaded, retrieving the page.
|
||||
const pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW);
|
||||
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Document loaded, retrieving the page.
|
||||
const pdfPage = await pdfDocument.getPage(PAGE_TO_VIEW);
|
||||
|
||||
// Creating the page view with default parameters.
|
||||
const pdfPageView = new pdfjsViewer.PDFPageView({
|
||||
container,
|
||||
id: PAGE_TO_VIEW,
|
||||
scale: SCALE,
|
||||
defaultViewport: pdfPage.getViewport({ scale: SCALE }),
|
||||
eventBus,
|
||||
});
|
||||
// Associate the actual page with the view, and draw it.
|
||||
pdfPageView.setPdfPage(pdfPage);
|
||||
pdfPageView.draw();
|
||||
// Creating the page view with default parameters.
|
||||
const pdfPageView = new pdfjsViewer.PDFPageView({
|
||||
container,
|
||||
id: PAGE_TO_VIEW,
|
||||
scale: SCALE,
|
||||
defaultViewport: pdfPage.getViewport({ scale: SCALE }),
|
||||
eventBus,
|
||||
});
|
||||
// Associate the actual page with the view, and draw it.
|
||||
pdfPageView.setPdfPage(pdfPage);
|
||||
return pdfPageView.draw();
|
||||
})();
|
||||
@ -37,8 +37,8 @@ limitations under the License.
|
||||
|
||||
<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>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
|
||||
</head>
|
||||
|
||||
<body tabindex="1">
|
||||
@ -46,6 +46,6 @@ limitations under the License.
|
||||
<div id="viewer" class="pdfViewer"></div>
|
||||
</div>
|
||||
|
||||
<script src="simpleviewer.mjs" type="module"></script>
|
||||
<script src="simpleviewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
|
||||
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
|
||||
// The workerSrc property shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
//
|
||||
@ -35,10 +37,7 @@ const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const ENABLE_XFA = true;
|
||||
const SEARCH_FOR = ""; // try "Mozilla";
|
||||
|
||||
const SANDBOX_BUNDLE_SRC = new URL(
|
||||
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
|
||||
window.location
|
||||
);
|
||||
const SANDBOX_BUNDLE_SRC = "../../node_modules/pdfjs-dist/build/pdf.sandbox.js";
|
||||
|
||||
const container = document.getElementById("viewerContainer");
|
||||
|
||||
@ -88,10 +87,11 @@ const loadingTask = pdfjsLib.getDocument({
|
||||
cMapPacked: CMAP_PACKED,
|
||||
enableXfa: ENABLE_XFA,
|
||||
});
|
||||
(async function () {
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Document loaded, specifying document for the viewer and
|
||||
// the (optional) linkService.
|
||||
pdfViewer.setDocument(pdfDocument);
|
||||
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Document loaded, specifying document for the viewer and
|
||||
// the (optional) linkService.
|
||||
pdfViewer.setDocument(pdfDocument);
|
||||
|
||||
pdfLinkService.setDocument(pdfDocument, null);
|
||||
pdfLinkService.setDocument(pdfDocument, null);
|
||||
})();
|
||||
@ -37,8 +37,8 @@ limitations under the License.
|
||||
|
||||
<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>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
|
||||
</head>
|
||||
|
||||
<body tabindex="1">
|
||||
@ -46,6 +46,6 @@ limitations under the License.
|
||||
<div id="viewer" class="pdfViewer"></div>
|
||||
</div>
|
||||
|
||||
<script src="singlepageviewer.mjs" type="module"></script>
|
||||
<script src="singlepageviewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
|
||||
@ -21,7 +23,7 @@ if (!pdfjsLib.getDocument || !pdfjsViewer.PDFSinglePageViewer) {
|
||||
// The workerSrc property shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
//
|
||||
@ -35,10 +37,7 @@ const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const ENABLE_XFA = true;
|
||||
const SEARCH_FOR = ""; // try "Mozilla";
|
||||
|
||||
const SANDBOX_BUNDLE_SRC = new URL(
|
||||
"../../node_modules/pdfjs-dist/build/pdf.sandbox.mjs",
|
||||
window.location
|
||||
);
|
||||
const SANDBOX_BUNDLE_SRC = "../../node_modules/pdfjs-dist/build/pdf.sandbox.js";
|
||||
|
||||
const container = document.getElementById("viewerContainer");
|
||||
|
||||
@ -88,10 +87,10 @@ const loadingTask = pdfjsLib.getDocument({
|
||||
cMapPacked: CMAP_PACKED,
|
||||
enableXfa: ENABLE_XFA,
|
||||
});
|
||||
loadingTask.promise.then(function (pdfDocument) {
|
||||
// Document loaded, specifying document for the viewer and
|
||||
// the (optional) linkService.
|
||||
pdfSinglePageViewer.setDocument(pdfDocument);
|
||||
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Document loaded, specifying document for the viewer and
|
||||
// the (optional) linkService.
|
||||
pdfSinglePageViewer.setDocument(pdfDocument);
|
||||
|
||||
pdfLinkService.setDocument(pdfDocument, null);
|
||||
pdfLinkService.setDocument(pdfDocument, null);
|
||||
});
|
||||
@ -29,12 +29,12 @@ limitations under the License.
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.js"></script>
|
||||
</head>
|
||||
|
||||
<body tabindex="1">
|
||||
<canvas id="jpegCanvas" width="0" height="0"></canvas>
|
||||
|
||||
<script src="jpeg_viewer.mjs" type="module"></script>
|
||||
<script src="jpeg_viewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
63
examples/image_decoders/jpeg_viewer.js
Normal file
63
examples/image_decoders/jpeg_viewer.js
Normal file
@ -0,0 +1,63 @@
|
||||
/* Copyright 2018 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!pdfjsImageDecoders.JpegImage) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using `gulp dist-install`");
|
||||
}
|
||||
|
||||
const JPEG_IMAGE = "fish.jpg";
|
||||
|
||||
const jpegCanvas = document.getElementById("jpegCanvas");
|
||||
const jpegCtx = jpegCanvas.getContext("2d");
|
||||
|
||||
(async function () {
|
||||
// Load the image data, and convert it to a Uint8Array.
|
||||
//
|
||||
const response = await fetch(JPEG_IMAGE);
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
const typedArrayImage = new Uint8Array(await response.arrayBuffer());
|
||||
|
||||
// Parse the image data using `JpegImage`.
|
||||
//
|
||||
const jpegImage = new pdfjsImageDecoders.JpegImage();
|
||||
jpegImage.parse(typedArrayImage);
|
||||
|
||||
const width = jpegImage.width,
|
||||
height = jpegImage.height;
|
||||
const jpegData = jpegImage.getData({
|
||||
width,
|
||||
height,
|
||||
forceRGB: true,
|
||||
});
|
||||
|
||||
// Render the JPEG image on a <canvas>.
|
||||
//
|
||||
const imageData = jpegCtx.createImageData(width, height);
|
||||
const imageBytes = imageData.data;
|
||||
for (let j = 0, k = 0, jj = width * height * 4; j < jj; ) {
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = 255;
|
||||
}
|
||||
jpegCanvas.width = width;
|
||||
jpegCanvas.height = height;
|
||||
jpegCtx.putImageData(imageData, 0, 0);
|
||||
})();
|
||||
@ -1,59 +0,0 @@
|
||||
/* Copyright 2018 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!pdfjsImageDecoders.JpegImage) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using `gulp dist-install`");
|
||||
}
|
||||
|
||||
const JPEG_IMAGE = "fish.jpg";
|
||||
|
||||
const jpegCanvas = document.getElementById("jpegCanvas");
|
||||
const jpegCtx = jpegCanvas.getContext("2d");
|
||||
|
||||
// Load the image data, and convert it to a Uint8Array.
|
||||
//
|
||||
const response = await fetch(JPEG_IMAGE);
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
const typedArrayImage = new Uint8Array(await response.arrayBuffer());
|
||||
|
||||
// Parse the image data using `JpegImage`.
|
||||
//
|
||||
const jpegImage = new pdfjsImageDecoders.JpegImage();
|
||||
jpegImage.parse(typedArrayImage);
|
||||
|
||||
const width = jpegImage.width,
|
||||
height = jpegImage.height;
|
||||
const jpegData = jpegImage.getData({
|
||||
width,
|
||||
height,
|
||||
forceRGB: true,
|
||||
});
|
||||
|
||||
// Render the JPEG image on a <canvas>.
|
||||
//
|
||||
const imageData = jpegCtx.createImageData(width, height);
|
||||
const imageBytes = imageData.data;
|
||||
for (let j = 0, k = 0, jj = width * height * 4; j < jj; ) {
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = jpegData[k++];
|
||||
imageBytes[j++] = 255;
|
||||
}
|
||||
jpegCanvas.width = width;
|
||||
jpegCanvas.height = height;
|
||||
jpegCtx.putImageData(imageData, 0, 0);
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
|
||||
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
|
||||
<script id="script" type="module">
|
||||
<script id="script">
|
||||
//
|
||||
// If absolute URL from the remote server is provided, configure the CORS
|
||||
// header on that server.
|
||||
@ -23,46 +23,48 @@
|
||||
// The workerSrc property shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.js';
|
||||
|
||||
//
|
||||
// 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;
|
||||
(async () => {
|
||||
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;
|
||||
|
||||
//
|
||||
// Prepare canvas using PDF page dimensions
|
||||
//
|
||||
const canvas = document.getElementById("the-canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
//
|
||||
// Prepare canvas using PDF page dimensions
|
||||
//
|
||||
const canvas = document.getElementById("the-canvas");
|
||||
const context = 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";
|
||||
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";
|
||||
|
||||
const transform = outputScale !== 1
|
||||
? [outputScale, 0, 0, outputScale, 0, 0]
|
||||
: null;
|
||||
const transform = outputScale !== 1
|
||||
? [outputScale, 0, 0, outputScale, 0, 0]
|
||||
: null;
|
||||
|
||||
//
|
||||
// Render PDF page into canvas context
|
||||
//
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
transform,
|
||||
viewport,
|
||||
};
|
||||
page.render(renderContext);
|
||||
//
|
||||
// Render PDF page into canvas context
|
||||
//
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
transform,
|
||||
viewport,
|
||||
};
|
||||
page.render(renderContext);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<hr>
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
|
||||
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
|
||||
<script id="script" type="module">
|
||||
<script id="script">
|
||||
// atob() is used to convert base64 encoded PDF to binary-like data.
|
||||
// (See also https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/
|
||||
// Base64_encoding_and_decoding.)
|
||||
@ -35,39 +35,41 @@
|
||||
// The workerSrc property shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.js';
|
||||
|
||||
// Opening PDF by passing its binary data as a string. It is still preferable
|
||||
// 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;
|
||||
(async function() {
|
||||
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;
|
||||
|
||||
// Prepare canvas using PDF page dimensions.
|
||||
var canvas = document.getElementById('the-canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
// Prepare canvas using PDF page dimensions.
|
||||
var canvas = document.getElementById('the-canvas');
|
||||
var context = 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";
|
||||
canvas.width = Math.floor(viewport.width * outputScale);
|
||||
canvas.height = Math.floor(viewport.height * outputScale);
|
||||
canvas.style.width = Math.floor(viewport.width) + "px";
|
||||
canvas.style.height = Math.floor(viewport.height) + "px";
|
||||
|
||||
var transform = outputScale !== 1
|
||||
? [outputScale, 0, 0, outputScale, 0, 0]
|
||||
: null;
|
||||
var transform = outputScale !== 1
|
||||
? [outputScale, 0, 0, outputScale, 0, 0]
|
||||
: null;
|
||||
|
||||
// Render PDF page into canvas context.
|
||||
var renderContext = {
|
||||
canvasContext: context,
|
||||
transform,
|
||||
viewport,
|
||||
};
|
||||
page.render(renderContext);
|
||||
// Render PDF page into canvas context.
|
||||
var renderContext = {
|
||||
canvasContext: context,
|
||||
transform,
|
||||
viewport,
|
||||
};
|
||||
page.render(renderContext);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<hr>
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
<h1>'Previous/Next' example</h1>
|
||||
|
||||
<div>
|
||||
<button id="prev" type="button">Previous</button>
|
||||
<button id="next" type="button">Next</button>
|
||||
<button id="prev">Previous</button>
|
||||
<button id="next">Next</button>
|
||||
|
||||
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
|
||||
</div>
|
||||
@ -19,9 +19,9 @@
|
||||
<canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
|
||||
</div>
|
||||
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
|
||||
<script id="script" type="module">
|
||||
<script id="script">
|
||||
//
|
||||
// If absolute URL from the remote server is provided, configure the CORS
|
||||
// header on that server.
|
||||
@ -34,7 +34,7 @@
|
||||
// shall be specified.
|
||||
//
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
|
||||
'../../node_modules/pdfjs-dist/build/pdf.worker.js';
|
||||
|
||||
var pdfDoc = null,
|
||||
pageNum = 1,
|
||||
@ -128,11 +128,13 @@
|
||||
* Asynchronously downloads PDF.
|
||||
*/
|
||||
var loadingTask = pdfjsLib.getDocument(url);
|
||||
pdfDoc = await loadingTask.promise;
|
||||
document.getElementById('page_count').textContent = pdfDoc.numPages;
|
||||
loadingTask.promise.then(function(pdfDoc_) {
|
||||
pdfDoc = pdfDoc_;
|
||||
document.getElementById('page_count').textContent = pdfDoc.numPages;
|
||||
|
||||
// Initial/first page rendering
|
||||
renderPage(pageNum);
|
||||
// Initial/first page rendering
|
||||
renderPage(pageNum);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@ -26,12 +26,12 @@ html {
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: rgb(244 244 244 / 1);
|
||||
background-color: rgba(244, 244, 244, 1);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
border-bottom: 1px solid rgb(216 216 216 / 1);
|
||||
color: rgb(133 133 133 / 1);
|
||||
border-bottom: 1px solid rgba(216, 216, 216, 1);
|
||||
color: rgba(133, 133, 133, 1);
|
||||
font-size: 23px;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
@ -44,7 +44,7 @@ header h1 {
|
||||
|
||||
body {
|
||||
background: url(images/document_bg.png);
|
||||
color: rgb(255 255 255 / 1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-family: sans-serif;
|
||||
font-size: 10px;
|
||||
height: 100%;
|
||||
@ -71,7 +71,7 @@ footer {
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
box-shadow: 0 -0.2rem 0.5rem rgb(50 50 50 / 0.75);
|
||||
box-shadow: 0 -0.2rem 0.5rem rgba(50, 50, 50, 0.75);
|
||||
}
|
||||
|
||||
.toolbarButton {
|
||||
@ -81,7 +81,7 @@ footer {
|
||||
border-width: 0;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: rgb(0 0 0 / 0);
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.toolbarButton.pageUp {
|
||||
@ -110,11 +110,11 @@ footer {
|
||||
left: 36%;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
background-color: rgb(0 0 0 / 0);
|
||||
background-color: rgba(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);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
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;
|
||||
@ -190,8 +190,8 @@ canvas {
|
||||
|
||||
position: relative;
|
||||
height: 0.6rem;
|
||||
background-color: rgb(51 51 51 / 1);
|
||||
border-bottom: 1px solid rgb(51 51 51 / 1);
|
||||
background-color: rgba(51, 51, 51, 1);
|
||||
border-bottom: 1px solid rgba(51, 51, 51, 1);
|
||||
}
|
||||
|
||||
#loadingBar .progress {
|
||||
@ -201,7 +201,7 @@ canvas {
|
||||
transform: scaleX(var(--progressBar-percent));
|
||||
transform-origin: 0 0;
|
||||
height: 100%;
|
||||
background-color: rgb(221 221 221 / 1);
|
||||
background-color: rgba(221, 221, 221, 1);
|
||||
overflow: hidden;
|
||||
transition: transform 200ms;
|
||||
}
|
||||
@ -220,7 +220,7 @@ canvas {
|
||||
|
||||
#loadingBar.indeterminate .progress {
|
||||
transform: none;
|
||||
background-color: rgb(153 153 153 / 1);
|
||||
background-color: rgba(153, 153, 153, 1);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -232,11 +232,39 @@ canvas {
|
||||
width: 5rem;
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
rgb(153 153 153 / 1) 0%,
|
||||
rgb(255 255 255 / 1) 50%,
|
||||
rgb(153 153 153 / 1) 100%
|
||||
rgba(153, 153, 153, 1) 0%,
|
||||
rgba(255, 255, 255, 1) 50%,
|
||||
rgba(153, 153, 153, 1) 100%
|
||||
);
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
animation: progressIndeterminate 2s linear infinite;
|
||||
}
|
||||
|
||||
#errorWrapper {
|
||||
background: none repeat scroll 0 0 rgba(255, 85, 85, 1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 3.2rem;
|
||||
z-index: 1000;
|
||||
padding: 0.3rem;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#errorMessageLeft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#errorMessageRight {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#errorMoreInfo {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
color: rgba(0, 0, 0, 1);
|
||||
padding: 0.3rem;
|
||||
margin: 0.3rem;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
@ -24,8 +24,8 @@ limitations under the License.
|
||||
<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>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -42,16 +42,35 @@ limitations under the License.
|
||||
<div class="glimmer"></div>
|
||||
</div>
|
||||
|
||||
<div id="errorWrapper" hidden="true">
|
||||
<div id="errorMessageLeft">
|
||||
<span id="errorMessage"></span>
|
||||
<button id="errorShowMore">
|
||||
More Information
|
||||
</button>
|
||||
<button id="errorShowLess">
|
||||
Less Information
|
||||
</button>
|
||||
</div>
|
||||
<div id="errorMessageRight">
|
||||
<button id="errorClose">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearBoth"></div>
|
||||
<textarea id="errorMoreInfo" hidden="true" readonly="readonly"></textarea>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<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">
|
||||
|
||||
<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.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
|
||||
@ -25,7 +27,7 @@ const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
|
||||
const CMAP_PACKED = true;
|
||||
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
|
||||
|
||||
const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const DEFAULT_SCALE_DELTA = 1.1;
|
||||
@ -87,17 +89,41 @@ const PDFViewerApplication = {
|
||||
self.loadingBar.hide();
|
||||
self.setTitleUsingMetadata(pdfDocument);
|
||||
},
|
||||
function (reason) {
|
||||
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";
|
||||
function (exception) {
|
||||
const message = exception && exception.message;
|
||||
const l10n = self.l10n;
|
||||
let loadingErrorMessage;
|
||||
|
||||
if (exception instanceof pdfjsLib.InvalidPDFException) {
|
||||
// change error message also for other builds
|
||||
loadingErrorMessage = l10n.get(
|
||||
"invalid_file_error",
|
||||
null,
|
||||
"Invalid or corrupted PDF file."
|
||||
);
|
||||
} else if (exception instanceof pdfjsLib.MissingPDFException) {
|
||||
// special message for missing PDFs
|
||||
loadingErrorMessage = l10n.get(
|
||||
"missing_file_error",
|
||||
null,
|
||||
"Missing PDF file."
|
||||
);
|
||||
} else if (exception instanceof pdfjsLib.UnexpectedResponseException) {
|
||||
loadingErrorMessage = l10n.get(
|
||||
"unexpected_response_error",
|
||||
null,
|
||||
"Unexpected server response."
|
||||
);
|
||||
} else {
|
||||
loadingErrorMessage = l10n.get(
|
||||
"loading_error",
|
||||
null,
|
||||
"An error occurred while loading the PDF."
|
||||
);
|
||||
}
|
||||
self.l10n.get(key).then(msg => {
|
||||
self.error(msg, { message: reason?.message });
|
||||
|
||||
loadingErrorMessage.then(function (msg) {
|
||||
self.error(msg, { message });
|
||||
});
|
||||
self.loadingBar.hide();
|
||||
}
|
||||
@ -110,6 +136,9 @@ const PDFViewerApplication = {
|
||||
* destruction is completed.
|
||||
*/
|
||||
close() {
|
||||
const errorWrapper = document.getElementById("errorWrapper");
|
||||
errorWrapper.hidden = true;
|
||||
|
||||
if (!this.pdfLoadingTask) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -202,25 +231,79 @@ const PDFViewerApplication = {
|
||||
},
|
||||
|
||||
error: function pdfViewError(message, moreInfo) {
|
||||
const l10n = this.l10n;
|
||||
const moreInfoText = [
|
||||
`PDF.js v${pdfjsLib.version || "?"} (build: ${pdfjsLib.build || "?"})`,
|
||||
l10n.get(
|
||||
"error_version_info",
|
||||
{ version: pdfjsLib.version || "?", build: pdfjsLib.build || "?" },
|
||||
"PDF.js v{{version}} (build: {{build}})"
|
||||
),
|
||||
];
|
||||
if (moreInfo) {
|
||||
moreInfoText.push(`Message: ${moreInfo.message}`);
|
||||
|
||||
if (moreInfo) {
|
||||
moreInfoText.push(
|
||||
l10n.get(
|
||||
"error_message",
|
||||
{ message: moreInfo.message },
|
||||
"Message: {{message}}"
|
||||
)
|
||||
);
|
||||
if (moreInfo.stack) {
|
||||
moreInfoText.push(`Stack: ${moreInfo.stack}`);
|
||||
moreInfoText.push(
|
||||
l10n.get("error_stack", { stack: moreInfo.stack }, "Stack: {{stack}}")
|
||||
);
|
||||
} else {
|
||||
if (moreInfo.filename) {
|
||||
moreInfoText.push(`File: ${moreInfo.filename}`);
|
||||
moreInfoText.push(
|
||||
l10n.get(
|
||||
"error_file",
|
||||
{ file: moreInfo.filename },
|
||||
"File: {{file}}"
|
||||
)
|
||||
);
|
||||
}
|
||||
if (moreInfo.lineNumber) {
|
||||
moreInfoText.push(`Line: ${moreInfo.lineNumber}`);
|
||||
moreInfoText.push(
|
||||
l10n.get(
|
||||
"error_line",
|
||||
{ line: moreInfo.lineNumber },
|
||||
"Line: {{line}}"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.error(`${message}\n\n${moreInfoText.join("\n")}`);
|
||||
const errorWrapper = document.getElementById("errorWrapper");
|
||||
errorWrapper.hidden = false;
|
||||
|
||||
const errorMessage = document.getElementById("errorMessage");
|
||||
errorMessage.textContent = message;
|
||||
|
||||
const closeButton = document.getElementById("errorClose");
|
||||
closeButton.onclick = function () {
|
||||
errorWrapper.hidden = true;
|
||||
};
|
||||
|
||||
const errorMoreInfo = document.getElementById("errorMoreInfo");
|
||||
const moreInfoButton = document.getElementById("errorShowMore");
|
||||
const lessInfoButton = document.getElementById("errorShowLess");
|
||||
moreInfoButton.onclick = function () {
|
||||
errorMoreInfo.hidden = false;
|
||||
moreInfoButton.hidden = true;
|
||||
lessInfoButton.hidden = false;
|
||||
errorMoreInfo.style.height = errorMoreInfo.scrollHeight + "px";
|
||||
};
|
||||
lessInfoButton.onclick = function () {
|
||||
errorMoreInfo.hidden = true;
|
||||
moreInfoButton.hidden = false;
|
||||
lessInfoButton.hidden = true;
|
||||
};
|
||||
moreInfoButton.hidden = false;
|
||||
lessInfoButton.hidden = true;
|
||||
Promise.all(moreInfoText).then(function (parts) {
|
||||
errorMoreInfo.value = parts.join("\n");
|
||||
});
|
||||
},
|
||||
|
||||
progress: function pdfViewProgress(level) {
|
||||
@ -272,7 +355,7 @@ const PDFViewerApplication = {
|
||||
});
|
||||
this.pdfLinkService = linkService;
|
||||
|
||||
this.l10n = new pdfjsViewer.GenericL10n();
|
||||
this.l10n = pdfjsViewer.NullL10n;
|
||||
|
||||
const container = document.getElementById("viewerContainer");
|
||||
const pdfViewer = new pdfjsViewer.PDFViewer({
|
||||
9
examples/node/.eslintrc
Normal file
9
examples/node/.eslintrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
}
|
||||
285
examples/node/domstubs.js
Normal file
285
examples/node/domstubs.js
Normal file
@ -0,0 +1,285 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function xmlEncode(s) {
|
||||
let i = 0,
|
||||
ch;
|
||||
s = String(s);
|
||||
while (
|
||||
i < s.length &&
|
||||
(ch = s[i]) !== "&" &&
|
||||
ch !== "<" &&
|
||||
ch !== '"' &&
|
||||
ch !== "\n" &&
|
||||
ch !== "\r" &&
|
||||
ch !== "\t"
|
||||
) {
|
||||
i++;
|
||||
}
|
||||
if (i >= s.length) {
|
||||
return s;
|
||||
}
|
||||
let buf = s.substring(0, i);
|
||||
while (i < s.length) {
|
||||
ch = s[i++];
|
||||
switch (ch) {
|
||||
case "&":
|
||||
buf += "&";
|
||||
break;
|
||||
case "<":
|
||||
buf += "<";
|
||||
break;
|
||||
case '"':
|
||||
buf += """;
|
||||
break;
|
||||
case "\n":
|
||||
buf += "
";
|
||||
break;
|
||||
case "\r":
|
||||
buf += "
";
|
||||
break;
|
||||
case "\t":
|
||||
buf += "	";
|
||||
break;
|
||||
default:
|
||||
buf += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function DOMElement(name) {
|
||||
this.nodeName = name;
|
||||
this.childNodes = [];
|
||||
this.attributes = {};
|
||||
this.textContent = "";
|
||||
|
||||
if (name === "style") {
|
||||
this.sheet = {
|
||||
cssRules: [],
|
||||
insertRule(rule) {
|
||||
this.cssRules.push(rule);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
DOMElement.prototype = {
|
||||
getAttribute: function DOMElement_getAttribute(name) {
|
||||
if (name in this.attributes) {
|
||||
return this.attributes[name];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getAttributeNS: function DOMElement_getAttributeNS(NS, name) {
|
||||
// Fast path
|
||||
if (name in this.attributes) {
|
||||
return this.attributes[name];
|
||||
}
|
||||
// Slow path - used by test/unit/display_svg_spec.js
|
||||
// Assuming that there is only one matching attribute for a given name,
|
||||
// across all namespaces.
|
||||
if (NS) {
|
||||
const suffix = ":" + name;
|
||||
for (const fullName in this.attributes) {
|
||||
if (fullName.slice(-suffix.length) === suffix) {
|
||||
return this.attributes[fullName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
setAttribute: function DOMElement_setAttribute(name, value) {
|
||||
this.attributes[name] = value || "";
|
||||
},
|
||||
|
||||
setAttributeNS: function DOMElement_setAttributeNS(NS, name, value) {
|
||||
this.setAttribute(name, value);
|
||||
},
|
||||
|
||||
append: function DOMElement_append(...elements) {
|
||||
const childNodes = this.childNodes;
|
||||
for (const element of elements) {
|
||||
if (!childNodes.includes(element)) {
|
||||
childNodes.push(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
appendChild: function DOMElement_appendChild(element) {
|
||||
const childNodes = this.childNodes;
|
||||
if (!childNodes.includes(element)) {
|
||||
childNodes.push(element);
|
||||
}
|
||||
},
|
||||
|
||||
hasChildNodes: function DOMElement_hasChildNodes() {
|
||||
return this.childNodes.length !== 0;
|
||||
},
|
||||
|
||||
cloneNode: function DOMElement_cloneNode() {
|
||||
const newNode = new DOMElement(this.nodeName);
|
||||
newNode.childNodes = this.childNodes;
|
||||
newNode.attributes = this.attributes;
|
||||
newNode.textContent = this.textContent;
|
||||
return newNode;
|
||||
},
|
||||
|
||||
// This method is offered for convenience. It is recommended to directly use
|
||||
// getSerializer because that allows you to process the chunks as they come
|
||||
// instead of requiring the whole image to fit in memory.
|
||||
toString: function DOMElement_toString() {
|
||||
const buf = [];
|
||||
const serializer = this.getSerializer();
|
||||
let chunk;
|
||||
while ((chunk = serializer.getNext()) !== null) {
|
||||
buf.push(chunk);
|
||||
}
|
||||
return buf.join("");
|
||||
},
|
||||
|
||||
getSerializer: function DOMElement_getSerializer() {
|
||||
return new DOMElementSerializer(this);
|
||||
},
|
||||
};
|
||||
|
||||
function DOMElementSerializer(node) {
|
||||
this._node = node;
|
||||
this._state = 0;
|
||||
this._loopIndex = 0;
|
||||
this._attributeKeys = null;
|
||||
this._childSerializer = null;
|
||||
}
|
||||
DOMElementSerializer.prototype = {
|
||||
/**
|
||||
* Yields the next chunk in the serialization of the element.
|
||||
*
|
||||
* @returns {string|null} null if the element has fully been serialized.
|
||||
*/
|
||||
getNext: function DOMElementSerializer_getNext() {
|
||||
const node = this._node;
|
||||
switch (this._state) {
|
||||
case 0: // Start opening tag.
|
||||
++this._state;
|
||||
return "<" + node.nodeName;
|
||||
case 1: // Add SVG namespace if this is the root element.
|
||||
++this._state;
|
||||
if (node.nodeName === "svg:svg") {
|
||||
return (
|
||||
' xmlns:xlink="http://www.w3.org/1999/xlink"' +
|
||||
' xmlns:svg="http://www.w3.org/2000/svg"'
|
||||
);
|
||||
}
|
||||
/* falls through */
|
||||
case 2: // Initialize variables for looping over attributes.
|
||||
++this._state;
|
||||
this._loopIndex = 0;
|
||||
this._attributeKeys = Object.keys(node.attributes);
|
||||
/* falls through */
|
||||
case 3: // Serialize any attributes and end opening tag.
|
||||
if (this._loopIndex < this._attributeKeys.length) {
|
||||
const name = this._attributeKeys[this._loopIndex++];
|
||||
return " " + name + '="' + xmlEncode(node.attributes[name]) + '"';
|
||||
}
|
||||
++this._state;
|
||||
return ">";
|
||||
case 4: // Serialize textContent for tspan/style elements.
|
||||
if (node.nodeName === "svg:tspan" || node.nodeName === "svg:style") {
|
||||
this._state = 6;
|
||||
return xmlEncode(node.textContent);
|
||||
}
|
||||
++this._state;
|
||||
this._loopIndex = 0;
|
||||
/* falls through */
|
||||
case 5: // Serialize child nodes (only for non-tspan/style elements).
|
||||
while (true) {
|
||||
const value =
|
||||
this._childSerializer && this._childSerializer.getNext();
|
||||
if (value !== null) {
|
||||
return value;
|
||||
}
|
||||
const nextChild = node.childNodes[this._loopIndex++];
|
||||
if (nextChild) {
|
||||
this._childSerializer = new DOMElementSerializer(nextChild);
|
||||
} else {
|
||||
this._childSerializer = null;
|
||||
++this._state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* falls through */
|
||||
case 6: // Ending tag.
|
||||
++this._state;
|
||||
return "</" + node.nodeName + ">";
|
||||
case 7: // Done.
|
||||
return null;
|
||||
default:
|
||||
throw new Error("Unexpected serialization state: " + this._state);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const document = {
|
||||
childNodes: [],
|
||||
|
||||
get currentScript() {
|
||||
return { src: "" };
|
||||
},
|
||||
|
||||
get documentElement() {
|
||||
return this;
|
||||
},
|
||||
|
||||
createElementNS(NS, element) {
|
||||
const elObject = new DOMElement(element);
|
||||
return elObject;
|
||||
},
|
||||
|
||||
createElement(element) {
|
||||
return this.createElementNS("", element);
|
||||
},
|
||||
|
||||
getElementsByTagName(element) {
|
||||
if (element === "head") {
|
||||
return [this.head || (this.head = new DOMElement("head"))];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
function Image() {
|
||||
this._src = null;
|
||||
this.onload = null;
|
||||
}
|
||||
Image.prototype = {
|
||||
get src() {
|
||||
return this._src;
|
||||
},
|
||||
set src(value) {
|
||||
this._src = value;
|
||||
if (this.onload) {
|
||||
this.onload();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
exports.document = document;
|
||||
exports.Image = Image;
|
||||
|
||||
const exported_symbols = Object.keys(exports);
|
||||
|
||||
exports.setStubs = function (namespace) {
|
||||
exported_symbols.forEach(function (key) {
|
||||
console.assert(!(key in namespace), "property should not be set: " + key);
|
||||
namespace[key] = exports[key];
|
||||
});
|
||||
};
|
||||
exports.unsetStubs = function (namespace) {
|
||||
exported_symbols.forEach(function (key) {
|
||||
console.assert(key in namespace, "property should be set: " + key);
|
||||
delete namespace[key];
|
||||
});
|
||||
};
|
||||
@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
// Run `gulp dist-install` to generate 'pdfjs-dist' npm package files.
|
||||
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
|
||||
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
|
||||
|
||||
// Loading file from file system into typed array
|
||||
const pdfPath =
|
||||
@ -14,7 +14,7 @@ const pdfPath =
|
||||
|
||||
// Will be using promises to load document, pages and misc data instead of
|
||||
// callback.
|
||||
const loadingTask = getDocument(pdfPath);
|
||||
const loadingTask = pdfjsLib.getDocument(pdfPath);
|
||||
loadingTask.promise
|
||||
.then(function (doc) {
|
||||
const numPages = doc.numPages;
|
||||
@ -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
|
||||
|
||||
112
examples/node/pdf2png/pdf2png.js
Normal file
112
examples/node/pdf2png/pdf2png.js
Normal file
@ -0,0 +1,112 @@
|
||||
/* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Canvas = require("canvas");
|
||||
const assert = require("assert").strict;
|
||||
const fs = require("fs");
|
||||
|
||||
function NodeCanvasFactory() {}
|
||||
NodeCanvasFactory.prototype = {
|
||||
create: function 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: function NodeCanvasFactory_reset(canvasAndContext, width, height) {
|
||||
assert(canvasAndContext.canvas, "Canvas is not specified");
|
||||
assert(width > 0 && height > 0, "Invalid canvas size");
|
||||
canvasAndContext.canvas.width = width;
|
||||
canvasAndContext.canvas.height = height;
|
||||
},
|
||||
|
||||
destroy: function NodeCanvasFactory_destroy(canvasAndContext) {
|
||||
assert(canvasAndContext.canvas, "Canvas is not specified");
|
||||
|
||||
// Zeroing the width and height cause Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
canvasAndContext.canvas.width = 0;
|
||||
canvasAndContext.canvas.height = 0;
|
||||
canvasAndContext.canvas = null;
|
||||
canvasAndContext.context = null;
|
||||
},
|
||||
};
|
||||
|
||||
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
|
||||
const CMAP_PACKED = true;
|
||||
|
||||
// Where the standard fonts are located.
|
||||
const STANDARD_FONT_DATA_URL =
|
||||
"../../../node_modules/pdfjs-dist/standard_fonts/";
|
||||
|
||||
const canvasFactory = new NodeCanvasFactory();
|
||||
|
||||
// Loading file from file system into typed array.
|
||||
const pdfPath =
|
||||
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const data = new Uint8Array(fs.readFileSync(pdfPath));
|
||||
|
||||
// Load the PDF file.
|
||||
const loadingTask = pdfjsLib.getDocument({
|
||||
data,
|
||||
cMapUrl: CMAP_URL,
|
||||
cMapPacked: CMAP_PACKED,
|
||||
standardFontDataUrl: STANDARD_FONT_DATA_URL,
|
||||
canvasFactory,
|
||||
});
|
||||
|
||||
(async function () {
|
||||
try {
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
console.log("# PDF document loaded.");
|
||||
// Get the first page.
|
||||
const page = await pdfDocument.getPage(1);
|
||||
// Render the page on a Node canvas with 100% scale.
|
||||
const viewport = page.getViewport({ scale: 1.0 });
|
||||
const canvasAndContext = canvasFactory.create(
|
||||
viewport.width,
|
||||
viewport.height
|
||||
);
|
||||
const renderContext = {
|
||||
canvasContext: canvasAndContext.context,
|
||||
viewport,
|
||||
};
|
||||
|
||||
const renderTask = page.render(renderContext);
|
||||
await renderTask.promise;
|
||||
// Convert the canvas to an image buffer.
|
||||
const image = canvasAndContext.canvas.toBuffer();
|
||||
fs.writeFile("output.png", image, function (error) {
|
||||
if (error) {
|
||||
console.error("Error: " + error);
|
||||
} else {
|
||||
console.log(
|
||||
"Finished converting first page of PDF file to a PNG image."
|
||||
);
|
||||
}
|
||||
});
|
||||
// Release page resources.
|
||||
page.cleanup();
|
||||
} catch (reason) {
|
||||
console.log(reason);
|
||||
}
|
||||
})();
|
||||
@ -1,72 +0,0 @@
|
||||
/* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
|
||||
const CMAP_PACKED = true;
|
||||
|
||||
// Where the standard fonts are located.
|
||||
const STANDARD_FONT_DATA_URL =
|
||||
"../../../node_modules/pdfjs-dist/standard_fonts/";
|
||||
|
||||
// Loading file from file system into typed array.
|
||||
const pdfPath =
|
||||
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const data = new Uint8Array(fs.readFileSync(pdfPath));
|
||||
|
||||
// Load the PDF file.
|
||||
const loadingTask = getDocument({
|
||||
data,
|
||||
cMapUrl: CMAP_URL,
|
||||
cMapPacked: CMAP_PACKED,
|
||||
standardFontDataUrl: STANDARD_FONT_DATA_URL,
|
||||
});
|
||||
|
||||
try {
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
console.log("# PDF document loaded.");
|
||||
// Get the first page.
|
||||
const page = await pdfDocument.getPage(1);
|
||||
// Render the page on a Node canvas with 100% scale.
|
||||
const canvasFactory = pdfDocument.canvasFactory;
|
||||
const viewport = page.getViewport({ scale: 1.0 });
|
||||
const canvasAndContext = canvasFactory.create(
|
||||
viewport.width,
|
||||
viewport.height
|
||||
);
|
||||
const renderContext = {
|
||||
canvasContext: canvasAndContext.context,
|
||||
viewport,
|
||||
};
|
||||
|
||||
const renderTask = page.render(renderContext);
|
||||
await renderTask.promise;
|
||||
// Convert the canvas to an image buffer.
|
||||
const image = canvasAndContext.canvas.toBuffer("image/png");
|
||||
fs.writeFile("output.png", image, function (error) {
|
||||
if (error) {
|
||||
console.error("Error: " + error);
|
||||
} else {
|
||||
console.log("Finished converting first page of PDF file to a PNG image.");
|
||||
}
|
||||
});
|
||||
// Release page resources.
|
||||
page.cleanup();
|
||||
} catch (reason) {
|
||||
console.log(reason);
|
||||
}
|
||||
128
examples/node/pdf2svg.js
Normal file
128
examples/node/pdf2svg.js
Normal file
@ -0,0 +1,128 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
//
|
||||
// Node tool to dump SVG output into a file.
|
||||
//
|
||||
|
||||
const fs = require("fs");
|
||||
const util = require("util");
|
||||
const path = require("path");
|
||||
const stream = require("stream");
|
||||
|
||||
// HACK few hacks to let PDF.js be loaded not as a module in global space.
|
||||
require("./domstubs.js").setStubs(global);
|
||||
|
||||
// Run `gulp dist-install` to generate 'pdfjs-dist' npm package files.
|
||||
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
|
||||
const CMAP_PACKED = true;
|
||||
|
||||
// Loading file from file system into typed array
|
||||
const pdfPath =
|
||||
process.argv[2] || "../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
const data = new Uint8Array(fs.readFileSync(pdfPath));
|
||||
|
||||
const outputDirectory = "./svgdump";
|
||||
|
||||
try {
|
||||
// Note: This creates a directory only one level deep. If you want to create
|
||||
// multiple subdirectories on the fly, use the mkdirp module from npm.
|
||||
fs.mkdirSync(outputDirectory);
|
||||
} catch (e) {
|
||||
if (e.code !== "EEXIST") {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Dumps svg outputs to a folder called svgdump
|
||||
function getFilePathForPage(pageNum) {
|
||||
const name = path.basename(pdfPath, path.extname(pdfPath));
|
||||
return path.join(outputDirectory, `${name}-${pageNum}.svg`);
|
||||
}
|
||||
|
||||
/**
|
||||
* A readable stream which offers a stream representing the serialization of a
|
||||
* given DOM element (as defined by domstubs.js).
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {DOMElement} options.svgElement The element to serialize
|
||||
*/
|
||||
function ReadableSVGStream(options) {
|
||||
if (!(this instanceof ReadableSVGStream)) {
|
||||
return new ReadableSVGStream(options);
|
||||
}
|
||||
stream.Readable.call(this, options);
|
||||
this.serializer = options.svgElement.getSerializer();
|
||||
}
|
||||
util.inherits(ReadableSVGStream, stream.Readable);
|
||||
// Implements https://nodejs.org/api/stream.html#stream_readable_read_size_1
|
||||
ReadableSVGStream.prototype._read = function () {
|
||||
let chunk;
|
||||
while ((chunk = this.serializer.getNext()) !== null) {
|
||||
if (!this.push(chunk)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.push(null);
|
||||
};
|
||||
|
||||
// Streams the SVG element to the given file path.
|
||||
function writeSvgToFile(svgElement, filePath) {
|
||||
let readableSvgStream = new ReadableSVGStream({
|
||||
svgElement,
|
||||
});
|
||||
const writableStream = fs.createWriteStream(filePath);
|
||||
return new Promise(function (resolve, reject) {
|
||||
readableSvgStream.once("error", reject);
|
||||
writableStream.once("error", reject);
|
||||
writableStream.once("finish", resolve);
|
||||
readableSvgStream.pipe(writableStream);
|
||||
}).catch(function (err) {
|
||||
readableSvgStream = null; // Explicitly null because of v8 bug 6512.
|
||||
writableStream.end();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
// Will be using async/await to load document, pages and misc data.
|
||||
const loadingTask = pdfjsLib.getDocument({
|
||||
data,
|
||||
cMapUrl: CMAP_URL,
|
||||
cMapPacked: CMAP_PACKED,
|
||||
fontExtraProperties: true,
|
||||
});
|
||||
(async function () {
|
||||
const doc = await loadingTask.promise;
|
||||
const numPages = doc.numPages;
|
||||
console.log("# Document Loaded");
|
||||
console.log(`Number of Pages: ${numPages}`);
|
||||
console.log();
|
||||
|
||||
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
|
||||
try {
|
||||
const page = await doc.getPage(pageNum);
|
||||
console.log(`# Page ${pageNum}`);
|
||||
const viewport = page.getViewport({ scale: 1.0 });
|
||||
console.log(`Size: ${viewport.width}x${viewport.height}`);
|
||||
console.log();
|
||||
|
||||
const opList = await page.getOperatorList();
|
||||
const svgGfx = new pdfjsLib.SVGGraphics(
|
||||
page.commonObjs,
|
||||
page.objs,
|
||||
/* forceDataSchema = */ true
|
||||
);
|
||||
svgGfx.embedFonts = true;
|
||||
const svg = await svgGfx.getSVG(opList, viewport);
|
||||
await writeSvgToFile(svg, getFilePathForPage(pageNum));
|
||||
// Release page resources.
|
||||
page.cleanup();
|
||||
} catch (err) {
|
||||
console.log(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
console.log("# End of Document");
|
||||
})();
|
||||
@ -3,8 +3,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>
|
||||
<script src="../../node_modules/pdfjs-dist/build/pdf.js"></script>
|
||||
<script src="pdf2svg.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Text-only PDF.js example</p>
|
||||
|
||||
@ -19,7 +19,7 @@ const PAGE_SCALE = 1.5;
|
||||
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.mjs";
|
||||
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
|
||||
|
||||
function buildSVG(viewport, textContent) {
|
||||
// Building SVG with size of the viewport (for simplicity)
|
||||
9
examples/webpack/.eslintrc
Normal file
9
examples/webpack/.eslintrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
}
|
||||
@ -27,7 +27,9 @@ otherwise the build is not guaranteed to work correctly.
|
||||
## Worker loading
|
||||
|
||||
If you are getting the `Setting up fake worker` warning, make sure you are
|
||||
importing `pdfjs-dist/webpack.mjs` which is the zero-configuration method for
|
||||
importing `pdfjs-dist/webpack` which is the zero-configuration method for
|
||||
Webpack users. Installing `worker-loader` is no longer necessary.
|
||||
|
||||
import * as pdfjsLib from 'pdfjs-dist/webpack.mjs';
|
||||
import * as pdfjsLib from 'pdfjs-dist/webpack';
|
||||
|
||||
For a full working example refer to [this repository](https://github.com/yurydelendik/pdfjs-react).
|
||||
|
||||
35
examples/webpack/main.js
Normal file
35
examples/webpack/main.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
// Hello world example for webpack.
|
||||
|
||||
const pdfjsLib = require("pdfjs-dist");
|
||||
|
||||
const pdfPath = "../learning/helloworld.pdf";
|
||||
|
||||
// Setting worker path to worker bundle.
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../build/webpack/pdf.worker.bundle.js";
|
||||
|
||||
// Loading a document.
|
||||
const loadingTask = pdfjsLib.getDocument(pdfPath);
|
||||
loadingTask.promise
|
||||
.then(function (pdfDocument) {
|
||||
// Request a first page
|
||||
return pdfDocument.getPage(1).then(function (pdfPage) {
|
||||
// Display page on the existing canvas with 100% scale.
|
||||
const viewport = pdfPage.getViewport({ scale: 1.0 });
|
||||
const canvas = document.getElementById("theCanvas");
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const renderTask = pdfPage.render({
|
||||
canvasContext: ctx,
|
||||
viewport,
|
||||
});
|
||||
return renderTask.promise;
|
||||
});
|
||||
})
|
||||
.catch(function (reason) {
|
||||
console.error("Error: " + reason);
|
||||
});
|
||||
@ -1,29 +0,0 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
// Hello world example for webpack.
|
||||
|
||||
import * as pdfjsLib from "pdfjs-dist";
|
||||
|
||||
const pdfPath = "../learning/helloworld.pdf";
|
||||
|
||||
// Setting worker path to worker bundle.
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||
"../../build/webpack/pdf.worker.bundle.js";
|
||||
|
||||
// Loading a document.
|
||||
const loadingTask = pdfjsLib.getDocument(pdfPath);
|
||||
const pdfDocument = await loadingTask.promise;
|
||||
// Request a first page
|
||||
const pdfPage = await pdfDocument.getPage(1);
|
||||
// Display page on the existing canvas with 100% scale.
|
||||
const viewport = pdfPage.getViewport({ scale: 1.0 });
|
||||
const canvas = document.getElementById("theCanvas");
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const renderTask = pdfPage.render({
|
||||
canvasContext: ctx,
|
||||
viewport,
|
||||
});
|
||||
await renderTask.promise;
|
||||
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "webpack-pdf.js-example",
|
||||
"version": "0.2.0",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "webpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack": "^5.11.1",
|
||||
"webpack-cli": "^4.3.1",
|
||||
"pdfjs-dist": "../../node_modules/pdfjs-dist"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
|
||||
const webpack = require("webpack"); // eslint-disable-line no-unused-vars
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: {
|
||||
main: "./main.mjs",
|
||||
"pdf.worker": "pdfjs-dist/build/pdf.worker.mjs",
|
||||
main: "./main.js",
|
||||
"pdf.worker": "pdfjs-dist/build/pdf.worker.entry",
|
||||
},
|
||||
mode: "none",
|
||||
output: {
|
||||
|
||||
22
extensions/chromium/.eslintrc
Normal file
22
extensions/chromium/.eslintrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": [
|
||||
../../.eslintrc
|
||||
],
|
||||
|
||||
"env": {
|
||||
"webextensions": true
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"mozilla"
|
||||
],
|
||||
|
||||
"parserOptions": {
|
||||
"sourceType": "script"
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"mozilla/import-globals": "error",
|
||||
"no-var": "off",
|
||||
},
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,7 @@ limitations under the License.
|
||||
}
|
||||
var scheme = url.slice(0, schemeIndex).toLowerCase();
|
||||
if (schemes.includes(scheme)) {
|
||||
url = url.split("#", 1)[0];
|
||||
url = url.split("#")[0];
|
||||
if (url.charAt(schemeIndex) === ":") {
|
||||
url = encodeURIComponent(url);
|
||||
}
|
||||
@ -55,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);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
BIN
extensions/chromium/icon19.png
Normal file
BIN
extensions/chromium/icon19.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 679 B |
BIN
extensions/chromium/icon38.png
Normal file
BIN
extensions/chromium/icon38.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@ -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,63 @@
|
||||
"16": "icon16.png"
|
||||
},
|
||||
"permissions": [
|
||||
"alarms",
|
||||
"declarativeNetRequestWithHostAccess",
|
||||
"webRequest",
|
||||
"fileBrowserHandler",
|
||||
"webRequest", "webRequestBlocking",
|
||||
"<all_urls>",
|
||||
"tabs",
|
||||
"webNavigation",
|
||||
"storage"
|
||||
],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*", "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_scripts": [{
|
||||
"matches": [
|
||||
"http://*/*",
|
||||
"https://*/*",
|
||||
"ftp://*/*",
|
||||
"file://*/*"
|
||||
],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"css": ["contentstyle.css"],
|
||||
"js": ["contentscript.js"]
|
||||
}],
|
||||
"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:*"
|
||||
]
|
||||
}
|
||||
|
||||
@ -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 }) => {
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@ -19,28 +19,23 @@ limitations under the License.
|
||||
<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: 1em 0;
|
||||
}
|
||||
.checkbox label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.checkbox label input {
|
||||
flex-shrink: 0;
|
||||
margin: 0.65em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="settings-boxes"></div>
|
||||
<button id="reset-button" type="button">Restore default settings</button>
|
||||
<button id="reset-button">Restore default settings</button>
|
||||
|
||||
<template id="checkbox-template">
|
||||
<div class="settings-row checkbox">
|
||||
<!-- Chromium's style: //src/extensions/renderer/resources/extension.css -->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox">
|
||||
<span></span>
|
||||
|
||||
45
extensions/chromium/pageAction/background.js
Normal file
45
extensions/chromium/pageAction/background.js
Normal 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);
|
||||
}
|
||||
});
|
||||
})();
|
||||
44
extensions/chromium/pageAction/popup.html
Normal file
44
extensions/chromium/pageAction/popup.html
Normal 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>
|
||||
@ -1,4 +1,4 @@
|
||||
/* Copyright 2023 Mozilla Foundation
|
||||
/* 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.
|
||||
@ -13,27 +13,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Used in 'highlight' tests */
|
||||
"use strict";
|
||||
|
||||
.highlight {
|
||||
position: absolute;
|
||||
mix-blend-mode: multiply;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.highlightOutline {
|
||||
position: absolute;
|
||||
mix-blend-mode: normal;
|
||||
fill-rule: evenodd;
|
||||
fill: none;
|
||||
|
||||
.mainOutline {
|
||||
stroke: white;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.secondaryOutline {
|
||||
stroke: blue;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
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();
|
||||
}
|
||||
102
extensions/chromium/pdfHandler-vcros.js
Normal file
102
extensions/chromium/pdfHandler-vcros.js
Normal 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);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
})();
|
||||
24
extensions/chromium/pdfHandler.html
Normal file
24
extensions/chromium/pdfHandler.html
Normal 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>
|
||||
@ -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 prority (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;
|
||||
});
|
||||
|
||||
@ -5,7 +5,11 @@
|
||||
"title": "Theme",
|
||||
"description": "The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.",
|
||||
"type": "integer",
|
||||
"enum": [0, 1, 2],
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"default": 2
|
||||
},
|
||||
"showPreviousViewOnLoad": {
|
||||
@ -17,7 +21,11 @@
|
||||
"title": "View position on load",
|
||||
"description": "The position in the document upon load.\n -1 = Default (uses OpenAction if available, otherwise equal to `viewOnLoad = 0`).\n 0 = The last viewed page/position.\n 1 = The initial page/position.",
|
||||
"type": "integer",
|
||||
"enum": [-1, 0, 1],
|
||||
"enum": [
|
||||
-1,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"defaultZoomDelay": {
|
||||
@ -37,7 +45,13 @@
|
||||
"title": "Sidebar state on load",
|
||||
"description": "Controls the state of the sidebar upon load.\n -1 = Default (uses PageMode if available, otherwise the last position if available/enabled).\n 0 = Do not show sidebar.\n 1 = Show thumbnails in sidebar.\n 2 = Show document outline in sidebar.\n 3 = Show attachments in sidebar.",
|
||||
"type": "integer",
|
||||
"enum": [-1, 0, 1, 2, 3],
|
||||
"enum": [
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"default": -1
|
||||
},
|
||||
"enableHandToolOnLoad": {
|
||||
@ -45,45 +59,14 @@
|
||||
"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": ""
|
||||
},
|
||||
"enableSignatureEditor": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enableUpdatedAddImage": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"cursorToolOnLoad": {
|
||||
"title": "Cursor tool on load",
|
||||
"description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.",
|
||||
"type": "integer",
|
||||
"enum": [0, 1],
|
||||
"enum": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"pdfBugEnabled": {
|
||||
@ -98,13 +81,9 @@
|
||||
"description": "Whether to allow execution of active content (JavaScript) by PDF files.",
|
||||
"default": false
|
||||
},
|
||||
"enableHighlightFloatingButton": {
|
||||
"enableStampEditor": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"highlightEditorColors": {
|
||||
"type": "string",
|
||||
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
|
||||
"default": true
|
||||
},
|
||||
"disableRange": {
|
||||
"title": "Disable range requests",
|
||||
@ -137,14 +116,23 @@
|
||||
"title": "Text layer mode",
|
||||
"description": "Controls if the text layer is enabled, and the selection mode that is used.\n 0 = Disabled.\n 1 = Enabled.",
|
||||
"type": "integer",
|
||||
"enum": [0, 1],
|
||||
"enum": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"default": 1
|
||||
},
|
||||
"externalLinkTarget": {
|
||||
"title": "External links target window",
|
||||
"description": "Controls how external links will be opened.\n 0 = default.\n 1 = replaces current window.\n 2 = new window/tab.\n 3 = parent.\n 4 = in top window.",
|
||||
"type": "integer",
|
||||
"enum": [0, 1, 2, 3, 4],
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"disablePageLabels": {
|
||||
@ -164,12 +152,22 @@
|
||||
},
|
||||
"annotationMode": {
|
||||
"type": "integer",
|
||||
"enum": [0, 1, 2, 3],
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"default": 2
|
||||
},
|
||||
"annotationEditorMode": {
|
||||
"type": "integer",
|
||||
"enum": [-1, 0, 3, 15],
|
||||
"enum": [
|
||||
-1,
|
||||
0,
|
||||
3,
|
||||
15
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"enablePermissions": {
|
||||
@ -200,14 +198,25 @@
|
||||
"title": "Scroll mode on load",
|
||||
"description": "Controls how the viewer scrolls upon load.\n -1 = Default (uses the last position if available/enabled).\n 3 = Page scrolling.\n 0 = Vertical scrolling.\n 1 = Horizontal scrolling.\n 2 = Wrapped scrolling.",
|
||||
"type": "integer",
|
||||
"enum": [-1, 0, 1, 2, 3],
|
||||
"enum": [
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"default": -1
|
||||
},
|
||||
"spreadModeOnLoad": {
|
||||
"title": "Spread mode on load",
|
||||
"description": "Whether the viewer should join pages into spreads upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = No spreads.\n 1 = Odd spreads.\n 2 = Even spreads.",
|
||||
"type": "integer",
|
||||
"enum": [-1, 0, 1, 2],
|
||||
"enum": [
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"default": -1
|
||||
},
|
||||
"forcePageColors": {
|
||||
@ -224,11 +233,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": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
}
|
||||
});
|
||||
|
||||
@ -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>
|
||||
31
extensions/chromium/restoretab.js
Normal file
31
extensions/chromium/restoretab.js
Normal 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();
|
||||
@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
26
extensions/firefox/.eslintrc
Normal file
26
extensions/firefox/.eslintrc
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
// 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",
|
||||
],
|
||||
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
},
|
||||
|
||||
"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",
|
||||
},
|
||||
}
|
||||
@ -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")
|
||||
);
|
||||
133
extensions/firefox/tools/l10n.js
Normal file
133
extensions/firefox/tools/l10n.js
Normal file
@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
// Small subset of the webL10n API by Fabien Cazenave for PDF.js extension.
|
||||
(function (window) {
|
||||
let gL10nData = null;
|
||||
let gLanguage = "";
|
||||
let gExternalLocalizerServices = null;
|
||||
let gReadyState = "loading";
|
||||
|
||||
// fetch an l10n objects
|
||||
function getL10nData(key) {
|
||||
gL10nData ||= gExternalLocalizerServices.getStrings();
|
||||
|
||||
const data = gL10nData?.[key];
|
||||
if (!data) {
|
||||
console.warn("[l10n] #" + key + " missing for [" + gLanguage + "]");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// replace {{arguments}} with their values
|
||||
function substArguments(text, args) {
|
||||
if (!args) {
|
||||
return text;
|
||||
}
|
||||
return text.replaceAll(/\{\{\s*(\w+)\s*\}\}/g, function (all, name) {
|
||||
return name in args ? args[name] : "{{" + name + "}}";
|
||||
});
|
||||
}
|
||||
|
||||
// translate a string
|
||||
function translateString(key, args, fallback) {
|
||||
const i = key.lastIndexOf(".");
|
||||
let name, property;
|
||||
if (i >= 0) {
|
||||
name = key.substring(0, i);
|
||||
property = key.substring(i + 1);
|
||||
} else {
|
||||
name = key;
|
||||
property = "textContent";
|
||||
}
|
||||
const data = getL10nData(name);
|
||||
const value = data?.[property] || fallback;
|
||||
if (!value) {
|
||||
return "{{" + key + "}}";
|
||||
}
|
||||
return substArguments(value, args);
|
||||
}
|
||||
|
||||
// translate an HTML element
|
||||
function translateElement(element) {
|
||||
if (!element?.dataset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the related l10n object
|
||||
const key = element.dataset.l10nId;
|
||||
const data = getL10nData(key);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get arguments (if any)
|
||||
// TODO: more flexible parser?
|
||||
let args;
|
||||
if (element.dataset.l10nArgs) {
|
||||
try {
|
||||
args = JSON.parse(element.dataset.l10nArgs);
|
||||
} catch {
|
||||
console.warn("[l10n] could not parse arguments for #" + key + "");
|
||||
}
|
||||
}
|
||||
|
||||
// translate element
|
||||
// TODO: security check?
|
||||
for (const k in data) {
|
||||
element[k] = substArguments(data[k], args);
|
||||
}
|
||||
}
|
||||
|
||||
// translate an HTML subtree
|
||||
function translateFragment(element) {
|
||||
element ||= document.querySelector("html");
|
||||
|
||||
// check all translatable children (= w/ a `data-l10n-id' attribute)
|
||||
const children = element.querySelectorAll("*[data-l10n-id]");
|
||||
const elementCount = children.length;
|
||||
for (let i = 0; i < elementCount; i++) {
|
||||
translateElement(children[i]);
|
||||
}
|
||||
|
||||
// translate element itself if necessary
|
||||
if (element.dataset.l10nId) {
|
||||
translateElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
// Public API
|
||||
document.mozL10n = {
|
||||
// get a localized string
|
||||
get: translateString,
|
||||
|
||||
// get the document language
|
||||
getLanguage() {
|
||||
return gLanguage;
|
||||
},
|
||||
|
||||
// get the direction (ltr|rtl) of the current language
|
||||
getDirection() {
|
||||
// http://www.w3.org/International/questions/qa-scripts
|
||||
// Arabic, Hebrew, Farsi, Pashto, Urdu
|
||||
const rtlList = ["ar", "he", "fa", "ps", "ur"];
|
||||
|
||||
// use the short language code for "full" codes like 'ar-sa' (issue 5440)
|
||||
const shortCode = gLanguage.split("-")[0];
|
||||
|
||||
return rtlList.includes(shortCode) ? "rtl" : "ltr";
|
||||
},
|
||||
|
||||
getReadyState() {
|
||||
return gReadyState;
|
||||
},
|
||||
|
||||
setExternalLocalizerServices(externalLocalizerServices) {
|
||||
gExternalLocalizerServices = externalLocalizerServices;
|
||||
gLanguage = gExternalLocalizerServices.getLocale();
|
||||
gReadyState = "complete";
|
||||
},
|
||||
|
||||
// translate an element or document fragment
|
||||
translate: translateFragment,
|
||||
};
|
||||
})(this);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user