Skip to content

Content Script Extension for Document Builder

Using the extension

This section describes how to use the document-builder API to generate PDF and Word documents from Content Script. The main Script API object you use is the docbuilder service, which exposes two factory methods — docbuilder.createPDF and docbuilder.createDoc — both of which take a name and a builder closure and return a CSResource that can be downloaded, attached to an email, or saved to Content Server.

def res = docbuilder.createPDF('hello') {
    document {
        paragraph 'Hello, World!'
    }
}

// Save to Content Server under a folder
def folder = docman.getNode(2000)
docman.addNode(folder, res)

The same DSL produces a .docx if you call createDoc:

def res = docbuilder.createDoc('hello') {
    document {
        paragraph 'Hello, World!'
    }
}

The closure is a Groovy builder DSL: every nested method call (document, paragraph, image, table, textfield, …) is a node factory. Most factories accept an attribute map (font, margin, background, …) plus children inside a nested closure. Numeric units (absoluteX:50, width:120, margin:[top:10], …) are interpreted in points unless you opt in to the unit category described below.

Document structure

Every script must open a top-level document { ... } block. Inside it you can use any of the registered factories:

Factory Purpose
paragraph Block of text, images, links, and inline form fields
heading1heading6 Heading paragraphs
text Inline text run inside a paragraph
link Hyperlink inside a paragraph
lineBreak Soft line break inside a paragraph
pageBreak Hard page break (PDF / Word)
image Embed an image
table / row / cell Tables
section New document section (size, orientation, header, footer)
complexHeader / complexFooter Section-scoped headers and footers
textBox Floating text box
textList / listLevel Numbered or bulleted lists
tableOfContent Generate a table of contents
comment / commentStart / commentEnd / reply Word-style comments
change Track-change run
footnote / endnote Footnotes and endnotes
mergefield Mail-merge placeholder
textfield / checkfield / listfield Interactive form fields (PDF + Word)
listboxfield / radiofield / signaturefield / datefield Interactive form fields (PDF only)

Page size, orientation, and margins

The document node accepts page size and orientation directly:

docbuilder.createPDF('letter') {
    document(size:'letter', orientation:'portrait',
            margin:[top:72, bottom:72, left:72, right:72]) {
        paragraph 'A4 portrait page with 1-inch margins.'
    }
}

size accepts a named paper size ('a4', 'letter', 'legal') or a [width, height] list expressed in points. orientation is 'portrait' or 'landscape'. Numeric attributes are points; use the unit category below to write inch(1) or cm(2.5) instead.

Sections

Use section { ... } to start a new section with its own size, orientation, header, or footer. Multiple sections produce one Word section per node and one PDF region per node.

document {
    section(orientation:'portrait') {
        paragraph 'Cover page'
    }
    section(orientation:'landscape', type:'nextPage') {
        paragraph 'Wide tables go here'
    }
}

type is 'nextPage' (default; section starts on a new page) or 'continuous'.

Headers and footers

complexHeader and complexFooter define re-rendered header and footer bands. They live directly inside document (or section) and accept the same children as paragraph.

document {
    complexHeader {
        paragraph 'Confidential — Internal use only'
    }
    complexFooter {
        paragraph 'Page ##pageNumber## of ##pageCount##'
    }
    paragraph 'Body text'
}

The Word builder substitutes ##pageNumber## and ##pageCount## with live page-number fields.

Embedded fonts

PdfDocumentBuilder.addFont(...) lets you ship a .ttf font with the PDF so widget text and body text render the same on every viewer. The Word builder ignores embedded fonts (Word relies on the system font map).

The signature is addFont(String path, Map fontProperties) — Groovy lets you pass the named properties directly so the call reads naturally. addFont must be called inside the document { } block (the underlying Document model is created by that factory; calling addFont before it raises a MissingMethodException / NullPointerException).

docbuilder.createPDF('embedded') {
    document {
        addFont('/opt/fonts/calibri.ttf', name:'Calibri', bold:false, italic:false)
        paragraph(font:[family:'Calibri', size:11], 'Body text in Calibri')
    }
}

