PDFCarry
PDFCarry worked example Synthetic files, automated setup, AI-assisted. Not a customer document. Not an accessibility certification. Who made this Also in this folder: plain-text article and evidence pack README

What still stands when the client sends version two

Recorded accessibility checks on a tagged PDF, before and after a revised export.

The client's re-export changed one sentence, added one paragraph, and flattened every heading to a paragraph in the tag tree. Item by item and check by check, this page shows what PDFCarry carried from the earlier review, what it reopened, and what it refused.

Upper part of page 2 of the baseline guide: the heading Preparing for a shift, the subheading Before visitors arrive, and four short paragraphs. The second reads: The weekly orientation begins at 10 am on Monday.
Baseline, page 2 Four paragraphs. Orientation at 10 am on Monday. Headings tagged H1 and H2. Every check on this version has a review record, all of them automated synthetic setup rather than a specialist's review.
Upper part of page 2 of the revision: the same heading and subheading, now five paragraphs. The orientation sentence reads 11 am on Tuesday, and a new final paragraph reads Bring the updated shift checklist to orientation.
Revision, page 2 Five paragraphs. Orientation now 11 am on Tuesday, plus a new final sentence. The headings keep their appearance, but the export tagged every one of them as a paragraph.

Upper part of each page, shown from the unaltered page previews in this folder. Full pages appear in the walkthrough below.

After the revision was imported: 58 scopes on 20 items

Item and scope. An item is one piece of content, or the document properties. A scope is one check on one item: structure, reading order, language, alternative text for a graphic, or metadata for the document. Evidence is recorded and carried per scope, so item counts and scope counts are never added together.

Items still needing a reviewer
19 of 20. Only the document properties item is fully covered.
Heading roles restored
6: H1 and H2 on each page, although the incoming file tagged them P.
Appearance
The exported PDF renders identical to the incoming revision on all three pages. Both originals are unchanged.
Machine validation
veraPDF 1.30.2, PDF/UA-1 profile: passed. That covers machine-checkable rules only. The 19 items above still need a person.

Second file, a changed table. The revision changed one number inside a table and arrived untagged. PDFCarry refused to reuse the table and carried its cells as loose items. That export also failed machine validation on three rules, not all of them about the table. Read the refusal.

Who made this. The PDFCarry product team at RYOX Sales Pty Ltd, with AI assistance. Claude (Fable) wrote and designed the walkthrough. An OpenAI Codex agent (Astra) generated the sample files and ran the engine that produced the evidence. No accessibility specialist reviewed these documents. Every review record on this page is automated setup, labelled as such in the files.

What it is not. Not a customer result, not a measured time saving, not a claim about every export producer, and not PDF/UA or WCAG certification. The engine used was the locally installed PDFCarry 0.2.2 Python engine at source commit f33848a, not a signed public installer. These results were prepared on 7 September 2026. The current Windows release has a 14-day trial and paid subscriptions; this educational example and its files remain free to inspect.

When this helps, and when it does not

Fixing the source and re-exporting is often the sensible route. A well-structured Word or InDesign file that exports correct tags removes the problem at its origin, and nothing here argues against that where it is available. This workflow is for the situation where tagging and checking happened on the PDF, the client has sent a new export, and that PDF-side work would otherwise be lost.

The first project costs real work. Someone has to review the baseline and record what they actually checked before there is anything to carry. Value appears from the second revision onward, and only when the revision stays within what the engine can match. In this example the baseline review was automated so the mechanics could be shown; in practice it is your own review time.

1. The baseline

A fictional three-page volunteer guide for a fictional library, generated with ReportLab and pikepdf so its structure is known exactly. Each page has an H1, an H2 and four paragraphs, tagged as such, with the header strip, footer and page number marked as artifacts. The document has a title, an English language declaration and a PDF/UA-1 identification in its metadata.

Page 1 of the baseline guide. A thin teal rule under a small header, then the heading Harbour Library volunteer guide, the subheading Welcome to the team, and four short paragraphs about a volunteer shift. A footer notes the content is fictional.
guide-baseline.pdf, page 1 of 3.
Page 2 of the baseline guide. Heading Preparing for a shift, subheading Before visitors arrive, and four paragraphs. The second paragraph reads: The weekly orientation begins at 10 am on Monday.
guide-baseline.pdf, page 2 of 3, the full page. The orientation sentence is the one that changes.
Pages
3
Items after import
19: document properties plus 18 text elements
Scopes after import
55, all in the state imported-baseline
Review events after import
0. Importing a tagged file creates no review records. Tags are not evidence.

