Skip to content

Adding pdf support for uploading pdf's, analyzing them, and then create pdf-first forms with supplemental pdf analysis.#35

Open
travist wants to merge 13 commits into
mainfrom
adding-pdf-support
Open

Adding pdf support for uploading pdf's, analyzing them, and then create pdf-first forms with supplemental pdf analysis.#35
travist wants to merge 13 commits into
mainfrom
adding-pdf-support

Conversation

@travist

@travist travist commented Jul 14, 2026

Copy link
Copy Markdown
Member

No description provided.

@blakekrammes blakekrammes self-requested a review July 14, 2026 17:40
new Blob([new Uint8Array(pdfBytes)], { type: 'application/pdf' }),
basename(filePath)
);
const uploaded = await formioFetch('pdf-proxy/upload', {}, cfg, {

@blakekrammes blakekrammes Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to use the pdf_upload tool with this endpoint.

I had to modify it to just use upload instead of the pdf proxy path pdf-proxy/upload.

Our docs on the PDF upload endpoint say to use {{ baseUrl }}/{{ projectName }}/upload https://apidocs.form.io/#ca22c9a7-7d6f-c4b0-627a-b17ce8cbb632

vs listing uploaded PDFs {{ baseUrl }}/pdf-proxy/pdf/{{ projectId }}/file https://apidocs.form.io/#88cf61ec-2cbc-ced8-2d04-96c7be0ad0d3

Am I missing something?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error in the API server

TypeError: Only absolute URLs are supported
    at getNodeRequestOptions (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1327:9)
    at /Users/blakekrammes/projects/nirvana/node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1440:19
    at new Promise (<anonymous>)
    at fetch (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1437:9)
    at /Users/blakekrammes/projects/nirvana/apps/formio-server/src/middleware/pdfProxy/router.js:114:30
    at Layer.handle [as handle_request] (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/express@4.22.1/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/express@4.22.1/node_modules/express/lib/router/index.js:328:13)
    at /Users/blakekrammes/projects/nirvana/node_modules/.pnpm/express@4.22.1/node_modules/express/lib/router/index.js:286:9
    at router.process_params (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/express@4.22.1/node_modules/express/lib/router/index.js:346:12)
    at next (/Users/blakekrammes/projects/nirvana/node_modules/.pnpm/express@4.22.1/node_modules/express/lib/router/index.js:280:10)

**Structural pass (script, deterministic).** Dump every AcroForm field: fully-qualified name, field type, required flag, choice options, tooltip, page, and rect. Tooltips (`/TU`) are the highest-quality label source — PDF authors put the human label there.

```bash
python3 - "$PDF_PATH" <<'EOF'

@blakekrammes blakekrammes Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I ran this skill, claude took some liberties with this script:

 import json, sys
   from pypdf import PdfReader
   reader = PdfReader(sys.argv[1])
   dump = []
   for page_number, page in enumerate(reader.pages, start=1):
       for annotation in page.get('/Annots') or []:
           field = annotation.get_object()
           if field.get('/Subtype') == '/Widget':
               name = field.get('/T')
               parent = field.get('/Parent')
               while parent is not None:
                   p = parent.get_object()
                   pt = p.get('/T')
                   if pt:
                       name = f"{pt}.{name}" if name else pt
                   parent = p.get('/Parent')
               ft = field.get('/FT')
               ff = field.get('/Ff')
               if ft is None and field.get('/Parent'):
                   p = field['/Parent'].get_object()
                   ft = p.get('/FT')
                   ff = ff or p.get('/Ff')
               dump.append({
                   'name': name,
                   'fieldType': str(ft) if ft else None,
                   'required': bool(int(ff or 0) & 2),
                   'options': field.get('/Opt'),
                   'tooltip': field.get('/TU'),
                   'page': page_number,
                   'rect': [round(float(v), 1) for v in field.get('/Rect')],
               })
   print(f"dumped {len(dump)} widgets", file=sys.stderr)
   json.dump(dump, sys.stdout, indent=1, default=str)

@blakekrammes blakekrammes Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like claude retries/thrashes a bit trying to find tooltips.

And then it writes an ad hoc python script to the tmp folder, which it runs to enrich the pdf fields.

It'd be nice if it showed a deterministic TUI summary of the field mappings after enrichment.

Even if I ask claude to edit some of mappings, it doesn't show me the list of fields and their mappings (I can see them in the python script it created) in the TUI.

Screenshot 2026-07-15 at 2 37 48 PM

This is a really cool feature. It feels a little indeterministic to me though. It seems like claude will take liberties with the python scripts in the skill and come up with new ones on the fly. It'd be nice if this was more standardized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants