Skip to content

fix(preflight): judge off-page and obscured content on clipped geometry (#118) - #159

Open
mberrys wants to merge 5 commits into
devfrom
cc/issue-118-clip-aware-geometry
Open

mberrys wants to merge 5 commits into
devfrom
cc/issue-118-clip-aware-geometry

Conversation

@mberrys

@mberrys mberrys commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #118 (L01-13). off-page-content and obscured-content now judge painted, clipped geometry instead of raw bounding boxes. The hidden-content processor tracks the effective clip (W/W*, Form XObject BBox, text clip, with q/Q restore).

  • off-page-content: painted bounds are intersected with the clip before the page test. A mark the clip removes entirely paints nothing and is not reported; a mark whose visible part lies off the page is.
  • obscured-content: each painted item keeps its visible region, and later opaque fills subtract from it in paint order, so coverage by clipped geometry or by several adjacent objects is found. A blend mode or soft mask over earlier paint cannot be decided, so the page reports check-incomplete. Occlusion tracking is capped at 1000 objects per page and reports incomplete beyond that.

Proof

Fixtures off-page-content-clipped.pdf and obscured-content-clipped.pdf (generator loop-preflight/tools/generate_clip_aware_geometry_fixtures.py) were committed first (e9bec03).

Fixture Before After
off-page-content-clipped pass: true, 0 warnings 1 off-page warning at 300..400
obscured-content-clipped pass: true, 0 warnings 2 obscured warnings (40..60 clipped mark, 100..120 mark covered by two rectangles)

Baseline PdfTool was built from the fixture commit (engine unchanged). Local (Windows, MSVC Release): UnitTestsPreflightEngine, UnitTestsPreflightCorpus, UnitTestsPreflightVerdict, UnitTestsPreflightChecks, UnitTestsEvidenceGraph, UnitTestsContentProcessorLimits all exit 0. Regenerating corpus snapshots produced only the two new files (other differences were line endings), so the golden corpus is unchanged. generate-architecture-catalogs.py --check, check_source_integrity.py, check_preflight_truth_source.py and check-change.py --dry-run pass. New tests cover clipped and unclipped off-page cases, Q restoring the clip, union coverage, a cover that misses the mark, and the blend-mode incomplete path.

Not run locally

Full check-change build/ctest/clang-tidy (no clang-tidy-18), Linux lanes, and the remaining mapped test targets; left to CI.

Remaining risk

  • Images, shadings and transparency groups are not modelled for occlusion; strokes use rectangle bounds.
  • Obscured findings stay heuristic (confidence unchanged, so existing goldens are untouched).
  • Any blend mode or soft mask over earlier paint now makes the page incomplete, which may be noisy on transparency-heavy pages.
  • This overlaps HiddenContentProcessor edits in feat(preflight): evaluate optional-content membership for print and screen (#115) #157; expect a textual merge conflict in preflightengine.cpp.

Handoff

Changed: preflightengine.cpp (processor, emission), tests, fixtures and generator, two snapshots, overlay (both rows landed, closed_by their check ids), regenerated catalogs, change record and evidence. Anti-slop review: no new profile parameters and no API changes; the clip stack reuses existing processor hooks, and undecidable transparency is reported rather than guessed.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

mberrys and others added 4 commits September 30, 2026 17:42
… fixtures (#118)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed geometry (#118)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…118)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…occlusion (#118)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
}
continue;
}
item.remaining = item.remaining.subtracted(painted.region);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Quadratic path subtraction. Every opaque fill subtracts its path from every intersecting tracked item (up to 1000). A full-page background collects a hole per fill, so each QPainterPath::subtracted gets more expensive. This runs for every check id that builds a HiddenContentProcessor (hidden-layers, invisible-content, off-page-content), not only obscured-content. At minimum, only track occlusion when check.id == obscured-content.

Pairwise path subtraction ran for every hidden-content check id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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