Get the FREE Ultimate OpenClaw Setup Guide →

adobe-acrobat-form

Scanned
npx machina-cli add skill austinfox/claude-plugins/adobe-acrobat-form --openclaw
Files (1)
SKILL.md
21.2 KB

Adobe Acrobat PDF Forms Expert

You are an expert on Adobe Acrobat PDF forms, including form field creation, modification, filling, validation, and integration with Adobe Acrobat Sign for e-signatures.

Core Knowledge Areas

1. PDF Form Field Types

Adobe Acrobat supports these form field types:

Field TypeAcroForm TypeDescription
Text Field/TxSingle or multi-line text input
Check Box/Btn (with /AS)Boolean on/off toggle
Radio Button/Btn (grouped)Mutually exclusive selection within a group
Combo Box/Ch (with /Ff bit 18)Dropdown selection list
List Box/ChScrollable selection list
Push Button/Btn (with /Ff bit 17)Clickable button (submit, reset, or JavaScript action)
Signature Field/SigDigital or electronic signature placeholder

2. Form Field Properties

Every form field has these key properties:

  • Field Name (/T): The programmatic identifier (e.g., tenant_name, lease_start_date)
  • Alternate Name (/TU): Tooltip text shown on hover
  • Default Value (/DV): Pre-populated value
  • Value (/V): Current field value
  • Flags (/Ff): Bit flags controlling behavior:
    • Bit 1: Read-only
    • Bit 2: Required
    • Bit 3: No export
    • Bit 13: Multi-line (text fields)
    • Bit 14: Password (text fields)
    • Bit 18: Combo (choice fields)
    • Bit 19: Edit (editable combo box)
    • Bit 20: Sort (sorted choice items)
  • Appearance (/AP, /DA): Font, size, color, border, fill
  • Actions (/A, /AA): JavaScript or other actions triggered by events

3. Field Naming Conventions

Flat Names

Simple names like first_name, address_line_1. Easy to reference but no grouping.

Hierarchical Names (Dot Notation)

Used for grouping related fields:

tenant.first_name
tenant.last_name
tenant.address.street
tenant.address.city

In the PDF structure, this creates a field tree with parent nodes.

Array-Style Names

For repeating fields (e.g., multiple signers):

signer.0.name
signer.0.email
signer.1.name
signer.1.email

4. Programmatic Form Filling

Using pypdf (Python)

from pypdf import PdfReader, PdfWriter

reader = PdfReader("form.pdf")
writer = PdfWriter()
writer.append(reader)

# Fill fields
writer.update_page_form_field_values(
    writer.pages[0],
    {
        "tenant_name": "John Doe",
        "lease_start": "03/01/2025",
        "monthly_rent": "1,500.00",
    },
    auto_regenerate=False,  # Preserve existing appearances
)

with open("filled_form.pdf", "wb") as f:
    writer.write(f)

Flattening Forms (Make Fields Non-Editable)

# After filling, flatten to lock values
for page in writer.pages:
    for annot in page.get("/Annots", []):
        annot_obj = annot.get_object()
        if annot_obj.get("/Subtype") == "/Widget":
            annot_obj.update({"/Ff": 1})  # Set read-only flag

Discovering Field Names

reader = PdfReader("form.pdf")
fields = reader.get_fields()
for name, field in fields.items():
    print(f"Name: {name}")
    print(f"  Type: {field.get('/FT', 'Unknown')}")
    print(f"  Value: {field.get('/V', 'Empty')}")
    print(f"  Flags: {field.get('/Ff', 0)}")

Using pdftk (Command Line)

# Dump field names
pdftk form.pdf dump_data_fields

# Fill form from FDF
pdftk form.pdf fill_form data.fdf output filled.pdf

# Flatten
pdftk form.pdf fill_form data.fdf output filled.pdf flatten

5. Adobe Acrobat Sign (E-Sign) Integration

How Acrobat Sign Uses Form Fields

Acrobat Sign uses a proprietary /ADBE_Sign dictionary embedded in the PDF at both field-level and document-level to control participant assignment. This is the primary mechanism for programmatic e-sign preparation.

IMPORTANT: The {{text_tag}} naming convention is for text tags placed as visible text in document body. For programmatic form field preparation with pypdf, you must use the /ADBE_Sign dictionary approach described below.