Embedded fonts are also added to the AcroForm defaultResources map so a textfield(font:[family:'Calibri', ...]) renders correctly in every viewer.

Working with the unit category

By default, every numeric attribute (absoluteX:50, margin:[top:10], width:120) is interpreted in points. The docbuilder service wraps the closure in a UnitCategory so you can write more readable values:

docbuilder.createPDF('units') {
    document(margin:[top:1.inch, bottom:2.cm, left:36.pt, right:36.pt]) {
        paragraph 'Margins set in mixed units.'
    }
}

Available unit accessors:

Suffix Meaning
.pt / .px Points (no conversion)
.inch / .inches Inches → points
.cm / .centimeter / .centimeters Centimetres → points

Paragraphs and text

A paragraph is the smallest block element. It accepts inline text passed as a positional argument, plus any of the inline factories (text, link, image, lineBreak, mergefield, form fields).

document {
    paragraph 'A simple paragraph.'

    paragraph(font:[family:'Helvetica', size:12, color:'#222222'],
              align:'justify',
              margin:[top:6, bottom:6]) {
        text 'A '
        text('bold', font:[bold:true])
        text ' word and a '
        link('link', url:'https://example.com')
        text '.'
        lineBreak()
        text 'Wrapped onto a second line.'
    }

    heading1 'Chapter title'
    heading2 'Subsection'
}

Common attributes on text-bearing nodes (paragraph, heading1heading6, text, link, cell, form fields) are:

Attribute Purpose
font:[family, size, color, bold, italic, underline, strike] Run-level font
background:'#RRGGBB' Background fill
border:[size, color] Border properties (width in points, hex colour)
margin:[top, bottom, left, right] Outer margins (block nodes)
align:'left'\|'center'\|'right'\|'justify' Horizontal alignment (paragraph)

Images

Images are embedded by URL or by raw bytes. If you supply only width or height the other dimension is inferred from the natural aspect ratio:

document {
    paragraph {
        image(url:'https://example.com/logo.png', width:120)
    }

    // From a CSDocument fetched via docman
    def node = docman.getNode(2000)
    paragraph {
        image(data:node.getVersion().contents.bytes, type:'jpg', width:200, height:80)
    }
}

type is 'jpg' (default) or 'png'. Images placed directly under document (without a wrapping paragraph) are auto-wrapped in a one-line paragraph.

Tables

Tables are emitted as <w:tbl> in Word and laid out cell-by-cell in PDF. columns lets you fix relative widths; padding sets the inner cell padding in points; border styles every cell border.

document {
    table(width:540, columns:[2, 1, 1], padding:6,
          border:[size:1, color:'#888888']) {
        row {
            cell('Item', font:[bold:true])
            cell('Qty', font:[bold:true])
            cell('Price', font:[bold:true])
        }
        row {
            cell 'Widget A'
            cell '10'
            cell '€12.00'
        }
        row {
            cell 'Widget B'
            cell '4'
            cell '€48.50'
        }
    }
}

Cells accept colspan, rowspan, align, background, and per-side borders:[top:[…], bottom:[…], left:[…], right:[…]].

Inline content placed directly inside a cell { … } closure (form fields, text, image, link, lineBreak) is auto-wrapped in the cell's implicit paragraph, so all three forms below are equivalent:

// Inline content under a cell — auto-wrapped in an implicit paragraph
row { cell { textfield(name:'x', width:200) } }

// Explicit paragraph wrapper — same result, useful when you need paragraph attributes
row { cell { paragraph { textfield(name:'x', width:200) } } }

// Positional shorthand — cell(node) is treated as a child instead of being stringified
row { cell(textfield(name:'x', width:200)) }

Wrap content in an explicit paragraph { … } when you need to set align, font, or margin on the paragraph itself, or when you want multiple form fields on the same line. For multiline (tall) form fields — textfield(multiline:true), signaturefield, listboxfield — give the field its own paragraph so the layout has a predictable line height.

