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:
		
						commit
						d9d3115a7b
					
				| @ -130,6 +130,11 @@ class Catalog { | |||||||
|     return shadow(this, "acroForm", acroForm); |     return shadow(this, "acroForm", acroForm); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   get acroFormRef() { | ||||||
|  |     const value = this._catDict.getRaw("AcroForm"); | ||||||
|  |     return shadow(this, "acroFormRef", isRef(value) ? value : null); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   get metadata() { |   get metadata() { | ||||||
|     const streamRef = this._catDict.getRaw("Metadata"); |     const streamRef = this._catDict.getRaw("Metadata"); | ||||||
|     if (!isRef(streamRef)) { |     if (!isRef(streamRef)) { | ||||||
|  | |||||||
| @ -573,6 +573,7 @@ class WorkerMessageHandler { | |||||||
|         const promises = [ |         const promises = [ | ||||||
|           pdfManager.onLoadedStream(), |           pdfManager.onLoadedStream(), | ||||||
|           pdfManager.ensureCatalog("acroForm"), |           pdfManager.ensureCatalog("acroForm"), | ||||||
|  |           pdfManager.ensureCatalog("acroFormRef"), | ||||||
|           pdfManager.ensureDoc("xref"), |           pdfManager.ensureDoc("xref"), | ||||||
|           pdfManager.ensureDoc("startXRef"), |           pdfManager.ensureDoc("startXRef"), | ||||||
|         ]; |         ]; | ||||||
| @ -597,6 +598,7 @@ class WorkerMessageHandler { | |||||||
|         return Promise.all(promises).then(function ([ |         return Promise.all(promises).then(function ([ | ||||||
|           stream, |           stream, | ||||||
|           acroForm, |           acroForm, | ||||||
|  |           acroFormRef, | ||||||
|           xref, |           xref, | ||||||
|           startXRef, |           startXRef, | ||||||
|           ...refs |           ...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 xfaDatasets = null; | ||||||
|  |           let hasDatasets = false; | ||||||
|           if (Array.isArray(xfa)) { |           if (Array.isArray(xfa)) { | ||||||
|             for (let i = 0, ii = xfa.length; i < ii; i += 2) { |             for (let i = 0, ii = xfa.length; i < ii; i += 2) { | ||||||
|               if (xfa[i] === "datasets") { |               if (xfa[i] === "datasets") { | ||||||
|                 xfaDatasets = xfa[i + 1]; |                 xfaDatasets = xfa[i + 1]; | ||||||
|  |                 acroFormRef = null; | ||||||
|  |                 hasDatasets = true; | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|  |             if (xfaDatasets === null) { | ||||||
|  |               xfaDatasets = xref.getNewRef(); | ||||||
|  |             } | ||||||
|           } else { |           } else { | ||||||
|  |             acroFormRef = null; | ||||||
|             // TODO: Support XFA streams.
 |             // TODO: Support XFA streams.
 | ||||||
|             warn("Unsupported XFA type."); |             warn("Unsupported XFA type."); | ||||||
|           } |           } | ||||||
| @ -666,6 +675,9 @@ class WorkerMessageHandler { | |||||||
|             newRefs, |             newRefs, | ||||||
|             xref, |             xref, | ||||||
|             datasetsRef: xfaDatasets, |             datasetsRef: xfaDatasets, | ||||||
|  |             hasDatasets, | ||||||
|  |             acroFormRef, | ||||||
|  |             acroForm, | ||||||
|             xfaData, |             xfaData, | ||||||
|           }); |           }); | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -146,10 +146,54 @@ function writeXFADataForAcroform(str, newRefs) { | |||||||
|   return buffer.join(""); |   return buffer.join(""); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateXFA(xfaData, datasetsRef, newRefs, xref) { | function updateXFA({ | ||||||
|   if (datasetsRef === null || xref === null) { |   xfaData, | ||||||
|  |   datasetsRef, | ||||||
|  |   hasDatasets, | ||||||
|  |   acroFormRef, | ||||||
|  |   acroForm, | ||||||
|  |   newRefs, | ||||||
|  |   xref, | ||||||
|  |   xrefInfo, | ||||||
|  | }) { | ||||||
|  |   if (xref === null) { | ||||||
|     return; |     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) { |   if (xfaData === null) { | ||||||
|     const datasets = xref.fetchIfRef(datasetsRef); |     const datasets = xref.fetchIfRef(datasetsRef); | ||||||
|     xfaData = writeXFADataForAcroform(datasets.getString(), newRefs); |     xfaData = writeXFADataForAcroform(datasets.getString(), newRefs); | ||||||
| @ -178,9 +222,21 @@ function incrementalUpdate({ | |||||||
|   newRefs, |   newRefs, | ||||||
|   xref = null, |   xref = null, | ||||||
|   datasetsRef = null, |   datasetsRef = null, | ||||||
|  |   hasDatasets = false, | ||||||
|  |   acroFormRef = null, | ||||||
|  |   acroForm = null, | ||||||
|   xfaData = null, |   xfaData = null, | ||||||
| }) { | }) { | ||||||
|   updateXFA(xfaData, datasetsRef, newRefs, xref); |   updateXFA({ | ||||||
|  |     xfaData, | ||||||
|  |     datasetsRef, | ||||||
|  |     hasDatasets, | ||||||
|  |     acroFormRef, | ||||||
|  |     acroForm, | ||||||
|  |     newRefs, | ||||||
|  |     xref, | ||||||
|  |     xrefInfo, | ||||||
|  |   }); | ||||||
| 
 | 
 | ||||||
|   const newXref = new Dict(null); |   const newXref = new Dict(null); | ||||||
|   const refForXrefTable = xrefInfo.newRef; |   const refForXrefTable = xrefInfo.newRef; | ||||||
|  | |||||||
| @ -142,4 +142,67 @@ describe("Writer", function () { | |||||||
|       expect(buffer.join("")).toEqual(expected); |       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); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user