Commit 65e2c46
✨ Add gradient and image-filled text (Feature 3) (#4)
* feat: add gradient and image-filled text (Feature 3)
Adds the ability to fill text glyphs with LinearGradient, RadialGradient,
or a TextFillImage instead of a flat color.
- New `TextFillImage` model with `path` and `fit` fields
- New `fill` field on `TextLayer` and `TextPart`
- `Canvas.text()` accepts a `fill` parameter
- Per-part fill overrides layer-level fill for that segment
- Block-level fill for multiline text: gradient/image spans the entire
text block rather than repeating per line
- Full JSON round-trip support via Pydantic discriminated union
- Effects (shadow, glow, stroke, background) compose correctly with fill
- 25 new tests covering models, JSON serialization, and rendering
https://claude.ai/code/session_01QK5iq4VqPsjWK8R9zJp1cg
* fix: resolve review bugs in gradient/image-filled text (Feature 3)
- Fix letter_spacing silently dropped when TextPart has both fill and
letter_spacing set (_render_text_part now uses letter-spaced mask path)
- Fix letter_spacing silently dropped in multiline fill path
(_render_multiline_text now pre-computes spaced widths for block bounds
and draws stroke/mask character-by-character when letter_spacing is set)
- Add TextFillImage path validation in _validate_image_paths (checks layer
fill and per-part fills before rendering)
- Add rendering tests for TextFillImage and TextPart fill+letter_spacing
https://claude.ai/code/session_01QK5iq4VqPsjWK8R9zJp1cg
* fix: exclude fill=None from TextLayer/TextPart JSON serialization
Adding fill to TextLayer and TextPart caused "fill": null to appear in
all serialized text layers, breaking existing snapshot tests. Fix by
adding a model_serializer to both classes that omits the fill key when
it is None. JSON round-trips still work because fill defaults to None
when the key is absent.
Also reverts the snapshot changes in test_canvas.py and test_text_layers.py
that were patching around the serialization noise.
https://claude.ai/code/session_01QK5iq4VqPsjWK8R9zJp1cg
* Revert "fix: exclude fill=None from TextLayer/TextPart JSON serialization"
This reverts commit 1300bf4.
* test: replace test_text_fill with rendering snapshots and validation tests
- Delete tests/test_text_fill.py (model/rendering smoke tests that were
too trivial to be worth maintaining separately)
- Add 7 snapshot tests to test_rendering.py covering: linear gradient fill,
radial gradient fill, image fill (TextFillImage), fill + stroke, fill +
letter spacing, multiline block fill, and rich text per-part fill
- Add TestTextFill class to test_text_layers.py with 8 validation and
serialization tests: JSON serialization for all 3 fill types, null fill
serialization, JSON round-trip, TextPart fill serialization, and
FileNotFoundError for missing TextFillImage paths (layer-level and part-level)
https://claude.ai/code/session_01QK5iq4VqPsjWK8R9zJp1cg
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 84b2158 commit 65e2c46
59 files changed
Lines changed: 609 additions & 23 deletions
File tree
- quickthumb
- tests
- snapshots
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| 48 | + | |
47 | 49 | | |
48 | 50 | | |
49 | 51 | | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
176 | 187 | | |
177 | 188 | | |
178 | 189 | | |
| |||
262 | 273 | | |
263 | 274 | | |
264 | 275 | | |
| 276 | + | |
265 | 277 | | |
266 | 278 | | |
267 | 279 | | |
| |||
318 | 330 | | |
319 | 331 | | |
320 | 332 | | |
| 333 | + | |
321 | 334 | | |
322 | 335 | | |
323 | 336 | | |
| |||
0 commit comments