From b6e5a112136e8e4a644fb3cf2e3cc6c3b540057e Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Sun, 23 Aug 2026 19:06:20 +0100 Subject: [PATCH] feat(svg): honour the opacity family and warn on what the reader approximates --- CHANGELOG.md | 55 +++ assets/readme/examples/emoji-clip-path.pdf | Bin 255301 -> 255581 bytes assets/readme/examples/emoji-shortcodes.pdf | Bin 44617 -> 44751 bytes .../compose/document/style/DocumentPaint.java | 7 +- .../compose/document/svg/SvgGradients.java | 11 +- .../demcha/compose/document/svg/SvgIcon.java | 25 +- .../compose/document/svg/SvgIconReader.java | 190 ++++++++-- .../compose/document/svg/SvgStyles.java | 87 ++++- .../svg/SvgIconOpacityAndWarningsTest.java | 347 ++++++++++++++++++ .../compose/document/svg/SvgPathTest.java | 33 ++ .../compose/document/svg/SvgStylesTest.java | 46 +++ .../semantic/docx/DocxSemanticBackend.java | 25 ++ .../docx/DocxInlineRunDropWarningTest.java | 104 ++++++ 13 files changed, 876 insertions(+), 54 deletions(-) create mode 100644 core/src/test/java/com/demcha/compose/document/svg/SvgIconOpacityAndWarningsTest.java create mode 100644 render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxInlineRunDropWarningTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a6680edbb..24ffab2be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,61 @@ follow semantic versioning; release dates are ISO 8601. ## v2.2.1 — Planned +### Fixed + +- **The SVG reader honours the opacity family.** `opacity`, `fill-opacity` and + `stroke-opacity` — attribute or `style=""`, number or percentage, with SVG's + inheritance for the paint slots and composition for group `opacity` — now + multiply into each layer's flat paint alpha, on top of any alpha the colour + already carries from an 8-digit hex or `rgba()`. They were read from nowhere + before: a translucent logo landed on the page fully opaque, and the only fix + was editing the SVG. A slot whose product reaches zero is treated like + `fill="none"`, so an `opacity="0"` guide layer no longer paints at all. A + *partial* opacity cannot reach a gradient slot — the `DocumentPaint` contract + refuses translucent stops because shadings carry no alpha — so a gradient + under `fill-opacity="0.5"` still paints opaque, now with a one-line warning + naming the approximation. Group opacity is the per-layer approximation of SVG's + offscreen compositing — overlapping siblings inside one translucent group + darken where a browser would flatten them first — which is the same trade + every lightweight icon reader makes. + +- **What the SVG reader cannot honour now says so.** `fill-rule="evenodd"` was + read from nowhere and filled with non-zero winding — a donut whose hole is cut + by a same-direction subpath came out solid, silently; it still renders the + same way, but the icon now logs one warning naming the approximation (a + `fill-rule` value outside nonzero / evenodd / inherit, previously unread, is + now refused like any other bad presentation value). A `mask="url(#id)"` or + `filter="url(#id)"` attribute — whose definition sits in ``, where the + walk never looks — paints unmasked / unfiltered as before, but the + referencing attribute is now read and warned about, because that attribute is + the only place the divergence is observable. Element kinds outside the + reader's vocabulary (`mask`, `pattern`, `marker`, `