Skip to content

Commit b832748

Browse files
Merge pull request #21588 from calixteman/fix/pdf-editor-struct-attribute-revisions
Handle revisioned structure attributes
2 parents fd77b3e + eb58ccc commit b832748

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

src/core/editor/pdf_editor.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,12 +709,21 @@ class PDFEditor {
709709
}
710710
for (let attr of attributes) {
711711
attr = this.xrefWrapper.fetchIfRef(attr);
712+
if (!(attr instanceof Dict)) {
713+
// An attribute array may interleave dictionaries and revision
714+
// numbers (ISO 32000-2, 14.7.6.3).
715+
continue;
716+
}
712717
if (isName(attr.get("O"), "Table") && attr.has("Headers")) {
713718
const headers = this.xrefWrapper.fetchIfRef(attr.getRaw("Headers"));
714719
if (Array.isArray(headers)) {
715720
for (let i = 0, ii = headers.length; i < ii; i++) {
721+
const header = this.xrefWrapper.fetchIfRef(headers[i]);
722+
if (typeof header !== "string") {
723+
continue;
724+
}
716725
const newId = dedupIDs.get(
717-
stringToPDFString(headers[i], /* keepEscapeSequence = */ false)
726+
stringToPDFString(header, /* keepEscapeSequence = */ false)
718727
);
719728
if (newId) {
720729
headers[i] = newId;

test/unit/api_spec.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6801,6 +6801,79 @@ small scripts as well as for`);
68016801
await loadingTask.destroy();
68026802
});
68036803

6804+
it("preserves revisioned structure attributes", async function () {
6805+
const objects = [
6806+
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R " +
6807+
"/StructTreeRoot 4 0 R >>\nendobj\n",
6808+
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
6809+
"3 0 obj\n<< /Type /Page /Parent 2 0 R /StructParents 0 " +
6810+
"/MediaBox [0 0 100 100] >>\nendobj\n",
6811+
"4 0 obj\n<< /Type /StructTreeRoot /K [5 0 R] " +
6812+
"/ParentTree 7 0 R /ParentTreeNextKey 1 >>\nendobj\n",
6813+
"5 0 obj\n<< /Type /StructElem /S /Document /P 4 0 R " +
6814+
"/K 6 0 R >>\nendobj\n",
6815+
"6 0 obj\n<< /Type /StructElem /S /P /P 5 0 R /Pg 3 0 R /K 0 " +
6816+
"/A [<< /O /Layout /Placement /Block >> 0] >>\nendobj\n",
6817+
"7 0 obj\n<< /Nums [0 [6 0 R]] >>\nendobj\n",
6818+
];
6819+
let loadingTask = getDocument({ data: assemblePdf(objects) });
6820+
let pdfDoc = await loadingTask.promise;
6821+
const data = await pdfDoc.extractPages([{ document: null }]);
6822+
await loadingTask.destroy();
6823+
6824+
const pdfString = bytesToString(data);
6825+
expect(pdfString).toContain("/O /Layout");
6826+
expect(pdfString).toContain("/Placement");
6827+
expect(pdfString).toMatch(
6828+
/\/A\s+\[<<\s+\/O\s+\/Layout\s+\/Placement\s+\/Block>>\s+0\]/
6829+
);
6830+
6831+
loadingTask = getDocument({ data });
6832+
pdfDoc = await loadingTask.promise;
6833+
const tree = await (await pdfDoc.getPage(1)).getStructTree();
6834+
expect(tree.children[0].role).toEqual("Document");
6835+
expect(tree.children[0].children[0].role).toEqual("P");
6836+
await loadingTask.destroy();
6837+
});
6838+
6839+
it("remaps indirect table header ids when deduplicating struct trees", async function () {
6840+
const objects = [
6841+
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R " +
6842+
"/StructTreeRoot 4 0 R >>\nendobj\n",
6843+
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
6844+
"3 0 obj\n<< /Type /Page /Parent 2 0 R /StructParents 0 " +
6845+
"/MediaBox [0 0 100 100] >>\nendobj\n",
6846+
"4 0 obj\n<< /Type /StructTreeRoot /K [5 0 R] " +
6847+
"/ParentTree 9 0 R /ParentTreeNextKey 1 /IDTree 10 0 R >>\nendobj\n",
6848+
"5 0 obj\n<< /Type /StructElem /S /Table /P 4 0 R /Pg 3 0 R " +
6849+
"/K [6 0 R 7 0 R] >>\nendobj\n",
6850+
"6 0 obj\n<< /Type /StructElem /S /TH /P 5 0 R /Pg 3 0 R " +
6851+
"/ID (h1) /K 0 >>\nendobj\n",
6852+
"7 0 obj\n<< /Type /StructElem /S /TD /P 5 0 R /Pg 3 0 R /K 1 " +
6853+
"/A << /O /Table /Headers [8 0 R] >> >>\nendobj\n",
6854+
"8 0 obj\n(h1)\nendobj\n",
6855+
"9 0 obj\n<< /Nums [0 [6 0 R 7 0 R]] >>\nendobj\n",
6856+
"10 0 obj\n<< /Names [(h1) 6 0 R] >>\nendobj\n",
6857+
];
6858+
6859+
let loadingTask = getDocument({ data: assemblePdf(objects) });
6860+
let pdfDoc = await loadingTask.promise;
6861+
const data = await pdfDoc.extractPages([
6862+
{ document: null },
6863+
{ document: null },
6864+
]);
6865+
await loadingTask.destroy();
6866+
6867+
const pdfString = bytesToString(data);
6868+
expect(pdfString).toContain("/ID (h1_1)");
6869+
expect(pdfString).toContain("/Headers [(h1_1)]");
6870+
6871+
loadingTask = getDocument({ data });
6872+
pdfDoc = await loadingTask.promise;
6873+
expect(pdfDoc.numPages).toEqual(2);
6874+
await loadingTask.destroy();
6875+
});
6876+
68046877
it("extract pages and merge struct trees", async function () {
68056878
let loadingTask = getDocument(
68066879
buildGetDocumentParams("two_paragraphs.pdf")

0 commit comments

Comments
 (0)