PDF stage 4.3: clipping#561
Conversation
Track the clipping path in the graphics state and honour it in the SVG output (ISO 32000-1 8.5.4). - `W`/`W*` set a pending clip that the next painting or `n` operator installs as the intersection with the current clip. The painting operator that installs a clip is itself clipped only by the *previous* clip (the new region scopes following content), so `paint_path` snapshots the clip before committing the pending one. - The clip is part of the saved/restored state, so `q`/`Q` and form-XObject invocation scope it like the CTM. Each emitted `PathElement` carries a snapshot of the clip in force. - Form-XObject `/BBox` clipping (deferred in stage 2) lands here via the same machinery: the box is parsed onto the `XObject` and intersected into the clip — mapped through the form `/Matrix` + CTM — under the invocation's save/restore. - HTML: each page's clip regions become nested `<clipPath>` defs (intersection expressed by chaining `clip-path` from one to the next), emitted once in a hidden `<svg>` and referenced per path. Dedups shared prefixes. Tests: clip rect limits a later fill; even-odd rule; a clip excludes its own paint; nested clips intersect; `q`/`Q` save/restore; form `/BBox` clips its content. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7Lp7cZPX84gvGmaGA6bHq
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23cac8f2f2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| /// The clip in force when this path was painted: the intersection of these | ||
| /// regions (empty = unclipped). Snapshotted from the graphics state at paint | ||
| /// time so the renderer can install it without replaying the clip stack. | ||
| std::vector<ClipPath> clip; |
There was a problem hiding this comment.
Apply clipping to text output as well
When a stream sets a clip and then paints text, such as 0 0 50 50 re W n BT ... Tj ET, or when text is inside a form clipped by /BBox, the current clipping path applies to that text too. This patch snapshots the clip only on PathElement; TextElement carries no clip and the HTML path writes text spans without any clip-path, so these PDFs still render text outside the clipped region even though vector paths are clipped.
Useful? React with 👍 / 👎.
🤖 Generated with Claude Code
Stage 4.3 of the in-house PDF renderer: honour the clipping path in the SVG output (ISO 32000-1 8.5.4). Stacked on
mainafter stage 4.2 (#560).What changed
W/W*set a pending clip that the next painting (orn) operator installs; that operator's own paint is clipped only by the previous clip, sopaint_pathsnapshots before committing.q/Qand form-XObject invocation scope it like the CTM. EachPathElementcarries a clip snapshot./BBoxclipping (deferred in stage 2) lands here: parsed onto theXObjectand intersected through the form/Matrix+ CTM, scoped by the invocation's save/restore.<clipPath>defs (intersection chained viaclip-path), emitted once in a hidden<svg>and referenced per path; shared prefixes deduped.Tests
Extractor-level (inline content streams): clip rect limits a later fill; even-odd rule; a clip excludes its own paint; nested clips intersect;
q/Qsave/restore; form/BBoxclips its content. Full suite green.