diff --git a/.github/workflows/pr-visual-validation.yml b/.github/workflows/pr-visual-validation.yml
index 2ae5802..4aa9a41 100644
--- a/.github/workflows/pr-visual-validation.yml
+++ b/.github/workflows/pr-visual-validation.yml
@@ -68,6 +68,9 @@ jobs:
env:
ARTIFACT_DIGEST: ${{ steps.upload.outputs.artifact-digest }}
ARTIFACT_URL: ${{ steps.upload.outputs.artifact-url }}
+ BASE_REPOSITORY: ${{ github.repository }}
+ HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
shell: bash
run: |
set -euo pipefail
@@ -79,6 +82,12 @@ jobs:
echo "Artifact SHA-256: \`${ARTIFACT_DIGEST}\`"
echo
echo "After downloading and unzipping, open \`review.html\` for side-by-side inspection."
+ echo
+ if [[ "${HEAD_REPOSITORY}" == "${BASE_REPOSITORY}" ]]; then
+ echo "[Open this exact commit's rendered capability demo](https://rawcdn.githack.com/${HEAD_REPOSITORY}/${HEAD_SHA}/examples/review.html)"
+ else
+ echo "External HTML preview is intentionally omitted for fork pull requests; review the generated artifact instead."
+ fi
} >> "${GITHUB_STEP_SUMMARY}"
link:
@@ -98,6 +107,7 @@ jobs:
env:
ARTIFACT_DIGEST: ${{ needs.build.outputs.artifact_digest }}
ARTIFACT_URL: ${{ needs.build.outputs.artifact_url }}
+ HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
@@ -129,6 +139,8 @@ jobs:
echo
echo "No external content, OCR, AI, or ML is used; provenance and licensing are included in \`manifest.json\`."
echo
+ echo "[Open this exact commit's rendered capability demo](https://rawcdn.githack.com/${HEAD_REPOSITORY}/${HEAD_SHA}/examples/review.html)."
+ echo
echo "[Browse this revision's committed capability demo](https://github.com/${REPOSITORY}/tree/${HEAD_SHA}/examples); it is separate from this PR artifact."
echo ""
} > updated-body.md
diff --git a/README.md b/README.md
index 8c52554..ca1e8f1 100644
--- a/README.md
+++ b/README.md
@@ -153,10 +153,18 @@ Every accepted semantic element can retain evidence linking it back to its sourc
## Capability demo
-[Open the rendered, side-by-side capability demo](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/review.html)
-or [browse its committed inputs and exact outputs](examples/README.md). The three
-complex source PDFs, fixture prose, and assets are first-party project material
-under the bundled MIT license; no downloaded content, OCR, AI, or ML is used.
+[Browse this revision's committed inputs and exact outputs](examples/README.md).
+On `main`, [open the rendered, side-by-side capability demo](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/review.html).
+Same-repository pull requests receive a commit-pinned rendered-demo link in
+their description, so reviewing a child branch never silently opens `main`.
+For security, fork pull requests expose the generated review artifact but not
+a one-click third-party rendering of contributor-controlled HTML.
+
+The three complex source PDFs, fixture prose, and assets are first-party project
+material under the bundled MIT license; no downloaded content, OCR, AI, or ML
+is used. They remain separate because Tagged-PDF structure trees, AcroForm
+fields, and outlines are document-catalog semantics: concatenating their pages
+would change the evidence being tested rather than merely combine examples.
The committed `examples/` tree is distinct from temporary pull-request review
artifacts. CI regenerates it from the same case definitions and fails if any
diff --git a/examples/README.md b/examples/README.md
index bbdc93b..b271baa 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -3,16 +3,18 @@
All inputs and fixture prose are first-party project material under the bundled MIT license.
No network content, OCR, AI, or ML was used.
+The three PDFs are intentionally isolated: Tagged-PDF structure trees, AcroForm fields, and outlines are document-catalog semantics. Concatenating their pages would alter the evidence being tested and make failures less diagnostic.
+
This directory is the committed, reproducible capability demo. Pull-request review artifacts are generated separately and are never written here.
-[Open the rendered side-by-side demo](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/review.html). GitHub displays committed HTML files as source code; this third-party browser preview renders the same files from `main` without a project website.
+[Open the rendered main-branch side-by-side demo](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/review.html). GitHub displays committed HTML files as source code; this third-party browser preview renders the same files from `main` without a project website. Same-repository pull requests receive a separate commit-pinned rendered link in their description.
-Each row links the source PDF to the exact output committed from the same CocoaPDF revision.
+Relative row links resolve against the revision being viewed. External rendered-HTML links are explicitly labeled as `main`; a same-repository PR description supplies the exact commit-pinned rendered demo.
Full semantic JSON is committed. Report summaries omit only duplicate semantic graphs and glyph-heavy internals; the temporary PR artifact retains every full report.
| Case | Coverage | Input | Outputs |
| --- | --- | --- | --- |
-| `strategic_corner_cases` | Broad V1-V4 formatting, Unicode, lists, tables, figures, forms, columns, security, and fallback coverage. | [PDF](cases/strategic_corner_cases/input.pdf) | [full Markdown](cases/strategic_corner_cases/full/output.md), [full rendered HTML](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/strategic_corner_cases/full/output.html), [full semantic JSON](cases/strategic_corner_cases/full/output.json), [full report](cases/strategic_corner_cases/full/output.report.summary.json) |
-| `tagged_semantics` | Tagged heading, sibling ordered-list isolation, MCID provenance, and tagged table structure. | [PDF](cases/tagged_semantics/input.pdf) | [full Markdown](cases/tagged_semantics/full/output.md), [full rendered HTML](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/tagged_semantics/full/output.html), [full semantic JSON](cases/tagged_semantics/full/output.json), [full report](cases/tagged_semantics/full/output.report.summary.json) |
-| `scope_and_adversarial` | Page-range outline/AcroForm scope, dot-leader finance recovery, and diagram-versus-form false-positive resistance. | [PDF](cases/scope_and_adversarial/input.pdf) | [full Markdown](cases/scope_and_adversarial/full/output.md), [full rendered HTML](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/scope_and_adversarial/full/output.html), [full semantic JSON](cases/scope_and_adversarial/full/output.json), [full report](cases/scope_and_adversarial/full/output.report.summary.json)
[page-2 Markdown](cases/scope_and_adversarial/page-2/output.md), [page-2 rendered HTML](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/scope_and_adversarial/page-2/output.html), [page-2 semantic JSON](cases/scope_and_adversarial/page-2/output.json), [page-2 report](cases/scope_and_adversarial/page-2/output.report.summary.json) |
+| `strategic_corner_cases` | Broad V1-V4 formatting, Unicode, lists, tables, figures, forms, columns, security, and fallback coverage. | [PDF](cases/strategic_corner_cases/input.pdf) | [full Markdown](cases/strategic_corner_cases/full/output.md), [full rendered HTML on main](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/strategic_corner_cases/full/output.html), [full semantic JSON](cases/strategic_corner_cases/full/output.json), [full report](cases/strategic_corner_cases/full/output.report.summary.json) |
+| `tagged_semantics` | Tagged heading, sibling ordered-list isolation, MCID provenance, and tagged table structure. | [PDF](cases/tagged_semantics/input.pdf) | [full Markdown](cases/tagged_semantics/full/output.md), [full rendered HTML on main](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/tagged_semantics/full/output.html), [full semantic JSON](cases/tagged_semantics/full/output.json), [full report](cases/tagged_semantics/full/output.report.summary.json) |
+| `scope_and_adversarial` | Page-range outline/AcroForm scope, valid heading anchors, dot-leader finance recovery, and two-sided diagram-versus-form/table fidelity. | [PDF](cases/scope_and_adversarial/input.pdf) | [full Markdown](cases/scope_and_adversarial/full/output.md), [full rendered HTML on main](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/scope_and_adversarial/full/output.html), [full semantic JSON](cases/scope_and_adversarial/full/output.json), [full report](cases/scope_and_adversarial/full/output.report.summary.json)
[page-2 Markdown](cases/scope_and_adversarial/page-2/output.md), [page-2 rendered HTML on main](https://raw.githack.com/sayantandey/CocoaPDF/main/examples/cases/scope_and_adversarial/page-2/output.html), [page-2 semantic JSON](cases/scope_and_adversarial/page-2/output.json), [page-2 report](cases/scope_and_adversarial/page-2/output.report.summary.json) |
diff --git a/examples/cases/scope_and_adversarial/full/assertions.json b/examples/cases/scope_and_adversarial/full/assertions.json
index 6813a86..8b470fb 100644
--- a/examples/cases/scope_and_adversarial/full/assertions.json
+++ b/examples/cases/scope_and_adversarial/full/assertions.json
@@ -20,6 +20,102 @@
{
"passed": true,
"detail": "OCR remains disabled"
+ },
+ {
+ "passed": true,
+ "detail": "page-only outline destinations resolve to unique page headings"
+ },
+ {
+ "passed": true,
+ "detail": "unresolved navigation never emits a literal None anchor"
+ },
+ {
+ "passed": true,
+ "detail": "both full-document AcroForm fields are retained"
+ },
+ {
+ "passed": true,
+ "detail": "the finance grid remains a structural table"
+ },
+ {
+ "passed": true,
+ "detail": "five outlined diagram panels are retained as one vector figure"
+ },
+ {
+ "passed": true,
+ "detail": "all native diagram labels remain inside the vector figure"
+ },
+ {
+ "passed": true,
+ "detail": "the SVG retains the outlined-panel stroke geometry"
+ },
+ {
+ "passed": true,
+ "detail": "diagram labels are not flattened into false prose lines"
+ },
+ {
+ "passed": true,
+ "detail": "Markdown and HTML both reference the generated diagram asset"
+ },
+ {
+ "passed": true,
+ "detail": "semantic JSON records one vector image node"
+ },
+ {
+ "passed": true,
+ "detail": "the vector figure retains page-2 provenance"
+ },
+ {
+ "passed": true,
+ "detail": "the vector figure remains traceable to its source glyphs"
+ },
+ {
+ "passed": true,
+ "detail": "diagram labels provide deterministic alternative text"
+ },
+ {
+ "passed": true,
+ "detail": "the vector figure records PDF-native artwork evidence"
+ },
+ {
+ "passed": true,
+ "detail": "report geometry shows one page-spanning, multi-row diagram"
+ },
+ {
+ "passed": true,
+ "detail": "diagram preservation does not invoke image-text extraction"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField has one page-scoped semantic field"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField retains explicit PDF widget appearance evidence"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField preserves its explicitly large text size"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField preserves its dark blue-gray text color"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField preserves its pale blue-gray background"
+ },
+ {
+ "passed": true,
+ "detail": "SecondField appearance remains traceable to independent PDF-native evidence"
+ },
+ {
+ "passed": true,
+ "detail": "HTML renders only the evidenced field with its size and colors"
+ },
+ {
+ "passed": true,
+ "detail": "documentary form output never creates an active browser control"
}
]
}
diff --git a/examples/cases/scope_and_adversarial/full/assets/vector-a60ea913b6a0a8ba.svg b/examples/cases/scope_and_adversarial/full/assets/vector-a60ea913b6a0a8ba.svg
new file mode 100644
index 0000000..6412f5a
--- /dev/null
+++ b/examples/cases/scope_and_adversarial/full/assets/vector-a60ea913b6a0a8ba.svg
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/examples/cases/scope_and_adversarial/full/output.html b/examples/cases/scope_and_adversarial/full/output.html
index 8400a9c..4a4a10d 100644
--- a/examples/cases/scope_and_adversarial/full/output.html
+++ b/examples/cases/scope_and_adversarial/full/output.html
@@ -25,10 +25,13 @@
.cocoapdf-table-note { font-size: 0.9em; font-style: italic; }
.cocoapdf-cross-reference { text-decoration: none; }
.cocoapdf-form { display: grid; gap: 0.65rem; margin: 1rem 0; }
+.cocoapdf-form-field { display: grid; grid-template-columns: minmax(8rem, max-content) 1fr; column-gap: 0.5rem; }
+.cocoapdf-form-field-name { font-weight: 600; }
+.cocoapdf-form-field-value-evidenced { align-items: center; box-sizing: border-box; display: inline-flex; padding: 0 0.25rem; }
.cocoapdf-equation { overflow-x: auto; margin: 1rem 0; }
First page body and first field.
FirstField: Alpha
@@ -45,8 +48,9 @@SecondField: Beta
Diagram boxes below are not form controls.
-Parse Layout Render
-Decode Reconcile
- +