pdfpythonverified

pdf fillable form skill

reportlab helpers for a one-page fillable AcroForm PDF: drawn title, labelled real text-field widgets with exact names, and a real checkbox widget.

rec_0b825e5402a34e4da7f7c044b7d1e56d · banked 2026-09-04 · by neruva
SKILL.md
Certificate

A program checked this on cases it had never seen. You can run it.

Download the checker
Model alone
44%
With this skill
100%
Near-misses it rejects
5/5
Signed
ed25519
What was checked
  • Output built to the specification is accepted.
  • Each of these deliberate breaks is rejected: missing_field, checkbox_to_text, rename_field, missing_title, flatten_widgets.
  • An empty file is rejected.
What was not
  • Anything a person would call taste: layout, tone, whether it looks good. No program can check that, and this one does not claim to.
  • Behaviour outside the specification's clauses.
  • Inputs the checker was never given. See the evidence line for the corpus.
Scorecard

A one-page fillable PDF: a drawn title, labelled real text-field widgets with exact names, and a real checkbox.

The model on its own
44%16 unseen cases
The same model with this skill
100%deepseek-chat
A frontier model on its own
100%same cases
Cost per task with the skill
0.146 cents20x cheaper than the frontier model
Smallest model measured
Qwen3-0.6B, on a laptop, no API16/16
Cost to build it once
$0.15
Checked by
hand-written, opens the PDF and reads the form fields back
Where it does not apply
  • Builds a form. It does not read an existing scanned one.
  • XFA forms are out of scope.

Every figure comes from one run, kept in the repository as probe6b_pdf_form_result.json.

Evidence

  • held-out 16 form specs, best-of-2: deepseek-chat cold 0.438 -> 1.000 with rung (CI +0.375..+0.750), 0.15c/form vs Sonnet cold 1.000 at 2.92c; forged by Sonnet in one round for $0.97 incl. generality checks

Use it

# in Claude Code (MCP tools from neruva-mcp)
rung_search(q="pdf_fillable_form_rungs")
rung_install(id="rec_0b825e5402a34e4da7f7c044b7d1e56d", dir="~/.claude/skills")
# the skill folder is now loaded like any other skill

Usage guide

What the model reads to call the skill. This is the whole interface.

Show the guide
HELPER FUNCTIONS REFERENCE

1. create_form_canvas(output_path, page_size=None)
   Returns: (canvas.Canvas, width, height) — a reportlab canvas ready for AcroForm widgets.
   Example:
     c, w, h = create_form_canvas("out.pdf")

2. draw_title(c, text, x, y, font="Helvetica-Bold", size=18)
   Draws a title string on the canvas at (x, y).
   Returns: y (float), the same y passed in (for layout chaining).
   Example:
     draw_title(c, "Intake Form", 72, 720)

3. draw_label(c, text, x, y, font="Helvetica", size=12)
   Draws a plain text label (e.g. next to a field) on the canvas.
   Returns: y (float).
   Example:
     draw_label(c, "Email:", 72, 650)

4. add_text_field(c, name, x, y, width, height, value="", font="Helvetica", font_size=12, border=True, tooltip=None)
   Adds a real fillable AcroForm text-field widget named exactly `name`.
   Returns: None (mutates the canvas/PDF).
   Example:
     add_text_field(c, "email", 220, 645, 250, 20)

5. add_checkbox_field(c, name, x, y, size=12, checked=False, tooltip=None, border=True)
   Adds a real fillable AcroForm checkbox widget named exactly `name`.
   Returns: None.
   Example:
     add_checkbox_field(c, "agree_terms", 72, 550, size=14)

6. save_form(c)
   Finalizes and writes the PDF to disk.
   Returns: None.
   Example:
     save_form(c)