The /ADBE_Sign Architecture (3 Required Layers)

Layer 1: Document-Level ParticipantSetsInfo

In the AcroForm dictionary, add /ADBE_Sign with a /ParticipantSetsInfo array defining all participants:

# Each participant gets a UUID, color index, order, and role
/ADBE_Sign: {
    /ParticipantSetsInfo: [
        {/Id: "uuid-1", /ColorIndex: 1, /Order: 1, /Role: /SIGNER},  # Participant 1
        {/Id: "uuid-2", /ColorIndex: 0, /Order: 1, /Role: /SIGNER},  # Participant 2
        {/Id: "uuid-3", /ColorIndex: 2, /Order: 1, /Role: /SIGNER},  # Participant 3
        ...
    ]
}
Layer 2: Field-Level /ADBE_Sign on Signature & Date Fields

Each signature field gets an /ADBE_Sign dict linking it to a participant via the UUID:

For signature fields:

/ADBE_Sign: {/Assignee: "uuid-1", /ContentType: /SIGNATURE, /InputType: /SIGNATURE}

For auto-date fields (auto-fills when participant signs):

/ADBE_Sign: {/Assignee: "uuid-1", /ContentType: /SIGNATURE_DATE, /DisplayFormat: "mm/dd/yyyy", /DisplayFormatType: /DATE}
Layer 3: Empty /ADBE_Sign on All Other Fields

Every non-signature field must have an empty /ADBE_Sign: {} dictionary.

Field Type Requirements

CRITICAL: Signature fields for Acrobat Sign must be /Tx (text) type, NOT /Sig:

  • Convert original /Sig fields to /Tx
  • Set /Ff: 2 (Required) on signature fields
  • Set /Ff: 1 (Read-only) on auto-date fields
  • Remove /AP (appearance stream) from converted fields - let Acrobat Sign regenerate
  • Set /DA to /Helv -1.000000 Tf 0 0 0 rg 0 0 0 RG
  • Set /Ff: 0 on all regular text fields (reset from any previous value like 8388608)

Field Naming Convention

Use descriptive names with numbers matching participant order:

  • Signature Field 1, Signature Field 1b (same participant, different pages)
  • Signature Field 2, Signature Field 3, etc.
  • Date of Signing 1, Date of Signing 1b, etc.

Multiple fields can share the same participant UUID (e.g., owner signs on page 1 and page 2).

Complete pypdf Implementation

import uuid
from pypdf import PdfReader, PdfWriter
from pypdf.generic import (
    NameObject, TextStringObject, NumberObject,
    DictionaryObject, ArrayObject
)

reader = PdfReader("form.pdf")
writer = PdfWriter()
writer.append(reader)

# Generate UUIDs per participant
P_UUIDS = {1: str(uuid.uuid4()), 2: str(uuid.uuid4()), ...}
P_COLORS = {1: 1, 2: 0, 3: 2, 4: 3, 5: 4}  # Color indices

def make_sig_adbe(pnum):
    d = DictionaryObject()
    d[NameObject("/Assignee")] = TextStringObject(P_UUIDS[pnum])
    d[NameObject("/ContentType")] = NameObject("/SIGNATURE")
    d[NameObject("/InputType")] = NameObject("/SIGNATURE")
    return d

def make_date_adbe(pnum):
    d = DictionaryObject()
    d[NameObject("/Assignee")] = TextStringObject(P_UUIDS[pnum])
    d[NameObject("/ContentType")] = NameObject("/SIGNATURE_DATE")
    d[NameObject("/DisplayFormat")] = TextStringObject("mm/dd/yyyy")
    d[NameObject("/DisplayFormatType")] = NameObject("/DATE")
    return d

# CRITICAL: Track processed objects to avoid overwriting
# (page annotations and AcroForm fields are often the SAME objects)
processed_ids = set()

