Skip to content

Add accessibility checks, CSV export, and fixture PDFs - #126

Merged
jsnmrs merged 17 commits into
mainfrom
updates
Oct 14, 2026
Merged

jsnmrs merged 17 commits into
mainfrom
updates

Conversation

@jsnmrs

@jsnmrs jsnmrs commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds eight regex-based checks, a disclaimer, CSV export, and two handwritten fixture PDFs. Also fixes three pre-existing issues found while reviewing the checks.

New checks

  • PDF version note. The existing PDF Version flag warns when the effective version is below 1.7, which PDF/UA-1 requires. A catalog /Version entry that raises the header version is honored.
  • Suspects flag. Warns when the mark information dictionary sets Suspects to true, and stays silent otherwise.
  • Bookmarks. Reports whether the catalog references an outline. No count is shown because the first /Count in a file is usually the page tree.
  • Form fields. Appears only when the file has an AcroForm, and compares the number of fields with the number of TU descriptions.
  • Headings, lists, tables, and figures. Count structure tags and warn on a missing H1, tables without TH cells, and figures with fewer alt text entries than figure tags.

The structure checks run only when the tag tree is readable as plain text. Producers that store it in compressed object streams, including Acrobat and Word, get a single Structure flag saying it could not be inspected, instead of false warnings. Every structure regex requires the /S key so names in other dictionaries can't match. Without that, /L in the linearization dictionary made every linearized file report a list.

Other changes

  • A disclaimer above the file picker saying the tool detects missing features but can't confirm a feature is correct.
  • CSV export. Every flag is recorded as it renders, and a Download results as CSV button appears when checks finish. Cells are quoted, values that start with a formula character get a leading apostrophe, and the file starts with a byte order mark.
  • Two uncompressed fixture PDFs in examples/, one that passes each new check and one that fails each. No existing example contained forms, tables, figures, or a Suspects flag.
  • Help text on the page for every new check, and README updates.
  • Status labels. Each failure flag ends with (failure) and each warning flag with (inspect), so status no longer depends on background color alone (WCAG 1.4.1). The visually hidden ::after text that only screen readers received is replaced by this visible text, so nothing is announced twice. Success and default flags carry no label. File-level error flags now wrap their text in a span, matching labeled flags, so the label gets the same spacing.

Fixes to existing behavior

  • The Tagged flag showed the StructTreeRoot object number as a tag count. It now reports Yes or No.
  • The Marked regex required Marked to be the first key in its dictionary with exactly one space. It now matches any layout.
  • The flag renderer kept two link parameters that nothing read. Renamed to addLabeledFlag with status, label, and value.

Verification

  • Ran the check functions in Node against all 14 example files and compared with the expected matrix.
  • The eight existing flags are byte-identical to main across the 12 original files.
  • Loaded the page in a browser, uploaded all 14 files, confirmed the rendered output matched, downloaded the CSV, and ran axe with zero violations.
  • Checked the status labels at desktop and phone widths with a sample PDF and a non-PDF file. Each label appears once in the accessibility tree after the value, default flags expose nothing extra, and axe reported zero violations with results rendered.

jsnmrs added 16 commits October 13, 2026 18:08
Two small uncompressed PDFs that exercise checks none of the existing examples can reach: headings, lists, tables, figure alt text, bookmarks, form field descriptions, and the Suspects flag. One passes each check and one fails each check.
The PDF Version flag now warns when the effective version is below 1.7, which PDF/UA-1 requires. A catalog /Version entry that raises the header version is honored. A new Suspects check warns when the mark information dictionary sets Suspects to true, and stays silent otherwise.
Bookmarks reports whether the catalog references an outline. No count is shown because the first /Count in a file is usually the page tree. Form Fields appears only when the file has an AcroForm, and compares the number of fields with the number of TU descriptions. Adds countMatches and pluralize helpers for the structure checks to share.
Structure checks run only when the file is tagged and the tag tree is readable as plain text. Producers that store the tag tree in compressed object streams get a single Structure flag saying it could not be inspected, instead of false warnings. Every structure regex requires the /S key so names in other dictionaries, such as /L in a linearization dictionary, can't match. Headings warns when no H1 is present, tables warns when no TH cells are present, and figures warns when alt text entries are fewer than figure tags.
Short callout above the file picker saying the tool detects missing features but can't confirm a feature is correct, and naming PAC 2024, Acrobat's checker, and a screen reader as the next steps.
Every flag is also recorded in a results array as it is rendered, and a Download results as CSV button appears once checks finish. Cells are quoted, inner quotes doubled, and values that start with a formula character get a leading apostrophe because titles and producer strings come from the PDF. The file starts with a byte order mark so spreadsheets read accented characters correctly.
README lists the new checks under features, results, and visual states, adds the export step to usage, and replaces two limitations that are now partly covered with accurate wording about compressed tag trees and unvalidated tags. The example result on the page gains the Bookmarks and Structure rows the PAC 2024 file now produces.
The regex assumed Marked was the first key in the mark information dictionary with exactly one space before the value. A producer that writes Type first or uses other whitespace reported Marked as missing.
The number shown was the StructTreeRoot object number captured by the regex, not a count of tags. There is no reliable count to put in its place, because most producers compress the tag tree, so the check now reports Yes or No. The regex also requires the full indirect reference so a stray mention of the key can't match.
addFlagWithLink kept linkHref and linkText parameters that nothing read after result links were removed, and every caller passed the label twice. Renamed to addLabeledFlag with status, label, and value.
Status was conveyed to sighted users only by background and border color, which fails WCAG 1.4.1 Use of Color. The visually hidden ::after text that already existed for screen readers is now visible and reads (pass), (warning) or (failure). Default flags remain unlabeled because they carry neutral information.

addFlag now wraps its content in a span, matching addLabeledFlag, so the label gets the same spacing on file-level error flags. The data-status values and the CSV export are unchanged.
Warning flags are labeled (inspect) and success flags carry no label, so the README now describes those states the same way the page renders them.
@jsnmrs
jsnmrs marked this pull request as ready for review October 14, 2026 23:52
@jsnmrs jsnmrs closed this Oct 14, 2026
@jsnmrs jsnmrs reopened this Oct 14, 2026
@jsnmrs

jsnmrs commented Oct 14, 2026

Copy link
Copy Markdown
Owner Author

Actions needed a bump

@github-actions

Copy link
Copy Markdown
Contributor

Accessibility scan: all applicable checks passed

To see scan details, download the accessibility report.

Scan summary

  • URLs: 1 URL(s) passed, and 0 were not scannable
  • Rules: 19 check(s) passed, and 36 were not applicable

This scan used axe-core 4.11.3 with Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/155.0.0.0 Safari/537.36 and browser resolution 1920x1080.

The help sidebar showed a document title as success and a missing title as a warning, while the checks report them as default and failure. The Language check also emitted a lowercase value that no other check uses. The examples, the README, and the Language value now agree with the rendered output.
@github-actions

Copy link
Copy Markdown
Contributor

Accessibility scan: all applicable checks passed

To see scan details, download the accessibility report.

Scan summary

  • URLs: 1 URL(s) passed, and 0 were not scannable
  • Rules: 19 check(s) passed, and 36 were not applicable

This scan used axe-core 4.11.3 with Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/155.0.0.0 Safari/537.36 and browser resolution 1920x1080.

@jsnmrs
jsnmrs merged commit 6479121 into main Oct 14, 2026
1 check passed
@jsnmrs
jsnmrs deleted the updates branch October 14, 2026 23:59
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.

1 participant