Compare commits

..

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

534 changed files with 29157 additions and 55122 deletions

13
.eslintignore Normal file
View File

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

267
.eslintrc Normal file
View File

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

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

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

View File

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

View File

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

View File

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

13
.github/SECURITY.md vendored
View File

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

BIN
.github/security.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -11,7 +11,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node-version: [20, 22, 23] node-version: [18, lts/*, 21]
steps: steps:
- name: Checkout repository - name: Checkout repository
@ -25,7 +25,7 @@ jobs:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm install
- name: Run external tests - name: Run external tests
run: npx gulp externaltest run: npx gulp externaltest

View File

@ -27,10 +27,10 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Use Python 3.13 - name: Use Python 3.12
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: '3.13' python-version: '3.12'
cache: 'pip' cache: 'pip'
- name: Install Fluent dependencies - name: Install Fluent dependencies

View File

@ -46,12 +46,12 @@ jobs:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm install
- name: Use Python 3.13 - name: Use Python 3.12
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: '3.13' python-version: '3.12'
cache: 'pip' cache: 'pip'
- name: Install Fonttools - name: Install Fonttools

View File

@ -25,7 +25,7 @@ jobs:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm install
- name: Run lint - name: Run lint
run: npx gulp lint run: npx gulp lint

View File

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

View File

@ -27,7 +27,7 @@ jobs:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm install
- name: Build the website - name: Build the website
run: npx gulp web run: npx gulp web

View File

@ -25,7 +25,7 @@ jobs:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm install
- name: Run types tests - name: Run types tests
run: npx gulp typestest run: npx gulp typestest

7
.gitpod.Dockerfile vendored Normal file
View File

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

13
.gitpod.yml Normal file
View File

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

View File

@ -6,7 +6,6 @@ external/bcmaps/
external/builder/fixtures/ external/builder/fixtures/
external/builder/fixtures_babel/ external/builder/fixtures_babel/
external/quickjs/ external/quickjs/
test/stats/results/
test/tmp/ test/tmp/
test/pdfs/ test/pdfs/
web/locale/ web/locale/

View File

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

View File

@ -6,7 +6,6 @@ external/bcmaps/
external/builder/fixtures/ external/builder/fixtures/
external/builder/fixtures_babel/ external/builder/fixtures_babel/
external/quickjs/ external/quickjs/
test/stats/results/
test/tmp/ test/tmp/
test/pdfs/ test/pdfs/
web/locale/ web/locale/

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# PDF.js [![CI](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml/badge.svg?query=branch%3Amaster)](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml?query=branch%3Amaster) # PDF.js [![Build Status](https://github.com/mozilla/pdf.js/workflows/CI/badge.svg?branch=master)](https://github.com/mozilla/pdf.js/actions?query=workflow%3ACI+branch%3Amaster)
[PDF.js](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5. [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). + 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).* *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 Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the
directory `build/chromium`. directory `build/chromium`.
@ -52,15 +52,24 @@ To get a local copy of the current code, clone it using git:
$ cd pdf.js $ cd pdf.js
Next, install Node.js via the [official package](https://nodejs.org) or via Next, install Node.js via the [official package](https://nodejs.org) or via
[nvm](https://github.com/creationix/nvm). If everything worked out, install [nvm](https://github.com/creationix/nvm). You need to install the gulp package
all dependencies for PDF.js: globally (see also [gulp's getting started](https://github.com/gulpjs/gulp/tree/master/docs/getting-started)):
$ npm install -g gulp-cli@^2.3.0
If you prefer to not install `gulp-cli` globally, you have to prefix all the `gulp` commands with `npx` (for example, `npx gulp server` instead of `gulp server`).
If everything worked out, install all dependencies for PDF.js:
$ npm install $ npm install
> [!NOTE]
> On MacOS M1/M2 you may see some `node-gyp`-related errors when running `npm install`. This is because one of our dependencies, `"canvas"`, does not provide pre-built binaries for this platform and instead `npm` will try to build it from source. Please make sure to first install the necessary native dependencies using `brew`: https://github.com/Automattic/node-canvas#compiling.
Finally, you need to start a local web server as some browsers do not allow opening Finally, you need to start a local web server as some browsers do not allow opening
PDF files using a `file://` URL. Run: PDF files using a `file://` URL. Run:
$ npx gulp server $ gulp server
and then you can open: and then you can open:
@ -77,11 +86,11 @@ It is also possible to view all test PDF files on the right side by opening:
In order to bundle all `src/` files into two production scripts and build the generic In order to bundle all `src/` files into two production scripts and build the generic
viewer, run: viewer, run:
$ npx gulp generic $ gulp generic
If you need to support older browsers, run: 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/`). This will generate `pdf.js` and `pdf.worker.js` in the `build/generic/build/` directory (respectively `build/generic-legacy/build/`).
Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will
@ -90,7 +99,7 @@ be loaded by `pdf.js`. The PDF.js files are large and should be minified for pro
## Using PDF.js in a web application ## 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 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 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. [wiki page](https://github.com/mozilla/pdf.js/wiki/Setup-pdf.js-in-a-website) on this subject.
@ -107,7 +116,7 @@ You can play with the PDF.js API directly from your browser using the live demos
+ [Interactive examples](https://mozilla.github.io/pdf.js/examples/index.html#interactive-examples) + [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 For an introduction to the PDF.js code, check out the presentation by our
contributor Julian Viereck: contributor Julian Viereck:
@ -134,4 +143,4 @@ Talk to us on Matrix:
File an issue: File an issue:
+ https://github.com/mozilla/pdf.js/issues/new/choose + https://github.com/mozilla/pdf.js/issues/new

20
docs/config.json Normal file
View File

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

View File

@ -1,11 +1,8 @@
--- ---
title: API title: API
layout: layout.njk template: layout.jade
slug: api
--- ---
# API # 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. 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).
<iframe src="draft/index.html" title="PDF.js API documentation"></iframe>

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,7 +1,6 @@
--- ---
title: Examples title: Examples
layout: layout.njk template: layout.jade
slug: examples
--- ---
## Hello World Walkthrough ## 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. The example demonstrates how promises can be used to handle errors during loading.
It also demonstrates how to wait until a page is loaded and rendered. It also demonstrates how to wait until a page is loaded and rendered.
<script async src="https://jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script>
### Hello World using base64 encoded PDF ### Hello World using base64 encoded PDF
The PDF.js can accept any decoded base64 data as an array. The PDF.js can accept any decoded base64 data as an array.
<script async src="https://jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script>
### Previous/Next example ### Previous/Next example
The same canvas cannot be used to perform to draw two pages at the same time -- The same canvas cannot be used to perform to draw two pages at the same time --
the example demonstrates how to wait on previous operation to be complete. the example demonstrates how to wait on previous operation to be complete.
<script async src="https://jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script> <script async src="//jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script>

View File

@ -1,7 +1,6 @@
--- ---
title: Getting Started title: Getting Started
layout: layout.njk template: layout.jade
slug: getting_started
--- ---
# Getting Started # Getting Started
@ -77,7 +76,7 @@ Note that we only mention the most relevant files and folders.
### Prebuilt ### Prebuilt
```plaintext ```
├── build/ ├── build/
│ ├── pdf.mjs - display layer │ ├── pdf.mjs - display layer
│ ├── pdf.mjs.map - display layer's source map │ ├── pdf.mjs.map - display layer's source map
@ -97,7 +96,7 @@ Note that we only mention the most relevant files and folders.
### Source ### Source
```plaintext ```
├── docs/ - website source code ├── docs/ - website source code
├── examples/ - simple usage examples ├── examples/ - simple usage examples
├── extensions/ - browser extension source code ├── extensions/ - browser extension source code
@ -120,7 +119,7 @@ Note that we only mention the most relevant files and folders.
## Trying the Viewer ## 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 ## More Information

View File

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

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

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

View File

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

View File

@ -1,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
View File

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

View File

@ -9,8 +9,8 @@
<h1>'Previous/Next' example</h1> <h1>'Previous/Next' example</h1>
<div> <div>
<button id="prev" type="button">Previous</button> <button id="prev">Previous</button>
<button id="next" type="button">Next</button> <button id="next">Next</button>
&nbsp; &nbsp; &nbsp; &nbsp;
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span> <span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
</div> </div>

View File

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

View File

@ -43,13 +43,13 @@ limitations under the License.
</div> </div>
<footer> <footer>
<button class="toolbarButton pageUp" title="Previous Page" id="previous" type="button"></button> <button class="toolbarButton pageUp" title="Previous Page" id="previous"></button>
<button class="toolbarButton pageDown" title="Next Page" id="next" type="button"></button> <button class="toolbarButton pageDown" title="Next Page" id="next"></button>
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1"> <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1">
<button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut" type="button"></button> <button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut"></button>
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn" type="button"></button> <button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn"></button>
</footer> </footer>
<script src="viewer.mjs" type="module"></script> <script src="viewer.mjs" type="module"></script>

View File

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

9
examples/node/.eslintrc Normal file
View File

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

View File

@ -9,7 +9,9 @@ Install the dependencies and build the PDF.js library:
$ npm install $ npm install
$ gulp dist-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 $ cd examples/node/pdf2png
$ node pdf2png.mjs $ node pdf2png.js

View File

@ -13,9 +13,41 @@
* limitations under the License. * limitations under the License.
*/ */
import { strict as assert } from "assert";
import Canvas from "canvas";
import fs from "fs"; import fs from "fs";
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
class NodeCanvasFactory {
create(width, height) {
assert(width > 0 && height > 0, "Invalid canvas size");
const canvas = Canvas.createCanvas(width, height);
const context = canvas.getContext("2d");
return {
canvas,
context,
};
}
reset(canvasAndContext, width, height) {
assert(canvasAndContext.canvas, "Canvas is not specified");
assert(width > 0 && height > 0, "Invalid canvas size");
canvasAndContext.canvas.width = width;
canvasAndContext.canvas.height = height;
}
destroy(canvasAndContext) {
assert(canvasAndContext.canvas, "Canvas is not specified");
// Zeroing the width and height cause Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
canvasAndContext.canvas.width = 0;
canvasAndContext.canvas.height = 0;
canvasAndContext.canvas = null;
canvasAndContext.context = null;
}
}
// Some PDFs need external cmaps. // Some PDFs need external cmaps.
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/"; const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true; const CMAP_PACKED = true;
@ -24,6 +56,8 @@ const CMAP_PACKED = true;
const STANDARD_FONT_DATA_URL = const STANDARD_FONT_DATA_URL =
"../../../node_modules/pdfjs-dist/standard_fonts/"; "../../../node_modules/pdfjs-dist/standard_fonts/";
const canvasFactory = new NodeCanvasFactory();
// Loading file from file system into typed array. // Loading file from file system into typed array.
const pdfPath = const pdfPath =
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf"; process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
@ -35,6 +69,7 @@ const loadingTask = getDocument({
cMapUrl: CMAP_URL, cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED, cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL, standardFontDataUrl: STANDARD_FONT_DATA_URL,
canvasFactory,
}); });
try { try {
@ -43,7 +78,6 @@ try {
// Get the first page. // Get the first page.
const page = await pdfDocument.getPage(1); const page = await pdfDocument.getPage(1);
// Render the page on a Node canvas with 100% scale. // Render the page on a Node canvas with 100% scale.
const canvasFactory = pdfDocument.canvasFactory;
const viewport = page.getViewport({ scale: 1.0 }); const viewport = page.getViewport({ scale: 1.0 });
const canvasAndContext = canvasFactory.create( const canvasAndContext = canvasFactory.create(
viewport.width, viewport.width,
@ -57,7 +91,7 @@ try {
const renderTask = page.render(renderContext); const renderTask = page.render(renderContext);
await renderTask.promise; await renderTask.promise;
// Convert the canvas to an image buffer. // Convert the canvas to an image buffer.
const image = canvasAndContext.canvas.toBuffer("image/png"); const image = canvasAndContext.canvas.toBuffer();
fs.writeFile("output.png", image, function (error) { fs.writeFile("output.png", image, function (error) {
if (error) { if (error) {
console.error("Error: " + error); console.error("Error: " + error);

View File

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

View File

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

View File

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

View File

@ -17,12 +17,13 @@ limitations under the License.
"use strict"; "use strict";
(function ExtensionRouterClosure() { (function ExtensionRouterClosure() {
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html"); var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
var CRX_BASE_URL = chrome.runtime.getURL("/"); var CRX_BASE_URL = chrome.extension.getURL("/");
var schemes = [ var schemes = [
"http", "http",
"https", "https",
"ftp",
"file", "file",
"chrome-extension", "chrome-extension",
"blob", "blob",
@ -55,50 +56,73 @@ limitations under the License.
return undefined; return undefined;
} }
function resolveViewerURL(originalUrl) { // TODO(rob): Use declarativeWebRequest once declared URL-encoding is
if (originalUrl.startsWith(CRX_BASE_URL)) { // 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 // This listener converts chrome-extension://.../http://...pdf to
// chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
var url = parseExtensionURL(originalUrl); var url = parseExtensionURL(details.url);
if (url) { if (url) {
url = VIEWER_URL + "?file=" + url; url = VIEWER_URL + "?file=" + url;
var i = originalUrl.indexOf("#"); var i = details.url.indexOf("#");
if (i > 0) { if (i > 0) {
url += originalUrl.slice(i); url += details.url.slice(i);
} }
return url; console.log("Redirecting " + details.url + " to " + url);
} return { redirectUrl: 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 });
} }
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."); console.log("Set up extension URL router.");
Object.keys(localStorage).forEach(function (key) {
// The localStorage item is set upon unload by chromecom.js.
var parsedKey = /^unload-(\d+)-(true|false)-(.+)/.exec(key);
if (parsedKey) {
var timeStart = parseInt(parsedKey[1], 10);
var isHidden = parsedKey[2] === "true";
var url = parsedKey[3];
if (Date.now() - timeStart < 3000) {
// Is it a new item (younger than 3 seconds)? Assume that the extension
// just reloaded, so restore the tab (work-around for crbug.com/511670).
chrome.tabs.create({
url:
chrome.runtime.getURL("restoretab.html") +
"?" +
encodeURIComponent(url) +
"#" +
encodeURIComponent(localStorage.getItem(key)),
active: !isHidden,
});
}
localStorage.removeItem(key);
}
});
})(); })();

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

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

View File

@ -13,14 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
"use strict"; /* eslint strict: ["error", "function"] */
chrome.runtime.onInstalled.addListener(({ reason }) => { (function () {
if (reason !== "update") { "use strict";
// We only need to run migration logic for extension updates, not for new
// installs or browser updates.
return;
}
var storageLocal = chrome.storage.local; var storageLocal = chrome.storage.local;
var storageSync = chrome.storage.sync; 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 schema_location = chrome.runtime.getManifest().storage.managed_schema;
var res = await fetch(chrome.runtime.getURL(schema_location)); x.open("get", chrome.runtime.getURL(schema_location));
var storageManifest = await res.json(); x.onload = function () {
var storageKeys = Object.keys(storageManifest.properties); var storageKeys = Object.keys(x.response.properties);
callback(storageKeys); callback(storageKeys);
};
x.responseType = "json";
x.send();
} }
// Save |values| to storage.sync and delete the values with that key from // Save |values| to storage.sync and delete the values with that key from
@ -150,4 +150,4 @@ chrome.runtime.onInstalled.addListener(({ reason }) => {
} }
); );
} }
}); })();

View File

@ -19,28 +19,23 @@ limitations under the License.
<meta charset="utf-8"> <meta charset="utf-8">
<title>PDF.js viewer options</title> <title>PDF.js viewer options</title>
<style> <style>
/* TODO: Remove as much custom CSS as possible - crbug.com/446511 */
body { body {
min-width: 400px; /* a page at the settings page is at least 400px wide */ 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 */ margin: 14px 17px; /* already added by default in Chrome 40.0.2212.0 */
} }
.settings-row { .settings-row {
margin: 1em 0; margin: 0.65em 0;
}
.checkbox label {
display: inline-flex;
align-items: center;
}
.checkbox label input {
flex-shrink: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="settings-boxes"></div> <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"> <template id="checkbox-template">
<div class="settings-row checkbox"> <!-- Chromium's style: //src/extensions/renderer/resources/extension.css -->
<div class="checkbox">
<label> <label>
<input type="checkbox"> <input type="checkbox">
<span></span> <span></span>

View File

@ -0,0 +1,45 @@
/*
Copyright 2014 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
"use strict";
(function PageActionClosure() {
/**
* @param {number} tabId - ID of tab where the page action will be shown.
* @param {string} url - URL to be displayed in page action.
*/
function showPageAction(tabId, displayUrl) {
// rewriteUrlClosure in viewer.js ensures that the URL looks like
// chrome-extension://[extensionid]/http://example.com/file.pdf
var url = /^chrome-extension:\/\/[a-p]{32}\/([^#]+)/.exec(displayUrl);
if (url) {
url = url[1];
chrome.pageAction.setPopup({
tabId,
popup: "/pageAction/popup.html?file=" + encodeURIComponent(url),
});
chrome.pageAction.show(tabId);
} else {
console.log("Unable to get PDF url from " + displayUrl);
}
}
chrome.runtime.onMessage.addListener(function (message, sender) {
if (message === "showPageAction" && sender.tab) {
showPageAction(sender.tab.id, sender.tab.url);
}
});
})();

View File

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

View File

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

View File

@ -0,0 +1,102 @@
/*
Copyright 2014 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint strict: ["error", "function"] */
/* import-globals-from pdfHandler.js */
(function () {
"use strict";
if (!chrome.fileBrowserHandler) {
// Not on Chromium OS, bail out
return;
}
chrome.fileBrowserHandler.onExecute.addListener(onExecuteFileBrowserHandler);
/**
* Invoked when "Open with PDF Viewer" is chosen in the File browser.
*
* @param {string} id File browser action ID as specified in
* manifest.json
* @param {Object} details Object of type FileHandlerExecuteEventDetails
*/
function onExecuteFileBrowserHandler(id, details) {
if (id !== "open-as-pdf") {
return;
}
var fileEntries = details.entries;
// "tab_id" is the currently documented format, but it is inconsistent with
// the other Chrome APIs that use "tabId" (http://crbug.com/179767)
var tabId = details.tab_id || details.tabId;
if (tabId > 0) {
chrome.tabs.get(tabId, function (tab) {
openViewer(tab && tab.windowId, fileEntries);
});
} else {
// Re-use existing window, if available.
chrome.windows.getLastFocused(function (chromeWindow) {
var windowId = chromeWindow && chromeWindow.id;
if (windowId) {
chrome.windows.update(windowId, { focused: true });
}
openViewer(windowId, fileEntries);
});
}
}
/**
* Open the PDF Viewer for the given list of PDF files.
*
* @param {number} windowId
* @param {Array} fileEntries List of Entry objects (HTML5 FileSystem API)
*/
function openViewer(windowId, fileEntries) {
if (!fileEntries.length) {
return;
}
var fileEntry = fileEntries.shift();
var url = fileEntry.toURL();
// Use drive: alias to get shorter (more human-readable) URLs.
url = url.replace(
/^filesystem:chrome-extension:\/\/[a-p]{32}\/external\//,
"drive:"
);
url = getViewerURL(url);
if (windowId) {
chrome.tabs.create(
{
windowId,
active: true,
url,
},
function () {
openViewer(windowId, fileEntries);
}
);
} else {
chrome.windows.create(
{
type: "normal",
focused: true,
url,
},
function (chromeWindow) {
openViewer(chromeWindow.id, fileEntries);
}
);
}
}
})();

View File

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

View File

@ -13,256 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/* import-globals-from preserve-referer.js */
/* globals canRequestBody */ // From preserve-referer.js
"use strict"; "use strict";
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html"); var VIEWER_URL = chrome.extension.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],
});
}
}
function getViewerURL(pdf_url) { function getViewerURL(pdf_url) {
// |pdf_url| may contain a fragment such as "#page=2". That should be passed // |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; 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 * @param {Object} details First argument of the webRequest.onHeadersReceived
// API though, and we can replace the tab with the viewer. * event. The property "url" is read.
// The viewer will detect that it has no access to file:-URLs, and prompt the * @returns {boolean} True if the PDF file should be downloaded.
// user to activate file permissions. */
chrome.webNavigation.onBeforeNavigate.addListener( function isPdfDownloadable(details) {
function (details) { if (details.url.includes("pdfjs.action=download")) {
// Note: pdfjs.action=download is not checked here because that code path return true;
// is not reachable for local files through the viewer when we do not have }
// file:-access. // Display the PDF viewer regardless of the Content-Disposition header if the
if (details.frameId === 0) { // file is displayed in the main frame, since most often users want to view
chrome.extension.isAllowedFileSchemeAccess(function (isAllowedAccess) { // a PDF, and servers are often misconfigured.
if (isAllowedAccess) { // If the query string contains "=download", do not unconditionally force the
// Expected to be handled by DNR. Don't do anything. // viewer to open the PDF, but first check whether the Content-Disposition
return; // 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, { chrome.tabs.update(details.tabId, {
url: getViewerURL(details.url), 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) { chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message && message.action === "getParentOrigin") { if (message && message.action === "getParentOrigin") {
@ -358,11 +245,6 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
url, url,
}); });
} }
return undefined;
}
if (message && message.action === "canRequestBody") {
sendResponse(canRequestBody(sender.tab.id, sender.frameId));
return undefined;
} }
return undefined; return undefined;
}); });

View File

@ -45,37 +45,7 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"enableHWA": { "enableML": {
"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", "type": "boolean",
"default": false "default": false
}, },
@ -98,6 +68,10 @@
"description": "Whether to allow execution of active content (JavaScript) by PDF files.", "description": "Whether to allow execution of active content (JavaScript) by PDF files.",
"default": false "default": false
}, },
"enableHighlightEditor": {
"type": "boolean",
"default": false
},
"enableHighlightFloatingButton": { "enableHighlightFloatingButton": {
"type": "boolean", "type": "boolean",
"default": false "default": false
@ -106,6 +80,10 @@
"type": "string", "type": "string",
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F" "default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
}, },
"enableStampEditor": {
"type": "boolean",
"default": true
},
"disableRange": { "disableRange": {
"title": "Disable range requests", "title": "Disable range requests",
"description": "Whether to disable range requests (not recommended).", "description": "Whether to disable range requests (not recommended).",
@ -224,11 +202,6 @@
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode", "description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
"type": "string", "type": "string",
"default": "CanvasText" "default": "CanvasText"
},
"enableAutoLinking": {
"description": "Enable creation of hyperlinks from text that look like URLs.",
"type": "boolean",
"default": false
} }
} }
} }

View File

@ -13,14 +13,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/* import-globals-from pdfHandler.js */
/* exported saveReferer */
"use strict"; "use strict";
/** /**
* This file is one part of the Referer persistency implementation. The other * This file is one part of the Referer persistency implementation. The other
* part resides in chromecom.js. * part resides in chromecom.js.
* *
* This file collects Referer headers for every http(s) request, and temporarily * This file collects request headers for every http(s) request, and temporarily
* stores the request headers in a dictionary, for REFERRER_IN_MEMORY_TIME ms. * 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 * 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 * 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. * 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. // g_referrers[tabId][frameId] = referrer of PDF frame.
var g_referrers = {}; 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. (function () {
var g_postRequests = {}; var requestFilter = {
urls: ["*://*/*"],
var rIsReferer = /^referer$/i; types: ["main_frame", "sub_frame"],
chrome.webRequest.onSendHeaders.addListener( };
function saveReferer(details) { chrome.webRequest.onSendHeaders.addListener(
const { tabId, frameId, requestHeaders, method } = details; function (details) {
g_referrers[tabId] ??= {}; g_requestHeaders[details.requestId] = details.requestHeaders;
g_referrers[tabId][frameId] = requestHeaders.find(h => },
rIsReferer.test(h.name) requestFilter,
)?.value; ["requestHeaders", "extraHeaders"]
setCanRequestBody(tabId, frameId, method !== "GET"); );
forgetReferrerEventually(tabId); chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
}, chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
{ urls: ["*://*/*"], types: ["main_frame", "sub_frame"] }, chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
["requestHeaders", "extraHeaders"] function forgetHeaders(details) {
); delete g_requestHeaders[details.requestId];
function forgetReferrerEventually(tabId) {
if (g_referrerTimers[tabId]) {
clearTimeout(g_referrerTimers[tabId]);
} }
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 * @param {object} details - onHeadersReceived event data.
// tracking, it is still here to enable re-use of the webRequest listener above. */
function setCanRequestBody(tabId, frameId, isPOST) { function saveReferer(details) {
if (isPOST) { var referer =
g_postRequests[tabId] ??= new Set(); g_requestHeaders[details.requestId] &&
g_postRequests[tabId].add(frameId); getHeaderFromHeaders(g_requestHeaders[details.requestId], "referer");
} else { referer = (referer && referer.value) || "";
g_postRequests[tabId]?.delete(frameId); if (!g_referrers[details.tabId]) {
g_referrers[details.tabId] = {};
} }
g_referrers[details.tabId][details.frameId] = referer;
} }
function canRequestBody(tabId, frameId) { chrome.tabs.onRemoved.addListener(function (tabId) {
// Returns true unless the frame is known to be loaded through a POST request. delete g_referrers[tabId];
// 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);
}
// This method binds a webRequest event handler which adds the Referer header // 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 // 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") { if (port.name !== "chromecom-referrer") {
return; 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 tabId = port.sender.tab.id;
var frameId = port.sender.frameId; var frameId = port.sender.frameId;
var dnrRequestId;
// If the PDF is viewed for the first time, then the referer will be set here. // 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]) || ""; var referer = (g_referrers[tabId] && g_referrers[tabId][frameId]) || "";
port.onMessage.addListener(function (data) { port.onMessage.addListener(function (data) {
// If the viewer was opened directly (without opening a PDF URL first), then // 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) { if (data.referer) {
referer = data.referer; referer = data.referer;
} }
dnrRequestId = data.dnrRequestId; chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
setStickyReferrer(dnrRequestId, tabId, data.requestUrl, referer, () => { if (referer) {
// Acknowledge the message, and include the latest referer for this frame. // Only add a blocking request handler if the referer has to be rewritten.
port.postMessage(referer); 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. // The port is only disconnected when the other end reloads.
port.onDisconnect.addListener(function () { 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) { // Expose some response headers for fetch API calls from PDF.js;
if (!referer) { // This is a work-around for https://crbug.com/784528
unsetStickyReferrer(dnrRequestId); chrome.webRequest.onHeadersReceived.addListener(
callback(); exposeOnHeadersReceived,
return; {
} urls: ["https://*/*"],
const rule = { types: ["xmlhttprequest"],
id: dnrRequestId, tabId,
condition: {
urlFilter: `|${url}|`,
// The viewer and background are presumed to have the same origin:
initiatorDomains: [location.hostname], // = chrome.runtime.id.
resourceTypes: ["xmlhttprequest"],
tabIds: [tabId],
}, },
action: { ["blocking", "responseHeaders"]
type: "modifyHeaders",
requestHeaders: [{ operation: "set", header: "referer", value: referer }],
},
};
chrome.declarativeNetRequest.updateSessionRules(
{ removeRuleIds: [dnrRequestId], addRules: [rule] },
callback
); );
}
function unsetStickyReferrer(dnrRequestId) { function onBeforeSendHeaders(details) {
if (dnrRequestId) { if (details.frameId !== frameId) {
chrome.declarativeNetRequest.updateSessionRules({ return undefined;
removeRuleIds: [dnrRequestId], }
}); var headers = details.requestHeaders;
var refererHeader = getHeaderFromHeaders(headers, "referer");
if (!refererHeader) {
refererHeader = { name: "Referer" };
headers.push(refererHeader);
} else if (
refererHeader.value &&
refererHeader.value.lastIndexOf("chrome-extension:", 0) !== 0
) {
// Sanity check. If the referer is set, and the value is not the URL of
// this extension, then the request was not initiated by this extension.
return undefined;
}
refererHeader.value = referer;
return { requestHeaders: headers };
} }
}
function exposeOnHeadersReceived(details) {
if (details.frameId !== frameId) {
return undefined;
}
var headers = details.responseHeaders;
var aceh = getHeaderFromHeaders(headers, "access-control-expose-headers");
// List of headers that PDF.js uses in src/display/network_utils.js
var acehValue =
"accept-ranges,content-encoding,content-length,content-disposition";
if (aceh) {
aceh.value += "," + acehValue;
} else {
aceh = { name: "Access-Control-Expose-Headers", value: acehValue };
headers.push(aceh);
}
return { responseHeaders: headers };
}
});

View File

@ -1,5 +1,6 @@
/* <!doctype html>
Copyright 2024 Mozilla Foundation <!--
Copyright 2015 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -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. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ -->
<script src="restoretab.js"></script>
"use strict";
importScripts(
"options/migration.js",
"preserve-referer.js",
"pdfHandler.js",
"extension-router.js",
"suppress-update.js",
"telemetry.js"
);

View File

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

View File

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

View File

@ -42,35 +42,8 @@ limitations under the License.
return; return;
} }
// The localStorage API is unavailable in service workers. We store data in maybeSendPing();
// chrome.storage.local and use this "localStorage" object to enable setInterval(maybeSendPing, 36e5);
// 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 });
}
function maybeSendPing() { function maybeSendPing() {
getLoggingPref(function (didOptOut) { getLoggingPref(function (didOptOut) {
@ -88,20 +61,12 @@ limitations under the License.
// send more pings. // send more pings.
return; return;
} }
doSendPing();
});
}
function doSendPing() {
chrome.storage.local.get(localStorage, items => {
Object.assign(localStorage, items);
var lastTime = parseInt(localStorage.telemetryLastTime) || 0; var lastTime = parseInt(localStorage.telemetryLastTime) || 0;
var wasUpdated = didUpdateSinceLastCheck(); var wasUpdated = didUpdateSinceLastCheck();
if (!wasUpdated && Date.now() - lastTime < MINIMUM_TIME_BETWEEN_PING) { if (!wasUpdated && Date.now() - lastTime < MINIMUM_TIME_BETWEEN_PING) {
return; return;
} }
updateLocalStorage("telemetryLastTime", Date.now()); localStorage.telemetryLastTime = Date.now();
var deduplication_id = getDeduplicationId(wasUpdated); var deduplication_id = getDeduplicationId(wasUpdated);
var extension_version = chrome.runtime.getManifest().version; var extension_version = chrome.runtime.getManifest().version;
@ -139,7 +104,7 @@ limitations under the License.
for (const c of buf) { for (const c of buf) {
id += (c >>> 4).toString(16) + (c & 0xf).toString(16); id += (c >>> 4).toString(16) + (c & 0xf).toString(16);
} }
updateLocalStorage("telemetryDeduplicationId", id); localStorage.telemetryDeduplicationId = id;
} }
return id; return id;
} }
@ -154,7 +119,7 @@ limitations under the License.
if (!chromeVersion || localStorage.telemetryLastVersion === chromeVersion) { if (!chromeVersion || localStorage.telemetryLastVersion === chromeVersion) {
return false; return false;
} }
updateLocalStorage("telemetryLastVersion", chromeVersion); localStorage.telemetryLastVersion = chromeVersion;
return true; return true;
} }

View File

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

View File

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

9
external/.eslintrc vendored Normal file
View File

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

View File

@ -1,7 +1,10 @@
import { types as t, transformSync } from "@babel/core"; import { types as t, transformSync } from "@babel/core";
import fs from "fs";
import { join as joinPaths } from "path";
import vm from "vm"; import vm from "vm";
const PDFJS_PREPROCESSOR_NAME = "PDFJSDev"; const PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
const ROOT_PREFIX = "$ROOT/";
function isPDFJSPreprocessor(obj) { function isPDFJSPreprocessor(obj) {
return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME; return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME;
@ -37,6 +40,18 @@ function handlePreprocessorAction(ctx, actionName, args, path) {
return result; return result;
} }
break; break;
case "json":
if (!t.isStringLiteral(arg)) {
throw new Error("Path to JSON is not provided");
}
let jsonPath = arg.value;
if (jsonPath.startsWith(ROOT_PREFIX)) {
jsonPath = joinPaths(
ctx.rootPath,
jsonPath.substring(ROOT_PREFIX.length)
);
}
return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
} }
throw new Error("Unsupported action"); throw new Error("Unsupported action");
} catch (e) { } catch (e) {
@ -169,7 +184,7 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
path.replaceWith(t.importExpression(source)); path.replaceWith(t.importExpression(source));
} }
}, },
"BlockStatement|StaticBlock": { BlockStatement: {
// Visit node in post-order so that recursive flattening // Visit node in post-order so that recursive flattening
// of blocks works correctly. // of blocks works correctly.
exit(path) { exit(path) {
@ -200,10 +215,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
} }
subExpressionIndex++; subExpressionIndex++;
} }
if (node.type === "StaticBlock" && node.body.length === 0) {
path.remove();
}
}, },
}, },
Function: { Function: {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ async function downloadLanguageCodes() {
console.log("Downloading language codes...\n"); console.log("Downloading language codes...\n");
const ALL_LOCALES = const ALL_LOCALES =
"https://raw.githubusercontent.com/mozilla/gecko-dev/master/browser/locales/all-locales"; "https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/locales/all-locales";
const response = await fetch(ALL_LOCALES); const response = await fetch(ALL_LOCALES);
if (!response.ok) { if (!response.ok) {
@ -52,9 +52,8 @@ async function downloadLanguageFiles(root, langCode) {
// Constants for constructing the URLs. Translations are taken from the // Constants for constructing the URLs. Translations are taken from the
// Nightly channel as those are the most recent ones. // Nightly channel as those are the most recent ones.
const MOZ_CENTRAL_ROOT = const MOZ_CENTRAL_ROOT = "https://hg.mozilla.org/l10n-central/";
"https://raw.githubusercontent.com/mozilla-l10n/firefox-l10n/main/"; const MOZ_CENTRAL_PDFJS_DIR = "/raw-file/default/toolkit/toolkit/pdfviewer/";
const MOZ_CENTRAL_PDFJS_DIR = "/toolkit/toolkit/pdfviewer/";
// Defines which files to download for each language. // Defines which files to download for each language.
const files = ["viewer.ftl"]; const files = ["viewer.ftl"];

View File

@ -1,39 +0,0 @@
/*
* The copyright in this software is being made available under the 2-clauses
* BSD License, included below. This software may be subject to other third
* party and contributor rights, including patent rights, and no such rights
* are granted under this license.
*
* Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
* Copyright (c) 2002-2014, Professor Benoit Macq
* Copyright (c) 2003-2014, Antonin Descampe
* Copyright (c) 2003-2009, Francois-Olivier Devaux
* Copyright (c) 2005, Herve Drolon, FreeImage Team
* Copyright (c) 2002-2003, Yannick Verschueren
* Copyright (c) 2001-2003, David Janssens
* Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
* Copyright (c) 2012, CS Systemes d'Information, France
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

View File

@ -1,22 +0,0 @@
Copyright (c) 2024, Mozilla Foundation
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -9,4 +9,4 @@ In order to generate the file `openjpeg.js`:
## Licensing ## Licensing
[OpenJPEG](https://www.openjpeg.org/) is under [BSD 2-clause "Simplified" License](https://github.com/uclouvain/openjpeg/blob/master/LICENSE) [OpenJPEG](https://www.openjpeg.org/) is under [BSD 2-clause "Simplified" License](https://github.com/uclouvain/openjpeg/blob/master/LICENSE)
and [pdf.js.openjpeg](https://github.com/mozilla/pdf.js.openjpeg/) is released under [BSD 2-clause](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license so `openjpeg.js` is released under [BSD 2-clause](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license too. and [pdf.js.openjpeg](https://github.com/mozilla/pdf.js.openjpeg/) is released under [Apache 2](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license so `openjpeg.js` is released under [Apache 2](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license too.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
{
"source": {
"include": ["src/display/api.js"]
},
"opts": {
"destination": "build/jsdoc/"
},
"templates": {
"default": {
"includeDate": false
}
}
}

View File

@ -217,56 +217,9 @@ pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -204,56 +204,9 @@ pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingeb
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -249,56 +249,9 @@ pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -105,14 +105,6 @@ pdfjs-document-properties-button-label = خصائص المستند…
pdfjs-document-properties-file-name = اسم الملف: pdfjs-document-properties-file-name = اسم الملف:
pdfjs-document-properties-file-size = حجم الملف: pdfjs-document-properties-file-size = حجم الملف:
# Variables: # Variables:
# $kb (Number) - the PDF file size in kilobytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } ك.بايت ({ $b } بايتات)
# Variables:
# $mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } م.بايت ({ $b } بايتات)
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes # $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت) pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت)
@ -127,9 +119,6 @@ pdfjs-document-properties-keywords = الكلمات الأساسية:
pdfjs-document-properties-creation-date = تاريخ الإنشاء: pdfjs-document-properties-creation-date = تاريخ الإنشاء:
pdfjs-document-properties-modification-date = تاريخ التعديل: pdfjs-document-properties-modification-date = تاريخ التعديل:
# Variables: # Variables:
# $dateObj (Date) - the creation/modification date and time of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
# Variables:
# $date (Date) - the creation/modification date of the PDF file # $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file # $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }، { $time } pdfjs-document-properties-date-string = { $date }، { $time }
@ -227,7 +216,7 @@ pdfjs-find-next-button =
pdfjs-find-next-button-label = التالي pdfjs-find-next-button-label = التالي
pdfjs-find-highlight-checkbox = أبرِز الكل pdfjs-find-highlight-checkbox = أبرِز الكل
pdfjs-find-match-case-checkbox-label = طابق حالة الأحرف pdfjs-find-match-case-checkbox-label = طابق حالة الأحرف
pdfjs-find-match-diacritics-checkbox-label = طابِق التشكيل pdfjs-find-match-diacritics-checkbox-label = طابِق الحركات
pdfjs-find-entire-word-checkbox-label = كلمات كاملة pdfjs-find-entire-word-checkbox-label = كلمات كاملة
pdfjs-find-reached-top = تابعت من الأسفل بعدما وصلت إلى بداية المستند pdfjs-find-reached-top = تابعت من الأسفل بعدما وصلت إلى بداية المستند
pdfjs-find-reached-bottom = تابعت من الأعلى بعدما وصلت إلى نهاية المستند pdfjs-find-reached-bottom = تابعت من الأعلى بعدما وصلت إلى نهاية المستند
@ -294,9 +283,6 @@ pdfjs-annotation-date-string = { $date }، { $time }
# Some common types are e.g.: "Check", "Text", "Comment", "Note" # Some common types are e.g.: "Check", "Text", "Comment", "Note"
pdfjs-text-annotation-type = pdfjs-text-annotation-type =
.alt = [تعليق { $type }] .alt = [تعليق { $type }]
# Variables:
# $dateObj (Date) - the modification date and time of the annotation
pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
## Password ## Password
@ -320,6 +306,8 @@ pdfjs-editor-stamp-button-label = أضِف أو حرّر الصور
pdfjs-editor-highlight-button = pdfjs-editor-highlight-button =
.title = أبرِز .title = أبرِز
pdfjs-editor-highlight-button-label = أبرِز pdfjs-editor-highlight-button-label = أبرِز
pdfjs-highlight-floating-button =
.title = أبرِز
pdfjs-highlight-floating-button1 = pdfjs-highlight-floating-button1 =
.title = أبرِز .title = أبرِز
.aria-label = أبرِز .aria-label = أبرِز
@ -335,8 +323,6 @@ pdfjs-editor-remove-stamp-button =
.title = أزِل الصورة .title = أزِل الصورة
pdfjs-editor-remove-highlight-button = pdfjs-editor-remove-highlight-button =
.title = أزِل الإبراز .title = أزِل الإبراز
pdfjs-editor-remove-signature-button =
.title = أزِل التوقيع
## ##
@ -353,10 +339,6 @@ pdfjs-editor-stamp-add-image-button-label = أضِف صورة
pdfjs-editor-free-highlight-thickness-input = السماكة pdfjs-editor-free-highlight-thickness-input = السماكة
pdfjs-editor-free-highlight-thickness-title = pdfjs-editor-free-highlight-thickness-title =
.title = غيّر السُمك عند إبراز عناصر أُخرى غير النص .title = غيّر السُمك عند إبراز عناصر أُخرى غير النص
# .default-content is used as a placeholder in an empty text editor.
pdfjs-free-text2 =
.aria-label = محرِّر النص
.default-content = ابدأ في كتابة…
pdfjs-free-text = pdfjs-free-text =
.aria-label = محرِّر النص .aria-label = محرِّر النص
pdfjs-free-text-default-content = ابدأ الكتابة… pdfjs-free-text-default-content = ابدأ الكتابة…
@ -367,9 +349,8 @@ pdfjs-ink-canvas =
## Alt-text dialog ## Alt-text dialog
# Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button-label = نص بديل pdfjs-editor-alt-text-button-label = نص بديل
pdfjs-editor-alt-text-edit-button =
.aria-label = حرّر النص البديل
pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل
pdfjs-editor-alt-text-dialog-label = اختر خيار pdfjs-editor-alt-text-dialog-label = اختر خيار
pdfjs-editor-alt-text-dialog-description = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها. pdfjs-editor-alt-text-dialog-description = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها.
@ -383,9 +364,6 @@ pdfjs-editor-alt-text-decorative-tooltip = عُلّمت على أنها زخرف
# .placeholder: This is a placeholder for the alt text input area # .placeholder: This is a placeholder for the alt text input area
pdfjs-editor-alt-text-textarea = pdfjs-editor-alt-text-textarea =
.placeholder = على سبيل المثال، "يجلس شاب على الطاولة لتناول وجبة" .placeholder = على سبيل المثال، "يجلس شاب على الطاولة لتناول وجبة"
# Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button =
.aria-label = نص بديل
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
@ -398,22 +376,6 @@ pdfjs-editor-resizer-label-bottom-right = الزاوية اليُمنى السُ
pdfjs-editor-resizer-label-bottom-middle = أسفل الوسط - غيّر الحجم pdfjs-editor-resizer-label-bottom-middle = أسفل الوسط - غيّر الحجم
pdfjs-editor-resizer-label-bottom-left = الزاوية اليُسرى السُفلية - غيّر الحجم pdfjs-editor-resizer-label-bottom-left = الزاوية اليُسرى السُفلية - غيّر الحجم
pdfjs-editor-resizer-label-middle-left = مُنتصف اليسار - غيّر الحجم pdfjs-editor-resizer-label-middle-left = مُنتصف اليسار - غيّر الحجم
pdfjs-editor-resizer-top-left =
.aria-label = الزاوية اليُسرى العُليا — غيّر الحجم
pdfjs-editor-resizer-top-middle =
.aria-label = أعلى الوسط - غيّر الحجم
pdfjs-editor-resizer-top-right =
.aria-label = الزاوية اليُمنى العُليا - غيّر الحجم
pdfjs-editor-resizer-middle-right =
.aria-label = اليمين الأوسط - غيّر الحجم
pdfjs-editor-resizer-bottom-right =
.aria-label = الزاوية اليُمنى السُفلى - غيّر الحجم
pdfjs-editor-resizer-bottom-middle =
.aria-label = أسفل الوسط - غيّر الحجم
pdfjs-editor-resizer-bottom-left =
.aria-label = الزاوية اليُسرى السُفلية - غيّر الحجم
pdfjs-editor-resizer-middle-left =
.aria-label = مُنتصف اليسار - غيّر الحجم
## Color picker ## Color picker
@ -440,154 +402,3 @@ pdfjs-editor-colorpicker-red =
pdfjs-editor-highlight-show-all-button-label = أظهِر الكل pdfjs-editor-highlight-show-all-button-label = أظهِر الكل
pdfjs-editor-highlight-show-all-button = pdfjs-editor-highlight-show-all-button =
.title = أظهِر الكل .title = أظهِر الكل
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
# Modal header positioned above a text box where users can edit the alt text.
pdfjs-editor-new-alt-text-dialog-edit-label = حرّر النص البديل (وصف الصورة)
# Modal header positioned above a text box where users can add the alt text.
pdfjs-editor-new-alt-text-dialog-add-label = أضِف النص البديل (وصف الصورة)
pdfjs-editor-new-alt-text-textarea =
.placeholder = اكتب وصفك هنا…
# This text refers to the alt text box above this description. It offers a definition of alt text.
pdfjs-editor-new-alt-text-description = وصف مختصر للأشخاص الذين لا يستطيعون رؤية الصورة أو عندما لا يتم تحميل الصورة.
# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human.
pdfjs-editor-new-alt-text-disclaimer1 = أُنشئ هذا النص البديل تلقائيًا وقد يكون غير دقيق.
pdfjs-editor-new-alt-text-disclaimer-learn-more-url = اطّلع على المزيد
pdfjs-editor-new-alt-text-create-automatically-button-label = أنشئ نص بديل تلقائيًا
pdfjs-editor-new-alt-text-not-now-button = ليس الآن
pdfjs-editor-new-alt-text-error-title = لم يتمكن من إنشاء نص بديل تلقائيًا
pdfjs-editor-new-alt-text-error-description = يُرجى كتابة نص بديلك أو المحاولة مرة أخرى لاحقًا.
pdfjs-editor-new-alt-text-error-close-button = أغلق
# Variables:
# $totalSize (Number) - the total size (in MB) of the AI model.
# $downloadedSize (Number) - the downloaded size (in MB) of the AI model.
# $percent (Number) - the percentage of the downloaded size.
pdfjs-editor-new-alt-text-ai-model-downloading-progress = يُنزّل نموذج الذكاء الاصطناعي للنص البديل ({ $downloadedSize } من { $totalSize } م.بايت)
.aria-valuetext = يُنزّل نموذج الذكاء الاصطناعي للنص البديل ({ $downloadedSize } من { $totalSize } م.بايت)
# This is a button that users can click to edit the alt text they have already added.
pdfjs-editor-new-alt-text-added-button =
.aria-label = أُضِيف نص بديل
pdfjs-editor-new-alt-text-added-button-label = أُضِيف نص بديل
# This is a button that users can click to open the alt text editor and add alt text when it is not present.
pdfjs-editor-new-alt-text-missing-button =
.aria-label = نص بديل مفقود
pdfjs-editor-new-alt-text-missing-button-label = نص بديل مفقود
# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated.
pdfjs-editor-new-alt-text-to-review-button =
.aria-label = راجع النص البديل
pdfjs-editor-new-alt-text-to-review-button-label = راجع النص البديل
# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear.
# Variables:
# $generatedAltText (String) - the generated alt-text.
pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = أُنشئ تلقائيًا: { $generatedAltText }
## Image alt-text settings
pdfjs-image-alt-text-settings-button =
.title = إعدادات النص البديل للصورة
pdfjs-image-alt-text-settings-button-label = إعدادات النص البديل للصورة
pdfjs-editor-alt-text-settings-dialog-label = إعدادات النص البديل للصورة
pdfjs-editor-alt-text-settings-automatic-title = نص بديل تلقائي
pdfjs-editor-alt-text-settings-create-model-button-label = أنشئ نص بديل تلقائيًا
pdfjs-editor-alt-text-settings-create-model-description = يقترح أوصافًا لمساعدة الأشخاص الذين لا يستطيعون رؤية الصورة أو عندما لا يتم تحميل الصورة.
# Variables:
# $totalSize (Number) - the total size (in MB) of the AI model.
pdfjs-editor-alt-text-settings-download-model-label = نموذج الذكاء الاصطناعي للنص البديل ({ $totalSize } م.بايت)
pdfjs-editor-alt-text-settings-ai-model-description = يتم تشغيله محليًا على جهازك حتى تظل بياناتك خاصة. مطلوب للنص البديل التلقائي.
pdfjs-editor-alt-text-settings-delete-model-button = احذف
pdfjs-editor-alt-text-settings-download-model-button = نزّل
pdfjs-editor-alt-text-settings-downloading-model-button = يُنزل…
pdfjs-editor-alt-text-settings-editor-title = مُحرِّر النص البديل
pdfjs-editor-alt-text-settings-show-dialog-button-label = أظهِر مُحرِّر النص البديل على الفور عند إضافة صورة
pdfjs-editor-alt-text-settings-show-dialog-description = يساعدك على التأكد من أن جميع صورك تحتوي على نص بديل.
pdfjs-editor-alt-text-settings-close-button = أغلق
## "Annotations removed" bar
pdfjs-editor-undo-bar-message-highlight = أُزِيل البرز
pdfjs-editor-undo-bar-message-freetext = أُزيل النص
pdfjs-editor-undo-bar-message-ink = أُزِيلت الرسمة
pdfjs-editor-undo-bar-message-stamp = أُزيلت الصورة
pdfjs-editor-undo-bar-message-signature = أُزيل التوقيع
# Variables:
# $count (Number) - the number of removed annotations.
pdfjs-editor-undo-bar-message-multiple =
{ $count ->
[zero] أُزيل لا تعليق
[one] أُزيل تعليق
[two] أُزيل تعليقين
[few] أُزيلت { $count } تعليقات
[many] أُزيل { $count } تعليق
*[other] أُزيل { $count } تعليق
}
pdfjs-editor-undo-bar-undo-button =
.title = تراجع
pdfjs-editor-undo-bar-undo-button-label = تراجع
pdfjs-editor-undo-bar-close-button =
.title = أغلق
pdfjs-editor-undo-bar-close-button-label = أغلق
## Add a signature dialog
pdfjs-editor-add-signature-dialog-label = يتيح هذا النموذج للمستخدم إنشاء توقيع لإضافته إلى مستند PDF. ويمكن للمستخدم تحرير الاسم (الذي يعمل أيضًا كنص بديل)، وحفظ التوقيع بشكل اختياري للاستخدام المتكرر.
pdfjs-editor-add-signature-dialog-title = أضِف توقيعا
## Tab names
# Type is a verb (you can type your name as signature)
pdfjs-editor-add-signature-type-button = اكتب
.title = اكتب
# Draw is a verb (you can draw your signature)
pdfjs-editor-add-signature-draw-button = ارسم
.title = ارسم
pdfjs-editor-add-signature-image-button = صورة
.title = صورة
## Tab panels
pdfjs-editor-add-signature-type-input =
.aria-label = اكتب توقيعك
.placeholder = اكتب توقيعك
pdfjs-editor-add-signature-draw-placeholder = ارسم توقيعك
pdfjs-editor-add-signature-draw-thickness-range-label = السماكة
# Variables:
# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature.
pdfjs-editor-add-signature-draw-thickness-range =
.title = سمك الرسم: { $thickness }
pdfjs-editor-add-signature-image-placeholder = اسحب الملف هنا لرفعه
pdfjs-editor-add-signature-image-browse-link =
{ PLATFORM() ->
[macos] أو اختر ملفات الصور
*[other] أو تصفح ملفات الصور
}
## Controls
pdfjs-editor-add-signature-description-label = الوصف (نص بديل)
pdfjs-editor-add-signature-description-input =
.title = الوصف (نص بديل)
pdfjs-editor-add-signature-description-default-when-drawing = توقيع
pdfjs-editor-add-signature-clear-button-label = امحُ التوقيع
pdfjs-editor-add-signature-clear-button =
.title = امحُ التوقيع
pdfjs-editor-add-signature-save-checkbox = احفظ التوقيع
pdfjs-editor-add-signature-save-warning-message = لقد وصلت إلى الحد الأقصى وهو 5 توقيعات محفوظة. أزِل توقيع واحد لحفظ المزيد.
pdfjs-editor-add-signature-image-upload-error-title = تعذر رفع الصورة.
pdfjs-editor-add-signature-image-upload-error-description = تحقق من اتصال الشبكة لديك أو جرّب صورة أخرى.
pdfjs-editor-add-signature-error-close-button = أغلق
## Dialog buttons
pdfjs-editor-add-signature-cancel-button = ألغِ
pdfjs-editor-add-signature-add-button = أضِف
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -193,56 +193,9 @@ pdfjs-password-cancel-button = Encaboxar
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -249,56 +249,9 @@ pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -51,6 +51,12 @@ pdfjs-download-button-label = Сцягнуць
pdfjs-bookmark-button = pdfjs-bookmark-button =
.title = Дзейная старонка (паглядзець URL-адрас з дзейнай старонкі) .title = Дзейная старонка (паглядзець URL-адрас з дзейнай старонкі)
pdfjs-bookmark-button-label = Цяперашняя старонка pdfjs-bookmark-button-label = Цяперашняя старонка
# Used in Firefox for Android.
pdfjs-open-in-app-button =
.title = Адкрыць у праграме
# Used in Firefox for Android.
# Length of the translation matters since we are in a mobile context, with limited screen estate.
pdfjs-open-in-app-button-label = Адкрыць у праграме
## Secondary toolbar and context menu ## Secondary toolbar and context menu
@ -105,14 +111,6 @@ pdfjs-document-properties-button-label = Уласцівасці дакумент
pdfjs-document-properties-file-name = Назва файла: pdfjs-document-properties-file-name = Назва файла:
pdfjs-document-properties-file-size = Памер файла: pdfjs-document-properties-file-size = Памер файла:
# Variables: # Variables:
# $kb (Number) - the PDF file size in kilobytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байтаў)
# Variables:
# $mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байтаў)
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes # $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт) pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
@ -127,9 +125,6 @@ pdfjs-document-properties-keywords = Ключавыя словы:
pdfjs-document-properties-creation-date = Дата стварэння: pdfjs-document-properties-creation-date = Дата стварэння:
pdfjs-document-properties-modification-date = Дата змянення: pdfjs-document-properties-modification-date = Дата змянення:
# Variables: # Variables:
# $dateObj (Date) - the creation/modification date and time of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
# Variables:
# $date (Date) - the creation/modification date of the PDF file # $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file # $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time } pdfjs-document-properties-date-string = { $date }, { $time }
@ -288,9 +283,6 @@ pdfjs-annotation-date-string = { $date }, { $time }
# Some common types are e.g.: "Check", "Text", "Comment", "Note" # Some common types are e.g.: "Check", "Text", "Comment", "Note"
pdfjs-text-annotation-type = pdfjs-text-annotation-type =
.alt = [{ $type } Annotation] .alt = [{ $type } Annotation]
# Variables:
# $dateObj (Date) - the modification date and time of the annotation
pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
## Password ## Password
@ -314,13 +306,12 @@ pdfjs-editor-stamp-button-label = Дадаць або змяніць выявы
pdfjs-editor-highlight-button = pdfjs-editor-highlight-button =
.title = Вылучэнне .title = Вылучэнне
pdfjs-editor-highlight-button-label = Вылучэнне pdfjs-editor-highlight-button-label = Вылучэнне
pdfjs-highlight-floating-button =
.title = Вылучэнне
pdfjs-highlight-floating-button1 = pdfjs-highlight-floating-button1 =
.title = Падфарбаваць .title = Падфарбаваць
.aria-label = Падфарбаваць .aria-label = Падфарбаваць
pdfjs-highlight-floating-button-label = Падфарбаваць pdfjs-highlight-floating-button-label = Падфарбаваць
pdfjs-editor-signature-button =
.title = Дадаць подпіс
pdfjs-editor-signature-button-label = Дадаць подпіс
## Remove button for the various kind of editor. ## Remove button for the various kind of editor.
@ -332,8 +323,6 @@ pdfjs-editor-remove-stamp-button =
.title = Выдаліць выяву .title = Выдаліць выяву
pdfjs-editor-remove-highlight-button = pdfjs-editor-remove-highlight-button =
.title = Выдаліць падфарбоўку .title = Выдаліць падфарбоўку
pdfjs-editor-remove-signature-button =
.title = Выдаліць подпіс
## ##
@ -350,13 +339,6 @@ pdfjs-editor-stamp-add-image-button-label = Дадаць выяву
pdfjs-editor-free-highlight-thickness-input = Таўшчыня pdfjs-editor-free-highlight-thickness-input = Таўшчыня
pdfjs-editor-free-highlight-thickness-title = pdfjs-editor-free-highlight-thickness-title =
.title = Змяняць таўшчыню пры вылучэнні іншых элементаў, акрамя тэксту .title = Змяняць таўшчыню пры вылучэнні іншых элементаў, акрамя тэксту
pdfjs-editor-signature-add-signature-button =
.title = Дадаць новы подпіс
pdfjs-editor-signature-add-signature-button-label = Дадаць новы подпіс
# .default-content is used as a placeholder in an empty text editor.
pdfjs-free-text2 =
.aria-label = Тэкставы рэдактар
.default-content = Пачніце ўводзіць…
pdfjs-free-text = pdfjs-free-text =
.aria-label = Тэкставы рэдактар .aria-label = Тэкставы рэдактар
pdfjs-free-text-default-content = Пачніце набор тэксту… pdfjs-free-text-default-content = Пачніце набор тэксту…
@ -367,9 +349,8 @@ pdfjs-ink-canvas =
## Alt-text dialog ## Alt-text dialog
# Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button-label = Альтэрнатыўны тэкст pdfjs-editor-alt-text-button-label = Альтэрнатыўны тэкст
pdfjs-editor-alt-text-edit-button =
.aria-label = Змяніць альтэрнатыўны тэкст
pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст
pdfjs-editor-alt-text-dialog-label = Выберыце варыянт pdfjs-editor-alt-text-dialog-label = Выберыце варыянт
pdfjs-editor-alt-text-dialog-description = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца. pdfjs-editor-alt-text-dialog-description = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца.
@ -383,9 +364,6 @@ pdfjs-editor-alt-text-decorative-tooltip = Пазначаны як дэкара
# .placeholder: This is a placeholder for the alt text input area # .placeholder: This is a placeholder for the alt text input area
pdfjs-editor-alt-text-textarea = pdfjs-editor-alt-text-textarea =
.placeholder = Напрыклад, «Малады чалавек садзіцца за стол есці» .placeholder = Напрыклад, «Малады чалавек садзіцца за стол есці»
# Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button =
.aria-label = Альтэрнатыўны тэкст
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
@ -398,22 +376,6 @@ pdfjs-editor-resizer-label-bottom-right = Правы ніжні кут — зм
pdfjs-editor-resizer-label-bottom-middle = Пасярэдзіне ўнізе — змяніць памер pdfjs-editor-resizer-label-bottom-middle = Пасярэдзіне ўнізе — змяніць памер
pdfjs-editor-resizer-label-bottom-left = Левы ніжні кут — змяніць памер pdfjs-editor-resizer-label-bottom-left = Левы ніжні кут — змяніць памер
pdfjs-editor-resizer-label-middle-left = Пасярэдзіне злева — змяніць памер pdfjs-editor-resizer-label-middle-left = Пасярэдзіне злева — змяніць памер
pdfjs-editor-resizer-top-left =
.aria-label = Верхні левы кут — змяніць памер
pdfjs-editor-resizer-top-middle =
.aria-label = Уверсе пасярэдзіне — змяніць памер
pdfjs-editor-resizer-top-right =
.aria-label = Верхні правы кут — змяніць памер
pdfjs-editor-resizer-middle-right =
.aria-label = Пасярэдзіне справа — змяніць памер
pdfjs-editor-resizer-bottom-right =
.aria-label = Правы ніжні кут — змяніць памер
pdfjs-editor-resizer-bottom-middle =
.aria-label = Пасярэдзіне ўнізе — змяніць памер
pdfjs-editor-resizer-bottom-left =
.aria-label = Левы ніжні кут — змяніць памер
pdfjs-editor-resizer-middle-left =
.aria-label = Пасярэдзіне злева — змяніць памер
## Color picker ## Color picker
@ -440,157 +402,3 @@ pdfjs-editor-colorpicker-red =
pdfjs-editor-highlight-show-all-button-label = Паказаць усе pdfjs-editor-highlight-show-all-button-label = Паказаць усе
pdfjs-editor-highlight-show-all-button = pdfjs-editor-highlight-show-all-button =
.title = Паказаць усе .title = Паказаць усе
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
# Modal header positioned above a text box where users can edit the alt text.
pdfjs-editor-new-alt-text-dialog-edit-label = Рэдагаваць тэкст для атрыбута alt (апісанне выявы)
# Modal header positioned above a text box where users can add the alt text.
pdfjs-editor-new-alt-text-dialog-add-label = Дадаць тэкст для атрыбута alt (апісанне выявы)
pdfjs-editor-new-alt-text-textarea =
.placeholder = Напішыце сваё апісанне тут…
# This text refers to the alt text box above this description. It offers a definition of alt text.
pdfjs-editor-new-alt-text-description = Кароткае апісанне для людзей, якія не бачаць выяву, ці калі выява не загружаецца.
# This is a required legal disclaimer that refers to the automatically created text inside the alt text box above this text. It disappears if the text is edited by a human.
pdfjs-editor-new-alt-text-disclaimer1 = Гэты тэкст для атрыбута alt быў створаны аўтаматычна і можа быць недакладным
pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Даведацца больш
pdfjs-editor-new-alt-text-create-automatically-button-label = Ствараць тэкст для атрыбута alt аўтаматычна
pdfjs-editor-new-alt-text-not-now-button = Не зараз
pdfjs-editor-new-alt-text-error-title = Не ўдалося аўтаматычна стварыць тэкст для атрыбута alt
pdfjs-editor-new-alt-text-error-description = Калі ласка, напішыце ўласны тэкст для атрыбута alt або паўтарыце спробу пазней.
pdfjs-editor-new-alt-text-error-close-button = Закрыць
# Variables:
# $totalSize (Number) - the total size (in MB) of the AI model.
# $downloadedSize (Number) - the downloaded size (in MB) of the AI model.
# $percent (Number) - the percentage of the downloaded size.
pdfjs-editor-new-alt-text-ai-model-downloading-progress = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ)
.aria-valuetext = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ)
# This is a button that users can click to edit the alt text they have already added.
pdfjs-editor-new-alt-text-added-button =
.aria-label = Тэкст для атрыбута alt дададзены
pdfjs-editor-new-alt-text-added-button-label = Тэкст для атрыбута alt дададзены
# This is a button that users can click to open the alt text editor and add alt text when it is not present.
pdfjs-editor-new-alt-text-missing-button =
.aria-label = Адсутнічае тэкст для атрыбута alt
pdfjs-editor-new-alt-text-missing-button-label = Адсутнічае тэкст для атрыбута alt
# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated.
pdfjs-editor-new-alt-text-to-review-button =
.aria-label = Водгук на тэкст для атрыбута alt
pdfjs-editor-new-alt-text-to-review-button-label = Водгук на тэкст для атрыбута alt
# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear.
# Variables:
# $generatedAltText (String) - the generated alt-text.
pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Створаны аўтаматычна: { $generatedAltText }
## Image alt-text settings
pdfjs-image-alt-text-settings-button =
.title = Налады альтэрнатыўнага тэксту для выявы
pdfjs-image-alt-text-settings-button-label = Налады альтэрнатыўнага тэксту для выявы
pdfjs-editor-alt-text-settings-dialog-label = Налады альтэрнатыўнага тэксту для выявы
pdfjs-editor-alt-text-settings-automatic-title = Аўтаматычны тэкст для атрыбута alt
pdfjs-editor-alt-text-settings-create-model-button-label = Ствараць тэкст для атрыбута alt аўтаматычна
pdfjs-editor-alt-text-settings-create-model-description = Прапануе апісанні, каб дапамагчы людзям, якія не бачаць выяву, ці калі выява не загружаецца.
# Variables:
# $totalSize (Number) - the total size (in MB) of the AI model.
pdfjs-editor-alt-text-settings-download-model-label = Мадэль ШІ для тэксту для атрыбута alt ({ $totalSize } МБ)
pdfjs-editor-alt-text-settings-ai-model-description = Працуе лакальна на вашай прыладзе, таму вашы звесткі застаюцца прыватнымі. Патрабуецца для аўтаматычнага альтэрнатыўнага тэксту.
pdfjs-editor-alt-text-settings-delete-model-button = Выдаліць
pdfjs-editor-alt-text-settings-download-model-button = Сцягнуць
pdfjs-editor-alt-text-settings-downloading-model-button = Сцягванне…
pdfjs-editor-alt-text-settings-editor-title = Рэдактар тэксту для атрыбута alt
pdfjs-editor-alt-text-settings-show-dialog-button-label = Адразу паказваць рэдактар тэксту для атрыбута alt пры даданні выявы
pdfjs-editor-alt-text-settings-show-dialog-description = Дапамагае пераканацца, што ўсе вашы выявы маюць альтэрнатыўны тэкст.
pdfjs-editor-alt-text-settings-close-button = Закрыць
## "Annotations removed" bar
pdfjs-editor-undo-bar-message-highlight = Падсвятленне выдалена
pdfjs-editor-undo-bar-message-freetext = Тэкст выдалены
pdfjs-editor-undo-bar-message-ink = Малюнак выдалены
pdfjs-editor-undo-bar-message-stamp = Відарыс выдалены
pdfjs-editor-undo-bar-message-signature = Подпіс выдалены
# Variables:
# $count (Number) - the number of removed annotations.
pdfjs-editor-undo-bar-message-multiple =
{ $count ->
[one] { $count } анатацыя выдалена
[few] { $count } анатацыі выдалена
*[many] { $count } анатацый выдалена
}
pdfjs-editor-undo-bar-undo-button =
.title = Адмяніць
pdfjs-editor-undo-bar-undo-button-label = Адмяніць
pdfjs-editor-undo-bar-close-button =
.title = Закрыць
pdfjs-editor-undo-bar-close-button-label = Закрыць
## Add a signature dialog
pdfjs-editor-add-signature-dialog-label = Гэты рэжым дазваляе карыстальніку ствараць подпіс для дадання ў дакумент PDF. Карыстальнік можа рэдагаваць імя (якое таксама служыць альтэрнатыўным тэкстам) і пры жаданні захаваць подпіс для паўторнага выкарыстання.
pdfjs-editor-add-signature-dialog-title = Дадаць подпіс
## Tab names
# Type is a verb (you can type your name as signature)
pdfjs-editor-add-signature-type-button = Увод
.title = Увод
# Draw is a verb (you can draw your signature)
pdfjs-editor-add-signature-draw-button = Маляваць
.title = Маляваць
pdfjs-editor-add-signature-image-button = Выява
.title = Выява
## Tab panels
pdfjs-editor-add-signature-type-input =
.aria-label = Увядзіце свой подпіс
.placeholder = Увядзіце свой подпіс
pdfjs-editor-add-signature-draw-placeholder = Намалюйце свой подпіс
pdfjs-editor-add-signature-draw-thickness-range-label = Таўшчыня
# Variables:
# $thickness (Number) - the thickness (in pixels) of the line used to draw a signature.
pdfjs-editor-add-signature-draw-thickness-range =
.title = Таўшчыня малюнка: { $thickness }
pdfjs-editor-add-signature-image-placeholder = Перацягнуць файл сюды, каб загрузіць
pdfjs-editor-add-signature-image-browse-link =
{ PLATFORM() ->
[macos] Або праглядайце файлы малюнкаў
*[other] Або праглядайце файлы малюнкаў
}
## Controls
pdfjs-editor-add-signature-description-label = Апісанне (альтэрнатыўны тэкст)
pdfjs-editor-add-signature-description-input =
.title = Апісанне (альтэрнатыўны тэкст)
pdfjs-editor-add-signature-description-default-when-drawing = Подпіс
pdfjs-editor-add-signature-clear-button-label = Выдаліць подпіс
pdfjs-editor-add-signature-clear-button =
.title = Выдаліць подпіс
pdfjs-editor-add-signature-save-checkbox = Захаваць подпіс
pdfjs-editor-add-signature-save-warning-message = Вы дасягнулі ліміту ў 5 захаваных подпісаў. Выдаліце адзін, каб захаваць іншы.
pdfjs-editor-add-signature-image-upload-error-title = Не ўдалося загрузіць выяву
pdfjs-editor-add-signature-image-upload-error-description = Праверце падключэнне да сеткі ці паспрабуйце іншую выяву.
pdfjs-editor-add-signature-error-close-button = Закрыць
## Dialog buttons
pdfjs-editor-add-signature-cancel-button = Скасаваць
pdfjs-editor-add-signature-add-button = Дадаць
pdfjs-editor-edit-signature-update-button = Абнавіць
## Main menu for adding/removing signatures
pdfjs-editor-delete-signature-button =
.title = Выдаліць подпіс
pdfjs-editor-delete-signature-button-label = Выдаліць подпіс
## Editor toolbar
pdfjs-editor-add-signature-edit-button-label = Рэдагаваць апісанне
## Edit signature description dialog
pdfjs-editor-edit-signature-dialog-title = Рэдагаваць апісанне

View File

@ -51,6 +51,12 @@ pdfjs-download-button-label = Изтегляне
pdfjs-bookmark-button = pdfjs-bookmark-button =
.title = Текуща страница (преглед на адреса на страницата) .title = Текуща страница (преглед на адреса на страницата)
pdfjs-bookmark-button-label = Текуща страница pdfjs-bookmark-button-label = Текуща страница
# Used in Firefox for Android.
pdfjs-open-in-app-button =
.title = Отваряне в приложение
# Used in Firefox for Android.
# Length of the translation matters since we are in a mobile context, with limited screen estate.
pdfjs-open-in-app-button-label = Отваряне в приложение
## Secondary toolbar and context menu ## Secondary toolbar and context menu
@ -105,14 +111,6 @@ pdfjs-document-properties-button-label = Свойства на документ
pdfjs-document-properties-file-name = Име на файл: pdfjs-document-properties-file-name = Име на файл:
pdfjs-document-properties-file-size = Големина на файл: pdfjs-document-properties-file-size = Големина на файл:
# Variables: # Variables:
# $kb (Number) - the PDF file size in kilobytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байта)
# Variables:
# $mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байта)
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes # $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байта) pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байта)
@ -127,9 +125,6 @@ pdfjs-document-properties-keywords = Ключови думи:
pdfjs-document-properties-creation-date = Дата на създаване: pdfjs-document-properties-creation-date = Дата на създаване:
pdfjs-document-properties-modification-date = Дата на промяна: pdfjs-document-properties-modification-date = Дата на промяна:
# Variables: # Variables:
# $dateObj (Date) - the creation/modification date and time of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
# Variables:
# $date (Date) - the creation/modification date of the PDF file # $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file # $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time } pdfjs-document-properties-date-string = { $date }, { $time }
@ -286,9 +281,6 @@ pdfjs-annotation-date-string = { $date }, { $time }
# Some common types are e.g.: "Check", "Text", "Comment", "Note" # Some common types are e.g.: "Check", "Text", "Comment", "Note"
pdfjs-text-annotation-type = pdfjs-text-annotation-type =
.alt = [Анотация { $type }] .alt = [Анотация { $type }]
# Variables:
# $dateObj (Date) - the modification date and time of the annotation
pdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
## Password ## Password
@ -309,6 +301,8 @@ pdfjs-editor-ink-button-label = Рисуване
pdfjs-editor-stamp-button = pdfjs-editor-stamp-button =
.title = Добавяне или променяне на изображения .title = Добавяне или променяне на изображения
pdfjs-editor-stamp-button-label = Добавяне или променяне на изображения pdfjs-editor-stamp-button-label = Добавяне или променяне на изображения
pdfjs-editor-remove-button =
.title = Премахване
## Remove button for the various kind of editor. ## Remove button for the various kind of editor.
@ -342,6 +336,7 @@ pdfjs-ink-canvas =
## Alt-text dialog ## Alt-text dialog
# Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button-label = Алтернативен текст pdfjs-editor-alt-text-button-label = Алтернативен текст
pdfjs-editor-alt-text-edit-button-label = Промяна на алтернативния текст pdfjs-editor-alt-text-edit-button-label = Промяна на алтернативния текст
pdfjs-editor-alt-text-dialog-label = Изберете от възможностите pdfjs-editor-alt-text-dialog-label = Изберете от възможностите
@ -368,22 +363,6 @@ pdfjs-editor-resizer-label-bottom-right = Долен десен ъгъл — п
pdfjs-editor-resizer-label-bottom-middle = Долу в средата — преоразмеряване pdfjs-editor-resizer-label-bottom-middle = Долу в средата — преоразмеряване
pdfjs-editor-resizer-label-bottom-left = Долен ляв ъгъл — преоразмеряване pdfjs-editor-resizer-label-bottom-left = Долен ляв ъгъл — преоразмеряване
pdfjs-editor-resizer-label-middle-left = Ляво в средата — преоразмеряване pdfjs-editor-resizer-label-middle-left = Ляво в средата — преоразмеряване
pdfjs-editor-resizer-top-left =
.aria-label = Горен ляв ъгъл — преоразмеряване
pdfjs-editor-resizer-top-middle =
.aria-label = Горе в средата — преоразмеряване
pdfjs-editor-resizer-top-right =
.aria-label = Горен десен ъгъл — преоразмеряване
pdfjs-editor-resizer-middle-right =
.aria-label = Дясно в средата — преоразмеряване
pdfjs-editor-resizer-bottom-right =
.aria-label = Долен десен ъгъл — преоразмеряване
pdfjs-editor-resizer-bottom-middle =
.aria-label = Долу в средата — преоразмеряване
pdfjs-editor-resizer-bottom-left =
.aria-label = Долен ляв ъгъл — преоразмеряване
pdfjs-editor-resizer-middle-left =
.aria-label = Ляво в средата — преоразмеряване
## Color picker ## Color picker
@ -403,42 +382,3 @@ pdfjs-editor-colorpicker-pink =
.title = Розово .title = Розово
pdfjs-editor-colorpicker-red = pdfjs-editor-colorpicker-red =
.title = Червено .title = Червено
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
pdfjs-editor-new-alt-text-not-now-button = Не сега
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -239,56 +239,9 @@ pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয়
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -239,56 +239,9 @@ pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fo
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -49,6 +49,12 @@ pdfjs-download-button =
# Length of the translation matters since we are in a mobile context, with limited screen estate. # Length of the translation matters since we are in a mobile context, with limited screen estate.
pdfjs-download-button-label = Pellgargañ pdfjs-download-button-label = Pellgargañ
pdfjs-bookmark-button-label = Pajenn a-vremañ pdfjs-bookmark-button-label = Pajenn a-vremañ
# Used in Firefox for Android.
pdfjs-open-in-app-button =
.title = Digeriñ en arload
# Used in Firefox for Android.
# Length of the translation matters since we are in a mobile context, with limited screen estate.
pdfjs-open-in-app-button-label = Digeriñ en arload
## Secondary toolbar and context menu ## Secondary toolbar and context menu
@ -208,7 +214,6 @@ pdfjs-find-next-button =
pdfjs-find-next-button-label = War-lerc'h pdfjs-find-next-button-label = War-lerc'h
pdfjs-find-highlight-checkbox = Usskediñ pep tra pdfjs-find-highlight-checkbox = Usskediñ pep tra
pdfjs-find-match-case-checkbox-label = Teurel evezh ouzh ar pennlizherennoù pdfjs-find-match-case-checkbox-label = Teurel evezh ouzh ar pennlizherennoù
pdfjs-find-match-diacritics-checkbox-label = Doujañ dan tiredoù
pdfjs-find-entire-word-checkbox-label = Gerioù a-bezh pdfjs-find-entire-word-checkbox-label = Gerioù a-bezh
pdfjs-find-reached-top = Tizhet eo bet derou ar bajenn, kenderc'hel diouzh an diaz pdfjs-find-reached-top = Tizhet eo bet derou ar bajenn, kenderc'hel diouzh an diaz
pdfjs-find-reached-bottom = Tizhet eo bet dibenn ar bajenn, kenderc'hel diouzh ar c'hrec'h pdfjs-find-reached-bottom = Tizhet eo bet dibenn ar bajenn, kenderc'hel diouzh ar c'hrec'h
@ -287,8 +292,6 @@ pdfjs-editor-ink-opacity-input = Boullder
pdfjs-editor-stamp-add-image-button = pdfjs-editor-stamp-add-image-button =
.title = Ouzhpennañ ur skeudenn .title = Ouzhpennañ ur skeudenn
pdfjs-editor-stamp-add-image-button-label = Ouzhpennañ ur skeudenn pdfjs-editor-stamp-add-image-button-label = Ouzhpennañ ur skeudenn
# This refers to the thickness of the line used for free highlighting (not bound to text)
pdfjs-editor-free-highlight-thickness-input = Tevded
pdfjs-free-text = pdfjs-free-text =
.aria-label = Aozer testennoù .aria-label = Aozer testennoù
pdfjs-ink = pdfjs-ink =
@ -308,60 +311,3 @@ pdfjs-editor-alt-text-save-button = Enrollañ
## Color picker ## Color picker
pdfjs-editor-colorpicker-button =
.title = Cheñch liv
pdfjs-editor-colorpicker-yellow =
.title = Melen
pdfjs-editor-colorpicker-blue =
.title = Glas
pdfjs-editor-colorpicker-pink =
.title = Roz
pdfjs-editor-colorpicker-red =
.title = Ruz
## Show all highlights
## This is a toggle button to show/hide all the highlights.
pdfjs-editor-highlight-show-all-button-label = Diskouez pep tra
pdfjs-editor-highlight-show-all-button =
.title = Diskouez pep tra
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
pdfjs-editor-new-alt-text-disclaimer-learn-more-url = Gouzout hiroch
pdfjs-editor-new-alt-text-error-close-button = Serriñ
## Image alt-text settings
pdfjs-editor-alt-text-settings-delete-model-button = Dilemel
pdfjs-editor-alt-text-settings-download-model-button = Pellgargañ
pdfjs-editor-alt-text-settings-downloading-model-button = O pellgargañ…
pdfjs-editor-alt-text-settings-close-button = Serriñ
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -210,56 +210,9 @@ pdfjs-web-fonts-disabled = वेब फन्टखौ लोरबां ख
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -215,56 +215,9 @@ pdfjs-web-fonts-disabled = Web fontovi su onemogućeni: nemoguće koristiti uba
## Editing ## Editing
## Remove button for the various kind of editor.
##
## Alt-text dialog ## Alt-text dialog
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

View File

@ -45,6 +45,12 @@ pdfjs-save-button-label = Desa
pdfjs-bookmark-button = pdfjs-bookmark-button =
.title = Pàgina actual (mostra l'URL de la pàgina actual) .title = Pàgina actual (mostra l'URL de la pàgina actual)
pdfjs-bookmark-button-label = Pàgina actual pdfjs-bookmark-button-label = Pàgina actual
# Used in Firefox for Android.
pdfjs-open-in-app-button =
.title = Obre en una aplicació
# Used in Firefox for Android.
# Length of the translation matters since we are in a mobile context, with limited screen estate.
pdfjs-open-in-app-button-label = Obre en una aplicació
## Secondary toolbar and context menu ## Secondary toolbar and context menu
@ -271,12 +277,6 @@ pdfjs-editor-free-text-button-label = Text
pdfjs-editor-ink-button = pdfjs-editor-ink-button =
.title = Dibuixa .title = Dibuixa
pdfjs-editor-ink-button-label = Dibuixa pdfjs-editor-ink-button-label = Dibuixa
## Remove button for the various kind of editor.
##
# Editor Parameters # Editor Parameters
pdfjs-editor-free-text-color-input = Color pdfjs-editor-free-text-color-input = Color
pdfjs-editor-free-text-size-input = Mida pdfjs-editor-free-text-size-input = Mida
@ -297,44 +297,3 @@ pdfjs-ink-canvas =
## Editor resizers ## Editor resizers
## This is used in an aria label to help to understand the role of the resizer. ## This is used in an aria label to help to understand the role of the resizer.
## Color picker
## Show all highlights
## This is a toggle button to show/hide all the highlights.
## New alt-text dialog
## Group note for entire feature: Alternative text (alt text) helps when people can't see the image. This feature includes a tool to create alt text automatically using an AI model that works locally on the user's device to preserve privacy.
## Image alt-text settings
## "Annotations removed" bar
## Add a signature dialog
## Tab names
## Tab panels
## Controls
## Dialog buttons
## Main menu for adding/removing signatures
## Editor toolbar
## Edit signature description dialog

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