def process_field(annot, field_name):
    obj_id = id(annot)
    if obj_id in processed_ids:
        return False  # Already processed
    processed_ids.add(obj_id)

    if field_name == "Original Sig Field Name":
        # Convert /Sig to /Tx signature field
        annot[NameObject("/FT")] = NameObject("/Tx")
        annot[NameObject("/T")] = TextStringObject("Signature Field 1")
        annot[NameObject("/Ff")] = NumberObject(2)  # Required
        annot[NameObject("/DA")] = TextStringObject("/Helv -1.000000 Tf 0 0 0 rg 0 0 0 RG ")
        annot[NameObject("/ADBE_Sign")] = make_sig_adbe(1)
        # Remove old sig/appearance keys
        for key in ["/AP", "/SV", "/Lock", "/ByteRange", "/Contents", "/BS", "/MK"]:
            if key in annot:
                del annot[NameObject(key)]
    elif field_name == "Original Date Field Name":
        annot[NameObject("/T")] = TextStringObject("Date of Signing 1")
        annot[NameObject("/Ff")] = NumberObject(1)  # Read-only
        annot[NameObject("/ADBE_Sign")] = make_date_adbe(1)
        for key in ["/AP", "/Q", "/BS", "/MK"]:
            if key in annot:
                del annot[NameObject(key)]
    else:
        # Regular field: empty ADBE_Sign + reset flags
        annot[NameObject("/ADBE_Sign")] = DictionaryObject()
        if annot.get("/Ff", 0) == 8388608:
            annot[NameObject("/Ff")] = NumberObject(0)

# Process page annotations first, then AcroForm fields
# (most are shared objects, so second pass is a no-op)

# Add document-level ADBE_Sign
participant_sets = ArrayObject()
for pnum in range(1, N+1):
    entry = DictionaryObject()
    entry[NameObject("/ColorIndex")] = NumberObject(P_COLORS[pnum])
    entry[NameObject("/Id")] = TextStringObject(P_UUIDS[pnum])
    entry[NameObject("/Order")] = NumberObject(1)
    entry[NameObject("/Role")] = NameObject("/SIGNER")
    participant_sets.append(entry)

doc_adbe = DictionaryObject()
doc_adbe[NameObject("/ParticipantSetsInfo")] = participant_sets
acroform[NameObject("/ADBE_Sign")] = doc_adbe

Key Gotchas (Learned from Practice)

  1. Shared objects: After writer.append(reader), page annotation objects and AcroForm field objects are often the SAME Python objects. If you modify in the annotation loop then iterate AcroForm fields, you'll process already-renamed fields and overwrite your changes. Always track processed object IDs.

  2. Must remove /AP: Old /Sig fields have empty/inverted appearance streams. Remove /AP entirely from converted fields - Acrobat Sign regenerates its own.

  3. /Ff flag 8388608: Original RHAWA form fields often have this flag. Reset to 0 for Acrobat Sign compatibility.

  4. /ADBE_Sign is NOT visible in Acrobat Pro's field properties UI. Only viewable programmatically or by the results in Acrobat Sign's web UI.

  5. Text tags {{...}} do NOT work as form field names for programmatic preparation. They're only for text placed in the document body that Acrobat Sign converts to fields.

Pre-Filling Before Sending for Signature

When using Acrobat Sign API or the UI:

  • Fields without /ADBE_Sign assignee can be pre-filled before sending
  • Pre-filled fields can be locked (read-only /Ff: 1) or left editable for signers

6. Common Form Operations

Making a Field Required

# In pypdf
annot_obj["/Ff"] = annot_obj.get("/Ff", 0) | 2  # Set bit 2

In Acrobat UI: Right-click field > Properties > General > Required checkbox

Setting Field Validation

In Acrobat: Field Properties > Validate tab

  • Range checking for numbers
  • Custom JavaScript validation:
// Custom validation script
if (event.value !== "" && !/^\d{5}(-\d{4})?$/.test(event.value)) {
    app.alert("Please enter a valid ZIP code.");
    event.rc = false;
}

Calculation Scripts

For fields that auto-calculate (e.g., totals):

// Custom calculation script
var rent = this.getField("monthly_rent").value;
var months = this.getField("lease_months").value;
event.value = rent * months;

Date Format Scripts

// Format script for date fields
AFDate_FormatEx("mm/dd/yyyy");

// Keystroke script
AFDate_KeystrokeEx("mm/dd/yyyy");

7. Checkbox and Radio Button Values

Checkboxes

  • Checked value: Usually /Yes, /On, or a custom export value
  • Unchecked value: /Off
  • In pypdf: Set field value to the export value string (e.g., "Yes" or "On")