Plain cell('label string') and cell('label', font:[bold:true]) keep working — they take a positional String value and emit a single text run.

Lists

textList produces numbered or bulleted lists. Nest listLevel to control numbering format and indentation:

document {
    textList(type:'number') {
        listLevel('First item')
        listLevel('Second item') {
            textList(type:'bullet') {
                listLevel('Sub-bullet 1')
                listLevel('Sub-bullet 2')
            }
        }
        listLevel('Third item')
    }
}

Tables of contents, comments, footnotes, endnotes

These factories follow the same closure pattern. They are emitted natively by Word (tableOfContent, comment, change, footnote, endnote) and either ignored or simulated in PDF depending on the type.

document {
    tableOfContent(title:'Contents', minLevel:1, maxLevel:3)

    heading1 'Introduction'
    paragraph {
        text 'This is the introduction'
        footnote 'See the methodology section for details.'
    }

    heading1 'Reviewer comments'
    comment(author:'Jane', date:'2025-01-15') {
        paragraph 'Please add a chart here.'
        reply(author:'John', date:'2025-01-16') {
            paragraph 'Done in revision 2.'
        }
    }
}

Interactive form fields

Both createPDF and createDoc understand the same form-field DSL. The PDF builder emits AcroForm widgets; the Word builder emits FORMTEXT / FORMCHECKBOX / FORMDROPDOWN legacy form-field markers.

Factory PDF Word
textfield PDTextField FORMTEXT
checkfield PDCheckBox FORMCHECKBOX
listfield PDComboBox FORMDROPDOWN
listboxfield PDListBox ignored — logged as WARN
radiofield PDRadioButton group ignored — logged as WARN
signaturefield PDSignatureField ignored — logged as WARN
datefield PDTextField + AFDate_* actions ignored — logged as WARN

Inline placement

When a form field is declared inside a paragraph and no absolute coordinates are set, it flows with the surrounding text and reserves a default width per type.

docbuilder.createPDF('contact-form') {
    document {
        heading1 'Contact us'

        paragraph {
            text 'Full name: '
            textfield(name:'fname', defaultValue:'',
                      maxLength:64, alignment:'left',
                      font:[family:'Helvetica', size:10],
                      background:'#F5F5F5',
                      borderStyle:'inset', width:200)
        }

        paragraph {
            text 'Country: '
            listfield(name:'country', items:['IT', 'FR', 'DE'], selectedIndex:0,
                      editable:false, width:120)
        }

        paragraph {
            checkfield(name:'agree', checked:false, size:12)
            text ' I accept the privacy policy.'
        }

        paragraph {
            text 'Notify me via: '
            radiofield(name:'channel', items:['email', 'sms', 'phone'],
                       labels:['E-mail', 'SMS', 'Phone'],
                       selected:'email', layout:'horizontal')
        }

        paragraph {
            text 'Date of birth: '
            datefield(name:'dob', dateFormat:'dd/MM/yyyy', width:120)
        }

        paragraph {
            signaturefield(name:'sig', helpText:'Sign here', width:200, height:60)
        }
    }
}

Page-relative absolute placement

If you set page together with any of absoluteX, absoluteY, width, height, the field detaches from paragraph flow and is placed on the requested 1-based page at the given top-left point coordinates (post-UnitCategory). Out-of-range pages throw IllegalStateException at flush time.

docbuilder.createPDF('contract') {
    document {
        // ... body content ...

        paragraph {
            // Static signature block on page 1, top-left
            textfield(name:'sigDate', page:1,
                      absoluteX:400, absoluteY:720,
                      width:120, height:16,
                      defaultValue:'2026-01-01')
            signaturefield(name:'sig', page:1,
                           absoluteX:400, absoluteY:740,
                           width:160, height:48,
                           helpText:'Sign here')
        }
    }
}

Form-field attribute reference

The following attributes apply to every form field:

