Support destinations in NameTrees with encoded keys (issue 14847)

Initially I considered updating the `NameOrNumberTree`-implementation to handle encoded keys, however that quickly became somewhat messy (especially in the `NameOrNumberTree.get`-method) since only NameTrees using string-keys.
Hence the easiest solution, as far as I'm concerned, was thus to just update the `Catalog.destinations`-getter instead. Please note that in the referenced PDF document the `Catalog.destination`-method will thus fallback to fetch all destinations, which should be fine since this is the very first case of encoded keys that we've seen.

Also changes the `NameOrNumberTree.getAll`-method to prevent a possible run-time error, although we've so far not seen such a case, for any non-Array Kids-entries found in a NameTree/NumberTree.

Finally, to improve overall consistency and to hopefully prevent future bugs, the patch also updates a couple of other `NameTree` call-sites to correctly handle encoded keys. (Note that the `Catalog.attachments`-getter was already doing this.)
This commit is contained in:
Jonas Jenwald 2022-04-27 10:34:31 +02:00
parent 981cd5bbfc
commit 71370d012b
5 changed files with 24 additions and 6 deletions

View File

@ -566,7 +566,7 @@ class Catalog {
for (const [key, value] of obj.getAll()) {
const dest = fetchDestination(value);
if (dest) {
dests[key] = dest;
dests[stringToPDFString(key)] = dest;
}
}
} else if (obj instanceof Dict) {
@ -954,7 +954,7 @@ class Catalog {
if (!xfaImages) {
xfaImages = new Dict(this.xref);
}
xfaImages.set(key, value);
xfaImages.set(stringToPDFString(key), value);
}
}
return shadow(this, "xfaImages", xfaImages);
@ -988,7 +988,7 @@ class Catalog {
if (obj instanceof Dict && obj.has("JavaScript")) {
const nameTree = new NameTree(obj.getRaw("JavaScript"), this.xref);
for (const [key, value] of nameTree.getAll()) {
appendIfJavaScriptDict(key, value);
appendIfJavaScriptDict(stringToPDFString(key), value);
}
}
// Append OpenAction "JavaScript" actions, if any, to the JavaScript map.

View File

@ -48,8 +48,10 @@ class NameOrNumberTree {
}
if (obj.has("Kids")) {
const kids = obj.get("Kids");
for (let i = 0, ii = kids.length; i < ii; i++) {
const kid = kids[i];
if (!Array.isArray(kids)) {
continue;
}
for (const kid of kids) {
if (processed.has(kid)) {
throw new FormatError(`Duplicate entry in "${this._type}" tree.`);
}
@ -103,7 +105,7 @@ class NameOrNumberTree {
} else if (key > xref.fetchIfRef(limits[1])) {
l = m + 1;
} else {
kidsOrEntries = xref.fetchIfRef(kids[m]);
kidsOrEntries = kid;
break;
}
}

View File

@ -53,6 +53,7 @@
!issue7544.pdf
!issue7507.pdf
!issue6931_reduced.pdf
!issue14847.pdf
!doc_actions.pdf
!issue7580.pdf
!issue7598.pdf

BIN
test/pdfs/issue14847.pdf Normal file

Binary file not shown.

View File

@ -1004,6 +1004,21 @@ describe("api", function () {
await loadingTask.destroy();
});
it("gets a destination, from /Names (NameTree) dictionary with keys using PDFDocEncoding (issue 14847)", async function () {
const loadingTask = getDocument(buildGetDocumentParams("issue14847.pdf"));
const pdfDoc = await loadingTask.promise;
const destination = await pdfDoc.getDestination("index");
expect(destination).toEqual([
{ num: 10, gen: 0 },
{ name: "XYZ" },
85.039,
728.504,
null,
]);
await loadingTask.destroy();
});
it("gets non-string destination", async function () {
let numberPromise = pdfDocument.getDestination(4.3);
let booleanPromise = pdfDocument.getDestination(true);