Compare commits

...

21 Commits

Author SHA1 Message Date
Tim van der Meij
702d60aa18
Merge pull request #21230 from calixteman/avoid_cycles
Avoid cycles when getting operator list in patterns
2026-05-10 18:15:01 +02:00
Tim van der Meij
3b58a339c8
Merge pull request #21213 from saripovdenis/perf-name-tree-getall-queue-index
perf: Avoid multi-second getDestinations stalls for PDFs with many named destinations
2026-05-10 18:13:12 +02:00
Jonas Jenwald
1079535a1e
Merge pull request #21249 from Snuffleupagus/open-filename-unconditional
Support providing a `filename` unconditionally, when passing binary data to `PDFViewerApplication.open`
2026-05-10 15:48:43 +02:00
Tim van der Meij
02c9051b64
Merge pull request #21245 from Snuffleupagus/getDocument-only-DocumentInitParameters
[api-major] Remove support for calling `getDocument` without  a parameter object, and remove the `PDFDocumentProxy.prototype.destroy` method
2026-05-10 14:30:15 +02:00
Tim van der Meij
559e2f2006
Merge pull request #21236 from calixteman/pixel_fallback_smask
Fall back to pixel-buffer SMask filtering when ctx.filter is unavailable
2026-05-10 14:27:52 +02:00
Tim van der Meij
47e32709b3
Merge pull request #21246 from Snuffleupagus/crypto_spec-fromHex
Use `Uint8Array.fromHex` in `test/unit/crypto_spec.js`
2026-05-10 14:12:33 +02:00
Tim van der Meij
33a6f3e206
Merge pull request #21238 from Snuffleupagus/PDFDataRangeTransport-unittest-faster
Make the `PDFDataRangeTransport` streaming unit-tests faster
2026-05-10 14:11:09 +02:00
Tim van der Meij
e8a051cee5
Merge pull request #21248 from mozilla/dependabot/npm_and_yarn/babel/plugin-transform-modules-systemjs-7.29.4
Bump @babel/plugin-transform-modules-systemjs from 7.29.0 to 7.29.4
2026-05-10 14:07:18 +02:00
Tim van der Meij
e511c88f11
Merge pull request #21244 from mozilla/dependabot/npm_and_yarn/fast-uri-3.1.2
Bump fast-uri from 3.1.0 to 3.1.2
2026-05-10 14:06:27 +02:00
Tim van der Meij
58ee4cd18d
Merge pull request #21243 from mozilla/dependabot/npm_and_yarn/fast-xml-builder-1.2.0
Bump fast-xml-builder from 1.1.5 to 1.2.0
2026-05-10 14:06:00 +02:00
Jonas Jenwald
0763c69b6a Support providing a filename unconditionally, when passing binary data to PDFViewerApplication.open
Previously this was only supported in Firefox, however when merging PDFs the `PDFViewerApplication.onSaveAndLoad` method will provide a `filename` unconditionally.
2026-05-10 13:51:33 +02:00
dependabot[bot]
f6e69b8a20
Bump @babel/plugin-transform-modules-systemjs from 7.29.0 to 7.29.4
Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.29.0 to 7.29.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.4/packages/babel-plugin-transform-modules-systemjs)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-modules-systemjs"
  dependency-version: 7.29.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-09 17:40:37 +00:00
Jonas Jenwald
eb184851a8 Use Uint8Array.fromHex in test/unit/crypto_spec.js
This replaces the old `hex2binary` helper function with the native `Uint8Array.fromHex` method.
2026-05-09 17:17:50 +02:00
Jonas Jenwald
43ce1bb491 [api-major] Remove the PDFDocumentProxy.prototype.destroy method
This is a left-over from very old code, which pre-dates the introduction of the `PDFDocumentLoadingTask` and it's nothing more than an alias for its `destroy` method.
Given that `PDFDocumentProxy` already provides a way to access the underlying `PDFDocumentLoadingTask` instance, it shouldn't be necessary to have an alias for one of its methods.

*Please note:* For any existing code relying on the removed method, updating it should be as simple as replacing `pdfDocument.destroy()` with `pdfDocument.loadingTask.destroy()`.

---

[1] If the `PDFDocumentProxy` class was added today, there's no chance that it'd include a `destroy` method.
2026-05-09 13:20:39 +02:00
Jonas Jenwald
2e7a2215e3 [api-major] Remove support for calling getDocument without a parameter object
This is a left-over from very old code[1], before there were a lot of `getDocument` options and when most of the library configuration was done via the (since removed) `PDFJS` global.
Given all the functionality added through the years, which require configuration[2], in practice it's now unlikely that calling `getDocument` without additional options will work except for the most trivial PDFs.

---

[1] If the `getDocument` function was added today, there's no chance that it'd support anything other than a parameter object.

[2] Note things such as CMaps, standard fonts, wasm-based image decoders, and ICC-based colour spaces.
2026-05-09 13:00:27 +02:00
dependabot[bot]
b5af8151f6
Bump fast-uri from 3.1.0 to 3.1.2
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-09 00:28:57 +00:00
dependabot[bot]
e94f9a9797
Bump fast-xml-builder from 1.1.5 to 1.2.0
Bumps [fast-xml-builder](https://github.com/NaturalIntelligence/fast-xml-builder) from 1.1.5 to 1.2.0.
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-builder/blob/main/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-builder/compare/v1.1.5...v1.2.0)

---
updated-dependencies:
- dependency-name: fast-xml-builder
  dependency-version: 1.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-08 18:32:54 +00:00
Jonas Jenwald
15921243d4 Make the PDFDataRangeTransport streaming unit-tests faster
The delay between chunks when testing streaming is necessary to avoid the entire PDF document arriving all at once, since that would render those unit-tests somewhat pointless.
However, the delay is unnecessarily large which causes these unit-tests to be slower than necessary.