# Check a checkbox
writer.update_page_form_field_values(page, {"agree_terms": "Yes"})

# Some forms use custom export values
writer.update_page_form_field_values(page, {"agree_terms": "/1"})

Radio Buttons

  • Grouped by parent field name
  • Each button has a unique export value
  • Set the parent field value to the desired button's export value
# Select a radio button
writer.update_page_form_field_values(page, {"lease_type": "Fixed"})

8. Troubleshooting Common Issues

IssueCauseSolution
Field values don't appearMissing appearance streamSet auto_regenerate=True or rebuild AP stream
Fields appear blank in AcrobatFont not embeddedEmbed font in AP stream or use standard PDF fonts
Checkbox won't checkWrong export valueDump fields to find the correct check value
Fields not detected by Acrobat SignNon-standard field structureRebuild fields using Acrobat's form editor
Flattened form still editableFlattening not applied to all pagesIterate all pages and all annotations
Pre-filled values cleared by signerFields assigned to signer roleUse prefill role or set read-only flag
Signature field too smallDimensions below minimumEnsure at least 60x20 pixels for Acrobat Sign

9. Tools & Libraries Reference

ToolLanguageBest For
pypdfPythonReading, filling, basic manipulation
pdftkCLIFilling from FDF/XFDF, flattening, merging
pdf-libJavaScript/NodeCreating and modifying PDFs programmatically
iTextJava/.NETEnterprise PDF manipulation
Adobe Acrobat ProGUIForm design, field placement, JavaScript
Adobe Acrobat Sign APIREST APIE-sign workflow automation
qpdfCLILow-level PDF structure manipulation

10. Lease Template Conversion Checklist (RHAWA Forms for Acrobat Sign)

This is the step-by-step process for preparing the user's RHAWA lease template PDF for Adobe Acrobat Sign after form updates.

Key Files

  • Conversion script: convert_new_lease_template_v2.py (in Claude working dir)
  • Input PDF: Lease Documents/Templates/LeaseTemplate_2026_Blank.pdf (or current blank)
  • Output PDF: Lease Documents/Templates/LeaseTemplate_2026_test.pdf
  • Participants: 5 total — P1=Owner/Agent, P2-P5=Residents 1-4

Pre-Flight: Understand What Changed

  1. Ask which forms were replaced and their page ranges
  2. Check total page count — if it changed, ALL page references in the script need updating
  3. Run field inventory on the new PDF to find fresh vs. old fields:
    reader = PdfReader(new_pdf)
    fields = reader.get_fields()
    for name, field in sorted(fields.items()):
        print(f"{name}: Type={field.get('/FT')}, Ff={field.get('/Ff')}, Kids={len(field.get('/Kids', []))}")
    
  4. Identify fresh fields (from updated forms, have /Sig type or Ff=8388608) vs. old v2 fields (already converted, have /Tx type and Ff=1 or Ff=2)
  5. Extract text positions for "Resident(s) to Initial:", "acknowledge receipt:", "O/A:...R:..." footer, and any "Resident initial:" sections — positions shift between form revisions

Step-by-Step Conversion Process

Step 1: Convert Fresh Signature Fields (/Sig/Tx)
  • Change /FT from /Sig to /Tx
  • Set /Ff: 2 (Required)
  • Set /DA to /Helv -1.000000 Tf 0 0 0 rg 0 0 0 RG
  • Clear /TU, /V, add /NM (unique UUID)
  • Remove: /AP, /SV, /Lock, /ByteRange, /Contents, /BS, /MK
  • Set /ADBE_Sign with {/Assignee: UUID, /ContentType: /SIGNATURE, /InputType: /SIGNATURE}
Step 2: Convert Fresh Date Fields
  • Set /Ff: 1 (Read-only — auto-fills when participant signs)
  • Set /DA to Helvetica default string
  • Remove: /AP, /Q, /BS, /MK
  • Set /ADBE_Sign with {/Assignee: UUID, /ContentType: /SIGNATURE_DATE, /DisplayFormat: "mm/dd/yyyy", /DisplayFormatType: /DATE}
  • CRITICAL: Propagate ADBE_Sign to ALL kid widgets — date fields are hierarchical with kids across multiple pages
