Skip to content

Commit d1d9b90

Browse files
Merge pull request #13415 from Snuffleupagus/getDestination-out-of-order
Improve handling of named destinations in out-of-order NameTrees (PR 10274 follow-up)
2 parents 5fdb126 + 8d56893 commit d1d9b90

File tree

6 files changed

+58
-27
lines changed

6 files changed

+58
-27
lines changed

src/core/catalog.js

+29-7
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ import { MetadataParser } from "./metadata_parser.js";
5353
import { StructTreeRoot } from "./struct_tree.js";
5454

5555
function fetchDestination(dest) {
56-
return isDict(dest) ? dest.get("D") : dest;
56+
if (dest instanceof Dict) {
57+
dest = dest.get("D");
58+
}
59+
return Array.isArray(dest) ? dest : null;
5760
}
5861

5962
class Catalog {
@@ -515,22 +518,41 @@ class Catalog {
515518
dests = Object.create(null);
516519
if (obj instanceof NameTree) {
517520
for (const [key, value] of obj.getAll()) {
518-
dests[key] = fetchDestination(value);
521+
const dest = fetchDestination(value);
522+
if (dest) {
523+
dests[key] = dest;
524+
}
519525
}
520526
} else if (obj instanceof Dict) {
521527
obj.forEach(function (key, value) {
522-
if (value) {
523-
dests[key] = fetchDestination(value);
528+
const dest = fetchDestination(value);
529+
if (dest) {
530+
dests[key] = dest;
524531
}
525532
});
526533
}
527534
return shadow(this, "destinations", dests);
528535
}
529536

530-
getDestination(destinationId) {
537+
getDestination(id) {
531538
const obj = this._readDests();
532-
if (obj instanceof NameTree || obj instanceof Dict) {
533-
return fetchDestination(obj.get(destinationId) || null);
539+
if (obj instanceof NameTree) {
540+
const dest = fetchDestination(obj.get(id));
541+
if (dest) {
542+
return dest;
543+
}
544+
// Fallback to checking the *entire* NameTree, in an attempt to handle
545+
// corrupt PDF documents with out-of-order NameTrees (fixes issue 10272).
546+
const allDest = this.destinations[id];
547+
if (allDest) {
548+
warn(`Found "${id}" at an incorrect position in the NameTree.`);
549+
return allDest;
550+
}
551+
} else if (obj instanceof Dict) {
552+
const dest = fetchDestination(obj.get(id));
553+
if (dest) {
554+
return dest;
555+
}
534556
}
535557
return null;
536558
}

src/core/name_number_tree.js

+1-18
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
import { FormatError, info, unreachable, warn } from "../shared/util.js";
16+
import { FormatError, unreachable, warn } from "../shared/util.js";
1717
import { isDict, RefSet } from "./primitives.js";
1818

1919
/**
@@ -133,23 +133,6 @@ class NameOrNumberTree {
133133
return xref.fetchIfRef(entries[m + 1]);
134134
}
135135
}
136-
137-
// Fallback to an exhaustive search, in an attempt to handle corrupt
138-
// PDF files where keys are not correctly ordered (fixes issue 10272).
139-
info(
140-
`Falling back to an exhaustive search, for key "${key}", ` +
141-
`in "${this._type}" tree.`
142-
);
143-
for (let m = 0, mm = entries.length; m < mm; m += 2) {
144-
const currentKey = xref.fetchIfRef(entries[m]);
145-
if (currentKey === key) {
146-
warn(
147-
`The "${key}" key was found at an incorrect, ` +
148-
`i.e. out-of-order, position in "${this._type}" tree.`
149-
);
150-
return xref.fetchIfRef(entries[m + 1]);
151-
}
152-
}
153136
}
154137
return null;
155138
}

src/display/api.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -730,8 +730,9 @@ class PDFDocumentProxy {
730730

731731
/**
732732
* @param {string} id - The named destination to get.
733-
* @returns {Promise<Array<any>>} A promise that is resolved with all
734-
* information of the given named destination.
733+
* @returns {Promise<Array<any> | null>} A promise that is resolved with all
734+
* information of the given named destination, or `null` when the named
735+
* destination is not present in the PDF file.
735736
*/
736737
getDestination(id) {
737738
return this._transport.getDestination(id);

test/pdfs/issue10272.pdf.link

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://github.com/mozilla/pdf.js/files/2598691/ATS_TEST_PVC_2_1.pdf

test/test_manifest.json

+6
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,12 @@
917917
"link": true,
918918
"type": "load"
919919
},
920+
{ "id": "issue10272",
921+
"file": "pdfs/issue10272.pdf",
922+
"md5": "bf3b2f74c6878d38a70dc0825f1b9a02",
923+
"link": true,
924+
"type": "other"
925+
},
920926
{ "id": "issue10388",
921927
"file": "pdfs/issue10388_reduced.pdf",
922928
"md5": "62a6b2adbea1535432bd94a3516e2d4c",

test/unit/api_spec.js

+18
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,24 @@ describe("api", function () {
669669
await loadingTask.destroy();
670670
});
671671

672+
it("gets a destination, from out-of-order /Names (NameTree) dictionary (issue 10272)", async function () {
673+
if (isNodeJS) {
674+
pending("Linked test-cases are not supported in Node.js.");
675+
}
676+
const loadingTask = getDocument(buildGetDocumentParams("issue10272.pdf"));
677+
const pdfDoc = await loadingTask.promise;
678+
const destination = await pdfDoc.getDestination("link_1");
679+
expect(destination).toEqual([
680+
{ num: 17, gen: 0 },
681+
{ name: "XYZ" },
682+
69,
683+
125,
684+
0,
685+
]);
686+
687+
await loadingTask.destroy();
688+
});
689+
672690
it("gets non-string destination", async function () {
673691
let numberPromise = pdfDocument.getDestination(4.3);
674692
let booleanPromise = pdfDocument.getDestination(true);

0 commit comments

Comments
 (0)