2. Record what was checked

PDFCarry stores a review event when a reviewer marks a scope on an item as checked. In a real project you record only what you actually verified, item by item, and you can leave things unchecked. For this example a script recorded all 55 scopes at once so that the next step has something to carry.

That shortcut is visible in the files, not hidden. Every event names its reviewer as SYNTHETIC fixture setup - not a professional reviewer and carries the note Automated synthetic records solely to demonstrate evidence carryover; no human accessibility review occurred. Reviewer names in PDFCarry are local labels, not authenticated signatures, which is another reason the manifest quotes them rather than trusting them.

Scopes recorded
55 of 55
Review events
19, one per item

3. The revision arrives

The revised export changes three things. The orientation sentence on page 2 now reads 11 am on Tuesday instead of 10 am on Monday. A fifth paragraph is added to page 2. And the producer has tagged every element, headings included, as a paragraph. Dropped heading roles are a familiar kind of damage from a re-export, and they are the reason the baseline structure is worth carrying.

Page 2 of the revision, full page: five paragraphs under Before visitors arrive. The orientation sentence now says 11 am on Tuesday, and a new final paragraph reads Bring the updated shift checklist to orientation. Visually the headings look the same as before.
guide-revision.pdf, page 2 of 3, the full page. The heading still looks like a heading; in the tag tree it is a paragraph.

The revision is imported into the same project. The original baseline file is never altered; both originals are kept and their hashes are checked after the run.

4. Inspect what carried

This is the full ledger for the guide case: one row per item, one cell per scope, read from results.json. The bar at the top of the page is this table summed.

Guide case: 20 items, 58 scopes, after the revision was imported. Read from results.json.
Item (text as it appears in the revision) Structure Reading order Language
Document
Document properties single scope: document metadata kept
Page 1
Harbour Library volunteer guide heading role H1 restored; the revision tagged it Pkeptreopenedkept
Welcome to the team heading role H2 restoredkeptreopenedkept
This fictional guide explains a small library volunteer shift.keptreopenedkept
A coordinator welcomes each volunteer at the front desk.keptreopenedkept
Read the shift notes before helping visitors find their books.keptreopenedkept
Ask the coordinator whenever a request is outside your role.keptreopenedkept
Page 2, where the content changed
Preparing for a shift heading role H1 restoredkeptreopenedkept
Before visitors arrive heading role H2 restoredkeptreopenedkept
Leave the entrance and the reading area clear of boxes.keptreopenedkept
The weekly orientation begins at 11 am on Tuesday. changed sentence; the baseline said 10 am on Mondaynonenonenone
Keep the large-print catalogue beside the information desk.keptreopenedkept
Tell the coordinator if any visitor information has changed.keptreopenedkept
Bring the updated shift checklist to orientation. new sentence, not in the baselinenonenonenone
Page 3
Closing the reading room heading role H1 restoredkeptreopenedkept
A consistent handover heading role H2 restoredkeptreopenedkept
Return reference copies to their labelled shelves.keptreopenedkept
Record unresolved questions in the handover notebook.keptreopenedkept
Leave the next shift a clear account of unfinished tasks.keptreopenedkept
Thank you for helping make the reading room welcoming.keptreopenedkept
  • kept: a prior review record carried to this version (35 scopes)
  • reopened: carried, then reopened with a recorded reason (17 scopes)
  • none: no carried review; the item is new or changed (6 scopes)

Nineteen of the twenty items still need some review. Only the document properties item is fully covered. That is the outcome of a revision like this one: the structure and language checks on unchanged text carried, the reading-order check reopened everywhere, and the two changed or new sentences start from nothing.

Headings came back

All six heading elements in the output carry their baseline roles, H1 and H2 on each page, even though the incoming file tagged them P. The engine restored a justified baseline role where the text and position matched; it did not guess new headings.

Reading order reopened on every carried item, not only page 2

Every one of the 17 carried text items shows reading order as reopened, with the recorded reason A dependency changed between document versions. Adding a paragraph changes the sequence, and in this run the engine did not attempt to localise that to one page. It reopened the reading-order check across the document. This is deliberately conservative. It also means the accurate summary is not two sentences need checking. It is: structure and language carried on 17 items, reading order needs a fresh pass everywhere, and two items start from nothing.

