Merge pull request #13967 from calixteman/no_datasets

XFA - Overwrite AcroForm dictionary when saving if no datasets in XFA (bug 1720179)
This commit is contained in:
Brendan Dahl 2021-09-03 09:53:36 -07:00 committed by GitHub
commit d9d3115a7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 4 deletions

View File

@ -130,6 +130,11 @@ class Catalog {
return shadow(this, "acroForm", acroForm);
}
get acroFormRef() {
const value = this._catDict.getRaw("AcroForm");
return shadow(this, "acroFormRef", isRef(value) ? value : null);
}
get metadata() {
const streamRef = this._catDict.getRaw("Metadata");
if (!isRef(streamRef)) {

View File

@ -573,6 +573,7 @@ class WorkerMessageHandler {
const promises = [
pdfManager.onLoadedStream(),
pdfManager.ensureCatalog("acroForm"),
pdfManager.ensureCatalog("acroFormRef"),
pdfManager.ensureDoc("xref"),
pdfManager.ensureDoc("startXRef"),
];
@ -597,6 +598,7 @@ class WorkerMessageHandler {
return Promise.all(promises).then(function ([
stream,
acroForm,
acroFormRef,
xref,
startXRef,
...refs
@ -621,15 +623,22 @@ class WorkerMessageHandler {
}
}
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || [];
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
let xfaDatasets = null;
let hasDatasets = false;
if (Array.isArray(xfa)) {
for (let i = 0, ii = xfa.length; i < ii; i += 2) {
if (xfa[i] === "datasets") {
xfaDatasets = xfa[i + 1];
acroFormRef = null;
hasDatasets = true;
}
}
if (xfaDatasets === null) {
xfaDatasets = xref.getNewRef();
}
} else {
acroFormRef = null;
// TODO: Support XFA streams.
warn("Unsupported XFA type.");
}
@ -666,6 +675,9 @@ class WorkerMessageHandler {
newRefs,
xref,
datasetsRef: xfaDatasets,
hasDatasets,
acroFormRef,
acroForm,
xfaData,
});
});

View File

@ -146,10 +146,54 @@ function writeXFADataForAcroform(str, newRefs) {
return buffer.join("");
}
function updateXFA(xfaData, datasetsRef, newRefs, xref) {
if (datasetsRef === null || xref === null) {
function updateXFA({
xfaData,
datasetsRef,
hasDatasets,
acroFormRef,
acroForm,
newRefs,
xref,
xrefInfo,
}) {
if (xref === null) {
return;
}
if (!hasDatasets) {
if (!acroFormRef) {
warn("XFA - Cannot save it");
return;
}
// We've a XFA array which doesn't contain a datasets entry.
// So we'll update the AcroForm dictionary to have an XFA containing
// the datasets.
const oldXfa = acroForm.get("XFA");
const newXfa = oldXfa.slice();
newXfa.splice(2, 0, "datasets");
newXfa.splice(3, 0, datasetsRef);
acroForm.set("XFA", newXfa);
const encrypt = xref.encrypt;
let transform = null;
if (encrypt) {
transform = encrypt.createCipherTransform(
acroFormRef.num,
acroFormRef.gen
);
}
const buffer = [`${acroFormRef.num} ${acroFormRef.gen} obj\n`];
writeDict(acroForm, buffer, transform);
buffer.push("\n");
acroForm.set("XFA", oldXfa);
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
}
if (xfaData === null) {
const datasets = xref.fetchIfRef(datasetsRef);
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
@ -178,9 +222,21 @@ function incrementalUpdate({
newRefs,
xref = null,
datasetsRef = null,
hasDatasets = false,
acroFormRef = null,
acroForm = null,
xfaData = null,
}) {
updateXFA(xfaData, datasetsRef, newRefs, xref);
updateXFA({
xfaData,
datasetsRef,
hasDatasets,
acroFormRef,
acroForm,
newRefs,
xref,
xrefInfo,
});
const newXref = new Dict(null);
const refForXrefTable = xrefInfo.newRef;

View File

@ -142,4 +142,67 @@ describe("Writer", function () {
expect(buffer.join("")).toEqual(expected);
});
});
describe("XFA", function () {
it("should update AcroForm when no datasets in XFA array", function () {
const originalData = new Uint8Array();
const newRefs = [];
const acroForm = new Dict(null);
acroForm.set("XFA", [
"preamble",
Ref.get(123, 0),
"postamble",
Ref.get(456, 0),
]);
const acroFormRef = Ref.get(789, 0);
const datasetsRef = Ref.get(101112, 0);
const xfaData = "<hello>world</hello>";
const xrefInfo = {
newRef: Ref.get(131415, 0),
startXRef: 314,
fileIds: null,
rootRef: null,
infoRef: null,
encryptRef: null,
filename: "foo.pdf",
info: {},
};
let data = incrementalUpdate({
originalData,
xrefInfo,
newRefs,
datasetsRef,
hasDatasets: false,
acroFormRef,
acroForm,
xfaData,
xref: {},
});
data = bytesToString(data);
const expected =
"\n" +
"789 0 obj\n" +
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
"101112 0 obj\n" +
"<< /Type /EmbeddedFile /Length 20>>\n" +
"stream\n" +
"<hello>world</hello>\n" +
"endstream\n" +
"endobj\n" +
"131415 0 obj\n" +
"<< /Size 131416 /Prev 314 /Type /XRef /Index [0 1 789 1 101112 1 131415 1] /W [1 1 2] /Length 16>> stream\n" +
"\u0000\u0001ÿÿ\u0001\u0001\u0000\u0000\u0001T\u0000\u0000\u0001²\u0000\u0000\n" +
"endstream\n" +
"endobj\n" +
"startxref\n" +
"178\n" +
"%%EOF\n";
expect(data).toEqual(expected);
});
});
});