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 |
heading1 … heading6 |
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, heading1–heading6, 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 | 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
itemsorder.
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 rawdefaultValue. helpTextrenders 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.familyisn't one of the standard 14 PDF fonts. WithoutaddFont(...), 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
#1A4F8Bis 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 hairlineborderColorreads cleaner than the viewer-defaultinsetbevel;insetlooks 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:6and an alternating-row colour. You can extend this with explicitcolumns:[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, socell { textfield(...) },cell { signaturefield(...) }, andcell(textfield(...))all attach the field to the cell with no need for an explicitparagraph { … }wrapper. Use an explicit paragraph only when you need to setalign,font, ormarginon 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.createPDFfor fillable PDF output anddocbuilder.createDocfor.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/listfieldwork in both formats.listboxfield,radiofield,signaturefield, anddatefieldare 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
.ttffont withaddFont('/path/to/font.ttf', name:'X', ...)inside thedocument { }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/ptaccessors to write1.inchinstead of72.