Changed and new text carried nothing

The altered orientation sentence and the new checklist sentence have no carried evidence on any scope. They are listed as imported-baseline, the same state every item had before anyone reviewed anything.

Scopes after revision
58: 35 kept, 17 reopened, 6 with no carried review
Items
20, of which 19 need some review and 1 is fully covered
Review events
19 before the revision, 19 after. Importing a revision creates no new review records.
Heading roles in output
H1, H2, H1, H2, H1, H2
Rendered pixels
Output identical to the incoming revision on all 3 pages
Originals
Both baseline and revision bytes unchanged after the run

5. Export with evidence

The export produces a PDF and a manifest. The manifest records the SHA-256 of that exact PDF, the purpose of the export, the machine validation result, and every carried, reopened or missing scope with its reviewer label, note and date. It applies to that file only. Edit the PDF anywhere else and the hash no longer matches.

Output file
guide-output-for-review.pdf
Manifest
guide-evidence.json
PDF SHA-256 in manifest
13c636341f7dd1ebbbf41e94ec8edef46f1fad8b5a57f3810cb1d68bd444b467
Export purpose
further-review. The engine will not mark this export as reviewed because human checks are incomplete.
Machine validation
veraPDF 1.30.2, PDF/UA-1 profile: passed, 106 rules and 2,663 checks, 0 failures
Validation scope, quoted from the manifest
Machine-checkable PDF/UA-1 requirements only; not full accessibility approval.

A passing validator and a nineteen-item review backlog sit side by side in the same manifest. That is the normal state of a revised document, and it is what the export hands to the next reviewer.

Page 2 of the exported output. Visually identical to the revision: five paragraphs, orientation at 11 am on Tuesday.
guide-output-for-review.pdf, page 2. Appearance is unchanged; only structure and metadata differ from the incoming file.

The refusal: a changed table

The second case is a one-page synthetic Word document from the repository's test fixtures. The baseline is tagged and its header row was explicitly promoted to header cells with column scope. The revision is an untagged Word export of the same page in which the amount for the Review row changes from 100 to 900.

Baseline table page. Heading A guide to accessible revisions, an intro line, a two-column table with header cells Service and Amount, rows Review 100 and Export 200, and a closing note.
table-baseline.pdf. Review, 100. Tagged, header cells recorded.
Revision table page. Same layout; the Review row now shows 900.
table-revision.pdf. Review, 900. Untagged.

The engine refused to reuse the table. The recorded warning is: Complete group text, boundaries or geometry could not be uniquely established. Role: Table. The output contains no Table structure at all. The cells are present as individual items, the number 900 is preserved, and the page renders identically, but the table as a table has to be rebuilt by a person, in PDFCarry if it can be uniquely reconstructed, or in an external editor.

Table case: 13 items, 40 scopes, after the revision was imported. Read from results.json.
Item Structure Reading order Language Alt text
Document and unchanged text
Document properties single scope: document metadatakept
A guide to accessible revisions heading role H1 carriedkeptreopenedkeptn/a
This introduction stays the same between revisions.keptreopenedkeptn/a
Table refused for reuse: cells carried as loose items, no Table structure
Servicenonenonenonen/a
Amountnonenonenonen/a
Graphic without text reported by the validator as a Figure lacking alternative textnonenonenonenone
Reviewnonenonenonen/a
900 changed value; the baseline said 100nonenonenonen/a
Graphic without textnonenonenonenone
Exportnonenonenonen/a
200nonenonenonen/a
Graphic without textnonenonenonenone
Unchanged text after the table
A stable closing note completes the document.keptreopenedkeptn/a
  • kept (7 scopes)
  • reopened (3 scopes)
  • none (30 scopes)
  • group refused for reuse

Some unrelated text did carry. The heading, the introduction and the closing note kept their structure and language evidence and reopened reading order, exactly as in the guide case. The refusal is scoped to the group that could not be matched, not the whole page.

What machine validation reported

The exported table output fails PDF/UA-1 machine validation on 3 rules and 5 checks. The report lists a missing PDF/UA identification, a missing document title, and three Figure elements without alternative text. Those three figures are drawings with no text. Whether they are table rules or something else is not something the validator determines, and this walkthrough does not attribute them.