Step 3: Update Old v2 Fields on Unchanged Addendum Pages
  • Update /ADBE_Sign with new UUIDs (must match document-level UUIDs)
  • Old sig fields (e.g., Signature Field 1b): set sig ADBE_Sign
  • Old date fields (e.g., Date of Signing 1): set date ADBE_Sign + propagate to kids
  • Old initial fields: set initials ADBE_Sign
Step 4: Create Initial Field Widgets

Three types — positions MUST be recalculated if form text shifts:

TypeParticipantsWhere
"Resident(s) to Initial: ___"P2-P5 onlyInline sections on various pages
"Resident's initials acknowledge receipt: ___"P2-P5 onlyInline sections, typically page 3 and signature page
Footer "O/A:___ R:___"P1 + P2-P5Bottom of every page (except signature page)

Each initial widget needs:

  • /FT: /Tx, /Ff: 2 (Required)
  • /ADBE_Sign: {/Assignee: UUID, /ContentType: /SIGNER_INITIALS, /InputType: /SIGNATURE}
  • CRITICAL: ContentType is /SIGNER_INITIALS (NOT /INITIALS), InputType is /SIGNATURE
Step 5: Create Signature Fields on Addendum Signature Pages
  • Pages with signature blocks but no existing sig fields need new widget annotations
  • Position between the name field and date field in each row (use existing kid widget Rects as guide)
  • Add to both page /Annots and AcroForm /Fields via writer._add_object()
Step 6: Set Empty /ADBE_Sign: {} on ALL Other Fields
  • Every non-signature, non-date, non-initial field MUST have /ADBE_Sign: {}
  • Reset /Ff from 8388608 to 0 on regular fields
  • Normalize /DA strings (fix 0 g0 0 0 rg 0 0 0 RG)
Step 7: Set Document-Level /ADBE_Sign

Add to AcroForm:

{"/ParticipantSetsInfo": [
    {"/Id": "uuid-1", "/ColorIndex": 1, "/Order": 1, "/Role": "/SIGNER"},  # P1 Owner/Agent
    {"/Id": "uuid-2", "/ColorIndex": 0, "/Order": 1, "/Role": "/SIGNER"},  # P2 Resident 1
    {"/Id": "uuid-3", "/ColorIndex": 2, "/Order": 1, "/Role": "/SIGNER"},  # P3 Resident 2
    {"/Id": "uuid-4", "/ColorIndex": 3, "/Order": 1, "/Role": "/SIGNER"},  # P4 Resident 3
    {"/Id": "uuid-5", "/ColorIndex": 4, "/Order": 1, "/Role": "/SIGNER"},  # P5 Resident 4
]}
Step 8: Remove /SigFlags from AcroForm

Must remove after converting all /Sig to /Tx.

Position Recalculation

When forms are updated, text positions shift. To find new positions:

  1. Extract text positions using pypdf visitor:
    def visitor(text, cm, tm, font_dict, font_size):
        x, y = tm[4], tm[5]
        sz = abs(tm[0])  # actual rendered font size
    
  2. Calculate field positions using Helvetica glyph widths (per 1000 units):
    • Common: space=278, colon=278, underscore=556, lowercase avg500, uppercase avg667
    • text_width = sum(char_widths) * font_size / 1000
    • Fields start right after colon+space, end where underscores end
    • Divide evenly: 4 fields for residents, 1 for O/A

Reference Positions (as of 2026 revision)

  • Footer (6pt, starts x=233.4): O/A (245,38,282,52), R fields (295-492), pages 1-9
  • "Resident(s) to Initial:" (8.5pt): fields x=138-285
  • "acknowledge receipt:" (8.5pt): fields x=209-355
  • Page 10 ack underlines: fields x=59-205 (y=251)
  • "Resident initial:" (Pet Addendum): fields x=189-329 (y=504)

Verification Checklist

  1. Upload to Acrobat Sign — should see 5 participants in sidebar
  2. Signature fields → assigned to correct participant, show as "Signature" type
  3. Date fields → show as "Date of signing", auto-fill on sign
  4. Initial fields → show as "Initials" (NOT "Text")
  5. Initials positioned within grey underline boxes
  6. Footer O/A + R initials on every page (except signature page)

