Skip to content

Commit eddd70a

Browse files
Merge pull request #21597 from Snuffleupagus/destinations-Map
[api-minor] Convert `getDestinations` to return data in a Map
2 parents b832748 + 6fbd222 commit eddd70a

4 files changed

Lines changed: 77 additions & 63 deletions

File tree

src/core/catalog.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -763,24 +763,28 @@ class Catalog {
763763
}
764764

765765
get destinations() {
766-
const rawDests = this.#readDests(),
767-
dests = Object.create(null);
768-
for (const obj of rawDests) {
766+
const dests = new Map();
767+
768+
for (const obj of this.#readDests()) {
769769
if (obj instanceof NameTree) {
770770
for (const [key, value] of obj.getAll()) {
771771
const dest = fetchDest(value);
772772
if (dest) {
773-
dests[stringToPDFString(key, /* keepEscapeSequence = */ true)] =
774-
dest;
773+
dests.set(
774+
stringToPDFString(key, /* keepEscapeSequence = */ true),
775+
dest
776+
);
775777
}
776778
}
777779
} else if (obj instanceof Dict) {
778780
for (const [key, value] of obj) {
779781
const dest = fetchDest(value);
780782
if (dest) {
781783
// Always let the NameTree take precedence.
782-
dests[stringToPDFString(key, /* keepEscapeSequence = */ true)] ||=
783-
dest;
784+
dests.getOrInsert(
785+
stringToPDFString(key, /* keepEscapeSequence = */ true),
786+
dest
787+
);
784788
}
785789
}
786790
}
@@ -791,11 +795,10 @@ class Catalog {
791795
getDestination(id) {
792796
// Avoid extra lookup/parsing when all destinations are already available.
793797
if (Object.hasOwn(this, "destinations")) {
794-
return this.destinations[id] ?? null;
798+
return this.destinations.get(id) ?? null;
795799
}
796800

797-
const rawDests = this.#readDests();
798-
for (const obj of rawDests) {
801+
for (const obj of this.#readDests()) {
799802
if (obj instanceof NameTree || obj instanceof Dict) {
800803
const dest = fetchDest(obj.get(id));
801804
if (dest) {
@@ -807,13 +810,7 @@ class Catalog {
807810
// Always fallback to checking all destinations, in order to support:
808811
// - PDF documents with out-of-order NameTrees (fixes issue 10272).
809812
// - Destination keys that use PDFDocEncoding (fixes issue 19835).
810-
if (rawDests.length) {
811-
const dest = this.destinations[id];
812-
if (dest) {
813-
return dest;
814-
}
815-
}
816-
return null;
813+
return this.destinations.get(id) ?? null;
817814
}
818815

819816
#readDests() {

src/core/editor/pdf_editor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,7 @@ class PDFEditor {
16241624
}
16251625
const { destinations, pagesMap } = documentData;
16261626
const newDestinations = (documentData.destinations = new Map());
1627-
for (const [key, dest] of Object.entries(destinations)) {
1627+
for (const [key, dest] of destinations) {
16281628
const pageRef = dest[0];
16291629
const pageData = pageRef instanceof Ref && pagesMap.get(pageRef);
16301630
if (!pageData) {

src/display/api.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ class PDFDocumentProxy {
775775
}
776776

777777
/**
778-
* @returns {Promise<Object<string, Array<any>>>} A promise that is resolved
778+
* @returns {Promise<Map<string, Array<any>>>} A promise that is resolved
779779
* with a mapping from named destinations to references.
780780
*
781781
* This can be slow for large documents. Use `getDestination` instead.
@@ -3085,9 +3085,7 @@ class WorkerTransport {
30853085
if (typeof id !== "string") {
30863086
return Promise.reject(new Error("Invalid destination request."));
30873087
}
3088-
return this.messageHandler.sendWithPromise("GetDestination", {
3089-
id,
3090-
});
3088+
return this.messageHandler.sendWithPromise("GetDestination", { id });
30913089
}
30923090

30933091
getPageLabels() {

test/unit/api_spec.js

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,9 +1467,11 @@ describe("api", function () {
14671467

14681468
it("gets destinations, from /Dests dictionary", async function () {
14691469
const destinations = await pdfDocument.getDestinations();
1470-
expect(destinations).toEqual({
1471-
chapter1: [{ gen: 0, num: 17 }, { name: "XYZ" }, 0, 841.89, null],
1472-
});
1470+
expect(destinations).toEqual(
1471+
new Map([
1472+
["chapter1", [{ gen: 0, num: 17 }, { name: "XYZ" }, 0, 841.89, null]],
1473+
])
1474+
);
14731475
});
14741476

14751477
it("gets a destination, from /Dests dictionary", async function () {
@@ -1494,10 +1496,12 @@ describe("api", function () {
14941496
const loadingTask = getDocument(buildGetDocumentParams("issue6204.pdf"));
14951497
const pdfDoc = await loadingTask.promise;
14961498
const destinations = await pdfDoc.getDestinations();
1497-
expect(destinations).toEqual({
1498-
"Page.1": [{ num: 1, gen: 0 }, { name: "XYZ" }, 0, 375, null],
1499-
"Page.2": [{ num: 6, gen: 0 }, { name: "XYZ" }, 0, 375, null],
1500-
});
1499+
expect(destinations).toEqual(
1500+
new Map([
1501+
["Page.1", [{ num: 1, gen: 0 }, { name: "XYZ" }, 0, 375, null]],
1502+
["Page.2", [{ num: 6, gen: 0 }, { name: "XYZ" }, 0, 375, null]],
1503+
])
1504+
);
15011505

15021506
await loadingTask.destroy();
15031507
});
@@ -1506,11 +1510,13 @@ describe("api", function () {
15061510
const loadingTask = getDocument(buildGetDocumentParams("issue19474.pdf"));
15071511
const pdfDoc = await loadingTask.promise;
15081512
const destinations = await pdfDoc.getDestinations();
1509-
expect(destinations).toEqual({
1510-
A: [{ num: 1, gen: 0 }, { name: "Fit" }],
1511-
B: [{ num: 4, gen: 0 }, { name: "Fit" }],
1512-
C: [{ num: 5, gen: 0 }, { name: "Fit" }],
1513-
});
1513+
expect(destinations).toEqual(
1514+
new Map([
1515+
["A", [{ num: 1, gen: 0 }, { name: "Fit" }]],
1516+
["B", [{ num: 4, gen: 0 }, { name: "Fit" }]],
1517+
["C", [{ num: 5, gen: 0 }, { name: "Fit" }]],
1518+
])
1519+
);
15141520

15151521
await loadingTask.destroy();
15161522
});
@@ -6418,7 +6424,10 @@ small scripts as well as for`);
64186424
expect(annotations.length).toEqual(2);
64196425
expect(annotations[0].dest).toEqual("foo");
64206426
expect(annotations[1].unsafeUrl).toEqual("other.pdf#target");
6421-
expect(Object.keys(await pdfDoc.getDestinations())).toEqual(["foo"]);
6427+
6428+
const destinations = await pdfDoc.getDestinations();
6429+
expect([...destinations.keys()]).toEqual(["foo"]);
6430+
64226431
await loadingTask.destroy();
64236432
});
64246433

@@ -6435,15 +6444,17 @@ small scripts as well as for`);
64356444
];
64366445
let loadingTask = getDocument({ data: assemblePdf(objects) });
64376446
let pdfDoc = await loadingTask.promise;
6438-
expect(Object.keys(await pdfDoc.getDestinations()))
6447+
let destinations = await pdfDoc.getDestinations();
6448+
expect([...destinations.keys()])
64396449
.withContext("before extraction")
64406450
.toEqual(["名"]);
64416451
const data = await pdfDoc.extractPages([{ document: null }]);
64426452
await loadingTask.destroy();
64436453

64446454
loadingTask = getDocument({ data });
64456455
pdfDoc = await loadingTask.promise;
6446-
expect(Object.keys(await pdfDoc.getDestinations()))
6456+
destinations = await pdfDoc.getDestinations();
6457+
expect([...destinations.keys()])
64476458
.withContext("after extraction")
64486459
.toEqual(["名"]);
64496460
await loadingTask.destroy();
@@ -6456,7 +6467,7 @@ small scripts as well as for`);
64566467
let pdfDoc = await loadingTask.promise;
64576468

64586469
let destinations = await pdfDoc.getDestinations();
6459-
expect(Object.keys(destinations).sort()).toEqual(["foo", "foo_p2"]);
6470+
expect([...destinations.keys()].sort()).toEqual(["foo", "foo_p2"]);
64606471

64616472
const data = await pdfDoc.extractPages([
64626473
{ document: null },
@@ -6468,7 +6479,7 @@ small scripts as well as for`);
64686479
pdfDoc = await loadingTask.promise;
64696480

64706481
destinations = await pdfDoc.getDestinations();
6471-
expect(Object.keys(destinations).sort()).toEqual([
6482+
expect([...destinations.keys()].sort()).toEqual([
64726483
"foo",
64736484
"foo_p2",
64746485
"foo_p2_1",
@@ -6489,10 +6500,12 @@ small scripts as well as for`);
64896500
let pdfDoc = await loadingTask.promise;
64906501
let pagesRef = await getPageRefs(pdfDoc);
64916502
let destinations = await pdfDoc.getDestinations();
6492-
expect(destinations).toEqual({
6493-
"Page.1": [pagesRef[0], { name: "XYZ" }, 0, 375, null],
6494-
"Page.2": [pagesRef[1], { name: "XYZ" }, 0, 375, null],
6495-
});
6503+
expect(destinations).toEqual(
6504+
new Map([
6505+
["Page.1", [pagesRef[0], { name: "XYZ" }, 0, 375, null]],
6506+
["Page.2", [pagesRef[1], { name: "XYZ" }, 0, 375, null]],
6507+
])
6508+
);
64966509

64976510
let data = await pdfDoc.extractPages([
64986511
{ document: null },
@@ -6507,12 +6520,14 @@ small scripts as well as for`);
65076520

65086521
pagesRef = await getPageRefs(pdfDoc);
65096522
destinations = await pdfDoc.getDestinations();
6510-
expect(destinations).toEqual({
6511-
"Page.1": [pagesRef[0], { name: "XYZ" }, 0, 375, null],
6512-
"Page.2": [pagesRef[1], { name: "XYZ" }, 0, 375, null],
6513-
"Page.1_p3": [pagesRef[2], { name: "XYZ" }, 0, 375, null],
6514-
"Page.2_p4": [pagesRef[3], { name: "XYZ" }, 0, 375, null],
6515-
});
6523+
expect(destinations).toEqual(
6524+
new Map([
6525+
["Page.1", [pagesRef[0], { name: "XYZ" }, 0, 375, null]],
6526+
["Page.2", [pagesRef[1], { name: "XYZ" }, 0, 375, null]],
6527+
["Page.1_p3", [pagesRef[2], { name: "XYZ" }, 0, 375, null]],
6528+
["Page.2_p4", [pagesRef[3], { name: "XYZ" }, 0, 375, null]],
6529+
])
6530+
);
65166531
const expectedDests = ["Page.2", "Page.1", "Page.2_p4", "Page.1_p3"];
65176532
for (let i = 1; i <= 4; i++) {
65186533
const pdfPage = await pdfDoc.getPage(i);
@@ -6534,16 +6549,18 @@ small scripts as well as for`);
65346549

65356550
pagesRef = await getPageRefs(pdfDoc);
65366551
destinations = await pdfDoc.getDestinations();
6537-
expect(destinations).toEqual({
6538-
"Page.1": [pagesRef[0], { name: "XYZ" }, 0, 375, null],
6539-
"Page.2": [pagesRef[1], { name: "XYZ" }, 0, 375, null],
6540-
"Page.1_p3": [pagesRef[2], { name: "XYZ" }, 0, 375, null],
6541-
"Page.2_p4": [pagesRef[3], { name: "XYZ" }, 0, 375, null],
6542-
"Page.1_p5": [pagesRef[4], { name: "XYZ" }, 0, 375, null],
6543-
"Page.2_p6": [pagesRef[5], { name: "XYZ" }, 0, 375, null],
6544-
"Page.1_p3_p7": [pagesRef[6], { name: "XYZ" }, 0, 375, null],
6545-
"Page.2_p4_p8": [pagesRef[7], { name: "XYZ" }, 0, 375, null],
6546-
});
6552+
expect(destinations).toEqual(
6553+
new Map([
6554+
["Page.1", [pagesRef[0], { name: "XYZ" }, 0, 375, null]],
6555+
["Page.2", [pagesRef[1], { name: "XYZ" }, 0, 375, null]],
6556+
["Page.1_p3", [pagesRef[2], { name: "XYZ" }, 0, 375, null]],
6557+
["Page.2_p4", [pagesRef[3], { name: "XYZ" }, 0, 375, null]],
6558+
["Page.1_p5", [pagesRef[4], { name: "XYZ" }, 0, 375, null]],
6559+
["Page.2_p6", [pagesRef[5], { name: "XYZ" }, 0, 375, null]],
6560+
["Page.1_p3_p7", [pagesRef[6], { name: "XYZ" }, 0, 375, null]],
6561+
["Page.2_p4_p8", [pagesRef[7], { name: "XYZ" }, 0, 375, null]],
6562+
])
6563+
);
65476564
expectedDests.push(
65486565
"Page.2_p6",
65496566
"Page.1_p5",
@@ -6575,10 +6592,12 @@ small scripts as well as for`);
65756592

65766593
const pagesRef = await getPageRefs(pdfDoc);
65776594
const destinations = await pdfDoc.getDestinations();
6578-
expect(destinations).toEqual({
6579-
"Page.1": [pagesRef[0], { name: "XYZ" }, 0, 375, null],
6580-
"Page.2": [pagesRef[1], { name: "XYZ" }, 0, 375, null],
6581-
});
6595+
expect(destinations).toEqual(
6596+
new Map([
6597+
["Page.1", [pagesRef[0], { name: "XYZ" }, 0, 375, null]],
6598+
["Page.2", [pagesRef[1], { name: "XYZ" }, 0, 375, null]],
6599+
])
6600+
);
65826601
const pdfPage = await pdfDoc.getPage(3);
65836602
const annots = await pdfPage.getAnnotations();
65846603
expect(annots.length).toEqual(0);

0 commit comments

Comments
 (0)