What the report cannot show is the table's missing semantics. In this run, the validator reported no missing-table-structure failure. That does not establish that the table is accessible. The refusal record and a person are what tell you the table needs rebuilding.

RuleFailed checksValidator message
ISO 14289-1 clause 5, test 11The document metadata stream doesn't contain PDF/UA Identification Schema
ISO 14289-1 clause 7.3, test 13Figure structure element neither has an alternate description nor a replacement text
ISO 14289-1 clause 7.1, test 91Metadata stream does not contain dc:title
Output file
table-output-for-review.pdf
Manifest
table-evidence.json
PDF SHA-256 in manifest
48d110dfd819db7d4af25beae1f83b08a0f2e3124a42af48aadd40e3857ff258
Export purpose
further-review
Machine validation
veraPDF 1.30.2, PDF/UA-1 profile: failed, 103 rules passed, 3 failed, 920 checks passed, 5 failed
Review events
11 before the revision, 11 after
Rendered pixels
Output identical to the incoming revision

What a specialist does next is ordinary work: set the title and PDF/UA identification, describe or mark the three graphics, and rebuild the table with its header cells. Then record those checks. The engine's contribution here was to refuse quietly and to say why, rather than carrying a header-cell review onto a table whose numbers had changed.

What this example does not show

  • No human review. All 30 review events across both cases are automated setup with the reviewer label quoted above. Nothing here was checked by an accessibility specialist.
  • No timing. Setup, review and export durations were not measured, and no time saving is claimed or implied.
  • Controlled producers. The guide was generated so its structure is exact, and the table comes from a small Word fixture. Real agency exports from Acrobat, InDesign, CommonLook or other tools were not tested in this run, and their behaviour may differ.
  • Small documents. Three pages and one page. The supported envelope is born-digital PDF 1.x up to 50 MB, 200 pages and 20,000 structure elements.
  • Out of scope by design: scanned pages, encrypted or signed files, interactive forms and XFA, PDF 2.0, complex or changed tables, mixed or stream-scoped structure, and untagged grouped graphics containing text. These need an external editor.
  • Not certification. A veraPDF pass covers machine-checkable PDF/UA-1 rules. Reading order, meaningful alternative text, heading logic and table relationships still need a person.
  • Not a release. The engine run used the installed local 0.2.2 Python engine, not a signed public installer. Windows only.

Files and checksums

Everything on this page can be checked against the files in this folder. Hashes below are copied from SHA256SUMS.txt. Page previews were rendered from the PDFs with pypdfium2 at 1.2 scale.

FileRoleSHA-256
guide-baseline.pdfGuide case, tagged baseline, 3 pages1a6ff2915faaa9b5b190749d0556c51d9a3a3a6f64706d1a8893fb19c8f06683
guide-revision.pdfGuide case, revision with headings tagged Pfc1e621742db299b94f6c1fdc337571818ed3ee3f4d6b06beb7f3268449f1852
guide-output-for-review.pdfGuide case, exported output13c636341f7dd1ebbbf41e94ec8edef46f1fad8b5a57f3810cb1d68bd444b467
guide-evidence.jsonGuide case, evidence manifest2fbde60298a18673f8c517d0893d18876d23e24524a7957ed753ae11a8f8f681
table-baseline.pdfTable case, tagged baseline with header cells394f8479914052f5538a95bfa848f28ac3a76b9de0dad6e818a7f60ca712a129
table-revision.pdfTable case, untagged revision, 100 changed to 9008e241ec3e0e2f7d323e715ff9029c73d04baf9509a4c28b00e90685cba98954a
table-output-for-review.pdfTable case, exported output48d110dfd819db7d4af25beae1f83b08a0f2e3124a42af48aadd40e3857ff258
table-evidence.jsonTable case, evidence manifest741dd87123a131db7a3f30c68ced1406ad57bad8063f46b74bf7ec6d2c108eed
results.jsonRun summary for both cases, the source of every count on this page524ed6a47af1507abdb1701cc8fc91ef4d37cad498dee3faa89c629b79ed7383
SHA256SUMS.txtChecksum list for the PDFs and JSON filesn/a
All twelve page previews

The README in this folder records the engine version, source commit, provenance of each fixture and the command that regenerates the pack. The article is the same walkthrough as plain text for editors who prefer it.