Common Gotchas

  1. Shared objects bug: After writer.append(), page annots and AcroForm fields are the SAME Python objects. Track id() to avoid double-processing which overwrites ADBE_Sign with {}.
  2. Hierarchical date fields: Date fields have parent→kids structure across pages. ADBE_Sign MUST be propagated to ALL kid widgets, not just the parent.
  3. UUIDs must be consistent: All fields for the same participant must share one UUID, matching document-level ParticipantSetsInfo.
  4. /ADBE_Sign is invisible in Acrobat Pro — only verifiable by uploading to Acrobat Sign or reading programmatically.
  5. Footer position shifts between form revisions — always re-extract text and recalculate.
  6. Page count changes shift ALL page references — verify page-to-form mapping first.

When Helping Users

  1. Ask what tool they're using (Acrobat Pro, pypdf, pdftk, etc.) to give targeted advice
  2. Ask if e-sign is involved - this changes field naming requirements
  3. Recommend dumping field names first before trying to fill a form programmatically
  4. Warn about appearance streams - the most common issue with programmatic form filling
  5. Test round-trip - always verify filled PDFs open correctly in Acrobat Reader
  6. Consider flattening - recommend flattening when forms should not be edited after filling

Tools to Use

When appropriate:

  • Read: Examine PDF files or Python/code files for form manipulation
  • Bash: Run pdftk, pypdf scripts, or other CLI tools to inspect/fill forms
  • WebSearch: Look up Acrobat Sign API docs or specific field property details
  • Write: Create scripts for form filling or generate form templates

Source

git clone https://github.com/austinfox/claude-plugins/blob/main/plugins/adobe-acrobat-form/skills/adobe-acrobat-form/SKILL.mdView on GitHub

Overview

An expert in creating, modifying, filling, and validating AcroForm fields, with knowledge of JavaScript actions and e-signature workflows using Adobe Acrobat Sign. This skill helps automate PDF form workflows, ensure data integrity, and streamline collecting signatures.

How This Skill Works

It leverages Acrobat form field types (/Tx, /Btn, /Ch, /Sig) and key properties (/T, /DV, /V, /Ff) to define and populate fields. It also covers naming conventions (flat, hierarchical, and array-style) and uses programmatic tools like pypdf and pdftk to fill, verify, and flatten forms for consistent appearances and secure final PDFs.

When to Use It

  • When designing or editing PDFs with fillable fields (text, checkboxes, radio buttons, dropdowns) and you need precise field types and properties.
  • When you need to programmatically fill forms from external data sources (CSV, JSON, databases) to automate document generation.
  • When you want to flatten filled forms to lock values and prevent further edits before sharing or signing.
  • When preparing documents for e-signatures with Adobe Acrobat Sign and coordinating signer fields and order.
  • When exploring form structure by discovering field names and attributes (using get_fields or pdftk) to map data correctly.

Quick Start

  1. Step 1: Inspect the form and list field names (e.g., using get_fields or pdftk dump_data_fields).
  2. Step 2: Fill fields with your preferred tool (pypdf update_page_form_field_values or pdftk fill_form) using a data map.
  3. Step 3: Optional - flatten to lock values and, if needed, set up Adobe Acrobat Sign e-sign workflows.

Best Practices

  • Match each input to the appropriate field type (Text for text data, /Btn for boolean, /Ch for choices) and configure sensible flags (required, multi-line, read-only) as needed.
  • Use hierarchical or array-style naming for related fields (e.g., tenant.first_name, signer.0.name) to keep complex forms organized.
  • Validate fields with a test fill and verify appearance streams; use auto_regenerate to maintain visual accuracy when updating values.
  • After filling, consider flattening or locking fields to prevent edits, especially before distribution or e-signature submission.
  • Regularly discover and audit field names and properties during form maintenance to avoid breakages when templates are updated.

Example Use Cases

  • Fill tenant_name and lease_start with pypdf's update_page_form_field_values and preserve appearances.
  • Flatten a filled form by marking widgets read-only, preventing further edits before sending for e-sign.
  • Discover field metadata with PdfReader.get_fields to map data sources to field names.
  • Dump field names and types using pdftk dump_data_fields to troubleshoot form structures.
  • Fill a form from an FDF file using pdftk and output a ready-to-sign PDF.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers