Avoid duplicate shared object clones

This commit is contained in:
Calixte Denizet 2026-07-13 11:39:10 +02:00
parent 01ba4c476d
commit 3a19a20d50
2 changed files with 29 additions and 0 deletions

View File

@ -271,6 +271,12 @@ class PDFEditor {
}
const oldRef = obj;
obj = await xref.fetchAsync(oldRef);
const mappedRef = oldRefMapping.get(oldRef);
if (mappedRef) {
// Another concurrent traversal may have allocated the clone while the
// source object was being fetched.
return mappedRef;
}
if (typeof obj === "number") {
// Simple value; no need to create a new reference.
return obj;

View File

@ -6286,6 +6286,29 @@ small scripts as well as for`);
await loadingTask.destroy();
});
it("clones shared indirect objects reached concurrently only once", async function () {
const pdfData = assemblePdf([
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n",
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
"3 0 obj\n<< /Type /Page /Parent 2 0 R " +
"/MediaBox [0 0 100 100] /Resources << /ExtGState " +
"<< /GS1 4 0 R /GS2 4 0 R >> >> >>\nendobj\n",
"4 0 obj\n<< /Type /ExtGState /CA 0.5 >>\nendobj\n",
]);
const loadingTask = getDocument({ data: pdfData });
const pdfDoc = await loadingTask.promise;
const data = await pdfDoc.extractPages([{ document: null }]);
expect(countMarker(data, "/Type /ExtGState")).toEqual(1);
await loadingTask.destroy();
const newLoadingTask = getDocument({ data });
const newPdfDoc = await newLoadingTask.promise;
expect(newPdfDoc.numPages).toEqual(1);
await newPdfDoc.getPage(1);
await newLoadingTask.destroy();
});
it("should merge two PDFs with page included ranges", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("tracemonkey.pdf")