diff --git a/examples/node/getinfo.mjs b/examples/node/getinfo.mjs index f3918f9acc873..2db6fcb639556 100644 --- a/examples/node/getinfo.mjs +++ b/examples/node/getinfo.mjs @@ -15,62 +15,44 @@ const pdfPath = // Will be using promises to load document, pages and misc data instead of // callback. const loadingTask = getDocument({ url: pdfPath }); -loadingTask.promise - .then(function (doc) { - const numPages = doc.numPages; - console.log("# Document Loaded"); - console.log("Number of Pages: " + numPages); +try { + const pdfDoc = await loadingTask.promise; + + const { numPages } = pdfDoc; + console.log("# Document Loaded"); + console.log(`Number of Pages: ${numPages}`); + console.log(); + + const { info, metadata } = await pdfDoc.getMetadata(); + console.log("# Metadata is Loaded"); + console.log("## Info"); + console.log(JSON.stringify(info, null, 2)); + console.log(); + if (metadata) { + console.log("## Metadata"); + console.log(JSON.stringify(Object.fromEntries(metadata), null, 2)); console.log(); + } - let lastPromise; // will be used to chain promises - lastPromise = doc.getMetadata().then(function (data) { - console.log("# Metadata Is Loaded"); - console.log("## Info"); - console.log(JSON.stringify(data.info, null, 2)); - console.log(); - if (data.metadata) { - console.log("## Metadata"); - console.log(JSON.stringify(data.metadata.getAll(), null, 2)); - console.log(); - } - }); + for (let i = 1; i <= numPages; i++) { + const pdfPage = await pdfDoc.getPage(i); + console.log(`# Page ${i}`); + const viewport = pdfPage.getViewport({ scale: 1.0 }); + console.log(`Size: ${viewport.width}x${viewport.height}`); + console.log(); + + const { items } = await pdfPage.getTextContent(); + // Content contains lots of information about the text layout and + // styles, but we need only strings at the moment + console.log("## Text Content"); + console.log(items.map(item => item.str).join(" ")); + console.log(); + // Release page resources. + pdfPage.cleanup(); + } - const loadPage = function (pageNum) { - return doc.getPage(pageNum).then(function (page) { - console.log("# Page " + pageNum); - const viewport = page.getViewport({ scale: 1.0 }); - console.log("Size: " + viewport.width + "x" + viewport.height); - console.log(); - return page - .getTextContent() - .then(function (content) { - // Content contains lots of information about the text layout and - // styles, but we need only strings at the moment - const strings = content.items.map(function (item) { - return item.str; - }); - console.log("## Text Content"); - console.log(strings.join(" ")); - // Release page resources. - page.cleanup(); - }) - .then(function () { - console.log(); - }); - }); - }; - // Loading of the first page will wait on metadata and subsequent loadings - // will wait on the previous pages. - for (let i = 1; i <= numPages; i++) { - lastPromise = lastPromise.then(loadPage.bind(null, i)); - } - return lastPromise; - }) - .then( - function () { - console.log("# End of Document"); - }, - function (err) { - console.error("Error: " + err); - } - ); + await loadingTask.destroy(); + console.log("# End of Document"); +} catch (ex) { + console.error(`Error: ${ex}`); +} diff --git a/src/core/catalog.js b/src/core/catalog.js index 028e1b5bfbb01..a615317d4303b 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -918,13 +918,9 @@ class Catalog { case "A": case "a": const LIMIT = 26; // Use only the characters A-Z, or a-z. - const A_UPPER_CASE = 0x41, - A_LOWER_CASE = 0x61; - - const baseCharCode = style === "a" ? A_LOWER_CASE : A_UPPER_CASE; const letterIndex = currentIndex - 1; const character = String.fromCharCode( - baseCharCode + (letterIndex % LIMIT) + style.charCodeAt(0) + (letterIndex % LIMIT) ); currentLabel = character.repeat(Math.floor(letterIndex / LIMIT) + 1); break; diff --git a/src/core/document.js b/src/core/document.js index 7e1adf0a68316..53a238cabb424 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -327,44 +327,45 @@ class Page { async #replaceIdByRef(annotations, deletedAnnotations, existingAnnotations) { const promises = []; for (const annotation of annotations) { - if (annotation.id) { - const ref = Ref.fromString(annotation.id); - if (!ref) { - warn(`A non-linked annotation cannot be modified: ${annotation.id}`); - continue; - } - if (annotation.deleted) { - deletedAnnotations.put(ref, ref); - if (annotation.popupRef) { - const popupRef = Ref.fromString(annotation.popupRef); - if (popupRef) { - deletedAnnotations.put(popupRef, popupRef); - } - } - continue; - } - if (annotation.popup?.deleted) { + if (!annotation.id) { + continue; + } + const ref = Ref.fromString(annotation.id); + if (!ref) { + warn(`A non-linked annotation cannot be modified: ${annotation.id}`); + continue; + } + if (annotation.deleted) { + deletedAnnotations.put(ref); + if (annotation.popupRef) { const popupRef = Ref.fromString(annotation.popupRef); if (popupRef) { - deletedAnnotations.put(popupRef, popupRef); + deletedAnnotations.put(popupRef); } } - existingAnnotations?.put(ref); - annotation.ref = ref; - promises.push( - this.xref.fetchAsync(ref).then( - obj => { - if (obj instanceof Dict) { - annotation.oldAnnotation = obj.clone(); - } - }, - () => { - warn(`Cannot fetch \`oldAnnotation\` for: ${ref}.`); - } - ) - ); - delete annotation.id; + continue; + } + if (annotation.popup?.deleted) { + const popupRef = Ref.fromString(annotation.popupRef); + if (popupRef) { + deletedAnnotations.put(popupRef); + } } + existingAnnotations?.put(ref); + annotation.ref = ref; + promises.push( + this.xref.fetchAsync(ref).then( + obj => { + if (obj instanceof Dict) { + annotation.oldAnnotation = obj.clone(); + } + }, + () => { + warn(`Cannot fetch \`oldAnnotation\` for: ${ref}.`); + } + ) + ); + delete annotation.id; } await Promise.all(promises); } @@ -375,7 +376,7 @@ class Page { } const partialEvaluator = this.#createPartialEvaluator(handler); - const deletedAnnotations = new RefMap(); + const deletedAnnotations = new RefSet(); const existingAnnotations = new RefSet(); await this.#replaceIdByRef( annotations, diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 96e2d5d81a570..aa35caa8f9f67 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -885,6 +885,16 @@ class PartialEvaluator { } } + #createTransferMap(fn) { + const transferFn = this._pdfFunctionFactory.create(fn), + tmp = new Float32Array(1); + return Uint8Array.from({ length: 256 }, (_, i) => { + tmp[0] = i / 255; + transferFn(tmp, 0, tmp, 0); + return (tmp[0] * 255) | 0; + }); + } + handleSMask( smask, resources, @@ -904,15 +914,7 @@ class PartialEvaluator { // we will build a map of integer values in range 0..255 to be fast. const transferObj = smask.get("TR"); if (isPDFFunction(transferObj)) { - const transferFn = this._pdfFunctionFactory.create(transferObj); - const transferMap = new Uint8Array(256); - const tmp = new Float32Array(1); - for (let i = 0; i < 256; i++) { - tmp[0] = i / 255; - transferFn(tmp, 0, tmp, 0); - transferMap[i] = (tmp[0] * 255) | 0; - } - smaskOptions.transferMap = transferMap; + smaskOptions.transferMap = this.#createTransferMap(transferObj); } return this.buildFormXObject( @@ -930,12 +932,9 @@ class PartialEvaluator { handleTransferFunction(tr) { let transferArray; if (Array.isArray(tr)) { - transferArray = tr; - if (tr.length > 1 && tr.every(map => map === tr[0])) { - // All entries in the array are the same, so we can just use one of - // them. - transferArray = [tr[0]]; - } + // If all entries in the array are the same, we can just use one of them. + transferArray = + tr.length > 1 && tr.every(map => map === tr[0]) ? [tr[0]] : tr; } else if (isPDFFunction(tr)) { transferArray = [tr]; } else { @@ -955,16 +954,7 @@ class PartialEvaluator { } else if (!isPDFFunction(transferObj)) { return null; // Not a valid transfer function object. } - - const transferFn = this._pdfFunctionFactory.create(transferObj); - const transferMap = new Uint8Array(256), - tmp = new Float32Array(1); - for (let j = 0; j < 256; j++) { - tmp[0] = j / 255; - transferFn(tmp, 0, tmp, 0); - transferMap[j] = (tmp[0] * 255) | 0; - } - transferMaps.push(transferMap); + transferMaps.push(this.#createTransferMap(transferObj)); numEffectfulFns++; } diff --git a/src/core/primitives.js b/src/core/primitives.js index 015d244030d2f..df2f3357c44f1 100644 --- a/src/core/primitives.js +++ b/src/core/primitives.js @@ -89,7 +89,7 @@ class Dict { return this.#map.size; } - #getValue(isAsync, key1, key2, key3) { + #getValue(isAsync, key1, key2) { let value = this.#map.get(key1); if (value === undefined && key2 !== undefined) { if ( @@ -99,15 +99,6 @@ class Dict { unreachable("Dict.#getValue: Expected keys to be ordered by length."); } value = this.#map.get(key2); - if (value === undefined && key3 !== undefined) { - if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) && - key3.length < key2.length - ) { - unreachable("Dict.#getValue: Expected keys to be ordered by length."); - } - value = this.#map.get(key3); - } } if (value instanceof Ref && this.xref) { return isAsync @@ -118,18 +109,18 @@ class Dict { } // Automatically dereferences Ref objects. - get(key1, key2, key3) { - return this.#getValue(/* isAsync = */ false, key1, key2, key3); + get(key1, key2) { + return this.#getValue(/* isAsync = */ false, key1, key2); } // Same as get(), but returns a promise and uses fetchIfRefAsync(). - async getAsync(key1, key2, key3) { - return this.#getValue(/* isAsync = */ true, key1, key2, key3); + async getAsync(key1, key2) { + return this.#getValue(/* isAsync = */ true, key1, key2); } // Same as get(), but dereferences all elements if the result is an Array. - getArray(key1, key2, key3) { - let value = this.#getValue(/* isAsync = */ false, key1, key2, key3); + getArray(key1, key2) { + let value = this.#getValue(/* isAsync = */ false, key1, key2); if (Array.isArray(value)) { value = value.slice(); // Ensure that we don't modify the Dict data. diff --git a/test/unit/primitives_spec.js b/test/unit/primitives_spec.js index fb31e53f08da1..096c7bd7e6bd9 100644 --- a/test/unit/primitives_spec.js +++ b/test/unit/primitives_spec.js @@ -98,14 +98,13 @@ describe("primitives", function () { expect(dict.get()).toBeUndefined(); expect(dict.get("Prev")).toBeUndefined(); expect(dict.get("D", "Decode")).toBeUndefined(); - expect(dict.get("FontFile", "FontFile2", "FontFile3")).toBeUndefined(); + expect(dict.get("FontFile", "FontFile2")).toBeUndefined(); }; let emptyDict, dictWithSizeKey, dictWithManyKeys; const storedSize = 42; const testFontFile = "file1"; const testFontFile2 = "file2"; - const testFontFile3 = "file3"; beforeAll(function () { emptyDict = new Dict(); @@ -116,7 +115,6 @@ describe("primitives", function () { dictWithManyKeys = new Dict(); dictWithManyKeys.set("FontFile", testFontFile); dictWithManyKeys.set("FontFile2", testFontFile2); - dictWithManyKeys.set("FontFile3", testFontFile3); }); afterAll(function () { @@ -153,7 +151,6 @@ describe("primitives", function () { expect(dictWithSizeKey.get("Size")).toEqual(storedSize); expect(dictWithSizeKey.get("Prev", "Size")).toEqual(storedSize); - expect(dictWithSizeKey.get("Prev", "Root", "Size")).toEqual(storedSize); }); it("should return invalid values for unknown keys when Size key is stored", function () { @@ -186,21 +183,16 @@ describe("primitives", function () { it("should return correct values for multiple stored keys", function () { expect(dictWithManyKeys.has("FontFile")).toBeTrue(); expect(dictWithManyKeys.has("FontFile2")).toBeTrue(); - expect(dictWithManyKeys.has("FontFile3")).toBeTrue(); - expect(dictWithManyKeys.get("FontFile3")).toEqual(testFontFile3); - expect(dictWithManyKeys.get("FontFile2", "FontFile3")).toEqual( - testFontFile2 + expect(dictWithManyKeys.get("FontFile", "FontFile2")).toEqual( + testFontFile ); - expect( - dictWithManyKeys.get("FontFile", "FontFile2", "FontFile3") - ).toEqual(testFontFile); }); it("should asynchronously fetch unknown keys", async function () { const keyPromises = [ dictWithManyKeys.getAsync("Size"), - dictWithSizeKey.getAsync("FontFile", "FontFile2", "FontFile3"), + dictWithSizeKey.getAsync("FontFile", "FontFile2"), ]; const values = await Promise.all(keyPromises); @@ -210,22 +202,19 @@ describe("primitives", function () { it("should asynchronously fetch correct values for multiple stored keys", async function () { const keyPromises = [ - dictWithManyKeys.getAsync("FontFile3"), - dictWithManyKeys.getAsync("FontFile2", "FontFile3"), - dictWithManyKeys.getAsync("FontFile", "FontFile2", "FontFile3"), + dictWithManyKeys.getAsync("FontFile2"), + dictWithManyKeys.getAsync("FontFile", "FontFile2"), ]; const values = await Promise.all(keyPromises); - expect(values[0]).toEqual(testFontFile3); - expect(values[1]).toEqual(testFontFile2); - expect(values[2]).toEqual(testFontFile); + expect(values[0]).toEqual(testFontFile2); + expect(values[1]).toEqual(testFontFile); }); it("should iterate through each stored key", function () { expect([...dictWithManyKeys]).toEqual([ ["FontFile", testFontFile], ["FontFile2", testFontFile2], - ["FontFile3", testFontFile3], ]); }); @@ -236,15 +225,9 @@ describe("primitives", function () { fontDict.set("FontFile", fontRef); expect(fontDict.getRaw("FontFile")).toEqual(fontRef); - expect(fontDict.get("FontFile", "FontFile2", "FontFile3")).toEqual( - testFontFile - ); + expect(fontDict.get("FontFile", "FontFile2")).toEqual(testFontFile); - const value = await fontDict.getAsync( - "FontFile", - "FontFile2", - "FontFile3" - ); + const value = await fontDict.getAsync("FontFile", "FontFile2"); expect(value).toEqual(testFontFile); }); @@ -275,7 +258,7 @@ describe("primitives", function () { }); it("should get all key names", function () { - const expectedKeys = ["FontFile", "FontFile2", "FontFile3"]; + const expectedKeys = ["FontFile", "FontFile2"]; const keys = [...dictWithManyKeys.getKeys()]; expect(keys.sort()).toEqual(expectedKeys); @@ -283,7 +266,7 @@ describe("primitives", function () { it("should get all raw values", function () { // Test direct objects: - const expectedRawValues1 = [testFontFile, testFontFile2, testFontFile3]; + const expectedRawValues1 = [testFontFile, testFontFile2]; const rawValues1 = [...dictWithManyKeys.getRawValues()]; expect(rawValues1.sort()).toEqual(expectedRawValues1); @@ -314,7 +297,6 @@ describe("primitives", function () { const expectedRawEntries = [ ["FontFile", testFontFile], ["FontFile2", testFontFile2], - ["FontFile3", testFontFile3], ]; const rawEntries = Array.from(dictWithManyKeys.getRawEntries()); expect(rawEntries.sort()).toEqual(expectedRawEntries); @@ -329,7 +311,7 @@ describe("primitives", function () { }); it("should correctly merge dictionaries", function () { - const expectedKeys = ["FontFile", "FontFile2", "FontFile3", "Size"]; + const expectedKeys = ["FontFile", "FontFile2", "Size"]; const fontFileDict = new Dict(); fontFileDict.set("FontFile", "Type1 font file");