Attribute Description
name Field name (required for AcroForm; must be unique within the document)
helpText Tooltip / alternate field name (Acrobat & Foxit only)
readOnly true to make the field non-editable
required true to mark the field as required
border:[size, color] Widget border width and colour
borderStyle 'solid' (default), 'dashed', 'beveled', 'inset', 'underline'
borderColor Override of border.color written to the AcroForm /MK /BC entry
background Hex colour for /MK /BG
font:[family, size, color, bold, italic] Default appearance (/DA)
page + absoluteX + absoluteY Absolute placement (1-based page, top-left in points)
width, height Widget rectangle size in points

Type-specific attributes:

Field Extra attributes
textfield defaultValue, maxLength, multiline, password, alignment (left, center, right), format (UPPERCASE/LOWERCASE/F_CAPITAL for text, INTEGER/DECIMAL/FLOAT/FLOAT_DECIMAL/CURRENCY/PERCENTAGE for numbers, SHORT/MEDIUM/LONG/ISO or any SimpleDateFormat pattern for dates), type ('Text'/'Number'/'Date')
checkfield checked, defaultValue, size, sizeAuto, onGlyph ('4' ✓ default, '8' ✗, 'l' ●, 'H' ★ — PDF only)
listfield items, selectedIndex, editable, alignment
listboxfield (PDF-only) items, selectedIndices, multiSelect
radiofield (PDF-only) items, labels, selected, layout ('horizontal'/'vertical'), optionGlyphSize, optionSpacing, optionGap
signaturefield (PDF-only) helpText
datefield (PDF-only) dateFormat (any SimpleDateFormat pattern or named: SHORT, MEDIUM, LONG, ISO)

Field-name uniqueness

Top-level field names must be unique within a document. Radio sub-widgets share their parent group's name and are exempt. Duplicates throw IllegalStateException at render time — pre-validate names up front if your DSL is generated dynamically.

Tab order

Tab order is best-effort and viewer-dependent:

  • Inline fields are tabbed in render order.
  • Absolute fields are tabbed in DSL declaration order.
  • Radio sub-widgets are tabbed in items order.

Each emitted page sets /Tabs = R (row order) so most viewers honour the annotation array order, but viewers that re-sort by structure tree may differ. For strict tab-order requirements, use absolute placement with row-aligned absoluteY values.

Viewer caveats

  • Date format enforcement uses AFDate_KeystrokeEx / AFDate_FormatEx. Acrobat and Foxit honour both. Chrome and Edge PDFium silently drop them and display the raw defaultValue.
  • helpText renders as a tooltip in Acrobat and Foxit and is mostly ignored by Chrome PDFium. Use the field name or visible label text for anything users must see.
  • Embedded fonts are required for any text-bearing field whose font.family isn't one of the standard 14 PDF fonts. Without addFont(...), the field renders blank in every viewer.

Skipped contexts (PDF only)

Form fields nested inside comment {...}, change {...}, complexHeader { }, or complexFooter { } are silently dropped from the PDF (logged as WARN). The Word builder still emits them correctly, so this only affects PDF output. Avoid declaring fields under these parents if both formats matter.

Word / PDF asymmetry

listboxfield, radiofield, signaturefield, and datefield are PDF-only. Calls from createDoc are silently dropped from the .docx (logged as WARN: <factory> ignored by Word builder). Code that targets both formats from a single DSL should treat these as no-ops in Word.

The PDF-specific styling attributes (alignment, multiline, password, borderStyle, borderColor, readOnly, required, onGlyph, page, absoluteX, absoluteY, width, height) are accepted by the factories so a single DSL works for both formats — but the Word emitter ignores them.

Persisting the result

Both factory methods return a CSResource. Typical patterns:

def res = docbuilder.createPDF('invoice') {
    document {
        heading1 'Invoice'
        paragraph 'Number: 2026-001'
    }
}

// 1. Save to Content Server under a folder
def folder = docman.getNode(2000)
docman.addNode(folder, res)

// 2. Attach to an email
mail.create('Please find your invoice attached.')
    .to('customer@example.com')
    .subject('Invoice 2026-001')
    .attach(res)
    .send()