7. build_one_page_form(output_path, title, text_fields, checkbox_fields=None, page_size=None, left_margin=72, top_margin=72, row_height=40, label_width=140, field_width=250, field_height=20, title_font="Helvetica-Bold", title_size=20, label_font="Helvetica", label_size=12)
   High-level builder: draws a title, then auto-lays-out a column of labelled text fields (list of dicts with 'name' and 'label', optional 'x'/'y'/'width'/'height' overrides), then any checkbox fields (list of dicts with 'name', optional 'label'/'x'/'y'/'size'/'checked'). Writes the finished PDF to output_path.
   Returns: None. Writes PDF to output_path.
   Example:
     build_one_page_form(
         "out.pdf",
         title="Intake Form",
         text_fields=[{"name": "employee_id", "label": "Employee ID:"}],
         checkbox_fields=[{"name": "agree_terms", "label": "I agree"}],
     )

Code

207 lines of python, hashed and signed below.

Show the code
import os

def create_form_canvas(output_path, page_size=None):
    """Create a reportlab Canvas configured for building an AcroForm PDF.

    Args:
        output_path (str): path to write the PDF to.
        page_size (tuple(float,float), optional): (width, height) in points. Defaults to letter.

    Returns:
        (canvas.Canvas, float, float): the canvas object, page width, page height.
    """
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import letter
    if page_size is None:
        page_size = letter
    c = canvas.Canvas(output_path, pagesize=page_size)
    width, height = page_size
    return c, width, height


def draw_title(c, text, x, y, font="Helvetica-Bold", size=18):
    """Draw a title string on the canvas.

    Args:
        c (canvas.Canvas): the canvas to draw on.
        text (str): the title text.
        x (float): x coordinate (points from left).
        y (float): y coordinate (points from bottom).
        font (str): font name. Defaults to Helvetica-Bold.
        size (int): font size. Defaults to 18.

    Returns:
        float: the y coordinate used (for chaining layout).
    """
    c.setFont(font, size)
    c.drawString(x, y, text)
    return y


def draw_label(c, text, x, y, font="Helvetica", size=12):
    """Draw a plain text label on the canvas (e.g. next to a form field).

    Args:
        c (canvas.Canvas): the canvas to draw on.
        text (str): the label text.
        x (float): x coordinate (points from left).
        y (float): y coordinate (points from bottom).
        font (str): font name. Defaults to Helvetica.
        size (int): font size. Defaults to 12.

    Returns:
        float: the y coordinate used (for chaining layout).
    """
    c.setFont(font, size)
    c.drawString(x, y, text)
    return y


def add_text_field(c, name, x, y, width, height, value="", font="Helvetica",
                    font_size=12, border=True, tooltip=None):
    """Add a real fillable AcroForm text field widget to the current page.

    Args:
        c (canvas.Canvas): the canvas (must have .acroForm, standard on reportlab Canvas).
        name (str): exact field name that will appear in the PDF's AcroForm.
        x (float): x coordinate of the field's lower-left corner.
        y (float): y coordinate of the field's lower-left corner.
        width (float): field width in points.
        height (float): field height in points.
        value (str): initial value of the field. Defaults to "".
        font (str): font used inside the field. Defaults to Helvetica.
        font_size (int): font size inside the field. Defaults to 12.
        border (bool): whether to draw a visible border. Defaults to True.
        tooltip (str, optional): tooltip text; defaults to the field name.

    Returns:
        None
    """
    c.acroForm.textfield(
        name=name,
        tooltip=tooltip or name,
        x=x, y=y, width=width, height=height,
        value=value,
        fontName=font,
        fontSize=font_size,
        borderStyle='inset' if border else None,
        borderWidth=1 if border else 0,
        forceBorder=border,
    )


def add_checkbox_field(c, name, x, y, size=12, checked=False, tooltip=None, border=True):
    """Add a real fillable AcroForm checkbox widget to the current page.

    Args:
        c (canvas.Canvas): the canvas (must have .acroForm).
        name (str): exact field name that will appear in the PDF's AcroForm.
        x (float): x coordinate of the checkbox's lower-left corner.
        y (float): y coordinate of the checkbox's lower-left corner.
        size (float): width/height of the checkbox in points. Defaults to 12.
        checked (bool): initial checked state. Defaults to False.
        tooltip (str, optional): tooltip text; defaults to the field name.
        border (bool): whether to draw a visible border. Defaults to True.

    Returns:
        None
    """
    c.acroForm.checkbox(
        name=name,
        tooltip=tooltip or name,
        x=x, y=y, size=size,
        checked=checked,
        borderStyle='inset' if border else None,
        borderWidth=1 if border else 0,
        forceBorder=border,
    )


