mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-02-08 00:21:11 +01:00
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c00591c1b6 | ||
|
|
280a02150e | ||
|
|
58ac273f1f | ||
|
|
b92bdf80a2 | ||
|
|
f302323c7e | ||
|
|
a0f3528053 | ||
|
|
ff42c0bd50 | ||
|
|
b3cd042ded | ||
|
|
01deb085f8 | ||
|
|
222a24c623 | ||
|
|
1e0ba4dfec | ||
|
|
22b97d1741 | ||
|
|
ea993bfc1b | ||
|
|
c7bea3b342 | ||
|
|
1c12b07726 | ||
|
|
88c2051698 | ||
|
|
bfd17b2586 | ||
|
|
d152e92185 | ||
|
|
f4326e17c4 | ||
|
|
3f21efc942 | ||
|
|
8eb9340fa7 | ||
|
|
031f633236 | ||
|
|
6509fdb1d6 | ||
|
|
586e85888b | ||
|
|
76dabeddb3 | ||
|
|
d25f13d1fd | ||
|
|
aa4d0f7c07 | ||
|
|
023af46186 | ||
|
|
839c257f87 | ||
|
|
ff7f87fc21 | ||
|
|
50a12e3e67 | ||
|
|
b517b5c597 | ||
|
|
384c6208b2 | ||
|
|
e4cd3176ab | ||
|
|
ecb09d62fc | ||
|
|
43273fde27 | ||
|
|
4ca205bac3 | ||
|
|
54d8c5e7b4 | ||
|
|
4a8fb4dde1 | ||
|
|
a80f10ff1a | ||
|
|
05b78ce03c | ||
|
|
987265720e | ||
|
|
62d5408cf0 | ||
|
|
814df09e21 | ||
|
|
d4fbae06d9 | ||
|
|
1370950843 | ||
|
|
5d02076313 | ||
|
|
2a83f955b0 | ||
|
|
06cf7dd7b0 | ||
|
|
35e78f7f11 | ||
|
|
a2909f9b66 | ||
|
|
9c903a0ebc | ||
|
|
471adfd023 | ||
|
|
7cdd03ad9b | ||
|
|
c0572c1c8f | ||
|
|
2cef80d05b | ||
|
|
5b368dd58a | ||
|
|
247ee02299 | ||
|
|
b3f35b6007 | ||
|
|
956c2a051a | ||
|
|
5505201930 | ||
|
|
dd6a0c6cf4 | ||
|
|
07a4aab246 | ||
|
|
806133379e | ||
|
|
48df8a5ea2 | ||
|
|
ab7d388ccb | ||
|
|
f2ac669ee4 | ||
|
|
001058abb2 | ||
|
|
50f2d4db65 | ||
|
|
9f660be8a2 | ||
|
|
640a3106d5 | ||
|
|
84b5866853 | ||
|
|
eaf605d720 | ||
|
|
4d9301fceb | ||
|
|
663d4cd6e7 | ||
|
|
45294d31cb | ||
|
|
95f62f3b33 | ||
|
|
23c7b1ac8e | ||
|
|
bfa44af327 | ||
|
|
109ea59fbc | ||
|
|
0aa4fc6af8 | ||
|
|
bfdcaadf7c | ||
|
|
bdc9323b15 | ||
|
|
3270c4a504 | ||
|
|
84d15dc453 | ||
|
|
d9f67bd8ee | ||
|
|
a4f4d460ca | ||
|
|
6a4a3b060d | ||
|
|
ce296d8d42 | ||
|
|
43bd7fa738 | ||
|
|
8188c87461 | ||
|
|
8abfd9a797 | ||
|
|
f04deeeddf | ||
|
|
3a20ea75b9 | ||
|
|
fef0cb1a6f | ||
|
|
eb014a36cc | ||
|
|
67673ea274 | ||
|
|
5e89981282 | ||
|
|
cbcb6279ad | ||
|
|
6612d7afaf | ||
|
|
cffd54e9c6 | ||
|
|
f40ab1a3f8 | ||
|
|
b5ed988267 | ||
|
|
a362a24779 | ||
|
|
a5010f99e7 | ||
|
|
1f69a3f6af | ||
|
|
6f5d5ac6d8 | ||
|
|
1dd6649b7d | ||
|
|
3532ac39d8 | ||
|
|
da463f2da9 | ||
|
|
91fa05d2e8 | ||
|
|
0ef085e23b | ||
|
|
a939f12391 | ||
|
|
adde05b530 | ||
|
|
54d84799c2 | ||
|
|
eab33828a9 | ||
|
|
cb36cbdc13 | ||
|
|
cb28250fbe | ||
|
|
98c1955bd4 | ||
|
|
5c06beae07 | ||
|
|
687cd848e0 | ||
|
|
13e070a8a3 | ||
|
|
2838e161b8 | ||
|
|
eccbcbe5eb | ||
|
|
424c7989aa | ||
|
|
430b8a9e90 | ||
|
|
f385fd9783 | ||
|
|
9677798ba0 |
3
.github/workflows/lint.yml
vendored
3
.github/workflows/lint.yml
vendored
@ -30,8 +30,5 @@ jobs:
|
|||||||
- name: Run lint
|
- name: Run lint
|
||||||
run: npx gulp lint
|
run: npx gulp lint
|
||||||
|
|
||||||
- name: Run lint-chromium
|
|
||||||
run: npx gulp lint-chromium
|
|
||||||
|
|
||||||
- name: Run lint-mozcentral
|
- name: Run lint-mozcentral
|
||||||
run: npx gulp lint-mozcentral
|
run: npx gulp lint-mozcentral
|
||||||
|
|||||||
31
.github/workflows/prefs_tests.yml
vendored
Normal file
31
.github/workflows/prefs_tests.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
name: Prefs tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
node-version: [lts/*]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run prefs tests
|
||||||
|
run: npx gulp prefstest
|
||||||
2
.github/workflows/types_tests.yml
vendored
2
.github/workflows/types_tests.yml
vendored
@ -28,4 +28,4 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run types tests
|
- name: Run types tests
|
||||||
run: npx gulp typestest
|
run: npx gulp types
|
||||||
|
|||||||
@ -31,10 +31,12 @@ export default [
|
|||||||
"**/docs/",
|
"**/docs/",
|
||||||
"**/node_modules/",
|
"**/node_modules/",
|
||||||
"external/bcmaps/",
|
"external/bcmaps/",
|
||||||
|
"external/brotli/",
|
||||||
"external/builder/fixtures/",
|
"external/builder/fixtures/",
|
||||||
"external/builder/fixtures_babel/",
|
"external/builder/fixtures_babel/",
|
||||||
"external/openjpeg/",
|
"external/openjpeg/",
|
||||||
"external/qcms/",
|
"external/qcms/",
|
||||||
|
"external/jbig2/",
|
||||||
"external/quickjs/",
|
"external/quickjs/",
|
||||||
"test/stats/results/",
|
"test/stats/results/",
|
||||||
"test/tmp/",
|
"test/tmp/",
|
||||||
@ -116,6 +118,7 @@ export default [
|
|||||||
"web",
|
"web",
|
||||||
"fluent-bundle",
|
"fluent-bundle",
|
||||||
"fluent-dom",
|
"fluent-dom",
|
||||||
|
"postcss-values-parser",
|
||||||
// See https://github.com/firebase/firebase-admin-node/discussions/1359.
|
// See https://github.com/firebase/firebase-admin-node/discussions/1359.
|
||||||
"eslint-plugin-perfectionist",
|
"eslint-plugin-perfectionist",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -46,19 +46,13 @@ const PDFViewerApplication = {
|
|||||||
* @returns {Promise} - Returns the promise, which is resolved when document
|
* @returns {Promise} - Returns the promise, which is resolved when document
|
||||||
* is opened.
|
* is opened.
|
||||||
*/
|
*/
|
||||||
open(params) {
|
async open(params) {
|
||||||
if (this.pdfLoadingTask) {
|
if (this.pdfLoadingTask) {
|
||||||
// We need to destroy already opened document
|
// We need to destroy already opened document.
|
||||||
return this.close().then(
|
await this.close();
|
||||||
function () {
|
|
||||||
// ... and repeat the open() call.
|
|
||||||
return this.open(params);
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = params.url;
|
const { url } = params;
|
||||||
const self = this;
|
|
||||||
this.setTitleUsingUrl(url);
|
this.setTitleUsingUrl(url);
|
||||||
|
|
||||||
// Loading document.
|
// Loading document.
|
||||||
@ -70,24 +64,22 @@ const PDFViewerApplication = {
|
|||||||
});
|
});
|
||||||
this.pdfLoadingTask = loadingTask;
|
this.pdfLoadingTask = loadingTask;
|
||||||
|
|
||||||
loadingTask.onProgress = function (progressData) {
|
loadingTask.onProgress = evt => this.progress(evt.percent);
|
||||||
self.progress(progressData.loaded / progressData.total);
|
|
||||||
};
|
|
||||||
|
|
||||||
return loadingTask.promise.then(
|
return loadingTask.promise.then(
|
||||||
function (pdfDocument) {
|
pdfDocument => {
|
||||||
// Document loaded, specifying document for the viewer.
|
// Document loaded, specifying document for the viewer.
|
||||||
self.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
self.pdfViewer.setDocument(pdfDocument);
|
this.pdfViewer.setDocument(pdfDocument);
|
||||||
self.pdfLinkService.setDocument(pdfDocument);
|
this.pdfLinkService.setDocument(pdfDocument);
|
||||||
self.pdfHistory.initialize({
|
this.pdfHistory.initialize({
|
||||||
fingerprint: pdfDocument.fingerprints[0],
|
fingerprint: pdfDocument.fingerprints[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
self.loadingBar.hide();
|
this.loadingBar.hide();
|
||||||
self.setTitleUsingMetadata(pdfDocument);
|
this.setTitleUsingMetadata(pdfDocument);
|
||||||
},
|
},
|
||||||
function (reason) {
|
reason => {
|
||||||
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";
|
||||||
@ -96,10 +88,10 @@ const PDFViewerApplication = {
|
|||||||
? "pdfjs-missing-file-error"
|
? "pdfjs-missing-file-error"
|
||||||
: "pdfjs-unexpected-response-error";
|
: "pdfjs-unexpected-response-error";
|
||||||
}
|
}
|
||||||
self.l10n.get(key).then(msg => {
|
this.l10n.get(key).then(msg => {
|
||||||
self.error(msg, { message: reason?.message });
|
this.error(msg, { message: reason.message });
|
||||||
});
|
});
|
||||||
self.loadingBar.hide();
|
this.loadingBar.hide();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -109,9 +101,9 @@ const PDFViewerApplication = {
|
|||||||
* @returns {Promise} - Returns the promise, which is resolved when all
|
* @returns {Promise} - Returns the promise, which is resolved when all
|
||||||
* destruction is completed.
|
* destruction is completed.
|
||||||
*/
|
*/
|
||||||
close() {
|
async close() {
|
||||||
if (!this.pdfLoadingTask) {
|
if (!this.pdfLoadingTask) {
|
||||||
return Promise.resolve();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = this.pdfLoadingTask.destroy();
|
const promise = this.pdfLoadingTask.destroy();
|
||||||
@ -128,7 +120,7 @@ const PDFViewerApplication = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise;
|
await promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
get loadingBar() {
|
get loadingBar() {
|
||||||
@ -152,48 +144,36 @@ const PDFViewerApplication = {
|
|||||||
this.setTitle(title);
|
this.setTitle(title);
|
||||||
},
|
},
|
||||||
|
|
||||||
setTitleUsingMetadata(pdfDocument) {
|
async setTitleUsingMetadata(pdfDocument) {
|
||||||
const self = this;
|
const { info, metadata } = await pdfDocument.getMetadata();
|
||||||
pdfDocument.getMetadata().then(function (data) {
|
this.documentInfo = info;
|
||||||
const info = data.info,
|
this.metadata = metadata;
|
||||||
metadata = data.metadata;
|
|
||||||
self.documentInfo = info;
|
|
||||||
self.metadata = metadata;
|
|
||||||
|
|
||||||
// Provides some basic debug information
|
// Provides some basic debug information
|
||||||
console.log(
|
console.log(
|
||||||
"PDF " +
|
`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` +
|
||||||
pdfDocument.fingerprints[0] +
|
`${(metadata?.get("pdf:producer") || info.Producer || "-").trim()} / ` +
|
||||||
" [" +
|
`${(metadata?.get("xmp:creatortool") || info.Creator || "-").trim()}` +
|
||||||
info.PDFFormatVersion +
|
`] (PDF.js: ${pdfjsLib.version || "?"} [${pdfjsLib.build || "?"}])`
|
||||||
" " +
|
);
|
||||||
(info.Producer || "-").trim() +
|
|
||||||
" / " +
|
|
||||||
(info.Creator || "-").trim() +
|
|
||||||
"]" +
|
|
||||||
" (PDF.js: " +
|
|
||||||
(pdfjsLib.version || "-") +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
|
|
||||||
let pdfTitle;
|
let pdfTitle;
|
||||||
if (metadata && metadata.has("dc:title")) {
|
if (metadata && metadata.has("dc:title")) {
|
||||||
const title = metadata.get("dc:title");
|
const title = metadata.get("dc:title");
|
||||||
// Ghostscript sometimes returns 'Untitled', so prevent setting the
|
// Ghostscript sometimes returns 'Untitled', so prevent setting the
|
||||||
// title to 'Untitled.
|
// title to 'Untitled.
|
||||||
if (title !== "Untitled") {
|
if (title !== "Untitled") {
|
||||||
pdfTitle = title;
|
pdfTitle = title;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!pdfTitle && info && info.Title) {
|
if (!pdfTitle && info && info.Title) {
|
||||||
pdfTitle = info.Title;
|
pdfTitle = info.Title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdfTitle) {
|
if (pdfTitle) {
|
||||||
self.setTitle(pdfTitle + " - " + document.title);
|
this.setTitle(pdfTitle + " - " + document.title);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setTitle: function pdfViewSetTitle(title) {
|
setTitle: function pdfViewSetTitle(title) {
|
||||||
@ -223,8 +203,7 @@ const PDFViewerApplication = {
|
|||||||
console.error(`${message}\n\n${moreInfoText.join("\n")}`);
|
console.error(`${message}\n\n${moreInfoText.join("\n")}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
progress: function pdfViewProgress(level) {
|
progress(percent) {
|
||||||
const percent = Math.round(level * 100);
|
|
||||||
// Updating the bar if value increases.
|
// Updating the bar if value increases.
|
||||||
if (percent > this.loadingBar.percent || isNaN(percent)) {
|
if (percent > this.loadingBar.percent || isNaN(percent)) {
|
||||||
this.loadingBar.percent = percent;
|
this.loadingBar.percent = percent;
|
||||||
|
|||||||
@ -1,252 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"viewerCssTheme": {
|
|
||||||
"title": "Theme",
|
|
||||||
"description": "The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [0, 1, 2],
|
|
||||||
"default": 2
|
|
||||||
},
|
|
||||||
"showPreviousViewOnLoad": {
|
|
||||||
"description": "DEPRECATED. Set viewOnLoad to 1 to disable showing the last page/position on load.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"viewOnLoad": {
|
|
||||||
"title": "View position on load",
|
|
||||||
"description": "The position in the document upon load.\n -1 = Default (uses OpenAction if available, otherwise equal to `viewOnLoad = 0`).\n 0 = The last viewed page/position.\n 1 = The initial page/position.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [-1, 0, 1],
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"defaultZoomDelay": {
|
|
||||||
"title": "Default zoom delay",
|
|
||||||
"description": "Delay (in ms) to wait before redrawing the canvas.",
|
|
||||||
"type": "integer",
|
|
||||||
"default": 400
|
|
||||||
},
|
|
||||||
"defaultZoomValue": {
|
|
||||||
"title": "Default zoom level",
|
|
||||||
"description": "Default zoom level of the viewer. Accepted values: 'auto', 'page-actual', 'page-width', 'page-height', 'page-fit', or a zoom level in percents.",
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "|auto|page-actual|page-width|page-height|page-fit|[0-9]+\\.?[0-9]*(,[0-9]+\\.?[0-9]*){0,2}",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"sidebarViewOnLoad": {
|
|
||||||
"title": "Sidebar state on load",
|
|
||||||
"description": "Controls the state of the sidebar upon load.\n -1 = Default (uses PageMode if available, otherwise the last position if available/enabled).\n 0 = Do not show sidebar.\n 1 = Show thumbnails in sidebar.\n 2 = Show document outline in sidebar.\n 3 = Show attachments in sidebar.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [-1, 0, 1, 2, 3],
|
|
||||||
"default": -1
|
|
||||||
},
|
|
||||||
"enableHandToolOnLoad": {
|
|
||||||
"description": "DEPRECATED. Set cursorToolOnLoad to 1 to enable the hand tool by default.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableHWA": {
|
|
||||||
"title": "Enable hardware acceleration",
|
|
||||||
"description": "Whether to enable hardware acceleration.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"enableAltText": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableGuessAltText": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"enableAltTextModelDownload": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"enableNewAltTextWhenAddingImage": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"altTextLearnMoreUrl": {
|
|
||||||
"type": "string",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"commentLearnMoreUrl": {
|
|
||||||
"type": "string",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"enableSignatureEditor": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableUpdatedAddImage": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"cursorToolOnLoad": {
|
|
||||||
"title": "Cursor tool on load",
|
|
||||||
"description": "The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [0, 1],
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"pdfBugEnabled": {
|
|
||||||
"title": "Enable debugging tools",
|
|
||||||
"description": "Whether to enable debugging tools.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableScripting": {
|
|
||||||
"title": "Enable active content (JavaScript) in PDFs",
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to allow execution of active content (JavaScript) by PDF files.",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableHighlightFloatingButton": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"highlightEditorColors": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F,yellow_HCM=#FFFFCC,green_HCM=#53FFBC,blue_HCM=#80EBFF,pink_HCM=#F6B8FF,red_HCM=#C50043"
|
|
||||||
},
|
|
||||||
"disableRange": {
|
|
||||||
"title": "Disable range requests",
|
|
||||||
"description": "Whether to disable range requests (not recommended).",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disableStream": {
|
|
||||||
"title": "Disable streaming for requests",
|
|
||||||
"description": "Whether to disable streaming for requests (not recommended).",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disableAutoFetch": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disableFontFace": {
|
|
||||||
"title": "Disable @font-face",
|
|
||||||
"description": "Whether to disable @font-face and fall back to canvas rendering (this is more resource-intensive).",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disableTextLayer": {
|
|
||||||
"description": "DEPRECATED. Set textLayerMode to 0 to disable the text selection layer by default.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"textLayerMode": {
|
|
||||||
"title": "Text layer mode",
|
|
||||||
"description": "Controls if the text layer is enabled, and the selection mode that is used.\n 0 = Disabled.\n 1 = Enabled.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [0, 1],
|
|
||||||
"default": 1
|
|
||||||
},
|
|
||||||
"externalLinkTarget": {
|
|
||||||
"title": "External links target window",
|
|
||||||
"description": "Controls how external links will be opened.\n 0 = default.\n 1 = replaces current window.\n 2 = new window/tab.\n 3 = parent.\n 4 = in top window.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [0, 1, 2, 3, 4],
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"disablePageLabels": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disablePageMode": {
|
|
||||||
"description": "DEPRECATED.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"disableTelemetry": {
|
|
||||||
"title": "Disable telemetry",
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to prevent the extension from reporting the extension and browser version to the extension developers.",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"annotationMode": {
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [0, 1, 2, 3],
|
|
||||||
"default": 2
|
|
||||||
},
|
|
||||||
"annotationEditorMode": {
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [-1, 0, 3, 15],
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"capCanvasAreaFactor": {
|
|
||||||
"type": "integer",
|
|
||||||
"default": 200
|
|
||||||
},
|
|
||||||
"enablePermissions": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enableXfa": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"historyUpdateUrl": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"ignoreDestinationZoom": {
|
|
||||||
"title": "Ignore the zoom argument in destinations",
|
|
||||||
"description": "When enabled it will maintain the currently active zoom level, rather than letting the PDF document modify it, when navigating to internal destinations.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"enablePrintAutoRotate": {
|
|
||||||
"title": "Automatically rotate printed pages",
|
|
||||||
"description": "When enabled, landscape pages are rotated when printed.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"scrollModeOnLoad": {
|
|
||||||
"title": "Scroll mode on load",
|
|
||||||
"description": "Controls how the viewer scrolls upon load.\n -1 = Default (uses the last position if available/enabled).\n 3 = Page scrolling.\n 0 = Vertical scrolling.\n 1 = Horizontal scrolling.\n 2 = Wrapped scrolling.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [-1, 0, 1, 2, 3],
|
|
||||||
"default": -1
|
|
||||||
},
|
|
||||||
"spreadModeOnLoad": {
|
|
||||||
"title": "Spread mode on load",
|
|
||||||
"description": "Whether the viewer should join pages into spreads upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = No spreads.\n 1 = Odd spreads.\n 2 = Even spreads.",
|
|
||||||
"type": "integer",
|
|
||||||
"enum": [-1, 0, 1, 2],
|
|
||||||
"default": -1
|
|
||||||
},
|
|
||||||
"forcePageColors": {
|
|
||||||
"description": "When enabled, the pdf rendering will use the high contrast mode colors",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"pageColorsBackground": {
|
|
||||||
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
|
|
||||||
"type": "string",
|
|
||||||
"default": "Canvas"
|
|
||||||
},
|
|
||||||
"pageColorsForeground": {
|
|
||||||
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
|
|
||||||
"type": "string",
|
|
||||||
"default": "CanvasText"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
external/brotli/LICENSE_BROTLI
vendored
Normal file
19
external/brotli/LICENSE_BROTLI
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
8
external/brotli/README.md
vendored
Normal file
8
external/brotli/README.md
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
## Release
|
||||||
|
|
||||||
|
In order to get the file `decoder.js`:
|
||||||
|
* `gulp release-brotli --hash` followed by the git hash of the revision.
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
|
||||||
|
[brotli](https://github.com/google/brotli/) is under [MIT License](https://github.com/google/brotli/blob/master/LICENSE)
|
||||||
2466
external/brotli/decode.js
vendored
Normal file
2466
external/brotli/decode.js
vendored
Normal file
File diff suppressed because one or more lines are too long
270
external/chromium/prefs.mjs
vendored
Normal file
270
external/chromium/prefs.mjs
vendored
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
/* Copyright 2026 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const prefsMetadata = {
|
||||||
|
annotationEditorMode: {
|
||||||
|
enum: [-1, 0, 3, 15],
|
||||||
|
},
|
||||||
|
annotationMode: {
|
||||||
|
enum: [0, 1, 2, 3],
|
||||||
|
},
|
||||||
|
cursorToolOnLoad: {
|
||||||
|
title: "Cursor tool on load",
|
||||||
|
description:
|
||||||
|
"The cursor tool that is enabled upon load.\n 0 = Text selection tool.\n 1 = Hand tool.",
|
||||||
|
enum: [0, 1],
|
||||||
|
},
|
||||||
|
defaultZoomDelay: {
|
||||||
|
title: "Default zoom delay",
|
||||||
|
description: "Delay (in ms) to wait before redrawing the canvas.",
|
||||||
|
},
|
||||||
|
defaultZoomValue: {
|
||||||
|
title: "Default zoom level",
|
||||||
|
description:
|
||||||
|
"Default zoom level of the viewer. Accepted values: 'auto', 'page-actual', 'page-width', 'page-height', 'page-fit', or a zoom level in percents.",
|
||||||
|
pattern:
|
||||||
|
"|auto|page-actual|page-width|page-height|page-fit|[0-9]+\\.?[0-9]*(,[0-9]+\\.?[0-9]*){0,2}",
|
||||||
|
},
|
||||||
|
disableFontFace: {
|
||||||
|
title: "Disable @font-face",
|
||||||
|
description:
|
||||||
|
"Whether to disable @font-face and fall back to canvas rendering (this is more resource-intensive).",
|
||||||
|
},
|
||||||
|
disableRange: {
|
||||||
|
title: "Disable range requests",
|
||||||
|
description: "Whether to disable range requests (not recommended).",
|
||||||
|
},
|
||||||
|
disableStream: {
|
||||||
|
title: "Disable streaming for requests",
|
||||||
|
description: "Whether to disable streaming for requests (not recommended).",
|
||||||
|
},
|
||||||
|
disableTelemetry: {
|
||||||
|
title: "Disable telemetry",
|
||||||
|
description:
|
||||||
|
"Whether to prevent the extension from reporting the extension and browser version to the extension developers.",
|
||||||
|
},
|
||||||
|
enableAutoLinking: {
|
||||||
|
description: "Enable creation of hyperlinks from text that look like URLs.",
|
||||||
|
},
|
||||||
|
enableComment: {
|
||||||
|
description: "Enable creation of comment annotations.",
|
||||||
|
},
|
||||||
|
enableHWA: {
|
||||||
|
title: "Enable hardware acceleration",
|
||||||
|
description: "Whether to enable hardware acceleration.",
|
||||||
|
},
|
||||||
|
enableOptimizedPartialRendering: {
|
||||||
|
description:
|
||||||
|
"Enable tracking of PDF operations to optimize partial rendering.",
|
||||||
|
},
|
||||||
|
enablePrintAutoRotate: {
|
||||||
|
title: "Automatically rotate printed pages",
|
||||||
|
description: "When enabled, landscape pages are rotated when printed.",
|
||||||
|
},
|
||||||
|
enableScripting: {
|
||||||
|
title: "Enable active content (JavaScript) in PDFs",
|
||||||
|
description:
|
||||||
|
"Whether to allow execution of active content (JavaScript) by PDF files.",
|
||||||
|
},
|
||||||
|
externalLinkTarget: {
|
||||||
|
title: "External links target window",
|
||||||
|
description:
|
||||||
|
"Controls how external links will be opened.\n 0 = default.\n 1 = replaces current window.\n 2 = new window/tab.\n 3 = parent.\n 4 = in top window.",
|
||||||
|
enum: [0, 1, 2, 3, 4],
|
||||||
|
},
|
||||||
|
forcePageColors: {
|
||||||
|
description:
|
||||||
|
"When enabled, the pdf rendering will use the high contrast mode colors",
|
||||||
|
},
|
||||||
|
ignoreDestinationZoom: {
|
||||||
|
title: "Ignore the zoom argument in destinations",
|
||||||
|
description:
|
||||||
|
"When enabled it will maintain the currently active zoom level, rather than letting the PDF document modify it, when navigating to internal destinations.",
|
||||||
|
},
|
||||||
|
pageColorsBackground: {
|
||||||
|
description:
|
||||||
|
"The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
|
||||||
|
},
|
||||||
|
pageColorsForeground: {
|
||||||
|
description:
|
||||||
|
"The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
|
||||||
|
},
|
||||||
|
pdfBugEnabled: {
|
||||||
|
title: "Enable debugging tools",
|
||||||
|
description: "Whether to enable debugging tools.",
|
||||||
|
},
|
||||||
|
scrollModeOnLoad: {
|
||||||
|
title: "Scroll mode on load",
|
||||||
|
description:
|
||||||
|
"Controls how the viewer scrolls upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = Vertical scrolling.\n 1 = Horizontal scrolling.\n 2 = Wrapped scrolling.\n 3 = Page scrolling.",
|
||||||
|
enum: [-1, 0, 1, 2, 3],
|
||||||
|
},
|
||||||
|
sidebarViewOnLoad: {
|
||||||
|
title: "Sidebar state on load",
|
||||||
|
description:
|
||||||
|
"Controls the state of the sidebar upon load.\n -1 = Default (uses PageMode if available, otherwise the last position if available/enabled).\n 0 = Do not show sidebar.\n 1 = Show thumbnails in sidebar.\n 2 = Show document outline in sidebar.\n 3 = Show attachments in sidebar.",
|
||||||
|
enum: [-1, 0, 1, 2, 3],
|
||||||
|
},
|
||||||
|
spreadModeOnLoad: {
|
||||||
|
title: "Spread mode on load",
|
||||||
|
description:
|
||||||
|
"Whether the viewer should join pages into spreads upon load.\n -1 = Default (uses the last position if available/enabled).\n 0 = No spreads.\n 1 = Odd spreads.\n 2 = Even spreads.",
|
||||||
|
enum: [-1, 0, 1, 2],
|
||||||
|
},
|
||||||
|
textLayerMode: {
|
||||||
|
title: "Text layer mode",
|
||||||
|
description:
|
||||||
|
"Controls if the text layer is enabled, and the selection mode that is used.\n 0 = Disabled.\n 1 = Enabled.",
|
||||||
|
enum: [0, 1],
|
||||||
|
},
|
||||||
|
viewerCssTheme: {
|
||||||
|
title: "Theme",
|
||||||
|
description:
|
||||||
|
"The theme to use.\n0 = Use system theme.\n1 = Light theme.\n2 = Dark theme.",
|
||||||
|
enum: [0, 1, 2],
|
||||||
|
},
|
||||||
|
viewOnLoad: {
|
||||||
|
title: "View position on load",
|
||||||
|
description:
|
||||||
|
"The position in the document upon load.\n -1 = Default (uses OpenAction if available, otherwise equal to `viewOnLoad = 0`).\n 0 = The last viewed page/position.\n 1 = The initial page/position.",
|
||||||
|
enum: [-1, 0, 1],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deprecated keys are allowed in the managed preferences file.
|
||||||
|
// The code maintainer is responsible for adding migration logic to
|
||||||
|
// extensions/chromium/options/migration.js and web/chromecom.js .
|
||||||
|
const deprecatedPrefs = {
|
||||||
|
disablePageMode: {
|
||||||
|
description: "DEPRECATED.",
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disableTextLayer: {
|
||||||
|
description:
|
||||||
|
"DEPRECATED. Set textLayerMode to 0 to disable the text selection layer by default.",
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
enableHandToolOnLoad: {
|
||||||
|
description:
|
||||||
|
"DEPRECATED. Set cursorToolOnLoad to 1 to enable the hand tool by default.",
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
showPreviousViewOnLoad: {
|
||||||
|
description:
|
||||||
|
"DEPRECATED. Set viewOnLoad to 1 to disable showing the last page/position on load.",
|
||||||
|
type: "boolean",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildPrefsSchema(prefs) {
|
||||||
|
const properties = Object.create(null);
|
||||||
|
|
||||||
|
for (const name in prefs) {
|
||||||
|
const pref = prefs[name];
|
||||||
|
let type = typeof pref;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "boolean":
|
||||||
|
case "string":
|
||||||
|
break;
|
||||||
|
case "number":
|
||||||
|
type = "integer";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid type (${type}) for "${name}"-preference.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = prefsMetadata[name];
|
||||||
|
if (metadata) {
|
||||||
|
let numMetadataKeys = 0;
|
||||||
|
// Do some (very basic) validation of the metadata.
|
||||||
|
for (const key in metadata) {
|
||||||
|
const entry = metadata[key];
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "default":
|
||||||
|
case "type":
|
||||||
|
throw new Error(
|
||||||
|
`Invalid key (${key}) in metadata for "${name}"-preference.`
|
||||||
|
);
|
||||||
|
case "description":
|
||||||
|
if (entry.startsWith("DEPRECATED.")) {
|
||||||
|
throw new Error(
|
||||||
|
`The \`description\` of the "${name}"-preference cannot begin with "DEPRECATED."`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
numMetadataKeys++;
|
||||||
|
}
|
||||||
|
if (numMetadataKeys === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`No metadata for "${name}"-preference, remove the entry.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
properties[name] = {
|
||||||
|
type,
|
||||||
|
default: pref,
|
||||||
|
...metadata,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name in prefsMetadata) {
|
||||||
|
if (!properties[name]) {
|
||||||
|
// Do *not* throw here, since keeping the metadata up-to-date should be
|
||||||
|
// the responsibility of the CHROMIUM-addon maintainer.
|
||||||
|
console.error(
|
||||||
|
`The "${name}"-preference was removed, add it to \`deprecatedPrefs\` instead.\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name in deprecatedPrefs) {
|
||||||
|
const entry = deprecatedPrefs[name];
|
||||||
|
|
||||||
|
if (properties[name]) {
|
||||||
|
throw new Error(
|
||||||
|
`The "${name}"-preference should not be listed as deprecated.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!entry.description?.startsWith("DEPRECATED.")) {
|
||||||
|
throw new Error(
|
||||||
|
`The \`description\` of the deprecated "${name}"-preference must begin with "DEPRECATED."`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const key of ["default", "type"]) {
|
||||||
|
if (key in entry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`A \`${key}\` entry must be provided for the deprecated "${name}"-preference.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
properties[name] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "object",
|
||||||
|
properties,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { buildPrefsSchema };
|
||||||
196
external/jbig2/LICENSE_JBIG2
vendored
Normal file
196
external/jbig2/LICENSE_JBIG2
vendored
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
// Copyright 2014 The PDFium Authors
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
1. Definitions.
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
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
|
||||||
|
https://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.
|
||||||
13
external/jbig2/LICENSE_PDFJS_JBIG2
vendored
Normal file
13
external/jbig2/LICENSE_PDFJS_JBIG2
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2026 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.
|
||||||
12
external/jbig2/README.md
vendored
Normal file
12
external/jbig2/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
## Build
|
||||||
|
|
||||||
|
In order to generate the file `jbig2.js`:
|
||||||
|
* git clone https://github.com/mozilla/pdf.js.jbig2/
|
||||||
|
* 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/jbig2/` to compile the decoder
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
|
||||||
|
[PDFium](https://pdfium.googlesource.com/pdfium/) is under [Apache-2.0](https://pdfium.googlesource.com/pdfium/+/main/LICENSE)
|
||||||
|
and [pdf.js.jbig2](https://github.com/mozilla/pdf.js.jbig2/) is released under [Apache-2.0](https://github.com/mozilla/pdf.js.jbig2/blob/main/LICENSE) license so `jbig2.js` is released under [Apache-2.0](https://github.com/mozilla/pdf.js.jbig2/blob/main/LICENSE) license too.
|
||||||
3
external/jbig2/jbig2.js
vendored
Normal file
3
external/jbig2/jbig2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
external/jbig2/jbig2.wasm
vendored
Normal file
BIN
external/jbig2/jbig2.wasm
vendored
Normal file
Binary file not shown.
@ -13,8 +13,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: Remove the exception below once someone figures out how to fix it.
|
|
||||||
// eslint-disable-next-line import/no-unresolved
|
|
||||||
import { parse, registerWalkers, Root } from "postcss-values-parser";
|
import { parse, registerWalkers, Root } from "postcss-values-parser";
|
||||||
import { isString } from "stylelint/lib/utils/validateTypes.mjs";
|
import { isString } from "stylelint/lib/utils/validateTypes.mjs";
|
||||||
import stylelint from "stylelint";
|
import stylelint from "stylelint";
|
||||||
|
|||||||
419
gulpfile.mjs
419
gulpfile.mjs
@ -20,7 +20,9 @@ import {
|
|||||||
import { exec, execSync, spawn, spawnSync } from "child_process";
|
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 { buildPrefsSchema } from "./external/chromium/prefs.mjs";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
import { finished } from "stream/promises";
|
||||||
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";
|
||||||
@ -67,6 +69,7 @@ const GH_PAGES_DIR = BUILD_DIR + "gh-pages/";
|
|||||||
const DIST_DIR = BUILD_DIR + "dist/";
|
const DIST_DIR = BUILD_DIR + "dist/";
|
||||||
const TYPES_DIR = BUILD_DIR + "types/";
|
const TYPES_DIR = BUILD_DIR + "types/";
|
||||||
const TMP_DIR = BUILD_DIR + "tmp/";
|
const TMP_DIR = BUILD_DIR + "tmp/";
|
||||||
|
const PREFSTEST_DIR = BUILD_DIR + "prefstest/";
|
||||||
const TYPESTEST_DIR = BUILD_DIR + "typestest/";
|
const TYPESTEST_DIR = BUILD_DIR + "typestest/";
|
||||||
const COMMON_WEB_FILES = [
|
const COMMON_WEB_FILES = [
|
||||||
"web/images/*.{png,svg,gif}",
|
"web/images/*.{png,svg,gif}",
|
||||||
@ -229,7 +232,7 @@ function createWebpackAlias(defines) {
|
|||||||
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";
|
||||||
|
|
||||||
viewerAlias["web-download_manager"] = "web/download_manager.js";
|
viewerAlias["web-download_manager"] = "web/chromecom.js";
|
||||||
viewerAlias["web-external_services"] = "web/chromecom.js";
|
viewerAlias["web-external_services"] = "web/chromecom.js";
|
||||||
viewerAlias["web-null_l10n"] = "web/l10n.js";
|
viewerAlias["web-null_l10n"] = "web/l10n.js";
|
||||||
viewerAlias["web-preferences"] = "web/chromecom.js";
|
viewerAlias["web-preferences"] = "web/chromecom.js";
|
||||||
@ -283,7 +286,6 @@ function createWebpackConfig(
|
|||||||
disableVersionInfo = false,
|
disableVersionInfo = false,
|
||||||
disableSourceMaps = false,
|
disableSourceMaps = false,
|
||||||
disableLicenseHeader = false,
|
disableLicenseHeader = false,
|
||||||
defaultPreferencesDir = null,
|
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
const versionInfo = !disableVersionInfo
|
const versionInfo = !disableVersionInfo
|
||||||
@ -294,9 +296,6 @@ function createWebpackConfig(
|
|||||||
BUNDLE_VERSION: versionInfo.version,
|
BUNDLE_VERSION: versionInfo.version,
|
||||||
BUNDLE_BUILD: versionInfo.commit,
|
BUNDLE_BUILD: versionInfo.commit,
|
||||||
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
||||||
DEFAULT_PREFERENCES: defaultPreferencesDir
|
|
||||||
? getDefaultPreferences(defaultPreferencesDir)
|
|
||||||
: {},
|
|
||||||
DEFAULT_FTL: defines.GENERIC ? getDefaultFtl() : "",
|
DEFAULT_FTL: defines.GENERIC ? getDefaultFtl() : "",
|
||||||
};
|
};
|
||||||
const licenseHeaderLibre = fs
|
const licenseHeaderLibre = fs
|
||||||
@ -452,51 +451,6 @@ function getVersionJSON() {
|
|||||||
return JSON.parse(fs.readFileSync(BUILD_DIR + "version.json").toString());
|
return JSON.parse(fs.readFileSync(BUILD_DIR + "version.json").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkChromePreferencesFile(chromePrefsPath, webPrefs) {
|
|
||||||
const chromePrefs = JSON.parse(fs.readFileSync(chromePrefsPath).toString());
|
|
||||||
const chromePrefsKeys = Object.keys(chromePrefs.properties).filter(key => {
|
|
||||||
const description = chromePrefs.properties[key].description;
|
|
||||||
// Deprecated keys are allowed in the managed preferences file.
|
|
||||||
// The code maintainer is responsible for adding migration logic to
|
|
||||||
// extensions/chromium/options/migration.js and web/chromecom.js .
|
|
||||||
return !description?.startsWith("DEPRECATED.");
|
|
||||||
});
|
|
||||||
|
|
||||||
let ret = true;
|
|
||||||
// Verify that every entry in webPrefs is also in preferences_schema.json.
|
|
||||||
for (const [key, value] of Object.entries(webPrefs)) {
|
|
||||||
if (!chromePrefsKeys.includes(key)) {
|
|
||||||
// Note: this would also reject keys that are present but marked as
|
|
||||||
// DEPRECATED. A key should not be marked as DEPRECATED if it is still
|
|
||||||
// listed in webPrefs.
|
|
||||||
ret = false;
|
|
||||||
console.log(
|
|
||||||
`Warning: ${chromePrefsPath} does not contain an entry for pref: ${key}`
|
|
||||||
);
|
|
||||||
} else if (chromePrefs.properties[key].default !== value) {
|
|
||||||
ret = false;
|
|
||||||
console.log(
|
|
||||||
`Warning: not the same values (for "${key}"): ` +
|
|
||||||
`${chromePrefs.properties[key].default} !== ${value}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that preferences_schema.json does not contain entries that are not
|
|
||||||
// in webPrefs (app_options.js).
|
|
||||||
for (const key of chromePrefsKeys) {
|
|
||||||
if (!(key in webPrefs)) {
|
|
||||||
ret = false;
|
|
||||||
console.log(
|
|
||||||
`Warning: ${chromePrefsPath} contains an unrecognized pref: ${key}. ` +
|
|
||||||
`Remove it, or prepend "DEPRECATED. " and add migration logic to ` +
|
|
||||||
`extensions/chromium/options/migration.js and web/chromecom.js.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
||||||
@ -590,36 +544,24 @@ function createWorkerBundle(defines) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWebBundle(defines, options) {
|
function createWebBundle(defines, options) {
|
||||||
const viewerFileConfig = createWebpackConfig(
|
const viewerFileConfig = createWebpackConfig(defines, {
|
||||||
defines,
|
filename: "viewer.mjs",
|
||||||
{
|
library: {
|
||||||
filename: "viewer.mjs",
|
type: "module",
|
||||||
library: {
|
|
||||||
type: "module",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
defaultPreferencesDir: options.defaultPreferencesDir,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return gulp
|
return gulp
|
||||||
.src("./web/viewer.js", { encoding: false })
|
.src("./web/viewer.js", { encoding: false })
|
||||||
.pipe(webpack2Stream(viewerFileConfig));
|
.pipe(webpack2Stream(viewerFileConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGVWebBundle(defines, options) {
|
function createGVWebBundle(defines, options) {
|
||||||
const viewerFileConfig = createWebpackConfig(
|
const viewerFileConfig = createWebpackConfig(defines, {
|
||||||
defines,
|
filename: "viewer-geckoview.mjs",
|
||||||
{
|
library: {
|
||||||
filename: "viewer-geckoview.mjs",
|
type: "module",
|
||||||
library: {
|
|
||||||
type: "module",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
defaultPreferencesDir: options.defaultPreferencesDir,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return gulp
|
return gulp
|
||||||
.src("./web/viewer-geckoview.js", { encoding: false })
|
.src("./web/viewer-geckoview.js", { encoding: false })
|
||||||
.pipe(webpack2Stream(viewerFileConfig));
|
.pipe(webpack2Stream(viewerFileConfig));
|
||||||
@ -696,6 +638,10 @@ function createWasmBundle() {
|
|||||||
base: "external/qcms",
|
base: "external/qcms",
|
||||||
encoding: false,
|
encoding: false,
|
||||||
}),
|
}),
|
||||||
|
gulp.src(["external/jbig2/*.wasm", "external/jbig2/LICENSE_*"], {
|
||||||
|
base: "external/jbig2",
|
||||||
|
encoding: false,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,8 +679,7 @@ function getTempFile(prefix, suffix) {
|
|||||||
|
|
||||||
function runTests(testsName, { bot = false, xfaOnly = false } = {}) {
|
function runTests(testsName, { bot = false, xfaOnly = false } = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log();
|
console.log("\n### Running " + testsName + " tests");
|
||||||
console.log("### Running " + testsName + " tests");
|
|
||||||
|
|
||||||
const PDF_TEST = process.env.PDF_TEST || "test_manifest.json";
|
const PDF_TEST = process.env.PDF_TEST || "test_manifest.json";
|
||||||
let forceNoChrome = false;
|
let forceNoChrome = false;
|
||||||
@ -819,8 +764,7 @@ function collectArgs(options, args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeRef(done, bot) {
|
function makeRef(done, bot) {
|
||||||
console.log();
|
console.log("\n### Creating reference images");
|
||||||
console.log("### Creating reference images");
|
|
||||||
|
|
||||||
let forceNoChrome = false;
|
let forceNoChrome = false;
|
||||||
const args = ["test.mjs", "--masterMode"];
|
const args = ["test.mjs", "--masterMode"];
|
||||||
@ -870,9 +814,30 @@ gulp.task("default", function (done) {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createBuildNumber(done) {
|
gulp.task("release-brotli", async function (done) {
|
||||||
|
const hashIndex = process.argv.indexOf("--hash");
|
||||||
|
if (hashIndex === -1 || hashIndex + 1 >= process.argv.length) {
|
||||||
|
throw new Error('Missing "--hash <commit-hash>" argument.');
|
||||||
|
}
|
||||||
console.log();
|
console.log();
|
||||||
console.log("### Getting extension build number");
|
console.log("### Getting Brotli js file for release");
|
||||||
|
|
||||||
|
const OUTPUT_DIR = "./external/brotli/";
|
||||||
|
const hash = process.argv[hashIndex + 1];
|
||||||
|
const url = `https://raw.githubusercontent.com/google/brotli/${hash}/js/decode.js`;
|
||||||
|
const outputPath = OUTPUT_DIR + "decode.js";
|
||||||
|
const res = await fetch(url);
|
||||||
|
const fileStream = fs.createWriteStream(outputPath, { flags: "w" });
|
||||||
|
await finished(stream.Readable.fromWeb(res.body).pipe(fileStream));
|
||||||
|
fileStream.end();
|
||||||
|
|
||||||
|
console.log(`Brotli js file saved to: ${outputPath}`);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
function createBuildNumber(done) {
|
||||||
|
console.log("\n### Getting extension build number");
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
"git log --format=oneline " + config.baseVersion + "..",
|
"git log --format=oneline " + config.baseVersion + "..",
|
||||||
@ -917,8 +882,7 @@ function createBuildNumber(done) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildDefaultPreferences(defines, dir) {
|
function buildDefaultPreferences(defines, dir) {
|
||||||
console.log();
|
console.log(`\n### Building default preferences (${dir})`);
|
||||||
console.log("### Building default preferences");
|
|
||||||
|
|
||||||
const bundleDefines = {
|
const bundleDefines = {
|
||||||
...defines,
|
...defines,
|
||||||
@ -944,12 +908,14 @@ function buildDefaultPreferences(defines, dir) {
|
|||||||
.pipe(gulp.dest(DEFAULT_PREFERENCES_DIR + dir));
|
.pipe(gulp.dest(DEFAULT_PREFERENCES_DIR + dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseDefaultPreferences(dir) {
|
function getDefaultPreferences(dir) {
|
||||||
console.log();
|
console.log(`\n### Parsing default preferences (${dir})`);
|
||||||
console.log("### Parsing default preferences");
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-unsanitized/method
|
const require = process
|
||||||
const { AppOptions, OptionKind } = await import(
|
.getBuiltinModule("module")
|
||||||
|
.createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const { AppOptions, OptionKind } = require(
|
||||||
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
|
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -958,20 +924,9 @@ async function parseDefaultPreferences(dir) {
|
|||||||
/* defaultOnly = */ true
|
/* defaultOnly = */ true
|
||||||
);
|
);
|
||||||
if (Object.keys(prefs).length === 0) {
|
if (Object.keys(prefs).length === 0) {
|
||||||
throw new Error("No default preferences found.");
|
throw new Error(`No default preferences found in "${dir}".`);
|
||||||
}
|
}
|
||||||
|
return prefs;
|
||||||
fs.writeFileSync(
|
|
||||||
DEFAULT_PREFERENCES_DIR + dir + "default_preferences.json",
|
|
||||||
JSON.stringify(prefs)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultPreferences(dir) {
|
|
||||||
const str = fs
|
|
||||||
.readFileSync(DEFAULT_PREFERENCES_DIR + dir + "default_preferences.json")
|
|
||||||
.toString();
|
|
||||||
return JSON.parse(str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultFtl() {
|
function getDefaultFtl() {
|
||||||
@ -992,8 +947,7 @@ function getDefaultFtl() {
|
|||||||
gulp.task("locale", function () {
|
gulp.task("locale", function () {
|
||||||
const VIEWER_LOCALE_OUTPUT = "web/locale/";
|
const VIEWER_LOCALE_OUTPUT = "web/locale/";
|
||||||
|
|
||||||
console.log();
|
console.log("\n### Building localization files");
|
||||||
console.log("### Building localization files");
|
|
||||||
|
|
||||||
fs.rmSync(VIEWER_LOCALE_OUTPUT, { recursive: true, force: true });
|
fs.rmSync(VIEWER_LOCALE_OUTPUT, { recursive: true, force: true });
|
||||||
fs.mkdirSync(VIEWER_LOCALE_OUTPUT, { recursive: true });
|
fs.mkdirSync(VIEWER_LOCALE_OUTPUT, { recursive: true });
|
||||||
@ -1041,8 +995,7 @@ gulp.task("cmaps", async function () {
|
|||||||
const CMAP_INPUT = "external/cmaps";
|
const CMAP_INPUT = "external/cmaps";
|
||||||
const VIEWER_CMAP_OUTPUT = "external/bcmaps";
|
const VIEWER_CMAP_OUTPUT = "external/bcmaps";
|
||||||
|
|
||||||
console.log();
|
console.log("\n### Building cmaps");
|
||||||
console.log("### Building cmaps");
|
|
||||||
|
|
||||||
// Testing a file that usually present.
|
// Testing a file that usually present.
|
||||||
if (!checkFile(CMAP_INPUT + "/UniJIS-UCS2-H")) {
|
if (!checkFile(CMAP_INPUT + "/UniJIS-UCS2-H")) {
|
||||||
@ -1103,11 +1056,7 @@ function buildGeneric(defines, dir) {
|
|||||||
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
|
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
|
||||||
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
|
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
|
||||||
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
|
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
|
||||||
createWebBundle(defines, {
|
createWebBundle(defines).pipe(gulp.dest(dir + "web")),
|
||||||
defaultPreferencesDir: defines.SKIP_BABEL
|
|
||||||
? "generic/"
|
|
||||||
: "generic-legacy/",
|
|
||||||
}).pipe(gulp.dest(dir + "web")),
|
|
||||||
gulp
|
gulp
|
||||||
.src(COMMON_WEB_FILES, { base: "web/", encoding: false })
|
.src(COMMON_WEB_FILES, { base: "web/", encoding: false })
|
||||||
.pipe(gulp.dest(dir + "web")),
|
.pipe(gulp.dest(dir + "web")),
|
||||||
@ -1151,17 +1100,10 @@ gulp.task(
|
|||||||
"locale",
|
"locale",
|
||||||
function scriptingGeneric() {
|
function scriptingGeneric() {
|
||||||
const defines = { ...DEFINES, GENERIC: true };
|
const defines = { ...DEFINES, GENERIC: true };
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "generic/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsGeneric() {
|
|
||||||
await parseDefaultPreferences("generic/");
|
|
||||||
},
|
},
|
||||||
function createGeneric() {
|
function createGeneric() {
|
||||||
console.log();
|
console.log("\n### Creating generic viewer");
|
||||||
console.log("### Creating generic viewer");
|
|
||||||
const defines = { ...DEFINES, GENERIC: true };
|
const defines = { ...DEFINES, GENERIC: true };
|
||||||
|
|
||||||
return buildGeneric(defines, GENERIC_DIR);
|
return buildGeneric(defines, GENERIC_DIR);
|
||||||
@ -1178,17 +1120,10 @@ gulp.task(
|
|||||||
"locale",
|
"locale",
|
||||||
function scriptingGenericLegacy() {
|
function scriptingGenericLegacy() {
|
||||||
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "generic-legacy/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsGenericLegacy() {
|
|
||||||
await parseDefaultPreferences("generic-legacy/");
|
|
||||||
},
|
},
|
||||||
function createGenericLegacy() {
|
function createGenericLegacy() {
|
||||||
console.log();
|
console.log("\n### Creating generic (legacy) viewer");
|
||||||
console.log("### Creating generic (legacy) viewer");
|
|
||||||
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
||||||
|
|
||||||
return buildGeneric(defines, GENERIC_LEGACY_DIR);
|
return buildGeneric(defines, GENERIC_LEGACY_DIR);
|
||||||
@ -1199,16 +1134,7 @@ gulp.task(
|
|||||||
function buildComponents(defines, dir) {
|
function buildComponents(defines, dir) {
|
||||||
fs.rmSync(dir, { recursive: true, force: true });
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
|
||||||
const COMPONENTS_IMAGES = [
|
const COMPONENTS_IMAGES = ["web/images/*.svg", "web/images/*.gif"];
|
||||||
"web/images/annotation-*.svg",
|
|
||||||
"web/images/loading-icon.gif",
|
|
||||||
"web/images/altText_*.svg",
|
|
||||||
"web/images/editor-toolbar-*.svg",
|
|
||||||
"web/images/messageBar_*.svg",
|
|
||||||
"web/images/toolbarButton-{editorHighlight,menuArrow}.svg",
|
|
||||||
"web/images/cursor-*.svg",
|
|
||||||
"web/images/comment-*.svg",
|
|
||||||
];
|
|
||||||
|
|
||||||
return ordered([
|
return ordered([
|
||||||
createComponentsBundle(defines).pipe(gulp.dest(dir)),
|
createComponentsBundle(defines).pipe(gulp.dest(dir)),
|
||||||
@ -1232,8 +1158,7 @@ function buildComponents(defines, dir) {
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"components",
|
"components",
|
||||||
gulp.series(createBuildNumber, function createComponents() {
|
gulp.series(createBuildNumber, function createComponents() {
|
||||||
console.log();
|
console.log("\n### Creating generic components");
|
||||||
console.log("### Creating generic components");
|
|
||||||
const defines = { ...DEFINES, COMPONENTS: true, GENERIC: true };
|
const defines = { ...DEFINES, COMPONENTS: true, GENERIC: true };
|
||||||
|
|
||||||
return buildComponents(defines, COMPONENTS_DIR);
|
return buildComponents(defines, COMPONENTS_DIR);
|
||||||
@ -1243,8 +1168,7 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"components-legacy",
|
"components-legacy",
|
||||||
gulp.series(createBuildNumber, function createComponentsLegacy() {
|
gulp.series(createBuildNumber, function createComponentsLegacy() {
|
||||||
console.log();
|
console.log("\n### Creating generic (legacy) components");
|
||||||
console.log("### Creating generic (legacy) components");
|
|
||||||
const defines = {
|
const defines = {
|
||||||
...DEFINES,
|
...DEFINES,
|
||||||
COMPONENTS: true,
|
COMPONENTS: true,
|
||||||
@ -1259,8 +1183,7 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"image_decoders",
|
"image_decoders",
|
||||||
gulp.series(createBuildNumber, function createImageDecoders() {
|
gulp.series(createBuildNumber, function createImageDecoders() {
|
||||||
console.log();
|
console.log("\n### Creating image decoders");
|
||||||
console.log("### Creating image decoders");
|
|
||||||
const defines = { ...DEFINES, GENERIC: true, IMAGE_DECODERS: true };
|
const defines = { ...DEFINES, GENERIC: true, IMAGE_DECODERS: true };
|
||||||
|
|
||||||
return createImageDecodersBundle(defines).pipe(
|
return createImageDecodersBundle(defines).pipe(
|
||||||
@ -1272,8 +1195,7 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"image_decoders-legacy",
|
"image_decoders-legacy",
|
||||||
gulp.series(createBuildNumber, function createImageDecodersLegacy() {
|
gulp.series(createBuildNumber, function createImageDecodersLegacy() {
|
||||||
console.log();
|
console.log("\n### Creating (legacy) image decoders");
|
||||||
console.log("### Creating (legacy) image decoders");
|
|
||||||
const defines = {
|
const defines = {
|
||||||
...DEFINES,
|
...DEFINES,
|
||||||
GENERIC: true,
|
GENERIC: true,
|
||||||
@ -1307,17 +1229,10 @@ gulp.task(
|
|||||||
"locale",
|
"locale",
|
||||||
function scriptingMinified() {
|
function scriptingMinified() {
|
||||||
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "minified/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsMinified() {
|
|
||||||
await parseDefaultPreferences("minified/");
|
|
||||||
},
|
},
|
||||||
function createMinified() {
|
function createMinified() {
|
||||||
console.log();
|
console.log("\n### Creating minified viewer");
|
||||||
console.log("### Creating minified viewer");
|
|
||||||
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
||||||
|
|
||||||
return buildMinified(defines, MINIFIED_DIR);
|
return buildMinified(defines, MINIFIED_DIR);
|
||||||
@ -1337,17 +1252,10 @@ gulp.task(
|
|||||||
GENERIC: true,
|
GENERIC: true,
|
||||||
SKIP_BABEL: false,
|
SKIP_BABEL: false,
|
||||||
};
|
};
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "minified-legacy/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsMinifiedLegacy() {
|
|
||||||
await parseDefaultPreferences("minified-legacy/");
|
|
||||||
},
|
},
|
||||||
function createMinifiedLegacy() {
|
function createMinifiedLegacy() {
|
||||||
console.log();
|
console.log("\n### Creating minified (legacy) viewer");
|
||||||
console.log("### Creating minified (legacy) viewer");
|
|
||||||
const defines = {
|
const defines = {
|
||||||
...DEFINES,
|
...DEFINES,
|
||||||
MINIFIED: true,
|
MINIFIED: true,
|
||||||
@ -1361,6 +1269,8 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
function createDefaultPrefsFile() {
|
function createDefaultPrefsFile() {
|
||||||
|
console.log("\n### Building mozilla-central preferences file");
|
||||||
|
|
||||||
const defaultFileName = "PdfJsDefaultPrefs.js",
|
const defaultFileName = "PdfJsDefaultPrefs.js",
|
||||||
overrideFileName = "PdfJsOverridePrefs.js";
|
overrideFileName = "PdfJsOverridePrefs.js";
|
||||||
const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
|
const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
|
||||||
@ -1399,12 +1309,8 @@ gulp.task(
|
|||||||
const defines = { ...DEFINES, MOZCENTRAL: true };
|
const defines = { ...DEFINES, MOZCENTRAL: true };
|
||||||
return buildDefaultPreferences(defines, "mozcentral/");
|
return buildDefaultPreferences(defines, "mozcentral/");
|
||||||
},
|
},
|
||||||
async function prefsMozcentral() {
|
|
||||||
await parseDefaultPreferences("mozcentral/");
|
|
||||||
},
|
|
||||||
function createMozcentral() {
|
function createMozcentral() {
|
||||||
console.log();
|
console.log("\n### Building mozilla-central extension");
|
||||||
console.log("### Building mozilla-central extension");
|
|
||||||
const defines = { ...DEFINES, MOZCENTRAL: true };
|
const defines = { ...DEFINES, MOZCENTRAL: true };
|
||||||
const gvDefines = { ...defines, GECKOVIEW: true };
|
const gvDefines = { ...defines, GECKOVIEW: true };
|
||||||
|
|
||||||
@ -1438,12 +1344,12 @@ gulp.task(
|
|||||||
createWorkerBundle(defines).pipe(
|
createWorkerBundle(defines).pipe(
|
||||||
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
||||||
),
|
),
|
||||||
createWebBundle(defines, { defaultPreferencesDir: "mozcentral/" }).pipe(
|
createWebBundle(defines).pipe(
|
||||||
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
||||||
|
),
|
||||||
|
createGVWebBundle(gvDefines).pipe(
|
||||||
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
||||||
),
|
),
|
||||||
createGVWebBundle(gvDefines, {
|
|
||||||
defaultPreferencesDir: "mozcentral/",
|
|
||||||
}).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
|
||||||
gulp
|
gulp
|
||||||
.src(MOZCENTRAL_WEB_FILES, { base: "web/", encoding: false })
|
.src(MOZCENTRAL_WEB_FILES, { base: "web/", encoding: false })
|
||||||
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
||||||
@ -1495,6 +1401,18 @@ gulp.task(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function createChromiumPrefsSchema() {
|
||||||
|
console.log("\n### Building Chromium preferences file");
|
||||||
|
|
||||||
|
const prefs = getDefaultPreferences("chromium/");
|
||||||
|
const chromiumPrefs = buildPrefsSchema(prefs);
|
||||||
|
|
||||||
|
return createStringSource(
|
||||||
|
"preferences_schema.json",
|
||||||
|
JSON.stringify(chromiumPrefs, null, 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"chromium",
|
"chromium",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@ -1507,12 +1425,8 @@ gulp.task(
|
|||||||
createTemporaryScriptingBundle(defines),
|
createTemporaryScriptingBundle(defines),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
async function prefsChromium() {
|
|
||||||
await parseDefaultPreferences("chromium/");
|
|
||||||
},
|
|
||||||
function createChromium() {
|
function createChromium() {
|
||||||
console.log();
|
console.log("\n### Building Chromium extension");
|
||||||
console.log("### Building Chromium extension");
|
|
||||||
const defines = { ...DEFINES, CHROME: true, SKIP_BABEL: false };
|
const defines = { ...DEFINES, CHROME: true, SKIP_BABEL: false };
|
||||||
|
|
||||||
const CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
|
const CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
|
||||||
@ -1538,7 +1452,7 @@ gulp.task(
|
|||||||
createSandboxBundle(defines).pipe(
|
createSandboxBundle(defines).pipe(
|
||||||
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
|
||||||
),
|
),
|
||||||
createWebBundle(defines, { defaultPreferencesDir: "chromium/" }).pipe(
|
createWebBundle(defines).pipe(
|
||||||
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
||||||
),
|
),
|
||||||
gulp
|
gulp
|
||||||
@ -1587,22 +1501,19 @@ gulp.task(
|
|||||||
.pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
|
.pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
|
||||||
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src(["extensions/chromium/**/*.{html,js,css,png}"], {
|
||||||
[
|
base: "extensions/chromium/",
|
||||||
"extensions/chromium/**/*.{html,js,css,png}",
|
encoding: false,
|
||||||
"extensions/chromium/preferences_schema.json",
|
})
|
||||||
],
|
|
||||||
{ base: "extensions/chromium/", encoding: false }
|
|
||||||
)
|
|
||||||
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
||||||
|
createChromiumPrefsSchema().pipe(gulp.dest(CHROME_BUILD_DIR)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("jsdoc", function (done) {
|
gulp.task("jsdoc", function (done) {
|
||||||
console.log();
|
console.log("\n### Generating documentation (JSDoc)");
|
||||||
console.log("### Generating documentation (JSDoc)");
|
|
||||||
|
|
||||||
fs.rmSync(JSDOC_BUILD_DIR, { recursive: true, force: true });
|
fs.rmSync(JSDOC_BUILD_DIR, { recursive: true, force: true });
|
||||||
fs.mkdirSync(JSDOC_BUILD_DIR, { recursive: true });
|
fs.mkdirSync(JSDOC_BUILD_DIR, { recursive: true });
|
||||||
@ -1675,9 +1586,6 @@ function buildLib(defines, dir) {
|
|||||||
BUNDLE_VERSION: versionInfo.version,
|
BUNDLE_VERSION: versionInfo.version,
|
||||||
BUNDLE_BUILD: versionInfo.commit,
|
BUNDLE_BUILD: versionInfo.commit,
|
||||||
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
||||||
DEFAULT_PREFERENCES: getDefaultPreferences(
|
|
||||||
defines.SKIP_BABEL ? "lib/" : "lib-legacy/"
|
|
||||||
),
|
|
||||||
DEFAULT_FTL: getDefaultFtl(),
|
DEFAULT_FTL: getDefaultFtl(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1696,6 +1604,8 @@ 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 }),
|
gulp.src("external/qcms/*.js", { base: "qcms/", encoding: false }),
|
||||||
|
gulp.src("external/jbig2/*.js", { base: "jbig2/", encoding: false }),
|
||||||
|
gulp.src("external/brotli/*.js", { base: "brotli/", encoding: false }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return buildLibHelper(bundleDefines, inputStream, dir);
|
return buildLibHelper(bundleDefines, inputStream, dir);
|
||||||
@ -1707,13 +1617,7 @@ gulp.task(
|
|||||||
createBuildNumber,
|
createBuildNumber,
|
||||||
function scriptingLib() {
|
function scriptingLib() {
|
||||||
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "lib/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsLib() {
|
|
||||||
await parseDefaultPreferences("lib/");
|
|
||||||
},
|
},
|
||||||
function createLib() {
|
function createLib() {
|
||||||
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
||||||
@ -1737,13 +1641,7 @@ gulp.task(
|
|||||||
LIB: true,
|
LIB: true,
|
||||||
SKIP_BABEL: false,
|
SKIP_BABEL: false,
|
||||||
};
|
};
|
||||||
return ordered([
|
return createTemporaryScriptingBundle(defines);
|
||||||
buildDefaultPreferences(defines, "lib-legacy/"),
|
|
||||||
createTemporaryScriptingBundle(defines),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async function prefsLibLegacy() {
|
|
||||||
await parseDefaultPreferences("lib-legacy/");
|
|
||||||
},
|
},
|
||||||
function createLibLegacy() {
|
function createLibLegacy() {
|
||||||
const defines = {
|
const defines = {
|
||||||
@ -1926,6 +1824,49 @@ gulp.task(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
"prefstest",
|
||||||
|
gulp.series(
|
||||||
|
setTestEnv,
|
||||||
|
function genericPrefs() {
|
||||||
|
const defines = { ...DEFINES, GENERIC: true };
|
||||||
|
return buildDefaultPreferences(defines, "generic/");
|
||||||
|
},
|
||||||
|
function genericLegacyPrefs() {
|
||||||
|
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
||||||
|
return buildDefaultPreferences(defines, "generic-legacy/");
|
||||||
|
},
|
||||||
|
function chromiumPrefs() {
|
||||||
|
const defines = { ...DEFINES, CHROME: true, SKIP_BABEL: false };
|
||||||
|
return buildDefaultPreferences(defines, "chromium/");
|
||||||
|
},
|
||||||
|
function mozcentralPrefs() {
|
||||||
|
const defines = { ...DEFINES, MOZCENTRAL: true };
|
||||||
|
return buildDefaultPreferences(defines, "mozcentral/");
|
||||||
|
},
|
||||||
|
function checkPrefs() {
|
||||||
|
console.log("\n### Checking preference generation");
|
||||||
|
|
||||||
|
// Check that the preferences were correctly generated,
|
||||||
|
// for all the relevant builds.
|
||||||
|
for (const dir of [
|
||||||
|
"generic/",
|
||||||
|
"generic-legacy/",
|
||||||
|
"chromium/",
|
||||||
|
"mozcentral/",
|
||||||
|
]) {
|
||||||
|
getDefaultPreferences(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all the relevant files can be generated.
|
||||||
|
return ordered([
|
||||||
|
createChromiumPrefsSchema().pipe(gulp.dest(PREFSTEST_DIR)),
|
||||||
|
createDefaultPrefsFile().pipe(gulp.dest(PREFSTEST_DIR)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"typestest",
|
"typestest",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@ -1959,8 +1900,7 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
function createBaseline(done) {
|
function createBaseline(done) {
|
||||||
console.log();
|
console.log("\n### Creating baseline environment");
|
||||||
console.log("### Creating baseline environment");
|
|
||||||
|
|
||||||
const baselineCommit = process.env.BASELINE;
|
const baselineCommit = process.env.BASELINE;
|
||||||
if (!baselineCommit) {
|
if (!baselineCommit) {
|
||||||
@ -2021,8 +1961,7 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("lint", function (done) {
|
gulp.task("lint", function (done) {
|
||||||
console.log();
|
console.log("\n### Linting JS/CSS/JSON/SVG/HTML files");
|
||||||
console.log("### Linting JS/CSS/JSON/SVG/HTML 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 = [
|
||||||
@ -2102,8 +2041,7 @@ gulp.task("lint", function (done) {
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"lint-mozcentral",
|
"lint-mozcentral",
|
||||||
gulp.series("mozcentral", function runLintMozcentral(done) {
|
gulp.series("mozcentral", function runLintMozcentral(done) {
|
||||||
console.log();
|
console.log("\n### Checking mozilla-central files");
|
||||||
console.log("### Checking mozilla-central files");
|
|
||||||
|
|
||||||
const styleLintOptions = [
|
const styleLintOptions = [
|
||||||
"../../node_modules/stylelint/bin/stylelint.mjs",
|
"../../node_modules/stylelint/bin/stylelint.mjs",
|
||||||
@ -2128,39 +2066,6 @@ gulp.task(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
|
||||||
"lint-chromium",
|
|
||||||
gulp.series(
|
|
||||||
function scriptingLintChromium() {
|
|
||||||
const defines = {
|
|
||||||
...DEFINES,
|
|
||||||
CHROME: true,
|
|
||||||
SKIP_BABEL: false,
|
|
||||||
TESTING: false,
|
|
||||||
};
|
|
||||||
return buildDefaultPreferences(defines, "lint-chromium/");
|
|
||||||
},
|
|
||||||
async function prefsLintChromium() {
|
|
||||||
await parseDefaultPreferences("lint-chromium/");
|
|
||||||
},
|
|
||||||
function runLintChromium(done) {
|
|
||||||
console.log();
|
|
||||||
console.log("### Checking supplemental Chromium files");
|
|
||||||
|
|
||||||
if (
|
|
||||||
!checkChromePreferencesFile(
|
|
||||||
"extensions/chromium/preferences_schema.json",
|
|
||||||
getDefaultPreferences("lint-chromium/")
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
done(new Error("chromium/preferences_schema is not in sync."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
gulp.task("dev-wasm", function () {
|
gulp.task("dev-wasm", function () {
|
||||||
const VIEWER_WASM_OUTPUT = "web/wasm/";
|
const VIEWER_WASM_OUTPUT = "web/wasm/";
|
||||||
|
|
||||||
@ -2180,8 +2085,7 @@ gulp.task(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
function createDevSandbox() {
|
function createDevSandbox() {
|
||||||
console.log();
|
console.log("\n### Building development sandbox");
|
||||||
console.log("### Building development sandbox");
|
|
||||||
|
|
||||||
const defines = { ...DEFINES, GENERIC: true, TESTING: true };
|
const defines = { ...DEFINES, GENERIC: true, TESTING: true };
|
||||||
const sandboxDir = BUILD_DIR + "dev-sandbox/";
|
const sandboxDir = BUILD_DIR + "dev-sandbox/";
|
||||||
@ -2207,7 +2111,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
function watchWasm() {
|
function watchWasm() {
|
||||||
gulp.watch(
|
gulp.watch(
|
||||||
["external/openjpeg/*", "external/qcms/*"],
|
["external/openjpeg/*", "external/qcms/*", "external/jbig2/*"],
|
||||||
{ ignoreInitial: false },
|
{ ignoreInitial: false },
|
||||||
gulp.series("dev-wasm")
|
gulp.series("dev-wasm")
|
||||||
);
|
);
|
||||||
@ -2225,8 +2129,7 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
async function createServer() {
|
async function createServer() {
|
||||||
console.log();
|
console.log("\n### Starting local server");
|
||||||
console.log("### Starting local server");
|
|
||||||
|
|
||||||
let port = 8888;
|
let port = 8888;
|
||||||
const i = process.argv.indexOf("--port");
|
const i = process.argv.indexOf("--port");
|
||||||
@ -2247,8 +2150,7 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("clean", function (done) {
|
gulp.task("clean", function (done) {
|
||||||
console.log();
|
console.log("\n### Cleaning up project builds");
|
||||||
console.log("### Cleaning up project builds");
|
|
||||||
|
|
||||||
fs.rmSync(BUILD_DIR, { recursive: true, force: true });
|
fs.rmSync(BUILD_DIR, { recursive: true, force: true });
|
||||||
done();
|
done();
|
||||||
@ -2257,8 +2159,7 @@ gulp.task("clean", function (done) {
|
|||||||
gulp.task("importl10n", async function () {
|
gulp.task("importl10n", async function () {
|
||||||
const { downloadL10n } = await import("./external/importL10n/locales.mjs");
|
const { downloadL10n } = await import("./external/importL10n/locales.mjs");
|
||||||
|
|
||||||
console.log();
|
console.log("\n### Importing translations from mozilla-central");
|
||||||
console.log("### Importing translations from mozilla-central");
|
|
||||||
|
|
||||||
if (!fs.existsSync(L10N_DIR)) {
|
if (!fs.existsSync(L10N_DIR)) {
|
||||||
fs.mkdirSync(L10N_DIR);
|
fs.mkdirSync(L10N_DIR);
|
||||||
@ -2267,8 +2168,7 @@ gulp.task("importl10n", async function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function ghPagesPrepare() {
|
function ghPagesPrepare() {
|
||||||
console.log();
|
console.log("\n### Creating web site");
|
||||||
console.log("### Creating web site");
|
|
||||||
|
|
||||||
fs.rmSync(GH_PAGES_DIR, { recursive: true, force: true });
|
fs.rmSync(GH_PAGES_DIR, { recursive: true, force: true });
|
||||||
|
|
||||||
@ -2369,7 +2269,8 @@ function packageJson() {
|
|||||||
bugs: DIST_BUGS_URL,
|
bugs: DIST_BUGS_URL,
|
||||||
license: DIST_LICENSE,
|
license: DIST_LICENSE,
|
||||||
optionalDependencies: {
|
optionalDependencies: {
|
||||||
"@napi-rs/canvas": "^0.1.84",
|
"@napi-rs/canvas": "^0.1.88",
|
||||||
|
"node-readable-to-web-readable-stream": "^0.4.2",
|
||||||
},
|
},
|
||||||
browser: {
|
browser: {
|
||||||
canvas: false,
|
canvas: false,
|
||||||
@ -2536,8 +2437,7 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"mozcentralbaseline",
|
"mozcentralbaseline",
|
||||||
gulp.series(createBaseline, function createMozcentralBaseline(done) {
|
gulp.series(createBaseline, function createMozcentralBaseline(done) {
|
||||||
console.log();
|
console.log("\n### Creating mozcentral baseline environment");
|
||||||
console.log("### Creating mozcentral baseline environment");
|
|
||||||
|
|
||||||
// Create a mozcentral build.
|
// Create a mozcentral build.
|
||||||
fs.rmSync(BASELINE_DIR + BUILD_DIR, { recursive: true, force: true });
|
fs.rmSync(BASELINE_DIR + BUILD_DIR, { recursive: true, force: true });
|
||||||
@ -2574,8 +2474,7 @@ gulp.task(
|
|||||||
"mozcentral",
|
"mozcentral",
|
||||||
"mozcentralbaseline",
|
"mozcentralbaseline",
|
||||||
function createMozcentralDiff(done) {
|
function createMozcentralDiff(done) {
|
||||||
console.log();
|
console.log("\n### Creating mozcentral diff");
|
||||||
console.log("### Creating mozcentral diff");
|
|
||||||
|
|
||||||
// Create the diff between the current mozcentral build and the
|
// Create the diff between the current mozcentral build and the
|
||||||
// baseline mozcentral build, which both exist at this point.
|
// baseline mozcentral build, which both exist at this point.
|
||||||
@ -2617,14 +2516,12 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("externaltest", function (done) {
|
gulp.task("externaltest", function (done) {
|
||||||
console.log();
|
console.log("\n### Running test-fixtures.js");
|
||||||
console.log("### Running test-fixtures.js");
|
|
||||||
safeSpawnSync("node", ["external/builder/test-fixtures.mjs"], {
|
safeSpawnSync("node", ["external/builder/test-fixtures.mjs"], {
|
||||||
stdio: "inherit",
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log();
|
console.log("\n### Running test-fixtures_babel.js");
|
||||||
console.log("### Running test-fixtures_babel.js");
|
|
||||||
safeSpawnSync("node", ["external/builder/test-fixtures_babel.mjs"], {
|
safeSpawnSync("node", ["external/builder/test-fixtures_babel.mjs"], {
|
||||||
stdio: "inherit",
|
stdio: "inherit",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -167,10 +167,10 @@ pdfjs-printing-not-ready = Warnowanje: PDF njejo se za śišćanje dopołnje zac
|
|||||||
## Tooltips and alt text for side panel toolbar buttons
|
## Tooltips and alt text for side panel toolbar buttons
|
||||||
|
|
||||||
pdfjs-toggle-sidebar-button =
|
pdfjs-toggle-sidebar-button =
|
||||||
.title = Bócnicu pokazaś/schowaś
|
.title = Bocnicu pokazaś/schowaś
|
||||||
pdfjs-toggle-sidebar-notification-button =
|
pdfjs-toggle-sidebar-notification-button =
|
||||||
.title = Bocnicu pśešaltowaś (dokument rozrědowanje/pśipiski/warstwy wopśimujo)
|
.title = Bocnicu pśešaltowaś (dokument rozrědowanje/pśipiski/warstwy wopśimujo)
|
||||||
pdfjs-toggle-sidebar-button-label = Bócnicu pokazaś/schowaś
|
pdfjs-toggle-sidebar-button-label = Bocnicu pokazaś/schowaś
|
||||||
pdfjs-document-outline-button =
|
pdfjs-document-outline-button =
|
||||||
.title = Dokumentowe naraźenje pokazaś (dwójne kliknjenje, aby se wšykne zapiski pokazali/schowali)
|
.title = Dokumentowe naraźenje pokazaś (dwójne kliknjenje, aby se wšykne zapiski pokazali/schowali)
|
||||||
pdfjs-document-outline-button-label = Dokumentowa struktura
|
pdfjs-document-outline-button-label = Dokumentowa struktura
|
||||||
@ -389,9 +389,9 @@ pdfjs-editor-comments-sidebar-title =
|
|||||||
*[other] { $count } komentarow
|
*[other] { $count } komentarow
|
||||||
}
|
}
|
||||||
pdfjs-editor-comments-sidebar-close-button =
|
pdfjs-editor-comments-sidebar-close-button =
|
||||||
.title = Bócnicu zacyniś
|
.title = Bocnicu zacyniś
|
||||||
.aria-label = Bócnicu zacyniś
|
.aria-label = Bocnicu zacyniś
|
||||||
pdfjs-editor-comments-sidebar-close-button-label = Bócnicu zacyniś
|
pdfjs-editor-comments-sidebar-close-button-label = Bocnicu zacyniś
|
||||||
# Instructional copy to add a comment by selecting text or an annotations.
|
# Instructional copy to add a comment by selecting text or an annotations.
|
||||||
pdfjs-editor-comments-sidebar-no-comments1 = Wiźiśo něco wobspomnjeśa gódnego? Wuzwigniśo to a zawóstajśo komentar.
|
pdfjs-editor-comments-sidebar-no-comments1 = Wiźiśo něco wobspomnjeśa gódnego? Wuzwigniśo to a zawóstajśo komentar.
|
||||||
pdfjs-editor-comments-sidebar-no-comments-link = Dalšne informacije
|
pdfjs-editor-comments-sidebar-no-comments-link = Dalšne informacije
|
||||||
|
|||||||
@ -532,15 +532,6 @@ pdfjs-editor-alt-text-settings-automatic-title = Automatic alt text
|
|||||||
pdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically
|
pdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically
|
||||||
pdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can’t see the image or when the image doesn’t load.
|
pdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can’t see the image or when the image doesn’t load.
|
||||||
|
|
||||||
# Variables:
|
|
||||||
# $totalSize (Number) - the total size (in MB) of the AI model.
|
|
||||||
pdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB)
|
|
||||||
|
|
||||||
pdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text.
|
|
||||||
pdfjs-editor-alt-text-settings-delete-model-button = Delete
|
|
||||||
pdfjs-editor-alt-text-settings-download-model-button = Download
|
|
||||||
pdfjs-editor-alt-text-settings-downloading-model-button = Downloading…
|
|
||||||
|
|
||||||
pdfjs-editor-alt-text-settings-editor-title = Alt text editor
|
pdfjs-editor-alt-text-settings-editor-title = Alt text editor
|
||||||
pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image
|
pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image
|
||||||
pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.
|
pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.
|
||||||
@ -561,6 +552,7 @@ pdfjs-editor-undo-bar-message-freetext = Text removed
|
|||||||
pdfjs-editor-undo-bar-message-ink = Drawing removed
|
pdfjs-editor-undo-bar-message-ink = Drawing removed
|
||||||
pdfjs-editor-undo-bar-message-stamp = Image removed
|
pdfjs-editor-undo-bar-message-stamp = Image removed
|
||||||
pdfjs-editor-undo-bar-message-signature = Signature removed
|
pdfjs-editor-undo-bar-message-signature = Signature removed
|
||||||
|
pdfjs-editor-undo-bar-message-comment = Comment removed
|
||||||
# Variables:
|
# Variables:
|
||||||
# $count (Number) - the number of removed annotations.
|
# $count (Number) - the number of removed annotations.
|
||||||
pdfjs-editor-undo-bar-message-multiple =
|
pdfjs-editor-undo-bar-message-multiple =
|
||||||
|
|||||||
@ -628,6 +628,11 @@ pdfjs-editor-edit-comment-dialog-text-input =
|
|||||||
.placeholder = Scomence a scrivi…
|
.placeholder = Scomence a scrivi…
|
||||||
pdfjs-editor-edit-comment-dialog-cancel-button = Anule
|
pdfjs-editor-edit-comment-dialog-cancel-button = Anule
|
||||||
|
|
||||||
|
## Edit a comment button in the editor toolbar
|
||||||
|
|
||||||
|
pdfjs-editor-add-comment-button =
|
||||||
|
.title = Zonte coment
|
||||||
|
|
||||||
## Main menu for adding/removing signatures
|
## Main menu for adding/removing signatures
|
||||||
|
|
||||||
pdfjs-editor-delete-signature-button1 =
|
pdfjs-editor-delete-signature-button1 =
|
||||||
|
|||||||
@ -318,6 +318,12 @@ pdfjs-editor-signature-button-label = Қолтаңбаны қосу
|
|||||||
|
|
||||||
## Default editor aria labels
|
## 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.
|
# Used when a signature editor is selected/hovered.
|
||||||
# Variables:
|
# Variables:
|
||||||
# $description (String) - a string describing/labeling the signature.
|
# $description (String) - a string describing/labeling the signature.
|
||||||
@ -380,6 +386,8 @@ pdfjs-editor-comments-sidebar-close-button =
|
|||||||
.title = Бүйір панелін жабу
|
.title = Бүйір панелін жабу
|
||||||
.aria-label = Бүйір панелін жабу
|
.aria-label = Бүйір панелін жабу
|
||||||
pdfjs-editor-comments-sidebar-close-button-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 = Көбірек білу
|
pdfjs-editor-comments-sidebar-no-comments-link = Көбірек білу
|
||||||
|
|
||||||
## Alt-text dialog
|
## Alt-text dialog
|
||||||
@ -513,6 +521,7 @@ pdfjs-editor-alt-text-settings-close-button = Жабу
|
|||||||
|
|
||||||
## Accessibility labels (announced by screen readers) for objects added to the editor.
|
## Accessibility labels (announced by screen readers) for objects added to the editor.
|
||||||
|
|
||||||
|
pdfjs-editor-highlight-added-alert = Ерекшелеу қосылды
|
||||||
pdfjs-editor-freetext-added-alert = Мәтін қосылды
|
pdfjs-editor-freetext-added-alert = Мәтін қосылды
|
||||||
pdfjs-editor-ink-added-alert = Сызба қосылды
|
pdfjs-editor-ink-added-alert = Сызба қосылды
|
||||||
pdfjs-editor-stamp-added-alert = Сурет қосылды
|
pdfjs-editor-stamp-added-alert = Сурет қосылды
|
||||||
@ -586,6 +595,8 @@ pdfjs-editor-add-signature-save-checkbox = Қолтаңбаны сақтау
|
|||||||
pdfjs-editor-add-signature-save-warning-message = Сақталған 5 қолтаңбаның шегіне жеттіңіз. Көбірек сақтау үшін біреуін алып тастаңыз.
|
pdfjs-editor-add-signature-save-warning-message = Сақталған 5 қолтаңбаның шегіне жеттіңіз. Көбірек сақтау үшін біреуін алып тастаңыз.
|
||||||
pdfjs-editor-add-signature-image-upload-error-title = Суретті жүктеп жіберу мүмкін емес.
|
pdfjs-editor-add-signature-image-upload-error-title = Суретті жүктеп жіберу мүмкін емес.
|
||||||
pdfjs-editor-add-signature-image-upload-error-description = Желі байланысын тексеріңіз немесе басқа бейнені қолданып көріңіз.
|
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 = Жабу
|
pdfjs-editor-add-signature-error-close-button = Жабу
|
||||||
|
|
||||||
## Dialog buttons
|
## Dialog buttons
|
||||||
@ -594,10 +605,27 @@ pdfjs-editor-add-signature-cancel-button = Бас тарту
|
|||||||
pdfjs-editor-add-signature-add-button = Қосу
|
pdfjs-editor-add-signature-add-button = Қосу
|
||||||
pdfjs-editor-edit-signature-update-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
|
## 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 = Жаңарту
|
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-save-button-when-adding = Қосу
|
||||||
|
pdfjs-editor-edit-comment-dialog-text-input =
|
||||||
|
.placeholder = Теріп бастаңыз…
|
||||||
pdfjs-editor-edit-comment-dialog-cancel-button = Бас тарту
|
pdfjs-editor-edit-comment-dialog-cancel-button = Бас тарту
|
||||||
|
|
||||||
## Edit a comment button in the editor toolbar
|
## Edit a comment button in the editor toolbar
|
||||||
|
|||||||
@ -309,6 +309,10 @@ pdfjs-editor-signature-add-signature-button-label = പുതിയ ഒപ്പ
|
|||||||
# $description (String) - a string describing/labeling the signature.
|
# $description (String) - a string describing/labeling the signature.
|
||||||
pdfjs-editor-add-saved-signature-button =
|
pdfjs-editor-add-saved-signature-button =
|
||||||
.title = കരുതിവച്ച ഒപ്പു് : { $description }
|
.title = കരുതിവച്ച ഒപ്പു് : { $description }
|
||||||
|
pdfjs-editor-comments-sidebar-close-button =
|
||||||
|
.title = അണിവക്കം അടയ്ക്കുക
|
||||||
|
.aria-label = അണിവക്കം അടയ്ക്കുക
|
||||||
|
pdfjs-editor-comments-sidebar-close-button-label = അണിവക്കം അടയ്ക്കുക
|
||||||
|
|
||||||
## Alt-text dialog
|
## Alt-text dialog
|
||||||
|
|
||||||
|
|||||||
@ -70,10 +70,10 @@ pdfjs-page-rotate-ccw-button =
|
|||||||
.title = Повернуть против часовой стрелки
|
.title = Повернуть против часовой стрелки
|
||||||
pdfjs-page-rotate-ccw-button-label = Повернуть против часовой стрелки
|
pdfjs-page-rotate-ccw-button-label = Повернуть против часовой стрелки
|
||||||
pdfjs-cursor-text-select-tool-button =
|
pdfjs-cursor-text-select-tool-button =
|
||||||
.title = Включить Инструмент «Выделение текста»
|
.title = Включить инструмент «Выделение текста»
|
||||||
pdfjs-cursor-text-select-tool-button-label = Инструмент «Выделение текста»
|
pdfjs-cursor-text-select-tool-button-label = Инструмент «Выделение текста»
|
||||||
pdfjs-cursor-hand-tool-button =
|
pdfjs-cursor-hand-tool-button =
|
||||||
.title = Включить Инструмент «Рука»
|
.title = Включить инструмент «Рука»
|
||||||
pdfjs-cursor-hand-tool-button-label = Инструмент «Рука»
|
pdfjs-cursor-hand-tool-button-label = Инструмент «Рука»
|
||||||
pdfjs-scroll-page-button =
|
pdfjs-scroll-page-button =
|
||||||
.title = Использовать прокрутку страниц
|
.title = Использовать прокрутку страниц
|
||||||
@ -363,7 +363,7 @@ 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-editor-add-signature-container =
|
||||||
.aria-label = Управление подписями и сохраненные подписи
|
.aria-label = Управление подписями и сохранённые подписи
|
||||||
pdfjs-editor-signature-add-signature-button =
|
pdfjs-editor-signature-add-signature-button =
|
||||||
.title = Добавить новую подпись
|
.title = Добавить новую подпись
|
||||||
pdfjs-editor-signature-add-signature-button-label = Добавить новую подпись
|
pdfjs-editor-signature-add-signature-button-label = Добавить новую подпись
|
||||||
|
|||||||
466
package-lock.json
generated
466
package-lock.json
generated
@ -15,12 +15,12 @@
|
|||||||
"@fluent/dom": "^0.10.2",
|
"@fluent/dom": "^0.10.2",
|
||||||
"@metalsmith/layouts": "^3.0.0",
|
"@metalsmith/layouts": "^3.0.0",
|
||||||
"@metalsmith/markdown": "^1.10.0",
|
"@metalsmith/markdown": "^1.10.0",
|
||||||
"@napi-rs/canvas": "^0.1.84",
|
"@napi-rs/canvas": "^0.1.88",
|
||||||
"@types/node": "^25.0.1",
|
"@types/node": "^25.0.3",
|
||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.23",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.0.0",
|
||||||
"cached-iterable": "^0.3.0",
|
"cached-iterable": "^0.3.0",
|
||||||
"caniuse-lite": "^1.0.30001760",
|
"caniuse-lite": "^1.0.30001762",
|
||||||
"core-js": "^3.47.0",
|
"core-js": "^3.47.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
@ -28,10 +28,10 @@
|
|||||||
"eslint-plugin-jasmine": "^4.2.2",
|
"eslint-plugin-jasmine": "^4.2.2",
|
||||||
"eslint-plugin-json": "^4.0.1",
|
"eslint-plugin-json": "^4.0.1",
|
||||||
"eslint-plugin-no-unsanitized": "^4.1.4",
|
"eslint-plugin-no-unsanitized": "^4.1.4",
|
||||||
"eslint-plugin-perfectionist": "^4.15.1",
|
"eslint-plugin-perfectionist": "^5.2.0",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"eslint-plugin-unicorn": "^62.0.0",
|
"eslint-plugin-unicorn": "^62.0.0",
|
||||||
"globals": "^16.5.0",
|
"globals": "^17.0.0",
|
||||||
"gulp": "^5.0.1",
|
"gulp": "^5.0.1",
|
||||||
"gulp-cli": "^3.1.0",
|
"gulp-cli": "^3.1.0",
|
||||||
"gulp-postcss": "^10.0.0",
|
"gulp-postcss": "^10.0.0",
|
||||||
@ -44,6 +44,7 @@
|
|||||||
"jstransformer-nunjucks": "^1.2.0",
|
"jstransformer-nunjucks": "^1.2.0",
|
||||||
"metalsmith": "^2.6.3",
|
"metalsmith": "^2.6.3",
|
||||||
"metalsmith-html-relative": "^2.0.9",
|
"metalsmith-html-relative": "^2.0.9",
|
||||||
|
"node-readable-to-web-readable-stream": "^0.4.2",
|
||||||
"ordered-read-streams": "^2.0.0",
|
"ordered-read-streams": "^2.0.0",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
@ -52,7 +53,7 @@
|
|||||||
"postcss-nesting": "^13.0.2",
|
"postcss-nesting": "^13.0.2",
|
||||||
"postcss-values-parser": "^7.0.0",
|
"postcss-values-parser": "^7.0.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"puppeteer": "^24.33.0",
|
"puppeteer": "^24.34.0",
|
||||||
"stylelint": "^16.26.1",
|
"stylelint": "^16.26.1",
|
||||||
"stylelint-prettier": "^5.0.3",
|
"stylelint-prettier": "^5.0.3",
|
||||||
"svglint": "^4.1.2",
|
"svglint": "^4.1.2",
|
||||||
@ -61,7 +62,7 @@
|
|||||||
"ttest": "^4.0.0",
|
"ttest": "^4.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vinyl": "^3.0.1",
|
"vinyl": "^3.0.1",
|
||||||
"webpack": "^5.103.0",
|
"webpack": "^5.104.1",
|
||||||
"webpack-stream": "^7.0.0",
|
"webpack-stream": "^7.0.0",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0"
|
||||||
},
|
},
|
||||||
@ -2377,9 +2378,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas": {
|
"node_modules/@napi-rs/canvas": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.88.tgz",
|
||||||
"integrity": "sha512-88FTNFs4uuiFKP0tUrPsEXhpe9dg7za9ILZJE08pGdUveMIDeana1zwfVkqRHJDPJFAmGY3dXmJ99dzsy57YnA==",
|
"integrity": "sha512-/p08f93LEbsL5mDZFQ3DBxcPv/I4QG9EDYRRq1WNlCOXVfAHBTHMSVMwxlqG/AtnSfUr9+vgfN7MKiyDo0+Weg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@ -2388,23 +2389,28 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@napi-rs/canvas-android-arm64": "0.1.84",
|
"@napi-rs/canvas-android-arm64": "0.1.88",
|
||||||
"@napi-rs/canvas-darwin-arm64": "0.1.84",
|
"@napi-rs/canvas-darwin-arm64": "0.1.88",
|
||||||
"@napi-rs/canvas-darwin-x64": "0.1.84",
|
"@napi-rs/canvas-darwin-x64": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.84",
|
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-arm64-gnu": "0.1.84",
|
"@napi-rs/canvas-linux-arm64-gnu": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-arm64-musl": "0.1.84",
|
"@napi-rs/canvas-linux-arm64-musl": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.84",
|
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-x64-gnu": "0.1.84",
|
"@napi-rs/canvas-linux-x64-gnu": "0.1.88",
|
||||||
"@napi-rs/canvas-linux-x64-musl": "0.1.84",
|
"@napi-rs/canvas-linux-x64-musl": "0.1.88",
|
||||||
"@napi-rs/canvas-win32-x64-msvc": "0.1.84"
|
"@napi-rs/canvas-win32-arm64-msvc": "0.1.88",
|
||||||
|
"@napi-rs/canvas-win32-x64-msvc": "0.1.88"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-android-arm64": {
|
"node_modules/@napi-rs/canvas-android-arm64": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.88.tgz",
|
||||||
"integrity": "sha512-pdvuqvj3qtwVryqgpAGornJLV6Ezpk39V6wT4JCnRVGy8I3Tk1au8qOalFGrx/r0Ig87hWslysPpHBxVpBMIww==",
|
"integrity": "sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -2416,12 +2422,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-darwin-arm64": {
|
"node_modules/@napi-rs/canvas-darwin-arm64": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.88.tgz",
|
||||||
"integrity": "sha512-A8IND3Hnv0R6abc6qCcCaOCujTLMmGxtucMTZ5vbQUrEN/scxi378MyTLtyWg+MRr6bwQJ6v/orqMS9datIcww==",
|
"integrity": "sha512-Xgywz0dDxOKSgx3eZnK85WgGMmGrQEW7ZLA/E7raZdlEE+xXCozobgqz2ZvYigpB6DJFYkqnwHjqCOTSDGlFdg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -2433,12 +2443,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-darwin-x64": {
|
"node_modules/@napi-rs/canvas-darwin-x64": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.88.tgz",
|
||||||
"integrity": "sha512-AUW45lJhYWwnA74LaNeqhvqYKK/2hNnBBBl03KRdqeCD4tKneUSrxUqIv8d22CBweOvrAASyKN3W87WO2zEr/A==",
|
"integrity": "sha512-Yz4wSCIQOUgNucgk+8NFtQxQxZV5NO8VKRl9ePKE6XoNyNVC8JDqtvhh3b3TPqKK8W5p2EQpAr1rjjm0mfBxdg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -2450,12 +2464,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
|
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.88.tgz",
|
||||||
"integrity": "sha512-8zs5ZqOrdgs4FioTxSBrkl/wHZB56bJNBqaIsfPL4ZkEQCinOkrFF7xIcXiHiKp93J3wUtbIzeVrhTIaWwqk+A==",
|
"integrity": "sha512-9gQM2SlTo76hYhxHi2XxWTAqpTOb+JtxMPEIr+H5nAhHhyEtNmTSDRtz93SP7mGd2G3Ojf2oF5tP9OdgtgXyKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -2467,12 +2485,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
|
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.88.tgz",
|
||||||
"integrity": "sha512-i204vtowOglJUpbAFWU5mqsJgH0lVpNk/Ml4mQtB4Lndd86oF+Otr6Mr5KQnZHqYGhlSIKiU2SYnUbhO28zGQA==",
|
"integrity": "sha512-7qgaOBMXuVRk9Fzztzr3BchQKXDxGbY+nwsovD3I/Sx81e+sX0ReEDYHTItNb0Je4NHbAl7D0MKyd4SvUc04sg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -2484,12 +2506,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
|
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.88.tgz",
|
||||||
"integrity": "sha512-VyZq0EEw+OILnWk7G3ZgLLPaz1ERaPP++jLjeyLMbFOF+Tr4zHzWKiKDsEV/cT7btLPZbVoR3VX+T9/QubnURQ==",
|
"integrity": "sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -2501,12 +2527,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
|
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.88.tgz",
|
||||||
"integrity": "sha512-PSMTh8DiThvLRsbtc/a065I/ceZk17EXAATv9uNvHgkgo7wdEfTh2C3aveNkBMGByVO3tvnvD5v/YFtZL07cIg==",
|
"integrity": "sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -2518,12 +2548,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
|
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.88.tgz",
|
||||||
"integrity": "sha512-N1GY3noO1oqgEo3rYQIwY44kfM11vA0lDbN0orTOHfCSUZTUyiYCY0nZ197QMahZBm1aR/vYgsWpV74MMMDuNA==",
|
"integrity": "sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -2535,12 +2569,16 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-linux-x64-musl": {
|
"node_modules/@napi-rs/canvas-linux-x64-musl": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.88.tgz",
|
||||||
"integrity": "sha512-vUZmua6ADqTWyHyei81aXIt9wp0yjeNwTH0KdhdeoBb6azHmFR8uKTukZMXfLCC3bnsW0t4lW7K78KNMknmtjg==",
|
"integrity": "sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -2552,12 +2590,37 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@napi-rs/canvas-win32-arm64-msvc": {
|
||||||
|
"version": "0.1.88",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.88.tgz",
|
||||||
|
"integrity": "sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
|
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.88",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.88.tgz",
|
||||||
"integrity": "sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og==",
|
"integrity": "sha512-ROVqbfS4QyZxYkqmaIBBpbz/BQvAR+05FXM5PAtTYVc0uyY8Y4BHJSMdGAaMf6TdIVRsQsiq+FG/dH9XhvWCFQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -2569,6 +2632,10 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
@ -2857,9 +2924,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.0.1",
|
"version": "25.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||||
"integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==",
|
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2896,14 +2963,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/project-service": {
|
"node_modules/@typescript-eslint/project-service": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.51.0.tgz",
|
||||||
"integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==",
|
"integrity": "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/tsconfig-utils": "^8.44.1",
|
"@typescript-eslint/tsconfig-utils": "^8.51.0",
|
||||||
"@typescript-eslint/types": "^8.44.1",
|
"@typescript-eslint/types": "^8.51.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2918,14 +2985,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz",
|
||||||
"integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==",
|
"integrity": "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.44.1",
|
"@typescript-eslint/types": "8.51.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.44.1"
|
"@typescript-eslint/visitor-keys": "8.51.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -2936,9 +3003,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz",
|
||||||
"integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==",
|
"integrity": "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2953,9 +3020,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz",
|
||||||
"integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==",
|
"integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2967,22 +3034,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz",
|
||||||
"integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==",
|
"integrity": "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/project-service": "8.44.1",
|
"@typescript-eslint/project-service": "8.51.0",
|
||||||
"@typescript-eslint/tsconfig-utils": "8.44.1",
|
"@typescript-eslint/tsconfig-utils": "8.51.0",
|
||||||
"@typescript-eslint/types": "8.44.1",
|
"@typescript-eslint/types": "8.51.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.44.1",
|
"@typescript-eslint/visitor-keys": "8.51.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
|
||||||
"is-glob": "^4.0.3",
|
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
"ts-api-utils": "^2.1.0"
|
"tinyglobby": "^0.2.15",
|
||||||
|
"ts-api-utils": "^2.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -3022,9 +3088,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -3035,16 +3101,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.51.0.tgz",
|
||||||
"integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==",
|
"integrity": "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.7.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.44.1",
|
"@typescript-eslint/scope-manager": "8.51.0",
|
||||||
"@typescript-eslint/types": "8.44.1",
|
"@typescript-eslint/types": "8.51.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.44.1"
|
"@typescript-eslint/typescript-estree": "8.51.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -3059,13 +3125,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.44.1",
|
"version": "8.51.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz",
|
||||||
"integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==",
|
"integrity": "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.44.1",
|
"@typescript-eslint/types": "8.51.0",
|
||||||
"eslint-visitor-keys": "^4.2.1"
|
"eslint-visitor-keys": "^4.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -3726,9 +3792,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.22",
|
"version": "10.4.23",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
|
||||||
"integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==",
|
"integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -3746,10 +3812,9 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.27.0",
|
"browserslist": "^4.28.1",
|
||||||
"caniuse-lite": "^1.0.30001754",
|
"caniuse-lite": "^1.0.30001760",
|
||||||
"fraction.js": "^5.3.4",
|
"fraction.js": "^5.3.4",
|
||||||
"normalize-range": "^0.1.2",
|
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
@ -3986,9 +4051,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.8.32",
|
"version": "2.9.11",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
|
||||||
"integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==",
|
"integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -3996,9 +4061,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/basic-ftp": {
|
"node_modules/basic-ftp": {
|
||||||
"version": "5.0.5",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
|
||||||
"integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
|
"integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -4097,9 +4162,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||||
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
|
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -4117,11 +4182,11 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.25",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001754",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
"electron-to-chromium": "^1.5.249",
|
"electron-to-chromium": "^1.5.263",
|
||||||
"node-releases": "^2.0.27",
|
"node-releases": "^2.0.27",
|
||||||
"update-browserslist-db": "^1.1.4"
|
"update-browserslist-db": "^1.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@ -4307,9 +4372,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001760",
|
"version": "1.0.30001762",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
|
||||||
"integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
|
"integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -4464,9 +4529,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chromium-bidi": {
|
"node_modules/chromium-bidi": {
|
||||||
"version": "11.0.0",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-12.0.1.tgz",
|
||||||
"integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==",
|
"integrity": "sha512-fGg+6jr0xjQhzpy5N4ErZxQ4wF7KLEvhGZXD6EgvZKDhu7iOhZXnZhcDxPJDcwTcrD48NPzOCo84RP2lv3Z+Cg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -5312,9 +5377,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.262",
|
"version": "1.5.267",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
|
||||||
"integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==",
|
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@ -5512,9 +5577,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.7.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
|
||||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -5829,18 +5894,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-perfectionist": {
|
"node_modules/eslint-plugin-perfectionist": {
|
||||||
"version": "4.15.1",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.2.0.tgz",
|
||||||
"integrity": "sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==",
|
"integrity": "sha512-rLD4VyA6sxcCPlG/koqjp0D46JTNRURSDs22Jr1JeVbOiu1BoeRdROnJoqDoGESuXbwxvGEnMSqClX/Q3HSMig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "^8.38.0",
|
"@typescript-eslint/utils": "^8.51.0",
|
||||||
"@typescript-eslint/utils": "^8.38.0",
|
|
||||||
"natural-orderby": "^5.0.0"
|
"natural-orderby": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.0.0 || >=20.0.0"
|
"node": "^20.0.0 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": ">=8.45.0"
|
"eslint": ">=8.45.0"
|
||||||
@ -5913,6 +5977,19 @@
|
|||||||
"eslint": ">=9.38.0"
|
"eslint": ">=9.38.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-unicorn/node_modules/globals": {
|
||||||
|
"version": "16.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
|
||||||
|
"integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-unicorn/node_modules/semver": {
|
"node_modules/eslint-plugin-unicorn/node_modules/semver": {
|
||||||
"version": "7.7.3",
|
"version": "7.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||||
@ -6883,9 +6960,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "16.5.0",
|
"version": "17.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz",
|
||||||
"integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
|
"integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -8703,9 +8780,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.23",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -9297,6 +9374,13 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-readable-to-web-readable-stream": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-readable-to-web-readable-stream/-/node-readable-to-web-readable-stream-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.27",
|
"version": "2.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||||
@ -9343,16 +9427,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/normalize-range": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/now-and-later": {
|
"node_modules/now-and-later": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz",
|
||||||
@ -10386,18 +10460,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/puppeteer": {
|
"node_modules/puppeteer": {
|
||||||
"version": "24.33.0",
|
"version": "24.34.0",
|
||||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.34.0.tgz",
|
||||||
"integrity": "sha512-nl3wsAztq5F8zybn4Tk41OCnYIzFIzGC6AN0WcF2KCUnWenajvRRPgBmS6LvNUV2HEeIzT2zRZHH0TgVxLDKew==",
|
"integrity": "sha512-Sdpl/zsYOsagZ4ICoZJPGZw8d9gZmK5DcxVal11dXi/1/t2eIXHjCf5NfmhDg5XnG9Nye+yo/LqMzIxie2rHTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@puppeteer/browsers": "2.11.0",
|
"@puppeteer/browsers": "2.11.0",
|
||||||
"chromium-bidi": "11.0.0",
|
"chromium-bidi": "12.0.1",
|
||||||
"cosmiconfig": "^9.0.0",
|
"cosmiconfig": "^9.0.0",
|
||||||
"devtools-protocol": "0.0.1534754",
|
"devtools-protocol": "0.0.1534754",
|
||||||
"puppeteer-core": "24.33.0",
|
"puppeteer-core": "24.34.0",
|
||||||
"typed-query-selector": "^2.12.0"
|
"typed-query-selector": "^2.12.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -10408,18 +10482,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/puppeteer-core": {
|
"node_modules/puppeteer-core": {
|
||||||
"version": "24.33.0",
|
"version": "24.34.0",
|
||||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.34.0.tgz",
|
||||||
"integrity": "sha512-tPTxVg+Qdj/8av4cy6szv3GlhxeOoNhiiMZ955fjxQyvPQE/6DjCa6ZyF/x0WJrlgBZtaLSP8TQgJb7FdLDXXA==",
|
"integrity": "sha512-24evawO+mUGW4mvS2a2ivwLdX3gk8zRLZr9HP+7+VT2vBQnm0oh9jJEZmUE3ePJhRkYlZ93i7OMpdcoi2qNCLg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@puppeteer/browsers": "2.11.0",
|
"@puppeteer/browsers": "2.11.0",
|
||||||
"chromium-bidi": "11.0.0",
|
"chromium-bidi": "12.0.1",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"devtools-protocol": "0.0.1534754",
|
"devtools-protocol": "0.0.1534754",
|
||||||
"typed-query-selector": "^2.12.0",
|
"typed-query-selector": "^2.12.0",
|
||||||
"webdriver-bidi-protocol": "0.3.9",
|
"webdriver-bidi-protocol": "0.3.10",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -12643,6 +12717,54 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tinyglobby": {
|
||||||
|
"version": "0.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fdir": "^6.5.0",
|
||||||
|
"picomatch": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/fdir": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@ -12680,9 +12802,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.1.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
|
||||||
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
|
"integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -13031,9 +13153,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.4",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||||
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
|
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -13270,16 +13392,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriver-bidi-protocol": {
|
"node_modules/webdriver-bidi-protocol": {
|
||||||
"version": "0.3.9",
|
"version": "0.3.10",
|
||||||
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.10.tgz",
|
||||||
"integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==",
|
"integrity": "sha512-5LAE43jAVLOhB/QqX4bwSiv0Hg1HBfMmOuwBSXHdvg4GMGu9Y0lIq7p4R/yySu6w74WmaR4GM4H9t2IwLW7hgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/webpack": {
|
"node_modules/webpack": {
|
||||||
"version": "5.103.0",
|
"version": "5.104.1",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz",
|
||||||
"integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
|
"integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -13291,10 +13413,10 @@
|
|||||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||||
"acorn": "^8.15.0",
|
"acorn": "^8.15.0",
|
||||||
"acorn-import-phases": "^1.0.3",
|
"acorn-import-phases": "^1.0.3",
|
||||||
"browserslist": "^4.26.3",
|
"browserslist": "^4.28.1",
|
||||||
"chrome-trace-event": "^1.0.2",
|
"chrome-trace-event": "^1.0.2",
|
||||||
"enhanced-resolve": "^5.17.3",
|
"enhanced-resolve": "^5.17.4",
|
||||||
"es-module-lexer": "^1.2.1",
|
"es-module-lexer": "^2.0.0",
|
||||||
"eslint-scope": "5.1.1",
|
"eslint-scope": "5.1.1",
|
||||||
"events": "^3.2.0",
|
"events": "^3.2.0",
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
@ -13305,7 +13427,7 @@
|
|||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"schema-utils": "^4.3.3",
|
"schema-utils": "^4.3.3",
|
||||||
"tapable": "^2.3.0",
|
"tapable": "^2.3.0",
|
||||||
"terser-webpack-plugin": "^5.3.11",
|
"terser-webpack-plugin": "^5.3.16",
|
||||||
"watchpack": "^2.4.4",
|
"watchpack": "^2.4.4",
|
||||||
"webpack-sources": "^3.3.3"
|
"webpack-sources": "^3.3.3"
|
||||||
},
|
},
|
||||||
|
|||||||
17
package.json
17
package.json
@ -10,12 +10,12 @@
|
|||||||
"@fluent/dom": "^0.10.2",
|
"@fluent/dom": "^0.10.2",
|
||||||
"@metalsmith/layouts": "^3.0.0",
|
"@metalsmith/layouts": "^3.0.0",
|
||||||
"@metalsmith/markdown": "^1.10.0",
|
"@metalsmith/markdown": "^1.10.0",
|
||||||
"@napi-rs/canvas": "^0.1.84",
|
"@napi-rs/canvas": "^0.1.88",
|
||||||
"@types/node": "^25.0.1",
|
"@types/node": "^25.0.3",
|
||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.23",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.0.0",
|
||||||
"cached-iterable": "^0.3.0",
|
"cached-iterable": "^0.3.0",
|
||||||
"caniuse-lite": "^1.0.30001760",
|
"caniuse-lite": "^1.0.30001762",
|
||||||
"core-js": "^3.47.0",
|
"core-js": "^3.47.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
@ -23,10 +23,10 @@
|
|||||||
"eslint-plugin-jasmine": "^4.2.2",
|
"eslint-plugin-jasmine": "^4.2.2",
|
||||||
"eslint-plugin-json": "^4.0.1",
|
"eslint-plugin-json": "^4.0.1",
|
||||||
"eslint-plugin-no-unsanitized": "^4.1.4",
|
"eslint-plugin-no-unsanitized": "^4.1.4",
|
||||||
"eslint-plugin-perfectionist": "^4.15.1",
|
"eslint-plugin-perfectionist": "^5.2.0",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"eslint-plugin-unicorn": "^62.0.0",
|
"eslint-plugin-unicorn": "^62.0.0",
|
||||||
"globals": "^16.5.0",
|
"globals": "^17.0.0",
|
||||||
"gulp": "^5.0.1",
|
"gulp": "^5.0.1",
|
||||||
"gulp-cli": "^3.1.0",
|
"gulp-cli": "^3.1.0",
|
||||||
"gulp-postcss": "^10.0.0",
|
"gulp-postcss": "^10.0.0",
|
||||||
@ -39,6 +39,7 @@
|
|||||||
"jstransformer-nunjucks": "^1.2.0",
|
"jstransformer-nunjucks": "^1.2.0",
|
||||||
"metalsmith": "^2.6.3",
|
"metalsmith": "^2.6.3",
|
||||||
"metalsmith-html-relative": "^2.0.9",
|
"metalsmith-html-relative": "^2.0.9",
|
||||||
|
"node-readable-to-web-readable-stream": "^0.4.2",
|
||||||
"ordered-read-streams": "^2.0.0",
|
"ordered-read-streams": "^2.0.0",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
@ -47,7 +48,7 @@
|
|||||||
"postcss-nesting": "^13.0.2",
|
"postcss-nesting": "^13.0.2",
|
||||||
"postcss-values-parser": "^7.0.0",
|
"postcss-values-parser": "^7.0.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"puppeteer": "^24.33.0",
|
"puppeteer": "^24.34.0",
|
||||||
"stylelint": "^16.26.1",
|
"stylelint": "^16.26.1",
|
||||||
"stylelint-prettier": "^5.0.3",
|
"stylelint-prettier": "^5.0.3",
|
||||||
"svglint": "^4.1.2",
|
"svglint": "^4.1.2",
|
||||||
@ -56,7 +57,7 @@
|
|||||||
"ttest": "^4.0.0",
|
"ttest": "^4.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vinyl": "^3.0.1",
|
"vinyl": "^3.0.1",
|
||||||
"webpack": "^5.103.0",
|
"webpack": "^5.104.1",
|
||||||
"webpack-stream": "^7.0.0",
|
"webpack-stream": "^7.0.0",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"stableVersion": "5.4.449",
|
"stableVersion": "5.4.624",
|
||||||
"baseVersion": "1b427a3af5e0a40c296a3cafb08edbd36d973ff1",
|
"baseVersion": "1b427a3af5e0a40c296a3cafb08edbd36d973ff1",
|
||||||
"versionPrefix": "5.4."
|
"versionPrefix": "5.4."
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5290,8 +5290,8 @@ class FileAttachmentAnnotation extends MarkupAnnotation {
|
|||||||
constructor(params) {
|
constructor(params) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
const { dict, xref } = params;
|
const { dict } = params;
|
||||||
const file = new FileSpec(dict.get("FS"), xref);
|
const file = new FileSpec(dict.get("FS"));
|
||||||
|
|
||||||
this.data.annotationType = AnnotationType.FILEATTACHMENT;
|
this.data.annotationType = AnnotationType.FILEATTACHMENT;
|
||||||
this.data.hasOwnCanvas = this.data.noRotate;
|
this.data.hasOwnCanvas = this.data.noRotate;
|
||||||
|
|||||||
@ -68,6 +68,10 @@ class BaseStream {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isImageStream() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
get canAsyncDecodeImageFromBuffer() {
|
get canAsyncDecodeImageFromBuffer() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
86
src/core/brotli_stream.js
Normal file
86
src/core/brotli_stream.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/* Copyright 2026 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BrotliDecode } from "../../external/brotli/decode.js";
|
||||||
|
import { DecodeStream } from "./decode_stream.js";
|
||||||
|
import { Stream } from "./stream.js";
|
||||||
|
|
||||||
|
class BrotliStream extends DecodeStream {
|
||||||
|
#isAsync = true;
|
||||||
|
|
||||||
|
constructor(stream, maybeLength) {
|
||||||
|
super(maybeLength);
|
||||||
|
|
||||||
|
this.stream = stream;
|
||||||
|
this.dict = stream.dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
readBlock() {
|
||||||
|
// TODO: add some telemetry to measure how often we fallback here.
|
||||||
|
// Get all bytes from the input stream
|
||||||
|
const bytes = this.stream.getBytes();
|
||||||
|
const decodedData = BrotliDecode(
|
||||||
|
new Int8Array(bytes.buffer, bytes.byteOffset, bytes.length)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.buffer = new Uint8Array(
|
||||||
|
decodedData.buffer,
|
||||||
|
decodedData.byteOffset,
|
||||||
|
decodedData.length
|
||||||
|
);
|
||||||
|
this.bufferLength = this.buffer.length;
|
||||||
|
this.eof = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getImageData(length, _decoderOptions) {
|
||||||
|
const data = await this.asyncGetBytes();
|
||||||
|
if (!data) {
|
||||||
|
return this.getBytes(length);
|
||||||
|
}
|
||||||
|
if (data.length <= length) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return data.subarray(0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncGetBytes() {
|
||||||
|
const { decompressed, compressed } =
|
||||||
|
await this.asyncGetBytesFromDecompressionStream("brotli");
|
||||||
|
if (decompressed) {
|
||||||
|
return decompressed;
|
||||||
|
}
|
||||||
|
// DecompressionStream failed (for example because there are some extra
|
||||||
|
// bytes after the end of the compressed data), so we fallback to our
|
||||||
|
// decoder.
|
||||||
|
// We already get the bytes from the underlying stream, so we just reuse
|
||||||
|
// them to avoid get them again.
|
||||||
|
|
||||||
|
this.#isAsync = false;
|
||||||
|
this.stream = new Stream(
|
||||||
|
compressed,
|
||||||
|
0,
|
||||||
|
compressed.length,
|
||||||
|
this.stream.dict
|
||||||
|
);
|
||||||
|
this.reset();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAsync() {
|
||||||
|
return this.#isAsync;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BrotliStream };
|
||||||
@ -1057,7 +1057,7 @@ class Catalog {
|
|||||||
if (obj instanceof Dict && obj.has("EmbeddedFiles")) {
|
if (obj instanceof Dict && obj.has("EmbeddedFiles")) {
|
||||||
const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
|
const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
|
||||||
for (const [key, value] of nameTree.getAll()) {
|
for (const [key, value] of nameTree.getAll()) {
|
||||||
const fs = new FileSpec(value, this.xref);
|
const fs = new FileSpec(value);
|
||||||
attachments ??= Object.create(null);
|
attachments ??= Object.create(null);
|
||||||
attachments[stringToPDFString(key, /* keepEscapeSequence = */ true)] =
|
attachments[stringToPDFString(key, /* keepEscapeSequence = */ true)] =
|
||||||
fs.serializable;
|
fs.serializable;
|
||||||
@ -1623,23 +1623,21 @@ class Catalog {
|
|||||||
case "GoToR":
|
case "GoToR":
|
||||||
const urlDict = action.get("F");
|
const urlDict = action.get("F");
|
||||||
if (urlDict instanceof Dict) {
|
if (urlDict instanceof Dict) {
|
||||||
const fs = new FileSpec(
|
const fs = new FileSpec(urlDict, /* skipContent = */ true);
|
||||||
urlDict,
|
({ rawFilename: url } = fs.serializable);
|
||||||
/* xref = */ null,
|
|
||||||
/* skipContent = */ true
|
|
||||||
);
|
|
||||||
const { rawFilename } = fs.serializable;
|
|
||||||
url = rawFilename;
|
|
||||||
} else if (typeof urlDict === "string") {
|
} else if (typeof urlDict === "string") {
|
||||||
url = urlDict;
|
url = urlDict;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: the destination is relative to the *remote* document.
|
// NOTE: the destination is relative to the *remote* document.
|
||||||
const remoteDest = fetchRemoteDest(action);
|
const remoteDest = fetchRemoteDest(action);
|
||||||
if (remoteDest && typeof url === "string") {
|
if (remoteDest) {
|
||||||
// NOTE: We don't use the `updateUrlHash` function here, since
|
// NOTE: We don't use the `updateUrlHash` function here, since
|
||||||
// the `createValidAbsoluteUrl` function (see below) already
|
// the `createValidAbsoluteUrl` function (see below) already handles
|
||||||
// handles parsing and validation of the final URL.
|
// parsing/validation of the final URL and manual splitting also
|
||||||
|
// ensures that the `unsafeUrl` property will be available/correct.
|
||||||
url = /* baseUrl = */ url.split("#", 1)[0] + "#" + remoteDest;
|
url = /* baseUrl = */ url.split("#", 1)[0] + "#" + remoteDest;
|
||||||
}
|
}
|
||||||
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
||||||
|
|||||||
@ -465,20 +465,33 @@ const blackTable3 = [
|
|||||||
* @param {Object} [options] - Decoding options.
|
* @param {Object} [options] - Decoding options.
|
||||||
*/
|
*/
|
||||||
class CCITTFaxDecoder {
|
class CCITTFaxDecoder {
|
||||||
constructor(source, options = {}) {
|
constructor(
|
||||||
|
source,
|
||||||
|
options = {
|
||||||
|
K: 0,
|
||||||
|
EndOfLine: false,
|
||||||
|
EncodedByteAlign: false,
|
||||||
|
Columns: 1728,
|
||||||
|
Rows: 0,
|
||||||
|
EndOfBlock: true,
|
||||||
|
BlackIs1: false,
|
||||||
|
}
|
||||||
|
) {
|
||||||
if (typeof source?.next !== "function") {
|
if (typeof source?.next !== "function") {
|
||||||
throw new Error('CCITTFaxDecoder - invalid "source" parameter.');
|
throw new Error('CCITTFaxDecoder - invalid "source" parameter.');
|
||||||
}
|
}
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.eof = false;
|
this.eof = false;
|
||||||
|
|
||||||
this.encoding = options.K || 0;
|
({
|
||||||
this.eoline = options.EndOfLine || false;
|
K: this.encoding,
|
||||||
this.byteAlign = options.EncodedByteAlign || false;
|
EndOfLine: this.eoline,
|
||||||
this.columns = options.Columns || 1728;
|
EncodedByteAlign: this.byteAlign,
|
||||||
this.rows = options.Rows || 0;
|
Columns: this.columns,
|
||||||
this.eoblock = options.EndOfBlock ?? true;
|
Rows: this.rows,
|
||||||
this.black = options.BlackIs1 || false;
|
EndOfBlock: this.eoblock,
|
||||||
|
BlackIs1: this.black,
|
||||||
|
} = options);
|
||||||
|
|
||||||
this.codingLine = new Uint32Array(this.columns + 1);
|
this.codingLine = new Uint32Array(this.columns + 1);
|
||||||
this.refLine = new Uint32Array(this.columns + 2);
|
this.refLine = new Uint32Array(this.columns + 2);
|
||||||
|
|||||||
@ -13,47 +13,114 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { shadow, warn } from "../shared/util.js";
|
||||||
import { CCITTFaxDecoder } from "./ccitt.js";
|
import { CCITTFaxDecoder } from "./ccitt.js";
|
||||||
import { DecodeStream } from "./decode_stream.js";
|
import { DecodeStream } from "./decode_stream.js";
|
||||||
import { Dict } from "./primitives.js";
|
import { Dict } from "./primitives.js";
|
||||||
|
import { JBig2CCITTFaxWasmImage } from "./jbig2_ccittFax_wasm.js";
|
||||||
|
|
||||||
class CCITTFaxStream extends DecodeStream {
|
class CCITTFaxStream extends DecodeStream {
|
||||||
constructor(str, maybeLength, params) {
|
constructor(str, maybeLength, params) {
|
||||||
super(maybeLength);
|
super(maybeLength);
|
||||||
|
|
||||||
this.stream = str;
|
this.stream = str;
|
||||||
|
this.maybeLength = maybeLength;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
|
|
||||||
if (!(params instanceof Dict)) {
|
if (!(params instanceof Dict)) {
|
||||||
params = Dict.empty;
|
params = Dict.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = {
|
this.params = {
|
||||||
next() {
|
K: params.get("K") || 0,
|
||||||
return str.getByte();
|
EndOfLine: !!params.get("EndOfLine"),
|
||||||
},
|
EncodedByteAlign: !!params.get("EncodedByteAlign"),
|
||||||
|
Columns: params.get("Columns") || 1728,
|
||||||
|
Rows: params.get("Rows") || 0,
|
||||||
|
EndOfBlock: !!(params.get("EndOfBlock") ?? true),
|
||||||
|
BlackIs1: !!params.get("BlackIs1"),
|
||||||
};
|
};
|
||||||
this.ccittFaxDecoder = new CCITTFaxDecoder(source, {
|
}
|
||||||
K: params.get("K"),
|
|
||||||
EndOfLine: params.get("EndOfLine"),
|
get bytes() {
|
||||||
EncodedByteAlign: params.get("EncodedByteAlign"),
|
// If `this.maybeLength` is null, we'll get the entire stream.
|
||||||
Columns: params.get("Columns"),
|
return shadow(this, "bytes", this.stream.getBytes(this.maybeLength));
|
||||||
Rows: params.get("Rows"),
|
|
||||||
EndOfBlock: params.get("EndOfBlock"),
|
|
||||||
BlackIs1: params.get("BlackIs1"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readBlock() {
|
readBlock() {
|
||||||
|
this.decodeImageFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
get isImageStream() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isAsyncDecoder() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async decodeImage(bytes, length, _decoderOptions) {
|
||||||
|
if (this.eof) {
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
if (!bytes) {
|
||||||
|
bytes = this.stream.isAsync
|
||||||
|
? (await this.stream.asyncGetBytes()) || this.bytes
|
||||||
|
: this.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.buffer = await JBig2CCITTFaxWasmImage.decode(
|
||||||
|
bytes,
|
||||||
|
this.dict.get("W", "Width"),
|
||||||
|
this.dict.get("H", "Height"),
|
||||||
|
null,
|
||||||
|
this.params
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
warn("CCITTFaxStream: Falling back to JS CCITTFax decoder.");
|
||||||
|
return this.decodeImageFallback(bytes, length);
|
||||||
|
}
|
||||||
|
this.bufferLength = this.buffer.length;
|
||||||
|
this.eof = true;
|
||||||
|
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeImageFallback(bytes, length) {
|
||||||
|
if (this.eof) {
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
const { params } = this;
|
||||||
|
if (!bytes) {
|
||||||
|
this.stream.reset();
|
||||||
|
bytes = this.bytes;
|
||||||
|
}
|
||||||
|
let pos = 0;
|
||||||
|
const source = {
|
||||||
|
next() {
|
||||||
|
return bytes[pos++] ?? -1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (length && this.buffer.byteLength < length) {
|
||||||
|
this.buffer = new Uint8Array(length);
|
||||||
|
}
|
||||||
|
this.ccittFaxDecoder = new CCITTFaxDecoder(source, params);
|
||||||
|
let outPos = 0;
|
||||||
while (!this.eof) {
|
while (!this.eof) {
|
||||||
const c = this.ccittFaxDecoder.readNextChar();
|
const c = this.ccittFaxDecoder.readNextChar();
|
||||||
if (c === -1) {
|
if (c === -1) {
|
||||||
this.eof = true;
|
this.eof = true;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
this.ensureBuffer(this.bufferLength + 1);
|
if (!length) {
|
||||||
this.buffer[this.bufferLength++] = c;
|
this.ensureBuffer(outPos + 1);
|
||||||
|
}
|
||||||
|
this.buffer[outPos++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.bufferLength = this.buffer.length;
|
||||||
|
return this.buffer.subarray(0, length || this.bufferLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { arrayBuffersToBytes, MissingDataException } from "./core_utils.js";
|
import { arrayBuffersToBytes, MissingDataException } from "./core_utils.js";
|
||||||
import { assert } from "../shared/util.js";
|
import { assert, MathClamp } from "../shared/util.js";
|
||||||
import { Stream } from "./stream.js";
|
import { Stream } from "./stream.js";
|
||||||
|
|
||||||
class ChunkedStream extends Stream {
|
class ChunkedStream extends Stream {
|
||||||
|
progressiveDataLength = 0;
|
||||||
|
|
||||||
|
_lastSuccessfulEnsureByteChunk = -1; // Single-entry cache
|
||||||
|
|
||||||
|
_loadedChunks = new Set();
|
||||||
|
|
||||||
constructor(length, chunkSize, manager) {
|
constructor(length, chunkSize, manager) {
|
||||||
super(
|
super(
|
||||||
/* arrayBuffer = */ new Uint8Array(length),
|
/* arrayBuffer = */ new Uint8Array(length),
|
||||||
@ -27,11 +33,8 @@ class ChunkedStream extends Stream {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.chunkSize = chunkSize;
|
this.chunkSize = chunkSize;
|
||||||
this._loadedChunks = new Set();
|
|
||||||
this.numChunks = Math.ceil(length / chunkSize);
|
this.numChunks = Math.ceil(length / chunkSize);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.progressiveDataLength = 0;
|
|
||||||
this.lastSuccessfulEnsureByteChunk = -1; // Single-entry cache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a particular stream does not implement one or more of these methods,
|
// If a particular stream does not implement one or more of these methods,
|
||||||
@ -67,6 +70,12 @@ class ChunkedStream extends Stream {
|
|||||||
throw new Error(`Bad end offset: ${end}`);
|
throw new Error(`Bad end offset: ${end}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
|
assert(
|
||||||
|
chunk instanceof ArrayBuffer,
|
||||||
|
"onReceiveData - expected an ArrayBuffer."
|
||||||
|
);
|
||||||
|
}
|
||||||
this.bytes.set(new Uint8Array(chunk), begin);
|
this.bytes.set(new Uint8Array(chunk), begin);
|
||||||
const beginChunk = Math.floor(begin / chunkSize);
|
const beginChunk = Math.floor(begin / chunkSize);
|
||||||
const endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
const endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
||||||
@ -82,6 +91,12 @@ class ChunkedStream extends Stream {
|
|||||||
let position = this.progressiveDataLength;
|
let position = this.progressiveDataLength;
|
||||||
const beginChunk = Math.floor(position / this.chunkSize);
|
const beginChunk = Math.floor(position / this.chunkSize);
|
||||||
|
|
||||||
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
|
assert(
|
||||||
|
data instanceof ArrayBuffer,
|
||||||
|
"onReceiveProgressiveData - expected an ArrayBuffer."
|
||||||
|
);
|
||||||
|
}
|
||||||
this.bytes.set(new Uint8Array(data), position);
|
this.bytes.set(new Uint8Array(data), position);
|
||||||
position += data.byteLength;
|
position += data.byteLength;
|
||||||
this.progressiveDataLength = position;
|
this.progressiveDataLength = position;
|
||||||
@ -106,14 +121,14 @@ class ChunkedStream extends Stream {
|
|||||||
if (chunk > this.numChunks) {
|
if (chunk > this.numChunks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (chunk === this.lastSuccessfulEnsureByteChunk) {
|
if (chunk === this._lastSuccessfulEnsureByteChunk) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._loadedChunks.has(chunk)) {
|
if (!this._loadedChunks.has(chunk)) {
|
||||||
throw new MissingDataException(pos, pos + 1);
|
throw new MissingDataException(pos, pos + 1);
|
||||||
}
|
}
|
||||||
this.lastSuccessfulEnsureByteChunk = chunk;
|
this._lastSuccessfulEnsureByteChunk = chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureRange(begin, end) {
|
ensureRange(begin, end) {
|
||||||
@ -257,40 +272,37 @@ class ChunkedStream extends Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ChunkedStreamManager {
|
class ChunkedStreamManager {
|
||||||
constructor(pdfNetworkStream, args) {
|
aborted = false;
|
||||||
|
|
||||||
|
currRequestId = 0;
|
||||||
|
|
||||||
|
_chunksNeededByRequest = new Map();
|
||||||
|
|
||||||
|
_loadedStreamCapability = Promise.withResolvers();
|
||||||
|
|
||||||
|
_promisesByRequest = new Map();
|
||||||
|
|
||||||
|
_requestsByChunk = new Map();
|
||||||
|
|
||||||
|
constructor(pdfStream, args) {
|
||||||
this.length = args.length;
|
this.length = args.length;
|
||||||
this.chunkSize = args.rangeChunkSize;
|
this.chunkSize = args.rangeChunkSize;
|
||||||
this.stream = new ChunkedStream(this.length, this.chunkSize, this);
|
this.stream = new ChunkedStream(this.length, this.chunkSize, this);
|
||||||
this.pdfNetworkStream = pdfNetworkStream;
|
this.pdfStream = pdfStream;
|
||||||
this.disableAutoFetch = args.disableAutoFetch;
|
this.disableAutoFetch = args.disableAutoFetch;
|
||||||
this.msgHandler = args.msgHandler;
|
this.msgHandler = args.msgHandler;
|
||||||
|
|
||||||
this.currRequestId = 0;
|
|
||||||
|
|
||||||
this._chunksNeededByRequest = new Map();
|
|
||||||
this._requestsByChunk = new Map();
|
|
||||||
this._promisesByRequest = new Map();
|
|
||||||
this.progressiveDataLength = 0;
|
|
||||||
this.aborted = false;
|
|
||||||
|
|
||||||
this._loadedStreamCapability = Promise.withResolvers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRequest(begin, end) {
|
sendRequest(begin, end) {
|
||||||
const rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);
|
const rangeReader = this.pdfStream.getRangeReader(begin, end);
|
||||||
if (!rangeReader.isStreamingSupported) {
|
|
||||||
rangeReader.onProgress = this.onProgress.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
let chunks = [],
|
let chunks = [];
|
||||||
loaded = 0;
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const readChunk = ({ value, done }) => {
|
const readChunk = ({ value, done }) => {
|
||||||
try {
|
try {
|
||||||
if (done) {
|
if (done) {
|
||||||
const chunkData = arrayBuffersToBytes(chunks);
|
resolve(arrayBuffersToBytes(chunks));
|
||||||
chunks = null;
|
chunks = null;
|
||||||
resolve(chunkData);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
@ -299,12 +311,6 @@ class ChunkedStreamManager {
|
|||||||
"readChunk (sendRequest) - expected an ArrayBuffer."
|
"readChunk (sendRequest) - expected an ArrayBuffer."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
loaded += value.byteLength;
|
|
||||||
|
|
||||||
if (rangeReader.isStreamingSupported) {
|
|
||||||
this.onProgress({ loaded });
|
|
||||||
}
|
|
||||||
|
|
||||||
chunks.push(value);
|
chunks.push(value);
|
||||||
rangeReader.read().then(readChunk, reject);
|
rangeReader.read().then(readChunk, reject);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -316,7 +322,7 @@ class ChunkedStreamManager {
|
|||||||
if (this.aborted) {
|
if (this.aborted) {
|
||||||
return; // Ignoring any data after abort.
|
return; // Ignoring any data after abort.
|
||||||
}
|
}
|
||||||
this.onReceiveData({ chunk: data, begin });
|
this.onReceiveData({ chunk: data.buffer, begin });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,34 +452,26 @@ class ChunkedStreamManager {
|
|||||||
return groupedChunks;
|
return groupedChunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
onProgress(args) {
|
|
||||||
this.msgHandler.send("DocProgress", {
|
|
||||||
loaded: this.stream.numChunksLoaded * this.chunkSize + args.loaded,
|
|
||||||
total: this.length,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onReceiveData(args) {
|
onReceiveData(args) {
|
||||||
|
const { chunkSize, length, stream } = this;
|
||||||
|
|
||||||
const chunk = args.chunk;
|
const chunk = args.chunk;
|
||||||
const isProgressive = args.begin === undefined;
|
const isProgressive = args.begin === undefined;
|
||||||
const begin = isProgressive ? this.progressiveDataLength : args.begin;
|
const begin = isProgressive ? stream.progressiveDataLength : args.begin;
|
||||||
const end = begin + chunk.byteLength;
|
const end = begin + chunk.byteLength;
|
||||||
|
|
||||||
const beginChunk = Math.floor(begin / this.chunkSize);
|
const beginChunk = Math.floor(begin / chunkSize);
|
||||||
const endChunk =
|
const endChunk =
|
||||||
end < this.length
|
end < length ? Math.floor(end / chunkSize) : Math.ceil(end / chunkSize);
|
||||||
? Math.floor(end / this.chunkSize)
|
|
||||||
: Math.ceil(end / this.chunkSize);
|
|
||||||
|
|
||||||
if (isProgressive) {
|
if (isProgressive) {
|
||||||
this.stream.onReceiveProgressiveData(chunk);
|
stream.onReceiveProgressiveData(chunk);
|
||||||
this.progressiveDataLength = end;
|
|
||||||
} else {
|
} else {
|
||||||
this.stream.onReceiveData(begin, chunk);
|
stream.onReceiveData(begin, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stream.isDataLoaded) {
|
if (stream.isDataLoaded) {
|
||||||
this._loadedStreamCapability.resolve(this.stream);
|
this._loadedStreamCapability.resolve(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadedRequests = [];
|
const loadedRequests = [];
|
||||||
@ -502,16 +500,16 @@ class ChunkedStreamManager {
|
|||||||
// unfetched chunk of the PDF file.
|
// unfetched chunk of the PDF file.
|
||||||
if (!this.disableAutoFetch && this._requestsByChunk.size === 0) {
|
if (!this.disableAutoFetch && this._requestsByChunk.size === 0) {
|
||||||
let nextEmptyChunk;
|
let nextEmptyChunk;
|
||||||
if (this.stream.numChunksLoaded === 1) {
|
if (stream.numChunksLoaded === 1) {
|
||||||
// This is a special optimization so that after fetching the first
|
// This is a special optimization so that after fetching the first
|
||||||
// chunk, rather than fetching the second chunk, we fetch the last
|
// chunk, rather than fetching the second chunk, we fetch the last
|
||||||
// chunk.
|
// chunk.
|
||||||
const lastChunk = this.stream.numChunks - 1;
|
const lastChunk = stream.numChunks - 1;
|
||||||
if (!this.stream.hasChunk(lastChunk)) {
|
if (!stream.hasChunk(lastChunk)) {
|
||||||
nextEmptyChunk = lastChunk;
|
nextEmptyChunk = lastChunk;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
|
nextEmptyChunk = stream.nextEmptyChunk(endChunk);
|
||||||
}
|
}
|
||||||
if (Number.isInteger(nextEmptyChunk)) {
|
if (Number.isInteger(nextEmptyChunk)) {
|
||||||
this._requestChunks([nextEmptyChunk]);
|
this._requestChunks([nextEmptyChunk]);
|
||||||
@ -525,8 +523,12 @@ class ChunkedStreamManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.msgHandler.send("DocProgress", {
|
this.msgHandler.send("DocProgress", {
|
||||||
loaded: this.stream.numChunksLoaded * this.chunkSize,
|
loaded: MathClamp(
|
||||||
total: this.length,
|
stream.numChunksLoaded * chunkSize,
|
||||||
|
stream.progressiveDataLength,
|
||||||
|
length
|
||||||
|
),
|
||||||
|
total: length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +546,7 @@ class ChunkedStreamManager {
|
|||||||
|
|
||||||
abort(reason) {
|
abort(reason) {
|
||||||
this.aborted = true;
|
this.aborted = true;
|
||||||
this.pdfNetworkStream?.cancelAllRequests(reason);
|
this.pdfStream?.cancelAllRequests(reason);
|
||||||
|
|
||||||
for (const capability of this._promisesByRequest.values()) {
|
for (const capability of this._promisesByRequest.values()) {
|
||||||
capability.reject(reason);
|
capability.reject(reason);
|
||||||
|
|||||||
@ -699,6 +699,12 @@ class CMapFactory {
|
|||||||
if (encoding instanceof Name) {
|
if (encoding instanceof Name) {
|
||||||
return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
|
return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
|
||||||
} else if (encoding instanceof BaseStream) {
|
} else if (encoding instanceof BaseStream) {
|
||||||
|
if (encoding.isAsync) {
|
||||||
|
const bytes = await encoding.asyncGetBytes();
|
||||||
|
if (bytes) {
|
||||||
|
encoding = new Stream(bytes, 0, bytes.length, encoding.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
const parsedCMap = await parseCMap(
|
const parsedCMap = await parseCMap(
|
||||||
/* cMap = */ new CMap(),
|
/* cMap = */ new CMap(),
|
||||||
/* lexer = */ new Lexer(encoding),
|
/* lexer = */ new Lexer(encoding),
|
||||||
|
|||||||
@ -102,12 +102,52 @@ class DecodeStream extends BaseStream {
|
|||||||
async getImageData(length, decoderOptions) {
|
async getImageData(length, decoderOptions) {
|
||||||
if (!this.canAsyncDecodeImageFromBuffer) {
|
if (!this.canAsyncDecodeImageFromBuffer) {
|
||||||
if (this.isAsyncDecoder) {
|
if (this.isAsyncDecoder) {
|
||||||
return this.decodeImage(null, decoderOptions);
|
return this.decodeImage(null, length, decoderOptions);
|
||||||
}
|
}
|
||||||
return this.getBytes(length, decoderOptions);
|
return this.getBytes(length, decoderOptions);
|
||||||
}
|
}
|
||||||
const data = await this.stream.asyncGetBytes();
|
const data = await this.stream.asyncGetBytes();
|
||||||
return this.decodeImage(data, decoderOptions);
|
return this.decodeImage(data, length, decoderOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncGetBytesFromDecompressionStream(name) {
|
||||||
|
this.stream.reset();
|
||||||
|
const bytes = this.stream.isAsync
|
||||||
|
? await this.stream.asyncGetBytes()
|
||||||
|
: this.stream.getBytes();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { readable, writable } = new DecompressionStream(name);
|
||||||
|
const writer = writable.getWriter();
|
||||||
|
await writer.ready;
|
||||||
|
|
||||||
|
// We can't await writer.write() because it'll block until the reader
|
||||||
|
// starts which happens few lines below.
|
||||||
|
writer
|
||||||
|
.write(bytes)
|
||||||
|
.then(async () => {
|
||||||
|
await writer.ready;
|
||||||
|
await writer.close();
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
|
||||||
|
const chunks = [];
|
||||||
|
let totalLength = 0;
|
||||||
|
|
||||||
|
for await (const chunk of readable) {
|
||||||
|
chunks.push(chunk);
|
||||||
|
totalLength += chunk.byteLength;
|
||||||
|
}
|
||||||
|
const data = new Uint8Array(totalLength);
|
||||||
|
let offset = 0;
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
data.set(chunk, offset);
|
||||||
|
offset += chunk.byteLength;
|
||||||
|
}
|
||||||
|
return { decompressed: data, compressed: bytes };
|
||||||
|
} catch {
|
||||||
|
return { decompressed: null, compressed: bytes };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
@ -148,7 +188,7 @@ class DecodeStream extends BaseStream {
|
|||||||
|
|
||||||
class StreamsSequenceStream extends DecodeStream {
|
class StreamsSequenceStream extends DecodeStream {
|
||||||
constructor(streams, onError = null) {
|
constructor(streams, onError = null) {
|
||||||
streams = streams.filter(s => s instanceof BaseStream);
|
streams = streams.filter(s => s instanceof BaseStream && !s.isImageStream);
|
||||||
|
|
||||||
let maybeLength = 0;
|
let maybeLength = 0;
|
||||||
for (const stream of streams) {
|
for (const stream of streams) {
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import {
|
|||||||
stringToBytes,
|
stringToBytes,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
stringToUTF8String,
|
stringToUTF8String,
|
||||||
toHexUtil,
|
|
||||||
unreachable,
|
unreachable,
|
||||||
Util,
|
Util,
|
||||||
warn,
|
warn,
|
||||||
@ -61,6 +60,7 @@ import {
|
|||||||
RefSetCache,
|
RefSetCache,
|
||||||
} from "./primitives.js";
|
} from "./primitives.js";
|
||||||
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
|
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
|
||||||
|
import { NullStream, Stream } from "./stream.js";
|
||||||
import { BaseStream } from "./base_stream.js";
|
import { BaseStream } from "./base_stream.js";
|
||||||
import { calculateMD5 } from "./calculate_md5.js";
|
import { calculateMD5 } from "./calculate_md5.js";
|
||||||
import { Catalog } from "./catalog.js";
|
import { Catalog } from "./catalog.js";
|
||||||
@ -68,7 +68,6 @@ import { clearGlobalCaches } from "./cleanup_helper.js";
|
|||||||
import { DatasetReader } from "./dataset_reader.js";
|
import { DatasetReader } from "./dataset_reader.js";
|
||||||
import { Intersector } from "./intersector.js";
|
import { Intersector } from "./intersector.js";
|
||||||
import { Linearization } from "./parser.js";
|
import { Linearization } from "./parser.js";
|
||||||
import { NullStream } from "./stream.js";
|
|
||||||
import { ObjectLoader } from "./object_loader.js";
|
import { ObjectLoader } from "./object_loader.js";
|
||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { PartialEvaluator } from "./evaluator.js";
|
import { PartialEvaluator } from "./evaluator.js";
|
||||||
@ -270,10 +269,32 @@ class Page {
|
|||||||
async getContentStream() {
|
async getContentStream() {
|
||||||
const content = await this.pdfManager.ensure(this, "content");
|
const content = await this.pdfManager.ensure(this, "content");
|
||||||
|
|
||||||
if (content instanceof BaseStream) {
|
if (content instanceof BaseStream && !content.isImageStream) {
|
||||||
|
if (content.isAsync) {
|
||||||
|
const bytes = await content.asyncGetBytes();
|
||||||
|
if (bytes) {
|
||||||
|
return new Stream(bytes, 0, bytes.length, content.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
if (Array.isArray(content)) {
|
if (Array.isArray(content)) {
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 0, ii = content.length; i < ii; i++) {
|
||||||
|
const item = content[i];
|
||||||
|
if (item instanceof BaseStream && item.isAsync) {
|
||||||
|
promises.push(
|
||||||
|
item.asyncGetBytes().then(bytes => {
|
||||||
|
if (bytes) {
|
||||||
|
content[i] = new Stream(bytes, 0, bytes.length, item.dict);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (promises.length > 0) {
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
return new StreamsSequenceStream(
|
return new StreamsSequenceStream(
|
||||||
content,
|
content,
|
||||||
this.#onSubStreamError.bind(this)
|
this.#onSubStreamError.bind(this)
|
||||||
@ -442,6 +463,8 @@ class Page {
|
|||||||
task,
|
task,
|
||||||
intent,
|
intent,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
|
pageId = this.pageIndex,
|
||||||
|
pageIndex = this.pageIndex,
|
||||||
annotationStorage = null,
|
annotationStorage = null,
|
||||||
modifiedIds = null,
|
modifiedIds = null,
|
||||||
}) {
|
}) {
|
||||||
@ -527,13 +550,12 @@ class Page {
|
|||||||
RESOURCES_KEYS_OPERATOR_LIST
|
RESOURCES_KEYS_OPERATOR_LIST
|
||||||
);
|
);
|
||||||
const opList = new OperatorList(intent, sink);
|
const opList = new OperatorList(intent, sink);
|
||||||
|
|
||||||
handler.send("StartRenderPage", {
|
handler.send("StartRenderPage", {
|
||||||
transparency: partialEvaluator.hasBlendModes(
|
transparency: partialEvaluator.hasBlendModes(
|
||||||
resources,
|
resources,
|
||||||
this.nonBlendModesSet
|
this.nonBlendModesSet
|
||||||
),
|
),
|
||||||
pageIndex: this.pageIndex,
|
pageIndex,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1605,8 +1627,8 @@ class PDFDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return shadow(this, "fingerprints", [
|
return shadow(this, "fingerprints", [
|
||||||
toHexUtil(hashOriginal),
|
hashOriginal.toHex(),
|
||||||
hashModified ? toHexUtil(hashModified) : null,
|
hashModified?.toHex() ?? null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -470,6 +470,8 @@ class PDFEditor {
|
|||||||
* included ranges (inclusive) or indices.
|
* included ranges (inclusive) or indices.
|
||||||
* @property {Array<Array<number>|number>} [excludePages]
|
* @property {Array<Array<number>|number>} [excludePages]
|
||||||
* excluded ranges (inclusive) or indices.
|
* excluded ranges (inclusive) or indices.
|
||||||
|
* @property {Array<number>} [pageIndices]
|
||||||
|
* position of the pages in the final document.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -482,10 +484,18 @@ class PDFEditor {
|
|||||||
let newIndex = 0;
|
let newIndex = 0;
|
||||||
this.hasSingleFile = pageInfos.length === 1;
|
this.hasSingleFile = pageInfos.length === 1;
|
||||||
const allDocumentData = [];
|
const allDocumentData = [];
|
||||||
for (const { document, includePages, excludePages } of pageInfos) {
|
for (const {
|
||||||
|
document,
|
||||||
|
includePages,
|
||||||
|
excludePages,
|
||||||
|
pageIndices,
|
||||||
|
} of pageInfos) {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (pageIndices) {
|
||||||
|
newIndex = -1;
|
||||||
|
}
|
||||||
const documentData = new DocumentData(document);
|
const documentData = new DocumentData(document);
|
||||||
allDocumentData.push(documentData);
|
allDocumentData.push(documentData);
|
||||||
promises.push(this.#collectDocumentData(documentData));
|
promises.push(this.#collectDocumentData(documentData));
|
||||||
@ -504,6 +514,7 @@ class PDFEditor {
|
|||||||
(deletedIndices ||= new Set()).add(page);
|
(deletedIndices ||= new Set()).add(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let pageIndex = 0;
|
||||||
for (let i = 0, ii = document.numPages; i < ii; i++) {
|
for (let i = 0, ii = document.numPages; i < ii; i++) {
|
||||||
if (deletedIndices?.has(i)) {
|
if (deletedIndices?.has(i)) {
|
||||||
continue;
|
continue;
|
||||||
@ -539,7 +550,23 @@ class PDFEditor {
|
|||||||
if (!takePage) {
|
if (!takePage) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const newPageIndex = newIndex++;
|
let newPageIndex;
|
||||||
|
if (pageIndices) {
|
||||||
|
newPageIndex = pageIndices[pageIndex++];
|
||||||
|
}
|
||||||
|
if (newPageIndex === undefined) {
|
||||||
|
if (newIndex !== -1) {
|
||||||
|
newPageIndex = newIndex++;
|
||||||
|
} else {
|
||||||
|
for (
|
||||||
|
newPageIndex = 0;
|
||||||
|
this.oldPages[newPageIndex] === undefined;
|
||||||
|
newPageIndex++
|
||||||
|
) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
promises.push(
|
promises.push(
|
||||||
document.getPage(i).then(page => {
|
document.getPage(i).then(page => {
|
||||||
this.oldPages[newPageIndex] = new PageData(page, documentData);
|
this.oldPages[newPageIndex] = new PageData(page, documentData);
|
||||||
|
|||||||
@ -1039,6 +1039,7 @@ class PartialEvaluator {
|
|||||||
if (
|
if (
|
||||||
isAddToPathSet ||
|
isAddToPathSet ||
|
||||||
state.fillColorSpace.name === "Pattern" ||
|
state.fillColorSpace.name === "Pattern" ||
|
||||||
|
state.strokeColorSpace.name === "Pattern" ||
|
||||||
font.disableFontFace
|
font.disableFontFace
|
||||||
) {
|
) {
|
||||||
PartialEvaluator.buildFontPaths(
|
PartialEvaluator.buildFontPaths(
|
||||||
@ -1705,7 +1706,7 @@ class PartialEvaluator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOperatorList({
|
async getOperatorList({
|
||||||
stream,
|
stream,
|
||||||
task,
|
task,
|
||||||
resources,
|
resources,
|
||||||
@ -1714,6 +1715,13 @@ class PartialEvaluator {
|
|||||||
fallbackFontDict = null,
|
fallbackFontDict = null,
|
||||||
prevRefs = null,
|
prevRefs = null,
|
||||||
}) {
|
}) {
|
||||||
|
if (stream.isAsync) {
|
||||||
|
const bytes = await stream.asyncGetBytes();
|
||||||
|
if (bytes) {
|
||||||
|
stream = new Stream(bytes, 0, bytes.length, stream.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const objId = stream.dict?.objId;
|
const objId = stream.dict?.objId;
|
||||||
const seenRefs = new RefSet(prevRefs);
|
const seenRefs = new RefSet(prevRefs);
|
||||||
|
|
||||||
@ -2372,7 +2380,7 @@ class PartialEvaluator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTextContent({
|
async getTextContent({
|
||||||
stream,
|
stream,
|
||||||
task,
|
task,
|
||||||
resources,
|
resources,
|
||||||
@ -2388,6 +2396,13 @@ class PartialEvaluator {
|
|||||||
prevRefs = null,
|
prevRefs = null,
|
||||||
intersector = null,
|
intersector = null,
|
||||||
}) {
|
}) {
|
||||||
|
if (stream.isAsync) {
|
||||||
|
const bytes = await stream.asyncGetBytes();
|
||||||
|
if (bytes) {
|
||||||
|
stream = new Stream(bytes, 0, bytes.length, stream.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const objId = stream.dict?.objId;
|
const objId = stream.dict?.objId;
|
||||||
const seenRefs = new RefSet(prevRefs);
|
const seenRefs = new RefSet(prevRefs);
|
||||||
|
|
||||||
@ -2523,7 +2538,7 @@ class PartialEvaluator {
|
|||||||
|
|
||||||
const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
|
const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
|
||||||
|
|
||||||
let textState;
|
let textState, currentTextState;
|
||||||
|
|
||||||
function pushWhitespace({
|
function pushWhitespace({
|
||||||
width = 0,
|
width = 0,
|
||||||
@ -2785,7 +2800,9 @@ class PartialEvaluator {
|
|||||||
|
|
||||||
// When the total height of the current chunk is negative
|
// When the total height of the current chunk is negative
|
||||||
// then we're writing from bottom to top.
|
// then we're writing from bottom to top.
|
||||||
const textOrientation = Math.sign(textContentItem.height);
|
const textOrientation = Math.sign(
|
||||||
|
textContentItem.height || textContentItem.totalHeight
|
||||||
|
);
|
||||||
if (advanceY < textOrientation * textContentItem.negativeSpaceMax) {
|
if (advanceY < textOrientation * textContentItem.negativeSpaceMax) {
|
||||||
if (
|
if (
|
||||||
Math.abs(advanceX) >
|
Math.abs(advanceX) >
|
||||||
@ -2849,7 +2866,9 @@ class PartialEvaluator {
|
|||||||
|
|
||||||
// When the total width of the current chunk is negative
|
// When the total width of the current chunk is negative
|
||||||
// then we're writing from right to left.
|
// then we're writing from right to left.
|
||||||
const textOrientation = Math.sign(textContentItem.width);
|
const textOrientation = Math.sign(
|
||||||
|
textContentItem.width || textContentItem.totalWidth
|
||||||
|
);
|
||||||
if (advanceX < textOrientation * textContentItem.negativeSpaceMax) {
|
if (advanceX < textOrientation * textContentItem.negativeSpaceMax) {
|
||||||
if (
|
if (
|
||||||
Math.abs(advanceY) >
|
Math.abs(advanceY) >
|
||||||
@ -2907,6 +2926,15 @@ class PartialEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildTextContentItem({ chars, extraSpacing }) {
|
function buildTextContentItem({ chars, extraSpacing }) {
|
||||||
|
if (
|
||||||
|
currentTextState !== textState &&
|
||||||
|
(currentTextState.fontName !== textState.fontName ||
|
||||||
|
currentTextState.fontSize !== textState.fontSize)
|
||||||
|
) {
|
||||||
|
flushTextContentItem();
|
||||||
|
currentTextState = textState.clone();
|
||||||
|
}
|
||||||
|
|
||||||
const font = textState.font;
|
const font = textState.font;
|
||||||
if (!chars) {
|
if (!chars) {
|
||||||
// Just move according to the space we have.
|
// Just move according to the space we have.
|
||||||
@ -3162,8 +3190,8 @@ class PartialEvaluator {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousState = textState;
|
|
||||||
textState = stateManager.state;
|
textState = stateManager.state;
|
||||||
|
currentTextState ||= textState.clone();
|
||||||
const fn = operation.fn;
|
const fn = operation.fn;
|
||||||
args = operation.args;
|
args = operation.args;
|
||||||
|
|
||||||
@ -3180,7 +3208,6 @@ class PartialEvaluator {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
flushTextContentItem();
|
|
||||||
textState.fontName = fontNameArg;
|
textState.fontName = fontNameArg;
|
||||||
textState.fontSize = fontSizeArg;
|
textState.fontSize = fontSizeArg;
|
||||||
next(handleSetFont(fontNameArg, null));
|
next(handleSetFont(fontNameArg, null));
|
||||||
@ -3537,14 +3564,10 @@ class PartialEvaluator {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPS.restore:
|
case OPS.restore:
|
||||||
if (
|
stateManager.restore();
|
||||||
previousState &&
|
break;
|
||||||
(previousState.font !== textState.font ||
|
case OPS.save:
|
||||||
previousState.fontSize !== textState.fontSize ||
|
stateManager.save();
|
||||||
previousState.fontName !== textState.fontName)
|
|
||||||
) {
|
|
||||||
flushTextContentItem();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
} // switch
|
} // switch
|
||||||
if (textContent.items.length >= (sink?.desiredSize ?? 1)) {
|
if (textContent.items.length >= (sink?.desiredSize ?? 1)) {
|
||||||
@ -4564,8 +4587,16 @@ class PartialEvaluator {
|
|||||||
if (fontFile) {
|
if (fontFile) {
|
||||||
if (!(fontFile instanceof BaseStream)) {
|
if (!(fontFile instanceof BaseStream)) {
|
||||||
throw new FormatError("FontFile should be a stream");
|
throw new FormatError("FontFile should be a stream");
|
||||||
} else if (fontFile.isEmpty) {
|
} else {
|
||||||
throw new FormatError("FontFile is empty");
|
if (fontFile.isAsync) {
|
||||||
|
const bytes = await fontFile.asyncGetBytes();
|
||||||
|
if (bytes) {
|
||||||
|
fontFile = new Stream(bytes, 0, bytes.length, fontFile.dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fontFile.isEmpty) {
|
||||||
|
throw new FormatError("FontFile is empty");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -5060,7 +5091,7 @@ class TextState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
const clone = Object.create(this);
|
const clone = Object.assign(Object.create(this), this);
|
||||||
clone.textMatrix = this.textMatrix.slice();
|
clone.textMatrix = this.textMatrix.slice();
|
||||||
clone.textLineMatrix = this.textLineMatrix.slice();
|
clone.textLineMatrix = this.textLineMatrix.slice();
|
||||||
clone.fontMatrix = this.fontMatrix.slice();
|
clone.fontMatrix = this.fontMatrix.slice();
|
||||||
|
|||||||
@ -13,26 +13,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { shadow, stringToPDFString, warn } from "../shared/util.js";
|
import { stringToPDFString, warn } from "../shared/util.js";
|
||||||
import { BaseStream } from "./base_stream.js";
|
import { BaseStream } from "./base_stream.js";
|
||||||
import { Dict } from "./primitives.js";
|
import { Dict } from "./primitives.js";
|
||||||
|
|
||||||
function pickPlatformItem(dict) {
|
function pickPlatformItem(dict) {
|
||||||
if (!(dict instanceof Dict)) {
|
if (dict instanceof Dict) {
|
||||||
return null;
|
// Look for the filename in this order: UF, F, Unix, Mac, DOS
|
||||||
}
|
for (const key of ["UF", "F", "Unix", "Mac", "DOS"]) {
|
||||||
// Look for the filename in this order:
|
if (dict.has(key)) {
|
||||||
// UF, F, Unix, Mac, DOS
|
return dict.get(key);
|
||||||
if (dict.has("UF")) {
|
}
|
||||||
return dict.get("UF");
|
}
|
||||||
} else if (dict.has("F")) {
|
|
||||||
return dict.get("F");
|
|
||||||
} else if (dict.has("Unix")) {
|
|
||||||
return dict.get("Unix");
|
|
||||||
} else if (dict.has("Mac")) {
|
|
||||||
return dict.get("Mac");
|
|
||||||
} else if (dict.has("DOS")) {
|
|
||||||
return dict.get("DOS");
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -51,11 +43,10 @@ function stripPath(str) {
|
|||||||
class FileSpec {
|
class FileSpec {
|
||||||
#contentAvailable = false;
|
#contentAvailable = false;
|
||||||
|
|
||||||
constructor(root, xref, skipContent = false) {
|
constructor(root, skipContent = false) {
|
||||||
if (!(root instanceof Dict)) {
|
if (!(root instanceof Dict)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.xref = xref;
|
|
||||||
this.root = root;
|
this.root = root;
|
||||||
if (root.has("FS")) {
|
if (root.has("FS")) {
|
||||||
this.fs = root.get("FS");
|
this.fs = root.get("FS");
|
||||||
@ -73,56 +64,46 @@ class FileSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get filename() {
|
get filename() {
|
||||||
let filename = "";
|
|
||||||
|
|
||||||
const item = pickPlatformItem(this.root);
|
const item = pickPlatformItem(this.root);
|
||||||
if (item && typeof item === "string") {
|
if (item && typeof item === "string") {
|
||||||
filename = stringToPDFString(item, /* keepEscapeSequence = */ true)
|
// NOTE: The following replacement order is INTENTIONAL, regardless of
|
||||||
|
// what some static code analysers (e.g. CodeQL) may claim.
|
||||||
|
return stringToPDFString(item, /* keepEscapeSequence = */ true)
|
||||||
.replaceAll("\\\\", "\\")
|
.replaceAll("\\\\", "\\")
|
||||||
.replaceAll("\\/", "/")
|
.replaceAll("\\/", "/")
|
||||||
.replaceAll("\\", "/");
|
.replaceAll("\\", "/");
|
||||||
}
|
}
|
||||||
return shadow(this, "filename", filename || "unnamed");
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
get content() {
|
get content() {
|
||||||
if (!this.#contentAvailable) {
|
if (!this.#contentAvailable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this._contentRef ||= pickPlatformItem(this.root?.get("EF"));
|
const ef = pickPlatformItem(this.root?.get("EF"));
|
||||||
|
|
||||||
let content = null;
|
if (ef instanceof BaseStream) {
|
||||||
if (this._contentRef) {
|
return ef.getBytes();
|
||||||
const fileObj = this.xref.fetchIfRef(this._contentRef);
|
|
||||||
if (fileObj instanceof BaseStream) {
|
|
||||||
content = fileObj.getBytes();
|
|
||||||
} else {
|
|
||||||
warn(
|
|
||||||
"Embedded file specification points to non-existing/invalid content"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn("Embedded file specification does not have any content");
|
|
||||||
}
|
}
|
||||||
return content;
|
warn("Embedded file specification points to non-existing/invalid content");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get description() {
|
get description() {
|
||||||
let description = "";
|
|
||||||
|
|
||||||
const desc = this.root?.get("Desc");
|
const desc = this.root?.get("Desc");
|
||||||
if (desc && typeof desc === "string") {
|
if (desc && typeof desc === "string") {
|
||||||
description = stringToPDFString(desc);
|
return stringToPDFString(desc);
|
||||||
}
|
}
|
||||||
return shadow(this, "description", description);
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
get serializable() {
|
get serializable() {
|
||||||
|
const { filename, content, description } = this;
|
||||||
return {
|
return {
|
||||||
rawFilename: this.filename,
|
rawFilename: filename,
|
||||||
filename: stripPath(this.filename),
|
filename: stripPath(filename) || "unnamed",
|
||||||
content: this.content,
|
content,
|
||||||
description: this.description,
|
description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,6 +122,8 @@ const fixedDistCodeTab = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
class FlateStream extends DecodeStream {
|
class FlateStream extends DecodeStream {
|
||||||
|
#isAsync = true;
|
||||||
|
|
||||||
constructor(str, maybeLength) {
|
constructor(str, maybeLength) {
|
||||||
super(maybeLength);
|
super(maybeLength);
|
||||||
|
|
||||||
@ -161,58 +163,30 @@ class FlateStream extends DecodeStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async asyncGetBytes() {
|
async asyncGetBytes() {
|
||||||
this.stream.reset();
|
const { decompressed, compressed } =
|
||||||
const bytes = this.stream.getBytes();
|
await this.asyncGetBytesFromDecompressionStream("deflate");
|
||||||
|
if (decompressed) {
|
||||||
try {
|
return decompressed;
|
||||||
const { readable, writable } = new DecompressionStream("deflate");
|
|
||||||
const writer = writable.getWriter();
|
|
||||||
await writer.ready;
|
|
||||||
|
|
||||||
// We can't await writer.write() because it'll block until the reader
|
|
||||||
// starts which happens few lines below.
|
|
||||||
writer
|
|
||||||
.write(bytes)
|
|
||||||
.then(async () => {
|
|
||||||
await writer.ready;
|
|
||||||
await writer.close();
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
|
|
||||||
const chunks = [];
|
|
||||||
let totalLength = 0;
|
|
||||||
|
|
||||||
for await (const chunk of readable) {
|
|
||||||
chunks.push(chunk);
|
|
||||||
totalLength += chunk.byteLength;
|
|
||||||
}
|
|
||||||
const data = new Uint8Array(totalLength);
|
|
||||||
let offset = 0;
|
|
||||||
for (const chunk of chunks) {
|
|
||||||
data.set(chunk, offset);
|
|
||||||
offset += chunk.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
} catch {
|
|
||||||
// DecompressionStream failed (for example because there are some extra
|
|
||||||
// bytes after the end of the compressed data), so we fallback to our
|
|
||||||
// decoder.
|
|
||||||
// We already get the bytes from the underlying stream, so we just reuse
|
|
||||||
// them to avoid get them again.
|
|
||||||
this.stream = new Stream(
|
|
||||||
bytes,
|
|
||||||
2 /* = header size (see ctor) */,
|
|
||||||
bytes.length,
|
|
||||||
this.stream.dict
|
|
||||||
);
|
|
||||||
this.reset();
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
// DecompressionStream failed (for example because there are some extra
|
||||||
|
// bytes after the end of the compressed data), so we fallback to our
|
||||||
|
// decoder.
|
||||||
|
// We already get the bytes from the underlying stream, so we just reuse
|
||||||
|
// them to avoid get them again.
|
||||||
|
|
||||||
|
this.#isAsync = false;
|
||||||
|
this.stream = new Stream(
|
||||||
|
compressed,
|
||||||
|
2 /* = header size (see ctor) */,
|
||||||
|
compressed.length,
|
||||||
|
this.stream.dict
|
||||||
|
);
|
||||||
|
this.reset();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAsync() {
|
get isAsync() {
|
||||||
return true;
|
return this.#isAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBits(bits) {
|
getBits(bits) {
|
||||||
|
|||||||
@ -369,7 +369,7 @@ class PDFImage {
|
|||||||
const inverseDecode = decode?.[0] > 0;
|
const inverseDecode = decode?.[0] > 0;
|
||||||
|
|
||||||
const computedLength = ((width + 7) >> 3) * height;
|
const computedLength = ((width + 7) >> 3) * height;
|
||||||
const imgArray = image.getBytes(computedLength);
|
const imgArray = await image.getImageData(computedLength);
|
||||||
|
|
||||||
const isSingleOpaquePixel =
|
const isSingleOpaquePixel =
|
||||||
width === 1 &&
|
width === 1 &&
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const MIN_IMAGE_DIM = 2048;
|
|||||||
// In Chrome, there aren't max dimensions but only a max area. So an image with
|
// In Chrome, there aren't max dimensions but only a max area. So an image with
|
||||||
// a very large dimensions is acceptable but it probably doesn't hurt to reduce
|
// a very large dimensions is acceptable but it probably doesn't hurt to reduce
|
||||||
// it when considering that it will finally rendered on a small canvas.
|
// it when considering that it will finally rendered on a small canvas.
|
||||||
const MAX_IMAGE_DIM = 65537;
|
const MAX_IMAGE_DIM = 32768;
|
||||||
const MAX_ERROR = 128;
|
const MAX_ERROR = 128;
|
||||||
|
|
||||||
// Large images are encoded in using the BMP format (it's a way faster than
|
// Large images are encoded in using the BMP format (it's a way faster than
|
||||||
|
|||||||
142
src/core/jbig2_ccittFax_wasm.js
Normal file
142
src/core/jbig2_ccittFax_wasm.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/* Copyright 2026 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseException, warn } from "../shared/util.js";
|
||||||
|
import { fetchBinaryData } from "./core_utils.js";
|
||||||
|
import JBig2 from "../../external/jbig2/jbig2.js";
|
||||||
|
|
||||||
|
class JBig2Error extends BaseException {
|
||||||
|
constructor(msg) {
|
||||||
|
super(msg, "Jbig2Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JBig2CCITTFaxWasmImage {
|
||||||
|
static #buffer = null;
|
||||||
|
|
||||||
|
static #handler = null;
|
||||||
|
|
||||||
|
static #modulePromise = null;
|
||||||
|
|
||||||
|
static #useWasm = true;
|
||||||
|
|
||||||
|
static #useWorkerFetch = true;
|
||||||
|
|
||||||
|
static #wasmUrl = null;
|
||||||
|
|
||||||
|
static setOptions({ handler, useWasm, useWorkerFetch, wasmUrl }) {
|
||||||
|
this.#useWasm = useWasm;
|
||||||
|
this.#useWorkerFetch = useWorkerFetch;
|
||||||
|
this.#wasmUrl = wasmUrl;
|
||||||
|
|
||||||
|
if (!useWorkerFetch) {
|
||||||
|
this.#handler = handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #instantiateWasm(fallbackCallback, imports, successCallback) {
|
||||||
|
const filename = "jbig2.wasm";
|
||||||
|
try {
|
||||||
|
if (!this.#buffer) {
|
||||||
|
if (this.#useWorkerFetch) {
|
||||||
|
this.#buffer = await fetchBinaryData(`${this.#wasmUrl}${filename}`);
|
||||||
|
} else {
|
||||||
|
this.#buffer = await this.#handler.sendWithPromise(
|
||||||
|
"FetchBinaryData",
|
||||||
|
{ type: "wasmFactory", filename }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const results = await WebAssembly.instantiate(this.#buffer, imports);
|
||||||
|
return successCallback(results.instance);
|
||||||
|
} catch (reason) {
|
||||||
|
warn(`JBig2Image#instantiateWasm: ${reason}`);
|
||||||
|
return fallbackCallback(null);
|
||||||
|
} finally {
|
||||||
|
this.#handler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async decode(bytes, width, height, globals, CCITTOptions) {
|
||||||
|
if (!this.#modulePromise) {
|
||||||
|
const { promise, resolve } = Promise.withResolvers();
|
||||||
|
const promises = [promise];
|
||||||
|
if (this.#useWasm) {
|
||||||
|
promises.push(
|
||||||
|
JBig2({
|
||||||
|
warn,
|
||||||
|
instantiateWasm: this.#instantiateWasm.bind(this, resolve),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
this.#modulePromise = Promise.race(promises);
|
||||||
|
}
|
||||||
|
const module = await this.#modulePromise;
|
||||||
|
if (!module) {
|
||||||
|
throw new JBig2Error("JBig2 failed to initialize");
|
||||||
|
}
|
||||||
|
let ptr, globalsPtr;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const size = bytes.length;
|
||||||
|
ptr = module._malloc(size);
|
||||||
|
module.writeArrayToMemory(bytes, ptr);
|
||||||
|
|
||||||
|
if (CCITTOptions) {
|
||||||
|
module._ccitt_decode(
|
||||||
|
ptr,
|
||||||
|
size,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
CCITTOptions.K,
|
||||||
|
CCITTOptions.EndOfLine ? 1 : 0,
|
||||||
|
CCITTOptions.EncodedByteAlign ? 1 : 0,
|
||||||
|
CCITTOptions.BlackIs1 ? 1 : 0,
|
||||||
|
CCITTOptions.Columns,
|
||||||
|
CCITTOptions.Rows
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const globalsSize = globals ? globals.length : 0;
|
||||||
|
if (globalsSize > 0) {
|
||||||
|
globalsPtr = module._malloc(globalsSize);
|
||||||
|
module.writeArrayToMemory(globals, globalsPtr);
|
||||||
|
}
|
||||||
|
module._jbig2_decode(ptr, size, width, height, globalsPtr, globalsSize);
|
||||||
|
}
|
||||||
|
if (!module.imageData) {
|
||||||
|
throw new JBig2Error("Unknown error");
|
||||||
|
}
|
||||||
|
const { imageData } = module;
|
||||||
|
module.imageData = null;
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
} finally {
|
||||||
|
if (ptr) {
|
||||||
|
module._free(ptr);
|
||||||
|
}
|
||||||
|
if (globalsPtr) {
|
||||||
|
module._free(globalsPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cleanup() {
|
||||||
|
this.#modulePromise = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { JBig2CCITTFaxWasmImage, JBig2Error };
|
||||||
@ -13,11 +13,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { shadow, warn } from "../shared/util.js";
|
||||||
import { BaseStream } from "./base_stream.js";
|
import { BaseStream } from "./base_stream.js";
|
||||||
import { DecodeStream } from "./decode_stream.js";
|
import { DecodeStream } from "./decode_stream.js";
|
||||||
import { Dict } from "./primitives.js";
|
import { Dict } from "./primitives.js";
|
||||||
|
import { JBig2CCITTFaxWasmImage } from "./jbig2_ccittFax_wasm.js";
|
||||||
import { Jbig2Image } from "./jbig2.js";
|
import { Jbig2Image } from "./jbig2.js";
|
||||||
import { shadow } from "../shared/util.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For JBIG2's we use a library to decode these images and
|
* For JBIG2's we use a library to decode these images and
|
||||||
@ -44,10 +45,47 @@ class Jbig2Stream extends DecodeStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readBlock() {
|
readBlock() {
|
||||||
this.decodeImage();
|
this.decodeImageFallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeImage(bytes) {
|
get isAsyncDecoder() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isImageStream() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async decodeImage(bytes, length, _decoderOptions) {
|
||||||
|
if (this.eof) {
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
bytes ||= this.bytes;
|
||||||
|
try {
|
||||||
|
let globals = null;
|
||||||
|
if (this.params instanceof Dict) {
|
||||||
|
const globalsStream = this.params.get("JBIG2Globals");
|
||||||
|
if (globalsStream instanceof BaseStream) {
|
||||||
|
globals = globalsStream.getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.buffer = await JBig2CCITTFaxWasmImage.decode(
|
||||||
|
bytes,
|
||||||
|
this.dict.get("Width"),
|
||||||
|
this.dict.get("Height"),
|
||||||
|
globals
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
warn("Jbig2Stream: Falling back to JS JBIG2 decoder.");
|
||||||
|
return this.decodeImageFallback(bytes, length);
|
||||||
|
}
|
||||||
|
this.bufferLength = this.buffer.length;
|
||||||
|
this.eof = true;
|
||||||
|
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeImageFallback(bytes, _length) {
|
||||||
if (this.eof) {
|
if (this.eof) {
|
||||||
return this.buffer;
|
return this.buffer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -194,6 +194,10 @@ class JpegStream extends DecodeStream {
|
|||||||
decoder?.close();
|
decoder?.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isImageStream() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { JpegStream };
|
export { JpegStream };
|
||||||
|
|||||||
@ -49,7 +49,7 @@ class JpxStream extends DecodeStream {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async decodeImage(bytes, decoderOptions) {
|
async decodeImage(bytes, _length, decoderOptions) {
|
||||||
if (this.eof) {
|
if (this.eof) {
|
||||||
return this.buffer;
|
return this.buffer;
|
||||||
}
|
}
|
||||||
@ -64,6 +64,10 @@ class JpxStream extends DecodeStream {
|
|||||||
get canAsyncDecodeImageFromBuffer() {
|
get canAsyncDecodeImageFromBuffer() {
|
||||||
return this.stream.isAsync;
|
return this.stream.isAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isImageStream() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { JpxStream };
|
export { JpxStream };
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import {
|
|||||||
import { NullStream, Stream } from "./stream.js";
|
import { NullStream, Stream } from "./stream.js";
|
||||||
import { Ascii85Stream } from "./ascii_85_stream.js";
|
import { Ascii85Stream } from "./ascii_85_stream.js";
|
||||||
import { AsciiHexStream } from "./ascii_hex_stream.js";
|
import { AsciiHexStream } from "./ascii_hex_stream.js";
|
||||||
|
import { BrotliStream } from "./brotli_stream.js";
|
||||||
import { CCITTFaxStream } from "./ccitt_stream.js";
|
import { CCITTFaxStream } from "./ccitt_stream.js";
|
||||||
import { FlateStream } from "./flate_stream.js";
|
import { FlateStream } from "./flate_stream.js";
|
||||||
import { Jbig2Stream } from "./jbig2_stream.js";
|
import { Jbig2Stream } from "./jbig2_stream.js";
|
||||||
@ -822,6 +823,8 @@ class Parser {
|
|||||||
return new RunLengthStream(stream, maybeLength);
|
return new RunLengthStream(stream, maybeLength);
|
||||||
case "JBIG2Decode":
|
case "JBIG2Decode":
|
||||||
return new Jbig2Stream(stream, maybeLength, params);
|
return new Jbig2Stream(stream, maybeLength, params);
|
||||||
|
case "BrotliDecode":
|
||||||
|
return new BrotliStream(stream, maybeLength);
|
||||||
}
|
}
|
||||||
warn(`Filter "${name}" is not supported.`);
|
warn(`Filter "${name}" is not supported.`);
|
||||||
return stream;
|
return stream;
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
import { ChunkedStreamManager } from "./chunked_stream.js";
|
import { ChunkedStreamManager } from "./chunked_stream.js";
|
||||||
import { ImageResizer } from "./image_resizer.js";
|
import { ImageResizer } from "./image_resizer.js";
|
||||||
|
import { JBig2CCITTFaxWasmImage } from "./jbig2_ccittFax_wasm.js";
|
||||||
import { JpegStream } from "./jpeg_stream.js";
|
import { JpegStream } from "./jpeg_stream.js";
|
||||||
import { JpxImage } from "./jpx.js";
|
import { JpxImage } from "./jpx.js";
|
||||||
import { MissingDataException } from "./core_utils.js";
|
import { MissingDataException } from "./core_utils.js";
|
||||||
@ -81,6 +82,7 @@ class BasePdfManager {
|
|||||||
JpxImage.setOptions(options);
|
JpxImage.setOptions(options);
|
||||||
IccColorSpace.setOptions(options);
|
IccColorSpace.setOptions(options);
|
||||||
CmykICCBasedCS.setOptions(options);
|
CmykICCBasedCS.setOptions(options);
|
||||||
|
JBig2CCITTFaxWasmImage.setOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
get docId() {
|
get docId() {
|
||||||
|
|||||||
@ -51,6 +51,7 @@ class Stream extends BaseStream {
|
|||||||
const strEnd = this.end;
|
const strEnd = this.end;
|
||||||
|
|
||||||
if (!length) {
|
if (!length) {
|
||||||
|
this.pos = strEnd;
|
||||||
return bytes.subarray(pos, strEnd);
|
return bytes.subarray(pos, strEnd);
|
||||||
}
|
}
|
||||||
let end = pos + length;
|
let end = pos + length;
|
||||||
|
|||||||
@ -219,23 +219,23 @@ class WorkerMessageHandler {
|
|||||||
|
|
||||||
return new LocalPdfManager(pdfManagerArgs);
|
return new LocalPdfManager(pdfManagerArgs);
|
||||||
}
|
}
|
||||||
const pdfStream = new PDFWorkerStream(handler),
|
const pdfStream = new PDFWorkerStream({ msgHandler: handler }),
|
||||||
fullRequest = pdfStream.getFullReader();
|
fullReader = pdfStream.getFullReader();
|
||||||
|
|
||||||
const pdfManagerCapability = Promise.withResolvers();
|
const pdfManagerCapability = Promise.withResolvers();
|
||||||
let newPdfManager,
|
let newPdfManager,
|
||||||
cachedChunks = [],
|
cachedChunks = [],
|
||||||
loaded = 0;
|
loaded = 0;
|
||||||
|
|
||||||
fullRequest.headersReady
|
fullReader.headersReady
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (!fullRequest.isRangeSupported) {
|
if (!fullReader.isRangeSupported) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pdfManagerArgs.source = pdfStream;
|
pdfManagerArgs.source = pdfStream;
|
||||||
pdfManagerArgs.length = fullRequest.contentLength;
|
pdfManagerArgs.length = fullReader.contentLength;
|
||||||
// We don't need auto-fetch when streaming is enabled.
|
// We don't need auto-fetch when streaming is enabled.
|
||||||
pdfManagerArgs.disableAutoFetch ||= fullRequest.isStreamingSupported;
|
pdfManagerArgs.disableAutoFetch ||= fullReader.isStreamingSupported;
|
||||||
|
|
||||||
newPdfManager = new NetworkPdfManager(pdfManagerArgs);
|
newPdfManager = new NetworkPdfManager(pdfManagerArgs);
|
||||||
// There may be a chance that `newPdfManager` is not initialized for
|
// There may be a chance that `newPdfManager` is not initialized for
|
||||||
@ -282,10 +282,10 @@ class WorkerMessageHandler {
|
|||||||
}
|
}
|
||||||
loaded += value.byteLength;
|
loaded += value.byteLength;
|
||||||
|
|
||||||
if (!fullRequest.isStreamingSupported) {
|
if (!fullReader.isStreamingSupported) {
|
||||||
handler.send("DocProgress", {
|
handler.send("DocProgress", {
|
||||||
loaded,
|
loaded,
|
||||||
total: Math.max(loaded, fullRequest.contentLength || 0),
|
total: Math.max(loaded, fullReader.contentLength || 0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,12 +294,12 @@ class WorkerMessageHandler {
|
|||||||
} else {
|
} else {
|
||||||
cachedChunks.push(value);
|
cachedChunks.push(value);
|
||||||
}
|
}
|
||||||
fullRequest.read().then(readChunk, reject);
|
fullReader.read().then(readChunk, reject);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fullRequest.read().then(readChunk, reject);
|
fullReader.read().then(readChunk, reject);
|
||||||
}).catch(function (e) {
|
}).catch(function (e) {
|
||||||
pdfManagerCapability.reject(e);
|
pdfManagerCapability.reject(e);
|
||||||
cancelXHRs = null;
|
cancelXHRs = null;
|
||||||
@ -319,7 +319,9 @@ class WorkerMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onFailure(ex) {
|
function onFailure(ex) {
|
||||||
ensureNotTerminated();
|
if (terminated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ex instanceof PasswordException) {
|
if (ex instanceof PasswordException) {
|
||||||
const task = new WorkerTask(`PasswordException: response ${ex.code}`);
|
const task = new WorkerTask(`PasswordException: response ${ex.code}`);
|
||||||
@ -853,8 +855,8 @@ class WorkerMessageHandler {
|
|||||||
);
|
);
|
||||||
|
|
||||||
handler.on("GetOperatorList", function (data, sink) {
|
handler.on("GetOperatorList", function (data, sink) {
|
||||||
const pageIndex = data.pageIndex;
|
const { pageId, pageIndex } = data;
|
||||||
pdfManager.getPage(pageIndex).then(function (page) {
|
pdfManager.getPage(pageId).then(function (page) {
|
||||||
const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);
|
const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);
|
||||||
startWorkerTask(task);
|
startWorkerTask(task);
|
||||||
|
|
||||||
@ -871,6 +873,7 @@ class WorkerMessageHandler {
|
|||||||
cacheKey: data.cacheKey,
|
cacheKey: data.cacheKey,
|
||||||
annotationStorage: data.annotationStorage,
|
annotationStorage: data.annotationStorage,
|
||||||
modifiedIds: data.modifiedIds,
|
modifiedIds: data.modifiedIds,
|
||||||
|
pageIndex,
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
function (operatorListInfo) {
|
function (operatorListInfo) {
|
||||||
@ -899,9 +902,10 @@ class WorkerMessageHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
handler.on("GetTextContent", function (data, sink) {
|
handler.on("GetTextContent", function (data, sink) {
|
||||||
const { pageIndex, includeMarkedContent, disableNormalization } = data;
|
const { pageId, pageIndex, includeMarkedContent, disableNormalization } =
|
||||||
|
data;
|
||||||
|
|
||||||
pdfManager.getPage(pageIndex).then(function (page) {
|
pdfManager.getPage(pageId).then(function (page) {
|
||||||
const task = new WorkerTask("GetTextContent: page " + pageIndex);
|
const task = new WorkerTask("GetTextContent: page " + pageIndex);
|
||||||
startWorkerTask(task);
|
startWorkerTask(task);
|
||||||
|
|
||||||
|
|||||||
@ -13,77 +13,35 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from "../shared/util.js";
|
import {
|
||||||
|
BasePDFStream,
|
||||||
|
BasePDFStreamRangeReader,
|
||||||
|
BasePDFStreamReader,
|
||||||
|
} from "../shared/base_pdf_stream.js";
|
||||||
|
|
||||||
/** @implements {IPDFStream} */
|
class PDFWorkerStream extends BasePDFStream {
|
||||||
class PDFWorkerStream {
|
constructor(source) {
|
||||||
constructor(msgHandler) {
|
super(source, PDFWorkerStreamReader, PDFWorkerStreamRangeReader);
|
||||||
this._msgHandler = msgHandler;
|
|
||||||
this._contentLength = null;
|
|
||||||
this._fullRequestReader = null;
|
|
||||||
this._rangeRequestReaders = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getFullReader() {
|
|
||||||
assert(
|
|
||||||
!this._fullRequestReader,
|
|
||||||
"PDFWorkerStream.getFullReader can only be called once."
|
|
||||||
);
|
|
||||||
this._fullRequestReader = new PDFWorkerStreamReader(this._msgHandler);
|
|
||||||
return this._fullRequestReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRangeReader(begin, end) {
|
|
||||||
const reader = new PDFWorkerStreamRangeReader(begin, end, this._msgHandler);
|
|
||||||
this._rangeRequestReaders.push(reader);
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAllRequests(reason) {
|
|
||||||
this._fullRequestReader?.cancel(reason);
|
|
||||||
|
|
||||||
for (const reader of this._rangeRequestReaders.slice(0)) {
|
|
||||||
reader.cancel(reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamReader} */
|
class PDFWorkerStreamReader extends BasePDFStreamReader {
|
||||||
class PDFWorkerStreamReader {
|
_reader = null;
|
||||||
constructor(msgHandler) {
|
|
||||||
this._msgHandler = msgHandler;
|
|
||||||
this.onProgress = null;
|
|
||||||
|
|
||||||
this._contentLength = null;
|
constructor(stream) {
|
||||||
this._isRangeSupported = false;
|
super(stream);
|
||||||
this._isStreamingSupported = false;
|
const { msgHandler } = stream._source;
|
||||||
|
|
||||||
const readableStream = this._msgHandler.sendWithStream("GetReader");
|
const readableStream = msgHandler.sendWithStream("GetReader");
|
||||||
this._reader = readableStream.getReader();
|
this._reader = readableStream.getReader();
|
||||||
|
|
||||||
this._headersReady = this._msgHandler
|
msgHandler.sendWithPromise("ReaderHeadersReady").then(data => {
|
||||||
.sendWithPromise("ReaderHeadersReady")
|
this._contentLength = data.contentLength;
|
||||||
.then(data => {
|
this._isStreamingSupported = data.isStreamingSupported;
|
||||||
this._isStreamingSupported = data.isStreamingSupported;
|
this._isRangeSupported = data.isRangeSupported;
|
||||||
this._isRangeSupported = data.isRangeSupported;
|
|
||||||
this._contentLength = data.contentLength;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get headersReady() {
|
this._headersCapability.resolve();
|
||||||
return this._headersReady;
|
}, this._headersCapability.reject);
|
||||||
}
|
|
||||||
|
|
||||||
get contentLength() {
|
|
||||||
return this._contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._isStreamingSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRangeSupported() {
|
|
||||||
return this._isRangeSupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
@ -101,23 +59,20 @@ class PDFWorkerStreamReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamRangeReader} */
|
class PDFWorkerStreamRangeReader extends BasePDFStreamRangeReader {
|
||||||
class PDFWorkerStreamRangeReader {
|
_reader = null;
|
||||||
constructor(begin, end, msgHandler) {
|
|
||||||
this._msgHandler = msgHandler;
|
|
||||||
this.onProgress = null;
|
|
||||||
|
|
||||||
const readableStream = this._msgHandler.sendWithStream("GetRangeReader", {
|
constructor(stream, begin, end) {
|
||||||
|
super(stream, begin, end);
|
||||||
|
const { msgHandler } = stream._source;
|
||||||
|
|
||||||
|
const readableStream = msgHandler.sendWithStream("GetRangeReader", {
|
||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
});
|
});
|
||||||
this._reader = readableStream.getReader();
|
this._reader = readableStream.getReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
const { value, done } = await this._reader.read();
|
const { value, done } = await this._reader.read();
|
||||||
if (done) {
|
if (done) {
|
||||||
|
|||||||
@ -90,7 +90,6 @@ import {
|
|||||||
XFAObject,
|
XFAObject,
|
||||||
XFAObjectArray,
|
XFAObjectArray,
|
||||||
} from "./xfa_object.js";
|
} from "./xfa_object.js";
|
||||||
import { fromBase64Util, Util, warn } from "../../shared/util.js";
|
|
||||||
import {
|
import {
|
||||||
getBBox,
|
getBBox,
|
||||||
getColor,
|
getColor,
|
||||||
@ -103,6 +102,7 @@ import {
|
|||||||
getStringOption,
|
getStringOption,
|
||||||
HTMLResult,
|
HTMLResult,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
|
import { Util, warn } from "../../shared/util.js";
|
||||||
import { getMetrics } from "./fonts.js";
|
import { getMetrics } from "./fonts.js";
|
||||||
import { recoverJsURL } from "../core_utils.js";
|
import { recoverJsURL } from "../core_utils.js";
|
||||||
import { searchNode } from "./som.js";
|
import { searchNode } from "./som.js";
|
||||||
@ -3420,7 +3420,7 @@ class Image extends StringObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!buffer && this.transferEncoding === "base64") {
|
if (!buffer && this.transferEncoding === "base64") {
|
||||||
buffer = fromBase64Util(this[$content]);
|
buffer = Uint8Array.fromBase64(this[$content]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
|
|||||||
@ -18,9 +18,6 @@
|
|||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../../web/text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
/** @typedef {import("../../web/text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../../web/interfaces").IDownloadManager} IDownloadManager */
|
|
||||||
/** @typedef {import("../../web/interfaces").IPDFLinkService} IPDFLinkService */
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../../web/struct_tree_layer_builder.js").StructTreeLayerBuilder} StructTreeLayerBuilder */
|
/** @typedef {import("../../web/struct_tree_layer_builder.js").StructTreeLayerBuilder} StructTreeLayerBuilder */
|
||||||
@ -57,8 +54,8 @@ const TIMEZONE_OFFSET = new Date().getTimezoneOffset() * 60 * 1000;
|
|||||||
* @typedef {Object} AnnotationElementParameters
|
* @typedef {Object} AnnotationElementParameters
|
||||||
* @property {Object} data
|
* @property {Object} data
|
||||||
* @property {HTMLDivElement} layer
|
* @property {HTMLDivElement} layer
|
||||||
* @property {IPDFLinkService} linkService
|
* @property {PDFLinkService} linkService
|
||||||
* @property {IDownloadManager} [downloadManager]
|
* @property {BaseDownloadManager} [downloadManager]
|
||||||
* @property {AnnotationStorage} [annotationStorage]
|
* @property {AnnotationStorage} [annotationStorage]
|
||||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||||
* for annotation icons. Include trailing slash.
|
* for annotation icons. Include trailing slash.
|
||||||
@ -293,7 +290,7 @@ class AnnotationElement {
|
|||||||
this.annotationStorage.setValue(`${AnnotationEditorPrefix}${data.id}`, {
|
this.annotationStorage.setValue(`${AnnotationEditorPrefix}${data.id}`, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
annotationType: data.annotationType,
|
annotationType: data.annotationType,
|
||||||
pageIndex: this.parent.page._pageIndex,
|
page: this.parent.page,
|
||||||
popup,
|
popup,
|
||||||
popupRef: data.popupRef,
|
popupRef: data.popupRef,
|
||||||
modificationDate: new Date(),
|
modificationDate: new Date(),
|
||||||
@ -3736,8 +3733,8 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
|||||||
* @property {HTMLDivElement} div
|
* @property {HTMLDivElement} div
|
||||||
* @property {Array} annotations
|
* @property {Array} annotations
|
||||||
* @property {PDFPageProxy} page
|
* @property {PDFPageProxy} page
|
||||||
* @property {IPDFLinkService} linkService
|
* @property {PDFLinkService} linkService
|
||||||
* @property {IDownloadManager} [downloadManager]
|
* @property {BaseDownloadManager} [downloadManager]
|
||||||
* @property {AnnotationStorage} [annotationStorage]
|
* @property {AnnotationStorage} [annotationStorage]
|
||||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||||
* for annotation icons. Include trailing slash.
|
* for annotation icons. Include trailing slash.
|
||||||
@ -4018,8 +4015,6 @@ class AnnotationLayer {
|
|||||||
* Add link annotations to the annotation layer.
|
* Add link annotations to the annotation layer.
|
||||||
*
|
*
|
||||||
* @param {Array<Object>} annotations
|
* @param {Array<Object>} annotations
|
||||||
* @param {IPDFLinkService} linkService
|
|
||||||
* @memberof AnnotationLayer
|
|
||||||
*/
|
*/
|
||||||
async addLinkAnnotations(annotations) {
|
async addLinkAnnotations(annotations) {
|
||||||
const elementParams = {
|
const elementParams = {
|
||||||
|
|||||||
@ -196,6 +196,10 @@ class AnnotationStorage {
|
|||||||
val instanceof AnnotationEditor
|
val instanceof AnnotationEditor
|
||||||
? val.serialize(/* isForCopying = */ false, context)
|
? val.serialize(/* isForCopying = */ false, context)
|
||||||
: val;
|
: val;
|
||||||
|
if (val.page) {
|
||||||
|
val.pageIndex = val.page._pageIndex;
|
||||||
|
delete val.page;
|
||||||
|
}
|
||||||
if (serialized) {
|
if (serialized) {
|
||||||
map.set(key, serialized);
|
map.set(key, serialized);
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import {
|
|||||||
getVerbosityLevel,
|
getVerbosityLevel,
|
||||||
info,
|
info,
|
||||||
isNodeJS,
|
isNodeJS,
|
||||||
|
MathClamp,
|
||||||
RenderingIntentFlag,
|
RenderingIntentFlag,
|
||||||
setVerbosityLevel,
|
setVerbosityLevel,
|
||||||
shadow,
|
shadow,
|
||||||
@ -40,6 +41,7 @@ import {
|
|||||||
deprecated,
|
deprecated,
|
||||||
isDataScheme,
|
isDataScheme,
|
||||||
isValidFetchUrl,
|
isValidFetchUrl,
|
||||||
|
PagesMapper,
|
||||||
PageViewport,
|
PageViewport,
|
||||||
RenderingCancelledException,
|
RenderingCancelledException,
|
||||||
StatTimer,
|
StatTimer,
|
||||||
@ -439,6 +441,7 @@ function getDocument(src = {}) {
|
|||||||
ownerDocument,
|
ownerDocument,
|
||||||
pdfBug,
|
pdfBug,
|
||||||
styleElement,
|
styleElement,
|
||||||
|
enableHWA,
|
||||||
loadingParams: {
|
loadingParams: {
|
||||||
disableAutoFetch,
|
disableAutoFetch,
|
||||||
enableXfa,
|
enableXfa,
|
||||||
@ -462,7 +465,8 @@ function getDocument(src = {}) {
|
|||||||
|
|
||||||
let networkStream;
|
let networkStream;
|
||||||
if (rangeTransport) {
|
if (rangeTransport) {
|
||||||
networkStream = new PDFDataTransportStream(rangeTransport, {
|
networkStream = new PDFDataTransportStream({
|
||||||
|
pdfDataRangeTransport: rangeTransport,
|
||||||
disableRange,
|
disableRange,
|
||||||
disableStream,
|
disableStream,
|
||||||
});
|
});
|
||||||
@ -507,8 +511,7 @@ function getDocument(src = {}) {
|
|||||||
task,
|
task,
|
||||||
networkStream,
|
networkStream,
|
||||||
transportParams,
|
transportParams,
|
||||||
transportFactory,
|
transportFactory
|
||||||
enableHWA
|
|
||||||
);
|
);
|
||||||
task._transport = transport;
|
task._transport = transport;
|
||||||
messageHandler.send("Ready", null);
|
messageHandler.send("Ready", null);
|
||||||
@ -523,6 +526,8 @@ function getDocument(src = {}) {
|
|||||||
* @typedef {Object} OnProgressParameters
|
* @typedef {Object} OnProgressParameters
|
||||||
* @property {number} loaded - Currently loaded number of bytes.
|
* @property {number} loaded - Currently loaded number of bytes.
|
||||||
* @property {number} total - Total number of bytes in the PDF file.
|
* @property {number} total - Total number of bytes in the PDF file.
|
||||||
|
* @property {number} percent - Currently loaded percentage, as an integer value
|
||||||
|
* in the [0, 100] range. If `total` is undefined, the percentage is `NaN`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -634,8 +639,6 @@ class PDFDataRangeTransport {
|
|||||||
|
|
||||||
#progressiveReadListeners = [];
|
#progressiveReadListeners = [];
|
||||||
|
|
||||||
#progressListeners = [];
|
|
||||||
|
|
||||||
#rangeListeners = [];
|
#rangeListeners = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -654,6 +657,18 @@ class PDFDataRangeTransport {
|
|||||||
this.initialData = initialData;
|
this.initialData = initialData;
|
||||||
this.progressiveDone = progressiveDone;
|
this.progressiveDone = progressiveDone;
|
||||||
this.contentDispositionFilename = contentDispositionFilename;
|
this.contentDispositionFilename = contentDispositionFilename;
|
||||||
|
|
||||||
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
|
Object.defineProperty(this, "onDataProgress", {
|
||||||
|
value: () => {
|
||||||
|
deprecated(
|
||||||
|
"`PDFDataRangeTransport.prototype.onDataProgress` - method was " +
|
||||||
|
"removed, since loading progress is now reported automatically " +
|
||||||
|
"through the `PDFDataTransportStream` class (and related code)."
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -663,13 +678,6 @@ class PDFDataRangeTransport {
|
|||||||
this.#rangeListeners.push(listener);
|
this.#rangeListeners.push(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {function} listener
|
|
||||||
*/
|
|
||||||
addProgressListener(listener) {
|
|
||||||
this.#progressListeners.push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} listener
|
* @param {function} listener
|
||||||
*/
|
*/
|
||||||
@ -694,18 +702,6 @@ class PDFDataRangeTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} loaded
|
|
||||||
* @param {number|undefined} total
|
|
||||||
*/
|
|
||||||
onDataProgress(loaded, total) {
|
|
||||||
this.#capability.promise.then(() => {
|
|
||||||
for (const listener of this.#progressListeners) {
|
|
||||||
listener(loaded, total);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array|null} chunk
|
* @param {Uint8Array|null} chunk
|
||||||
*/
|
*/
|
||||||
@ -1328,6 +1324,8 @@ class PDFDocumentProxy {
|
|||||||
class PDFPageProxy {
|
class PDFPageProxy {
|
||||||
#pendingCleanup = false;
|
#pendingCleanup = false;
|
||||||
|
|
||||||
|
#pagesMapper = PagesMapper.instance;
|
||||||
|
|
||||||
constructor(pageIndex, pageInfo, transport, pdfBug = false) {
|
constructor(pageIndex, pageInfo, transport, pdfBug = false) {
|
||||||
this._pageIndex = pageIndex;
|
this._pageIndex = pageIndex;
|
||||||
this._pageInfo = pageInfo;
|
this._pageInfo = pageInfo;
|
||||||
@ -1350,6 +1348,13 @@ class PDFPageProxy {
|
|||||||
return this._pageIndex + 1;
|
return this._pageIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} value - The page number to set. First page is 1.
|
||||||
|
*/
|
||||||
|
set pageNumber(value) {
|
||||||
|
this._pageIndex = value - 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number} The number of degrees the page is rotated clockwise.
|
* @type {number} The number of degrees the page is rotated clockwise.
|
||||||
*/
|
*/
|
||||||
@ -1699,6 +1704,7 @@ class PDFPageProxy {
|
|||||||
return this._transport.messageHandler.sendWithStream(
|
return this._transport.messageHandler.sendWithStream(
|
||||||
"GetTextContent",
|
"GetTextContent",
|
||||||
{
|
{
|
||||||
|
pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1,
|
||||||
pageIndex: this._pageIndex,
|
pageIndex: this._pageIndex,
|
||||||
includeMarkedContent: includeMarkedContent === true,
|
includeMarkedContent: includeMarkedContent === true,
|
||||||
disableNormalization: disableNormalization === true,
|
disableNormalization: disableNormalization === true,
|
||||||
@ -1884,6 +1890,7 @@ class PDFPageProxy {
|
|||||||
const readableStream = this._transport.messageHandler.sendWithStream(
|
const readableStream = this._transport.messageHandler.sendWithStream(
|
||||||
"GetOperatorList",
|
"GetOperatorList",
|
||||||
{
|
{
|
||||||
|
pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1,
|
||||||
pageIndex: this._pageIndex,
|
pageIndex: this._pageIndex,
|
||||||
intent: renderingIntent,
|
intent: renderingIntent,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
@ -2379,8 +2386,14 @@ class PDFWorker {
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
class WorkerTransport {
|
class WorkerTransport {
|
||||||
|
downloadInfoCapability = Promise.withResolvers();
|
||||||
|
|
||||||
|
#fullReader = null;
|
||||||
|
|
||||||
#methodPromises = new Map();
|
#methodPromises = new Map();
|
||||||
|
|
||||||
|
#networkStream = null;
|
||||||
|
|
||||||
#pageCache = new Map();
|
#pageCache = new Map();
|
||||||
|
|
||||||
#pagePromises = new Map();
|
#pagePromises = new Map();
|
||||||
@ -2389,21 +2402,19 @@ class WorkerTransport {
|
|||||||
|
|
||||||
#passwordCapability = null;
|
#passwordCapability = null;
|
||||||
|
|
||||||
constructor(
|
#pagesMapper = PagesMapper.instance;
|
||||||
messageHandler,
|
|
||||||
loadingTask,
|
constructor(messageHandler, loadingTask, networkStream, params, factory) {
|
||||||
networkStream,
|
|
||||||
params,
|
|
||||||
factory,
|
|
||||||
enableHWA
|
|
||||||
) {
|
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
this.loadingTask = loadingTask;
|
this.loadingTask = loadingTask;
|
||||||
|
this.#networkStream = networkStream;
|
||||||
|
|
||||||
this.commonObjs = new PDFObjects();
|
this.commonObjs = new PDFObjects();
|
||||||
this.fontLoader = new FontLoader({
|
this.fontLoader = new FontLoader({
|
||||||
ownerDocument: params.ownerDocument,
|
ownerDocument: params.ownerDocument,
|
||||||
styleElement: params.styleElement,
|
styleElement: params.styleElement,
|
||||||
});
|
});
|
||||||
|
this.enableHWA = params.enableHWA;
|
||||||
this.loadingParams = params.loadingParams;
|
this.loadingParams = params.loadingParams;
|
||||||
this._params = params;
|
this._params = params;
|
||||||
|
|
||||||
@ -2416,14 +2427,10 @@ class WorkerTransport {
|
|||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
this.destroyCapability = null;
|
this.destroyCapability = null;
|
||||||
|
|
||||||
this._networkStream = networkStream;
|
|
||||||
this._fullReader = null;
|
|
||||||
this._lastProgress = null;
|
|
||||||
this.downloadInfoCapability = Promise.withResolvers();
|
|
||||||
this.enableHWA = enableHWA;
|
|
||||||
|
|
||||||
this.setupMessageHandler();
|
this.setupMessageHandler();
|
||||||
|
|
||||||
|
this.#pagesMapper.addListener(this.#updateCaches.bind(this));
|
||||||
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
// For testing purposes.
|
// For testing purposes.
|
||||||
Object.defineProperty(this, "getNetworkStreamName", {
|
Object.defineProperty(this, "getNetworkStreamName", {
|
||||||
@ -2448,6 +2455,24 @@ class WorkerTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#updateCaches() {
|
||||||
|
const newPageCache = new Map();
|
||||||
|
const newPromiseCache = new Map();
|
||||||
|
for (let i = 0, ii = this.#pagesMapper.pagesNumber; i < ii; i++) {
|
||||||
|
const prevPageIndex = this.#pagesMapper.getPrevPageNumber(i + 1) - 1;
|
||||||
|
const page = this.#pageCache.get(prevPageIndex);
|
||||||
|
if (page) {
|
||||||
|
newPageCache.set(i, page);
|
||||||
|
}
|
||||||
|
const promise = this.#pagePromises.get(prevPageIndex);
|
||||||
|
if (promise) {
|
||||||
|
newPromiseCache.set(i, promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.#pageCache = newPageCache;
|
||||||
|
this.#pagePromises = newPromiseCache;
|
||||||
|
}
|
||||||
|
|
||||||
#cacheSimpleMethod(name, data = null) {
|
#cacheSimpleMethod(name, data = null) {
|
||||||
const cachedPromise = this.#methodPromises.get(name);
|
const cachedPromise = this.#methodPromises.get(name);
|
||||||
if (cachedPromise) {
|
if (cachedPromise) {
|
||||||
@ -2459,6 +2484,14 @@ class WorkerTransport {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onProgress({ loaded, total }) {
|
||||||
|
this.loadingTask.onProgress?.({
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
percent: MathClamp(Math.round((loaded / total) * 100), 0, 100),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get annotationStorage() {
|
get annotationStorage() {
|
||||||
return shadow(this, "annotationStorage", new AnnotationStorage());
|
return shadow(this, "annotationStorage", new AnnotationStorage());
|
||||||
}
|
}
|
||||||
@ -2570,7 +2603,7 @@ class WorkerTransport {
|
|||||||
this.filterFactory.destroy();
|
this.filterFactory.destroy();
|
||||||
TextLayer.cleanup();
|
TextLayer.cleanup();
|
||||||
|
|
||||||
this._networkStream?.cancelAllRequests(
|
this.#networkStream?.cancelAllRequests(
|
||||||
new AbortException("Worker was terminated.")
|
new AbortException("Worker was terminated.")
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2587,18 +2620,16 @@ class WorkerTransport {
|
|||||||
|
|
||||||
messageHandler.on("GetReader", (data, sink) => {
|
messageHandler.on("GetReader", (data, sink) => {
|
||||||
assert(
|
assert(
|
||||||
this._networkStream,
|
this.#networkStream,
|
||||||
"GetReader - no `IPDFStream` instance available."
|
"GetReader - no `BasePDFStream` instance available."
|
||||||
);
|
);
|
||||||
this._fullReader = this._networkStream.getFullReader();
|
this.#fullReader = this.#networkStream.getFullReader();
|
||||||
this._fullReader.onProgress = evt => {
|
// If stream or range turn out to be disabled, once `headersReady` is
|
||||||
this._lastProgress = {
|
// resolved, this is our only way to report loading progress.
|
||||||
loaded: evt.loaded,
|
this.#fullReader.onProgress = evt => this.#onProgress(evt);
|
||||||
total: evt.total,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
sink.onPull = () => {
|
sink.onPull = () => {
|
||||||
this._fullReader
|
this.#fullReader
|
||||||
.read()
|
.read()
|
||||||
.then(function ({ value, done }) {
|
.then(function ({ value, done }) {
|
||||||
if (done) {
|
if (done) {
|
||||||
@ -2619,7 +2650,7 @@ class WorkerTransport {
|
|||||||
};
|
};
|
||||||
|
|
||||||
sink.onCancel = reason => {
|
sink.onCancel = reason => {
|
||||||
this._fullReader.cancel(reason);
|
this.#fullReader.cancel(reason);
|
||||||
|
|
||||||
sink.ready.catch(readyReason => {
|
sink.ready.catch(readyReason => {
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
@ -2631,40 +2662,29 @@ class WorkerTransport {
|
|||||||
});
|
});
|
||||||
|
|
||||||
messageHandler.on("ReaderHeadersReady", async data => {
|
messageHandler.on("ReaderHeadersReady", async data => {
|
||||||
await this._fullReader.headersReady;
|
await this.#fullReader.headersReady;
|
||||||
|
|
||||||
const { isStreamingSupported, isRangeSupported, contentLength } =
|
const { isStreamingSupported, isRangeSupported, contentLength } =
|
||||||
this._fullReader;
|
this.#fullReader;
|
||||||
|
|
||||||
// If stream or range are disabled, it's our only way to report
|
if (isStreamingSupported && isRangeSupported) {
|
||||||
// loading progress.
|
this.#fullReader.onProgress = null; // See comment in "GetReader" above.
|
||||||
if (!isStreamingSupported || !isRangeSupported) {
|
|
||||||
if (this._lastProgress) {
|
|
||||||
loadingTask.onProgress?.(this._lastProgress);
|
|
||||||
}
|
|
||||||
this._fullReader.onProgress = evt => {
|
|
||||||
loadingTask.onProgress?.({
|
|
||||||
loaded: evt.loaded,
|
|
||||||
total: evt.total,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { isStreamingSupported, isRangeSupported, contentLength };
|
return { isStreamingSupported, isRangeSupported, contentLength };
|
||||||
});
|
});
|
||||||
|
|
||||||
messageHandler.on("GetRangeReader", (data, sink) => {
|
messageHandler.on("GetRangeReader", (data, sink) => {
|
||||||
assert(
|
assert(
|
||||||
this._networkStream,
|
this.#networkStream,
|
||||||
"GetRangeReader - no `IPDFStream` instance available."
|
"GetRangeReader - no `BasePDFStream` instance available."
|
||||||
);
|
);
|
||||||
const rangeReader = this._networkStream.getRangeReader(
|
const rangeReader = this.#networkStream.getRangeReader(
|
||||||
data.begin,
|
data.begin,
|
||||||
data.end
|
data.end
|
||||||
);
|
);
|
||||||
|
|
||||||
// When streaming is enabled, it's possible that the data requested here
|
// When streaming is enabled, it's possible that the data requested here
|
||||||
// has already been fetched via the `_fullRequestReader` implementation.
|
// has already been fetched via the `#fullReader` implementation.
|
||||||
// However, given that the PDF data is loaded asynchronously on the
|
// However, given that the PDF data is loaded asynchronously on the
|
||||||
// main-thread and then sent via `postMessage` to the worker-thread,
|
// main-thread and then sent via `postMessage` to the worker-thread,
|
||||||
// it may not have been available during parsing (hence the attempt to
|
// it may not have been available during parsing (hence the attempt to
|
||||||
@ -2672,7 +2692,7 @@ class WorkerTransport {
|
|||||||
//
|
//
|
||||||
// To avoid wasting time and resources here, we'll thus *not* dispatch
|
// To avoid wasting time and resources here, we'll thus *not* dispatch
|
||||||
// range requests if the data was already loaded but has not been sent to
|
// range requests if the data was already loaded but has not been sent to
|
||||||
// the worker-thread yet (which will happen via the `_fullRequestReader`).
|
// the worker-thread yet (which will happen via the `#fullReader`).
|
||||||
if (!rangeReader) {
|
if (!rangeReader) {
|
||||||
sink.close();
|
sink.close();
|
||||||
return;
|
return;
|
||||||
@ -2710,6 +2730,7 @@ class WorkerTransport {
|
|||||||
});
|
});
|
||||||
|
|
||||||
messageHandler.on("GetDoc", ({ pdfInfo }) => {
|
messageHandler.on("GetDoc", ({ pdfInfo }) => {
|
||||||
|
this.#pagesMapper.pagesNumber = pdfInfo.numPages;
|
||||||
this._numPages = pdfInfo.numPages;
|
this._numPages = pdfInfo.numPages;
|
||||||
this._htmlForXfa = pdfInfo.htmlForXfa;
|
this._htmlForXfa = pdfInfo.htmlForXfa;
|
||||||
delete pdfInfo.htmlForXfa;
|
delete pdfInfo.htmlForXfa;
|
||||||
@ -2745,10 +2766,7 @@ class WorkerTransport {
|
|||||||
messageHandler.on("DataLoaded", data => {
|
messageHandler.on("DataLoaded", data => {
|
||||||
// For consistency: Ensure that progress is always reported when the
|
// For consistency: Ensure that progress is always reported when the
|
||||||
// entire PDF file has been loaded, regardless of how it was fetched.
|
// entire PDF file has been loaded, regardless of how it was fetched.
|
||||||
loadingTask.onProgress?.({
|
this.#onProgress({ loaded: data.length, total: data.length });
|
||||||
loaded: data.length,
|
|
||||||
total: data.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.downloadInfoCapability.resolve(data);
|
this.downloadInfoCapability.resolve(data);
|
||||||
});
|
});
|
||||||
@ -2871,10 +2889,7 @@ class WorkerTransport {
|
|||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
return; // Ignore any pending requests if the worker was terminated.
|
return; // Ignore any pending requests if the worker was terminated.
|
||||||
}
|
}
|
||||||
loadingTask.onProgress?.({
|
this.#onProgress(data);
|
||||||
loaded: data.loaded,
|
|
||||||
total: data.total,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
messageHandler.on("FetchBinaryData", async data => {
|
messageHandler.on("FetchBinaryData", async data => {
|
||||||
@ -2915,7 +2930,7 @@ class WorkerTransport {
|
|||||||
isPureXfa: !!this._htmlForXfa,
|
isPureXfa: !!this._htmlForXfa,
|
||||||
numPages: this._numPages,
|
numPages: this._numPages,
|
||||||
annotationStorage: map,
|
annotationStorage: map,
|
||||||
filename: this._fullReader?.filename ?? null,
|
filename: this.#fullReader?.filename ?? null,
|
||||||
},
|
},
|
||||||
transfer
|
transfer
|
||||||
)
|
)
|
||||||
@ -2932,26 +2947,27 @@ class WorkerTransport {
|
|||||||
if (
|
if (
|
||||||
!Number.isInteger(pageNumber) ||
|
!Number.isInteger(pageNumber) ||
|
||||||
pageNumber <= 0 ||
|
pageNumber <= 0 ||
|
||||||
pageNumber > this._numPages
|
pageNumber > this.#pagesMapper.pagesNumber
|
||||||
) {
|
) {
|
||||||
return Promise.reject(new Error("Invalid page request."));
|
return Promise.reject(new Error("Invalid page request."));
|
||||||
}
|
}
|
||||||
|
const pageIndex = pageNumber - 1;
|
||||||
|
const newPageIndex = this.#pagesMapper.getPageId(pageNumber) - 1;
|
||||||
|
|
||||||
const pageIndex = pageNumber - 1,
|
const cachedPromise = this.#pagePromises.get(pageIndex);
|
||||||
cachedPromise = this.#pagePromises.get(pageIndex);
|
|
||||||
if (cachedPromise) {
|
if (cachedPromise) {
|
||||||
return cachedPromise;
|
return cachedPromise;
|
||||||
}
|
}
|
||||||
const promise = this.messageHandler
|
const promise = this.messageHandler
|
||||||
.sendWithPromise("GetPage", {
|
.sendWithPromise("GetPage", {
|
||||||
pageIndex,
|
pageIndex: newPageIndex,
|
||||||
})
|
})
|
||||||
.then(pageInfo => {
|
.then(pageInfo => {
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
throw new Error("Transport destroyed");
|
throw new Error("Transport destroyed");
|
||||||
}
|
}
|
||||||
if (pageInfo.refStr) {
|
if (pageInfo.refStr) {
|
||||||
this.#pageRefCache.set(pageInfo.refStr, pageNumber);
|
this.#pageRefCache.set(pageInfo.refStr, newPageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = new PDFPageProxy(
|
const page = new PDFPageProxy(
|
||||||
@ -2967,19 +2983,20 @@ class WorkerTransport {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageIndex(ref) {
|
async getPageIndex(ref) {
|
||||||
if (!isRefProxy(ref)) {
|
if (!isRefProxy(ref)) {
|
||||||
return Promise.reject(new Error("Invalid pageIndex request."));
|
throw new Error("Invalid pageIndex request.");
|
||||||
}
|
}
|
||||||
return this.messageHandler.sendWithPromise("GetPageIndex", {
|
const index = await this.messageHandler.sendWithPromise("GetPageIndex", {
|
||||||
num: ref.num,
|
num: ref.num,
|
||||||
gen: ref.gen,
|
gen: ref.gen,
|
||||||
});
|
});
|
||||||
|
return this.#pagesMapper.getPageNumber(index + 1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnnotations(pageIndex, intent) {
|
getAnnotations(pageIndex, intent) {
|
||||||
return this.messageHandler.sendWithPromise("GetAnnotations", {
|
return this.messageHandler.sendWithPromise("GetAnnotations", {
|
||||||
pageIndex,
|
pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1,
|
||||||
intent,
|
intent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -3046,13 +3063,13 @@ class WorkerTransport {
|
|||||||
|
|
||||||
getPageJSActions(pageIndex) {
|
getPageJSActions(pageIndex) {
|
||||||
return this.messageHandler.sendWithPromise("GetPageJSActions", {
|
return this.messageHandler.sendWithPromise("GetPageJSActions", {
|
||||||
pageIndex,
|
pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getStructTree(pageIndex) {
|
getStructTree(pageIndex) {
|
||||||
return this.messageHandler.sendWithPromise("GetStructTree", {
|
return this.messageHandler.sendWithPromise("GetStructTree", {
|
||||||
pageIndex,
|
pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3081,8 +3098,8 @@ class WorkerTransport {
|
|||||||
.then(results => ({
|
.then(results => ({
|
||||||
info: results[0],
|
info: results[0],
|
||||||
metadata: results[1] ? new Metadata(results[1]) : null,
|
metadata: results[1] ? new Metadata(results[1]) : null,
|
||||||
contentDispositionFilename: this._fullReader?.filename ?? null,
|
contentDispositionFilename: this.#fullReader?.filename ?? null,
|
||||||
contentLength: this._fullReader?.contentLength ?? null,
|
contentLength: this.#fullReader?.contentLength ?? null,
|
||||||
hasStructTree: results[2],
|
hasStructTree: results[2],
|
||||||
}));
|
}));
|
||||||
this.#methodPromises.set(name, promise);
|
this.#methodPromises.set(name, promise);
|
||||||
@ -3122,7 +3139,10 @@ class WorkerTransport {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;
|
const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;
|
||||||
return this.#pageRefCache.get(refStr) ?? null;
|
const pageIndex = this.#pageRefCache.get(refStr);
|
||||||
|
return pageIndex >= 0
|
||||||
|
? this.#pagesMapper.getPageNumber(pageIndex + 1)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3130,7 +3150,7 @@ class WorkerTransport {
|
|||||||
* Allows controlling of the rendering tasks.
|
* Allows controlling of the rendering tasks.
|
||||||
*/
|
*/
|
||||||
class RenderTask {
|
class RenderTask {
|
||||||
#internalRenderTask = null;
|
_internalRenderTask = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for incremental rendering -- a function that will be called
|
* Callback for incremental rendering -- a function that will be called
|
||||||
@ -3151,12 +3171,12 @@ class RenderTask {
|
|||||||
onError = null;
|
onError = null;
|
||||||
|
|
||||||
constructor(internalRenderTask) {
|
constructor(internalRenderTask) {
|
||||||
this.#internalRenderTask = internalRenderTask;
|
this._internalRenderTask = internalRenderTask;
|
||||||
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
// For testing purposes.
|
// For testing purposes.
|
||||||
Object.defineProperty(this, "getOperatorList", {
|
Object.defineProperty(this, "getOperatorList", {
|
||||||
value: () => this.#internalRenderTask.operatorList,
|
value: () => this._internalRenderTask.operatorList,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3166,7 +3186,7 @@ class RenderTask {
|
|||||||
* @type {Promise<void>}
|
* @type {Promise<void>}
|
||||||
*/
|
*/
|
||||||
get promise() {
|
get promise() {
|
||||||
return this.#internalRenderTask.capability.promise;
|
return this._internalRenderTask.capability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3177,7 +3197,7 @@ class RenderTask {
|
|||||||
* @param {number} [extraDelay]
|
* @param {number} [extraDelay]
|
||||||
*/
|
*/
|
||||||
cancel(extraDelay = 0) {
|
cancel(extraDelay = 0) {
|
||||||
this.#internalRenderTask.cancel(/* error = */ null, extraDelay);
|
this._internalRenderTask.cancel(/* error = */ null, extraDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3185,11 +3205,11 @@ class RenderTask {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
get separateAnnots() {
|
get separateAnnots() {
|
||||||
const { separateAnnots } = this.#internalRenderTask.operatorList;
|
const { separateAnnots } = this._internalRenderTask.operatorList;
|
||||||
if (!separateAnnots) {
|
if (!separateAnnots) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { annotationCanvasMap } = this.#internalRenderTask;
|
const { annotationCanvasMap } = this._internalRenderTask;
|
||||||
return (
|
return (
|
||||||
separateAnnots.form ||
|
separateAnnots.form ||
|
||||||
(separateAnnots.canvas && annotationCanvasMap?.size > 0)
|
(separateAnnots.canvas && annotationCanvasMap?.size > 0)
|
||||||
@ -3389,7 +3409,6 @@ class InternalRenderTask {
|
|||||||
if (this.operatorList.lastChunk) {
|
if (this.operatorList.lastChunk) {
|
||||||
this.gfx.endDrawing();
|
this.gfx.endDrawing();
|
||||||
InternalRenderTask.#canvasInUse.delete(this._canvas);
|
InternalRenderTask.#canvasInUse.delete(this._canvas);
|
||||||
|
|
||||||
this.callback();
|
this.callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ function getUrlProp(val) {
|
|||||||
return null; // The 'url' is unused with `PDFDataRangeTransport`.
|
return null; // The 'url' is unused with `PDFDataRangeTransport`.
|
||||||
}
|
}
|
||||||
if (val instanceof URL) {
|
if (val instanceof URL) {
|
||||||
return val.href;
|
return val;
|
||||||
}
|
}
|
||||||
if (typeof val === "string") {
|
if (typeof val === "string") {
|
||||||
if (
|
if (
|
||||||
@ -33,13 +33,18 @@ function getUrlProp(val) {
|
|||||||
PDFJSDev.test("GENERIC") &&
|
PDFJSDev.test("GENERIC") &&
|
||||||
isNodeJS
|
isNodeJS
|
||||||
) {
|
) {
|
||||||
return val; // Use the url as-is in Node.js environments.
|
if (/^[a-z][a-z0-9\-+.]+:/i.test(val)) {
|
||||||
|
return new URL(val);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const url = process.getBuiltinModule("url");
|
||||||
|
return new URL(url.pathToFileURL(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The full path is required in the 'url' field.
|
// The full path is required in the 'url' field.
|
||||||
const url = URL.parse(val, window.location);
|
const url = URL.parse(val, window.location);
|
||||||
if (url) {
|
if (url) {
|
||||||
return url.href;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
BaseException,
|
BaseException,
|
||||||
DrawOPS,
|
DrawOPS,
|
||||||
FeatureTest,
|
FeatureTest,
|
||||||
|
MathClamp,
|
||||||
shadow,
|
shadow,
|
||||||
Util,
|
Util,
|
||||||
warn,
|
warn,
|
||||||
@ -463,7 +464,7 @@ function isValidFetchUrl(url, baseUrl) {
|
|||||||
}
|
}
|
||||||
const res = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url);
|
const res = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url);
|
||||||
// The Fetch API only supports the http/https protocols, and not file/ftp.
|
// The Fetch API only supports the http/https protocols, and not file/ftp.
|
||||||
return res?.protocol === "http:" || res?.protocol === "https:";
|
return /https?:/.test(res?.protocol ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -798,7 +799,7 @@ class CSSConstants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyOpacity(r, g, b, opacity) {
|
function applyOpacity(r, g, b, opacity) {
|
||||||
opacity = Math.min(Math.max(opacity ?? 1, 0), 1);
|
opacity = MathClamp(opacity ?? 1, 0, 1);
|
||||||
const white = 255 * (1 - opacity);
|
const white = 255 * (1 - opacity);
|
||||||
r = Math.round(r * opacity + white);
|
r = Math.round(r * opacity + white);
|
||||||
g = Math.round(g * opacity + white);
|
g = Math.round(g * opacity + white);
|
||||||
@ -1034,6 +1035,226 @@ function makePathFromDrawOPS(data) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps between page IDs and page numbers, allowing bidirectional conversion
|
||||||
|
* between the two representations. This is useful when the page numbering
|
||||||
|
* in the PDF document doesn't match the default sequential ordering.
|
||||||
|
*/
|
||||||
|
class PagesMapper {
|
||||||
|
/**
|
||||||
|
* Maps page IDs to their corresponding page numbers.
|
||||||
|
* @type {Uint32Array|null}
|
||||||
|
*/
|
||||||
|
static #idToPageNumber = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps page numbers to their corresponding page IDs.
|
||||||
|
* @type {Uint32Array|null}
|
||||||
|
*/
|
||||||
|
static #pageNumberToId = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous mapping of page IDs to page numbers.
|
||||||
|
* @type {Uint32Array|null}
|
||||||
|
*/
|
||||||
|
static #prevIdToPageNumber = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of pages.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
static #pagesNumber = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners for page changes.
|
||||||
|
* @type {Array<function>}
|
||||||
|
*/
|
||||||
|
static #listeners = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total number of pages.
|
||||||
|
* @returns {number} The number of pages.
|
||||||
|
*/
|
||||||
|
get pagesNumber() {
|
||||||
|
return PagesMapper.#pagesNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the total number of pages and initializes default mappings
|
||||||
|
* where page IDs equal page numbers (1-indexed).
|
||||||
|
* @param {number} n - The total number of pages.
|
||||||
|
*/
|
||||||
|
set pagesNumber(n) {
|
||||||
|
if (PagesMapper.#pagesNumber === n) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PagesMapper.#pagesNumber = n;
|
||||||
|
if (n === 0) {
|
||||||
|
PagesMapper.#pageNumberToId = null;
|
||||||
|
PagesMapper.#idToPageNumber = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addListener(listener) {
|
||||||
|
PagesMapper.#listeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeListener(listener) {
|
||||||
|
const index = PagesMapper.#listeners.indexOf(listener);
|
||||||
|
if (index >= 0) {
|
||||||
|
PagesMapper.#listeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateListeners() {
|
||||||
|
for (const listener of PagesMapper.#listeners) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#init(mustInit) {
|
||||||
|
if (PagesMapper.#pageNumberToId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const n = PagesMapper.#pagesNumber;
|
||||||
|
|
||||||
|
// Allocate a single array for better memory locality.
|
||||||
|
const array = new Uint32Array(3 * n);
|
||||||
|
const pageNumberToId = (PagesMapper.#pageNumberToId = array.subarray(0, n));
|
||||||
|
const idToPageNumber = (PagesMapper.#idToPageNumber = array.subarray(
|
||||||
|
n,
|
||||||
|
2 * n
|
||||||
|
));
|
||||||
|
if (mustInit) {
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
pageNumberToId[i] = idToPageNumber[i] = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PagesMapper.#prevIdToPageNumber = array.subarray(2 * n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move a set of pages to a new position while keeping ID→number mappings in
|
||||||
|
* sync.
|
||||||
|
*
|
||||||
|
* @param {Set<number>} selectedPages - Page numbers being moved (1-indexed).
|
||||||
|
* @param {number[]} pagesToMove - Ordered list of page numbers to move.
|
||||||
|
* @param {number} index - Zero-based insertion index in the page-number list.
|
||||||
|
*/
|
||||||
|
movePages(selectedPages, pagesToMove, index) {
|
||||||
|
this.#init(true);
|
||||||
|
const pageNumberToId = PagesMapper.#pageNumberToId;
|
||||||
|
const idToPageNumber = PagesMapper.#idToPageNumber;
|
||||||
|
PagesMapper.#prevIdToPageNumber.set(idToPageNumber);
|
||||||
|
const movedCount = pagesToMove.length;
|
||||||
|
const mappedPagesToMove = new Uint32Array(movedCount);
|
||||||
|
let removedBeforeTarget = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < movedCount; i++) {
|
||||||
|
const pageIndex = pagesToMove[i] - 1;
|
||||||
|
mappedPagesToMove[i] = pageNumberToId[pageIndex];
|
||||||
|
if (pageIndex < index) {
|
||||||
|
removedBeforeTarget += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pagesNumber = PagesMapper.#pagesNumber;
|
||||||
|
// target index after removing elements that were before it
|
||||||
|
let adjustedTarget = index - removedBeforeTarget;
|
||||||
|
const remainingLen = pagesNumber - movedCount;
|
||||||
|
adjustedTarget = MathClamp(adjustedTarget, 0, remainingLen);
|
||||||
|
|
||||||
|
// Create the new mapping.
|
||||||
|
// First copy over the pages that are not being moved.
|
||||||
|
// Then insert the moved pages at the target position.
|
||||||
|
for (let i = 0, r = 0; i < pagesNumber; i++) {
|
||||||
|
if (!selectedPages.has(i + 1)) {
|
||||||
|
pageNumberToId[r++] = pageNumberToId[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift the pages after the target position.
|
||||||
|
pageNumberToId.copyWithin(
|
||||||
|
adjustedTarget + movedCount,
|
||||||
|
adjustedTarget,
|
||||||
|
remainingLen
|
||||||
|
);
|
||||||
|
// Finally insert the moved pages.
|
||||||
|
pageNumberToId.set(mappedPagesToMove, adjustedTarget);
|
||||||
|
|
||||||
|
let hasChanged = false;
|
||||||
|
for (let i = 0, ii = pagesNumber; i < ii; i++) {
|
||||||
|
const id = pageNumberToId[i];
|
||||||
|
hasChanged ||= id !== i + 1;
|
||||||
|
idToPageNumber[id - 1] = i + 1;
|
||||||
|
}
|
||||||
|
this.#updateListeners();
|
||||||
|
|
||||||
|
if (!hasChanged) {
|
||||||
|
// Reset.
|
||||||
|
this.pagesNumber = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the page mappings have been altered from their initial state.
|
||||||
|
* @returns {boolean} True if the mappings have been altered, false otherwise.
|
||||||
|
*/
|
||||||
|
hasBeenAltered() {
|
||||||
|
return PagesMapper.#pageNumberToId !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current page mapping suitable for saving.
|
||||||
|
* @returns {Object} An object containing the page indices.
|
||||||
|
*/
|
||||||
|
getPageMappingForSaving() {
|
||||||
|
// Saving is index-based.
|
||||||
|
return {
|
||||||
|
pageIndices: PagesMapper.#idToPageNumber
|
||||||
|
? PagesMapper.#idToPageNumber.map(x => x - 1)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrevPageNumber(pageNumber) {
|
||||||
|
return PagesMapper.#prevIdToPageNumber[
|
||||||
|
PagesMapper.#pageNumberToId[pageNumber - 1] - 1
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the page number for a given page ID.
|
||||||
|
* @param {number} id - The page ID (1-indexed).
|
||||||
|
* @returns {number} The page number, or the ID itself if no mapping exists.
|
||||||
|
*/
|
||||||
|
getPageNumber(id) {
|
||||||
|
return PagesMapper.#idToPageNumber?.[id - 1] ?? id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the page ID for a given page number.
|
||||||
|
* @param {number} pageNumber - The page number (1-indexed).
|
||||||
|
* @returns {number} The page ID, or the page number itself if no mapping
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
|
getPageId(pageNumber) {
|
||||||
|
return PagesMapper.#pageNumberToId?.[pageNumber - 1] ?? pageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or creates a singleton instance of PagesMapper.
|
||||||
|
* @returns {PagesMapper} The singleton instance.
|
||||||
|
*/
|
||||||
|
static get instance() {
|
||||||
|
return shadow(this, "instance", new PagesMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
getMapping() {
|
||||||
|
return PagesMapper.#pageNumberToId.subarray(0, this.pagesNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
applyOpacity,
|
applyOpacity,
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
@ -1054,6 +1275,7 @@ export {
|
|||||||
makePathFromDrawOPS,
|
makePathFromDrawOPS,
|
||||||
noContextMenu,
|
noContextMenu,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
|
PagesMapper,
|
||||||
PageViewport,
|
PageViewport,
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
PixelsPerInch,
|
PixelsPerInch,
|
||||||
|
|||||||
@ -30,10 +30,6 @@ class DrawLayer {
|
|||||||
|
|
||||||
static #id = 0;
|
static #id = 0;
|
||||||
|
|
||||||
constructor({ pageIndex }) {
|
|
||||||
this.pageIndex = pageIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
setParent(parent) {
|
setParent(parent) {
|
||||||
if (!this.#parent) {
|
if (!this.#parent) {
|
||||||
this.#parent = parent;
|
this.#parent = parent;
|
||||||
@ -103,7 +99,7 @@ class DrawLayer {
|
|||||||
root.append(defs);
|
root.append(defs);
|
||||||
const path = DrawLayer._svgFactory.createElement("path");
|
const path = DrawLayer._svgFactory.createElement("path");
|
||||||
defs.append(path);
|
defs.append(path);
|
||||||
const pathId = `path_p${this.pageIndex}_${id}`;
|
const pathId = `path_${id}`;
|
||||||
path.setAttribute("id", pathId);
|
path.setAttribute("id", pathId);
|
||||||
path.setAttribute("vector-effect", "non-scaling-stroke");
|
path.setAttribute("vector-effect", "non-scaling-stroke");
|
||||||
|
|
||||||
@ -135,7 +131,7 @@ class DrawLayer {
|
|||||||
root.append(defs);
|
root.append(defs);
|
||||||
const path = DrawLayer._svgFactory.createElement("path");
|
const path = DrawLayer._svgFactory.createElement("path");
|
||||||
defs.append(path);
|
defs.append(path);
|
||||||
const pathId = `path_p${this.pageIndex}_${id}`;
|
const pathId = `path_${id}`;
|
||||||
path.setAttribute("id", pathId);
|
path.setAttribute("id", pathId);
|
||||||
path.setAttribute("vector-effect", "non-scaling-stroke");
|
path.setAttribute("vector-effect", "non-scaling-stroke");
|
||||||
|
|
||||||
@ -143,7 +139,7 @@ class DrawLayer {
|
|||||||
if (mustRemoveSelfIntersections) {
|
if (mustRemoveSelfIntersections) {
|
||||||
const mask = DrawLayer._svgFactory.createElement("mask");
|
const mask = DrawLayer._svgFactory.createElement("mask");
|
||||||
defs.append(mask);
|
defs.append(mask);
|
||||||
maskId = `mask_p${this.pageIndex}_${id}`;
|
maskId = `mask_${id}`;
|
||||||
mask.setAttribute("id", maskId);
|
mask.setAttribute("id", maskId);
|
||||||
mask.setAttribute("maskUnits", "objectBoundingBox");
|
mask.setAttribute("maskUnits", "objectBoundingBox");
|
||||||
const rect = DrawLayer._svgFactory.createElement("rect");
|
const rect = DrawLayer._svgFactory.createElement("rect");
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
/** @typedef {import("../display_utils.js").PageViewport} PageViewport */
|
/** @typedef {import("../display_utils.js").PageViewport} PageViewport */
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../../../web/text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
/** @typedef {import("../../../web/text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
||||||
/** @typedef {import("../../../web/interfaces").IL10n} IL10n */
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../annotation_layer.js").AnnotationLayer} AnnotationLayer */
|
/** @typedef {import("../annotation_layer.js").AnnotationLayer} AnnotationLayer */
|
||||||
/** @typedef {import("../draw_layer.js").DrawLayer} DrawLayer */
|
/** @typedef {import("../draw_layer.js").DrawLayer} DrawLayer */
|
||||||
@ -47,7 +46,7 @@ import { StampEditor } from "./stamp.js";
|
|||||||
* @property {boolean} enabled
|
* @property {boolean} enabled
|
||||||
* @property {TextAccessibilityManager} [accessibilityManager]
|
* @property {TextAccessibilityManager} [accessibilityManager]
|
||||||
* @property {number} pageIndex
|
* @property {number} pageIndex
|
||||||
* @property {IL10n} l10n
|
* @property {L10n} l10n
|
||||||
* @property {AnnotationLayer} [annotationLayer]
|
* @property {AnnotationLayer} [annotationLayer]
|
||||||
* @property {HTMLDivElement} [textLayer]
|
* @property {HTMLDivElement} [textLayer]
|
||||||
* @property {DrawLayer} drawLayer
|
* @property {DrawLayer} drawLayer
|
||||||
@ -144,6 +143,10 @@ class AnnotationEditorLayer {
|
|||||||
this.#uiManager.addLayer(this);
|
this.#uiManager.addLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePageIndex(newPageIndex) {
|
||||||
|
this.pageIndex = newPageIndex;
|
||||||
|
}
|
||||||
|
|
||||||
get isEmpty() {
|
get isEmpty() {
|
||||||
return this.#editors.size === 0;
|
return this.#editors.size === 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -314,6 +314,20 @@ class Comment {
|
|||||||
this.#deleted = false;
|
this.#deleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the comment data (used for undo).
|
||||||
|
* @param {Object} data - The comment data to restore.
|
||||||
|
* @param {string} data.text - The comment text.
|
||||||
|
* @param {string|null} data.richText - The rich text content.
|
||||||
|
* @param {Date|null} data.date - The original date.
|
||||||
|
*/
|
||||||
|
restoreData({ text, richText, date }) {
|
||||||
|
this.#text = text;
|
||||||
|
this.#richText = richText;
|
||||||
|
this.#date = date;
|
||||||
|
this.#deleted = false;
|
||||||
|
}
|
||||||
|
|
||||||
setInitialText(text, richText = null) {
|
setInitialText(text, richText = null) {
|
||||||
this.#initialText = text;
|
this.#initialText = text;
|
||||||
this.data = text;
|
this.data = text;
|
||||||
|
|||||||
@ -13,10 +13,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fromBase64Util, toBase64Util, warn } from "../../../shared/util.js";
|
|
||||||
import { ContourDrawOutline } from "./contour.js";
|
import { ContourDrawOutline } from "./contour.js";
|
||||||
import { InkDrawOutline } from "./inkdraw.js";
|
import { InkDrawOutline } from "./inkdraw.js";
|
||||||
import { Outline } from "./outline.js";
|
import { Outline } from "./outline.js";
|
||||||
|
import { warn } from "../../../shared/util.js";
|
||||||
|
|
||||||
const BASE_HEADER_LENGTH = 8;
|
const BASE_HEADER_LENGTH = 8;
|
||||||
const POINTS_PROPERTIES_NUMBER = 3;
|
const POINTS_PROPERTIES_NUMBER = 3;
|
||||||
@ -749,12 +749,12 @@ class SignatureExtractor {
|
|||||||
const buf = await new Response(cs.readable).arrayBuffer();
|
const buf = await new Response(cs.readable).arrayBuffer();
|
||||||
const bytes = new Uint8Array(buf);
|
const bytes = new Uint8Array(buf);
|
||||||
|
|
||||||
return toBase64Util(bytes);
|
return bytes.toBase64();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async decompressSignature(signatureData) {
|
static async decompressSignature(signatureData) {
|
||||||
try {
|
try {
|
||||||
const bytes = fromBase64Util(signatureData);
|
const bytes = Uint8Array.fromBase64(signatureData);
|
||||||
const { readable, writable } = new DecompressionStream("deflate-raw");
|
const { readable, writable } = new DecompressionStream("deflate-raw");
|
||||||
const writer = writable.getWriter();
|
const writer = writable.getWriter();
|
||||||
await writer.ready;
|
await writer.ready;
|
||||||
|
|||||||
@ -209,6 +209,10 @@ class AnnotationEditor {
|
|||||||
this.deleted = false;
|
this.deleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePageIndex(newPageIndex) {
|
||||||
|
this.pageIndex = newPageIndex;
|
||||||
|
}
|
||||||
|
|
||||||
get editorType() {
|
get editorType() {
|
||||||
return Object.getPrototypeOf(this).constructor._type;
|
return Object.getPrototypeOf(this).constructor._type;
|
||||||
}
|
}
|
||||||
@ -1204,6 +1208,9 @@ class AnnotationEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get comment() {
|
get comment() {
|
||||||
|
if (!this.#comment) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const {
|
const {
|
||||||
data: { richText, text, date, deleted },
|
data: { richText, text, date, deleted },
|
||||||
} = this.#comment;
|
} = this.#comment;
|
||||||
@ -1217,9 +1224,14 @@ class AnnotationEditor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
set comment(text) {
|
set comment(value) {
|
||||||
this.#comment ||= new Comment(this);
|
this.#comment ||= new Comment(this);
|
||||||
this.#comment.data = text;
|
if (typeof value === "object" && value !== null) {
|
||||||
|
// Restore full comment data (used for undo).
|
||||||
|
this.#comment.restoreData(value);
|
||||||
|
} else {
|
||||||
|
this.#comment.data = value;
|
||||||
|
}
|
||||||
if (this.hasComment) {
|
if (this.hasComment) {
|
||||||
this.removeCommentButtonFromToolbar();
|
this.removeCommentButtonFromToolbar();
|
||||||
this.addStandaloneCommentButton();
|
this.addStandaloneCommentButton();
|
||||||
|
|||||||
@ -948,6 +948,7 @@ class AnnotationEditorUIManager {
|
|||||||
evt => this.updateParams(evt.type, evt.value),
|
evt => this.updateParams(evt.type, evt.value),
|
||||||
{ signal }
|
{ signal }
|
||||||
);
|
);
|
||||||
|
eventBus._on("pagesedited", this.onPagesEdited.bind(this), { signal });
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
() => {
|
() => {
|
||||||
@ -1177,6 +1178,23 @@ class AnnotationEditorUIManager {
|
|||||||
this.#commentManager?.removeComments([editor.uid]);
|
this.#commentManager?.removeComments([editor.uid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a comment from an editor with undo support.
|
||||||
|
* @param {AnnotationEditor} editor - The editor whose comment to delete.
|
||||||
|
* @param {Object} savedData - The comment data to save for undo.
|
||||||
|
*/
|
||||||
|
deleteComment(editor, savedData) {
|
||||||
|
const undo = () => {
|
||||||
|
editor.comment = savedData;
|
||||||
|
};
|
||||||
|
const cmd = () => {
|
||||||
|
this._editorUndoBar?.show(undo, "comment");
|
||||||
|
this.toggleComment(/* editor = */ null);
|
||||||
|
editor.comment = null;
|
||||||
|
};
|
||||||
|
this.addCommands({ cmd, undo, mustExec: true });
|
||||||
|
}
|
||||||
|
|
||||||
toggleComment(editor, isSelected, visibility = undefined) {
|
toggleComment(editor, isSelected, visibility = undefined) {
|
||||||
this.#commentManager?.toggleCommentPopup(editor, isSelected, visibility);
|
this.#commentManager?.toggleCommentPopup(editor, isSelected, visibility);
|
||||||
}
|
}
|
||||||
@ -1242,6 +1260,26 @@ class AnnotationEditorUIManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPagesEdited({ pagesMapper }) {
|
||||||
|
for (const editor of this.#allEditors.values()) {
|
||||||
|
editor.updatePageIndex(
|
||||||
|
pagesMapper.getPrevPageNumber(editor.pageIndex + 1) - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const allLayers = this.#allLayers;
|
||||||
|
const newAllLayers = (this.#allLayers = new Map());
|
||||||
|
for (const [pageIndex, layer] of allLayers) {
|
||||||
|
const prevPageIndex = pagesMapper.getPrevPageNumber(pageIndex + 1) - 1;
|
||||||
|
if (prevPageIndex === -1) {
|
||||||
|
// TODO: handle the case where the deletion of the page has been undone.
|
||||||
|
layer.destroy();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newAllLayers.set(prevPageIndex, layer);
|
||||||
|
layer.updatePageIndex(prevPageIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onPageChanging({ pageNumber }) {
|
onPageChanging({ pageNumber }) {
|
||||||
this.#currentPageIndex = pageNumber - 1;
|
this.#currentPageIndex = pageNumber - 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,13 +14,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AbortException, assert, warn } from "../shared/util.js";
|
import { AbortException, assert, warn } from "../shared/util.js";
|
||||||
|
import {
|
||||||
|
BasePDFStream,
|
||||||
|
BasePDFStreamRangeReader,
|
||||||
|
BasePDFStreamReader,
|
||||||
|
} from "../shared/base_pdf_stream.js";
|
||||||
import {
|
import {
|
||||||
createHeaders,
|
createHeaders,
|
||||||
createResponseError,
|
createResponseError,
|
||||||
|
ensureResponseOrigin,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
getResponseOrigin,
|
getResponseOrigin,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
validateResponseStatus,
|
|
||||||
} from "./network_utils.js";
|
} from "./network_utils.js";
|
||||||
|
|
||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||||
@ -29,15 +34,21 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFetchOptions(headers, withCredentials, abortController) {
|
function fetchUrl(url, headers, withCredentials, abortController) {
|
||||||
return {
|
return fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers,
|
headers,
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
mode: "cors",
|
mode: "cors",
|
||||||
credentials: withCredentials ? "include" : "same-origin",
|
credentials: withCredentials ? "include" : "same-origin",
|
||||||
redirect: "follow",
|
redirect: "follow",
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureResponseStatus(status, url) {
|
||||||
|
if (status !== 200 && status !== 206) {
|
||||||
|
throw createResponseError(status, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArrayBuffer(val) {
|
function getArrayBuffer(val) {
|
||||||
@ -51,95 +62,58 @@ function getArrayBuffer(val) {
|
|||||||
return new Uint8Array(val).buffer;
|
return new Uint8Array(val).buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStream} */
|
class PDFFetchStream extends BasePDFStream {
|
||||||
class PDFFetchStream {
|
|
||||||
_responseOrigin = null;
|
_responseOrigin = null;
|
||||||
|
|
||||||
constructor(source) {
|
constructor(source) {
|
||||||
this.source = source;
|
super(source, PDFFetchStreamReader, PDFFetchStreamRangeReader);
|
||||||
this.isHttp = /^https?:/i.test(source.url);
|
const { httpHeaders, url } = source;
|
||||||
this.headers = createHeaders(this.isHttp, source.httpHeaders);
|
|
||||||
|
|
||||||
this._fullRequestReader = null;
|
|
||||||
this._rangeRequestReaders = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get _progressiveDataLength() {
|
|
||||||
return this._fullRequestReader?._loaded ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFullReader() {
|
|
||||||
assert(
|
assert(
|
||||||
!this._fullRequestReader,
|
/https?:/.test(url.protocol),
|
||||||
"PDFFetchStream.getFullReader can only be called once."
|
"PDFFetchStream only supports http(s):// URLs."
|
||||||
);
|
);
|
||||||
this._fullRequestReader = new PDFFetchStreamReader(this);
|
this.headers = createHeaders(/* isHttp = */ true, httpHeaders);
|
||||||
return this._fullRequestReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRangeReader(begin, end) {
|
|
||||||
if (end <= this._progressiveDataLength) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const reader = new PDFFetchStreamRangeReader(this, begin, end);
|
|
||||||
this._rangeRequestReaders.push(reader);
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAllRequests(reason) {
|
|
||||||
this._fullRequestReader?.cancel(reason);
|
|
||||||
|
|
||||||
for (const reader of this._rangeRequestReaders.slice(0)) {
|
|
||||||
reader.cancel(reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamReader} */
|
class PDFFetchStreamReader extends BasePDFStreamReader {
|
||||||
class PDFFetchStreamReader {
|
_abortController = new AbortController();
|
||||||
constructor(stream) {
|
|
||||||
this._stream = stream;
|
|
||||||
this._reader = null;
|
|
||||||
this._loaded = 0;
|
|
||||||
this._filename = null;
|
|
||||||
const source = stream.source;
|
|
||||||
this._withCredentials = source.withCredentials || false;
|
|
||||||
this._contentLength = source.length;
|
|
||||||
this._headersCapability = Promise.withResolvers();
|
|
||||||
this._disableRange = source.disableRange || false;
|
|
||||||
this._rangeChunkSize = source.rangeChunkSize;
|
|
||||||
if (!this._rangeChunkSize && !this._disableRange) {
|
|
||||||
this._disableRange = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._abortController = new AbortController();
|
_reader = null;
|
||||||
this._isStreamingSupported = !source.disableStream;
|
|
||||||
this._isRangeSupported = !source.disableRange;
|
constructor(stream) {
|
||||||
|
super(stream);
|
||||||
|
const {
|
||||||
|
disableRange,
|
||||||
|
disableStream,
|
||||||
|
length,
|
||||||
|
rangeChunkSize,
|
||||||
|
url,
|
||||||
|
withCredentials,
|
||||||
|
} = stream._source;
|
||||||
|
|
||||||
|
this._contentLength = length;
|
||||||
|
this._isStreamingSupported = !disableStream;
|
||||||
|
this._isRangeSupported = !disableRange;
|
||||||
// Always create a copy of the headers.
|
// Always create a copy of the headers.
|
||||||
const headers = new Headers(stream.headers);
|
const headers = new Headers(stream.headers);
|
||||||
|
|
||||||
const url = source.url;
|
fetchUrl(url, headers, withCredentials, this._abortController)
|
||||||
fetch(
|
|
||||||
url,
|
|
||||||
createFetchOptions(headers, this._withCredentials, this._abortController)
|
|
||||||
)
|
|
||||||
.then(response => {
|
.then(response => {
|
||||||
stream._responseOrigin = getResponseOrigin(response.url);
|
stream._responseOrigin = getResponseOrigin(response.url);
|
||||||
|
|
||||||
if (!validateResponseStatus(response.status)) {
|
ensureResponseStatus(response.status, url);
|
||||||
throw createResponseError(response.status, url);
|
|
||||||
}
|
|
||||||
this._reader = response.body.getReader();
|
this._reader = response.body.getReader();
|
||||||
this._headersCapability.resolve();
|
|
||||||
|
|
||||||
const responseHeaders = response.headers;
|
const responseHeaders = response.headers;
|
||||||
|
|
||||||
const { allowRangeRequests, suggestedLength } =
|
const { allowRangeRequests, suggestedLength } =
|
||||||
validateRangeRequestCapabilities({
|
validateRangeRequestCapabilities({
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
isHttp: stream.isHttp,
|
isHttp: true,
|
||||||
rangeChunkSize: this._rangeChunkSize,
|
rangeChunkSize,
|
||||||
disableRange: this._disableRange,
|
disableRange,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._isRangeSupported = allowRangeRequests;
|
this._isRangeSupported = allowRangeRequests;
|
||||||
@ -153,30 +127,10 @@ class PDFFetchStreamReader {
|
|||||||
if (!this._isStreamingSupported && this._isRangeSupported) {
|
if (!this._isStreamingSupported && this._isRangeSupported) {
|
||||||
this.cancel(new AbortException("Streaming is disabled."));
|
this.cancel(new AbortException("Streaming is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._headersCapability.resolve();
|
||||||
})
|
})
|
||||||
.catch(this._headersCapability.reject);
|
.catch(this._headersCapability.reject);
|
||||||
|
|
||||||
this.onProgress = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get headersReady() {
|
|
||||||
return this._headersCapability.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
get filename() {
|
|
||||||
return this._filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contentLength() {
|
|
||||||
return this._contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRangeSupported() {
|
|
||||||
return this._isRangeSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._isStreamingSupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
@ -186,10 +140,7 @@ class PDFFetchStreamReader {
|
|||||||
return { value, done };
|
return { value, done };
|
||||||
}
|
}
|
||||||
this._loaded += value.byteLength;
|
this._loaded += value.byteLength;
|
||||||
this.onProgress?.({
|
this._callOnProgress();
|
||||||
loaded: this._loaded,
|
|
||||||
total: this._contentLength,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { value: getArrayBuffer(value), done: false };
|
return { value: getArrayBuffer(value), done: false };
|
||||||
}
|
}
|
||||||
@ -200,48 +151,32 @@ class PDFFetchStreamReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamRangeReader} */
|
class PDFFetchStreamRangeReader extends BasePDFStreamRangeReader {
|
||||||
class PDFFetchStreamRangeReader {
|
_abortController = new AbortController();
|
||||||
constructor(stream, begin, end) {
|
|
||||||
this._stream = stream;
|
_readCapability = Promise.withResolvers();
|
||||||
this._reader = null;
|
|
||||||
this._loaded = 0;
|
_reader = null;
|
||||||
const source = stream.source;
|
|
||||||
this._withCredentials = source.withCredentials || false;
|
constructor(stream, begin, end) {
|
||||||
this._readCapability = Promise.withResolvers();
|
super(stream, begin, end);
|
||||||
this._isStreamingSupported = !source.disableStream;
|
const { url, withCredentials } = stream._source;
|
||||||
|
|
||||||
this._abortController = new AbortController();
|
|
||||||
// Always create a copy of the headers.
|
// Always create a copy of the headers.
|
||||||
const headers = new Headers(stream.headers);
|
const headers = new Headers(stream.headers);
|
||||||
headers.append("Range", `bytes=${begin}-${end - 1}`);
|
headers.append("Range", `bytes=${begin}-${end - 1}`);
|
||||||
|
|
||||||
const url = source.url;
|
fetchUrl(url, headers, withCredentials, this._abortController)
|
||||||
fetch(
|
|
||||||
url,
|
|
||||||
createFetchOptions(headers, this._withCredentials, this._abortController)
|
|
||||||
)
|
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const responseOrigin = getResponseOrigin(response.url);
|
const responseOrigin = getResponseOrigin(response.url);
|
||||||
|
|
||||||
if (responseOrigin !== stream._responseOrigin) {
|
ensureResponseOrigin(responseOrigin, stream._responseOrigin);
|
||||||
throw new Error(
|
ensureResponseStatus(response.status, url);
|
||||||
`Expected range response-origin "${responseOrigin}" to match "${stream._responseOrigin}".`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!validateResponseStatus(response.status)) {
|
|
||||||
throw createResponseError(response.status, url);
|
|
||||||
}
|
|
||||||
this._readCapability.resolve();
|
|
||||||
this._reader = response.body.getReader();
|
this._reader = response.body.getReader();
|
||||||
|
|
||||||
|
this._readCapability.resolve();
|
||||||
})
|
})
|
||||||
.catch(this._readCapability.reject);
|
.catch(this._readCapability.reject);
|
||||||
|
|
||||||
this.onProgress = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._isStreamingSupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
@ -250,9 +185,6 @@ class PDFFetchStreamRangeReader {
|
|||||||
if (done) {
|
if (done) {
|
||||||
return { value, done };
|
return { value, done };
|
||||||
}
|
}
|
||||||
this._loaded += value.byteLength;
|
|
||||||
this.onProgress?.({ loaded: this._loaded });
|
|
||||||
|
|
||||||
return { value: getArrayBuffer(value), done: false };
|
return { value: getArrayBuffer(value), done: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import {
|
|||||||
isNodeJS,
|
isNodeJS,
|
||||||
shadow,
|
shadow,
|
||||||
string32,
|
string32,
|
||||||
toBase64Util,
|
|
||||||
unreachable,
|
unreachable,
|
||||||
warn,
|
warn,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
@ -408,7 +407,7 @@ class FontFaceObject {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Add the @font-face rule to the document.
|
// Add the @font-face rule to the document.
|
||||||
const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`;
|
const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`;
|
||||||
let rule;
|
let rule;
|
||||||
if (!this.cssFontInfo) {
|
if (!this.cssFontInfo) {
|
||||||
rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
|
rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
|
||||||
|
|||||||
@ -14,9 +14,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert, stringToBytes, warn } from "../shared/util.js";
|
import { assert, stringToBytes, warn } from "../shared/util.js";
|
||||||
|
import {
|
||||||
|
BasePDFStream,
|
||||||
|
BasePDFStreamRangeReader,
|
||||||
|
BasePDFStreamReader,
|
||||||
|
} from "../shared/base_pdf_stream.js";
|
||||||
import {
|
import {
|
||||||
createHeaders,
|
createHeaders,
|
||||||
createResponseError,
|
createResponseError,
|
||||||
|
ensureResponseOrigin,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
getResponseOrigin,
|
getResponseOrigin,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
@ -31,77 +37,77 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|||||||
const OK_RESPONSE = 200;
|
const OK_RESPONSE = 200;
|
||||||
const PARTIAL_CONTENT_RESPONSE = 206;
|
const PARTIAL_CONTENT_RESPONSE = 206;
|
||||||
|
|
||||||
function getArrayBuffer(xhr) {
|
function getArrayBuffer(val) {
|
||||||
const data = xhr.response;
|
return typeof val !== "string" ? val : stringToBytes(val).buffer;
|
||||||
if (typeof data !== "string") {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return stringToBytes(data).buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NetworkManager {
|
class PDFNetworkStream extends BasePDFStream {
|
||||||
|
#pendingRequests = new WeakMap();
|
||||||
|
|
||||||
_responseOrigin = null;
|
_responseOrigin = null;
|
||||||
|
|
||||||
constructor({ url, httpHeaders, withCredentials }) {
|
constructor(source) {
|
||||||
this.url = url;
|
super(source, PDFNetworkStreamReader, PDFNetworkStreamRangeReader);
|
||||||
this.isHttp = /^https?:/i.test(url);
|
const { httpHeaders, url } = source;
|
||||||
this.headers = createHeaders(this.isHttp, httpHeaders);
|
|
||||||
this.withCredentials = withCredentials || false;
|
|
||||||
|
|
||||||
this.currXhrId = 0;
|
this.url = url;
|
||||||
this.pendingRequests = Object.create(null);
|
this.isHttp = /https?:/.test(url.protocol);
|
||||||
|
this.headers = createHeaders(this.isHttp, httpHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
request(args) {
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
_request(args) {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
const xhrId = this.currXhrId++;
|
const pendingRequest = {
|
||||||
const pendingRequest = (this.pendingRequests[xhrId] = { xhr });
|
validateStatus: null,
|
||||||
|
onHeadersReceived: args.onHeadersReceived,
|
||||||
|
onDone: args.onDone,
|
||||||
|
onError: args.onError,
|
||||||
|
onProgress: args.onProgress,
|
||||||
|
};
|
||||||
|
this.#pendingRequests.set(xhr, pendingRequest);
|
||||||
|
|
||||||
xhr.open("GET", this.url);
|
xhr.open("GET", this.url);
|
||||||
xhr.withCredentials = this.withCredentials;
|
xhr.withCredentials = this._source.withCredentials;
|
||||||
for (const [key, val] of this.headers) {
|
for (const [key, val] of this.headers) {
|
||||||
xhr.setRequestHeader(key, val);
|
xhr.setRequestHeader(key, val);
|
||||||
}
|
}
|
||||||
if (this.isHttp && "begin" in args && "end" in args) {
|
if (this.isHttp && "begin" in args && "end" in args) {
|
||||||
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
|
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
|
||||||
pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;
|
|
||||||
|
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
|
||||||
|
// "A server MAY ignore the Range header". This means it's possible to
|
||||||
|
// get a 200 rather than a 206 response from a range request.
|
||||||
|
pendingRequest.validateStatus = status =>
|
||||||
|
status === PARTIAL_CONTENT_RESPONSE || status === OK_RESPONSE;
|
||||||
} else {
|
} else {
|
||||||
pendingRequest.expectedStatus = OK_RESPONSE;
|
pendingRequest.validateStatus = status => status === OK_RESPONSE;
|
||||||
}
|
}
|
||||||
xhr.responseType = "arraybuffer";
|
xhr.responseType = "arraybuffer";
|
||||||
|
|
||||||
assert(args.onError, "Expected `onError` callback to be provided.");
|
assert(args.onError, "Expected `onError` callback to be provided.");
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => args.onError(xhr.status);
|
||||||
args.onError(xhr.status);
|
xhr.onreadystatechange = this.#onStateChange.bind(this, xhr);
|
||||||
};
|
xhr.onprogress = this.#onProgress.bind(this, xhr);
|
||||||
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
|
|
||||||
xhr.onprogress = this.onProgress.bind(this, xhrId);
|
|
||||||
|
|
||||||
pendingRequest.onHeadersReceived = args.onHeadersReceived;
|
|
||||||
pendingRequest.onDone = args.onDone;
|
|
||||||
pendingRequest.onError = args.onError;
|
|
||||||
pendingRequest.onProgress = args.onProgress;
|
|
||||||
|
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
|
|
||||||
return xhrId;
|
return xhr;
|
||||||
}
|
}
|
||||||
|
|
||||||
onProgress(xhrId, evt) {
|
#onProgress(xhr, evt) {
|
||||||
const pendingRequest = this.pendingRequests[xhrId];
|
const pendingRequest = this.#pendingRequests.get(xhr);
|
||||||
if (!pendingRequest) {
|
pendingRequest?.onProgress?.(evt);
|
||||||
return; // Maybe abortRequest was called...
|
|
||||||
}
|
|
||||||
pendingRequest.onProgress?.(evt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStateChange(xhrId, evt) {
|
#onStateChange(xhr, evt) {
|
||||||
const pendingRequest = this.pendingRequests[xhrId];
|
const pendingRequest = this.#pendingRequests.get(xhr);
|
||||||
if (!pendingRequest) {
|
if (!pendingRequest) {
|
||||||
return; // Maybe abortRequest was called...
|
return; // Maybe abortRequest was called...
|
||||||
}
|
}
|
||||||
|
|
||||||
const xhr = pendingRequest.xhr;
|
|
||||||
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
|
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
|
||||||
pendingRequest.onHeadersReceived();
|
pendingRequest.onHeadersReceived();
|
||||||
delete pendingRequest.onHeadersReceived;
|
delete pendingRequest.onHeadersReceived;
|
||||||
@ -111,13 +117,12 @@ class NetworkManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(xhrId in this.pendingRequests)) {
|
if (!this.#pendingRequests.has(xhr)) {
|
||||||
// The XHR request might have been aborted in onHeadersReceived()
|
// The XHR request might have been aborted in onHeadersReceived()
|
||||||
// callback, in which case we should abort request.
|
// callback, in which case we should abort request.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.#pendingRequests.delete(xhr);
|
||||||
delete this.pendingRequests[xhrId];
|
|
||||||
|
|
||||||
// Success status == 0 can be on ftp, file and other protocols.
|
// Success status == 0 can be on ftp, file and other protocols.
|
||||||
if (xhr.status === 0 && this.isHttp) {
|
if (xhr.status === 0 && this.isHttp) {
|
||||||
@ -126,147 +131,79 @@ class NetworkManager {
|
|||||||
}
|
}
|
||||||
const xhrStatus = xhr.status || OK_RESPONSE;
|
const xhrStatus = xhr.status || OK_RESPONSE;
|
||||||
|
|
||||||
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
|
if (!pendingRequest.validateStatus(xhrStatus)) {
|
||||||
// "A server MAY ignore the Range header". This means it's possible to
|
|
||||||
// get a 200 rather than a 206 response from a range request.
|
|
||||||
const ok_response_on_range_request =
|
|
||||||
xhrStatus === OK_RESPONSE &&
|
|
||||||
pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!ok_response_on_range_request &&
|
|
||||||
xhrStatus !== pendingRequest.expectedStatus
|
|
||||||
) {
|
|
||||||
pendingRequest.onError(xhr.status);
|
pendingRequest.onError(xhr.status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chunk = getArrayBuffer(xhr);
|
const chunk = getArrayBuffer(xhr.response);
|
||||||
if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
|
if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
|
||||||
const rangeHeader = xhr.getResponseHeader("Content-Range");
|
const rangeHeader = xhr.getResponseHeader("Content-Range");
|
||||||
const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
|
if (/bytes (\d+)-(\d+)\/(\d+)/.test(rangeHeader)) {
|
||||||
if (matches) {
|
pendingRequest.onDone(chunk);
|
||||||
pendingRequest.onDone({
|
|
||||||
begin: parseInt(matches[1], 10),
|
|
||||||
chunk,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
warn(`Missing or invalid "Content-Range" header.`);
|
warn(`Missing or invalid "Content-Range" header.`);
|
||||||
pendingRequest.onError(0);
|
pendingRequest.onError(0);
|
||||||
}
|
}
|
||||||
} else if (chunk) {
|
} else if (chunk) {
|
||||||
pendingRequest.onDone({
|
pendingRequest.onDone(chunk);
|
||||||
begin: 0,
|
|
||||||
chunk,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
pendingRequest.onError(xhr.status);
|
pendingRequest.onError(xhr.status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRequestXhr(xhrId) {
|
/**
|
||||||
return this.pendingRequests[xhrId].xhr;
|
* Abort the request, if it's pending.
|
||||||
}
|
* @ignore
|
||||||
|
*/
|
||||||
isPendingRequest(xhrId) {
|
_abortRequest(xhr) {
|
||||||
return xhrId in this.pendingRequests;
|
if (this.#pendingRequests.has(xhr)) {
|
||||||
}
|
this.#pendingRequests.delete(xhr);
|
||||||
|
xhr.abort();
|
||||||
abortRequest(xhrId) {
|
|
||||||
const xhr = this.pendingRequests[xhrId].xhr;
|
|
||||||
delete this.pendingRequests[xhrId];
|
|
||||||
xhr.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @implements {IPDFStream} */
|
|
||||||
class PDFNetworkStream {
|
|
||||||
constructor(source) {
|
|
||||||
this._source = source;
|
|
||||||
this._manager = new NetworkManager(source);
|
|
||||||
this._rangeChunkSize = source.rangeChunkSize;
|
|
||||||
this._fullRequestReader = null;
|
|
||||||
this._rangeRequestReaders = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_onRangeRequestReaderClosed(reader) {
|
|
||||||
const i = this._rangeRequestReaders.indexOf(reader);
|
|
||||||
if (i >= 0) {
|
|
||||||
this._rangeRequestReaders.splice(i, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getFullReader() {
|
|
||||||
assert(
|
|
||||||
!this._fullRequestReader,
|
|
||||||
"PDFNetworkStream.getFullReader can only be called once."
|
|
||||||
);
|
|
||||||
this._fullRequestReader = new PDFNetworkStreamFullRequestReader(
|
|
||||||
this._manager,
|
|
||||||
this._source
|
|
||||||
);
|
|
||||||
return this._fullRequestReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRangeReader(begin, end) {
|
getRangeReader(begin, end) {
|
||||||
const reader = new PDFNetworkStreamRangeRequestReader(
|
const reader = super.getRangeReader(begin, end);
|
||||||
this._manager,
|
|
||||||
begin,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
reader.onClosed = this._onRangeRequestReaderClosed.bind(this);
|
|
||||||
this._rangeRequestReaders.push(reader);
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAllRequests(reason) {
|
if (reader) {
|
||||||
this._fullRequestReader?.cancel(reason);
|
reader.onClosed = () => this._rangeReaders.delete(reader);
|
||||||
|
|
||||||
for (const reader of this._rangeRequestReaders.slice(0)) {
|
|
||||||
reader.cancel(reason);
|
|
||||||
}
|
}
|
||||||
|
return reader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamReader} */
|
class PDFNetworkStreamReader extends BasePDFStreamReader {
|
||||||
class PDFNetworkStreamFullRequestReader {
|
_cachedChunks = [];
|
||||||
constructor(manager, source) {
|
|
||||||
this._manager = manager;
|
|
||||||
|
|
||||||
this._url = source.url;
|
_done = false;
|
||||||
this._fullRequestId = manager.request({
|
|
||||||
onHeadersReceived: this._onHeadersReceived.bind(this),
|
_requests = [];
|
||||||
onDone: this._onDone.bind(this),
|
|
||||||
onError: this._onError.bind(this),
|
_storedError = null;
|
||||||
onProgress: this._onProgress.bind(this),
|
|
||||||
|
constructor(stream) {
|
||||||
|
super(stream);
|
||||||
|
const { length } = stream._source;
|
||||||
|
|
||||||
|
this._contentLength = length;
|
||||||
|
// Note that `XMLHttpRequest` doesn't support streaming, and range requests
|
||||||
|
// will be enabled (if supported) in `this.#onHeadersReceived` below.
|
||||||
|
|
||||||
|
this._fullRequestXhr = stream._request({
|
||||||
|
onHeadersReceived: this.#onHeadersReceived.bind(this),
|
||||||
|
onDone: this.#onDone.bind(this),
|
||||||
|
onError: this.#onError.bind(this),
|
||||||
|
onProgress: this.#onProgress.bind(this),
|
||||||
});
|
});
|
||||||
this._headersCapability = Promise.withResolvers();
|
|
||||||
this._disableRange = source.disableRange || false;
|
|
||||||
this._contentLength = source.length; // Optional
|
|
||||||
this._rangeChunkSize = source.rangeChunkSize;
|
|
||||||
if (!this._rangeChunkSize && !this._disableRange) {
|
|
||||||
this._disableRange = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isStreamingSupported = false;
|
|
||||||
this._isRangeSupported = false;
|
|
||||||
|
|
||||||
this._cachedChunks = [];
|
|
||||||
this._requests = [];
|
|
||||||
this._done = false;
|
|
||||||
this._storedError = undefined;
|
|
||||||
this._filename = null;
|
|
||||||
|
|
||||||
this.onProgress = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onHeadersReceived() {
|
#onHeadersReceived() {
|
||||||
const fullRequestXhrId = this._fullRequestId;
|
const stream = this._stream;
|
||||||
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
|
const { disableRange, rangeChunkSize } = stream._source;
|
||||||
|
const fullRequestXhr = this._fullRequestXhr;
|
||||||
|
|
||||||
this._manager._responseOrigin = getResponseOrigin(
|
stream._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL);
|
||||||
fullRequestXhr.responseURL
|
|
||||||
);
|
|
||||||
|
|
||||||
const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
|
const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
|
||||||
const responseHeaders = new Headers(
|
const responseHeaders = new Headers(
|
||||||
@ -285,9 +222,9 @@ class PDFNetworkStreamFullRequestReader {
|
|||||||
const { allowRangeRequests, suggestedLength } =
|
const { allowRangeRequests, suggestedLength } =
|
||||||
validateRangeRequestCapabilities({
|
validateRangeRequestCapabilities({
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
isHttp: this._manager.isHttp,
|
isHttp: stream.isHttp,
|
||||||
rangeChunkSize: this._rangeChunkSize,
|
rangeChunkSize,
|
||||||
disableRange: this._disableRange,
|
disableRange,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (allowRangeRequests) {
|
if (allowRangeRequests) {
|
||||||
@ -303,68 +240,46 @@ class PDFNetworkStreamFullRequestReader {
|
|||||||
// requests, there will be an issue for sites where you can only
|
// requests, there will be an issue for sites where you can only
|
||||||
// request the pdf once. However, if this is the case, then the
|
// request the pdf once. However, if this is the case, then the
|
||||||
// server should not be returning that it can support range requests.
|
// server should not be returning that it can support range requests.
|
||||||
this._manager.abortRequest(fullRequestXhrId);
|
stream._abortRequest(fullRequestXhr);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._headersCapability.resolve();
|
this._headersCapability.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDone(data) {
|
#onDone(chunk) {
|
||||||
if (data) {
|
if (this._requests.length > 0) {
|
||||||
if (this._requests.length > 0) {
|
const capability = this._requests.shift();
|
||||||
const requestCapability = this._requests.shift();
|
capability.resolve({ value: chunk, done: false });
|
||||||
requestCapability.resolve({ value: data.chunk, done: false });
|
} else {
|
||||||
} else {
|
this._cachedChunks.push(chunk);
|
||||||
this._cachedChunks.push(data.chunk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this._done = true;
|
this._done = true;
|
||||||
if (this._cachedChunks.length > 0) {
|
if (this._cachedChunks.length > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError(status) {
|
#onError(status) {
|
||||||
this._storedError = createResponseError(status, this._url);
|
this._storedError = createResponseError(status, this._stream.url);
|
||||||
this._headersCapability.reject(this._storedError);
|
this._headersCapability.reject(this._storedError);
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.reject(this._storedError);
|
capability.reject(this._storedError);
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
this._cachedChunks.length = 0;
|
this._cachedChunks.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onProgress(evt) {
|
#onProgress(evt) {
|
||||||
this.onProgress?.({
|
this.onProgress?.({
|
||||||
loaded: evt.loaded,
|
loaded: evt.loaded,
|
||||||
total: evt.lengthComputable ? evt.total : this._contentLength,
|
total: evt.lengthComputable ? evt.total : this._contentLength,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get filename() {
|
|
||||||
return this._filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRangeSupported() {
|
|
||||||
return this._isRangeSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._isStreamingSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contentLength() {
|
|
||||||
return this._contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
get headersReady() {
|
|
||||||
return this._headersCapability.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
await this._headersCapability.promise;
|
await this._headersCapability.promise;
|
||||||
|
|
||||||
@ -378,100 +293,82 @@ class PDFNetworkStreamFullRequestReader {
|
|||||||
if (this._done) {
|
if (this._done) {
|
||||||
return { value: undefined, done: true };
|
return { value: undefined, done: true };
|
||||||
}
|
}
|
||||||
const requestCapability = Promise.withResolvers();
|
const capability = Promise.withResolvers();
|
||||||
this._requests.push(requestCapability);
|
this._requests.push(capability);
|
||||||
return requestCapability.promise;
|
return capability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
this._headersCapability.reject(reason);
|
this._headersCapability.reject(reason);
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
if (this._manager.isPendingRequest(this._fullRequestId)) {
|
|
||||||
this._manager.abortRequest(this._fullRequestId);
|
this._stream._abortRequest(this._fullRequestXhr);
|
||||||
}
|
this._fullRequestXhr = null;
|
||||||
this._fullRequestReader = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamRangeReader} */
|
class PDFNetworkStreamRangeReader extends BasePDFStreamRangeReader {
|
||||||
class PDFNetworkStreamRangeRequestReader {
|
onClosed = null;
|
||||||
constructor(manager, begin, end) {
|
|
||||||
this._manager = manager;
|
|
||||||
|
|
||||||
this._url = manager.url;
|
_done = false;
|
||||||
this._requestId = manager.request({
|
|
||||||
|
_queuedChunk = null;
|
||||||
|
|
||||||
|
_requests = [];
|
||||||
|
|
||||||
|
_storedError = null;
|
||||||
|
|
||||||
|
constructor(stream, begin, end) {
|
||||||
|
super(stream, begin, end);
|
||||||
|
|
||||||
|
this._requestXhr = stream._request({
|
||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
onHeadersReceived: this._onHeadersReceived.bind(this),
|
onHeadersReceived: this.#onHeadersReceived.bind(this),
|
||||||
onDone: this._onDone.bind(this),
|
onDone: this.#onDone.bind(this),
|
||||||
onError: this._onError.bind(this),
|
onError: this.#onError.bind(this),
|
||||||
onProgress: this._onProgress.bind(this),
|
onProgress: null,
|
||||||
});
|
});
|
||||||
this._requests = [];
|
|
||||||
this._queuedChunk = null;
|
|
||||||
this._done = false;
|
|
||||||
this._storedError = undefined;
|
|
||||||
|
|
||||||
this.onProgress = null;
|
|
||||||
this.onClosed = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onHeadersReceived() {
|
#onHeadersReceived() {
|
||||||
const responseOrigin = getResponseOrigin(
|
const responseOrigin = getResponseOrigin(this._requestXhr?.responseURL);
|
||||||
this._manager.getRequestXhr(this._requestId)?.responseURL
|
try {
|
||||||
);
|
ensureResponseOrigin(responseOrigin, this._stream._responseOrigin);
|
||||||
|
} catch (ex) {
|
||||||
if (responseOrigin !== this._manager._responseOrigin) {
|
this._storedError = ex;
|
||||||
this._storedError = new Error(
|
this.#onError(0);
|
||||||
`Expected range response-origin "${responseOrigin}" to match "${this._manager._responseOrigin}".`
|
|
||||||
);
|
|
||||||
this._onError(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_close() {
|
#onDone(chunk) {
|
||||||
this.onClosed?.(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDone(data) {
|
|
||||||
const chunk = data.chunk;
|
|
||||||
if (this._requests.length > 0) {
|
if (this._requests.length > 0) {
|
||||||
const requestCapability = this._requests.shift();
|
const capability = this._requests.shift();
|
||||||
requestCapability.resolve({ value: chunk, done: false });
|
capability.resolve({ value: chunk, done: false });
|
||||||
} else {
|
} else {
|
||||||
this._queuedChunk = chunk;
|
this._queuedChunk = chunk;
|
||||||
}
|
}
|
||||||
this._done = true;
|
this._done = true;
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
this._close();
|
this.onClosed?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError(status) {
|
#onError(status) {
|
||||||
this._storedError ??= createResponseError(status, this._url);
|
this._storedError ??= createResponseError(status, this._stream.url);
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.reject(this._storedError);
|
capability.reject(this._storedError);
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
this._queuedChunk = null;
|
this._queuedChunk = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onProgress(evt) {
|
|
||||||
if (!this.isStreamingSupported) {
|
|
||||||
this.onProgress?.({ loaded: evt.loaded });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
if (this._storedError) {
|
if (this._storedError) {
|
||||||
throw this._storedError;
|
throw this._storedError;
|
||||||
@ -484,21 +381,20 @@ class PDFNetworkStreamRangeRequestReader {
|
|||||||
if (this._done) {
|
if (this._done) {
|
||||||
return { value: undefined, done: true };
|
return { value: undefined, done: true };
|
||||||
}
|
}
|
||||||
const requestCapability = Promise.withResolvers();
|
const capability = Promise.withResolvers();
|
||||||
this._requests.push(requestCapability);
|
this._requests.push(capability);
|
||||||
return requestCapability.promise;
|
return capability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
if (this._manager.isPendingRequest(this._requestId)) {
|
|
||||||
this._manager.abortRequest(this._requestId);
|
this._stream._abortRequest(this._requestXhr);
|
||||||
}
|
this.onClosed?.();
|
||||||
this._close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,21 +101,25 @@ function extractFilenameFromHeader(responseHeaders) {
|
|||||||
|
|
||||||
function createResponseError(status, url) {
|
function createResponseError(status, url) {
|
||||||
return new ResponseException(
|
return new ResponseException(
|
||||||
`Unexpected server response (${status}) while retrieving PDF "${url}".`,
|
`Unexpected server response (${status}) while retrieving PDF "${url.href}".`,
|
||||||
status,
|
status,
|
||||||
/* missing = */ status === 404 || (status === 0 && url.startsWith("file:"))
|
/* missing = */ status === 404 || (status === 0 && url.protocol === "file:")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateResponseStatus(status) {
|
function ensureResponseOrigin(rangeOrigin, origin) {
|
||||||
return status === 200 || status === 206;
|
if (rangeOrigin !== origin) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected range response-origin "${rangeOrigin}" to match "${origin}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createHeaders,
|
createHeaders,
|
||||||
createResponseError,
|
createResponseError,
|
||||||
|
ensureResponseOrigin,
|
||||||
extractFilenameFromHeader,
|
extractFilenameFromHeader,
|
||||||
getResponseOrigin,
|
getResponseOrigin,
|
||||||
validateRangeRequestCapabilities,
|
validateRangeRequestCapabilities,
|
||||||
validateResponseStatus,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,12 @@
|
|||||||
*/
|
*/
|
||||||
/* globals process */
|
/* globals process */
|
||||||
|
|
||||||
import { AbortException, assert } from "../shared/util.js";
|
import { AbortException, assert, warn } from "../shared/util.js";
|
||||||
|
import {
|
||||||
|
BasePDFStream,
|
||||||
|
BasePDFStreamRangeReader,
|
||||||
|
BasePDFStreamReader,
|
||||||
|
} from "../shared/base_pdf_stream.js";
|
||||||
import { createResponseError } from "./network_utils.js";
|
import { createResponseError } from "./network_utils.js";
|
||||||
|
|
||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||||
@ -23,273 +28,144 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlRegex = /^[a-z][a-z0-9\-+.]+:/i;
|
function getReadableStream(readStream) {
|
||||||
|
const { Readable } = process.getBuiltinModule("stream");
|
||||||
|
|
||||||
function parseUrlOrPath(sourceUrl) {
|
if (typeof Readable.toWeb === "function") {
|
||||||
if (urlRegex.test(sourceUrl)) {
|
// See https://nodejs.org/api/stream.html#streamreadabletowebstreamreadable-options
|
||||||
return new URL(sourceUrl);
|
return Readable.toWeb(readStream);
|
||||||
}
|
}
|
||||||
const url = process.getBuiltinModule("url");
|
// Fallback to support Node.js versions older than `24.0.0` and `22.17.0`.
|
||||||
return new URL(url.pathToFileURL(sourceUrl));
|
const require = process
|
||||||
|
.getBuiltinModule("module")
|
||||||
|
.createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const polyfill = require("node-readable-to-web-readable-stream");
|
||||||
|
return polyfill.makeDefaultReadableStreamFromNodeReadable(readStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PDFNodeStream {
|
function getArrayBuffer(val) {
|
||||||
|
if (val instanceof Uint8Array) {
|
||||||
|
return val.buffer;
|
||||||
|
}
|
||||||
|
if (val instanceof ArrayBuffer) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
warn(`getArrayBuffer - unexpected data format: ${val}`);
|
||||||
|
return new Uint8Array(val).buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PDFNodeStream extends BasePDFStream {
|
||||||
constructor(source) {
|
constructor(source) {
|
||||||
this.source = source;
|
super(source, PDFNodeStreamReader, PDFNodeStreamRangeReader);
|
||||||
this.url = parseUrlOrPath(source.url);
|
const { url } = source;
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
this.url.protocol === "file:",
|
url.protocol === "file:",
|
||||||
"PDFNodeStream only supports file:// URLs."
|
"PDFNodeStream only supports file:// URLs."
|
||||||
);
|
);
|
||||||
|
|
||||||
this._fullRequestReader = null;
|
|
||||||
this._rangeRequestReaders = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get _progressiveDataLength() {
|
|
||||||
return this._fullRequestReader?._loaded ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFullReader() {
|
|
||||||
assert(
|
|
||||||
!this._fullRequestReader,
|
|
||||||
"PDFNodeStream.getFullReader can only be called once."
|
|
||||||
);
|
|
||||||
this._fullRequestReader = new PDFNodeStreamFsFullReader(this);
|
|
||||||
return this._fullRequestReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRangeReader(start, end) {
|
|
||||||
if (end <= this._progressiveDataLength) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const rangeReader = new PDFNodeStreamFsRangeReader(this, start, end);
|
|
||||||
this._rangeRequestReaders.push(rangeReader);
|
|
||||||
return rangeReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAllRequests(reason) {
|
|
||||||
this._fullRequestReader?.cancel(reason);
|
|
||||||
|
|
||||||
for (const reader of this._rangeRequestReaders.slice(0)) {
|
|
||||||
reader.cancel(reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PDFNodeStreamFsFullReader {
|
class PDFNodeStreamReader extends BasePDFStreamReader {
|
||||||
|
_reader = null;
|
||||||
|
|
||||||
constructor(stream) {
|
constructor(stream) {
|
||||||
this._url = stream.url;
|
super(stream);
|
||||||
this._done = false;
|
const { disableRange, disableStream, length, rangeChunkSize, url } =
|
||||||
this._storedError = null;
|
stream._source;
|
||||||
this.onProgress = null;
|
|
||||||
const source = stream.source;
|
|
||||||
this._contentLength = source.length; // optional
|
|
||||||
this._loaded = 0;
|
|
||||||
this._filename = null;
|
|
||||||
|
|
||||||
this._disableRange = source.disableRange || false;
|
this._contentLength = length;
|
||||||
this._rangeChunkSize = source.rangeChunkSize;
|
this._isStreamingSupported = !disableStream;
|
||||||
if (!this._rangeChunkSize && !this._disableRange) {
|
this._isRangeSupported = !disableRange;
|
||||||
this._disableRange = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isStreamingSupported = !source.disableStream;
|
|
||||||
this._isRangeSupported = !source.disableRange;
|
|
||||||
|
|
||||||
this._readableStream = null;
|
|
||||||
this._readCapability = Promise.withResolvers();
|
|
||||||
this._headersCapability = Promise.withResolvers();
|
|
||||||
|
|
||||||
const fs = process.getBuiltinModule("fs");
|
const fs = process.getBuiltinModule("fs");
|
||||||
fs.promises.lstat(this._url).then(
|
fs.promises
|
||||||
stat => {
|
.lstat(url)
|
||||||
// Setting right content length.
|
.then(stat => {
|
||||||
this._contentLength = stat.size;
|
const readStream = fs.createReadStream(url);
|
||||||
|
const readableStream = getReadableStream(readStream);
|
||||||
|
|
||||||
this._setReadableStream(fs.createReadStream(this._url));
|
this._reader = readableStream.getReader();
|
||||||
this._headersCapability.resolve();
|
|
||||||
},
|
const { size } = stat;
|
||||||
error => {
|
if (size <= 2 * rangeChunkSize) {
|
||||||
if (error.code === "ENOENT") {
|
// The file size is smaller than the size of two chunks, so it doesn't
|
||||||
error = createResponseError(/* status = */ 0, this._url.href);
|
// make any sense to abort the request and retry with a range request.
|
||||||
|
this._isRangeSupported = false;
|
||||||
|
}
|
||||||
|
// Setting right content length.
|
||||||
|
this._contentLength = size;
|
||||||
|
|
||||||
|
// We need to stop reading when range is supported and streaming is
|
||||||
|
// disabled.
|
||||||
|
if (!this._isStreamingSupported && this._isRangeSupported) {
|
||||||
|
this.cancel(new AbortException("Streaming is disabled."));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._headersCapability.resolve();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (error.code === "ENOENT") {
|
||||||
|
error = createResponseError(/* status = */ 0, url);
|
||||||
}
|
}
|
||||||
this._storedError = error;
|
|
||||||
this._headersCapability.reject(error);
|
this._headersCapability.reject(error);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get headersReady() {
|
|
||||||
return this._headersCapability.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
get filename() {
|
|
||||||
return this._filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contentLength() {
|
|
||||||
return this._contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRangeSupported() {
|
|
||||||
return this._isRangeSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._isStreamingSupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
await this._readCapability.promise;
|
await this._headersCapability.promise;
|
||||||
if (this._done) {
|
const { value, done } = await this._reader.read();
|
||||||
return { value: undefined, done: true };
|
if (done) {
|
||||||
}
|
return { value, done };
|
||||||
if (this._storedError) {
|
|
||||||
throw this._storedError;
|
|
||||||
}
|
}
|
||||||
|
this._loaded += value.byteLength;
|
||||||
|
this._callOnProgress();
|
||||||
|
|
||||||
const chunk = this._readableStream.read();
|
return { value: getArrayBuffer(value), done: false };
|
||||||
if (chunk === null) {
|
|
||||||
this._readCapability = Promise.withResolvers();
|
|
||||||
return this.read();
|
|
||||||
}
|
|
||||||
this._loaded += chunk.length;
|
|
||||||
this.onProgress?.({
|
|
||||||
loaded: this._loaded,
|
|
||||||
total: this._contentLength,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure that `read()` method returns ArrayBuffer.
|
|
||||||
const buffer = new Uint8Array(chunk).buffer;
|
|
||||||
return { value: buffer, done: false };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
// Call `this._error()` method when cancel is called
|
this._reader?.cancel(reason);
|
||||||
// before _readableStream is set.
|
|
||||||
if (!this._readableStream) {
|
|
||||||
this._error(reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._readableStream.destroy(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
_error(reason) {
|
|
||||||
this._storedError = reason;
|
|
||||||
this._readCapability.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setReadableStream(readableStream) {
|
|
||||||
this._readableStream = readableStream;
|
|
||||||
readableStream.on("readable", () => {
|
|
||||||
this._readCapability.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
readableStream.on("end", () => {
|
|
||||||
// Destroy readable to minimize resource usage.
|
|
||||||
readableStream.destroy();
|
|
||||||
this._done = true;
|
|
||||||
this._readCapability.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
readableStream.on("error", reason => {
|
|
||||||
this._error(reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
// We need to stop reading when range is supported and streaming is
|
|
||||||
// disabled.
|
|
||||||
if (!this._isStreamingSupported && this._isRangeSupported) {
|
|
||||||
this._error(new AbortException("streaming is disabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy ReadableStream if already in errored state.
|
|
||||||
if (this._storedError) {
|
|
||||||
this._readableStream.destroy(this._storedError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PDFNodeStreamFsRangeReader {
|
class PDFNodeStreamRangeReader extends BasePDFStreamRangeReader {
|
||||||
constructor(stream, start, end) {
|
_readCapability = Promise.withResolvers();
|
||||||
this._url = stream.url;
|
|
||||||
this._done = false;
|
_reader = null;
|
||||||
this._storedError = null;
|
|
||||||
this.onProgress = null;
|
constructor(stream, begin, end) {
|
||||||
this._loaded = 0;
|
super(stream, begin, end);
|
||||||
this._readableStream = null;
|
const { url } = stream._source;
|
||||||
this._readCapability = Promise.withResolvers();
|
|
||||||
const source = stream.source;
|
|
||||||
this._isStreamingSupported = !source.disableStream;
|
|
||||||
|
|
||||||
const fs = process.getBuiltinModule("fs");
|
const fs = process.getBuiltinModule("fs");
|
||||||
this._setReadableStream(
|
try {
|
||||||
fs.createReadStream(this._url, { start, end: end - 1 })
|
const readStream = fs.createReadStream(url, {
|
||||||
);
|
start: begin,
|
||||||
}
|
end: end - 1,
|
||||||
|
});
|
||||||
|
const readableStream = getReadableStream(readStream);
|
||||||
|
|
||||||
get isStreamingSupported() {
|
this._reader = readableStream.getReader();
|
||||||
return this._isStreamingSupported;
|
|
||||||
|
this._readCapability.resolve();
|
||||||
|
} catch (error) {
|
||||||
|
this._readCapability.reject(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
await this._readCapability.promise;
|
await this._readCapability.promise;
|
||||||
if (this._done) {
|
const { value, done } = await this._reader.read();
|
||||||
return { value: undefined, done: true };
|
if (done) {
|
||||||
|
return { value, done };
|
||||||
}
|
}
|
||||||
if (this._storedError) {
|
return { value: getArrayBuffer(value), done: false };
|
||||||
throw this._storedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chunk = this._readableStream.read();
|
|
||||||
if (chunk === null) {
|
|
||||||
this._readCapability = Promise.withResolvers();
|
|
||||||
return this.read();
|
|
||||||
}
|
|
||||||
this._loaded += chunk.length;
|
|
||||||
this.onProgress?.({ loaded: this._loaded });
|
|
||||||
|
|
||||||
// Ensure that `read()` method returns ArrayBuffer.
|
|
||||||
const buffer = new Uint8Array(chunk).buffer;
|
|
||||||
return { value: buffer, done: false };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
// Call `this._error()` method when cancel is called
|
this._reader?.cancel(reason);
|
||||||
// before _readableStream is set.
|
|
||||||
if (!this._readableStream) {
|
|
||||||
this._error(reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._readableStream.destroy(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
_error(reason) {
|
|
||||||
this._storedError = reason;
|
|
||||||
this._readCapability.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setReadableStream(readableStream) {
|
|
||||||
this._readableStream = readableStream;
|
|
||||||
readableStream.on("readable", () => {
|
|
||||||
this._readCapability.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
readableStream.on("end", () => {
|
|
||||||
// Destroy readableStream to minimize resource usage.
|
|
||||||
readableStream.destroy();
|
|
||||||
this._done = true;
|
|
||||||
this._readCapability.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
readableStream.on("error", reason => {
|
|
||||||
this._error(reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Destroy readableStream if already in errored state.
|
|
||||||
if (this._storedError) {
|
|
||||||
this._readableStream.destroy(this._storedError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -294,6 +294,9 @@ class TextLayer {
|
|||||||
if (item.id) {
|
if (item.id) {
|
||||||
this.#container.setAttribute("id", `${item.id}`);
|
this.#container.setAttribute("id", `${item.id}`);
|
||||||
}
|
}
|
||||||
|
if (item.tag === "Artifact") {
|
||||||
|
this.#container.ariaHidden = true;
|
||||||
|
}
|
||||||
parent.append(this.#container);
|
parent.append(this.#container);
|
||||||
} else if (item.type === "endMarkedContent") {
|
} else if (item.type === "endMarkedContent") {
|
||||||
this.#container = this.#container.parentNode;
|
this.#container = this.#container.parentNode;
|
||||||
|
|||||||
@ -13,185 +13,141 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @typedef {import("../interfaces").IPDFStream} IPDFStream */
|
import {
|
||||||
/** @typedef {import("../interfaces").IPDFStreamReader} IPDFStreamReader */
|
BasePDFStream,
|
||||||
// eslint-disable-next-line max-len
|
BasePDFStreamRangeReader,
|
||||||
/** @typedef {import("../interfaces").IPDFStreamRangeReader} IPDFStreamRangeReader */
|
BasePDFStreamReader,
|
||||||
|
} from "../shared/base_pdf_stream.js";
|
||||||
import { assert } from "../shared/util.js";
|
import { assert } from "../shared/util.js";
|
||||||
import { isPdfFile } from "./display_utils.js";
|
import { isPdfFile } from "./display_utils.js";
|
||||||
|
|
||||||
/** @implements {IPDFStream} */
|
function getArrayBuffer(val) {
|
||||||
class PDFDataTransportStream {
|
// Prevent any possible issues by only transferring a Uint8Array that
|
||||||
constructor(
|
// completely "utilizes" its underlying ArrayBuffer.
|
||||||
pdfDataRangeTransport,
|
return val instanceof Uint8Array && val.byteLength === val.buffer.byteLength
|
||||||
{ disableRange = false, disableStream = false }
|
? val.buffer
|
||||||
) {
|
: new Uint8Array(val).buffer;
|
||||||
assert(
|
}
|
||||||
pdfDataRangeTransport,
|
|
||||||
'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.'
|
|
||||||
);
|
|
||||||
const { length, initialData, progressiveDone, contentDispositionFilename } =
|
|
||||||
pdfDataRangeTransport;
|
|
||||||
|
|
||||||
this._queuedChunks = [];
|
class PDFDataTransportStream extends BasePDFStream {
|
||||||
this._progressiveDone = progressiveDone;
|
_progressiveDone = false;
|
||||||
this._contentDispositionFilename = contentDispositionFilename;
|
|
||||||
|
_queuedChunks = [];
|
||||||
|
|
||||||
|
constructor(source) {
|
||||||
|
super(
|
||||||
|
source,
|
||||||
|
PDFDataTransportStreamReader,
|
||||||
|
PDFDataTransportStreamRangeReader
|
||||||
|
);
|
||||||
|
const { pdfDataRangeTransport } = source;
|
||||||
|
const { initialData, progressiveDone } = pdfDataRangeTransport;
|
||||||
|
|
||||||
if (initialData?.length > 0) {
|
if (initialData?.length > 0) {
|
||||||
// Prevent any possible issues by only transferring a Uint8Array that
|
const buffer = getArrayBuffer(initialData);
|
||||||
// completely "utilizes" its underlying ArrayBuffer.
|
|
||||||
const buffer =
|
|
||||||
initialData instanceof Uint8Array &&
|
|
||||||
initialData.byteLength === initialData.buffer.byteLength
|
|
||||||
? initialData.buffer
|
|
||||||
: new Uint8Array(initialData).buffer;
|
|
||||||
this._queuedChunks.push(buffer);
|
this._queuedChunks.push(buffer);
|
||||||
}
|
}
|
||||||
|
this._progressiveDone = progressiveDone;
|
||||||
this._pdfDataRangeTransport = pdfDataRangeTransport;
|
|
||||||
this._isStreamingSupported = !disableStream;
|
|
||||||
this._isRangeSupported = !disableRange;
|
|
||||||
this._contentLength = length;
|
|
||||||
|
|
||||||
this._fullRequestReader = null;
|
|
||||||
this._rangeReaders = [];
|
|
||||||
|
|
||||||
pdfDataRangeTransport.addRangeListener((begin, chunk) => {
|
pdfDataRangeTransport.addRangeListener((begin, chunk) => {
|
||||||
this._onReceiveData({ begin, chunk });
|
this.#onReceiveData(begin, chunk);
|
||||||
});
|
|
||||||
|
|
||||||
pdfDataRangeTransport.addProgressListener((loaded, total) => {
|
|
||||||
this._onProgress({ loaded, total });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pdfDataRangeTransport.addProgressiveReadListener(chunk => {
|
pdfDataRangeTransport.addProgressiveReadListener(chunk => {
|
||||||
this._onReceiveData({ chunk });
|
this.#onReceiveData(/* begin = */ undefined, chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
pdfDataRangeTransport.addProgressiveDoneListener(() => {
|
pdfDataRangeTransport.addProgressiveDoneListener(() => {
|
||||||
this._onProgressiveDone();
|
this._fullReader?.progressiveDone();
|
||||||
|
this._progressiveDone = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
pdfDataRangeTransport.transportReady();
|
pdfDataRangeTransport.transportReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onReceiveData({ begin, chunk }) {
|
#onReceiveData(begin, chunk) {
|
||||||
// Prevent any possible issues by only transferring a Uint8Array that
|
const buffer = getArrayBuffer(chunk);
|
||||||
// completely "utilizes" its underlying ArrayBuffer.
|
|
||||||
const buffer =
|
|
||||||
chunk instanceof Uint8Array &&
|
|
||||||
chunk.byteLength === chunk.buffer.byteLength
|
|
||||||
? chunk.buffer
|
|
||||||
: new Uint8Array(chunk).buffer;
|
|
||||||
|
|
||||||
if (begin === undefined) {
|
if (begin === undefined) {
|
||||||
if (this._fullRequestReader) {
|
if (this._fullReader) {
|
||||||
this._fullRequestReader._enqueue(buffer);
|
this._fullReader._enqueue(buffer);
|
||||||
} else {
|
} else {
|
||||||
this._queuedChunks.push(buffer);
|
this._queuedChunks.push(buffer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const found = this._rangeReaders.some(function (rangeReader) {
|
const rangeReader = this._rangeReaders
|
||||||
if (rangeReader._begin !== begin) {
|
.keys()
|
||||||
return false;
|
.find(r => r._begin === begin);
|
||||||
}
|
|
||||||
rangeReader._enqueue(buffer);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
assert(
|
assert(
|
||||||
found,
|
rangeReader,
|
||||||
"_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."
|
"#onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."
|
||||||
);
|
);
|
||||||
}
|
rangeReader._enqueue(buffer);
|
||||||
}
|
|
||||||
|
|
||||||
get _progressiveDataLength() {
|
|
||||||
return this._fullRequestReader?._loaded ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_onProgress(evt) {
|
|
||||||
if (evt.total === undefined) {
|
|
||||||
// Reporting to first range reader, if it exists.
|
|
||||||
this._rangeReaders[0]?.onProgress?.({ loaded: evt.loaded });
|
|
||||||
} else {
|
|
||||||
this._fullRequestReader?.onProgress?.({
|
|
||||||
loaded: evt.loaded,
|
|
||||||
total: evt.total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onProgressiveDone() {
|
|
||||||
this._fullRequestReader?.progressiveDone();
|
|
||||||
this._progressiveDone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeRangeReader(reader) {
|
|
||||||
const i = this._rangeReaders.indexOf(reader);
|
|
||||||
if (i >= 0) {
|
|
||||||
this._rangeReaders.splice(i, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getFullReader() {
|
getFullReader() {
|
||||||
assert(
|
const reader = super.getFullReader();
|
||||||
!this._fullRequestReader,
|
|
||||||
"PDFDataTransportStream.getFullReader can only be called once."
|
|
||||||
);
|
|
||||||
const queuedChunks = this._queuedChunks;
|
|
||||||
this._queuedChunks = null;
|
this._queuedChunks = null;
|
||||||
return new PDFDataTransportStreamReader(
|
return reader;
|
||||||
this,
|
|
||||||
queuedChunks,
|
|
||||||
this._progressiveDone,
|
|
||||||
this._contentDispositionFilename
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRangeReader(begin, end) {
|
getRangeReader(begin, end) {
|
||||||
if (end <= this._progressiveDataLength) {
|
const reader = super.getRangeReader(begin, end);
|
||||||
return null;
|
|
||||||
|
if (reader) {
|
||||||
|
reader.onDone = () => this._rangeReaders.delete(reader);
|
||||||
|
|
||||||
|
this._source.pdfDataRangeTransport.requestDataRange(begin, end);
|
||||||
}
|
}
|
||||||
const reader = new PDFDataTransportStreamRangeReader(this, begin, end);
|
|
||||||
this._pdfDataRangeTransport.requestDataRange(begin, end);
|
|
||||||
this._rangeReaders.push(reader);
|
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllRequests(reason) {
|
cancelAllRequests(reason) {
|
||||||
this._fullRequestReader?.cancel(reason);
|
super.cancelAllRequests(reason);
|
||||||
|
|
||||||
for (const reader of this._rangeReaders.slice(0)) {
|
this._source.pdfDataRangeTransport.abort();
|
||||||
reader.cancel(reason);
|
|
||||||
}
|
|
||||||
this._pdfDataRangeTransport.abort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamReader} */
|
class PDFDataTransportStreamReader extends BasePDFStreamReader {
|
||||||
class PDFDataTransportStreamReader {
|
_done = false;
|
||||||
constructor(
|
|
||||||
stream,
|
_queuedChunks = null;
|
||||||
queuedChunks,
|
|
||||||
progressiveDone = false,
|
_requests = [];
|
||||||
contentDispositionFilename = null
|
|
||||||
) {
|
constructor(stream) {
|
||||||
this._stream = stream;
|
super(stream);
|
||||||
this._done = progressiveDone || false;
|
const { pdfDataRangeTransport, disableRange, disableStream } =
|
||||||
this._filename = isPdfFile(contentDispositionFilename)
|
stream._source;
|
||||||
? contentDispositionFilename
|
const { length, contentDispositionFilename } = pdfDataRangeTransport;
|
||||||
: null;
|
|
||||||
this._queuedChunks = queuedChunks || [];
|
this._queuedChunks = stream._queuedChunks || [];
|
||||||
this._loaded = 0;
|
|
||||||
for (const chunk of this._queuedChunks) {
|
for (const chunk of this._queuedChunks) {
|
||||||
this._loaded += chunk.byteLength;
|
this._loaded += chunk.byteLength;
|
||||||
}
|
}
|
||||||
this._requests = [];
|
this._done = stream._progressiveDone;
|
||||||
this._headersReady = Promise.resolve();
|
|
||||||
stream._fullRequestReader = this;
|
|
||||||
|
|
||||||
this.onProgress = null;
|
this._contentLength = length;
|
||||||
|
this._isStreamingSupported = !disableStream;
|
||||||
|
this._isRangeSupported = !disableRange;
|
||||||
|
|
||||||
|
if (isPdfFile(contentDispositionFilename)) {
|
||||||
|
this._filename = contentDispositionFilename;
|
||||||
|
}
|
||||||
|
this._headersCapability.resolve();
|
||||||
|
|
||||||
|
// Report loading progress when there is `initialData`, and `_enqueue` has
|
||||||
|
// not been invoked, but with a small delay to give an `onProgress` callback
|
||||||
|
// a chance to be registered first.
|
||||||
|
const loaded = this._loaded;
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
if (loaded > 0 && this._loaded === loaded) {
|
||||||
|
this._callOnProgress();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_enqueue(chunk) {
|
_enqueue(chunk) {
|
||||||
@ -199,32 +155,13 @@ class PDFDataTransportStreamReader {
|
|||||||
return; // Ignore new data.
|
return; // Ignore new data.
|
||||||
}
|
}
|
||||||
if (this._requests.length > 0) {
|
if (this._requests.length > 0) {
|
||||||
const requestCapability = this._requests.shift();
|
const capability = this._requests.shift();
|
||||||
requestCapability.resolve({ value: chunk, done: false });
|
capability.resolve({ value: chunk, done: false });
|
||||||
} else {
|
} else {
|
||||||
this._queuedChunks.push(chunk);
|
this._queuedChunks.push(chunk);
|
||||||
}
|
}
|
||||||
this._loaded += chunk.byteLength;
|
this._loaded += chunk.byteLength;
|
||||||
}
|
this._callOnProgress();
|
||||||
|
|
||||||
get headersReady() {
|
|
||||||
return this._headersReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
get filename() {
|
|
||||||
return this._filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRangeSupported() {
|
|
||||||
return this._stream._isRangeSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return this._stream._isStreamingSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
get contentLength() {
|
|
||||||
return this._stream._contentLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
@ -235,38 +172,38 @@ class PDFDataTransportStreamReader {
|
|||||||
if (this._done) {
|
if (this._done) {
|
||||||
return { value: undefined, done: true };
|
return { value: undefined, done: true };
|
||||||
}
|
}
|
||||||
const requestCapability = Promise.withResolvers();
|
const capability = Promise.withResolvers();
|
||||||
this._requests.push(requestCapability);
|
this._requests.push(capability);
|
||||||
return requestCapability.promise;
|
return capability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressiveDone() {
|
progressiveDone() {
|
||||||
if (this._done) {
|
this._done ||= true;
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._done = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @implements {IPDFStreamRangeReader} */
|
class PDFDataTransportStreamRangeReader extends BasePDFStreamRangeReader {
|
||||||
class PDFDataTransportStreamRangeReader {
|
onDone = null;
|
||||||
constructor(stream, begin, end) {
|
|
||||||
this._stream = stream;
|
|
||||||
this._begin = begin;
|
|
||||||
this._end = end;
|
|
||||||
this._queuedChunk = null;
|
|
||||||
this._requests = [];
|
|
||||||
this._done = false;
|
|
||||||
|
|
||||||
this.onProgress = null;
|
_begin = -1;
|
||||||
|
|
||||||
|
_done = false;
|
||||||
|
|
||||||
|
_queuedChunk = null;
|
||||||
|
|
||||||
|
_requests = [];
|
||||||
|
|
||||||
|
constructor(stream, begin, end) {
|
||||||
|
super(stream, begin, end);
|
||||||
|
this._begin = begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
_enqueue(chunk) {
|
_enqueue(chunk) {
|
||||||
@ -276,19 +213,16 @@ class PDFDataTransportStreamRangeReader {
|
|||||||
if (this._requests.length === 0) {
|
if (this._requests.length === 0) {
|
||||||
this._queuedChunk = chunk;
|
this._queuedChunk = chunk;
|
||||||
} else {
|
} else {
|
||||||
const requestsCapability = this._requests.shift();
|
const firstCapability = this._requests.shift();
|
||||||
requestsCapability.resolve({ value: chunk, done: false });
|
firstCapability.resolve({ value: chunk, done: false });
|
||||||
for (const requestCapability of this._requests) {
|
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
for (const capability of this._requests) {
|
||||||
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
}
|
}
|
||||||
this._done = true;
|
this._done = true;
|
||||||
this._stream._removeRangeReader(this);
|
this.onDone?.();
|
||||||
}
|
|
||||||
|
|
||||||
get isStreamingSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async read() {
|
async read() {
|
||||||
@ -300,18 +234,18 @@ class PDFDataTransportStreamRangeReader {
|
|||||||
if (this._done) {
|
if (this._done) {
|
||||||
return { value: undefined, done: true };
|
return { value: undefined, done: true };
|
||||||
}
|
}
|
||||||
const requestCapability = Promise.withResolvers();
|
const capability = Promise.withResolvers();
|
||||||
this._requests.push(requestCapability);
|
this._requests.push(capability);
|
||||||
return requestCapability.promise;
|
return capability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(reason) {
|
cancel(reason) {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
for (const requestCapability of this._requests) {
|
for (const capability of this._requests) {
|
||||||
requestCapability.resolve({ value: undefined, done: true });
|
capability.resolve({ value: undefined, done: true });
|
||||||
}
|
}
|
||||||
this._requests.length = 0;
|
this._requests.length = 0;
|
||||||
this._stream._removeRangeReader(this);
|
this.onDone?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("./annotation_storage").AnnotationStorage} AnnotationStorage */
|
/** @typedef {import("./annotation_storage").AnnotationStorage} AnnotationStorage */
|
||||||
/** @typedef {import("./display_utils").PageViewport} PageViewport */
|
/** @typedef {import("./display_utils").PageViewport} PageViewport */
|
||||||
/** @typedef {import("../../web/interfaces").IPDFLinkService} IPDFLinkService */
|
|
||||||
|
|
||||||
import { XfaText } from "./xfa_text.js";
|
import { XfaText } from "./xfa_text.js";
|
||||||
|
|
||||||
@ -26,7 +25,7 @@ import { XfaText } from "./xfa_text.js";
|
|||||||
* @property {HTMLDivElement} div
|
* @property {HTMLDivElement} div
|
||||||
* @property {Object} xfaHtml
|
* @property {Object} xfaHtml
|
||||||
* @property {AnnotationStorage} [annotationStorage]
|
* @property {AnnotationStorage} [annotationStorage]
|
||||||
* @property {IPDFLinkService} linkService
|
* @property {PDFLinkService} linkService
|
||||||
* @property {string} [intent] - (default value is 'display').
|
* @property {string} [intent] - (default value is 'display').
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,7 @@ import {
|
|||||||
isPdfFile,
|
isPdfFile,
|
||||||
noContextMenu,
|
noContextMenu,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
|
PagesMapper,
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
PixelsPerInch,
|
PixelsPerInch,
|
||||||
RenderingCancelledException,
|
RenderingCancelledException,
|
||||||
@ -128,6 +129,7 @@ globalThis.pdfjsLib = {
|
|||||||
normalizeUnicode,
|
normalizeUnicode,
|
||||||
OPS,
|
OPS,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
|
PagesMapper,
|
||||||
PasswordResponses,
|
PasswordResponses,
|
||||||
PDFDataRangeTransport,
|
PDFDataRangeTransport,
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
@ -187,6 +189,7 @@ export {
|
|||||||
normalizeUnicode,
|
normalizeUnicode,
|
||||||
OPS,
|
OPS,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
|
PagesMapper,
|
||||||
PasswordResponses,
|
PasswordResponses,
|
||||||
PDFDataRangeTransport,
|
PDFDataRangeTransport,
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
|
|||||||
@ -13,56 +13,123 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { assert, unreachable } from "./util.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that represents PDF data transport. If possible, it allows
|
* Interface that represents PDF data transport. If possible, it allows
|
||||||
* progressively load entire or fragment of the PDF binary data.
|
* progressively load entire or fragment of the PDF binary data.
|
||||||
*
|
|
||||||
* @interface
|
|
||||||
*/
|
*/
|
||||||
class IPDFStream {
|
class BasePDFStream {
|
||||||
|
#PDFStreamReader = null;
|
||||||
|
|
||||||
|
#PDFStreamRangeReader = null;
|
||||||
|
|
||||||
|
_fullReader = null;
|
||||||
|
|
||||||
|
_rangeReaders = new Set();
|
||||||
|
|
||||||
|
_source = null;
|
||||||
|
|
||||||
|
constructor(source, PDFStreamReader, PDFStreamRangeReader) {
|
||||||
|
if (
|
||||||
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
||||||
|
this.constructor === BasePDFStream
|
||||||
|
) {
|
||||||
|
unreachable("Cannot initialize BasePDFStream.");
|
||||||
|
}
|
||||||
|
this._source = source;
|
||||||
|
|
||||||
|
this.#PDFStreamReader = PDFStreamReader;
|
||||||
|
this.#PDFStreamRangeReader = PDFStreamRangeReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
get _progressiveDataLength() {
|
||||||
|
return this._fullReader?._loaded ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a reader for the entire PDF data.
|
* Gets a reader for the entire PDF data.
|
||||||
* @returns {IPDFStreamReader}
|
* @returns {BasePDFStreamReader}
|
||||||
*/
|
*/
|
||||||
getFullReader() {
|
getFullReader() {
|
||||||
return null;
|
assert(
|
||||||
|
!this._fullReader,
|
||||||
|
"BasePDFStream.getFullReader can only be called once."
|
||||||
|
);
|
||||||
|
return (this._fullReader = new this.#PDFStreamReader(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a reader for the range of the PDF data.
|
* Gets a reader for the range of the PDF data.
|
||||||
*
|
*
|
||||||
* NOTE: Currently this method is only expected to be invoked *after*
|
* NOTE: Currently this method is only expected to be invoked *after*
|
||||||
* the `IPDFStreamReader.prototype.headersReady` promise has resolved.
|
* the `BasePDFStreamReader.prototype.headersReady` promise has resolved.
|
||||||
*
|
*
|
||||||
* @param {number} begin - the start offset of the data.
|
* @param {number} begin - the start offset of the data.
|
||||||
* @param {number} end - the end offset of the data.
|
* @param {number} end - the end offset of the data.
|
||||||
* @returns {IPDFStreamRangeReader}
|
* @returns {BasePDFStreamRangeReader}
|
||||||
*/
|
*/
|
||||||
getRangeReader(begin, end) {
|
getRangeReader(begin, end) {
|
||||||
return null;
|
if (end <= this._progressiveDataLength) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const reader = new this.#PDFStreamRangeReader(this, begin, end);
|
||||||
|
this._rangeReaders.add(reader);
|
||||||
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels all opened reader and closes all their opened requests.
|
* Cancels all opened reader and closes all their opened requests.
|
||||||
* @param {Object} reason - the reason for cancelling
|
* @param {Object} reason - the reason for cancelling
|
||||||
*/
|
*/
|
||||||
cancelAllRequests(reason) {}
|
cancelAllRequests(reason) {
|
||||||
|
this._fullReader?.cancel(reason);
|
||||||
|
|
||||||
|
// Always create a copy of the rangeReaders.
|
||||||
|
for (const reader of new Set(this._rangeReaders)) {
|
||||||
|
reader.cancel(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for a PDF binary data reader.
|
* Interface for a PDF binary data reader.
|
||||||
*
|
|
||||||
* @interface
|
|
||||||
*/
|
*/
|
||||||
class IPDFStreamReader {
|
class BasePDFStreamReader {
|
||||||
constructor() {
|
/**
|
||||||
/**
|
* Sets or gets the progress callback. The callback can be useful when the
|
||||||
* Sets or gets the progress callback. The callback can be useful when the
|
* isStreamingSupported property of the object is defined as false.
|
||||||
* isStreamingSupported property of the object is defined as false.
|
* The callback is called with one parameter: an object with the loaded and
|
||||||
* The callback is called with one parameter: an object with the loaded and
|
* total properties.
|
||||||
* total properties.
|
*/
|
||||||
*/
|
onProgress = null;
|
||||||
this.onProgress = null;
|
|
||||||
|
_contentLength = 0;
|
||||||
|
|
||||||
|
_filename = null;
|
||||||
|
|
||||||
|
_headersCapability = Promise.withResolvers();
|
||||||
|
|
||||||
|
_isRangeSupported = false;
|
||||||
|
|
||||||
|
_isStreamingSupported = false;
|
||||||
|
|
||||||
|
_loaded = 0;
|
||||||
|
|
||||||
|
_stream = null;
|
||||||
|
|
||||||
|
constructor(stream) {
|
||||||
|
if (
|
||||||
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
||||||
|
this.constructor === BasePDFStreamReader
|
||||||
|
) {
|
||||||
|
unreachable("Cannot initialize BasePDFStreamReader.");
|
||||||
|
}
|
||||||
|
this._stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
_callOnProgress() {
|
||||||
|
this.onProgress?.({ loaded: this._loaded, total: this._contentLength });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +138,7 @@ class IPDFStreamReader {
|
|||||||
* @type {Promise}
|
* @type {Promise}
|
||||||
*/
|
*/
|
||||||
get headersReady() {
|
get headersReady() {
|
||||||
return Promise.resolve();
|
return this._headersCapability.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +148,7 @@ class IPDFStreamReader {
|
|||||||
* header is missing/invalid.
|
* header is missing/invalid.
|
||||||
*/
|
*/
|
||||||
get filename() {
|
get filename() {
|
||||||
return null;
|
return this._filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +157,7 @@ class IPDFStreamReader {
|
|||||||
* @type {number} The data length (or 0 if unknown).
|
* @type {number} The data length (or 0 if unknown).
|
||||||
*/
|
*/
|
||||||
get contentLength() {
|
get contentLength() {
|
||||||
return 0;
|
return this._contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +167,7 @@ class IPDFStreamReader {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
get isRangeSupported() {
|
get isRangeSupported() {
|
||||||
return false;
|
return this._isRangeSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +176,7 @@ class IPDFStreamReader {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
get isStreamingSupported() {
|
get isStreamingSupported() {
|
||||||
return false;
|
return this._isStreamingSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,37 +187,33 @@ class IPDFStreamReader {
|
|||||||
* set to true.
|
* set to true.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async read() {}
|
async read() {
|
||||||
|
unreachable("Abstract method `read` called");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels all pending read requests and closes the stream.
|
* Cancels all pending read requests and closes the stream.
|
||||||
* @param {Object} reason
|
* @param {Object} reason
|
||||||
*/
|
*/
|
||||||
cancel(reason) {}
|
cancel(reason) {
|
||||||
|
unreachable("Abstract method `cancel` called");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for a PDF binary data fragment reader.
|
* Interface for a PDF binary data fragment reader.
|
||||||
*
|
|
||||||
* @interface
|
|
||||||
*/
|
*/
|
||||||
class IPDFStreamRangeReader {
|
class BasePDFStreamRangeReader {
|
||||||
constructor() {
|
_stream = null;
|
||||||
/**
|
|
||||||
* Sets or gets the progress callback. The callback can be useful when the
|
|
||||||
* isStreamingSupported property of the object is defined as false.
|
|
||||||
* The callback is called with one parameter: an object with the loaded
|
|
||||||
* property.
|
|
||||||
*/
|
|
||||||
this.onProgress = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
constructor(stream, begin, end) {
|
||||||
* Gets ability of the stream to progressively load binary data.
|
if (
|
||||||
* @type {boolean}
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
||||||
*/
|
this.constructor === BasePDFStreamRangeReader
|
||||||
get isStreamingSupported() {
|
) {
|
||||||
return false;
|
unreachable("Cannot initialize BasePDFStreamRangeReader.");
|
||||||
|
}
|
||||||
|
this._stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,13 +224,17 @@ class IPDFStreamRangeReader {
|
|||||||
* set to true.
|
* set to true.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async read() {}
|
async read() {
|
||||||
|
unreachable("Abstract method `read` called");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels all pending read requests and closes the stream.
|
* Cancels all pending read requests and closes the stream.
|
||||||
* @param {Object} reason
|
* @param {Object} reason
|
||||||
*/
|
*/
|
||||||
cancel(reason) {}
|
cancel(reason) {
|
||||||
|
unreachable("Abstract method `cancel` called");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { IPDFStream, IPDFStreamRangeReader, IPDFStreamReader };
|
export { BasePDFStream, BasePDFStreamRangeReader, BasePDFStreamReader };
|
||||||
@ -1235,47 +1235,12 @@ function MathClamp(v, min, max) {
|
|||||||
return Math.min(Math.max(v, min), max);
|
return Math.min(Math.max(v, min), max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this once `Uint8Array.prototype.toHex` is generally available.
|
// TODO: Remove this once `Math.sumPrecise` is generally available.
|
||||||
function toHexUtil(arr) {
|
|
||||||
if (Uint8Array.prototype.toHex) {
|
|
||||||
return arr.toHex();
|
|
||||||
}
|
|
||||||
return Array.from(arr, num => hexNumbers[num]).join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this once `Uint8Array.prototype.toBase64` is generally
|
|
||||||
// available.
|
|
||||||
function toBase64Util(arr) {
|
|
||||||
if (Uint8Array.prototype.toBase64) {
|
|
||||||
return arr.toBase64();
|
|
||||||
}
|
|
||||||
return btoa(bytesToString(arr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this once `Uint8Array.fromBase64` is generally available.
|
|
||||||
function fromBase64Util(str) {
|
|
||||||
if (Uint8Array.fromBase64) {
|
|
||||||
return Uint8Array.fromBase64(str);
|
|
||||||
}
|
|
||||||
return stringToBytes(atob(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this once https://bugzilla.mozilla.org/show_bug.cgi?id=1928493
|
|
||||||
// is fixed.
|
|
||||||
if (
|
if (
|
||||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("SKIP_BABEL")) &&
|
(typeof PDFJSDev === "undefined" ||
|
||||||
typeof Promise.try !== "function"
|
PDFJSDev.test("SKIP_BABEL && !MOZCENTRAL")) &&
|
||||||
|
typeof Math.sumPrecise !== "function"
|
||||||
) {
|
) {
|
||||||
Promise.try = function (fn, ...args) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
resolve(fn(...args));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this once the `javascript.options.experimental.math_sumprecise`
|
|
||||||
// preference is removed from Firefox.
|
|
||||||
if (typeof Math.sumPrecise !== "function") {
|
|
||||||
// Note that this isn't a "proper" polyfill, but since we're only using it to
|
// Note that this isn't a "proper" polyfill, but since we're only using it to
|
||||||
// replace `Array.prototype.reduce()` invocations it should be fine.
|
// replace `Array.prototype.reduce()` invocations it should be fine.
|
||||||
Math.sumPrecise = function (numbers) {
|
Math.sumPrecise = function (numbers) {
|
||||||
@ -1338,7 +1303,6 @@ export {
|
|||||||
FeatureTest,
|
FeatureTest,
|
||||||
FONT_IDENTITY_MATRIX,
|
FONT_IDENTITY_MATRIX,
|
||||||
FormatError,
|
FormatError,
|
||||||
fromBase64Util,
|
|
||||||
getModificationDate,
|
getModificationDate,
|
||||||
getUuid,
|
getUuid,
|
||||||
getVerbosityLevel,
|
getVerbosityLevel,
|
||||||
@ -1368,8 +1332,6 @@ export {
|
|||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
stringToUTF8String,
|
stringToUTF8String,
|
||||||
TextRenderingMode,
|
TextRenderingMode,
|
||||||
toBase64Util,
|
|
||||||
toHexUtil,
|
|
||||||
UnknownErrorException,
|
UnknownErrorException,
|
||||||
unreachable,
|
unreachable,
|
||||||
updateUrlHash,
|
updateUrlHash,
|
||||||
|
|||||||
@ -628,6 +628,13 @@ class Driver {
|
|||||||
`[${this.currentTask + 1}/${this.manifest.length}] ${task.id}:\n`
|
`[${this.currentTask + 1}/${this.manifest.length}] ${task.id}:\n`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (task.type === "skip-because-failing") {
|
||||||
|
this._log(` Skipping file "${task.file} because it's failing"\n`);
|
||||||
|
this.currentTask++;
|
||||||
|
this._nextTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Support *linked* test-cases for the other suites, e.g. unit- and
|
// Support *linked* test-cases for the other suites, e.g. unit- and
|
||||||
// integration-tests, without needing to run them as reference-tests.
|
// integration-tests, without needing to run them as reference-tests.
|
||||||
if (task.type === "other") {
|
if (task.type === "other") {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { decodeFontData, ttx, verifyTtxOutput } from "./fontutils.js";
|
import { ttx, verifyTtxOutput } from "./fontutils.js";
|
||||||
|
|
||||||
describe("font1", function () {
|
describe("font1", function () {
|
||||||
const font1_1 = decodeFontData(
|
const font1_1 = Uint8Array.fromBase64(
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
"T1RUTwAJAIAAAwAQQ0ZGIP/t0rAAAACcAAADKU9TLzJDxycMAAADyAAAAGBjbWFwwFIBcgAABCgAAABUaGVhZKsnTJ4AAAR8AAAANmhoZWEDHvxTAAAEtAAAACRobXR4AAAAAAAABNgAAAA4bWF4cAAOUAAAAAUQAAAABm5hbWX8Fq+xAAAFGAAAAfhwb3N0AAMAAAAABxAAAAAgAQAEAgABAQEMS0hQRkxFK01UU1kAAQEBOfgeAPgfAfggAvghA/gXBIv+Tvqn+bAFHQAAAMgPHQAAAL0QHQAAANsRHQAAACcdAAADARL4IAwWAAcBAQgUGx5TV19yYWRpY2FsY2lyY2xlY29weXJ0c2ltaWxhcjEuMUNvcHlyaWdodCAoQykgMTk5MiwgMTk5MyBUaGUgVGVYcGxvcmF0b3JzIENvcnBvcmF0aW9uTVRTWU1hdGhUaW1lAAAAAAkAAg0YQ0RmZ3AAAKYAqAGIAYkADAAeAFwAXgGHAAoCAAEAAwAWAFoAtgDxARcBNgGKAd4CDiAO93W9Ad/4+AP5TPd1Fb38+FkHDvfslp/3PtH3Pp8B9xjR9zDQ9zDRFPz4P/eAFfd193UFRQb7UvtS+1L3UgVFBvd1+3X7dvt1BdIG91L3UvdS+1IF0gYO+MT7ZbP5vLMBw7P5vLMD+kT3fxX3iPtc91z7iPuI+1z7XPuI+4j3XPtc94j3iPdc91z3iB78UPwoFft0+0f3SPd093T3R/dI93T3dPdI+0j7dPt0+0j7SPt0Hw73Zb33Br0Bw/kwA/ln+C8VT3o8Lz8hMvc4+xYbP0E/WncfQIwH3KLi0Mb3AuL7OPcUG9nc272ZH9IHDjig97O997SfAfgBvQP5aPd1Fb37yffIWfvI+8lZ98n7yL33yAcO9MP3JsMBw/kwA/lo98cVw/0wUwf5MPteFcP9MFMHDkX7SaD4JJ/4JJ8B9yXVA/dv9w0V0n6yPZwejQfZnZiy0hr3PAfQn7HSmx6WByRNd/sLH/tGB0t7bEZ5HtB4m2xLGvtFB/sMyXfyHpYHRJt3sdAaDkX7SaD4JJ/4JJ8B9yvVA/d19xwVy5uq0J4eRp17qssa90UH9wxNnyQegAfSe59lRhr7PAdEmGTZeh6JBz15fmREGvs8B0Z3ZUR7HoAH8smf9wsfDvgq/k6g99/k+LCfAcD5yAP4Kf5OFZUG+F76fQVWBvwe/fT7cffE+yz7KJp23dsFDnie+GWenJD3K54G+2WiBx4KBI8MCb0KvQufqQwMqZ8MDfmgFPhMFQAAAAAAAwIkAfQABQAAAooCuwAAAIwCigK7AAAB3wAxAQIAAAAABgAAAAAAAAAAAAABEAAAAAAAAAAAAAAAKjIxKgAAAEPgBwMc/EYAZAMcA7oAAAAAAAAAAAAAAAAAAABDAAMAAAABAAMAAQAAAAwABABIAAAACgAIAAIAAgBEAGcAcOAH//8AAABDAGYAcOAA////wv+h/5kAAAABAAAAAAAAAAQAAAABAAEAAgACAAMAAwAEAAQAAQAAAAAQAAAAAABfDzz1AAAD6AAAAACeC34nAAAAAJ4LficAAPxGD/8DHAAAABEAAAAAAAAAAAABAAADHPxGAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAOAAAAAAAUAPYAAQAAAAAAAAAQAAAAAQAAAAAAAQALABAAAQAAAAAAAgAHABsAAQAAAAAAAwAIACIAAQAAAAAABAALACoAAQAAAAAABQAMADUAAQAAAAAABgAAAEEAAQAAAAAABwAHAEEAAQAAAAAACAAHAEgAAQAAAAAACQAHAE8AAwABBAkAAAAgAFYAAwABBAkAAQAWAHYAAwABBAkAAgAOAIwAAwABBAkAAwAQAJoAAwABBAkABAAWAKoAAwABBAkABQAYAMAAAwABBAkABgAAANgAAwABBAkABwAOANgAAwABBAkACAAOAOYAAwABBAkACQAOAPRPcmlnaW5hbCBsaWNlbmNlS0hQRkxFK01UU1lVbmtub3dudW5pcXVlSURLSFBGTEUrTVRTWVZlcnNpb24gMC4xMVVua25vd25Vbmtub3duVW5rbm93bgBPAHIAaQBnAGkAbgBhAGwAIABsAGkAYwBlAG4AYwBlAEsASABQAEYATABFACsATQBUAFMAWQBVAG4AawBuAG8AdwBuAHUAbgBpAHEAdQBlAEkARABLAEgAUABGAEwARQArAE0AVABTAFkAVgBlAHIAcwBpAG8AbgAgADAALgAxADEAVQBuAGsAbgBvAHcAbgBVAG4AawBuAG8AdwBuAFUAbgBrAG4AbwB3AG4AAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
"T1RUTwAJAIAAAwAQQ0ZGIP/t0rAAAACcAAADKU9TLzJDxycMAAADyAAAAGBjbWFwwFIBcgAABCgAAABUaGVhZKsnTJ4AAAR8AAAANmhoZWEDHvxTAAAEtAAAACRobXR4AAAAAAAABNgAAAA4bWF4cAAOUAAAAAUQAAAABm5hbWX8Fq+xAAAFGAAAAfhwb3N0AAMAAAAABxAAAAAgAQAEAgABAQEMS0hQRkxFK01UU1kAAQEBOfgeAPgfAfggAvghA/gXBIv+Tvqn+bAFHQAAAMgPHQAAAL0QHQAAANsRHQAAACcdAAADARL4IAwWAAcBAQgUGx5TV19yYWRpY2FsY2lyY2xlY29weXJ0c2ltaWxhcjEuMUNvcHlyaWdodCAoQykgMTk5MiwgMTk5MyBUaGUgVGVYcGxvcmF0b3JzIENvcnBvcmF0aW9uTVRTWU1hdGhUaW1lAAAAAAkAAg0YQ0RmZ3AAAKYAqAGIAYkADAAeAFwAXgGHAAoCAAEAAwAWAFoAtgDxARcBNgGKAd4CDiAO93W9Ad/4+AP5TPd1Fb38+FkHDvfslp/3PtH3Pp8B9xjR9zDQ9zDRFPz4P/eAFfd193UFRQb7UvtS+1L3UgVFBvd1+3X7dvt1BdIG91L3UvdS+1IF0gYO+MT7ZbP5vLMBw7P5vLMD+kT3fxX3iPtc91z7iPuI+1z7XPuI+4j3XPtc94j3iPdc91z3iB78UPwoFft0+0f3SPd093T3R/dI93T3dPdI+0j7dPt0+0j7SPt0Hw73Zb33Br0Bw/kwA/ln+C8VT3o8Lz8hMvc4+xYbP0E/WncfQIwH3KLi0Mb3AuL7OPcUG9nc272ZH9IHDjig97O997SfAfgBvQP5aPd1Fb37yffIWfvI+8lZ98n7yL33yAcO9MP3JsMBw/kwA/lo98cVw/0wUwf5MPteFcP9MFMHDkX7SaD4JJ/4JJ8B9yXVA/dv9w0V0n6yPZwejQfZnZiy0hr3PAfQn7HSmx6WByRNd/sLH/tGB0t7bEZ5HtB4m2xLGvtFB/sMyXfyHpYHRJt3sdAaDkX7SaD4JJ/4JJ8B9yvVA/d19xwVy5uq0J4eRp17qssa90UH9wxNnyQegAfSe59lRhr7PAdEmGTZeh6JBz15fmREGvs8B0Z3ZUR7HoAH8smf9wsfDvgq/k6g99/k+LCfAcD5yAP4Kf5OFZUG+F76fQVWBvwe/fT7cffE+yz7KJp23dsFDnie+GWenJD3K54G+2WiBx4KBI8MCb0KvQufqQwMqZ8MDfmgFPhMFQAAAAAAAwIkAfQABQAAAooCuwAAAIwCigK7AAAB3wAxAQIAAAAABgAAAAAAAAAAAAABEAAAAAAAAAAAAAAAKjIxKgAAAEPgBwMc/EYAZAMcA7oAAAAAAAAAAAAAAAAAAABDAAMAAAABAAMAAQAAAAwABABIAAAACgAIAAIAAgBEAGcAcOAH//8AAABDAGYAcOAA////wv+h/5kAAAABAAAAAAAAAAQAAAABAAEAAgACAAMAAwAEAAQAAQAAAAAQAAAAAABfDzz1AAAD6AAAAACeC34nAAAAAJ4LficAAPxGD/8DHAAAABEAAAAAAAAAAAABAAADHPxGAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAOAAAAAAAUAPYAAQAAAAAAAAAQAAAAAQAAAAAAAQALABAAAQAAAAAAAgAHABsAAQAAAAAAAwAIACIAAQAAAAAABAALACoAAQAAAAAABQAMADUAAQAAAAAABgAAAEEAAQAAAAAABwAHAEEAAQAAAAAACAAHAEgAAQAAAAAACQAHAE8AAwABBAkAAAAgAFYAAwABBAkAAQAWAHYAAwABBAkAAgAOAIwAAwABBAkAAwAQAJoAAwABBAkABAAWAKoAAwABBAkABQAYAMAAAwABBAkABgAAANgAAwABBAkABwAOANgAAwABBAkACAAOAOYAAwABBAkACQAOAPRPcmlnaW5hbCBsaWNlbmNlS0hQRkxFK01UU1lVbmtub3dudW5pcXVlSURLSFBGTEUrTVRTWVZlcnNpb24gMC4xMVVua25vd25Vbmtub3duVW5rbm93bgBPAHIAaQBnAGkAbgBhAGwAIABsAGkAYwBlAG4AYwBlAEsASABQAEYATABFACsATQBUAFMAWQBVAG4AawBuAG8AdwBuAHUAbgBpAHEAdQBlAEkARABLAEgAUABGAEwARQArAE0AVABTAFkAVgBlAHIAcwBpAG8AbgAgADAALgAxADEAVQBuAGsAbgBvAHcAbgBVAG4AawBuAG8AdwBuAFUAbgBrAG4AbwB3AG4AAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -14,22 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { bytesToString, stringToBytes } from "../../src/shared/util.js";
|
|
||||||
|
|
||||||
function decodeFontData(base64) {
|
|
||||||
const str = atob(base64);
|
|
||||||
return stringToBytes(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeFontData(data) {
|
|
||||||
const str = bytesToString(data);
|
|
||||||
return btoa(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ttx(data) {
|
async function ttx(data) {
|
||||||
const response = await fetch("/ttx", {
|
const response = await fetch("/ttx", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: encodeFontData(data),
|
body: data.toBase64(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -45,4 +33,4 @@ function verifyTtxOutput(output) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { decodeFontData, encodeFontData, ttx, verifyTtxOutput };
|
export { ttx, verifyTtxOutput };
|
||||||
|
|||||||
@ -430,4 +430,185 @@ describe("accessibility", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Artifacts must be aria-hidden", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("bug1937438_mml_from_latex.pdf", ".textLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that some artifacts are aria-hidden", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const parentSquareRootHidden = await page.evaluate(() => {
|
||||||
|
for (const span of document.querySelectorAll(".textLayer span")) {
|
||||||
|
if (span.textContent === "√") {
|
||||||
|
return span.parentElement.getAttribute("aria-hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
expect(parentSquareRootHidden)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual("true");
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("No alt-text with MathML", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("bug2004951.pdf", ".textLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that there's no alt-text on the MathML node", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const isSanitizerSupported = await page.evaluate(() => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
return typeof Sanitizer !== "undefined";
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const ariaLabel = await page.$eval(
|
||||||
|
"span[aria-owns='p3R_mc2']",
|
||||||
|
el => el.getAttribute("aria-label") || ""
|
||||||
|
);
|
||||||
|
if (isSanitizerSupported) {
|
||||||
|
expect(ariaLabel).withContext(`In ${browserName}`).toEqual("");
|
||||||
|
} else {
|
||||||
|
expect(ariaLabel)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual("cube root of , x plus y end cube root ");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Text elements must be aria-hidden when there's MathML and annotations", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("bug2009627.pdf", ".textLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the text in text layer is aria-hidden", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const isSanitizerSupported = await page.evaluate(() => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
return typeof Sanitizer !== "undefined";
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const ariaHidden = await page.evaluate(() =>
|
||||||
|
Array.from(
|
||||||
|
document.querySelectorAll(".structTree :has(> math)")
|
||||||
|
).map(el =>
|
||||||
|
document
|
||||||
|
.getElementById(el.getAttribute("aria-owns"))
|
||||||
|
.getAttribute("aria-hidden")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (isSanitizerSupported) {
|
||||||
|
expect(ariaHidden)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(["true", "true", "true"]);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(
|
||||||
|
`Pending in Chrome: Sanitizer API (in ${browserName}) is not supported`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("A TH in a TR itself in a TBody is rowheader", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("bug2014080.pdf", ".textLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the table has the right structure", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
let elementRole = await page.evaluate(() =>
|
||||||
|
Array.from(
|
||||||
|
document.querySelector(".structTree [role='table']").children
|
||||||
|
).map(child => child.getAttribute("role"))
|
||||||
|
);
|
||||||
|
|
||||||
|
// THeader and TBody must be rowgroup.
|
||||||
|
expect(elementRole)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(["rowgroup", "rowgroup"]);
|
||||||
|
|
||||||
|
elementRole = await page.evaluate(() =>
|
||||||
|
Array.from(
|
||||||
|
document.querySelector(
|
||||||
|
".structTree [role='table'] > [role='rowgroup'] > [role='row']"
|
||||||
|
).children
|
||||||
|
).map(child => child.getAttribute("role"))
|
||||||
|
);
|
||||||
|
|
||||||
|
// THeader has 3 columnheader.
|
||||||
|
expect(elementRole)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(["columnheader", "columnheader", "columnheader"]);
|
||||||
|
|
||||||
|
elementRole = await page.evaluate(() =>
|
||||||
|
Array.from(
|
||||||
|
document.querySelector(
|
||||||
|
".structTree [role='table'] > [role='rowgroup']:nth-child(2)"
|
||||||
|
).children
|
||||||
|
).map(child => child.getAttribute("role"))
|
||||||
|
);
|
||||||
|
|
||||||
|
// TBody has 5 rows.
|
||||||
|
expect(elementRole)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(["row", "row", "row", "row", "row"]);
|
||||||
|
|
||||||
|
elementRole = await page.evaluate(() =>
|
||||||
|
Array.from(
|
||||||
|
document.querySelector(
|
||||||
|
".structTree [role='table'] > [role='rowgroup']:nth-child(2) > [role='row']:first-child"
|
||||||
|
).children
|
||||||
|
).map(child => child.getAttribute("role"))
|
||||||
|
);
|
||||||
|
// First row has a rowheader and 2 cells.
|
||||||
|
expect(elementRole)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(["rowheader", "cell", "cell"]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import {
|
|||||||
highlightSpan,
|
highlightSpan,
|
||||||
kbModifierDown,
|
kbModifierDown,
|
||||||
kbModifierUp,
|
kbModifierUp,
|
||||||
|
kbRedo,
|
||||||
|
kbUndo,
|
||||||
loadAndWait,
|
loadAndWait,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
selectEditor,
|
selectEditor,
|
||||||
@ -109,7 +111,7 @@ describe("Comment", () => {
|
|||||||
".annotationEditorLayer",
|
".annotationEditorLayer",
|
||||||
"page-width",
|
"page-width",
|
||||||
null,
|
null,
|
||||||
{ enableComment: true, localeProperties: "ar" }
|
{ enableComment: true, locale: "ar" }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -965,4 +967,213 @@ describe("Comment", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Undo deletion popup for comments (bug 1999154)", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"tracemonkey.pdf",
|
||||||
|
".annotationEditorLayer",
|
||||||
|
"page-fit",
|
||||||
|
null,
|
||||||
|
{ enableComment: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that deleting a comment can be undone using the undo button", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToHighlight(page);
|
||||||
|
await highlightSpan(page, 1, "Abstract");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
const comment = "Test comment for undo";
|
||||||
|
await editComment(page, editorSelector, comment);
|
||||||
|
|
||||||
|
// Stay in highlight mode - don't disable it
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
|
||||||
|
// Capture the date before deletion
|
||||||
|
const dateBefore = await page.evaluate(
|
||||||
|
() =>
|
||||||
|
document.querySelector("#commentPopup .commentPopupTime")
|
||||||
|
?.textContent
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitAndClick(page, "button.commentPopupDelete");
|
||||||
|
|
||||||
|
await page.waitForSelector("#editorUndoBar", { visible: true });
|
||||||
|
await page.waitForSelector("#editorUndoBarUndoButton", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await page.click("#editorUndoBarUndoButton");
|
||||||
|
|
||||||
|
// Check that the comment is restored by hovering to show the popup
|
||||||
|
await page.hover(`${editorSelector} .annotationCommentButton`);
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
const popupText = await page.evaluate(
|
||||||
|
() =>
|
||||||
|
document.querySelector("#commentPopup .commentPopupText")
|
||||||
|
?.textContent
|
||||||
|
);
|
||||||
|
expect(popupText).withContext(`In ${browserName}`).toEqual(comment);
|
||||||
|
|
||||||
|
// Check that the date is preserved
|
||||||
|
const dateAfter = await page.evaluate(
|
||||||
|
() =>
|
||||||
|
document.querySelector("#commentPopup .commentPopupTime")
|
||||||
|
?.textContent
|
||||||
|
);
|
||||||
|
expect(dateAfter)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(dateBefore);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the undo deletion popup displays 'Comment removed' message", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToHighlight(page);
|
||||||
|
await highlightSpan(page, 1, "Abstract");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
await editComment(page, editorSelector, "Test comment");
|
||||||
|
|
||||||
|
// Stay in highlight mode - don't disable it
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
await waitAndClick(page, "button.commentPopupDelete");
|
||||||
|
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const messageElement = document.querySelector(
|
||||||
|
"#editorUndoBarMessage"
|
||||||
|
);
|
||||||
|
return messageElement && messageElement.textContent.trim() !== "";
|
||||||
|
});
|
||||||
|
const message = await page.waitForSelector("#editorUndoBarMessage");
|
||||||
|
const messageText = await page.evaluate(
|
||||||
|
el => el.textContent,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
expect(messageText)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toContain("Comment removed");
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the undo bar closes when clicking the close button", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToHighlight(page);
|
||||||
|
await highlightSpan(page, 1, "Abstract");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
await editComment(page, editorSelector, "Test comment");
|
||||||
|
|
||||||
|
// Stay in highlight mode - don't disable it
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
await waitAndClick(page, "button.commentPopupDelete");
|
||||||
|
|
||||||
|
await page.waitForSelector("#editorUndoBar", { visible: true });
|
||||||
|
await waitAndClick(page, "#editorUndoBarCloseButton");
|
||||||
|
await page.waitForSelector("#editorUndoBar", { hidden: true });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that deleting a comment can be undone using Ctrl+Z", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToHighlight(page);
|
||||||
|
await highlightSpan(page, 1, "Abstract");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
const comment = "Test comment for Ctrl+Z undo";
|
||||||
|
await editComment(page, editorSelector, comment);
|
||||||
|
|
||||||
|
// Stay in highlight mode - don't disable it
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
await waitAndClick(page, "button.commentPopupDelete");
|
||||||
|
|
||||||
|
await page.waitForSelector("#editorUndoBar", { visible: true });
|
||||||
|
|
||||||
|
// Use Ctrl+Z to undo
|
||||||
|
await kbUndo(page);
|
||||||
|
|
||||||
|
// The undo bar should be hidden after undo
|
||||||
|
await page.waitForSelector("#editorUndoBar", { hidden: true });
|
||||||
|
|
||||||
|
// Check that the comment is restored by hovering to show the popup
|
||||||
|
await page.hover(`${editorSelector} .annotationCommentButton`);
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
const popupText = await page.evaluate(
|
||||||
|
() =>
|
||||||
|
document.querySelector("#commentPopup .commentPopupText")
|
||||||
|
?.textContent
|
||||||
|
);
|
||||||
|
expect(popupText).withContext(`In ${browserName}`).toEqual(comment);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the comment popup is hidden after redo", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToHighlight(page);
|
||||||
|
await highlightSpan(page, 1, "Abstract");
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
const comment = "Test comment for redo";
|
||||||
|
await editComment(page, editorSelector, comment);
|
||||||
|
|
||||||
|
// Show the popup by clicking the comment button
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
|
||||||
|
// Delete the comment
|
||||||
|
await waitAndClick(page, "button.commentPopupDelete");
|
||||||
|
await page.waitForSelector("#editorUndoBar", { visible: true });
|
||||||
|
|
||||||
|
// Undo the deletion
|
||||||
|
await kbUndo(page);
|
||||||
|
await page.waitForSelector("#editorUndoBar", { hidden: true });
|
||||||
|
|
||||||
|
// Show the popup again by clicking the comment button
|
||||||
|
await waitAndClick(
|
||||||
|
page,
|
||||||
|
`${editorSelector} .annotationCommentButton`
|
||||||
|
);
|
||||||
|
await page.waitForSelector("#commentPopup", { visible: true });
|
||||||
|
|
||||||
|
// Redo the deletion - popup should be hidden
|
||||||
|
await kbRedo(page);
|
||||||
|
await page.waitForSelector("#commentPopup", { hidden: true });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3645,4 +3645,46 @@ describe("FreeText Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("No exception when moving (issue 20571)", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"tracemonkey.pdf",
|
||||||
|
".annotationEditorLayer",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the buttons work correctly", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await switchToFreeText(page);
|
||||||
|
|
||||||
|
const rect = await getRect(page, ".annotationEditorLayer");
|
||||||
|
await createFreeTextEditor({
|
||||||
|
page,
|
||||||
|
x: rect.x + 100,
|
||||||
|
y: rect.y + 100,
|
||||||
|
data: "Hello PDF.js World !!",
|
||||||
|
});
|
||||||
|
|
||||||
|
await switchToFreeText(page, /* disable = */ true);
|
||||||
|
await switchToFreeText(page);
|
||||||
|
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
await selectEditor(page, editorSelector);
|
||||||
|
await dragAndDrop(page, editorSelector, [[10, 10]]);
|
||||||
|
|
||||||
|
await switchToFreeText(page, /* disable = */ true);
|
||||||
|
await switchToFreeText(page);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,6 +37,7 @@ async function runTests(results) {
|
|||||||
"freetext_editor_spec.mjs",
|
"freetext_editor_spec.mjs",
|
||||||
"highlight_editor_spec.mjs",
|
"highlight_editor_spec.mjs",
|
||||||
"ink_editor_spec.mjs",
|
"ink_editor_spec.mjs",
|
||||||
|
"reorganize_pages_spec.mjs",
|
||||||
"scripting_spec.mjs",
|
"scripting_spec.mjs",
|
||||||
"signature_editor_spec.mjs",
|
"signature_editor_spec.mjs",
|
||||||
"stamp_editor_spec.mjs",
|
"stamp_editor_spec.mjs",
|
||||||
|
|||||||
633
test/integration/reorganize_pages_spec.mjs
Normal file
633
test/integration/reorganize_pages_spec.mjs
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
/* Copyright 2026 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
awaitPromise,
|
||||||
|
clearInput,
|
||||||
|
closePages,
|
||||||
|
createPromise,
|
||||||
|
dragAndDrop,
|
||||||
|
getAnnotationSelector,
|
||||||
|
getRect,
|
||||||
|
getThumbnailSelector,
|
||||||
|
loadAndWait,
|
||||||
|
scrollIntoView,
|
||||||
|
waitForDOMMutation,
|
||||||
|
} from "./test_utils.mjs";
|
||||||
|
|
||||||
|
async function waitForThumbnailVisible(page, pageNums) {
|
||||||
|
await page.click("#viewsManagerToggleButton");
|
||||||
|
|
||||||
|
const thumbSelector = "#thumbnailsView .thumbnailImage";
|
||||||
|
await page.waitForSelector(thumbSelector, { visible: true });
|
||||||
|
if (!pageNums) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(pageNums)) {
|
||||||
|
pageNums = [pageNums];
|
||||||
|
}
|
||||||
|
return Promise.all(
|
||||||
|
pageNums.map(pageNum =>
|
||||||
|
page.waitForSelector(getThumbnailSelector(pageNum), { visible: true })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForPagesEdited(page) {
|
||||||
|
return createPromise(page, resolve => {
|
||||||
|
window.PDFViewerApplication.eventBus.on(
|
||||||
|
"pagesedited",
|
||||||
|
({ pagesMapper }) => {
|
||||||
|
resolve(Array.from(pagesMapper.getMapping()));
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForHavingContents(page, expected) {
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// Make sure all the pages will be visible.
|
||||||
|
window.PDFViewerApplication.pdfViewer.scrollMode = 2 /* = ScrollMode.WRAPPED = */;
|
||||||
|
window.PDFViewerApplication.pdfViewer.updateScale({
|
||||||
|
drawingDelay: 0,
|
||||||
|
scaleFactor: 0.01,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return page.waitForFunction(
|
||||||
|
ex => {
|
||||||
|
const buffer = [];
|
||||||
|
for (const textLayer of document.querySelectorAll(".textLayer")) {
|
||||||
|
buffer.push(parseInt(textLayer.textContent.trim(), 10));
|
||||||
|
}
|
||||||
|
return ex.length === buffer.length && ex.every((v, i) => v === buffer[i]);
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchResults(page) {
|
||||||
|
return page.evaluate(() => {
|
||||||
|
const pages = document.querySelectorAll(".page");
|
||||||
|
const results = [];
|
||||||
|
for (let i = 0; i < pages.length; i++) {
|
||||||
|
const domPage = pages[i];
|
||||||
|
const highlights = domPage.querySelectorAll("span.highlight");
|
||||||
|
if (highlights.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
results.push([
|
||||||
|
i + 1,
|
||||||
|
Array.from(highlights).map(span => span.textContent),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function movePages(page, selectedPages, atIndex) {
|
||||||
|
return page.evaluate(
|
||||||
|
(selected, index) => {
|
||||||
|
const viewer = window.PDFViewerApplication.pdfViewer;
|
||||||
|
const pagesToMove = Array.from(selected).sort((a, b) => a - b);
|
||||||
|
viewer.pagesMapper.pagesNumber =
|
||||||
|
document.querySelectorAll(".page").length;
|
||||||
|
viewer.pagesMapper.movePages(new Set(pagesToMove), pagesToMove, index);
|
||||||
|
window.PDFViewerApplication.eventBus.dispatch("pagesedited", {
|
||||||
|
pagesMapper: viewer.pagesMapper,
|
||||||
|
index,
|
||||||
|
pagesToMove,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectedPages,
|
||||||
|
atIndex
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Reorganize Pages View", () => {
|
||||||
|
describe("Drag & Drop", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number.pdf",
|
||||||
|
"#viewsManagerToggleButton",
|
||||||
|
"page-fit",
|
||||||
|
null,
|
||||||
|
{ enableSplitMerge: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a drag marker when dragging a thumbnail", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
const handleAddedMarker = await waitForDOMMutation(
|
||||||
|
page,
|
||||||
|
mutationList => {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
if (mutation.type !== "childList") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const node of mutation.addedNodes) {
|
||||||
|
if (node.classList.contains("dragMarker")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const handleRemovedMarker = await waitForDOMMutation(
|
||||||
|
page,
|
||||||
|
mutationList => {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
if (mutation.type !== "childList") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const node of mutation.removedNodes) {
|
||||||
|
if (node.classList.contains("dragMarker")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const dndPromise = dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
await dndPromise;
|
||||||
|
await awaitPromise(handleAddedMarker);
|
||||||
|
await awaitPromise(handleRemovedMarker);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reorder thumbnails after dropping", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
const handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
const pagesMapping = await awaitPromise(handlePagesEdited);
|
||||||
|
const expected = [
|
||||||
|
2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
];
|
||||||
|
expect(pagesMapping)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(expected);
|
||||||
|
await waitForHavingContents(page, expected);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reorder thumbnails after dropping at position 0", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
const handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(2),
|
||||||
|
[[0, rect1.y - rect2.y - rect1.height]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
const pagesMapping = await awaitPromise(handlePagesEdited);
|
||||||
|
const expected = [
|
||||||
|
2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
];
|
||||||
|
expect(pagesMapping)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(expected);
|
||||||
|
await waitForHavingContents(page, expected);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reorder thumbnails after dropping two adjacent pages", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
const rect4 = await getRect(page, getThumbnailSelector(4));
|
||||||
|
await page.click(`.thumbnail:has(${getThumbnailSelector(1)}) input`);
|
||||||
|
|
||||||
|
const handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(2),
|
||||||
|
[[0, rect4.y - rect2.y]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
const pagesMapping = await awaitPromise(handlePagesEdited);
|
||||||
|
const expected = [
|
||||||
|
3, 4, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
];
|
||||||
|
expect(pagesMapping)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(expected);
|
||||||
|
await waitForHavingContents(page, expected);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reorder thumbnails after dropping two non-adjacent pages", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
await (await page.$(".thumbnail[page-id='14'")).scrollIntoView();
|
||||||
|
await page.waitForSelector(getThumbnailSelector(14), {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await page.click(`.thumbnail:has(${getThumbnailSelector(14)}) input`);
|
||||||
|
await (await page.$(".thumbnail[page-id='1'")).scrollIntoView();
|
||||||
|
await page.waitForSelector(getThumbnailSelector(1), {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
const pagesMapping = await awaitPromise(handlePagesEdited);
|
||||||
|
const expected = [
|
||||||
|
2, 1, 14, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
|
||||||
|
];
|
||||||
|
expect(pagesMapping)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(expected);
|
||||||
|
await waitForHavingContents(page, expected);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should select the dropped page (bug 2010820)", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
await page.click(getThumbnailSelector(2));
|
||||||
|
await page.waitForSelector(
|
||||||
|
`${getThumbnailSelector(2)}[aria-current="page"]`
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePagesEdited = await waitForPagesEdited(page);
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
await awaitPromise(handlePagesEdited);
|
||||||
|
await page.waitForSelector(
|
||||||
|
`${getThumbnailSelector(2)}[aria-current="page"]`
|
||||||
|
);
|
||||||
|
await page.waitForSelector(
|
||||||
|
`${getThumbnailSelector(1)}[aria-current="false"]`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Search in pdf", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number.pdf",
|
||||||
|
"#viewsManagerToggleButton",
|
||||||
|
"1",
|
||||||
|
null,
|
||||||
|
{ enableSplitMerge: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check if the search is working after moving pages", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#viewFindButton");
|
||||||
|
await page.waitForSelector(":has(> #findHighlightAll)", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await page.click(":has(> #findHighlightAll)");
|
||||||
|
|
||||||
|
await page.waitForSelector("#findInput", { visible: true });
|
||||||
|
await page.type("#findInput", "1");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => document.querySelectorAll("span.highlight").length === 10
|
||||||
|
);
|
||||||
|
|
||||||
|
let results = await getSearchResults(page);
|
||||||
|
expect(results)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([
|
||||||
|
// Page number, [matches]
|
||||||
|
[1, ["1"]],
|
||||||
|
[10, ["1"]],
|
||||||
|
[11, ["1", "1"]],
|
||||||
|
[12, ["1"]],
|
||||||
|
[13, ["1"]],
|
||||||
|
[14, ["1"]],
|
||||||
|
[15, ["1"]],
|
||||||
|
[16, ["1"]],
|
||||||
|
[17, ["1"]],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await movePages(page, [11, 2], 3);
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => document.querySelectorAll("span.highlight").length === 0
|
||||||
|
);
|
||||||
|
|
||||||
|
await clearInput(page, "#findInput", true);
|
||||||
|
await page.type("#findInput", "1");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => document.querySelectorAll("span.highlight").length === 10
|
||||||
|
);
|
||||||
|
|
||||||
|
results = await getSearchResults(page);
|
||||||
|
expect(results)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([
|
||||||
|
// Page number, [matches]
|
||||||
|
[1, ["1"]],
|
||||||
|
[4, ["1", "1"]],
|
||||||
|
[11, ["1"]],
|
||||||
|
[12, ["1"]],
|
||||||
|
[13, ["1"]],
|
||||||
|
[14, ["1"]],
|
||||||
|
[15, ["1"]],
|
||||||
|
[16, ["1"]],
|
||||||
|
[17, ["1"]],
|
||||||
|
]);
|
||||||
|
|
||||||
|
await movePages(page, [13], 0);
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => document.querySelectorAll("span.highlight").length === 0
|
||||||
|
);
|
||||||
|
|
||||||
|
await clearInput(page, "#findInput", true);
|
||||||
|
await page.type("#findInput", "1");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => document.querySelectorAll("span.highlight").length === 10
|
||||||
|
);
|
||||||
|
|
||||||
|
results = await getSearchResults(page);
|
||||||
|
expect(results)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([
|
||||||
|
// Page number, [matches]
|
||||||
|
[1, ["1"]],
|
||||||
|
[2, ["1"]],
|
||||||
|
[5, ["1", "1"]],
|
||||||
|
[12, ["1"]],
|
||||||
|
[13, ["1"]],
|
||||||
|
[14, ["1"]],
|
||||||
|
[15, ["1"]],
|
||||||
|
[16, ["1"]],
|
||||||
|
[17, ["1"]],
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Links and outlines", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number_and_link.pdf",
|
||||||
|
"#viewsManagerToggleButton",
|
||||||
|
"page-fit",
|
||||||
|
null,
|
||||||
|
{ enableSplitMerge: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check that link is updated after moving pages", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
await movePages(page, [2], 10);
|
||||||
|
await scrollIntoView(page, getAnnotationSelector("107R"));
|
||||||
|
await page.click(getAnnotationSelector("107R"));
|
||||||
|
const currentPage = await page.$eval(
|
||||||
|
"#pageNumber",
|
||||||
|
el => el.valueAsNumber
|
||||||
|
);
|
||||||
|
expect(currentPage).withContext(`In ${browserName}`).toBe(10);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check that outlines are updated after moving pages", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
await movePages(page, [2, 4], 10);
|
||||||
|
|
||||||
|
await page.click("#viewsManagerSelectorButton");
|
||||||
|
await page.click("#outlinesViewMenu");
|
||||||
|
await page.waitForSelector("#outlinesView", { visible: true });
|
||||||
|
|
||||||
|
await page.click("#outlinesView .treeItem:nth-child(2)");
|
||||||
|
|
||||||
|
const currentPage = await page.$eval(
|
||||||
|
"#pageNumber",
|
||||||
|
el => el.valueAsNumber
|
||||||
|
);
|
||||||
|
// 9 because 2 and 4 were moved after page 10.
|
||||||
|
expect(currentPage).withContext(`In ${browserName}`).toBe(9);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Drag marker must have the right non-zero dimensions", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number_and_link.pdf",
|
||||||
|
"#viewsManagerToggleButton",
|
||||||
|
"1",
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
enableSplitMerge: true,
|
||||||
|
sidebarViewOnLoad: 2 /* = SidebarView.OUTLINES */,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check if the drag marker width is non-zero", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.waitForSelector("#outlinesView", { visible: true });
|
||||||
|
await page.waitForSelector("#viewsManagerSelectorButton", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await page.click("#viewsManagerSelectorButton");
|
||||||
|
await page.waitForSelector("#thumbnailsViewMenu", { visible: true });
|
||||||
|
await page.click("#thumbnailsViewMenu");
|
||||||
|
|
||||||
|
const thumbSelector = "#thumbnailsView .thumbnailImage";
|
||||||
|
await page.waitForSelector(thumbSelector, { visible: true });
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
const handleAddedMarker = await waitForDOMMutation(
|
||||||
|
page,
|
||||||
|
mutationList => {
|
||||||
|
for (const mutation of mutationList) {
|
||||||
|
if (mutation.type !== "childList") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const node of mutation.addedNodes) {
|
||||||
|
if (node.classList.contains("dragMarker")) {
|
||||||
|
const rect = node.getBoundingClientRect();
|
||||||
|
return rect.width !== 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
await awaitPromise(handleAddedMarker);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Save a pdf", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"page_with_number.pdf",
|
||||||
|
"#viewsManagerToggleButton",
|
||||||
|
"1",
|
||||||
|
null,
|
||||||
|
{ enableSplitMerge: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check that a save is triggered", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitForThumbnailVisible(page, 1);
|
||||||
|
await page.waitForSelector("#viewsManagerStatusActionButton", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
const rect1 = await getRect(page, getThumbnailSelector(1));
|
||||||
|
const rect2 = await getRect(page, getThumbnailSelector(2));
|
||||||
|
|
||||||
|
await dragAndDrop(
|
||||||
|
page,
|
||||||
|
getThumbnailSelector(1),
|
||||||
|
[[0, rect2.y - rect1.y + rect2.height / 2]],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSaveAs = await createPromise(page, resolve => {
|
||||||
|
window.PDFViewerApplication.eventBus.on(
|
||||||
|
"savepageseditedpdf",
|
||||||
|
({ data }) => {
|
||||||
|
resolve(Array.from(data.pageIndices));
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.click("#viewsManagerStatusActionButton");
|
||||||
|
await page.waitForSelector("#viewsManagerStatusActionSaveAs", {
|
||||||
|
visible: true,
|
||||||
|
});
|
||||||
|
await page.click("#viewsManagerStatusActionSaveAs");
|
||||||
|
const pageIndices = await awaitPromise(handleSaveAs);
|
||||||
|
expect(pageIndices)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual([
|
||||||
|
1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -177,7 +177,7 @@ describe("Signature Editor", () => {
|
|||||||
const editorSelector = getEditorSelector(0);
|
const editorSelector = getEditorSelector(0);
|
||||||
await page.waitForSelector(editorSelector, { visible: true });
|
await page.waitForSelector(editorSelector, { visible: true });
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`,
|
`.canvasWrapper > svg use[href="#path_0"]`,
|
||||||
{ visible: true }
|
{ visible: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ describe("Signature Editor", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
".canvasWrapper > svg use[href='#path_p1_0']"
|
".canvasWrapper > svg use[href='#path_0']"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -340,7 +340,7 @@ describe("Signature Editor", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
".canvasWrapper > svg use[href='#path_p1_0']"
|
".canvasWrapper > svg use[href='#path_0']"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -427,7 +427,7 @@ describe("Signature Editor", () => {
|
|||||||
const editorSelector = getEditorSelector(0);
|
const editorSelector = getEditorSelector(0);
|
||||||
await page.waitForSelector(editorSelector, { visible: true });
|
await page.waitForSelector(editorSelector, { visible: true });
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`,
|
`.canvasWrapper > svg use[href="#path_0"]`,
|
||||||
{ visible: true }
|
{ visible: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -527,13 +527,13 @@ describe("Signature Editor", () => {
|
|||||||
const editorSelector = getEditorSelector(0);
|
const editorSelector = getEditorSelector(0);
|
||||||
await page.waitForSelector(editorSelector, { visible: true });
|
await page.waitForSelector(editorSelector, { visible: true });
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`,
|
`.canvasWrapper > svg use[href="#path_0"]`,
|
||||||
{ visible: true }
|
{ visible: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const color = await page.evaluate(() => {
|
const color = await page.evaluate(() => {
|
||||||
const use = document.querySelector(
|
const use = document.querySelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`
|
`.canvasWrapper > svg use[href="#path_0"]`
|
||||||
);
|
);
|
||||||
return use.parentNode.getAttribute("fill");
|
return use.parentNode.getAttribute("fill");
|
||||||
});
|
});
|
||||||
@ -583,13 +583,13 @@ describe("Signature Editor", () => {
|
|||||||
const editorSelector = getEditorSelector(0);
|
const editorSelector = getEditorSelector(0);
|
||||||
await page.waitForSelector(editorSelector, { visible: true });
|
await page.waitForSelector(editorSelector, { visible: true });
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`,
|
`.canvasWrapper > svg use[href="#path_0"]`,
|
||||||
{ visible: true }
|
{ visible: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const color = await page.evaluate(() => {
|
const color = await page.evaluate(() => {
|
||||||
const use = document.querySelector(
|
const use = document.querySelector(
|
||||||
`.canvasWrapper > svg use[href="#path_p1_0"]`
|
`.canvasWrapper > svg use[href="#path_0"]`
|
||||||
);
|
);
|
||||||
return use.parentNode.getAttribute("fill");
|
return use.parentNode.getAttribute("fill");
|
||||||
});
|
});
|
||||||
@ -672,7 +672,7 @@ describe("Signature Editor", () => {
|
|||||||
});
|
});
|
||||||
const { width, height } = await getRect(
|
const { width, height } = await getRect(
|
||||||
page,
|
page,
|
||||||
".canvasWrapper > svg use[href='#path_p1_0']"
|
".canvasWrapper > svg use[href='#path_0']"
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(Math.abs(contentWidth / width - contentHeight / height))
|
expect(Math.abs(contentWidth / width - contentHeight / height))
|
||||||
|
|||||||
@ -1174,7 +1174,9 @@ describe("Stamp Editor", () => {
|
|||||||
// Run sequentially to avoid clipboard issues.
|
// Run sequentially to avoid clipboard issues.
|
||||||
for (const [, page] of pages) {
|
for (const [, page] of pages) {
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.PDFViewerApplication.mlManager.enableAltTextModelDownload = false;
|
const { mlManager } = window.PDFViewerApplication;
|
||||||
|
mlManager.enableGuessAltText =
|
||||||
|
mlManager.enableAltTextModelDownload = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
await switchToStamp(page);
|
await switchToStamp(page);
|
||||||
@ -1197,7 +1199,9 @@ describe("Stamp Editor", () => {
|
|||||||
// Run sequentially to avoid clipboard issues.
|
// Run sequentially to avoid clipboard issues.
|
||||||
for (const [browserName, page] of pages) {
|
for (const [browserName, page] of pages) {
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.PDFViewerApplication.mlManager.enableAltTextModelDownload = true;
|
const { mlManager } = window.PDFViewerApplication;
|
||||||
|
mlManager.enableGuessAltText =
|
||||||
|
mlManager.enableAltTextModelDownload = true;
|
||||||
});
|
});
|
||||||
await switchToStamp(page);
|
await switchToStamp(page);
|
||||||
|
|
||||||
|
|||||||
@ -158,6 +158,24 @@ async function waitForSandboxTrip(page) {
|
|||||||
await awaitPromise(handle);
|
await awaitPromise(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForDOMMutation(page, callback) {
|
||||||
|
return page.evaluateHandle(
|
||||||
|
cb => [
|
||||||
|
new Promise(resolve => {
|
||||||
|
const mutationObserver = new MutationObserver(mutationList => {
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
if (eval(`(${cb})`)(mutationList)) {
|
||||||
|
mutationObserver.disconnect();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mutationObserver.observe(document, { childList: true, subtree: true });
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
callback.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function waitForTimeout(milliseconds) {
|
function waitForTimeout(milliseconds) {
|
||||||
/**
|
/**
|
||||||
* Wait for the given number of milliseconds.
|
* Wait for the given number of milliseconds.
|
||||||
@ -234,6 +252,10 @@ function getAnnotationSelector(id) {
|
|||||||
return `[data-annotation-id="${id}"]`;
|
return `[data-annotation-id="${id}"]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getThumbnailSelector(pageNumber) {
|
||||||
|
return `.thumbnailImage[data-l10n-args='{"page":${pageNumber}}']`;
|
||||||
|
}
|
||||||
|
|
||||||
async function getSpanRectFromText(page, pageNumber, text) {
|
async function getSpanRectFromText(page, pageNumber, text) {
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.page[data-page-number="${pageNumber}"] > .textLayer .endOfContent`
|
`.page[data-page-number="${pageNumber}"] > .textLayer .endOfContent`
|
||||||
@ -957,6 +979,7 @@ export {
|
|||||||
getSelector,
|
getSelector,
|
||||||
getSerialized,
|
getSerialized,
|
||||||
getSpanRectFromText,
|
getSpanRectFromText,
|
||||||
|
getThumbnailSelector,
|
||||||
getXY,
|
getXY,
|
||||||
highlightSpan,
|
highlightSpan,
|
||||||
isCanvasMonochrome,
|
isCanvasMonochrome,
|
||||||
@ -991,6 +1014,7 @@ export {
|
|||||||
waitAndClick,
|
waitAndClick,
|
||||||
waitForAnnotationEditorLayer,
|
waitForAnnotationEditorLayer,
|
||||||
waitForAnnotationModeChanged,
|
waitForAnnotationModeChanged,
|
||||||
|
waitForDOMMutation,
|
||||||
waitForEntryInStorage,
|
waitForEntryInStorage,
|
||||||
waitForEvent,
|
waitForEvent,
|
||||||
waitForNoElement,
|
waitForNoElement,
|
||||||
|
|||||||
@ -124,6 +124,13 @@ describe("PDF Thumbnail View", () => {
|
|||||||
.withContext(`In ${browserName}`)
|
.withContext(`In ${browserName}`)
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
|
|
||||||
|
await kbFocusNext(page);
|
||||||
|
expect(
|
||||||
|
await isElementFocused(page, "#viewsManagerStatusActionButton")
|
||||||
|
)
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toBe(true);
|
||||||
|
|
||||||
await kbFocusNext(page);
|
await kbFocusNext(page);
|
||||||
expect(
|
expect(
|
||||||
await isElementFocused(
|
await isElementFocused(
|
||||||
|
|||||||
105
test/pdfs/.gitignore
vendored
105
test/pdfs/.gitignore
vendored
@ -768,3 +768,108 @@
|
|||||||
!issue20513.pdf
|
!issue20513.pdf
|
||||||
!issue20516.pdf
|
!issue20516.pdf
|
||||||
!issue20489.pdf
|
!issue20489.pdf
|
||||||
|
!bug2004951.pdf
|
||||||
|
!bitmap-composite-and-xnor-halftone.pdf
|
||||||
|
!bitmap-composite-and-xnor-refine.pdf
|
||||||
|
!bitmap-composite-and-xnor-text.pdf
|
||||||
|
!bitmap-composite-and-xnor.pdf
|
||||||
|
!bitmap-composite-or-xor-replace-halftone.pdf
|
||||||
|
!bitmap-composite-or-xor-replace-refine.pdf
|
||||||
|
!bitmap-composite-or-xor-replace-text.pdf
|
||||||
|
!bitmap-composite-or-xor-replace.pdf
|
||||||
|
!bitmap-customat-tpgdon.pdf
|
||||||
|
!bitmap-customat.pdf
|
||||||
|
!bitmap-halftone-10bpp-mmr.pdf
|
||||||
|
!bitmap-halftone-10bpp.pdf
|
||||||
|
!bitmap-halftone-composite.pdf
|
||||||
|
!bitmap-halftone-grid.pdf
|
||||||
|
!bitmap-halftone-refine.pdf
|
||||||
|
!bitmap-halftone-skip-dummy.pdf
|
||||||
|
!bitmap-halftone-skip-grid-template1.pdf
|
||||||
|
!bitmap-halftone-skip-grid-template2.pdf
|
||||||
|
!bitmap-halftone-skip-grid-template3.pdf
|
||||||
|
!bitmap-halftone-skip-grid.pdf
|
||||||
|
!bitmap-halftone-template1.pdf
|
||||||
|
!bitmap-halftone-template2.pdf
|
||||||
|
!bitmap-halftone-template3.pdf
|
||||||
|
!bitmap-halftone.pdf
|
||||||
|
!bitmap-initially-unknown-size.pdf
|
||||||
|
!bitmap-mmr.pdf
|
||||||
|
!bitmap-p32-eof.pdf
|
||||||
|
!bitmap-randomaccess.pdf
|
||||||
|
!bitmap-refine-customat-tpgron.pdf
|
||||||
|
!bitmap-refine-customat.pdf
|
||||||
|
!bitmap-refine-lossless.pdf
|
||||||
|
!bitmap-refine-page-subrect.pdf
|
||||||
|
!bitmap-refine-page.pdf
|
||||||
|
!bitmap-refine-refine.pdf
|
||||||
|
!bitmap-refine-template1-tpgron.pdf
|
||||||
|
!bitmap-refine-template1.pdf
|
||||||
|
!bitmap-refine-tpgron.pdf
|
||||||
|
!bitmap-refine.pdf
|
||||||
|
!bitmap-stripe-initially-unknown-height.pdf
|
||||||
|
!bitmap-stripe-last-implicit.pdf
|
||||||
|
!bitmap-stripe-single-no-end-of-stripe.pdf
|
||||||
|
!bitmap-stripe-single.pdf
|
||||||
|
!bitmap-stripe.pdf
|
||||||
|
!bitmap-symbol-big-segmentid.pdf
|
||||||
|
!bitmap-symbol-context-reuse.pdf
|
||||||
|
!bitmap-symbol-empty.pdf
|
||||||
|
!bitmap-symbol-negative-sbdsoffset.pdf
|
||||||
|
!bitmap-symbol-refine.pdf
|
||||||
|
!bitmap-symbol-symbolrefine-textrefine.pdf
|
||||||
|
!bitmap-symbol-symbolrefineone-customat.pdf
|
||||||
|
!bitmap-symbol-symbolrefineone-template1.pdf
|
||||||
|
!bitmap-symbol-symbolrefineone.pdf
|
||||||
|
!bitmap-symbol-symbolrefineseveral.pdf
|
||||||
|
!bitmap-symbol-symhuff-texthuff.pdf
|
||||||
|
!bitmap-symbol-symhuff-texthuffB10B13.pdf
|
||||||
|
!bitmap-symbol-symhuffB5B3-texthuffB7B9B12.pdf
|
||||||
|
!bitmap-symbol-symhuffcustom-texthuffcustom.pdf
|
||||||
|
!bitmap-symbol-symhuffrefine-textrefine.pdf
|
||||||
|
!bitmap-symbol-symhuffrefineone.pdf
|
||||||
|
!bitmap-symbol-symhuffrefineseveral.pdf
|
||||||
|
!bitmap-symbol-symhuffuncompressed-texthuff.pdf
|
||||||
|
!bitmap-symbol-textbottomleft.pdf
|
||||||
|
!bitmap-symbol-textbottomlefttranspose.pdf
|
||||||
|
!bitmap-symbol-textbottomright.pdf
|
||||||
|
!bitmap-symbol-textbottomrighttranspose.pdf
|
||||||
|
!bitmap-symbol-textcomposite.pdf
|
||||||
|
!bitmap-symbol-texthuffrefine.pdf
|
||||||
|
!bitmap-symbol-texthuffrefineB15.pdf
|
||||||
|
!bitmap-symbol-texthuffrefinecustom.pdf
|
||||||
|
!bitmap-symbol-texthuffrefinecustomdims.pdf
|
||||||
|
!bitmap-symbol-texthuffrefinecustompos.pdf
|
||||||
|
!bitmap-symbol-texthuffrefinecustomposdims.pdf
|
||||||
|
!bitmap-symbol-texthuffrefinecustomsize.pdf
|
||||||
|
!bitmap-symbol-textrefine-customat.pdf
|
||||||
|
!bitmap-symbol-textrefine-negative-delta-width.pdf
|
||||||
|
!bitmap-symbol-textrefine.pdf
|
||||||
|
!bitmap-symbol-texttopright.pdf
|
||||||
|
!bitmap-symbol-texttoprighttranspose.pdf
|
||||||
|
!bitmap-symbol-texttranspose.pdf
|
||||||
|
!bitmap-symbol.pdf
|
||||||
|
!bitmap-template1-customat-tpgdon.pdf
|
||||||
|
!bitmap-template1-customat.pdf
|
||||||
|
!bitmap-template1-tpgdon.pdf
|
||||||
|
!bitmap-template1.pdf
|
||||||
|
!bitmap-template2-customat-tpgdon.pdf
|
||||||
|
!bitmap-template2-customat.pdf
|
||||||
|
!bitmap-template2-tpgdon.pdf
|
||||||
|
!bitmap-template2.pdf
|
||||||
|
!bitmap-template3-customat-tpgdon.pdf
|
||||||
|
!bitmap-template3-customat.pdf
|
||||||
|
!bitmap-template3-tpgdon.pdf
|
||||||
|
!bitmap-template3.pdf
|
||||||
|
!bitmap-tpgdon.pdf
|
||||||
|
!bitmap-trailing-7fff-stripped-harder-refine.pdf
|
||||||
|
!bitmap-trailing-7fff-stripped-harder.pdf
|
||||||
|
!bitmap-trailing-7fff-stripped.pdf
|
||||||
|
!bitmap.pdf
|
||||||
|
!bomb_giant.pdf
|
||||||
|
!bug2009627.pdf
|
||||||
|
!page_with_number.pdf
|
||||||
|
!page_with_number_and_link.pdf
|
||||||
|
!Brotli-Prototype-FileA.pdf
|
||||||
|
!bug2013793.pdf
|
||||||
|
!bug2014080.pdf
|
||||||
|
|||||||
BIN
test/pdfs/Brotli-Prototype-FileA.pdf
Normal file
BIN
test/pdfs/Brotli-Prototype-FileA.pdf
Normal file
Binary file not shown.
1
test/pdfs/JBIG2Globals.pdf.link
Normal file
1
test/pdfs/JBIG2Globals.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/pdfminer/pdfminer.six/files/8399364/64.pdf
|
||||||
BIN
test/pdfs/bitmap-composite-and-xnor-halftone.pdf
Normal file
BIN
test/pdfs/bitmap-composite-and-xnor-halftone.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-and-xnor-refine.pdf
Normal file
BIN
test/pdfs/bitmap-composite-and-xnor-refine.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-and-xnor-text.pdf
Normal file
BIN
test/pdfs/bitmap-composite-and-xnor-text.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-and-xnor.pdf
Normal file
BIN
test/pdfs/bitmap-composite-and-xnor.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-or-xor-replace-halftone.pdf
Normal file
BIN
test/pdfs/bitmap-composite-or-xor-replace-halftone.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-or-xor-replace-refine.pdf
Normal file
BIN
test/pdfs/bitmap-composite-or-xor-replace-refine.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-or-xor-replace-text.pdf
Normal file
BIN
test/pdfs/bitmap-composite-or-xor-replace-text.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/bitmap-composite-or-xor-replace.pdf
Normal file
BIN
test/pdfs/bitmap-composite-or-xor-replace.pdf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user