// 3. Stream to the page
out.contentType = 'application/pdf'
out.headers['Content-Disposition'] = "attachment; filename=\"${res.name}\""
out << res.content.bytes

CSResource.content is a temporary input stream backed by a temp file; the file is cleaned up automatically at the end of the request. If you need the bytes more than once, copy them into a buffer first.

End-to-end example

The script below produces a polished, fillable Service Agreement PDF. It demonstrates a confidentiality header, a paginated footer, a coloured heading hierarchy, a styled comparison table, every supported inline form-field type, a multi-line notes area, and an absolutely-placed signature block on page 1.

def res = docbuilder.createPDF('service-agreement') {
    document(size:'a4',
             margin:[top:1.inch, bottom:1.inch, left:1.inch, right:1.inch]) {

        // Optional: embed a TrueType font so body text and form-field text render
        // identically across viewers. Uncomment if the .ttf is on disk.
        // addFont('/opt/fonts/calibri.ttf', name:'Calibri', bold:false, italic:false)

        // ----- Header / footer ------------------------------------------------
        complexHeader {
            paragraph(align:'center', font:[size:9, color:'#888888']) {
                text 'CONFIDENTIAL — Service Agreement'
            }
        }
        complexFooter {
            paragraph(align:'right', font:[size:9, color:'#888888']) {
                text 'Page ##pageNumber## of ##pageCount##'
            }
        }

        // ----- Title block ----------------------------------------------------
        heading1(font:[size:22, bold:true, color:'#1A1A1A']) {
            text 'Service Agreement'
        }
        paragraph(font:[size:10, italic:true, color:'#666666'],
                  margin:[bottom:14]) {
            text 'This Agreement is entered into by the customer and the service provider on the date noted below.'
        }

        // ----- 1. Customer information ---------------------------------------
        heading2(font:[size:13, bold:true, color:'#1A4F8B']) {
            text '1. Customer information'
        }
        paragraph {
            text 'Customer name: '
            textfield(name:'customer', maxLength:80, alignment:'left',
                      background:'#F5F8FC', borderStyle:'solid',
                      borderColor:'#9FB6D1', width:300)
        }
        paragraph {
            text 'Company:           '
            textfield(name:'company', maxLength:80, alignment:'left',
                      background:'#F5F8FC', borderStyle:'solid',
                      borderColor:'#9FB6D1', width:300)
        }
        paragraph(margin:[bottom:14]) {
            text 'Email:                  '
            textfield(name:'email', maxLength:120, alignment:'left',
                      background:'#F5F8FC', borderStyle:'solid',
                      borderColor:'#9FB6D1', width:300)
        }

        // ----- 2. Service selection ------------------------------------------
        heading2(font:[size:13, bold:true, color:'#1A4F8B']) {
            text '2. Service selection'
        }
        paragraph {
            text 'Plan: '
            listfield(name:'plan', items:['Basic', 'Standard', 'Enterprise'],
                      selectedIndex:1, editable:false,
                      background:'#F5F8FC', borderStyle:'solid',
                      borderColor:'#9FB6D1', width:160)
        }
        paragraph {
            text 'Auto-renew: '
            radiofield(name:'renew', items:['yes', 'no'],
                       labels:['Yes', 'No'],
                       selected:'yes', layout:'horizontal')
        }
        paragraph(margin:[bottom:14]) {
            text 'Add-ons:  '
            checkfield(name:'addonSupport', size:11)
            text '  Premium support     '
            checkfield(name:'addonAnalytics', size:11)
            text '  Analytics    '
            checkfield(name:'addonSso', size:11)
            text '  Single sign-on'
        }

        // ----- 3. Pricing summary --------------------------------------------
        heading2(font:[size:13, bold:true, color:'#1A4F8B']) {
            text '3. Pricing summary'
        }
        table(width:6.5.inch, columns:[2, 1, 1], padding:6,
              border:[size:0.5, color:'#9FB6D1'],
              margin:[bottom:14]) {
            row(background:'#1A4F8B') {
                cell('Tier',    font:[bold:true, color:'#FFFFFF'])
                cell('Monthly', font:[bold:true, color:'#FFFFFF'])
                cell('Annual',  font:[bold:true, color:'#FFFFFF'])
            }
            row {
                cell 'Basic'
                cell '$ 19'
                cell '$ 199'
            }
            row(background:'#F5F8FC') {
                cell 'Standard'
                cell '$ 49'
                cell '$ 499'
            }
            row {
                cell 'Enterprise'
                cell 'Custom'
                cell 'Custom'
            }
        }

        // ----- 4. Acknowledgement --------------------------------------------
        heading2(font:[size:13, bold:true, color:'#1A4F8B']) {
            text '4. Acknowledgement'
        }
        paragraph {
            checkfield(name:'agree', size:12)
            text '  I have read and accept the Terms and Conditions.'
        }
        paragraph {
            text 'Effective date: '
            datefield(name:'effectiveOn', dateFormat:'dd/MM/yyyy', width:120)
        }
        paragraph {
            text 'Notes:'
            lineBreak()
            textfield(name:'notes', multiline:true,
                      maxLength:500, alignment:'left',
                      background:'#F5F8FC', borderStyle:'solid',
                      borderColor:'#9FB6D1',
                      width:6.5.inch, height:60)
        }

        // ----- Authorised signature block (absolute placement on page 1) -----
        paragraph {
            textfield(name:'sigName',
                      page:1, absoluteX:1.inch, absoluteY:9.4.inch,
                      width:240, height:18, helpText:'Print your name')
            datefield(name:'sigDate', dateFormat:'dd/MM/yyyy',
                      page:1, absoluteX:5.inch, absoluteY:9.4.inch,
                      width:120, height:18, helpText:'Date signed')
            signaturefield(name:'sig',
                           page:1, absoluteX:1.inch, absoluteY:9.7.inch,
                           width:6.5.inch, height:60,
                           helpText:'Authorised signature')
        }
    }
}

