ADA PDF Remediation Prompt for UC Compliance — Free AI + CLT
Python script: ada_tag_pdf.py
Tool overview and workflow
╔══════════════════════════════════════════════════════════════════════╗ ║ ADA PDF REMEDIATION — FREE-TIER VERSION (Two-Step) ║ ║ WCAG 2.1 AA / PDF/UA Compliance ║ ║ ║ ║ This version splits the work into two steps so you can use a ║ ║ FREE AI tier (no code execution needed) for the expensive part ║ ║ (vision analysis + alt-text generation), then run a local Python ║ ║ script to inject the tags into the PDF. ║ ║ ║ ║ Compatible free tiers for Step 1: ║ ║ • ChatGPT Free (GPT-4o with vision) ║ ║ • Google Gemini Free (with PDF upload) ║ ║ • Claude Free (with PDF upload) ║ ║ • Any AI with vision that can read uploaded PDFs ║ ║ ║ ║ • Python 3.9+ ║ ║ • pip install pikepdf ║ ║ ║ ║ FILES IN THIS PACKAGE: ║ ║ 1. This file — contains the prompt to paste (Step 1) ║ ║ 2. ada_tag_pdf.py — the local script to run (Step 2) ║ ║ ║ ║ WORKFLOW: ║ ║ Step 1: Copy the prompt below → paste into AI → upload PDF ║ ║ → AI returns a JSON element catalog → save as .json ║ ║ Step 2: python ada_tag_pdf.py input.pdf elements.json ║ ║ → produces input_ada.pdf (fully tagged) ║ ║ ║ ║ NOTE: I have had trouble getting ZotGPT to cooperate with this ║ ║ command as it runs out of tokens before rendering the json, ║ ║ even when setting the token limit to its maximum. I have access ║ ║ to Gemini Pro via my UCI Google Account, at no cost to me, and ║ ║ found that to handle the json creation quickly, and without ║ ║ hitting token limits or other issues. ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════╝
Step 1 instructions
STEP 1 INSTRUCTIONS:
1. Copy the prompt below using the button (or select it manually).
2. Paste it into a new conversation with any AI that supports
vision and PDF uploads (including free tiers).
3. Upload your PDF in the same message or immediately after.
4. The AI will return a JSON block. Copy that JSON and save it
to a file (e.g., elements.json).
5. Proceed to Step 2: python ada_tag_pdf.py input.pdf elements.json
(updated April 21, 2026)
AI prompt text to copy
You are an ADA/Section 508 accessibility analyst. I am uploading a PDF that needs to be made accessible to comply with WCAG 2.1 Level AA and PDF/UA (ISO 14289) standards for a University of California campus accessibility review.
Your task is ANALYSIS ONLY — you do not need to write or run any code. Examine every page of this PDF and identify every visual element that requires alternative text for screen reader accessibility, AND identify the document's heading hierarchy so the downstream pipeline can build a properly nested structure tree.
---
### WHAT TO IDENTIFY
Examine each page and find every instance of:
- **Headings** — any rendered text that visually functions as a title, section header, or subheading. This includes the document/cover title, per-page slide titles, and subsection headings within a page. Classify each with a heading level (see "HEADING HIERARCHY" below). Campus accessibility checkers (including Panorama) will fail the PDF if headings are missing, unnested, or empty, so treat this category as mandatory — every content page with a visible title needs a heading entry.
- **Equations** — inline or display math, formulas, chemical notation, any symbolic expression that is not plain text. This includes LaTeX-rendered equations, equation images from tools like LaTeXiT or MathType, and handwritten formulas. **Classify these as `Formula`, not `Figure`** — the downstream pipeline uses this type to create the correct PDF/UA structure element (`/Formula` vs `/Figure`).
- **Figures** — photographs, illustrations, diagrams, flowcharts, circuit diagrams, maps, schematics, or any non-text visual content that conveys information.
- **Plots and charts** — line plots, bar charts, scatter plots, histograms, pie charts, box plots, or any data visualization.
- **Images** — photographs, logos, icons, clip art, screenshots, or QR codes.
- **Tables rendered as images** — tables that are graphical rather than text-based.
- **Rendered prose blocks** — paragraphs of body text rendered as graphics (common in Keynote/LaTeXiT decks where every text block is a separate rendered element). Classify as `Paragraph`.
Do NOT flag purely decorative backgrounds (e.g., a solid-color slide background) or plain text that is already extractable (non-rendered body text in a Word/LaTeX-native PDF).
**IMPORTANT — Slides with mixed equation and prose content:** Presentation tools like
Keynote with LaTeXiT render every text block (equations, bullet points, headings, prose
paragraphs) as a separate graphical element in the PDF. On a typical lecture slide, you
might see 5–8 distinct graphical regions: a heading, two prose paragraphs, and three
equations. You must identify **every** visually distinct rendered block, not just the ones
that look like traditional "figures." If a slide has a heading rendered as a graphic, a
prose paragraph rendered as a graphic, and three equations — that is five elements:
one `Heading`, one `Paragraph`, and three `Formula`s.
When in doubt about whether something is a separate element, err on the side of splitting
rather than combining. The pipeline can merge entries that map to the same structural
element, but it cannot split an entry that was incorrectly combined.
---
### HEADING HIERARCHY
The downstream pipeline builds a WCAG 2.1-compliant heading tree. Your classification must
respect these rules:
1. **Exactly one `level: 1` heading per document.** This is the document title — usually
on a dedicated title/cover slide, or the most prominent heading on the first page. If
the first content page IS the title page (no separate cover), mark that slide's title
as `level: 1` and do NOT also mark it as `level: 2`.
2. **Per-page slide/section titles are `level: 2`** on every page *except* the one page
where the `level: 1` heading lives.
3. **Subheadings within a page** (e.g., a smaller bold subtitle under the slide title) are
`level: 3`. Deeper nesting (`level: 4`, `level: 5`) is rare in lecture material but is
allowed if visually warranted.
4. **No level skips.** Each heading's `level` may be at most one greater than the most
recent heading's level in document order. `H1 → H2` is fine; `H1 → H3` is not. If a
visual subheading looks like it should be H3 but there is no H2 above it, classify it
as H2 instead.
5. **Pages with no visible title** (transition slides, pure-image pages, Q&A slides) get
no heading entry. Skipping one page is fine; inventing an empty heading is worse than
omitting it.
6. **If the document has no visible title at all** (e.g., a problem set that starts with
"Problem 1" with no cover), promote the first rendered heading to `level: 1` and
classify the rest as `level: 2`. Do not invent an H1 that isn't in the rendered output.
---
### HOW TO DESCRIBE EACH ELEMENT
For each element, provide:
1. **page** — the page number (starting from 1)
2. **index** — a sequential integer starting from 1, incrementing for every element across the entire document
3. **type** — one of: `Heading`, `Paragraph`, `Figure`, `Formula`, `Image`, `Diagram`, `Table`
- Use `Heading` for rendered title/header text (document title, slide titles, subsection headings). Always include the `level` field for Heading entries.
- Use `Paragraph` for rendered prose/body-text blocks (bullet lists, explanatory paragraphs rendered as graphics).
- Use `Formula` for any mathematical expression, equation, or symbolic notation — even short inline ones like "k = ±√(2mE/ℏ²)"
- Use `Figure` for non-mathematical visual content (diagrams, photos, plots, charts)
- Use `Image` for photographs, QR codes, logos, and screenshots
- Use `Diagram` for flowcharts, circuit diagrams, or schematic layouts
4. **level** — integer 1–6, **required when `type` is `Heading`**, omit otherwise. See "HEADING HIERARCHY" above.
5. **alt_text** — a complete, screen-reader-quality description:
- For **headings**: transcribe the heading text exactly as rendered, preserving original capitalization and punctuation. No prose description — just the text. The pipeline uses this string to locate the matching MCID in the content stream.
- For **paragraphs**: transcribe the text content verbatim. If the block is a bullet list, join bullets with newlines or semicolons in the order they appear.
- For **equations/formulas**: write the math in spoken English (e.g., "i h-bar times partial derivative of Psi with respect to t equals H-hat times Psi") AND include a LaTeX representation after the prefix `LaTeX:`.
- For **plots/charts**: describe the axes, units, data trend, and key quantitative takeaway. Include specific numbers visible on the chart.
- For **figures/diagrams**: describe what is depicted, its spatial layout, any labels, and its purpose in context.
- For **photographs**: describe what is shown and why it is relevant.
- For **QR codes**: note that it is a QR code and state the URL if visible on the slide.
- Alt-text for non-heading, non-paragraph elements should be 1–4 sentences. A screen reader user should fully understand the content without seeing the image.
6. **position** — the approximate vertical position of this element on the page: `"top"`, `"upper"`, `"middle"`, `"lower"`, or `"bottom"`. This helps the downstream pipeline match entries to the PDF's internal content stream order. Use `"top"` for slide titles / page headings.
Also provide:
7. **title** — a short document title derived from the PDF content (e.g., course name + lecture topic). Provide this ONCE, before the elements list. This should match (or closely match) the alt_text of the `level: 1` heading entry.
---
### ELEMENT COUNTING GUIDANCE
To help the downstream pipeline match your analysis to the PDF's internal structure:
- **Count every visually distinct rendered block on each page.** On a typical lecture slide
with a heading, two bullet-point blocks, and three equations, you should produce 6 entries
for that page — one `Heading`, two `Paragraph`s, three `Formula`s.
- **Equations that appear as a single visual group** (e.g., two lines of a derivation with
no text between them) should be treated as **one** element.
- **Bullet point lists** rendered as a single graphical block are **one** `Paragraph`
element, not one per bullet.
- A **page-level fallback alt-text** entry (type `Figure`, covering the whole page) is
acceptable when the page layout is too complex to reliably decompose into individual
elements. Mark it with `"position": "full_page"`. The downstream pipeline will apply
this alt-text to all figure elements on that page. A full-page fallback does NOT exempt
the page from having a `Heading` entry — always include the page title as a separate
`Heading` entry even when you use a full_page fallback for the rest.
---
### RESPONSE FORMAT
Respond with ONLY a single JSON code block — no commentary before or after. Use this exact structure:
```json
{
"title": "Physics 113A — Lecture 14: The Schrödinger Equation",
"elements": [
{
"page": 1,
"index": 1,
"type": "Heading",
"level": 1,
"position": "top",
"alt_text": "Physics 113A — Lecture 14: The Schrödinger Equation"
},
{
"page": 2,
"index": 2,
"type": "Heading",
"level": 2,
"position": "top",
"alt_text": "Time-Dependent Form"
},
{
"page": 2,
"index": 3,
"type": "Formula",
"position": "middle",
"alt_text": "The Schrödinger equation states that i h-bar times the partial derivative of Psi with respect to t equals H-hat times Psi. LaTeX: i\\hbar\\frac{\\partial\\Psi}{\\partial t} = \\hat{H}\\Psi"
},
{
"page": 2,
"index": 4,
"type": "Paragraph",
"position": "lower",
"alt_text": "Here H-hat is the Hamiltonian operator, which for a single particle in a potential V(x) takes the form of the kinetic energy operator plus V(x)."
},
{
"page": 3,
"index": 5,
"type": "Heading",
"level": 2,
"position": "top",
"alt_text": "Infinite Square Well"
},
{
"page": 3,
"index": 6,
"type": "Figure",
"position": "middle",
"alt_text": "A diagram showing a particle in a one-dimensional infinite square well potential. The potential is zero between x=0 and x=L, and infinite outside that region. The first three energy eigenfunctions are drawn as sinusoidal waves inside the well."
},
{
"page": 3,
"index": 7,
"type": "Heading",
"level": 3,
"position": "lower",
"alt_text": "Boundary Conditions"
},
{
"page": 4,
"index": 8,
"type": "Image",
"position": "middle",
"alt_text": "QR code linking to https://phet.colorado.edu/en/simulation/quantum-tunneling"
}
]
}
```
Note in the example above:
- Exactly one `level: 1` heading (the document title, on page 1).
- Page 2 and page 3 each have a `level: 2` heading at the top.
- Page 3 has a `level: 3` subheading ("Boundary Conditions") — allowed because the most recent heading above it was `level: 2`.
- Page 4 has no heading entry (image-only page).
- No heading skips a level.
If a page has NO rendered elements (plain extractable text only), do not include any entries for that page.
If the PDF has NO rendered elements at all and no visible headings, respond with:
```json
{
"title": "Document title",
"elements": []
}
```
Remember: respond with JSON only. No introduction, no summary, no markdown outside the code block.