From e135aad920c67f7fe3c781934d9eadbd43825b5d Mon Sep 17 00:00:00 2001 From: Jim Pravetz Date: Thu, 18 Jul 2019 21:28:36 -0700 Subject: [PATCH] Adding hierarchical field support to AcroForms --- demo/test-acroform.js | 19 ++++------ lib/mixins/acroform.js | 73 +++++++++++++++++++++++++++---------- tests/unit/acroform.spec.js | 21 +++++++++++ 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/demo/test-acroform.js b/demo/test-acroform.js index f1efb1c..eb96ab4 100755 --- a/demo/test-acroform.js +++ b/demo/test-acroform.js @@ -13,17 +13,14 @@ doc.info['Author'] = 'Jim Pravetz'; // Register a font name for use later -doc.registerFont('myfont1', 'fonts/PalatinoBold.ttf') +doc.font('Helvetica') // establishes the default font +doc.initAcroForm(); -doc.font('Courier-Bold') // establishes the default font -doc.initAcroForm() +let rootField = doc.field('rootField'); +let child1Field = doc.field('child1Field', { parent: rootField }); +let child2Field = doc.field('child2Field', { parent: rootField }); +doc.formText('leaf1', 10, 10, 200, 40, { parent: child1Field }) +doc.formText('leaf2', 10, 60, 200, 40, { parent: child1Field }) +doc.formText('leaf3', 10, 110, 200, 40, { parent: child2Field }) -doc.font('myfont1') - .fontSize(25) - .text('Test Doc', 0, 20, { width: 612, align: 'center' }); -doc.font('Courier') - .fontSize(16) - .text('Courier subheading', 0, 50, { width: 612, align: 'center' }); - -doc.font('myfont1').formText('file0', 10, 100, 592, 400, { multiline: true }); doc.end(); diff --git a/lib/mixins/acroform.js b/lib/mixins/acroform.js index 0b608cc..52512e2 100644 --- a/lib/mixins/acroform.js +++ b/lib/mixins/acroform.js @@ -53,16 +53,60 @@ export default { Object.keys(this._acroform.fonts).forEach(name => { fontDict[name] = this._acroform.fonts[name] }); - // if (this._acroform.defaultFont) { - // let font = this._fontFamiles[this._acroform.defaultFont]; - // this._root.data.AcroForm.data.DA = new String(`/${font.id} 0 Tf 0 g`) - // } + this._root.data.AcroForm.data.Fields.forEach(fieldRef => { + this._endChild(fieldRef); + }); this._root.data.AcroForm.end(); } return this; }, - formAnnot (name, x, y, w, h, options = {}) { + _endChild (ref) { + if (Array.isArray(ref.data.Kids)) { + ref.data.Kids.forEach(childRef => { + this._endChild(childRef); + }); + ref.end(); + } + return this; + }, + + field (name, options = {}) { + let fieldDict = this._fieldDict(name, options); + let fieldRef = this.ref(fieldDict); + this._addToParent(fieldRef, options.parent); + delete options.parent; + return fieldRef; + }, + + widgetAnnot (name, x, y, w, h, options = {}) { + let fieldDict = this._fieldDict(name, null, options); + fieldDict.Subtype = 'Widget'; + fieldDict.F = 4; + let parent = options.parent; + delete options.parent; + + // Add Field annot to page, and get it's ref + this.annotate(x, y, w, h, fieldDict); + let annotRef = this.page.annotations[this.page.annotations.length - 1]; + + return this._addToParent(annotRef, parent); + }, + + _addToParent (fieldRef, parentRef) { + if (parentRef) { + if (!parentRef.data.Kids) { + parentRef.data.Kids = []; + } + parentRef.data.Kids.push(fieldRef); + fieldRef.data.Parent = parentRef; + } else { + this._root.data.AcroForm.data.Fields.push(fieldRef); + } + return this; + }, + + _fieldDict (name, options = {}) { if (!this._acroform) { throw new Error('Call document.initAcroForm() method before adding AcroForm Fields to document'); } @@ -72,20 +116,9 @@ export default { opts = this._resolveFont(opts); opts = this._resolveStrings(opts); opts = Object.assign(opts, { - Subtype: 'Widget', - F: 4, - FT: options.FT, T: new String(name), - Ff: opts.Ff }); - // Add Field annot to page - this.annotate(x, y, w, h, opts); - - // Hack to get annotRef back so we can add it to global fields array. The - // annotation is the last one pushed on the page list - let annotRef = this.page.annotations[this.page.annotations.length - 1]; - this._root.data.AcroForm.data.Fields.push(annotRef); - return this; + return opts; }, _resolveFlags (options) { @@ -144,7 +177,7 @@ export default { formButton (name, x, y, w, h, options = {}) { options.FT = 'Btn'; - return this.formAnnot(name, x, y, w, h, options) + return this.widgetAnnot(name, x, y, w, h, options) }, formPushButton (name, x, y, w, h, options = {}) { @@ -164,12 +197,12 @@ export default { formText (name, x, y, w, h, options = {}) { options.FT = 'Tx' - return this.formAnnot(name, x, y, w, h, options) + return this.widgetAnnot(name, x, y, w, h, options) }, formChoice (name, x, y, w, h, options = {}) { options.FT = 'Ch' - return this.formAnnot(name, x, y, w, h, options) + return this.widgetAnnot(name, x, y, w, h, options) }, diff --git a/tests/unit/acroform.spec.js b/tests/unit/acroform.spec.js index 9350b98..502b057 100644 --- a/tests/unit/acroform.spec.js +++ b/tests/unit/acroform.spec.js @@ -51,6 +51,27 @@ describe('AcroForm', () => { .text('Courier subheading', 0, 50, { width: 612, align: 'center' }); doc.font('myfont1').formText('file0', 10, 100, 592, 400, { multiline: true }); + + doc.end(); + + //expect(docData.length).toBe(2); + }); + + test('field heirarchy', () => { + + // const docData = logData(doc); + + + doc.font('Helvetica') // establishes the default font + doc.initAcroForm() + + let rootField = doc.field('rootField'); + let child1Field = doc.field('child1Field', { parent: rootField }); + let child2Field = doc.field('child2Field', { parent: rootField }); + doc.formText('leaf1', 10, 10, 200, 40, { parent: child1Field }) + doc.formText('leaf2', 10, 60, 200, 40, { parent: child1Field }) + doc.formText('leaf3', 10, 110, 200, 40, { parent: child2Field }) + doc.end(); //expect(docData.length).toBe(2);