From 0bee641fed9f3e5c922af5bcf613f5351a31762f Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 19 Mar 2026 09:40:16 +0100 Subject: [PATCH] Avoid to use a used slot when looking for a new page position --- src/core/editor/pdf_editor.js | 7 ++++++- test/unit/api_spec.js | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/core/editor/pdf_editor.js b/src/core/editor/pdf_editor.js index 8a726a496..0ecb141b5 100644 --- a/src/core/editor/pdf_editor.js +++ b/src/core/editor/pdf_editor.js @@ -616,15 +616,20 @@ class PDFEditor { if (newIndex !== -1) { newPageIndex = newIndex++; } else { + // Find the first available index in the newPages array. + // This is needed when the pageIndices option is used since the + // pages can be added in any order. for ( newPageIndex = 0; - this.oldPages[newPageIndex] === undefined; + this.oldPages[newPageIndex] !== undefined; newPageIndex++ ) { /* empty */ } } } + // Reserve the slot immediately because the page fetch is async. + this.oldPages[newPageIndex] = null; promises.push( document.getPage(i).then(page => { this.oldPages[newPageIndex] = new PageData(page, documentData); diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index c7bdfe23e..02b8b960c 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -6222,6 +6222,44 @@ small scripts as well as for`); await loadingTask.destroy(); }); + + it("fills missing pageIndices with the first free slots", async function () { + let loadingTask = getDocument( + buildGetDocumentParams("tracemonkey.pdf") + ); + let pdfDoc = await loadingTask.promise; + const data = await pdfDoc.extractPages([ + { document: null, includePages: [1, 3, 5], pageIndices: [1] }, + ]); + await loadingTask.destroy(); + + loadingTask = getDocument(data); + pdfDoc = await loadingTask.promise; + + expect(pdfDoc.numPages).toEqual(3); + + // Page 4 in the original document should occupy the first free slot. + let pdfPage = await pdfDoc.getPage(1); + let { items: textItems } = await pdfPage.getTextContent(); + expect(mergeText(textItems).includes("3. Trace Trees")).toBeTrue(); + + // Page 2 in the original document keeps its explicit destination slot. + pdfPage = await pdfDoc.getPage(2); + ({ items: textItems } = await pdfPage.getTextContent()); + expect( + mergeText(textItems).includes("2. Overview: Example Tracing Run") + ).toBeTrue(); + + // Page 6 in the original document should occupy the remaining free + // slot. + pdfPage = await pdfDoc.getPage(3); + ({ items: textItems } = await pdfPage.getTextContent()); + expect( + mergeText(textItems).includes("4. Nested Trace Tree Formation") + ).toBeTrue(); + + await loadingTask.destroy(); + }); }); describe("AcroForm", function () {