def save_form(c):
    """Finalize and write the PDF to disk.

    Args:
        c (canvas.Canvas): the canvas to save.

    Returns:
        None
    """
    c.save()


def build_one_page_form(output_path, title, text_fields, checkbox_fields=None,
                         page_size=None, left_margin=72, top_margin=72,
                         row_height=40, label_width=140, field_width=250,
                         field_height=20, title_font="Helvetica-Bold", title_size=20,
                         label_font="Helvetica", label_size=12):
    """Build a one-page fillable AcroForm PDF: a drawn title, a column of
    labelled real text-field widgets, and optional real checkbox widgets.
    General enough to build any single-page form of this shape.

    Args:
        output_path (str): path to write the resulting PDF to.
        title (str): title text drawn at the top of the page.
        text_fields (list[dict]): each dict describes one text field row:
            {"name": str (exact AcroForm field name, required),
             "label": str (visible label text, required),
             "x": float (optional, overrides auto layout, label position),
             "y": float (optional, overrides auto layout row position),
             "width": float (optional, field width, default field_width),
             "height": float (optional, field height, default field_height)}
        checkbox_fields (list[dict], optional): each dict describes one checkbox:
            {"name": str (exact AcroForm field name, required),
             "label": str (optional visible label text drawn to the right),
             "x": float (optional, overrides auto layout),
             "y": float (optional, overrides auto layout),
             "size": float (optional checkbox size, default 14),
             "checked": bool (optional initial state, default False)}
        page_size (tuple, optional): (width, height) in points. Defaults to letter.
        left_margin (float): left margin in points. Defaults to 72.
        top_margin (float): distance from top of page to the title baseline. Defaults to 72.
        row_height (float): vertical spacing between auto-laid-out rows. Defaults to 40.
        label_width (float): horizontal space reserved for each label before its field. Defaults to 140.
        field_width (float): default text field width. Defaults to 250.
        field_height (float): default text field height. Defaults to 20.
        title_font (str): font for the title. Defaults to Helvetica-Bold.
        title_size (int): font size for the title. Defaults to 20.
        label_font (str): font for labels and field text. Defaults to Helvetica.
        label_size (int): font size for labels and field text. Defaults to 12.

    Returns:
        None. Writes the PDF to output_path.
    """
    c, width, height = create_form_canvas(output_path, page_size=page_size)

    title_y = height - top_margin
    draw_title(c, title, left_margin, title_y, font=title_font, size=title_size)

    cursor_y = title_y - row_height

    for field in text_fields:
        name = field["name"]
        label = field["label"]
        x = field.get("x", left_margin)
        y = field.get("y", cursor_y)
        fw = field.get("width", field_width)
        fh = field.get("height", field_height)
        draw_label(c, label, x, y + (fh - label_size) / 2.0 + 2, font=label_font, size=label_size)
        field_x = x + label_width
        add_text_field(c, name, field_x, y, fw, fh, font=label_font, font_size=label_size)
        if "y" not in field:
            cursor_y -= row_height

    if checkbox_fields:
        for cb in checkbox_fields:
            name = cb["name"]
            label = cb.get("label", "")
            x = cb.get("x", left_margin)
            y = cb.get("y", cursor_y)
            size = cb.get("size", 14)
            if label:
                draw_label(c, label, x + size + 8, y, font=label_font, size=label_size)
            add_checkbox_field(c, name, x, y, size=size, checked=cb.get("checked", False))
            if "y" not in cb:
                cursor_y -= row_height

    save_form(c)

Certificate

Code sha256 56711152a527374451c086569617afb98cccb2eb6f75e6b55fc77bc38db69d92
Signature ed25519, present
Last re-checked
passing

Re-checked today. The signature was recomputed from the code served by the API, and the certificate matched.

A certificate proves the code has not changed. Re-running the check proves it still works today, on today's libraries.