Compare commits

..

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

658 changed files with 30369 additions and 79337 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/
*~/

266
.eslintrc Normal file
View File

@ -0,0 +1,266 @@
{
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module",
},
"plugins": [
"import",
"json",
"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"],
}],
"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-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"],
},
}

View File

@ -6,7 +6,6 @@ body:
attributes: attributes:
label: Attach (recommended) or Link to PDF file label: Attach (recommended) or Link to PDF file
description: Without this information the issue may be closed without comment description: Without this information the issue may be closed without comment
placeholder: Please place only the PDF file in this field
validations: validations:
required: true required: true

View File

@ -1,4 +1,4 @@
blank_issues_enabled: false blank_issues_enabled: true
contact_links: contact_links:
- name: Need help? - name: Need help?
url: https://github.com/mozilla/pdf.js/discussions url: https://github.com/mozilla/pdf.js/discussions

View File

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

View File

@ -11,16 +11,16 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node-version: [20, 22, 24] node-version: [18, lts/*]
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

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

View File

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

View File

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

View File

@ -15,12 +15,12 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

@ -17,12 +17,12 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
@ -34,4 +34,6 @@ jobs:
run: npx gulp dist run: npx gulp dist
- name: Publish the `pdfjs-dist` library to NPM - name: Publish the `pdfjs-dist` library to NPM
run: npm publish ./build/dist run: npm publish ./build/dist --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

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

View File

@ -15,12 +15,12 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

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

@ -5,10 +5,7 @@ node_modules/
external/bcmaps/ external/bcmaps/
external/builder/fixtures/ external/builder/fixtures/
external/builder/fixtures_babel/ external/builder/fixtures_babel/
external/openjpeg/
external/qcms/
external/quickjs/ external/quickjs/
test/stats/results/
test/tmp/ test/tmp/
test/pdfs/ test/pdfs/
web/locale/ web/locale/

View File

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

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

@ -18,18 +18,4 @@ export default {
}, },
], ],
}, },
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.
@ -14,7 +14,7 @@ get involved, visit:
+ [Issue Reporting Guide](https://github.com/mozilla/pdf.js/blob/master/.github/CONTRIBUTING.md) + [Issue Reporting Guide](https://github.com/mozilla/pdf.js/blob/master/.github/CONTRIBUTING.md)
+ [Code Contribution Guide](https://github.com/mozilla/pdf.js/wiki/Contributing) + [Code Contribution Guide](https://github.com/mozilla/pdf.js/wiki/Contributing)
+ [Frequently Asked Questions](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions) + [Frequently Asked Questions](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions)
+ [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?q=is%3Aissue%20state%3Aopen%20label%3Agood-beginner-bug) + [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?direction=desc&labels=good-beginner-bug&page=1&sort=created&state=open)
+ [Projects](https://github.com/mozilla/pdf.js/projects) + [Projects](https://github.com/mozilla/pdf.js/projects)
Feel free to stop by our [Matrix room](https://chat.mozilla.org/#/room/#pdfjs:mozilla.org) for questions or guidance. Feel free to stop by our [Matrix room](https://chat.mozilla.org/#/room/#pdfjs:mozilla.org) for questions or guidance.
@ -57,6 +57,9 @@ 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:
@ -90,7 +93,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.
@ -134,4 +137,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

View File

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

11
examples/.eslintrc Normal file
View File

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

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<!-- <!--
Copyright 2014 Mozilla Foundation Copyright 2014 Mozilla Foundation
@ -15,29 +15,29 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<html dir="ltr" mozdisallowselectionprint> <html dir="ltr" mozdisallowselectionprint>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate">
<title>PDF.js page viewer using built components</title> <title>PDF.js page viewer using built components</title>
<style> <style>
body { body {
background-color: #808080; background-color: #808080;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
</style> </style>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css" /> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<div id="pageContainer" class="pdfViewer singlePageView"></div> <div id="pageContainer" class="pdfViewer singlePageView"></div>
<script src="pageviewer.mjs" type="module"></script> <script src="pageviewer.mjs" type="module"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<!-- <!--
Copyright 2014 Mozilla Foundation Copyright 2014 Mozilla Foundation
@ -15,37 +15,37 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<html dir="ltr" mozdisallowselectionprint> <html dir="ltr" mozdisallowselectionprint>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate">
<title>PDF.js viewer using built components</title> <title>PDF.js viewer using built components</title>
<style> <style>
body { body {
background-color: #808080; background-color: #808080;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
#viewerContainer { #viewerContainer {
overflow: auto; overflow: auto;
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css" /> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<div id="viewerContainer"> <div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div> <div id="viewer" class="pdfViewer"></div>
</div> </div>
<script src="simpleviewer.mjs" type="module"></script> <script src="simpleviewer.mjs" type="module"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<!-- <!--
Copyright 2014 Mozilla Foundation Copyright 2014 Mozilla Foundation
@ -15,37 +15,37 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<html dir="ltr" mozdisallowselectionprint> <html dir="ltr" mozdisallowselectionprint>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate">
<title>PDF.js Single Page Viewer using built components</title> <title>PDF.js Single Page Viewer using built components</title>
<style> <style>
body { body {
background-color: #808080; background-color: #808080;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
#viewerContainer { #viewerContainer {
overflow: auto; overflow: auto;
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css" /> <link rel="stylesheet" href="../../node_modules/pdfjs-dist/web/pdf_viewer.css">
<script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/build/pdf.mjs" type="module"></script>
<script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/web/pdf_viewer.mjs" type="module"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<div id="viewerContainer"> <div id="viewerContainer">
<div id="viewer" class="pdfViewer"></div> <div id="viewer" class="pdfViewer"></div>
</div> </div>
<script src="singlepageviewer.mjs" type="module"></script> <script src="singlepageviewer.mjs" type="module"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
<!doctype html> <!DOCTYPE html>
<!-- <!--
Copyright 2018 Mozilla Foundation Copyright 2018 Mozilla Foundation
@ -15,26 +15,26 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<html dir="ltr" mozdisallowselectionprint> <html dir="ltr" mozdisallowselectionprint>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate">
<title>PDF.js standalone JpegImage parser</title> <title>PDF.js standalone JpegImage parser</title>
<style> <style>
body { body {
background-color: #808080; background-color: #808080;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
</style> </style>
<script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script> <script src="../../node_modules/pdfjs-dist/image_decoders/pdf.image_decoders.mjs" type="module"></script>
</head> </head>
<body tabindex="1"> <body tabindex="1">
<canvas id="jpegCanvas" width="0" height="0"></canvas> <canvas id="jpegCanvas" width="0" height="0"></canvas>
<script src="jpeg_viewer.mjs" type="module"></script> <script src="jpeg_viewer.mjs" type="module"></script>
</body> </body>
</html> </html>

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

@ -0,0 +1,17 @@
{
"extends": [
../../.eslintrc
],
"env": {
"webextensions": true
},
"parserOptions": {
"sourceType": "script"
},
"rules": {
"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,8 +17,8 @@ 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",
@ -46,7 +46,6 @@ limitations under the License.
} }
var scheme = url.slice(0, schemeIndex).toLowerCase(); var scheme = url.slice(0, schemeIndex).toLowerCase();
if (schemes.includes(scheme)) { if (schemes.includes(scheme)) {
// NOTE: We cannot use the `updateUrlHash` function in this context.
url = url.split("#", 1)[0]; url = url.split("#", 1)[0];
if (url.charAt(schemeIndex) === ":") { if (url.charAt(schemeIndex) === ":") {
url = encodeURIComponent(url); url = encodeURIComponent(url);
@ -56,50 +55,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);
}
});
})(); })();

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,14 +10,13 @@
"16": "icon16.png" "16": "icon16.png"
}, },
"permissions": [ "permissions": [
"alarms",
"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://*/*", "file://*/*"],
@ -27,35 +26,27 @@
"js": ["contentscript.js"] "js": ["contentscript.js"]
} }
], ],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
},
"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"
}, },
"incognito": "split", "incognito": "split",
"web_accessible_resources": [ "web_accessible_resources": [
{ "content/web/viewer.html",
"resources": [ "http:/*",
"content/web/viewer.html", "https:/*",
"http:/*", "file:/*",
"https:/*", "chrome-extension:/*",
"file:/*", "blob:*",
"chrome-extension:/*", "data:*",
"blob:*", "filesystem:/*",
"data:*", "drive:*"
"filesystem:/*",
"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

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

View File

@ -0,0 +1,22 @@
<!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="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.
*/ */
/* globals saveReferer */
/* 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 priority (highest priority rule first).
// The required "id" fields will be auto-generated later.
const addRules = [
{
// Do not redirect for URLs containing pdfjs.action=download.
condition: {
urlFilter: "pdfjs.action=download",
resourceTypes: ["main_frame", "sub_frame"],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// Redirect local PDF files if isAllowedFileSchemeAccess is true. No-op
// otherwise and then handled by webNavigation.onBeforeNavigate below.
condition: {
regexFilter: "^file://.*\\.pdf$",
resourceTypes: ["main_frame", "sub_frame"],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Respect the Content-Disposition:attachment header in sub_frame. But:
// Display the PDF viewer regardless of the Content-Disposition header if
// the file is displayed in the main frame, since most often users want to
// view a PDF, and servers are often misconfigured.
condition: {
urlFilter: "*",
resourceTypes: ["sub_frame"], // Note: no main_frame, handled below.
responseHeaders: [
{
header: "content-disposition",
values: ["attachment*"],
},
],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// If the query string contains "=download", do not unconditionally force
// viewer to open the PDF, but first check whether the Content-Disposition
// header specifies an attachment. This allows sites like Google Drive to
// operate correctly (#6106).
condition: {
urlFilter: "=download",
resourceTypes: ["main_frame"], // No sub_frame, was handled before.
responseHeaders: [
{
header: "content-disposition",
values: ["attachment*"],
},
],
},
action: ACTION_IGNORE_OTHER_RULES,
},
{
// Regular http(s) PDF requests.
condition: {
regexFilter: "^.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-type",
values: ["application/pdf", "application/pdf;*"],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Wrong MIME-type, but a PDF file according to the file name in the URL.
condition: {
regexFilter: "^.*\\.pdf\\b.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-type",
values: ["application/octet-stream", "application/octet-stream;*"],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
{
// Wrong MIME-type, but a PDF file according to Content-Disposition.
condition: {
regexFilter: "^.*$",
// The viewer does not have the original request context and issues a
// GET request. The original response to POST requests is unavailable.
excludedRequestMethods: ["post"],
resourceTypes: ["main_frame", "sub_frame"],
responseHeaders: [
{
header: "content-disposition",
values: ["*.pdf", '*.pdf"*', "*.pdf'*"],
},
],
// We only want to match by content-disposition if Content-Type is set
// to application/octet-stream. The responseHeaders condition is a
// logical OR instead of AND, so to simulate the AND condition we use
// the double negation of excludedResponseHeaders + excludedValues.
// This matches any request whose content-type header is set and not
// "application/octet-stream". It will also match if "content-type" is
// not set, but we are okay with that since the browser would usually
// try to sniff the MIME type in that case.
excludedResponseHeaders: [
{
header: "content-type",
excludedValues: [
"application/octet-stream",
"application/octet-stream;*",
],
},
],
},
action: ACTION_REDIRECT_TO_VIEWER,
},
];
for (const [i, rule] of addRules.entries()) {
// id must be unique and at least 1, but i starts at 0. So add +1.
rule.id = i + 1;
rule.priority = addRules.length - i;
}
try {
// Note: condition.responseHeaders is only supported in Chrome 128+, but
// does not trigger errors in Chrome 123 - 127 as explained at:
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
// We need to detect this and avoid registering rules, because otherwise all
// requests are redirected to the viewer instead of just PDF requests,
// because Chrome accepts rules while ignoring the responseHeaders condition
// - also reported at https://crbug.com/347186592
if (!(await isHeaderConditionSupported())) {
throw new Error("DNR responseHeaders condition is not supported.");
}
await chrome.declarativeNetRequest.updateDynamicRules({ addRules });
} catch (e) {
// When we do not register DNR rules for any reason, fall back to catching
// PDF documents via maybeRenderPdfDoc in contentscript.js.
console.error("Failed to register rules to redirect PDF requests.");
console.error(e);
}
}
// For the source and explanation of this logic, see
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
async function isHeaderConditionSupported() {
const ruleId = 123456; // Some rule ID that is not already used elsewhere.
try {
// Throws synchronously if not supported.
await chrome.declarativeNetRequest.updateSessionRules({
addRules: [
{
id: ruleId,
condition: {
responseHeaders: [{ header: "whatever" }],
urlFilter: "|does_not_match_anything",
},
action: { type: "block" },
},
],
});
} catch {
return false; // responseHeaders condition not supported.
}
// Chrome may recognize the properties but have the implementation behind a
// flag. When the implementation is disabled, validation is skipped too.
try {
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [ruleId],
addRules: [
{
id: ruleId,
condition: {
responseHeaders: [],
urlFilter: "|does_not_match_anything",
},
action: { type: "block" },
},
],
});
return false; // Validation skipped = feature disabled.
} catch {
return true; // Validation worked = feature enabled.
} finally {
await chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [ruleId],
});
}
}
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

@ -71,14 +71,6 @@
"type": "string", "type": "string",
"default": "" "default": ""
}, },
"commentLearnMoreUrl": {
"type": "string",
"default": ""
},
"enableSignatureEditor": {
"type": "boolean",
"default": false
},
"enableUpdatedAddImage": { "enableUpdatedAddImage": {
"type": "boolean", "type": "boolean",
"default": false "default": false
@ -108,7 +100,7 @@
}, },
"highlightEditorColors": { "highlightEditorColors": {
"type": "string", "type": "string",
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F,yellow_HCM=#FFFFCC,green_HCM=#53FFBC,blue_HCM=#80EBFF,pink_HCM=#F6B8FF,red_HCM=#C50043" "default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
}, },
"disableRange": { "disableRange": {
"title": "Disable range requests", "title": "Disable range requests",
@ -176,10 +168,6 @@
"enum": [-1, 0, 3, 15], "enum": [-1, 0, 3, 15],
"default": 0 "default": 0
}, },
"capCanvasAreaFactor": {
"type": "integer",
"default": 200
},
"enablePermissions": { "enablePermissions": {
"type": "boolean", "type": "boolean",
"default": false "default": false
@ -232,21 +220,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": true
},
"enableComment": {
"description": "Enable creation of comment annotations.",
"type": "boolean",
"default": false
},
"enableOptimizedPartialRendering": {
"description": "Enable tracking of PDF operations to optimize partial rendering.",
"type": "boolean",
"default": false
} }
} }
} }

View File

@ -13,14 +13,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/* globals getHeaderFromHeaders */
/* 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
@ -98,11 +89,8 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
} }
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 +99,49 @@ 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);
}); });
function onBeforeSendHeaders(details) {
if (details.frameId !== frameId) {
return undefined;
}
var headers = details.requestHeaders;
var refererHeader = getHeaderFromHeaders(headers, "referer");
if (!refererHeader) {
refererHeader = { name: "Referer" };
headers.push(refererHeader);
} else if (
refererHeader.value &&
refererHeader.value.lastIndexOf("chrome-extension:", 0) !== 0
) {
// Sanity check. If the referer is set, and the value is not the URL of
// this extension, then the request was not initiated by this extension.
return undefined;
}
refererHeader.value = referer;
return { requestHeaders: headers };
}
}); });
function setStickyReferrer(dnrRequestId, tabId, url, referer, callback) {
if (!referer) {
unsetStickyReferrer(dnrRequestId);
callback();
return;
}
const rule = {
id: dnrRequestId,
condition: {
urlFilter: `|${url}|`,
// The viewer and background are presumed to have the same origin:
initiatorDomains: [location.hostname], // = chrome.runtime.id.
resourceTypes: ["xmlhttprequest"],
tabIds: [tabId],
},
action: {
type: "modifyHeaders",
requestHeaders: [{ operation: "set", header: "referer", value: referer }],
},
};
chrome.declarativeNetRequest.updateSessionRules(
{ removeRuleIds: [dnrRequestId], addRules: [rule] },
callback
);
}
function unsetStickyReferrer(dnrRequestId) {
if (dnrRequestId) {
chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [dnrRequestId],
});
}
}

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

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) {
@ -47,25 +62,6 @@ function handlePreprocessorAction(ctx, actionName, args, path) {
} }
function babelPluginPDFJSPreprocessor(babel, ctx) { function babelPluginPDFJSPreprocessor(babel, ctx) {
function removeUnusedFunctions(path) {
let removed;
do {
removed = false;
path.scope.crawl();
for (const name in path.scope.bindings) {
const binding = path.scope.bindings[name];
if (!binding.referenced) {
const { path: bindingPath } = binding;
if (bindingPath.isFunctionDeclaration()) {
bindingPath.remove();
removed = true;
}
}
}
// If we removed some functions, there might be new unused ones
} while (removed);
}
return { return {
name: "babel-plugin-pdfjs-preprocessor", name: "babel-plugin-pdfjs-preprocessor",
manipulateOptions({ parserOpts }) { manipulateOptions({ parserOpts }) {
@ -172,22 +168,18 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
path.replaceWith(t.inherits(t.valueToNode(result), path.node)); path.replaceWith(t.inherits(t.valueToNode(result), path.node));
} }
if (t.isIdentifier(node.callee, { name: "__raw_import__" })) { if (t.isIdentifier(node.callee, { name: "__non_webpack_import__" })) {
if (node.arguments.length !== 1) { if (node.arguments.length !== 1) {
throw new Error("Invalid `__raw_import__` usage."); throw new Error("Invalid `__non_webpack_import__` usage.");
} }
// Replace it with a standard `import`-call and attempt to ensure that // Replace it with a standard `import`-call and
// various bundlers will leave it alone; this *must* include Webpack. // ensure that Webpack will leave it alone.
const source = node.arguments[0]; const source = node.arguments[0];
source.leadingComments = [ source.leadingComments = [
{ {
type: "CommentBlock", type: "CommentBlock",
value: "webpackIgnore: true", value: "webpackIgnore: true",
}, },
{
type: "CommentBlock",
value: "@vite-ignore",
},
]; ];
path.replaceWith(t.importExpression(source)); path.replaceWith(t.importExpression(source));
} }
@ -244,8 +236,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
// Function body ends with return without arg -- removing it. // Function body ends with return without arg -- removing it.
body.pop(); body.pop();
} }
removeUnusedFunctions(path);
}, },
}, },
ClassMethod: { ClassMethod: {
@ -268,26 +258,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
} }
}, },
}, },
Program: {
exit(path) {
if (path.node.sourceType === "module") {
removeUnusedFunctions(path);
}
},
},
MemberExpression(path) {
// The Emscripten Compiler (emcc) generates code that allows the caller
// to provide the Wasm module (thorugh Module.instantiateWasm), with
// a fallback in case .instantiateWasm is not provided.
// We always define instantiateWasm, so we can hard-code the check
// and let our dead code elimination logic remove the unused fallback.
if (
path.parentPath.isIfStatement({ test: path.node }) &&
path.matchesPattern("Module.instantiateWasm")
) {
path.replaceWith(t.booleanLiteral(true));
}
},
}, },
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@ -16,4 +16,3 @@ if ('1') {
function f1() { function f1() {
"1"; "1";
} }
f1();

View File

@ -32,4 +32,3 @@ function f1() {
"2"; "2";
} }
} }
f1();

View File

@ -1,7 +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( await import( /*webpackIgnore: true*/"./non-alias");
/*webpackIgnore: true*/
/*@vite-ignore*/
"./non-alias");

View File

@ -1,4 +1,4 @@
import { Test } from 'import-alias'; import { Test } from 'import-alias';
import { Test2 } from './non-alias'; import { Test2 } from './non-alias';
export { Test3 } from 'import-alias'; export { Test3 } from 'import-alias';
await __raw_import__("./non-alias"); await __non_webpack_import__("./non-alias");

View File

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

View File

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

View File

@ -1,8 +1,9 @@
import * as builder from "./builder.mjs"; import * as builder from "./builder.mjs";
import { fileURLToPath } from "url";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
const __dirname = import.meta.dirname; const __dirname = path.dirname(fileURLToPath(import.meta.url));
let errors = 0; let errors = 0;

View File

@ -1,8 +1,9 @@
import { fileURLToPath } from "url";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { preprocessPDFJSCode } from "./babel-plugin-pdfjs-preprocessor.mjs"; import { preprocessPDFJSCode } from "./babel-plugin-pdfjs-preprocessor.mjs";
const __dirname = import.meta.dirname; const __dirname = path.dirname(fileURLToPath(import.meta.url));
let errors = 0; let errors = 0;

Binary file not shown.

116
external/iccs/LICENSE vendored
View File

@ -1,116 +0,0 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
http://creativecommons.org/publicdomain/zero/1.0/

View File

@ -1,10 +0,0 @@
## ICC
The file CGATS001Compat-v2-micro.icc used to convert colors from CMYK to RGB comes from:
https://github.com/saucecontrol/Compact-ICC-Profiles
at revision bdd84663061bc4ae95ca70decff54f581e27f702.
## Licensing
[CGATS001Compat-v2-micro.icc](https://github.com/saucecontrol/Compact-ICC-Profiles/blob/master/profiles/CGATS001Compat-v2-micro.icc) is under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).

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-firefox/firefox/main/browser/locales/all-locales"; "https://raw.githubusercontent.com/mozilla/gecko-dev/master/browser/locales/all-locales";
const response = await fetch(ALL_LOCALES); const response = await fetch(ALL_LOCALES);
if (!response.ok) { if (!response.ok) {

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.

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

View File

@ -1,22 +0,0 @@
Copyright (c) 2025, 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

@ -1,21 +0,0 @@
qcms
Copyright (C) 2009-2024 Mozilla Corporation
Copyright (C) 1998-2007 Marti Maria
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,12 +0,0 @@
## Build
In order to generate the files `qcms.js` and `qcms_bg.wasm`:
* git clone https://github.com/mozilla/pdf.js.qcms/
* the build requires to have a [Docker](https://www.docker.com/) setup and then:
* `node build.js -C` to build the Docker image
* `node build.js -co /pdf.js/external/qcms/` to compile the decoder
## Licensing
[qcms](https://github.com/FirefoxGraphics/qcms) is under [MIT](https://github.com/FirefoxGraphics/qcms/blob/main/COPYING)
and [pdf.js.qcms](https://github.com/mozilla/pdf.js.qcms/) is released under [MIT](https://github.com/mozilla/pdf.js.qcms/blob/main/LICENSE) license so `qcms.js` and `qcms_bg.wasm` are released under [MIT](https://github.com/mozilla/pdf.js.qcms/blob/main/LICENSE) license too.

259
external/qcms/qcms.js vendored
View File

@ -1,259 +0,0 @@
/* THIS FILE IS GENERATED - DO NOT EDIT */
import { copy_result, copy_rgb, make_cssRGB } from './qcms_utils.js';
let wasm;
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1, 1) >>> 0;
getUint8ArrayMemory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {Uint8Array} src
*/
export function qcms_convert_array(transformer, src) {
const ptr0 = passArray8ToWasm0(src, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.qcms_convert_array(transformer, ptr0, len0);
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {number} src
* @param {boolean} css
*/
export function qcms_convert_one(transformer, src, css) {
wasm.qcms_convert_one(transformer, src, css);
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {number} src1
* @param {number} src2
* @param {number} src3
* @param {boolean} css
*/
export function qcms_convert_three(transformer, src1, src2, src3, css) {
wasm.qcms_convert_three(transformer, src1, src2, src3, css);
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
* @param {number} src1
* @param {number} src2
* @param {number} src3
* @param {number} src4
* @param {boolean} css
*/
export function qcms_convert_four(transformer, src1, src2, src3, src4, css) {
wasm.qcms_convert_four(transformer, src1, src2, src3, src4, css);
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {Uint8Array} mem
* @param {DataType} in_type
* @param {Intent} intent
* @returns {number}
*/
export function qcms_transformer_from_memory(mem, in_type, intent) {
const ptr0 = passArray8ToWasm0(mem, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.qcms_transformer_from_memory(ptr0, len0, in_type, intent);
return ret >>> 0;
}
/**
* # Safety
*
* This function is called directly from JavaScript.
* @param {number} transformer
*/
export function qcms_drop_transformer(transformer) {
wasm.qcms_drop_transformer(transformer);
}
/**
* @enum {0 | 1 | 2 | 3 | 4 | 5}
*/
export const DataType = Object.freeze({
RGB8: 0, "0": "RGB8",
RGBA8: 1, "1": "RGBA8",
BGRA8: 2, "2": "BGRA8",
Gray8: 3, "3": "Gray8",
GrayA8: 4, "4": "GrayA8",
CMYK: 5, "5": "CMYK",
});
/**
* @enum {0 | 1 | 2 | 3}
*/
export const Intent = Object.freeze({
Perceptual: 0, "0": "Perceptual",
RelativeColorimetric: 1, "1": "RelativeColorimetric",
Saturation: 2, "2": "Saturation",
AbsoluteColorimetric: 3, "3": "AbsoluteColorimetric",
});
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbg_copyresult_b08ee7d273f295dd = function(arg0, arg1) {
copy_result(arg0 >>> 0, arg1 >>> 0);
};
imports.wbg.__wbg_copyrgb_d60ce17bb05d9b67 = function(arg0) {
copy_rgb(arg0 >>> 0);
};
imports.wbg.__wbg_makecssRGB_893bf0cd9fdb302d = function(arg0) {
make_cssRGB(arg0 >>> 0);
};
imports.wbg.__wbindgen_init_externref_table = function() {
const table = wasm.__wbindgen_export_0;
const offset = table.grow(4);
table.set(0, undefined);
table.set(offset + 0, undefined);
table.set(offset + 1, null);
table.set(offset + 2, true);
table.set(offset + 3, false);
;
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
return imports;
}
function __wbg_init_memory(imports, memory) {
}
function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
cachedUint8ArrayMemory0 = null;
wasm.__wbindgen_start();
return wasm;
}
function initSync(module) {
if (wasm !== undefined) return wasm;
if (typeof module !== 'undefined') {
if (Object.getPrototypeOf(module) === Object.prototype) {
({module} = module)
} else {
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
}
}
const imports = __wbg_get_imports();
__wbg_init_memory(imports);
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}
async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;
if (typeof module_or_path !== 'undefined') {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
}
const imports = __wbg_get_imports();
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}
__wbg_init_memory(imports);
const { instance, module } = await __wbg_load(await module_or_path, imports);
return __wbg_finalize_init(instance, module);
}
export { initSync };
export default __wbg_init;

Binary file not shown.

View File

@ -1,84 +0,0 @@
/* Copyright 2025 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.
*/
class QCMS {
static #memoryArray = null;
static _memory = null;
static _mustAddAlpha = false;
static _destBuffer = null;
static _destOffset = 0;
static _destLength = 0;
static _cssColor = "";
static _makeHexColor = null;
static get _memoryArray() {
const array = this.#memoryArray;
if (array?.byteLength) {
return array;
}
return (this.#memoryArray = new Uint8Array(this._memory.buffer));
}
}
function copy_result(ptr, len) {
// This function is called from the wasm module (it's an external
// "C" function). Its goal is to copy the result from the wasm memory
// to the destination buffer without any intermediate copies.
const { _mustAddAlpha, _destBuffer, _destOffset, _destLength, _memoryArray } =
QCMS;
if (len === _destLength) {
_destBuffer.set(_memoryArray.subarray(ptr, ptr + len), _destOffset);
return;
}
if (_mustAddAlpha) {
for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = _memoryArray[i + 2];
_destBuffer[j + 3] = 255;
}
} else {
for (let i = ptr, ii = ptr + len, j = _destOffset; i < ii; i += 3, j += 4) {
_destBuffer[j] = _memoryArray[i];
_destBuffer[j + 1] = _memoryArray[i + 1];
_destBuffer[j + 2] = _memoryArray[i + 2];
}
}
}
function copy_rgb(ptr) {
const { _destBuffer, _destOffset, _memoryArray } = QCMS;
_destBuffer[_destOffset] = _memoryArray[ptr];
_destBuffer[_destOffset + 1] = _memoryArray[ptr + 1];
_destBuffer[_destOffset + 2] = _memoryArray[ptr + 2];
}
function make_cssRGB(ptr) {
const { _memoryArray } = QCMS;
QCMS._cssColor = QCMS._makeHexColor(
_memoryArray[ptr],
_memoryArray[ptr + 1],
_memoryArray[ptr + 2]
);
}
export { copy_result, copy_rgb, make_cssRGB, QCMS };

View File

@ -12,6 +12,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* eslint-env node */
import { import {
babelPluginPDFJSPreprocessor, babelPluginPDFJSPreprocessor,
@ -21,6 +22,7 @@ import { exec, execSync, spawn, spawnSync } from "child_process";
import autoprefixer from "autoprefixer"; import autoprefixer from "autoprefixer";
import babel from "@babel/core"; import babel from "@babel/core";
import crypto from "crypto"; import crypto from "crypto";
import { fileURLToPath } from "url";
import fs from "fs"; import fs from "fs";
import gulp from "gulp"; import gulp from "gulp";
import hljs from "highlight.js"; import hljs from "highlight.js";
@ -30,9 +32,9 @@ import Metalsmith from "metalsmith";
import ordered from "ordered-read-streams"; import ordered from "ordered-read-streams";
import path from "path"; import path from "path";
import postcss from "gulp-postcss"; import postcss from "gulp-postcss";
import postcssDarkThemeClass from "postcss-dark-theme-class";
import postcssDirPseudoClass from "postcss-dir-pseudo-class"; import postcssDirPseudoClass from "postcss-dir-pseudo-class";
import postcssDiscardComments from "postcss-discard-comments"; import postcssDiscardComments from "postcss-discard-comments";
import postcssLightDarkFunction from "@csstools/postcss-light-dark-function";
import postcssNesting from "postcss-nesting"; import postcssNesting from "postcss-nesting";
import { preprocess } from "./external/builder/builder.mjs"; import { preprocess } from "./external/builder/builder.mjs";
import relative from "metalsmith-html-relative"; import relative from "metalsmith-html-relative";
@ -45,7 +47,7 @@ import webpack2 from "webpack";
import webpackStream from "webpack-stream"; import webpackStream from "webpack-stream";
import zip from "gulp-zip"; import zip from "gulp-zip";
const __dirname = import.meta.dirname; const __dirname = path.dirname(fileURLToPath(import.meta.url));
const BUILD_DIR = "build/"; const BUILD_DIR = "build/";
const L10N_DIR = "l10n/"; const L10N_DIR = "l10n/";
@ -79,10 +81,10 @@ const config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString());
const ENV_TARGETS = [ const ENV_TARGETS = [
"last 2 versions", "last 2 versions",
"Chrome >= 110", "Chrome >= 98",
"Firefox ESR", "Firefox ESR",
"Safari >= 16.4", "Safari >= 16.4",
"Node >= 20", "Node >= 18",
"> 1%", "> 1%",
"not IE > 0", "not IE > 0",
"not dead", "not dead",
@ -96,7 +98,7 @@ const AUTOPREFIXER_CONFIG = {
const BABEL_TARGETS = ENV_TARGETS.join(", "); const BABEL_TARGETS = ENV_TARGETS.join(", ");
const BABEL_PRESET_ENV_OPTS = Object.freeze({ const BABEL_PRESET_ENV_OPTS = Object.freeze({
corejs: "3.46.0", corejs: "3.38.1",
exclude: ["web.structured-clone"], exclude: ["web.structured-clone"],
shippedProposals: true, shippedProposals: true,
useBuiltIns: "usage", useBuiltIns: "usage",
@ -189,9 +191,6 @@ function createWebpackAlias(defines) {
"fluent-dom": "node_modules/@fluent/dom/esm/index.js", "fluent-dom": "node_modules/@fluent/dom/esm/index.js",
}; };
const libraryAlias = { const libraryAlias = {
"display-cmap_reader_factory": "src/display/stubs.js",
"display-standard_fontdata_factory": "src/display/stubs.js",
"display-wasm_factory": "src/display/stubs.js",
"display-fetch_stream": "src/display/stubs.js", "display-fetch_stream": "src/display/stubs.js",
"display-network": "src/display/stubs.js", "display-network": "src/display/stubs.js",
"display-node_stream": "src/display/stubs.js", "display-node_stream": "src/display/stubs.js",
@ -216,16 +215,10 @@ function createWebpackAlias(defines) {
"web-preferences": "", "web-preferences": "",
"web-print_service": "", "web-print_service": "",
"web-secondary_toolbar": "web/secondary_toolbar.js", "web-secondary_toolbar": "web/secondary_toolbar.js",
"web-signature_manager": "web/signature_manager.js",
"web-toolbar": "web/toolbar.js", "web-toolbar": "web/toolbar.js",
}; };
if (defines.CHROME) { if (defines.CHROME) {
libraryAlias["display-cmap_reader_factory"] =
"src/display/cmap_reader_factory.js";
libraryAlias["display-standard_fontdata_factory"] =
"src/display/standard_fontdata_factory.js";
libraryAlias["display-wasm_factory"] = "src/display/wasm_factory.js";
libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js"; libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js";
libraryAlias["display-network"] = "src/display/network.js"; libraryAlias["display-network"] = "src/display/network.js";
@ -238,11 +231,6 @@ function createWebpackAlias(defines) {
// Aliases defined here must also be replicated in the paths section of // Aliases defined here must also be replicated in the paths section of
// the tsconfig.json file for the type generation to work. // the tsconfig.json file for the type generation to work.
// In the tsconfig.json files, the .js extension must be omitted. // In the tsconfig.json files, the .js extension must be omitted.
libraryAlias["display-cmap_reader_factory"] =
"src/display/cmap_reader_factory.js";
libraryAlias["display-standard_fontdata_factory"] =
"src/display/standard_fontdata_factory.js";
libraryAlias["display-wasm_factory"] = "src/display/wasm_factory.js";
libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js"; libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js";
libraryAlias["display-network"] = "src/display/network.js"; libraryAlias["display-network"] = "src/display/network.js";
libraryAlias["display-node_stream"] = "src/display/node_stream.js"; libraryAlias["display-node_stream"] = "src/display/node_stream.js";
@ -302,12 +290,6 @@ function createWebpackConfig(
const licenseHeaderLibre = fs const licenseHeaderLibre = fs
.readFileSync("./src/license_header_libre.js") .readFileSync("./src/license_header_libre.js")
.toString(); .toString();
const versionInfoHeader = [
"/**",
` * pdfjsVersion = ${versionInfo.version}`,
` * pdfjsBuild = ${versionInfo.commit}`,
" */",
].join("\n");
const enableSourceMaps = const enableSourceMaps =
!bundleDefines.MOZCENTRAL && !bundleDefines.MOZCENTRAL &&
!bundleDefines.CHROME && !bundleDefines.CHROME &&
@ -341,39 +323,9 @@ function createWebpackConfig(
const plugins = []; const plugins = [];
if (!disableLicenseHeader) { if (!disableLicenseHeader) {
plugins.push( plugins.push(
new webpack2.BannerPlugin({ new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true })
banner: licenseHeaderLibre + "\n" + versionInfoHeader,
raw: true,
})
); );
} }
plugins.push({
/** @param {import('webpack').Compiler} compiler */
apply(compiler) {
const errors = [];
compiler.hooks.afterCompile.tap("VerifyImportMeta", compilation => {
for (const asset of compilation.getAssets()) {
if (asset.name.endsWith(".mjs")) {
const source = asset.source.source();
if (
typeof source === "string" &&
/new URL\([^,)]*,\s*import\.meta\.url/.test(source)
) {
errors.push(
`Output module ${asset.name} uses new URL(..., import.meta.url)`
);
}
}
}
});
compiler.hooks.afterEmit.tap("VerifyImportMeta", compilation => {
// Emit the errors after emitting the files, so that it's possible to
// look at the contents of the invalid bundle.
compilation.errors.push(...errors);
});
},
});
const alias = createWebpackAlias(bundleDefines); const alias = createWebpackAlias(bundleDefines);
const experiments = isModule ? { outputModule: true } : undefined; const experiments = isModule ? { outputModule: true } : undefined;
@ -397,8 +349,9 @@ function createWebpackConfig(
// V8 chokes on very long sequences, work around that. // V8 chokes on very long sequences, work around that.
sequences: false, sequences: false,
}, },
format: { mangle: {
comments: /@lic|webpackIgnore|@vite-ignore|pdfjsVersion/i, // Ensure that the `tweakWebpackOutput` function works.
reserved: ["__webpack_exports__"],
}, },
keep_classnames: true, keep_classnames: true,
keep_fnames: true, keep_fnames: true,
@ -418,15 +371,8 @@ function createWebpackConfig(
}, },
devtool: enableSourceMaps ? "source-map" : undefined, devtool: enableSourceMaps ? "source-map" : undefined,
module: { module: {
parser: {
javascript: {
importMeta: false,
url: false,
},
},
rules: [ rules: [
{ {
test: /\.[mc]?js$/,
loader: "babel-loader", loader: "babel-loader",
exclude: babelExcludeRegExp, exclude: babelExcludeRegExp,
options: { options: {
@ -497,6 +443,13 @@ function checkChromePreferencesFile(chromePrefsPath, webPrefs) {
return ret; return ret;
} }
function tweakWebpackOutput(jsName) {
return replace(
/((?:\s|,)__webpack_exports__)(?:\s?)=(?:\s?)({};)/gm,
(match, p1, p2) => `${p1} = globalThis.${jsName} = ${p2}`
);
}
function createMainBundle(defines) { function createMainBundle(defines) {
const mainFileConfig = createWebpackConfig(defines, { const mainFileConfig = createWebpackConfig(defines, {
filename: defines.MINIFIED ? "pdf.min.mjs" : "pdf.mjs", filename: defines.MINIFIED ? "pdf.min.mjs" : "pdf.mjs",
@ -506,7 +459,8 @@ function createMainBundle(defines) {
}); });
return gulp return gulp
.src("./src/pdf.js", { encoding: false }) .src("./src/pdf.js", { encoding: false })
.pipe(webpack2Stream(mainFileConfig)); .pipe(webpack2Stream(mainFileConfig))
.pipe(tweakWebpackOutput("pdfjsLib"));
} }
function createScriptingBundle(defines, extraOptions = undefined) { function createScriptingBundle(defines, extraOptions = undefined) {
@ -574,7 +528,8 @@ function createSandboxBundle(defines, extraOptions = undefined) {
return gulp return gulp
.src("./src/pdf.sandbox.js", { encoding: false }) .src("./src/pdf.sandbox.js", { encoding: false })
.pipe(webpack2Stream(sandboxFileConfig)); .pipe(webpack2Stream(sandboxFileConfig))
.pipe(tweakWebpackOutput("pdfjsSandbox"));
} }
function createWorkerBundle(defines) { function createWorkerBundle(defines) {
@ -586,7 +541,8 @@ function createWorkerBundle(defines) {
}); });
return gulp return gulp
.src("./src/pdf.worker.js", { encoding: false }) .src("./src/pdf.worker.js", { encoding: false })
.pipe(webpack2Stream(workerFileConfig)); .pipe(webpack2Stream(workerFileConfig))
.pipe(tweakWebpackOutput("pdfjsWorker"));
} }
function createWebBundle(defines, options) { function createWebBundle(defines, options) {
@ -634,7 +590,8 @@ function createComponentsBundle(defines) {
}); });
return gulp return gulp
.src("./web/pdf_viewer.component.js", { encoding: false }) .src("./web/pdf_viewer.component.js", { encoding: false })
.pipe(webpack2Stream(componentsFileConfig)); .pipe(webpack2Stream(componentsFileConfig))
.pipe(tweakWebpackOutput("pdfjsViewer"));
} }
function createImageDecodersBundle(defines) { function createImageDecodersBundle(defines) {
@ -648,7 +605,8 @@ function createImageDecodersBundle(defines) {
}); });
return gulp return gulp
.src("./src/pdf.image_decoders.js", { encoding: false }) .src("./src/pdf.image_decoders.js", { encoding: false })
.pipe(webpack2Stream(componentsFileConfig)); .pipe(webpack2Stream(componentsFileConfig))
.pipe(tweakWebpackOutput("pdfjsImageDecoders"));
} }
function createCMapBundle() { function createCMapBundle() {
@ -658,19 +616,13 @@ function createCMapBundle() {
}); });
} }
function createICCBundle() {
return gulp.src(["external/iccs/*.icc", "external/iccs/LICENSE"], {
base: "external/iccs",
encoding: false,
});
}
function createStandardFontBundle() { function createStandardFontBundle() {
return gulp.src( return gulp.src(
[ [
"external/standard_fonts/*.pfb", "external/standard_fonts/*.pfb",
"external/standard_fonts/*.ttf", "external/standard_fonts/*.ttf",
"external/standard_fonts/LICENSE_*", "external/standard_fonts/LICENSE_FOXIT",
"external/standard_fonts/LICENSE_LIBERATION",
], ],
{ {
base: "external/standard_fonts", base: "external/standard_fonts",
@ -679,26 +631,6 @@ function createStandardFontBundle() {
); );
} }
function createWasmBundle() {
return ordered([
gulp.src(
[
"external/openjpeg/*.wasm",
"external/openjpeg/openjpeg_nowasm_fallback.js",
"external/openjpeg/LICENSE_*",
],
{
base: "external/openjpeg",
encoding: false,
}
),
gulp.src(["external/qcms/*.wasm", "external/qcms/LICENSE_*"], {
base: "external/qcms",
encoding: false,
}),
]);
}
function checkFile(filePath) { function checkFile(filePath) {
try { try {
const stat = fs.lstatSync(filePath); const stat = fs.lstatSync(filePath);
@ -744,21 +676,17 @@ function runTests(testsName, { bot = false, xfaOnly = false } = {}) {
if (!bot) { if (!bot) {
args.push("--reftest"); args.push("--reftest");
} else { } else {
// The browser-tests are too slow in Google Chrome on the bots, const os = process.env.OS;
// causing a timeout, hence disabling them for now. if (/windows/i.test(os)) {
forceNoChrome = true; // The browser-tests are too slow in Google Chrome on the Windows
// bot, causing a timeout, hence disabling them for now.
forceNoChrome = true;
}
} }
if (xfaOnly) { if (xfaOnly) {
args.push("--xfaOnly"); args.push("--xfaOnly");
} }
args.push("--manifestFile=" + PDF_TEST); args.push("--manifestFile=" + PDF_TEST);
collectArgs(
{
names: ["-t", "--testfilter"],
hasValue: true,
},
args
);
break; break;
case "unit": case "unit":
args.push("--unitTest"); args.push("--unitTest");
@ -796,28 +724,6 @@ function runTests(testsName, { bot = false, xfaOnly = false } = {}) {
}); });
} }
function collectArgs(options, args) {
if (!Array.isArray(options)) {
options = [options];
}
for (let i = 0, ii = process.argv.length; i < ii; i++) {
const arg = process.argv[i];
const option = options.find(opt => opt.names.includes(arg));
if (!option) {
continue;
}
if (!option.hasValue) {
args.push(arg);
continue;
}
const next = process.argv[i + 1];
if (next && !next.startsWith("-")) {
args.push(arg, next);
i += 1;
}
}
}
function makeRef(done, bot) { function makeRef(done, bot) {
console.log(); console.log();
console.log("### Creating reference images"); console.log("### Creating reference images");
@ -825,10 +731,12 @@ function makeRef(done, bot) {
let forceNoChrome = false; let forceNoChrome = false;
const args = ["test.mjs", "--masterMode"]; const args = ["test.mjs", "--masterMode"];
if (bot) { if (bot) {
// The browser-tests are too slow in Google Chrome on the bots, const os = process.env.OS;
// causing a timeout, hence disabling them for now. if (/windows/i.test(os)) {
forceNoChrome = true; // The browser-tests are too slow in Google Chrome on the Windows
// bot, causing a timeout, hence disabling them for now.
forceNoChrome = true;
}
args.push("--noPrompts", "--strictVerify"); args.push("--noPrompts", "--strictVerify");
} }
if (process.argv.includes("--noChrome") || forceNoChrome) { if (process.argv.includes("--noChrome") || forceNoChrome) {
@ -840,13 +748,6 @@ function makeRef(done, bot) {
if (process.argv.includes("--headless")) { if (process.argv.includes("--headless")) {
args.push("--headless"); args.push("--headless");
} }
collectArgs(
{
names: ["-t", "--testfilter"],
hasValue: true,
},
args
);
const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" }); const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" });
testProcess.on("close", function (code) { testProcess.on("close", function (code) {
@ -1120,9 +1021,7 @@ function buildGeneric(defines, dir) {
}) })
.pipe(gulp.dest(dir + "web")), .pipe(gulp.dest(dir + "web")),
createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")), createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")),
createICCBundle().pipe(gulp.dest(dir + "web/iccs")),
createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")), createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")),
createWasmBundle().pipe(gulp.dest(dir + "web/wasm")),
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")), preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
preprocessCSS("web/viewer.css", defines) preprocessCSS("web/viewer.css", defines)
@ -1131,7 +1030,7 @@ function buildGeneric(defines, dir) {
postcssDirPseudoClass(), postcssDirPseudoClass(),
discardCommentsCSS(), discardCommentsCSS(),
postcssNesting(), postcssNesting(),
postcssLightDarkFunction({ preserve: true }), postcssDarkThemeClass(),
autoprefixer(AUTOPREFIXER_CONFIG), autoprefixer(AUTOPREFIXER_CONFIG),
]) ])
) )
@ -1208,7 +1107,6 @@ function buildComponents(defines, dir) {
"web/images/messageBar_*.svg", "web/images/messageBar_*.svg",
"web/images/toolbarButton-{editorHighlight,menuArrow}.svg", "web/images/toolbarButton-{editorHighlight,menuArrow}.svg",
"web/images/cursor-*.svg", "web/images/cursor-*.svg",
"web/images/comment-*.svg",
]; ];
return ordered([ return ordered([
@ -1222,7 +1120,6 @@ function buildComponents(defines, dir) {
postcssDirPseudoClass(), postcssDirPseudoClass(),
discardCommentsCSS(), discardCommentsCSS(),
postcssNesting(), postcssNesting(),
postcssLightDarkFunction({ preserve: true }),
autoprefixer(AUTOPREFIXER_CONFIG), autoprefixer(AUTOPREFIXER_CONFIG),
]) ])
) )
@ -1451,11 +1348,9 @@ gulp.task(
createCMapBundle().pipe( createCMapBundle().pipe(
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps") gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps")
), ),
createICCBundle().pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/iccs")),
createStandardFontBundle().pipe( createStandardFontBundle().pipe(
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts") gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts")
), ),
createWasmBundle().pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/wasm")),
preprocessHTML("web/viewer.html", defines).pipe( preprocessHTML("web/viewer.html", defines).pipe(
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web") gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
@ -1555,15 +1450,9 @@ gulp.task(
createCMapBundle().pipe( createCMapBundle().pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps") gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")
), ),
createICCBundle().pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/iccs")
),
createStandardFontBundle().pipe( createStandardFontBundle().pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts") gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts")
), ),
createWasmBundle().pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/wasm")
),
preprocessHTML("web/viewer.html", defines).pipe( preprocessHTML("web/viewer.html", defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web") gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
@ -1574,7 +1463,7 @@ gulp.task(
postcssDirPseudoClass(), postcssDirPseudoClass(),
discardCommentsCSS(), discardCommentsCSS(),
postcssNesting(), postcssNesting(),
postcssLightDarkFunction({ preserve: true }), postcssDarkThemeClass(),
autoprefixer(AUTOPREFIXER_CONFIG), autoprefixer(AUTOPREFIXER_CONFIG),
]) ])
) )
@ -1648,9 +1537,6 @@ function buildLibHelper(bundleDefines, inputStream, outputDir) {
defines: bundleDefines, defines: bundleDefines,
map: { map: {
"pdfjs-lib": "../pdf.js", "pdfjs-lib": "../pdf.js",
"display-cmap_reader_factory": "./cmap_reader_factory.js",
"display-standard_fontdata_factory": "./standard_fontdata_factory.js",
"display-wasm_factory": "./wasm_factory.js",
"display-fetch_stream": "./fetch_stream.js", "display-fetch_stream": "./fetch_stream.js",
"display-network": "./network.js", "display-network": "./network.js",
"display-node_stream": "./node_stream.js", "display-node_stream": "./node_stream.js",
@ -1696,7 +1582,6 @@ function buildLib(defines, dir) {
}), }),
gulp.src("test/unit/*.js", { base: ".", encoding: false }), gulp.src("test/unit/*.js", { base: ".", encoding: false }),
gulp.src("external/openjpeg/*.js", { base: "openjpeg/", encoding: false }), gulp.src("external/openjpeg/*.js", { base: "openjpeg/", encoding: false }),
gulp.src("external/qcms/*.js", { base: "qcms/", encoding: false }),
]); ]);
return buildLibHelper(bundleDefines, inputStream, dir); return buildLibHelper(bundleDefines, inputStream, dir);
@ -2000,34 +1885,31 @@ function createBaseline(done) {
gulp.task( gulp.task(
"unittestcli", "unittestcli",
gulp.series( gulp.series(setTestEnv, "lib-legacy", function runUnitTestCli(done) {
setTestEnv, const options = [
"generic-legacy", "node_modules/jasmine/bin/jasmine",
"lib-legacy", "JASMINE_CONFIG_PATH=test/unit/clitests.json",
function runUnitTestCli(done) { ];
const options = [ const jasmineProcess = startNode(options, { stdio: "inherit" });
"node_modules/jasmine/bin/jasmine", jasmineProcess.on("close", function (code) {
"JASMINE_CONFIG_PATH=test/unit/clitests.json", if (code !== 0) {
]; done(new Error("Unit tests failed."));
const jasmineProcess = startNode(options, { stdio: "inherit" }); return;
jasmineProcess.on("close", function (code) { }
if (code !== 0) { done();
done(new Error("Unit tests failed.")); });
return; })
}
done();
});
}
)
); );
gulp.task("lint", function (done) { gulp.task("lint", function (done) {
console.log(); console.log();
console.log("### Linting JS/CSS/JSON/SVG/HTML files"); console.log("### Linting JS/CSS/JSON/SVG files");
// Ensure that we lint the Firefox specific *.jsm files too. // Ensure that we lint the Firefox specific *.jsm files too.
const esLintOptions = [ const esLintOptions = [
"node_modules/eslint/bin/eslint", "node_modules/eslint/bin/eslint",
"--ext",
".js,.jsm,.mjs,.json",
".", ".",
"--report-unused-disable-directives", "--report-unused-disable-directives",
]; ];
@ -2047,19 +1929,17 @@ gulp.task("lint", function (done) {
const prettierOptions = [ const prettierOptions = [
"node_modules/prettier/bin/prettier.cjs", "node_modules/prettier/bin/prettier.cjs",
"**/*.json", "**/*.json",
"**/*.html",
]; ];
if (process.argv.includes("--fix")) { if (process.argv.includes("--fix")) {
prettierOptions.push("--log-level", "error", "--write"); prettierOptions.push("--log-level", "silent", "--write");
} else { } else {
prettierOptions.push("--log-level", "warn", "--check"); prettierOptions.push("--log-level", "warn", "--check");
} }
const svgLintOptions = [ const svgLintOptions = [
"node_modules/svglint/bin/cli.js", "node_modules/svglint/bin/cli.js",
"**/*.svg", "web/**/*.svg",
"--ci", "--ci",
"--no-summary",
]; ];
const esLintProcess = startNode(esLintOptions, { stdio: "inherit" }); const esLintProcess = startNode(esLintOptions, { stdio: "inherit" });
@ -2084,7 +1964,12 @@ gulp.task("lint", function (done) {
} }
const svgLintProcess = startNode(svgLintOptions, { const svgLintProcess = startNode(svgLintOptions, {
stdio: "inherit", stdio: "pipe",
});
svgLintProcess.stdout.setEncoding("utf8");
svgLintProcess.stdout.on("data", m => {
m = m.toString().replace(/-+ Summary -+.*/ms, "");
console.log(m);
}); });
svgLintProcess.on("close", function (svgLintCode) { svgLintProcess.on("close", function (svgLintCode) {
if (svgLintCode !== 0) { if (svgLintCode !== 0) {
@ -2133,15 +2018,6 @@ gulp.task(
) )
); );
gulp.task("dev-wasm", function () {
const VIEWER_WASM_OUTPUT = "web/wasm/";
fs.rmSync(VIEWER_WASM_OUTPUT, { recursive: true, force: true });
fs.mkdirSync(VIEWER_WASM_OUTPUT, { recursive: true });
return createWasmBundle().pipe(gulp.dest(VIEWER_WASM_OUTPUT));
});
gulp.task( gulp.task(
"dev-sandbox", "dev-sandbox",
gulp.series( gulp.series(
@ -2177,13 +2053,6 @@ gulp.task(
gulp.series("locale") gulp.series("locale")
); );
}, },
function watchWasm() {
gulp.watch(
["external/openjpeg/*", "external/qcms/*"],
{ ignoreInitial: false },
gulp.series("dev-wasm")
);
},
function watchDevSandbox() { function watchDevSandbox() {
gulp.watch( gulp.watch(
[ [
@ -2288,8 +2157,6 @@ gulp.task("metalsmith", async function () {
.use( .use(
layouts({ layouts({
directory: "docs/templates", directory: "docs/templates",
pattern: "**",
transform: "nunjucks",
}) })
) )
.use(relative()) .use(relative())
@ -2341,7 +2208,8 @@ function packageJson() {
bugs: DIST_BUGS_URL, bugs: DIST_BUGS_URL,
license: DIST_LICENSE, license: DIST_LICENSE,
optionalDependencies: { optionalDependencies: {
"@napi-rs/canvas": "^0.1.81", canvas: "^2.11.2",
path2d: "^0.2.1",
}, },
browser: { browser: {
canvas: false, canvas: false,
@ -2355,7 +2223,7 @@ function packageJson() {
url: `git+${DIST_GIT_URL}`, url: `git+${DIST_GIT_URL}`,
}, },
engines: { engines: {
node: ">=20.16.0 || >=22.3.0", node: ">=18",
}, },
scripts: {}, scripts: {},
}; };
@ -2400,24 +2268,12 @@ gulp.task(
encoding: false, encoding: false,
}) })
.pipe(gulp.dest(DIST_DIR)), .pipe(gulp.dest(DIST_DIR)),
gulp
.src(GENERIC_DIR + "web/iccs/**/*", {
base: GENERIC_DIR + "web",
encoding: false,
})
.pipe(gulp.dest(DIST_DIR)),
gulp gulp
.src(GENERIC_DIR + "web/standard_fonts/**/*", { .src(GENERIC_DIR + "web/standard_fonts/**/*", {
base: GENERIC_DIR + "web", base: GENERIC_DIR + "web",
encoding: false, encoding: false,
}) })
.pipe(gulp.dest(DIST_DIR)), .pipe(gulp.dest(DIST_DIR)),
gulp
.src(GENERIC_DIR + "web/wasm/**/*", {
base: GENERIC_DIR + "web",
encoding: false,
})
.pipe(gulp.dest(DIST_DIR)),
gulp gulp
.src( .src(
[ [

View File

@ -71,12 +71,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Jami me gin acoya… pdfjs-document-properties-button-label = Jami me gin acoya…
pdfjs-document-properties-file-name = Nying pwail: pdfjs-document-properties-file-name = Nying pwail:
pdfjs-document-properties-file-size = Dit pa pwail: pdfjs-document-properties-file-size = Dit pa pwail:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
pdfjs-document-properties-title = Wiye: pdfjs-document-properties-title = Wiye:
pdfjs-document-properties-author = Ngat mucoyo: pdfjs-document-properties-author = Ngat mucoyo:
pdfjs-document-properties-subject = Subjek: pdfjs-document-properties-subject = Subjek:
pdfjs-document-properties-keywords = Lok mapire tek: pdfjs-document-properties-keywords = Lok mapire tek:
pdfjs-document-properties-creation-date = Nino dwe me cwec: pdfjs-document-properties-creation-date = Nino dwe me cwec:
pdfjs-document-properties-modification-date = Nino dwe me yub: pdfjs-document-properties-modification-date = Nino dwe me yub:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Lacwec: pdfjs-document-properties-creator = Lacwec:
pdfjs-document-properties-producer = Layub PDF: pdfjs-document-properties-producer = Layub PDF:
pdfjs-document-properties-version = Kit PDF: pdfjs-document-properties-version = Kit PDF:
@ -173,6 +185,9 @@ pdfjs-page-scale-actual = Dite kikome
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = Bal otime kun cano PDF. pdfjs-loading-error = Bal otime kun cano PDF.
@ -198,3 +213,13 @@ pdfjs-password-invalid = Mung me donyo pe atir. Tim ber i tem doki.
pdfjs-password-ok-button = OK pdfjs-password-ok-button = OK
pdfjs-password-cancel-button = Juki pdfjs-password-cancel-button = Juki
pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit pa coc me PDF ma kiketo i kine. pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit pa coc me PDF ma kiketo i kine.
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -71,17 +71,37 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Dokumenteienskappe… pdfjs-document-properties-button-label = Dokumenteienskappe…
pdfjs-document-properties-file-name = Lêernaam: pdfjs-document-properties-file-name = Lêernaam:
pdfjs-document-properties-file-size = Lêergrootte: pdfjs-document-properties-file-size = Lêergrootte:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } kG ({ $size_b } grepe)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MG ({ $size_b } grepe)
pdfjs-document-properties-title = Titel: pdfjs-document-properties-title = Titel:
pdfjs-document-properties-author = Outeur: pdfjs-document-properties-author = Outeur:
pdfjs-document-properties-subject = Onderwerp: pdfjs-document-properties-subject = Onderwerp:
pdfjs-document-properties-keywords = Sleutelwoorde: pdfjs-document-properties-keywords = Sleutelwoorde:
pdfjs-document-properties-creation-date = Skeppingsdatum: pdfjs-document-properties-creation-date = Skeppingsdatum:
pdfjs-document-properties-modification-date = Wysigingsdatum: pdfjs-document-properties-modification-date = Wysigingsdatum:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Skepper: pdfjs-document-properties-creator = Skepper:
pdfjs-document-properties-producer = PDF-vervaardiger: pdfjs-document-properties-producer = PDF-vervaardiger:
pdfjs-document-properties-version = PDF-weergawe: pdfjs-document-properties-version = PDF-weergawe:
pdfjs-document-properties-page-count = Aantal bladsye: pdfjs-document-properties-page-count = Aantal bladsye:
## Variables:
## $width (Number) - the width of the (current) page
## $height (Number) - the height of the (current) page
## $unit (String) - the unit of measurement of the (current) page
## $name (String) - the name of the (current) page
## $orientation (String) - the orientation of the (current) page
## ##
pdfjs-document-properties-close-button = Sluit pdfjs-document-properties-close-button = Sluit
@ -152,6 +172,9 @@ pdfjs-page-scale-actual = Werklike grootte
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = 'n Fout het voorgekom met die laai van die PDF. pdfjs-loading-error = 'n Fout het voorgekom met die laai van die PDF.
@ -177,3 +200,13 @@ pdfjs-password-invalid = Ongeldige wagwoord. Probeer gerus weer.
pdfjs-password-ok-button = OK pdfjs-password-ok-button = OK
pdfjs-password-cancel-button = Kanselleer pdfjs-password-cancel-button = Kanselleer
pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingebed is, gebruik nie. pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingebed is, gebruik nie.
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -89,12 +89,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Propiedatz d'o documento... pdfjs-document-properties-button-label = Propiedatz d'o documento...
pdfjs-document-properties-file-name = Nombre de fichero: pdfjs-document-properties-file-name = Nombre de fichero:
pdfjs-document-properties-file-size = Grandaria d'o fichero: pdfjs-document-properties-file-size = Grandaria d'o fichero:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
pdfjs-document-properties-title = Titol: pdfjs-document-properties-title = Titol:
pdfjs-document-properties-author = Autor: pdfjs-document-properties-author = Autor:
pdfjs-document-properties-subject = Afer: pdfjs-document-properties-subject = Afer:
pdfjs-document-properties-keywords = Parolas clau: pdfjs-document-properties-keywords = Parolas clau:
pdfjs-document-properties-creation-date = Calendata de creyación: pdfjs-document-properties-creation-date = Calendata de creyación:
pdfjs-document-properties-modification-date = Calendata de modificación: pdfjs-document-properties-modification-date = Calendata de modificación:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Creyador: pdfjs-document-properties-creator = Creyador:
pdfjs-document-properties-producer = Creyador de PDF: pdfjs-document-properties-producer = Creyador de PDF:
pdfjs-document-properties-version = Versión de PDF: pdfjs-document-properties-version = Versión de PDF:
@ -201,6 +213,9 @@ pdfjs-page-scale-actual = Grandaria actual
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = S'ha produciu una error en cargar o PDF. pdfjs-loading-error = S'ha produciu una error en cargar o PDF.
@ -211,6 +226,10 @@ pdfjs-rendering-error = Ha ocurriu una error en renderizar a pachina.
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -226,3 +245,13 @@ pdfjs-password-invalid = Clau invalida. Torna a intentar-lo.
pdfjs-password-ok-button = Acceptar pdfjs-password-ok-button = Acceptar
pdfjs-password-cancel-button = Cancelar pdfjs-password-cancel-button = Cancelar
pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar fichers PDF. pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar fichers PDF.
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -105,13 +105,13 @@ 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 # $size_kb (Number) - the PDF file size in kilobytes
# $b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } ك.بايت ({ $b } بايتات) pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت)
# Variables: # Variables:
# $mb (Number) - the PDF file size in megabytes # $size_mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } م.بايت ({ $b } بايتات) pdfjs-document-properties-mb = { $size_mb } م.بايت ({ $size_b } بايت)
pdfjs-document-properties-title = العنوان: pdfjs-document-properties-title = العنوان:
pdfjs-document-properties-author = المؤلف: pdfjs-document-properties-author = المؤلف:
pdfjs-document-properties-subject = الموضوع: pdfjs-document-properties-subject = الموضوع:
@ -119,8 +119,9 @@ 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 # $date (Date) - the creation/modification date of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } # $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }، { $time }
pdfjs-document-properties-creator = المنشئ: pdfjs-document-properties-creator = المنشئ:
pdfjs-document-properties-producer = منتج PDF: pdfjs-document-properties-producer = منتج PDF:
pdfjs-document-properties-version = إصدارة PDF: pdfjs-document-properties-version = إصدارة PDF:
@ -215,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 = تابعت من الأعلى بعدما وصلت إلى نهاية المستند
@ -271,6 +272,10 @@ pdfjs-rendering-error = حدث خطأ أثناء عرض الصفحة.
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }، { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -278,9 +283,6 @@ pdfjs-rendering-error = حدث خطأ أثناء عرض الصفحة.
# 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
@ -304,29 +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 = أضِف توقيع
## Default editor aria labels
# “Highlight” is a noun, the string is used on the editor for highlights.
pdfjs-editor-highlight-editor =
.aria-label = محرِّر الإبراز
# “Drawing” is a noun, the string is used on the editor for drawings.
pdfjs-editor-ink-editor =
.aria-label = محرِّر الرسم
# Used when a signature editor is selected/hovered.
# Variables:
# $description (String) - a string describing/labeling the signature.
pdfjs-editor-signature-editor1 =
.aria-description = محرِّر التوقيع: { $description }
pdfjs-editor-stamp-editor =
.aria-label = محرِّر الصور
## Remove button for the various kind of editor. ## Remove button for the various kind of editor.
@ -338,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 = أزِل التوقيع
## ##
@ -356,26 +339,19 @@ 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-add-signature-container = pdfjs-free-text =
.aria-label = عناصر التحكم في التوقيع والتوقيعات المحفوظة
pdfjs-editor-signature-add-signature-button =
.title = أضِف توقيع جديد
pdfjs-editor-signature-add-signature-button-label = أضِف توقيع جديد
# Used on the button to use an already saved signature.
# Variables:
# $description (String) - a string describing/labeling the signature.
pdfjs-editor-add-saved-signature-button =
.title = التوقيع المحفوظ: { $description }
# .default-content is used as a placeholder in an empty text editor.
pdfjs-free-text2 =
.aria-label = محرِّر النص .aria-label = محرِّر النص
.default-content = ابدأ في كتابة… pdfjs-free-text-default-content = ابدأ الكتابة…
pdfjs-ink =
.aria-label = محرِّر الرسم
pdfjs-ink-canvas =
.aria-label = صورة أنشأها المستخدم
## 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 = pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل
.aria-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 = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها.
pdfjs-editor-alt-text-add-description-label = أضِف وصف pdfjs-editor-alt-text-add-description-label = أضِف وصف
@ -388,29 +364,18 @@ 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.
pdfjs-editor-resizer-top-left = pdfjs-editor-resizer-label-top-left = الزاوية اليُسرى العُليا — غيّر الحجم
.aria-label = الزاوية اليُسرى العُليا — غيّر الحجم pdfjs-editor-resizer-label-top-middle = أعلى الوسط - غيّر الحجم
pdfjs-editor-resizer-top-middle = pdfjs-editor-resizer-label-top-right = الزاوية اليُمنى العُليا - غيّر الحجم
.aria-label = أعلى الوسط - غيّر الحجم pdfjs-editor-resizer-label-middle-right = اليمين الأوسط - غيّر الحجم
pdfjs-editor-resizer-top-right = pdfjs-editor-resizer-label-bottom-right = الزاوية اليُمنى السُفلى - غيّر الحجم
.aria-label = الزاوية اليُمنى العُليا - غيّر الحجم pdfjs-editor-resizer-label-bottom-middle = أسفل الوسط - غيّر الحجم
pdfjs-editor-resizer-middle-right = pdfjs-editor-resizer-label-bottom-left = الزاوية اليُسرى السُفلية - غيّر الحجم
.aria-label = اليمين الأوسط - غيّر الحجم pdfjs-editor-resizer-label-middle-left = مُنتصف اليسار - غيّر الحجم
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
@ -437,159 +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.
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 = أضِف
pdfjs-editor-edit-signature-update-button = حدّث
## Main menu for adding/removing signatures
pdfjs-editor-delete-signature-button1 =
.title = أزِل التوقيع المحفوظ
pdfjs-editor-delete-signature-button-label1 = أزِل التوقيع المحفوظ
## Editor toolbar
pdfjs-editor-add-signature-edit-button-label = عدّل الوصف
## Edit signature description dialog
pdfjs-editor-edit-signature-dialog-title = عدّل الوصف

View File

@ -77,10 +77,22 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Propiedaes del documentu… pdfjs-document-properties-button-label = Propiedaes del documentu…
pdfjs-document-properties-file-name = Nome del ficheru: pdfjs-document-properties-file-name = Nome del ficheru:
pdfjs-document-properties-file-size = Tamañu del ficheru: pdfjs-document-properties-file-size = Tamañu del ficheru:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
pdfjs-document-properties-title = Títulu: pdfjs-document-properties-title = Títulu:
pdfjs-document-properties-keywords = Pallabres clave: pdfjs-document-properties-keywords = Pallabres clave:
pdfjs-document-properties-creation-date = Data de creación: pdfjs-document-properties-creation-date = Data de creación:
pdfjs-document-properties-modification-date = Data de modificación: pdfjs-document-properties-modification-date = Data de modificación:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-producer = Productor del PDF: pdfjs-document-properties-producer = Productor del PDF:
pdfjs-document-properties-version = Versión del PDF: pdfjs-document-properties-version = Versión del PDF:
pdfjs-document-properties-page-count = Númberu de páxines: pdfjs-document-properties-page-count = Númberu de páxines:
@ -166,7 +178,24 @@ pdfjs-page-landmark =
pdfjs-loading-error = Asocedió un fallu mentanto se cargaba'l PDF. pdfjs-loading-error = Asocedió un fallu mentanto se cargaba'l PDF.
## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
## Password ## Password
pdfjs-password-ok-button = Aceptar pdfjs-password-ok-button = Aceptar
pdfjs-password-cancel-button = Encaboxar pdfjs-password-cancel-button = Encaboxar
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -89,12 +89,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Sənəd xüsusiyyətləri… pdfjs-document-properties-button-label = Sənəd xüsusiyyətləri…
pdfjs-document-properties-file-name = Fayl adı: pdfjs-document-properties-file-name = Fayl adı:
pdfjs-document-properties-file-size = Fayl ölçüsü: pdfjs-document-properties-file-size = Fayl ölçüsü:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bayt)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bayt)
pdfjs-document-properties-title = Başlık: pdfjs-document-properties-title = Başlık:
pdfjs-document-properties-author = Müəllif: pdfjs-document-properties-author = Müəllif:
pdfjs-document-properties-subject = Mövzu: pdfjs-document-properties-subject = Mövzu:
pdfjs-document-properties-keywords = Açar sözlər: pdfjs-document-properties-keywords = Açar sözlər:
pdfjs-document-properties-creation-date = Yaradılış Tarixi : pdfjs-document-properties-creation-date = Yaradılış Tarixi :
pdfjs-document-properties-modification-date = Dəyişdirilmə Tarixi : pdfjs-document-properties-modification-date = Dəyişdirilmə Tarixi :
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Yaradan: pdfjs-document-properties-creator = Yaradan:
pdfjs-document-properties-producer = PDF yaradıcısı: pdfjs-document-properties-producer = PDF yaradıcısı:
pdfjs-document-properties-version = PDF versiyası: pdfjs-document-properties-version = PDF versiyası:
@ -201,6 +213,9 @@ pdfjs-page-scale-actual = Hazırkı Həcm
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = PDF yüklenərkən bir səhv yarandı. pdfjs-loading-error = PDF yüklenərkən bir səhv yarandı.
@ -211,6 +226,10 @@ pdfjs-rendering-error = Səhifə göstərilərkən səhv yarandı.
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -226,3 +245,13 @@ pdfjs-password-invalid = Parol səhvdir. Bir daha yoxlayın.
pdfjs-password-ok-button = Tamam pdfjs-password-ok-button = Tamam
pdfjs-password-cancel-button = Ləğv et pdfjs-password-cancel-button = Ləğv et
pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF şriftlərini istifadə etmək mümkün deyil. pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF şriftlərini istifadə etmək mümkün deyil.
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -105,13 +105,13 @@ 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 # $size_kb (Number) - the PDF file size in kilobytes
# $b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } КБ ({ $b } байтаў) pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
# Variables: # Variables:
# $mb (Number) - the PDF file size in megabytes # $size_mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes # $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байтаў) pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт)
pdfjs-document-properties-title = Загаловак: pdfjs-document-properties-title = Загаловак:
pdfjs-document-properties-author = Аўтар: pdfjs-document-properties-author = Аўтар:
pdfjs-document-properties-subject = Тэма: pdfjs-document-properties-subject = Тэма:
@ -121,6 +121,10 @@ pdfjs-document-properties-modification-date = Дата змянення:
# Variables: # Variables:
# $dateObj (Date) - the creation/modification date and time of the PDF file # $dateObj (Date) - the creation/modification date and time of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Стваральнік: pdfjs-document-properties-creator = Стваральнік:
pdfjs-document-properties-producer = Вырабнік PDF: pdfjs-document-properties-producer = Вырабнік PDF:
pdfjs-document-properties-version = Версія PDF: pdfjs-document-properties-version = Версія PDF:
@ -265,6 +269,10 @@ pdfjs-rendering-error = Здарылася памылка падчас адлю
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -288,13 +296,9 @@ pdfjs-web-fonts-disabled = Шрыфты Сеціва забаронены: не
pdfjs-editor-free-text-button = pdfjs-editor-free-text-button =
.title = Тэкст .title = Тэкст
pdfjs-editor-color-picker-free-text-input =
.title = Змяніць колер тэксту
pdfjs-editor-free-text-button-label = Тэкст pdfjs-editor-free-text-button-label = Тэкст
pdfjs-editor-ink-button = pdfjs-editor-ink-button =
.title = Маляваць .title = Маляваць
pdfjs-editor-color-picker-ink-input =
.title = Змяніць колер малюнка
pdfjs-editor-ink-button-label = Маляваць pdfjs-editor-ink-button-label = Маляваць
pdfjs-editor-stamp-button = pdfjs-editor-stamp-button =
.title = Дадаць або змяніць выявы .title = Дадаць або змяніць выявы
@ -306,33 +310,6 @@ pdfjs-highlight-floating-button1 =
.title = Падфарбаваць .title = Падфарбаваць
.aria-label = Падфарбаваць .aria-label = Падфарбаваць
pdfjs-highlight-floating-button-label = Падфарбаваць pdfjs-highlight-floating-button-label = Падфарбаваць
pdfjs-comment-floating-button =
.title = Каментаваць
.aria-label = Каментаваць
pdfjs-comment-floating-button-label = Каментаваць
pdfjs-editor-comment-button =
.title = Каментарый
.aria-label = Каментарый
pdfjs-editor-comment-button-label = Каментарый
pdfjs-editor-signature-button =
.title = Дадаць подпіс
pdfjs-editor-signature-button-label = Дадаць подпіс
## Default editor aria labels
# “Highlight” is a noun, the string is used on the editor for highlights.
pdfjs-editor-highlight-editor =
.aria-label = Рэдактар вылучэнняў
# “Drawing” is a noun, the string is used on the editor for drawings.
pdfjs-editor-ink-editor =
.aria-label = Рэдактар малюнкаў
# Used when a signature editor is selected/hovered.
# Variables:
# $description (String) - a string describing/labeling the signature.
pdfjs-editor-signature-editor1 =
.aria-description = Рэдактар подпісаў: { $description }
pdfjs-editor-stamp-editor =
.aria-label = Рэдактар выяў
## Remove button for the various kind of editor. ## Remove button for the various kind of editor.
@ -344,8 +321,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 = Выдаліць подпіс
## ##
@ -362,42 +337,19 @@ 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-add-signature-container = pdfjs-free-text =
.aria-label = Элементы кіравання подпісамі і захаваныя подпісы
pdfjs-editor-signature-add-signature-button =
.title = Дадаць новы подпіс
pdfjs-editor-signature-add-signature-button-label = Дадаць новы подпіс
# Used on the button to use an already saved signature.
# Variables:
# $description (String) - a string describing/labeling the signature.
pdfjs-editor-add-saved-signature-button =
.title = Захаваны подпіс: { $description }
# .default-content is used as a placeholder in an empty text editor.
pdfjs-free-text2 =
.aria-label = Тэкставы рэдактар .aria-label = Тэкставы рэдактар
.default-content = Пачніце ўводзіць… pdfjs-free-text-default-content = Пачніце набор тэксту…
# Used to show how many comments are present in the pdf file. pdfjs-ink =
# Variables: .aria-label = Графічны рэдактар
# $count (Number) - the number of comments. pdfjs-ink-canvas =
pdfjs-editor-comments-sidebar-title = .aria-label = Выява, створаная карыстальнікам
{ $count ->
[one] Каментарый
[few] Каментарыі
*[many] Каментарыі
}
pdfjs-editor-comments-sidebar-close-button =
.title = Закрыць бакавую панэль
.aria-label = Закрыць бакавую панэль
pdfjs-editor-comments-sidebar-close-button-label = Закрыць бакавую панэль
# Instructional copy to add a comment by selecting text or an annotations.
pdfjs-editor-comments-sidebar-no-comments1 = Бачыце штосьці вартае ўвагі? Вылучыце і пакіньце каментарый.
pdfjs-editor-comments-sidebar-no-comments-link = Падрабязней
## 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 = pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст
.aria-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 = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца.
pdfjs-editor-alt-text-add-description-label = Дадаць апісанне pdfjs-editor-alt-text-add-description-label = Дадаць апісанне
@ -410,29 +362,18 @@ 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.
pdfjs-editor-resizer-top-left = pdfjs-editor-resizer-label-top-left = Верхні левы кут — змяніць памер
.aria-label = Верхні левы кут — змяніць памер pdfjs-editor-resizer-label-top-middle = Уверсе пасярэдзіне — змяніць памер
pdfjs-editor-resizer-top-middle = pdfjs-editor-resizer-label-top-right = Верхні правы кут — змяніць памер
.aria-label = Уверсе пасярэдзіне — змяніць памер pdfjs-editor-resizer-label-middle-right = Пасярэдзіне справа — змяніць памер
pdfjs-editor-resizer-top-right = pdfjs-editor-resizer-label-bottom-right = Правы ніжні кут — змяніць памер
.aria-label = Верхні правы кут — змяніць памер pdfjs-editor-resizer-label-bottom-middle = Пасярэдзіне ўнізе — змяніць памер
pdfjs-editor-resizer-middle-right = pdfjs-editor-resizer-label-bottom-left = Левы ніжні кут — змяніць памер
.aria-label = Пасярэдзіне справа — змяніць памер pdfjs-editor-resizer-label-middle-left = Пасярэдзіне злева — змяніць памер
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
@ -482,19 +423,14 @@ pdfjs-editor-new-alt-text-error-close-button = Закрыць
# Variables: # Variables:
# $totalSize (Number) - the total size (in MB) of the AI model. # $totalSize (Number) - the total size (in MB) of the AI model.
# $downloadedSize (Number) - the downloaded 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 } МБ) pdfjs-editor-new-alt-text-ai-model-downloading-progress = Сцягванне мадэлі ШІ для тэксту для атрыбута alt ({ $downloadedSize } з { $totalSize } МБ)
.aria-valuetext = Сцягванне мадэлі ШІ для тэксту для атрыбута 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. # 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 дададзены 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. # 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 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. # 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 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. # "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: # Variables:
@ -521,134 +457,3 @@ pdfjs-editor-alt-text-settings-editor-title = Рэдактар тэксту дл
pdfjs-editor-alt-text-settings-show-dialog-button-label = Адразу паказваць рэдактар тэксту для атрыбута alt пры даданні выявы pdfjs-editor-alt-text-settings-show-dialog-button-label = Адразу паказваць рэдактар тэксту для атрыбута alt пры даданні выявы
pdfjs-editor-alt-text-settings-show-dialog-description = Дапамагае пераканацца, што ўсе вашы выявы маюць альтэрнатыўны тэкст. pdfjs-editor-alt-text-settings-show-dialog-description = Дапамагае пераканацца, што ўсе вашы выявы маюць альтэрнатыўны тэкст.
pdfjs-editor-alt-text-settings-close-button = Закрыць pdfjs-editor-alt-text-settings-close-button = Закрыць
## Accessibility labels (announced by screen readers) for objects added to the editor.
pdfjs-editor-highlight-added-alert = Вылучэнне дададзена
pdfjs-editor-freetext-added-alert = Тэкст дададзены
pdfjs-editor-ink-added-alert = Малюнак дададзены
pdfjs-editor-stamp-added-alert = Выява дададзена
pdfjs-editor-signature-added-alert = Подпіс дададзены
## "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-image-no-data-error-title = Не ўдалося пераўтварыць гэту выяву ў подпіс
pdfjs-editor-add-signature-image-no-data-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 = Абнавіць
## Comment popup
pdfjs-editor-edit-comment-popup-button-label = Змяніць каментарый
pdfjs-editor-edit-comment-popup-button =
.title = Змяніць каментарый
pdfjs-editor-delete-comment-popup-button-label = Выдаліць каментарый
pdfjs-editor-delete-comment-popup-button =
.title = Выдаліць каментарый
pdfjs-show-comment-button =
.title = Паказаць каментарый
## Edit a comment dialog
# An existing comment is edited
pdfjs-editor-edit-comment-dialog-title-when-editing = Змяніць каментарый
pdfjs-editor-edit-comment-dialog-save-button-when-editing = Абнавіць
# No existing comment
pdfjs-editor-edit-comment-dialog-title-when-adding = Дадаць каментарый
pdfjs-editor-edit-comment-dialog-save-button-when-adding = Дадаць
pdfjs-editor-edit-comment-dialog-text-input =
.placeholder = Пачніце набор тэксту…
pdfjs-editor-edit-comment-dialog-cancel-button = Скасаваць
## Edit a comment button in the editor toolbar
pdfjs-editor-edit-comment-button =
.title = Змяніць каментарый
pdfjs-editor-add-comment-button =
.title = Дадаць каментарый
## Main menu for adding/removing signatures
pdfjs-editor-delete-signature-button1 =
.title = Выдаліць захаваны подпіс
pdfjs-editor-delete-signature-button-label1 = Выдаліць захаваны подпіс
## Editor toolbar
pdfjs-editor-add-signature-edit-button-label = Рэдагаваць апісанне
## Edit signature description dialog
pdfjs-editor-edit-signature-dialog-title = Рэдагаваць апісанне

View File

@ -112,6 +112,14 @@ pdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) }
# $mb (Number) - the PDF file size in megabytes # $mb (Number) - the PDF file size in megabytes
# $b (Number) - the PDF file size in bytes # $b (Number) - the PDF file size in bytes
pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байта) pdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } МБ ({ $b } байта)
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байта)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байта)
pdfjs-document-properties-title = Заглавие: pdfjs-document-properties-title = Заглавие:
pdfjs-document-properties-author = Автор: pdfjs-document-properties-author = Автор:
pdfjs-document-properties-subject = Тема: pdfjs-document-properties-subject = Тема:
@ -121,6 +129,10 @@ pdfjs-document-properties-modification-date = Дата на промяна:
# Variables: # Variables:
# $dateObj (Date) - the creation/modification date and time of the PDF file # $dateObj (Date) - the creation/modification date and time of the PDF file
pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") } pdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: "short", timeStyle: "medium") }
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Създател: pdfjs-document-properties-creator = Създател:
pdfjs-document-properties-producer = PDF произведен от: pdfjs-document-properties-producer = PDF произведен от:
pdfjs-document-properties-version = Издание на PDF: pdfjs-document-properties-version = Издание на PDF:
@ -263,6 +275,10 @@ pdfjs-rendering-error = Грешка при изчертаване на стра
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -316,15 +332,19 @@ pdfjs-editor-ink-opacity-input = Прозрачност
pdfjs-editor-stamp-add-image-button = pdfjs-editor-stamp-add-image-button =
.title = Добавяне на изображение .title = Добавяне на изображение
pdfjs-editor-stamp-add-image-button-label = Добавяне на изображение pdfjs-editor-stamp-add-image-button-label = Добавяне на изображение
# .default-content is used as a placeholder in an empty text editor. pdfjs-free-text =
pdfjs-free-text2 =
.aria-label = Текстов редактор .aria-label = Текстов редактор
.default-content = Започнете да пишете… pdfjs-free-text-default-content = Започнете да пишете…
pdfjs-editor-comments-sidebar-close-button-label = Затваряне на страничната лента pdfjs-ink =
.aria-label = Промяна на рисунка
pdfjs-ink-canvas =
.aria-label = Изображение, създадено от потребител
## 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-dialog-label = Изберете от възможностите pdfjs-editor-alt-text-dialog-label = Изберете от възможностите
pdfjs-editor-alt-text-dialog-description = Алтернативният текст помага на потребителите, когато не могат да видят изображението или то не се зарежда. pdfjs-editor-alt-text-dialog-description = Алтернативният текст помага на потребителите, когато не могат да видят изображението или то не се зарежда.
pdfjs-editor-alt-text-add-description-label = Добавяне на описание pdfjs-editor-alt-text-add-description-label = Добавяне на описание
@ -341,22 +361,14 @@ pdfjs-editor-alt-text-textarea =
## 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.
pdfjs-editor-resizer-top-left = pdfjs-editor-resizer-label-top-left = Горен ляв ъгъл — преоразмеряване
.aria-label = Горен ляв ъгъл — преоразмеряване pdfjs-editor-resizer-label-top-middle = Горе в средата — преоразмеряване
pdfjs-editor-resizer-top-middle = pdfjs-editor-resizer-label-top-right = Горен десен ъгъл — преоразмеряване
.aria-label = Горе в средата — преоразмеряване pdfjs-editor-resizer-label-middle-right = Дясно в средата — преоразмеряване
pdfjs-editor-resizer-top-right = pdfjs-editor-resizer-label-bottom-right = Долен десен ъгъл — преоразмеряване
.aria-label = Горен десен ъгъл — преоразмеряване pdfjs-editor-resizer-label-bottom-middle = Долу в средата — преоразмеряване
pdfjs-editor-resizer-middle-right = pdfjs-editor-resizer-label-bottom-left = Долен ляв ъгъл — преоразмеряване
.aria-label = Дясно в средата — преоразмеряване pdfjs-editor-resizer-label-middle-left = Ляво в средата — преоразмеряване
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
@ -377,12 +389,13 @@ pdfjs-editor-colorpicker-pink =
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 ## 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. ## 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 = Научете повече
pdfjs-editor-new-alt-text-not-now-button = Не сега
## Image alt-text settings ## Image alt-text settings
pdfjs-editor-alt-text-settings-delete-model-button = Изтриване

View File

@ -85,12 +85,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = নথি বৈশিষ্ট্য… 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:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } কেবি ({ $size_b } বাইট)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } এমবি ({ $size_b } বাইট)
pdfjs-document-properties-title = শিরোনাম: pdfjs-document-properties-title = শিরোনাম:
pdfjs-document-properties-author = লেখক: pdfjs-document-properties-author = লেখক:
pdfjs-document-properties-subject = বিষয়: pdfjs-document-properties-subject = বিষয়:
pdfjs-document-properties-keywords = কীওয়ার্ড: 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:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = প্রস্তুতকারক: pdfjs-document-properties-creator = প্রস্তুতকারক:
pdfjs-document-properties-producer = পিডিএফ প্রস্তুতকারক: pdfjs-document-properties-producer = পিডিএফ প্রস্তুতকারক:
pdfjs-document-properties-version = পিডিএফ সংষ্করণ: pdfjs-document-properties-version = পিডিএফ সংষ্করণ:
@ -191,6 +203,9 @@ pdfjs-page-scale-actual = প্রকৃত আকার
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = পিডিএফ লোড করার সময় ত্রুটি দেখা দিয়েছে। pdfjs-loading-error = পিডিএফ লোড করার সময় ত্রুটি দেখা দিয়েছে।
@ -201,6 +216,10 @@ pdfjs-rendering-error = পাতা উপস্থাপনার সময়
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -216,3 +235,13 @@ pdfjs-password-invalid = ভুল পাসওয়ার্ড। অনুগ
pdfjs-password-ok-button = ঠিক আছে pdfjs-password-ok-button = ঠিক আছে
pdfjs-password-cancel-button = বাতিল pdfjs-password-cancel-button = বাতিল
pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয়: সংযুক্ত পিডিএফ ফন্ট ব্যবহার করা যাচ্ছে না। pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয়: সংযুক্ত পিডিএফ ফন্ট ব্যবহার করা যাচ্ছে না।
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -89,12 +89,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Document Properties… pdfjs-document-properties-button-label = Document Properties…
pdfjs-document-properties-file-name = File name: pdfjs-document-properties-file-name = File name:
pdfjs-document-properties-file-size = File size: pdfjs-document-properties-file-size = File size:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
pdfjs-document-properties-title = Title: pdfjs-document-properties-title = Title:
pdfjs-document-properties-author = Author: pdfjs-document-properties-author = Author:
pdfjs-document-properties-subject = Subject: pdfjs-document-properties-subject = Subject:
pdfjs-document-properties-keywords = Keywords: pdfjs-document-properties-keywords = Keywords:
pdfjs-document-properties-creation-date = Creation Date: pdfjs-document-properties-creation-date = Creation Date:
pdfjs-document-properties-modification-date = Modification Date: pdfjs-document-properties-modification-date = Modification Date:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Creator: pdfjs-document-properties-creator = Creator:
pdfjs-document-properties-producer = PDF Producer: pdfjs-document-properties-producer = PDF Producer:
pdfjs-document-properties-version = PDF Version: pdfjs-document-properties-version = PDF Version:
@ -195,6 +207,9 @@ pdfjs-page-scale-actual = Actual Size
# $scale (Number) - percent value for page scale # $scale (Number) - percent value for page scale
pdfjs-page-scale-percent = { $scale }% pdfjs-page-scale-percent = { $scale }%
## PDF page
## Loading indicator messages ## Loading indicator messages
pdfjs-loading-error = An error occurred while loading the PDF. pdfjs-loading-error = An error occurred while loading the PDF.
@ -220,3 +235,13 @@ pdfjs-password-invalid = Invalid password. Please try again.
pdfjs-password-ok-button = OK pdfjs-password-ok-button = OK
pdfjs-password-cancel-button = Cancel pdfjs-password-cancel-button = Cancel
pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts. pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
## Editing
## Alt-text dialog
## Editor resizers
## This is used in an aria label to help to understand the role of the resizer.

View File

@ -99,12 +99,24 @@ pdfjs-document-properties-button =
pdfjs-document-properties-button-label = Perzhioù an teul… pdfjs-document-properties-button-label = Perzhioù an teul…
pdfjs-document-properties-file-name = Anv restr: pdfjs-document-properties-file-name = Anv restr:
pdfjs-document-properties-file-size = Ment ar restr: pdfjs-document-properties-file-size = Ment ar restr:
# Variables:
# $size_kb (Number) - the PDF file size in kilobytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-kb = { $size_kb } Ke ({ $size_b } eizhbit)
# Variables:
# $size_mb (Number) - the PDF file size in megabytes
# $size_b (Number) - the PDF file size in bytes
pdfjs-document-properties-mb = { $size_mb } Me ({ $size_b } eizhbit)
pdfjs-document-properties-title = Titl: pdfjs-document-properties-title = Titl:
pdfjs-document-properties-author = Aozer: pdfjs-document-properties-author = Aozer:
pdfjs-document-properties-subject = Danvez: pdfjs-document-properties-subject = Danvez:
pdfjs-document-properties-keywords = Gerioù-alc'hwez: pdfjs-document-properties-keywords = Gerioù-alc'hwez:
pdfjs-document-properties-creation-date = Deiziad krouiñ: pdfjs-document-properties-creation-date = Deiziad krouiñ:
pdfjs-document-properties-modification-date = Deiziad kemmañ: pdfjs-document-properties-modification-date = Deiziad kemmañ:
# Variables:
# $date (Date) - the creation/modification date of the PDF file
# $time (Time) - the creation/modification time of the PDF file
pdfjs-document-properties-date-string = { $date }, { $time }
pdfjs-document-properties-creator = Krouer: pdfjs-document-properties-creator = Krouer:
pdfjs-document-properties-producer = Kenderc'her PDF: pdfjs-document-properties-producer = Kenderc'her PDF:
pdfjs-document-properties-version = Handelv PDF: pdfjs-document-properties-version = Handelv PDF:
@ -194,7 +206,7 @@ pdfjs-find-previous-button-label = Kent
pdfjs-find-next-button = pdfjs-find-next-button =
.title = Kavout an tamm frazenn war-lerc'h o klotañ ganti .title = Kavout an tamm frazenn war-lerc'h o klotañ ganti
pdfjs-find-next-button-label = War-lerc'h pdfjs-find-next-button-label = War-lerc'h
pdfjs-find-highlight-checkbox = Sklêrijennañ 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-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
@ -229,6 +241,10 @@ pdfjs-rendering-error = Degouezhet ez eus bet ur fazi e-pad skrammañ ar bajenna
## Annotations ## Annotations
# Variables:
# $date (Date) - the modification date of the annotation
# $time (Time) - the modification time of the annotation
pdfjs-annotation-date-string = { $date }, { $time }
# .alt: This is used as a tooltip. # .alt: This is used as a tooltip.
# Variables: # Variables:
# $type (String) - an annotation type from a list defined in the PDF spec # $type (String) - an annotation type from a list defined in the PDF spec
@ -256,13 +272,9 @@ pdfjs-editor-ink-button-label = Tresañ
pdfjs-editor-stamp-button = pdfjs-editor-stamp-button =
.title = Ouzhpennañ pe aozañ skeudennoù .title = Ouzhpennañ pe aozañ skeudennoù
pdfjs-editor-stamp-button-label = Ouzhpennañ pe aozañ skeudennoù pdfjs-editor-stamp-button-label = Ouzhpennañ pe aozañ skeudennoù
pdfjs-editor-highlight-button =
.title = Sklerijennañ ## Remove button for the various kind of editor.
pdfjs-editor-highlight-button-label = Sklerijennañ
pdfjs-highlight-floating-button1 =
.title = Sklerijennañ
.aria-label = Sklerijennañ
pdfjs-highlight-floating-button-label = Sklerijennañ
## ##
@ -275,49 +287,26 @@ 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-free-text =
pdfjs-editor-free-highlight-thickness-input = Tevded .aria-label = Aozer testennoù
pdfjs-ink =
.aria-label = Aozer tresoù
pdfjs-ink-canvas =
.aria-label = Skeudenn bet krouet gant an implijer·ez
## Alt-text dialog ## Alt-text dialog
pdfjs-editor-alt-text-add-description-label = Ouzhpennañ un deskrivadur pdfjs-editor-alt-text-add-description-label = Ouzhpennañ un deskrivadur
pdfjs-editor-alt-text-cancel-button = Nullañ pdfjs-editor-alt-text-cancel-button = Nullañ
pdfjs-editor-alt-text-save-button = Enrollañ pdfjs-editor-alt-text-save-button = Enrollañ
# .placeholder: This is a placeholder for the alt text input area
pdfjs-editor-alt-text-textarea = ## Editor resizers
.placeholder = Da skouer, “Ur paotr yaouank ouzh taol o tebriñ boued” ## This is used in an aria label to help to understand the role of the resizer.
## Color picker ## Color picker
# This means "Color used to highlight text"
pdfjs-editor-highlight-colorpicker-label = Liv sklêrijennañ
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 ## Show all highlights
## This is a toggle button to show/hide all the 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ñ

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