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); | ||||
|   } | ||||
| 
 | ||||
|   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)) { | ||||
|  | ||||
| @ -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, | ||||
|           }); | ||||
|         }); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user