def folder = docman.getNode(2000)
docman.addNode(folder, res)

A few stylistic notes that make the output look polished:

  • The accent colour #1A4F8B is reused on every section heading and on the table-header row to give the document a consistent identity. Pair it with a faint complementary fill (#F5F8FC) on form-field backgrounds and on the table's banded row — the result is much easier on the eye than the default white.
  • borderStyle:'solid' plus a hairline borderColor reads cleaner than the viewer-default inset bevel; inset looks dated in modern viewers like Chrome / Edge PDFium.
  • Adding margin:[bottom:14] to the last paragraph of every section gives consistent vertical rhythm without needing an explicit spacer paragraph.
  • Tables become much more readable with padding:6 and an alternating-row colour. You can extend this with explicit columns:[2,1,1] weights to control column proportions.
  • The signature block uses absolute placement so it always lands at the bottom of page 1 regardless of how much body content was added.
  • Inline content placed directly under a cell { … } is auto-wrapped in the cell's implicit paragraph, so cell { textfield(...) }, cell { signaturefield(...) }, and cell(textfield(...)) all attach the field to the cell with no need for an explicit paragraph { … } wrapper. Use an explicit paragraph only when you need to set align, font, or margin on the surrounding text block, or to keep multiple inline elements on a single line.

Document Builder service APIs

Method Summary
CSResource
createPDF(String name, Closure builderClosure)
Renders a fillable PDF using the supplied builder DSL. Returns a CSResource containing the generated .pdf. Throws ExecutionFaultException on failure.
CSResource
createDoc(String name, Closure builderClosure)
Renders a Word document using the supplied builder DSL. Returns a CSResource containing the generated .docx. Throws ExecutionFaultException on failure.

Builder DSL — top-level factories