Also, update the range unit-tests to check the expected number of fetches *exactly* since those values are not supposed to vary.
2026-05-08 12:44:32 +02:00
Calixte Denizet
29fcf0aa76
Avoid cycles when getting operator list in patterns 2026-05-07 22:30:51 +02:00
calixteman
b8232710c8
Fall back to pixel-buffer SMask filtering when ctx.filter is unavailable
For now OffscreenCanvas in worker threads doesn't support ctx.filter,
so we need to fall back to a more expensive pixel-buffer SMask filtering in that case.
As a side effect, this also allows to support correctly smask in Safari.
2026-05-07 22:27:04 +02:00
saripovdenis
473f9b4592 Avoid quadratic traversal in NameOrNumberTree.getAll
Using Array.prototype.shift() to drain the traversal queue makes each
visited node move the remaining queued entries. For large name/number
trees this can make getAll() spend quadratic time in queue management.

Iterate over the queue with for...of instead. Children pushed while
iterating are still visited, and the queue no longer needs repeated
front removals.
2026-05-06 09:51:57 +08:00
18 changed files with 384 additions and 224 deletions

View File

@ -18,13 +18,13 @@ This tutorial shows how PDF.js can be used as a library in a web browser.
The object structure of PDF.js loosely follows the structure of an actual PDF. At the top level there is a document object. From the document, more information and individual pages can be fetched. To get the document:
```js
pdfjsLib.getDocument('helloworld.pdf')
pdfjsLib.getDocument({ url: "helloworld.pdf" })
```
Remember though that PDF.js uses promises, and the above will return a `PDFDocumentLoadingTask` instance that has a `promise` property which is resolved with the document object.
```js
var loadingTask = pdfjsLib.getDocument('helloworld.pdf');
var loadingTask = pdfjsLib.getDocument({ url: "helloworld.pdf" });
loadingTask.promise.then(function(pdf) {
// you can now use *pdf* here
});
@ -48,8 +48,8 @@ var viewport = page.getViewport({ scale: scale, });
// Support HiDPI-screens.
var outputScale = window.devicePixelRatio || 1;
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
var canvas = document.getElementById("the-canvas");
var context = canvas.getContext("2d");
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);

View File

@ -26,7 +26,7 @@
//
// Asynchronous download PDF
//
const loadingTask = pdfjsLib.getDocument(url);
const loadingTask = pdfjsLib.getDocument({ url });
const pdf = await loadingTask.promise;
//
// Fetch the first page

View File

@ -123,7 +123,7 @@
/**
* Asynchronously downloads PDF.
*/
var loadingTask = pdfjsLib.getDocument(url);
var loadingTask = pdfjsLib.getDocument({ url });
pdfDoc = await loadingTask.promise;
document.getElementById("page_count").textContent = pdfDoc.numPages;

View File

@ -14,7 +14,7 @@ const pdfPath =
// Will be using promises to load document, pages and misc data instead of
// callback.
const loadingTask = getDocument(pdfPath);
const loadingTask = getDocument({ url: pdfPath });
loadingTask.promise
.then(function (doc) {
const numPages = doc.numPages;

37
package-lock.json generated
View File

@ -1003,9 +1003,9 @@
}
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
"integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
"version": "7.29.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz",
"integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6009,9 +6009,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"dev": true,
"funding": [
{
@ -6026,9 +6026,9 @@
"license": "BSD-3-Clause"
},
"node_modules/fast-xml-builder": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz",
"integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
"integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
"dev": true,
"funding": [
{
@ -6038,7 +6038,8 @@
],
"license": "MIT",
"dependencies": {
"path-expression-matcher": "^1.1.3"
"path-expression-matcher": "^1.5.0",
"xml-naming": "^0.1.0"
}
},
"node_modules/fast-xml-parser": {
@ -12687,6 +12688,22 @@
}
}
},
"node_modules/xml-naming": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
"integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/xmlcreate": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",

View File

@ -982,7 +982,8 @@ class PartialEvaluator {
patternDict,
operatorList,
task,
localTilingPatternCache
localTilingPatternCache,
seenRefs
) {
// Create an IR of the pattern code.
const tilingOpList = new CheckedOperatorList();
@ -998,6 +999,7 @@ class PartialEvaluator {
task,
resources: patternResources,
operatorList: tilingOpList,
prevRefs: seenRefs,
})
.then(function () {
const operatorListIR = tilingOpList.getIR();
@ -1041,7 +1043,8 @@ class PartialEvaluator {
task,
state,
fallbackFontDict = null,
cssFontInfo = null
cssFontInfo = null,
seenRefs = null
) {
const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null;
@ -1051,7 +1054,8 @@ class PartialEvaluator {
resources,
task,
fallbackFontDict,
cssFontInfo
cssFontInfo,
seenRefs
);
if (translated.font.isType3Font) {
@ -1152,7 +1156,10 @@ class PartialEvaluator {
value[0],
operatorList,
task,
stateManager.state
stateManager.state,
/* fallbackFontDict = */ null,
/* cssFontInfo = */ null,
seenRefs
).then(function (loadedName) {
operatorList.addDependency(loadedName);
gStateObj.push([key, [loadedName, value[1]]]);
@ -1230,7 +1237,8 @@ class PartialEvaluator {
resources,
task,
fallbackFontDict = null,
cssFontInfo = null
cssFontInfo = null,
seenRefs = null
) {
const errorFont = async () =>
new TranslatedFont({
@ -1366,7 +1374,7 @@ class PartialEvaluator {
if (translatedFont.isType3Font) {
try {
await translated.loadType3Data(this, resources, task);
await translated.loadType3Data(this, resources, task, seenRefs);
} catch (reason) {
throw new Error(`Type3 font load error: ${reason}`);
}
@ -1577,7 +1585,8 @@ class PartialEvaluator {
task,
localColorSpaceCache,
localTilingPatternCache,
localShadingPatternCache
localShadingPatternCache,
seenRefs
) {
// compile tiling patterns
const patternName = args.pop();
@ -1619,7 +1628,8 @@ class PartialEvaluator {
dict,
operatorList,
task,
localTilingPatternCache
localTilingPatternCache,
seenRefs
);
} else if (typeNum === PatternType.SHADING) {
const shading = dict.get("Shading");
@ -1934,7 +1944,9 @@ class PartialEvaluator {
operatorList,
task,
stateManager.state,
fallbackFontDict
fallbackFontDict,
/* cssFontInfo = */ null,
seenRefs
)
.then(function (loadedName) {
operatorList.addDependency(loadedName);
@ -2112,7 +2124,8 @@ class PartialEvaluator {
task,
localColorSpaceCache,
localTilingPatternCache,
localShadingPatternCache
localShadingPatternCache,
seenRefs
)
);
return;
@ -2144,7 +2157,8 @@ class PartialEvaluator {
task,
localColorSpaceCache,
localTilingPatternCache,
localShadingPatternCache
localShadingPatternCache,
seenRefs
)
);
return;
@ -2731,7 +2745,10 @@ class PartialEvaluator {
fontName,
fontRef,
resources,
task
task,
/* fallbackFontDict = */ null,
/* cssFontInfo = */ null,
seenRefs
);
textState.loadedName = translated.loadedName;
@ -4893,7 +4910,7 @@ class TranslatedFont {
);
}
loadType3Data(evaluator, resources, task) {
loadType3Data(evaluator, resources, task, seenRefs = null) {
if (this.#type3Loaded) {
return this.#type3Loaded;
}
@ -4931,6 +4948,7 @@ class TranslatedFont {
task,
resources: fontResources,
operatorList,
prevRefs: seenRefs,
})
.then(() => {
// According to the PDF specification, section "9.6.5 Type 3 Fonts"

View File

@ -46,8 +46,8 @@ class NameOrNumberTree {
processed.put(this.root);
}
const queue = [this.root];
while (queue.length > 0) {
const obj = xref.fetchIfRef(queue.shift());
for (const node of queue) {
const obj = xref.fetchIfRef(node);
if (!(obj instanceof Dict)) {
continue;
}

View File

@ -221,19 +221,10 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
* XHR as fallback) is used, which means it must follow same origin rules,
* e.g. no cross-domain requests without CORS.
*
* @param {string | URL | TypedArray | ArrayBuffer | DocumentInitParameters}
* src - Can be a URL where a PDF file is located, a typed array (Uint8Array)
* already populated with data, or a parameter object.
* @param {DocumentInitParameters} src - Parameter object.
* @returns {PDFDocumentLoadingTask}
*/
function getDocument(src = {}) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
if (typeof src === "string" || src instanceof URL) {
src = { url: src };
} else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) {
src = { data: src };
}
}
const task = new PDFDocumentLoadingTask();
const { docId } = task;
@ -1035,13 +1026,6 @@ class PDFDocumentProxy {
return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
}
/**
* Destroys the current document instance and terminates the worker.
*/
destroy() {
return this.loadingTask.destroy();
}
/**
* @param {RefProxy} ref - The page reference.
* @returns {number | null} The page number, if it's cached.

View File

@ -1279,13 +1279,20 @@ class CanvasGraphics {
const useLayerSize =
layerW * layerH < SMASK_LAYER_TO_MASK_AREA_RATIO * maskArea;
let filterUrl = null;
if (hasFilter) {
filterUrl =
subtype === "Alpha"
? this.filterFactory.addAlphaFilter(transferMap)
: this.filterFactory.addLuminosityFilter(transferMap);
}
// Bundle the filter URL with the spec needed for the pixel-buffer
// fallback (see _bakeSMaskCanvas). subtype + transferMap let the
// fallback reproduce the SVG filter without an extra round-trip
// through the filter factory.
const filterSpec = hasFilter
? {
url:
subtype === "Alpha"
? this.filterFactory.addAlphaFilter(transferMap)
: this.filterFactory.addLuminosityFilter(transferMap),
subtype,
transferMap,
}
: null;
// Alpha SMasks must not bake /BC into the prepared canvas (see
// filteredOOBAlpha comment above).
@ -1300,7 +1307,7 @@ class CanvasGraphics {
layerW,
layerH,
bakedBackdrop,
filterUrl
filterSpec
);
offsetX = 0;
offsetY = 0;
@ -1312,7 +1319,7 @@ class CanvasGraphics {
maskCanvas.width,
maskCanvas.height,
bakedBackdrop,
filterUrl
filterSpec
);
offsetX = smask.offsetX;
offsetY = smask.offsetY;
@ -1330,8 +1337,9 @@ class CanvasGraphics {
/**
* Bake the mask plus optional backdrop into a (w x h) canvas with the
* mask drawn at (drawX, drawY), then optionally pipe through
* `filterUrl`. Returns the prepared canvas-factory entry.
* mask drawn at (drawX, drawY), then optionally pipe through the SVG
* filter described by `filterSpec`. Returns the prepared canvas-
* factory entry.
*
* The backdrop fill uses destination-atop so transparent / partial-
* alpha pixels inside the mask see the backdrop *before* filtering
@ -1344,9 +1352,17 @@ class CanvasGraphics {
* transferMap[0], matching the spec's transparent extension of the
* mask group. No-backdrop mask-size prebakes have no OOB region;
* destination-in handles OOB at compose time.
*
* Some browsers (e.g. older Safari) silently ignore SVG `url(#id)`
* filters on a 2D canvas: the assignment is accepted but
* `ctx.filter` reads back as "none" and `drawImage` produces an
* unfiltered copy. We detect that and fall back to a pixel-buffer
* loop that reproduces the SVG filter exactly (matrix luminance and
* `feFuncA` transferMap, both with sRGB color-interpolation, i.e.
* straight on gamma-encoded byte values).
*/
_bakeSMaskCanvas(maskCanvas, drawX, drawY, w, h, backdrop, filterUrl) {
if (!backdrop && !filterUrl) {
_bakeSMaskCanvas(maskCanvas, drawX, drawY, w, h, backdrop, filterSpec) {
if (!backdrop && !filterSpec) {
// Caller (_prepareSMaskCanvas) gates on this; without either,
// the prebake would just be a wasted copy of the mask.
unreachable("_bakeSMaskCanvas with neither backdrop nor filter");
@ -1359,14 +1375,45 @@ class CanvasGraphics {
sCtx.fillStyle = backdrop;
sCtx.fillRect(0, 0, w, h);
}
if (!filterUrl) {
if (!filterSpec) {
return srcEntry;
}
const preparedEntry = this.canvasFactory.create(w, h);
const pCtx = preparedEntry.context;
pCtx.filter = filterUrl;
// Pre-assign read: undefined means no canvas filter API (assigning
// would just set a JS property and post-assign read would lie).
// Post-assign "none"/"" means the URL was rejected (Firefox
// normalizes accepted url(#id) to an absolute URL).
const filterSupported = pCtx.filter !== undefined;
pCtx.filter = filterSpec.url;
const filterApplied =
filterSupported && pCtx.filter !== "none" && pCtx.filter !== "";
pCtx.drawImage(srcEntry.canvas, 0, 0);
pCtx.filter = "none";
if (filterSupported) {
pCtx.filter = "none";
}
if (!filterApplied) {
const img = pCtx.getImageData(0, 0, w, h);
const { data } = img;
const { transferMap } = filterSpec;
if (filterSpec.subtype === "Luminosity") {
for (let i = 0, ii = data.length; i < ii; i += 4) {
// Match #addLuminosityConversion: a' = 0.3*R + 0.59*G + 0.11*B,
// RGB -> 0; then optional transferMap on alpha.
const a =
(0.3 * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2] + 0.5) | 0;
data[i] = data[i + 1] = data[i + 2] = 0;
data[i + 3] = transferMap?.[a] ?? a;
}
} else {
// Alpha: transferMap is guaranteed by _prepareSMaskCanvas's
// hasFilter gate.
for (let i = 3, ii = data.length; i < ii; i += 4) {
data[i] = transferMap[data[i]];
}
}
pCtx.putImageData(img, 0, 0);
}
this.canvasFactory.destroy(srcEntry);
return preparedEntry;
}

View File

@ -866,7 +866,7 @@ class Driver {
await loadingTask.destroy();
delete task.annotationStorage;
return getDocument(data).promise;
return getDocument({ data }).promise;
});
}
@ -969,7 +969,7 @@ class Driver {
// Wipe out the link to the pdfdoc so it can be GC'ed.
for (const task of [this.currentTask, ...this.taskQueue]) {
if (task?.pdfDoc) {
destroyedPromises.push(task.pdfDoc.destroy());
destroyedPromises.push(task.pdfDoc.loadingTask.destroy());
delete task.pdfDoc;
}
}

View File

@ -912,3 +912,4 @@
!smask_alpha_oob_transfer.pdf
!smask_alpha_bc.pdf
!smask_luminosity_oob_transfer.pdf
!operator_list_cycle.pdf

View File

@ -0,0 +1,98 @@
%PDF-1.7
% ò¤ô
1 0 obj <<
/Kids [3 0 R]
/Type /Pages
/Count 1
>>
endobj
2 0 obj <<
/Type /Catalog
/Pages 1 0 R
>>
endobj
3 0 obj <<
/Resources 11 0 R
/Type /Page
/Contents 10 0 R
/Parent 1 0 R
>>
endobj
10 0 obj <<
>>
stream
1 0 0 1 315.779 733.039 cm
1 0 0 1 2.835 -173.614 cm
1.04704 0 0 1.04704 0 0 cm
/Im6 Do
endstream
endobj
11 0 obj <<
/XObject <<
/Im6 12 0 R
>>
>>
endobj
12 0 obj <<
/Subtype /Form
/Resources <<
/XObject <<
/x15 13 0 R
>>
>>
>>
stream
/x15 Do
endstream
endobj
13 0 obj <<
/Subtype /Form
/Resources <<
/Pattern <<
/p31 14 0 R
>>
>>
>>
stream
q /Pattern cs /p31 scn /a0 gs
0 0 224.720001 160.399994 re f
Q
endstream
endobj
14 0 obj <<
/PatternType 1
/BBox [0 0 225 161]
/Resources <<
/XObject <<
/x47 12 0 R
>>
>>
>>
stream
/x47 Do
endstream
endobj
xref
0 15
0000000000 65535 f
4294967295 00000 n
0000000078 00000 n
0000000131 00000 n
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000221 00000 n
0000000348 00000 n
0000000405 00000 n
0000000531 00000 n
0000000712 00000 n
trailer <<
/Root 2 0 R
/Size 110
>>
startxref
860
%%EOF

View File

@ -10,7 +10,7 @@ class MainTest {
}
loadPdf() {
this.task = getDocument("file://" + this.file);
this.task = getDocument({ url: "file://" + this.file });
return this.task.promise;
}
}

View File

@ -10,7 +10,7 @@ class MainTest {
}
loadPdf() {
this.task = getDocument("file://" + this.file);
this.task = getDocument({ url: "file://" + this.file });
return this.task.promise;
}
}

View File

@ -121,7 +121,7 @@ describe("api", function () {
describe("getDocument", function () {
it("creates pdf doc from URL-string", async function () {
const urlStr = TEST_PDFS_PATH + basicApiFileName;
const loadingTask = getDocument(urlStr);
const loadingTask = getDocument({ url: urlStr });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
const pdfDocument = await loadingTask.promise;
@ -135,7 +135,7 @@ describe("api", function () {
it("creates pdf doc from URL-object", async function () {
const urlObj = TestPdfsServer.resolveURL(basicApiFileName);
const loadingTask = getDocument(urlObj);
const loadingTask = getDocument({ url: urlObj });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
const pdfDocument = await loadingTask.promise;
@ -210,7 +210,7 @@ describe("api", function () {
expect(typedArrayPdf).toBeInstanceOf(Uint8Array);
expect(typedArrayPdf.length).toEqual(basicApiFileLength);
const loadingTask = getDocument(typedArrayPdf);
const loadingTask = getDocument({ data: typedArrayPdf });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
const progressReportedCapability = Promise.withResolvers();
@ -245,7 +245,7 @@ describe("api", function () {
expect(arrayBufferPdf).toBeInstanceOf(ArrayBuffer);
expect(arrayBufferPdf.byteLength).toEqual(basicApiFileLength);
const loadingTask = getDocument(arrayBufferPdf);
const loadingTask = getDocument({ data: arrayBufferPdf });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
const progressReportedCapability = Promise.withResolvers();
@ -516,7 +516,7 @@ describe("api", function () {
);
it("creates pdf doc from empty TypedArray", async function () {
const loadingTask = getDocument(new Uint8Array(0));
const loadingTask = getDocument({ data: new Uint8Array(0) });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
try {
@ -850,7 +850,7 @@ describe("api", function () {
expect(typedArrayPdf).toBeInstanceOf(Uint8Array);
expect(typedArrayPdf.length).toEqual(1116);
const loadingTask = getDocument(typedArrayPdf.slice());
const loadingTask = getDocument({ data: typedArrayPdf.slice() });
expect(loadingTask).toBeInstanceOf(PDFDocumentLoadingTask);
let passwordData = null;
@ -2407,7 +2407,7 @@ describe("api", function () {
expect(typedArrayPdf).toBeInstanceOf(Uint8Array);
expect(typedArrayPdf.length).toEqual(10719);
const loadingTask = getDocument(typedArrayPdf.slice());
const loadingTask = getDocument({ data: typedArrayPdf.slice() });
const pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
// Trigger parsing of the JPEG image.
@ -2463,7 +2463,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const pdfPage = await pdfDoc.getPage(1);
const annotations = await pdfPage.getAnnotations();
@ -2486,7 +2486,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const datasets = await pdfDoc.getXFADatasets();
@ -2522,7 +2522,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const datasets = await pdfDoc.getXFADatasets();
@ -2559,7 +2559,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
page = await pdfDoc.getPage(1);
const newStructTree = await page.getStructTree();
@ -2594,7 +2594,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const annotations = await pdfDoc.getAnnotArray(0);
@ -2673,7 +2673,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
// Ensure that the Annotation text-content was actually compressed.
typedArray = await pdfDoc.getData();
@ -2714,7 +2714,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const opList = await page.getOperatorList();
@ -2772,7 +2772,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -2867,7 +2867,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -2935,11 +2935,11 @@ describe("api", function () {
const blob = await getImageBlob("firefox_logo.png");
let loadingTask, pdfDoc;
let data = buildGetDocumentParams("empty.pdf");
let docParams = buildGetDocumentParams("empty.pdf");
for (let i = 1; i <= 2; i++) {
const bitmap = await createImageBitmap(blob);
loadingTask = getDocument(data);
loadingTask = getDocument(docParams);
pdfDoc = await loadingTask.promise;
pdfDoc.annotationStorage.setValue("pdfjs_internal_editor_0", {
annotationType: AnnotationEditorType.STAMP,
@ -2955,11 +2955,11 @@ describe("api", function () {
},
});
data = await pdfDoc.saveDocument();
docParams = { data: await pdfDoc.saveDocument() };
await loadingTask.destroy();
}
loadingTask = getDocument(data);
loadingTask = getDocument(docParams);
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -3018,7 +3018,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -3081,7 +3081,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -3119,7 +3119,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const annotations = await page.getAnnotations();
@ -3163,7 +3163,7 @@ describe("api", function () {
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const tree = await page.getStructTree();
@ -3203,7 +3203,7 @@ describe("api", function () {
params.url = url.href;
loadingTask = getDocument(params);
return loadingTask.promise
.then(pdf => pdf.destroy())
.then(() => loadingTask.destroy())
.then(
function () {
expect(expectSuccess).toEqual(true);
@ -4452,6 +4452,19 @@ have written that much by now. So, heres to squashing bugs.`);
}
);
it("gets operator list, from a PDF with a Form -> Form -> Pattern -> Form cycle", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("operator_list_cycle.pdf")
);
const pdfDoc = await loadingTask.promise;
const pdfPage = await pdfDoc.getPage(1);
const operatorList = await pdfPage.getOperatorList();
expect(operatorList.lastChunk).toEqual(true);
await loadingTask.destroy();
});
it("gets operator list, containing Annotation-operatorLists", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("annotation-line.pdf")
@ -5207,7 +5220,7 @@ have written that much by now. So, heres to squashing bugs.`);
describe("PDFDataRangeTransport", function () {
async function streamDelay() {
return new Promise(resolve => {
setTimeout(resolve, 250);
setTimeout(resolve, 100);
});
}
let dataPromise;
@ -5269,7 +5282,7 @@ have written that much by now. So, heres to squashing bugs.`);
expect(contentDispositionFilename).toEqual("aaa.pdf");
expect(contentLength).toEqual(dataLength);
expect(fetches).toBeGreaterThan(4);
expect(fetches).toEqual(5);
expect(initialProgress).toEqual({
loaded: initialDataLength,
@ -5428,7 +5441,7 @@ have written that much by now. So, heres to squashing bugs.`);
expect(contentDispositionFilename).toEqual(null);
expect(contentLength).toEqual(dataLength);
expect(fetches).toBeGreaterThan(2);
expect(fetches).toEqual(3);
expect(initialProgress.loaded).toBeGreaterThan(initialDataLength);
expect(initialProgress.total).toEqual(dataLength);
@ -5597,7 +5610,7 @@ small scripts as well as for`);
return;
}
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(expected.length);
@ -5631,7 +5644,7 @@ small scripts as well as for`);
{ document: pdfData2 },
{ document: pdfData3 },
]);
let newLoadingTask = getDocument(data);
let newLoadingTask = getDocument({ data });
let newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(9);
@ -5649,7 +5662,7 @@ small scripts as well as for`);
{ document: pdfData2 },
{ document: null },
]);
newLoadingTask = getDocument(data);
newLoadingTask = getDocument({ data });
newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(9);
for (let i = 1; i <= 9; i++) {
@ -5666,7 +5679,7 @@ small scripts as well as for`);
{ document: pdfData2, includePages: [0] },
{ document: pdfData3, includePages: [0] },
]);
newLoadingTask = getDocument(data);
newLoadingTask = getDocument({ data });
newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(3);
for (let i = 1; i <= 3; i++) {
@ -5681,7 +5694,7 @@ small scripts as well as for`);
{ document: pdfData2, excludePages: [0] },
{ document: pdfData3, excludePages: [0] },
]);
newLoadingTask = getDocument(data);
newLoadingTask = getDocument({ data });
newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(6);
for (let i = 1; i <= 6; i++) {
@ -5709,7 +5722,7 @@ small scripts as well as for`);
{ document: pdfData1, includePages: [[0, 0], 2] },
{ document: null, includePages: [[2, 4], 7] },
]);
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(6);
@ -5756,7 +5769,7 @@ small scripts as well as for`);
],
},
]);
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(6);
@ -5796,7 +5809,7 @@ small scripts as well as for`);
{ document: null, includePages: [0] },
{ document: pdfData1, password: "asdfasdf" },
]);
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(2);
@ -5845,7 +5858,7 @@ small scripts as well as for`);
includePages: [0, 1, 5, 7, 10],
});
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
const newPdfDoc = await loadingTask.promise;
labels = await newPdfDoc.getPageLabels();
expect(labels).toEqual(["i", "ii", "1", "a", "5"]);
@ -5869,7 +5882,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
destinations = await pdfDoc.getDestinations();
@ -5905,7 +5918,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(4);
@ -5932,7 +5945,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(8);
@ -5973,7 +5986,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -6010,7 +6023,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(4);
@ -6089,7 +6102,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(2);
@ -6192,7 +6205,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -6283,7 +6296,7 @@ small scripts as well as for`);
{ document: null, includePages: [1, 3, 5], pageIndices: [1, 2, 0] },
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -6365,7 +6378,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(2);
@ -6404,7 +6417,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -6449,7 +6462,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(6);
@ -6503,7 +6516,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(5);
@ -6533,7 +6546,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -6574,7 +6587,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const attachments = await pdfDoc.getAttachments();
@ -6601,7 +6614,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const attachments = await pdfDoc.getAttachments();
@ -6649,7 +6662,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(1);
@ -6701,7 +6714,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(2);
@ -6747,7 +6760,7 @@ small scripts as well as for`);
const data = await pdfDoc.extractPages([{ document: null }]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
const calculationOrder = await pdfDoc.getCalculationOrderIds();
@ -6778,7 +6791,7 @@ small scripts as well as for`);
expect(data).not.toBeNull();
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(2);
await newLoadingTask.destroy();
@ -6810,7 +6823,7 @@ small scripts as well as for`);
const data = await pdfDoc.extractPages([{ document: null }]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
const outline = await newPdfDoc.getOutline();
@ -6890,7 +6903,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
const outline = await newPdfDoc.getOutline();
@ -6928,7 +6941,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
const outline = await newPdfDoc.getOutline();
@ -6996,7 +7009,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(2);
@ -7056,7 +7069,7 @@ small scripts as well as for`);
const data = await pdfDoc.extractPages([{ document: null }]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
const outline = await newPdfDoc.getOutline();
@ -7084,7 +7097,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(2);
@ -7133,7 +7146,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(1);
@ -7165,7 +7178,7 @@ small scripts as well as for`);
const data = await pdfDoc.extractPages([{ document: null }]);
await loadingTask.destroy();
const newLoadingTask = getDocument(data);
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(1);
await newLoadingTask.destroy();
@ -7188,7 +7201,7 @@ small scripts as well as for`);
// Opening the result without a password must fail.
// Use a copy so the underlying ArrayBuffer is not detached before the
// second getDocument call below.
const passwordNeededLoadingTask = getDocument(data.slice());
const passwordNeededLoadingTask = getDocument({ data: data.slice() });
await passwordNeededLoadingTask.promise.then(
function () {
// Shouldn't get here.
@ -7227,7 +7240,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -7262,7 +7275,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -7296,7 +7309,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(23);
@ -7335,7 +7348,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -7368,7 +7381,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(4);
@ -7401,7 +7414,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -7433,7 +7446,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(3);
@ -7520,7 +7533,7 @@ small scripts as well as for`);
]);
await loadingTask.destroy();
loadingTask = getDocument(data);
loadingTask = getDocument({ data });
pdfDoc = await loadingTask.promise;
expect(pdfDoc.numPages).toEqual(2);

View File

@ -35,50 +35,36 @@ import { calculateMD5 } from "../../src/core/calculate_md5.js";
import { calculateSHA256 } from "../../src/core/calculate_sha256.js";
describe("crypto", function () {
function hex2binary(s) {
const digits = "0123456789ABCDEF";
s = s.toUpperCase();
const n = s.length >> 1;
const result = new Uint8Array(n);
for (let i = 0, j = 0; i < n; ++i) {
const d1 = s.charAt(j++);
const d2 = s.charAt(j++);
const value = (digits.indexOf(d1) << 4) | digits.indexOf(d2);
result[i] = value;
}
return result;
}
// RFC 1321, A.5 Test suite
describe("calculateMD5", function () {
it("should pass RFC 1321 test #1", function () {
const input = stringToBytes("");
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("d41d8cd98f00b204e9800998ecf8427e");
const expected = Uint8Array.fromHex("d41d8cd98f00b204e9800998ecf8427e");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #2", function () {
const input = stringToBytes("a");
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("0cc175b9c0f1b6a831c399e269772661");
const expected = Uint8Array.fromHex("0cc175b9c0f1b6a831c399e269772661");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #3", function () {
const input = stringToBytes("abc");
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("900150983cd24fb0d6963f7d28e17f72");
const expected = Uint8Array.fromHex("900150983cd24fb0d6963f7d28e17f72");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #4", function () {
const input = stringToBytes("message digest");
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("f96b697d7cb7938d525a2f31aaf161d0");
const expected = Uint8Array.fromHex("f96b697d7cb7938d525a2f31aaf161d0");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #5", function () {
const input = stringToBytes("abcdefghijklmnopqrstuvwxyz");
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("c3fcd3d76192e4007dfb496cca67e13b");
const expected = Uint8Array.fromHex("c3fcd3d76192e4007dfb496cca67e13b");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #6", function () {
@ -86,7 +72,7 @@ describe("crypto", function () {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
);
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("d174ab98d277d9f5a5611c2c9f419d9f");
const expected = Uint8Array.fromHex("d174ab98d277d9f5a5611c2c9f419d9f");
expect(result).toEqual(expected);
});
it("should pass RFC 1321 test #7", function () {
@ -95,7 +81,7 @@ describe("crypto", function () {
"90123456789012345678901234567890"
);
const result = calculateMD5(input, 0, input.length);
const expected = hex2binary("57edf4a22be3c955ac49da2e2107b67a");
const expected = Uint8Array.fromHex("57edf4a22be3c955ac49da2e2107b67a");
expect(result).toEqual(expected);
});
});
@ -103,40 +89,40 @@ describe("crypto", function () {
// http://www.freemedialibrary.com/index.php/RC4_test_vectors are used
describe("ARCFourCipher", function () {
it("should pass test #1", function () {
const key = hex2binary("0123456789abcdef");
const input = hex2binary("0123456789abcdef");
const key = Uint8Array.fromHex("0123456789abcdef");
const input = Uint8Array.fromHex("0123456789abcdef");
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary("75b7878099e0c596");
const expected = Uint8Array.fromHex("75b7878099e0c596");
expect(result).toEqual(expected);
});
it("should pass test #2", function () {
const key = hex2binary("0123456789abcdef");
const input = hex2binary("0000000000000000");
const key = Uint8Array.fromHex("0123456789abcdef");
const input = Uint8Array.fromHex("0000000000000000");
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary("7494c2e7104b0879");
const expected = Uint8Array.fromHex("7494c2e7104b0879");
expect(result).toEqual(expected);
});
it("should pass test #3", function () {
const key = hex2binary("0000000000000000");
const input = hex2binary("0000000000000000");
const key = Uint8Array.fromHex("0000000000000000");
const input = Uint8Array.fromHex("0000000000000000");
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary("de188941a3375d3a");
const expected = Uint8Array.fromHex("de188941a3375d3a");
expect(result).toEqual(expected);
});
it("should pass test #4", function () {
const key = hex2binary("ef012345");
const input = hex2binary("00000000000000000000");
const key = Uint8Array.fromHex("ef012345");
const input = Uint8Array.fromHex("00000000000000000000");
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary("d6a141a7ec3c38dfbd61");
const expected = Uint8Array.fromHex("d6a141a7ec3c38dfbd61");
expect(result).toEqual(expected);
});
it("should pass test #5", function () {
const key = hex2binary("0123456789abcdef");
const input = hex2binary(
const key = Uint8Array.fromHex("0123456789abcdef");
const input = Uint8Array.fromHex(
"010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
@ -156,7 +142,7 @@ describe("crypto", function () {
);
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76" +
"533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b" +
"1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377" +
@ -177,15 +163,15 @@ describe("crypto", function () {
expect(result).toEqual(expected);
});
it("should pass test #6", function () {
const key = hex2binary("fb029e3031323334");
const input = hex2binary(
const key = Uint8Array.fromHex("fb029e3031323334");
const input = Uint8Array.fromHex(
"aaaa0300000008004500004e661a00008011be640a0001220af" +
"fffff00890089003a000080a601100001000000000000204543454a4548454346434" +
"550464545494546464343414341434143414341414100002000011bd0b604"
);
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f" +
"58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53" +
"80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7"
@ -193,13 +179,13 @@ describe("crypto", function () {
expect(result).toEqual(expected);
});
it("should pass test #7", function () {
const key = hex2binary("0123456789abcdef");
const input = hex2binary(
const key = Uint8Array.fromHex("0123456789abcdef");
const input = Uint8Array.fromHex(
"123456789abcdef0123456789abcdef0123456789abcdef012345678"
);
const cipher = new ARCFourCipher(key);
const result = cipher.encryptBlock(input);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"66a0949f8af7d6891f7f832ba833c00c892ebe30143ce28740011ecf"
);
expect(result).toEqual(expected);
@ -210,7 +196,7 @@ describe("crypto", function () {
it("should properly hash abc", function () {
const input = stringToBytes("abc");
const result = calculateSHA256(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"
);
expect(result).toEqual(expected);
@ -220,7 +206,7 @@ describe("crypto", function () {
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
);
const result = calculateSHA256(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1"
);
expect(result).toEqual(expected);
@ -231,7 +217,7 @@ describe("crypto", function () {
it("should properly hash abc", function () {
const input = stringToBytes("abc");
const result = calculateSHA384(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163" +
"1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7"
);
@ -244,7 +230,7 @@ describe("crypto", function () {
"mnopqrstnopqrstu"
);
const result = calculateSHA384(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"09330C33F71147E83D192FC782CD1B4753111B173B3B05D2" +
"2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039"
);
@ -256,7 +242,7 @@ describe("crypto", function () {
it("should properly hash abc", function () {
const input = stringToBytes("abc");
const result = calculateSHA512(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" +
"0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" +
"454D4423643CE80E2A9AC94FA54CA49F"
@ -270,7 +256,7 @@ describe("crypto", function () {
"mnopqrstnopqrstu"
);
const result = calculateSHA512(input, 0, input.length);
const expected = hex2binary(
const expected = Uint8Array.fromHex(
"8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" +
"7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" +
"C7D329EEB6DD26545E96E55B874BE909"
@ -282,26 +268,25 @@ describe("crypto", function () {
describe("AES128", function () {
describe("Encryption", function () {
it("should be able to encrypt a block", function () {
const input = hex2binary("00112233445566778899aabbccddeeff");
const key = hex2binary("000102030405060708090a0b0c0d0e0f");
const iv = hex2binary("00000000000000000000000000000000");
const input = Uint8Array.fromHex("00112233445566778899aabbccddeeff");
const key = Uint8Array.fromHex("000102030405060708090a0b0c0d0e0f");
const iv = Uint8Array.fromHex("00000000000000000000000000000000");
const cipher = new AES128Cipher(key);
const result = cipher.encrypt(input, iv);
const expected = hex2binary("69c4e0d86a7b0430d8cdb78070b4c55a");
const expected = Uint8Array.fromHex("69c4e0d86a7b0430d8cdb78070b4c55a");
expect(result).toEqual(expected);
});
});
describe("Decryption", function () {
it("should be able to decrypt a block with IV in stream", function () {
const input = hex2binary(
"0000000000000000000000000000000069c4e0d86a7b0430d" +
"8cdb78070b4c55a"
const input = Uint8Array.fromHex(
"0000000000000000000000000000000069c4e0d86a7b0430d8cdb78070b4c55a"
);
const key = hex2binary("000102030405060708090a0b0c0d0e0f");
const key = Uint8Array.fromHex("000102030405060708090a0b0c0d0e0f");
const cipher = new AES128Cipher(key);
const result = cipher.decryptBlock(input);
const expected = hex2binary("00112233445566778899aabbccddeeff");
const expected = Uint8Array.fromHex("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
});
@ -310,44 +295,40 @@ describe("crypto", function () {
describe("AES256", function () {
describe("Encryption", function () {
it("should be able to encrypt a block", function () {
const input = hex2binary("00112233445566778899aabbccddeeff");
const key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
const input = Uint8Array.fromHex("00112233445566778899aabbccddeeff");
const key = Uint8Array.fromHex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
);
const iv = hex2binary("00000000000000000000000000000000");
const iv = Uint8Array.fromHex("00000000000000000000000000000000");
const cipher = new AES256Cipher(key);
const result = cipher.encrypt(input, iv);
const expected = hex2binary("8ea2b7ca516745bfeafc49904b496089");
const expected = Uint8Array.fromHex("8ea2b7ca516745bfeafc49904b496089");
expect(result).toEqual(expected);
});
});
describe("Decryption", function () {
it("should be able to decrypt a block with specified iv", function () {
const input = hex2binary("8ea2b7ca516745bfeafc49904b496089");
const key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
const input = Uint8Array.fromHex("8ea2b7ca516745bfeafc49904b496089");
const key = Uint8Array.fromHex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
);
const iv = hex2binary("00000000000000000000000000000000");
const iv = Uint8Array.fromHex("00000000000000000000000000000000");
const cipher = new AES256Cipher(key);
const result = cipher.decryptBlock(input, false, iv);
const expected = hex2binary("00112233445566778899aabbccddeeff");
const expected = Uint8Array.fromHex("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
it("should be able to decrypt a block with IV in stream", function () {
const input = hex2binary(
"000000000000000000000000000000008ea2b7ca516745bf" +
"eafc49904b496089"
const input = Uint8Array.fromHex(
"000000000000000000000000000000008ea2b7ca516745bfeafc49904b496089"
);
const key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
const key = Uint8Array.fromHex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
);
const cipher = new AES256Cipher(key);
const result = cipher.decryptBlock(input, false);
const expected = hex2binary("00112233445566778899aabbccddeeff");
const expected = Uint8Array.fromHex("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
});

View File

@ -1220,11 +1220,12 @@ const PDFViewerApplication = {
const workerParams = AppOptions.getAll(OptionKind.WORKER);
Object.assign(GlobalWorkerOptions, workerParams);
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
if (args.data && isPdfFile(args.filename)) {
this._contentDispositionFilename = args.filename;
}
} else if (args.url) {
if (args.data && isPdfFile(args.filename)) {
this._contentDispositionFilename = args.filename;
} else if (
(typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) &&
args.url
) {
// The Firefox built-in viewer always calls `setTitleUsingUrl`, before
// `initPassiveLoading`, and it never provides an `originalUrl` here.
this.setTitleUsingUrl(

View File

@ -95,7 +95,7 @@ async function openDocument(source, name) {
if (pdfDoc) {
pageView.reset();
await pdfDoc.destroy();
await pdfDoc.loadingTask.destroy();
pdfDoc = null;
}