Skip to content

PDF stage 4.9: axial & radial shadings (types 2/3)#573

Merged
andiwand merged 4 commits into
mainfrom
pdf-stage-4.9
Jun 28, 2026
Merged

PDF stage 4.9: axial & radial shadings (types 2/3)#573
andiwand merged 4 commits into
mainfrom
pdf-stage-4.9

Conversation

@andiwand

@andiwand andiwand commented Jun 28, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Stage 4.9 of the PDF→HTML pipeline: render axial (type 2) and radial (type 3) shadings as SVG gradients, both via the sh operator and via /PatternType 2 shading patterns selected by scn. Stacked on #572 (4.8); targets pdf-stage-4.8.

What

  • pdf_shading.{hpp,cpp}parse_shading resolves a /Shading dictionary and pre-samples its tint /Function across /Domain into 32 sRGB colour stops, so the renderer needs no function evaluator. Shading types other than 2/3 and malformed shadings return null; /Extend, /Background and /BBox are parsed.
  • Parserparse_resources now builds the /Shading and /Pattern resource tables (after /ColorSpace, so a named colour space resolves). A shading pattern resolves its /Shading; a tiling pattern is recognized and deferred to 4.10. GraphicsState::Color carries the /Pattern name selected by scn.
  • Extractorscn records the pattern name; paint_path resolves a shading pattern to PathElement::fill_shading plus the pattern /Matrix; the sh operator emits a ShadingElement flooding the current clip.
  • HTML — a GradientRegistry emits <linearGradient>/<radialGradient> defs with gradientUnits="userSpaceOnUse"; a shading-pattern fill paints the path through fill="url(#…)", and sh paints a clipped <rect>. /Extend is approximated by SVG's pad spread.

Tests

  • PdfShading: axial/radial parsing, domain/extend, background, unsupported type, short coords, bad function → null.
  • PdfPageExtractor: shading-pattern fill resolves fill_shading + matrix, unknown pattern leaves a plain fill, sh emits a ShadingElement at the CTM, unknown shading emits nothing.

All 197 PDF tests pass; full suite green.

Notes

  • Reference-output submodules are intentionally not bumped here (left for regeneration by the maintainer).
  • Mesh/function shadings (types 1, 4–7) and tiling patterns (/PatternType 1, next in 4.10) are out of scope.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 688d3a3478

ℹ️ 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".

Comment thread src/odr/internal/html/pdf_file.cpp
Base automatically changed from pdf-stage-4.8 to main June 28, 2026 14:47
andiwand and others added 2 commits June 28, 2026 17:24
Render axial (type 2) and radial (type 3) shadings as SVG gradients, both
via the `sh` operator and via `/PatternType 2` shading patterns selected by
`scn`.

- `pdf_shading.{hpp,cpp}`: `parse_shading` resolves a `/Shading` dictionary,
  pre-sampling its tint `/Function` across `/Domain` into 32 sRGB colour stops
  (no function evaluator needed at render time). Types other than 2/3 and
  malformed shadings return null; `/Extend`, `/Background` and `/BBox` are
  parsed.
- Parser: `parse_resources` now builds the `/Shading` and `/Pattern` resource
  tables (after `/ColorSpace`, so named colour spaces resolve). A shading
  pattern resolves its `/Shading`; a tiling pattern is recognized (rendered in
  4.10). `GraphicsState::Color` carries the selected `/Pattern` name.
- Extractor: `scn` records the pattern name; `paint_path` resolves a shading
  pattern to `PathElement::fill_shading` + the pattern `/Matrix`; the `sh`
  operator emits a `ShadingElement` flooding the current clip.
- HTML: a `GradientRegistry` emits `<linearGradient>`/`<radialGradient>` defs
  with `gradientUnits="userSpaceOnUse"`; a shading-pattern fill paints the path
  through `fill="url(#…)"`, and `sh` paints a clipped `<rect>`. `/Extend` is
  approximated by SVG's `pad` spread.

Tests: shading parsing (axial/radial, domain/extend, background, unsupported
type, short coords, bad function) and extractor wiring (shading-pattern fill,
unknown pattern, `sh` element, unknown shading).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…otes

Tightening from PR review, no behaviour change (197 PDF tests still green):

- Add `as_reals(const Object&)` to `pdf_object`, collapsing the repeated
  "array `Object` -> vector<double>" loops in `image_decode`, the colour-key
  `/Mask` branch, the inline-image `/Decode`, and the shading `read_numbers`.
- Add a file-local `bind_parser_io` template in the parser to bind the shared
  `resolve`/`load_stream` hooks of every typed context (colour space, function,
  shading), replacing four identical lambda pairs.
- Make the deferred shading features visible: `Shading::{extend,background,bbox}`
  are parsed but not yet honoured by the renderer (it always uses SVG `pad`
  spread). Documented on the `Shading` struct, the `GradientRegistry`, and
  `pdf/AGENTS.md`.
- Note the `Resources::shading` (shared_ptr) vs `Resources::pattern` (Element*)
  ownership asymmetry, and why only the gradient transform's translation is
  rounded. Reflow two clang-format comment artifacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uqx1dUpDUykRxui8FYwxvM
@andiwand andiwand enabled auto-merge (squash) June 28, 2026 15:41
@andiwand andiwand merged commit 2b02d2c into main Jun 28, 2026
11 checks passed
@andiwand andiwand deleted the pdf-stage-4.9 branch June 28, 2026 16:10
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