Method Summary
Document
document(Map attributes, Closure body)
Top-level node. Attributes: size, orientation, margin, font, template.
Section
section(Map attributes, Closure body)
Section block. Attributes: size, orientation, margin, type (nextPage/continuous), header, footer.
Header / Footer
complexHeader(Closure body) / complexFooter(Closure body)
Section-scoped header and footer bands. Use ##pageNumber## and ##pageCount## placeholders.
Paragraph
paragraph(String text), paragraph(Map attributes, Closure body)
Block of text, images, links, and inline form fields. Common attributes: font, align, margin, background, border.
Heading
heading1...heading6(String text)
Heading paragraphs (levels 1 to 6). Inherit paragraph attributes; default font sizes scale by level.
Text / Link
text(String value, Map attributes), link(String value, Map attributes)
Inline text run / hyperlink. link takes a url attribute.
LineBreak / PageBreak
lineBreak() / pageBreak()
Soft line break (inside a paragraph) and hard page break (between paragraphs).
Image
image(Map attributes)
Embed an image. Attributes: url or data, type (jpg/png), width, height, name.
Table / Row / Cell
table(Map attributes, Closure body) / row(Closure body) / cell(String value, Map attributes)
Tables. table takes width, columns, padding, border; cell takes colspan, rowspan, align, background, borders.
List
textList(Map attributes, Closure body) / listLevel(String value, Closure body)
Numbered or bulleted lists. type attribute: 'number' or 'bullet'.
TableOfContent
tableOfContent(Map attributes)
Generate a Word table of contents. Attributes: title, minLevel, maxLevel.
Comment / Reply
comment(Map attributes, Closure body) / reply(Map attributes, Closure body)
Word-style review comment threads. Attributes: author, date.
TrackChange
change(Map attributes, Closure body)
Word track-change run. Attributes: type (ins/del), author, date.
Footnote / Endnote
footnote(String text) / endnote(String text)
Footnotes and endnotes. Word only; ignored in PDF.
MergeField
mergefield(Map attributes)
Mail-merge placeholder. Attribute: name.
TextBox
textBox(Map attributes, Closure body)
Floating text box. Attributes: width, height, x, y, border, background.

Form-field factories

Method Summary
TextField
textfield(Map attributes)
Single-line or multiline text input. Emits FORMTEXT in Word, PDTextField in PDF.
CheckField
checkfield(Map attributes)
Boolean checkbox. Emits FORMCHECKBOX in Word, PDCheckBox in PDF.
ListField
listfield(Map attributes)
Combo / drop-down. Emits FORMDROPDOWN in Word, PDComboBox in PDF.
ListBoxField (PDF-only)
listboxfield(Map attributes)
Always-visible list box (with optional multi-select). Emits PDListBox in PDF; ignored by Word.
RadioField (PDF-only)
radiofield(Map attributes)
Mutually-exclusive radio group with horizontal or vertical layout. Emits a PDRadioButton with N child widgets in PDF; ignored by Word.
SignatureField (PDF-only)
signaturefield(Map attributes)
Digital-signature placeholder. Sets the AcroForm /SigFlags entry so Acrobat enables the signing UI.
DateField (PDF-only)
datefield(Map attributes)
Date-typed text field with AFDate_KeystrokeEx / AFDate_FormatEx validators. Acrobat / Foxit only; Chrome PDFium displays the raw defaultValue.

Summary

  • Use docbuilder.createPDF for fillable PDF output and docbuilder.createDoc for .docx. Both share the same DSL.
  • The DSL covers everything from paragraphs, tables, images, lists, headers / footers, comments, and footnotes to fully interactive form fields.
  • Form fields: textfield / checkfield / listfield work in both formats. listboxfield, radiofield, signaturefield, and datefield are PDF-only.
  • Inline placement flows fields with the surrounding paragraph; absolute placement (page + absoluteX + absoluteY + width + height) lands a field on a specific page in points.
  • Embed a .ttf font with addFont('/path/to/font.ttf', name:'X', ...) inside the document { } block when you use a non-standard font family on text-bearing widgets — without it, AcroForm widgets render blank in every viewer.
  • Pass numeric values in points by default; opt in to inch/cm/pt accessors to write 1.inch instead of 72.