diff --git a/src/core/editor/pdf_editor.js b/src/core/editor/pdf_editor.js index 8a726a496..25073ab14 100644 --- a/src/core/editor/pdf_editor.js +++ b/src/core/editor/pdf_editor.js @@ -1147,6 +1147,17 @@ class PDFEditor { */ #findDuplicateNamedDestinations() { const { namedDestinations } = this; + const getUniqueDestinationName = name => { + if (!namedDestinations.has(name)) { + return name; + } + for (let i = 1; ; i++) { + const dedupedName = `${name}_${i}`; + if (!namedDestinations.has(dedupedName)) { + return dedupedName; + } + } + }; for (let i = 0, ii = this.oldPages.length; i < ii; i++) { const page = this.oldPages[i]; const { @@ -1179,7 +1190,7 @@ class PDFEditor { continue; } // Create a new unique named destination. - const newName = `${pointingDest}_p${i + 1}`; + const newName = getUniqueDestinationName(`${pointingDest}_p${i + 1}`); dedupNamedDestinations.set(pointingDest, newName); namedDestinations.set(newName, dest); } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index c00337faa..5f8d1439d 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -522,6 +522,7 @@ !issue14048.pdf !issue11656.pdf !annotation-fileattachment.pdf +!named_dest_collision_for_editor.pdf !annotation-text-widget.pdf !issue7454.pdf !issue15443.pdf diff --git a/test/pdfs/named_dest_collision_for_editor.pdf b/test/pdfs/named_dest_collision_for_editor.pdf new file mode 100644 index 000000000..19bc70a74 --- /dev/null +++ b/test/pdfs/named_dest_collision_for_editor.pdf @@ -0,0 +1,40 @@ +%PDF-1.7 +1 0 obj +<< /Type /Catalog /Pages 2 0 R /Names << /Dests 6 0 R >> >> +endobj +2 0 obj +<< /Type /Pages /Kids [3 0 R] /Count 1 >> +endobj +3 0 obj +<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Resources << >> /Contents 7 0 R /Annots [4 0 R 5 0 R] >> +endobj +4 0 obj +<< /Type /Annot /Subtype /Link /Rect [10 150 90 170] /Border [0 0 0] /Dest (foo) >> +endobj +5 0 obj +<< /Type /Annot /Subtype /Link /Rect [10 120 90 140] /Border [0 0 0] /Dest (foo_p2) >> +endobj +6 0 obj +<< /Names [(foo) [3 0 R /Fit] (foo_p2) [3 0 R /Fit]] >> +endobj +7 0 obj +<< /Length 0 >> +stream + +endstream +endobj +xref +0 8 +0000000000 65535 f +0000000009 00000 n +0000000081 00000 n +0000000138 00000 n +0000000264 00000 n +0000000361 00000 n +0000000461 00000 n +0000000532 00000 n +trailer +<< /Size 8 /Root 1 0 R >> +startxref +581 +%%EOF diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index c7bdfe23e..30e990efc 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -5804,6 +5804,41 @@ small scripts as well as for`); }); describe("Named destinations", function () { + it("keeps colliding deduplicated destination names unique", async function () { + let loadingTask = getDocument( + buildGetDocumentParams("named_dest_collision_for_editor.pdf") + ); + let pdfDoc = await loadingTask.promise; + + let destinations = await pdfDoc.getDestinations(); + expect(Object.keys(destinations).sort()).toEqual(["foo", "foo_p2"]); + + const data = await pdfDoc.extractPages([ + { document: null }, + { document: null }, + ]); + await loadingTask.destroy(); + + loadingTask = getDocument(data); + pdfDoc = await loadingTask.promise; + + destinations = await pdfDoc.getDestinations(); + expect(Object.keys(destinations).sort()).toEqual([ + "foo", + "foo_p2", + "foo_p2_1", + "foo_p2_p2", + ]); + + const secondPage = await pdfDoc.getPage(2); + const annots = await secondPage.getAnnotations(); + expect(annots.length).toEqual(2); + expect(annots[0].dest).toEqual("foo_p2_1"); + expect(annots[1].dest).toEqual("foo_p2_p2"); + + await loadingTask.destroy(); + }); + it("extract page and check destinations", async function () { let loadingTask = getDocument(buildGetDocumentParams("issue6204.pdf")); let pdfDoc = await loadingTask.promise;