Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of named destinations in out-of-order NameTrees (PR 10274 follow-up) #13415

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 29 additions & 7 deletions src/core/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ import { MetadataParser } from "./metadata_parser.js";
import { StructTreeRoot } from "./struct_tree.js";

function fetchDestination(dest) {
return isDict(dest) ? dest.get("D") : dest;
if (dest instanceof Dict) {
dest = dest.get("D");
}
return Array.isArray(dest) ? dest : null;
}

class Catalog {
Expand Down Expand Up @@ -515,22 +518,41 @@ class Catalog {
dests = Object.create(null);
if (obj instanceof NameTree) {
for (const [key, value] of obj.getAll()) {
dests[key] = fetchDestination(value);
const dest = fetchDestination(value);
if (dest) {
dests[key] = dest;
}
}
} else if (obj instanceof Dict) {
obj.forEach(function (key, value) {
if (value) {
dests[key] = fetchDestination(value);
const dest = fetchDestination(value);
if (dest) {
dests[key] = dest;
}
});
}
return shadow(this, "destinations", dests);
}

getDestination(destinationId) {
getDestination(id) {
const obj = this._readDests();
if (obj instanceof NameTree || obj instanceof Dict) {
return fetchDestination(obj.get(destinationId) || null);
if (obj instanceof NameTree) {
const dest = fetchDestination(obj.get(id));
if (dest) {
return dest;
}
// Fallback to checking the *entire* NameTree, in an attempt to handle
// corrupt PDF documents with out-of-order NameTrees (fixes issue 10272).
const allDest = this.destinations[id];
if (allDest) {
warn(`Found "${id}" at an incorrect position in the NameTree.`);
return allDest;
}
} else if (obj instanceof Dict) {
const dest = fetchDestination(obj.get(id));
if (dest) {
return dest;
}
}
return null;
}
Expand Down
19 changes: 1 addition & 18 deletions src/core/name_number_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* limitations under the License.
*/

import { FormatError, info, unreachable, warn } from "../shared/util.js";
import { FormatError, unreachable, warn } from "../shared/util.js";
import { isDict, RefSet } from "./primitives.js";

/**
Expand Down Expand Up @@ -133,23 +133,6 @@ class NameOrNumberTree {
return xref.fetchIfRef(entries[m + 1]);
}
}

// Fallback to an exhaustive search, in an attempt to handle corrupt
// PDF files where keys are not correctly ordered (fixes issue 10272).
info(
`Falling back to an exhaustive search, for key "${key}", ` +
`in "${this._type}" tree.`
);
for (let m = 0, mm = entries.length; m < mm; m += 2) {
const currentKey = xref.fetchIfRef(entries[m]);
if (currentKey === key) {
warn(
`The "${key}" key was found at an incorrect, ` +
`i.e. out-of-order, position in "${this._type}" tree.`
);
return xref.fetchIfRef(entries[m + 1]);
}
}
}
return null;
}
Expand Down
5 changes: 3 additions & 2 deletions src/display/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,9 @@ class PDFDocumentProxy {

/**
* @param {string} id - The named destination to get.
* @returns {Promise<Array<any>>} A promise that is resolved with all
* information of the given named destination.
* @returns {Promise<Array<any> | null>} A promise that is resolved with all
* information of the given named destination, or `null` when the named
* destination is not present in the PDF file.
*/
getDestination(id) {
return this._transport.getDestination(id);
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/issue10272.pdf.link
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://github.com/mozilla/pdf.js/files/2598691/ATS_TEST_PVC_2_1.pdf
6 changes: 6 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,12 @@
"link": true,
"type": "load"
},
{ "id": "issue10272",
"file": "pdfs/issue10272.pdf",
"md5": "bf3b2f74c6878d38a70dc0825f1b9a02",
"link": true,
"type": "other"
},
{ "id": "issue10388",
"file": "pdfs/issue10388_reduced.pdf",
"md5": "62a6b2adbea1535432bd94a3516e2d4c",
Expand Down
18 changes: 18 additions & 0 deletions test/unit/api_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,24 @@ describe("api", function () {
await loadingTask.destroy();
});

it("gets a destination, from out-of-order /Names (NameTree) dictionary (issue 10272)", async function () {
if (isNodeJS) {
pending("Linked test-cases are not supported in Node.js.");
}
const loadingTask = getDocument(buildGetDocumentParams("issue10272.pdf"));
const pdfDoc = await loadingTask.promise;
const destination = await pdfDoc.getDestination("link_1");
expect(destination).toEqual([
{ num: 17, gen: 0 },
{ name: "XYZ" },
69,
125,
0,
]);

await loadingTask.destroy();
});

it("gets non-string destination", async function () {
let numberPromise = pdfDocument.getDestination(4.3);
let booleanPromise = pdfDocument.getDestination(true);
Expand Down