Skip to content

Commit d9d3115

Browse files
authored
Merge pull request #13967 from calixteman/no_datasets
XFA - Overwrite AcroForm dictionary when saving if no datasets in XFA (bug 1720179)
2 parents 804abb3 + 77b9657 commit d9d3115

File tree

4 files changed

+140
-4
lines changed

4 files changed

+140
-4
lines changed

src/core/catalog.js

+5
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ class Catalog {
130130
return shadow(this, "acroForm", acroForm);
131131
}
132132

133+
get acroFormRef() {
134+
const value = this._catDict.getRaw("AcroForm");
135+
return shadow(this, "acroFormRef", isRef(value) ? value : null);
136+
}
137+
133138
get metadata() {
134139
const streamRef = this._catDict.getRaw("Metadata");
135140
if (!isRef(streamRef)) {

src/core/worker.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ class WorkerMessageHandler {
573573
const promises = [
574574
pdfManager.onLoadedStream(),
575575
pdfManager.ensureCatalog("acroForm"),
576+
pdfManager.ensureCatalog("acroFormRef"),
576577
pdfManager.ensureDoc("xref"),
577578
pdfManager.ensureDoc("startXRef"),
578579
];
@@ -597,6 +598,7 @@ class WorkerMessageHandler {
597598
return Promise.all(promises).then(function ([
598599
stream,
599600
acroForm,
601+
acroFormRef,
600602
xref,
601603
startXRef,
602604
...refs
@@ -621,15 +623,22 @@ class WorkerMessageHandler {
621623
}
622624
}
623625

624-
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || [];
626+
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
625627
let xfaDatasets = null;
628+
let hasDatasets = false;
626629
if (Array.isArray(xfa)) {
627630
for (let i = 0, ii = xfa.length; i < ii; i += 2) {
628631
if (xfa[i] === "datasets") {
629632
xfaDatasets = xfa[i + 1];
633+
acroFormRef = null;
634+
hasDatasets = true;
630635
}
631636
}
637+
if (xfaDatasets === null) {
638+
xfaDatasets = xref.getNewRef();
639+
}
632640
} else {
641+
acroFormRef = null;
633642
// TODO: Support XFA streams.
634643
warn("Unsupported XFA type.");
635644
}
@@ -666,6 +675,9 @@ class WorkerMessageHandler {
666675
newRefs,
667676
xref,
668677
datasetsRef: xfaDatasets,
678+
hasDatasets,
679+
acroFormRef,
680+
acroForm,
669681
xfaData,
670682
});
671683
});

src/core/writer.js

+59-3
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,54 @@ function writeXFADataForAcroform(str, newRefs) {
146146
return buffer.join("");
147147
}
148148

149-
function updateXFA(xfaData, datasetsRef, newRefs, xref) {
150-
if (datasetsRef === null || xref === null) {
149+
function updateXFA({
150+
xfaData,
151+
datasetsRef,
152+
hasDatasets,
153+
acroFormRef,
154+
acroForm,
155+
newRefs,
156+
xref,
157+
xrefInfo,
158+
}) {
159+
if (xref === null) {
151160
return;
152161
}
162+
163+
if (!hasDatasets) {
164+
if (!acroFormRef) {
165+
warn("XFA - Cannot save it");
166+
return;
167+
}
168+
169+
// We've a XFA array which doesn't contain a datasets entry.
170+
// So we'll update the AcroForm dictionary to have an XFA containing
171+
// the datasets.
172+
const oldXfa = acroForm.get("XFA");
173+
const newXfa = oldXfa.slice();
174+
newXfa.splice(2, 0, "datasets");
175+
newXfa.splice(3, 0, datasetsRef);
176+
177+
acroForm.set("XFA", newXfa);
178+
179+
const encrypt = xref.encrypt;
180+
let transform = null;
181+
if (encrypt) {
182+
transform = encrypt.createCipherTransform(
183+
acroFormRef.num,
184+
acroFormRef.gen
185+
);
186+
}
187+
188+
const buffer = [`${acroFormRef.num} ${acroFormRef.gen} obj\n`];
189+
writeDict(acroForm, buffer, transform);
190+
buffer.push("\n");
191+
192+
acroForm.set("XFA", oldXfa);
193+
194+
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
195+
}
196+
153197
if (xfaData === null) {
154198
const datasets = xref.fetchIfRef(datasetsRef);
155199
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
@@ -178,9 +222,21 @@ function incrementalUpdate({
178222
newRefs,
179223
xref = null,
180224
datasetsRef = null,
225+
hasDatasets = false,
226+
acroFormRef = null,
227+
acroForm = null,
181228
xfaData = null,
182229
}) {
183-
updateXFA(xfaData, datasetsRef, newRefs, xref);
230+
updateXFA({
231+
xfaData,
232+
datasetsRef,
233+
hasDatasets,
234+
acroFormRef,
235+
acroForm,
236+
newRefs,
237+
xref,
238+
xrefInfo,
239+
});
184240

185241
const newXref = new Dict(null);
186242
const refForXrefTable = xrefInfo.newRef;

test/unit/writer_spec.js

+63
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,67 @@ describe("Writer", function () {
142142
expect(buffer.join("")).toEqual(expected);
143143
});
144144
});
145+
146+
describe("XFA", function () {
147+
it("should update AcroForm when no datasets in XFA array", function () {
148+
const originalData = new Uint8Array();
149+
const newRefs = [];
150+
151+
const acroForm = new Dict(null);
152+
acroForm.set("XFA", [
153+
"preamble",
154+
Ref.get(123, 0),
155+
"postamble",
156+
Ref.get(456, 0),
157+
]);
158+
const acroFormRef = Ref.get(789, 0);
159+
const datasetsRef = Ref.get(101112, 0);
160+
const xfaData = "<hello>world</hello>";
161+
162+
const xrefInfo = {
163+
newRef: Ref.get(131415, 0),
164+
startXRef: 314,
165+
fileIds: null,
166+
rootRef: null,
167+
infoRef: null,
168+
encryptRef: null,
169+
filename: "foo.pdf",
170+
info: {},
171+
};
172+
173+
let data = incrementalUpdate({
174+
originalData,
175+
xrefInfo,
176+
newRefs,
177+
datasetsRef,
178+
hasDatasets: false,
179+
acroFormRef,
180+
acroForm,
181+
xfaData,
182+
xref: {},
183+
});
184+
data = bytesToString(data);
185+
186+
const expected =
187+
"\n" +
188+
"789 0 obj\n" +
189+
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
190+
"101112 0 obj\n" +
191+
"<< /Type /EmbeddedFile /Length 20>>\n" +
192+
"stream\n" +
193+
"<hello>world</hello>\n" +
194+
"endstream\n" +
195+
"endobj\n" +
196+
"131415 0 obj\n" +
197+
"<< /Size 131416 /Prev 314 /Type /XRef /Index [0 1 789 1 101112 1 131415 1] /W [1 1 2] /Length 16>> stream\n" +
198+
"\u0000\u0001ÿÿ\u0001\u0001\u0000\u0000\u0001T\u0000\u0000\u0001²\u0000\u0000\n" +
199+
"endstream\n" +
200+
"endobj\n" +
201+
"startxref\n" +
202+
"178\n" +
203+
"%%EOF\n";
204+
205+
expect(data).toEqual(expected);
206+
});
207+
});
145208
});

0 commit comments

Comments
 (0)