返回 ppt-master
svg-effects.md
根目录 / skills / ppt-master / references / svg-effects.md
1 > See [`shared-standards-core.md`](./shared-standards-core.md) for the mandatory SVG foundation.
2
3 # SVG Effects and Geometry Specification
4
5 Authority for advanced paint, effects, transforms, freeform/radial geometry, and constructed visual styles. Default and Quick Generate load it before SVG authoring; other SVG-authoring routes follow their workflow trigger.
6
7 **Cross-reference map**: unqualified §1, §2, and §4 references point to [`shared-standards-core.md`](./shared-standards-core.md); §6 references are local to this file.
8
9 ## 6. Advanced SVG Effects and Authoring Techniques
10
11 **Mandatory**: Default and Quick Generate read this file completely before SVG
12 authoring and keep its compatible techniques in active construction vocabulary.
13 Before finalizing each page, run the §6.1 selection procedure, with the Visual
14 Job Router as recall for the jobs it diagnoses. Use §6.13 when diagnosed
15 jobs benefit from one coordinated page recipe.
16
17 **Default — situational use (may override when plain construction is stronger)**:
18 “Advanced” means capability depth, not an effect quota. During page authoring,
19 recall relevant techniques from content, hierarchy, legibility, semantics,
20 rhythm, and style; apply those that materially help.
21
22 ### 6.1 Availability, Precedence, and Fidelity
23
24 | Decision layer | Authority |
25 |---|---|
26 | Technical validity | Required / Forbidden / Conditional contracts in this file, over the shared core's closed authoring surface, the route's other required construction references, and any triggered module |
27 | Project values | Default: `<project_path>/spec_lock.md` anchors plus retained Design Spec/page context; Quick: anchors resolved in the current context |
28 | Aesthetic fit | Locked or Quick-resolved `visual_style` / `visual_style_behavior` |
29 | Per-page choice | Content purpose, hierarchy, legibility, semantics, and rhythm |
30
31 **Mandatory — job-first effect selection**: establish the editable semantic
32 skeleton first, then diagnose effect jobs before treating the page as complete.
33 Plain construction remains valid only when that diagnostic finds no unresolved
34 visual job.
35
36 | Pass | Decision |
37 |---|---|
38 | Skeleton / diagnose | Establish native information, relationships, and hierarchy. Before completion, check image/text integration, plane separation, focus, state/direction, material/style, and the recurring motif; keep plain construction when none needs treatment. |
39 | Surface / select | Name the target, confirm its owning subsection and fidelity, and let the Router recall candidates. Choose a compatible technique that fully performs the job; prefer simpler/native-stable alternatives only when communication is equal. `Approximate` requires review, not automatic rejection. |
40 | Integrate / stop | Align paint, contour, light, hierarchy, and z-order; combine only techniques with different jobs. Check legibility, editability, density, fidelity, and style; simplify failures, use legal alternatives, and bake only the smallest pixel-dependent layer. Keep authoritative text/data native. |
41
42 **Default — one dominant composition scaffold (may override when a second
43 scaffold performs an independent communication job)**: Integrate the page-scale
44 move and every active Structure / Image / Chart / Table branch into one dominant
45 system, sharing direction, contour, baseline, and z-order as applicable. Make a
46 branch-local system visibly subordinate when it cannot share that scaffold;
47 simplify any competing scaffold without a separate communication job.
48
49 #### Visual Job Router
50
51 **Reference — not a quota**: recall candidates for a diagnosed problem from
52 this table. A
53 page may use no listed technique, one technique, or several techniques with
54 different jobs. The table recalls constructions rather than bounding them: one
55 it never names is equally valid when it satisfies every applicable technical
56 contract — the shared core's closed authoring surface, this file, the route's
57 other required construction references, and any triggered module. Those
58 contracts are the boundary; membership in this table is not.
59
60 | Diagnosed visual problem | Candidate technique | Authority / stop |
61 |---|---|---|
62 | Meaningful direction, continuous value, or center focus is missing | Linear/radial gradient or channel alpha | §6.2 / §6.3; otherwise keep solid paint |
63 | Picture/card/overlay elevation or boundary is unclear | Object or picture/carrier shadow, restrained glow, or hairline | §6.4; equal peers stay flat; one light direction |
64 | Native copy and image do not integrate | Scrim, fade, wash, vignette, off-center spotlight, or faux glass | §6.5 and the Image-Treatment Implementation Map; verify contrast; no backdrop blur |
65 | Relationship state, direction, continuity, or boundary is unclear | Draft/optional/future → dash; direction → marker; undirected → solid; continuous flow → gradient stroke; repeated boundary → frame/contour/crop edge; exact grid → multi-subpath | §6.6 / §6.3; every line needs a job |
66 | Short display text needs notation, silhouette, or material/image emphasis | Removed/former → strike; eyebrow distinction → tracking; display silhouette → outline/gradient; justified material/image emphasis → native picture/texture fill; luminous metric → glow; semantic list → native bullet | §6.7 / §6.3 / §6.4; no decorative body-copy treatment |
67 | Tilt, repetition, or reversible asset direction helps composition | Rotate, translate/mirror, or local `<use>` | §6.8; never mirror text, logos, or directional evidence |
68 | Resolved style needs hand, print, pixel, facets, layers, ribbon, or line-plus-area | Matching constructed recipe | §6.11; no generic decorative freeform |
69 | Meaning needs an unmatched silhouette, radial hierarchy, gauge, or custom route | Freeform, explicit arc/sector, or calculated arrowhead | §6.9 / §6.10; prefer an equal stock shape/marker |
70 | Look depends on dense texture, source blur, per-pixel composite, reflection, or skew | Native-safe alternative or prepared/baked asset | §6.12; text/data stay editable |
71
72 #### Image-Treatment Implementation Map
73
74 **Reference — not a constraint**: when image composition names one of these
75 modifier or prepared-asset treatments, resolve its implementation here.
76 `Effect-only` keeps a visible capability here without restoring a layout ID.
77
78 | Image handles / treatment | Construction / boundary |
79 |---|---|
80 | `M2 · 01/03/04/08/09` · scrim, wash, fade, grid | Explicit solid/linear/radial layers over one picture; §6.2 / §6.3 / §6.5 |
81 | `M2 · 06/07` · atmospheric wash, watermark/receded field | Reduced picture alpha + optional wash; subordinate to native content; §6.2 / §6.5 |
82 | `M2 · 02/05` · vignette or spotlight | Radial layer with movable `fx/fy` or `cx/cy`; outer geometry `Approximate`; §6.3 / §6.5 |
83 | `M3 · 04` · lifted picture panel / visible overlay edge | Picture/carrier shadow, glow, or hairline; shadow one support shape for a framed/captioned panel; §6.4 |
84 | `M3 · 01/02/05` · frame, print frame, contour/cut edge | Registered native stroke/path; §6.6 |
85 | `M3 · 03; M1 · 09` · rotation, misregistration, Riso offset | Transform + explicit duplicate layers; §6.8 / §6.11 |
86 | `M1 · 03` + effect-only forms · paper cut, facets/folds, ribbon, staging | Ordered paths/facets + consistent paint/light; §6.11 / [`native-shape-authoring.md`](./native-shape-authoring.md) §7 |
87 | `M1 · 01/02/04–08` · crop, opening, subtraction, reveal | Direct clip or materialized Boolean; no `<mask>`; [`shared-standards-core.md`](./shared-standards-core.md) §1.2 / [`native-shape-authoring.md`](./native-shape-authoring.md) §6 |
88 | Effect-only · faux glass | Visible field + translucent panel + highlight; no blur or frosted-crop substitution; §6.5 |
89 | `A1 · 02–04; A3 · 02/03` · blur, duotone, blend, frost, desaturation | Prepared local bitmap/composite/derivative; registered frost is a blurred derivative; §6.12 |
90
91 **Reference — illustrative colors**: colors below demonstrate syntax only;
92 generated pages choose paint from the Default locked or Quick-resolved identity
93 anchors, visual style, content semantics, and current composition. A contextual
94 tint, gradient stop, shadow/glow paint, or one-off display color need not
95 already be a persistent identity role;
96 promote it only when it becomes a recurring named role. Fidelity labels are defined
97 in [`shared-standards-core.md`](./shared-standards-core.md). Review an `Approximate` result in native PPTX
98 when the effect carries material meaning.
99
100 ---
101
102 ### 6.2 Color, Alpha, and Opacity
103
104 Compatible paint grammar includes recognized named colors, `rgb()` / `rgba()`,
105 `hsl()` / `hsla()`, and `#RGB` / `#RGBA` / `#RRGGBB` / `#RRGGBBAA`. The
106 converter also tolerates legacy bare 3/4/6/8-digit hexadecimal tokens. The
107 shared converter implementation for §§6.2–6.8 is
108 [`utils.py`](../scripts/svg_to_pptx/drawingml/utils.py).
109
110 **Default — canonical generated paint tokens (may preserve compatible
111 alternatives)**: New `svg_output/` and reusable template SVGs write solid paint
112 as uppercase six-digit `#RRGGBB`. `fill` / `stroke` may instead use lowercase
113 `none` or the exact local reference form `url(#id)`. Named colors, lowercase or
114 short/alpha HEX, functional colors, and bare legacy HEX remain supported input.
115 The quality checker prints an optional canonical rewrite as a recommendation
116 warning; it does not require modification or block export.
117 Explicit empty, malformed, or unrecognized paint values are errors in both
118 Checker and exporter preflight; neither converts unknown intent into
119 `noFill` or default black. Omitted properties still follow their own element
120 contract, such as SVG's default fill or §6.3's required gradient-stop color.
121
122 | Intent | Canonical authoring | Native result / fidelity |
123 |---|---|---|
124 | Solid fill or text paint | `fill="#RRGGBB"` | Solid DrawingML paint; `Native-stable` |
125 | Fill/text alpha | Opaque `fill` + `fill-opacity="0..1"` | Fill/run alpha; `Native-stable` |
126 | Stroke alpha | Opaque `stroke` + `stroke-opacity="0..1"` | Line/outline alpha; `Native-stable` |
127 | Gradient-stop alpha | Opaque `stop-color` + `stop-opacity="0..1"` | Per-stop alpha; `Native-stable` |
128 | Shadow/glow alpha | Opaque `flood-color` + `flood-opacity="0..1"` | Glow is `Native-stable`; outer shadow is visually calibrated `Approximate` within §6.4 |
129 | Picture fade | `<image opacity="0..1">` | Picture `<a:alphaModFix>`; `Native-stable` |
130 | One atomic whole-object fade | Non-group element `opacity="0..1"` | Alpha compiled into its supported paint/effect channels; `Native-normalized` |
131 | Pattern alpha | Opaque pattern child paint + child fill/stroke opacity | Conditional; [`native-data-interface.md`](./native-data-interface.md) |
132 | CSS color alpha | Alpha-bearing named/functional/HEX paint | `Native-normalized`; recommendation warning only |
133 | Group fade | `<g opacity>` compatibility | `Approximate`; fidelity warning; §2.2 |
134
135 ```text
136 effective fill alpha
137 = color alpha × ancestor group opacity × element opacity × fill-opacity
138 ```
139
140 **Default — opaque color authority (may preserve compatible alpha colors)**:
141 New generated SVG puts alpha on the semantic channel that owns it. Existing or
142 intentional alpha-bearing color tokens remain convertible; they normalize into
143 the matching DrawingML color/alpha channels.
144
145 **Default — channel-specific alpha (may override for one atomic whole-object
146 fade)**: use `fill-opacity`, `stroke-opacity`, `stop-opacity`, or
147 `flood-opacity` when only that channel fades. Use element `opacity` only when
148 an image or one non-group atomic object intentionally fades all of its
149 supported paint/effect channels together. Do not use element `opacity` as an
150 alias for `rgba()` on a fill-only object.
151
152 **Default — alpha grammar (may preserve compatible alternatives)**: write
153 `opacity`, `fill-opacity`, `stroke-opacity`, `stop-opacity`, and
154 `flood-opacity` as finite unitless numbers from `0` to `1`. The converter also
155 accepts finite numeric values that SVG/CSS clamps into that interval;
156 `stop-opacity` and `flood-opacity` additionally accept finite percentages. The
157 checker reports those supported non-default spellings as recommendation warnings.
158 Malformed or non-finite values are errors in both Checker and exporter
159 preflight; neither substitutes an opaque default for unknown intent.
160 `fill="transparent"` / `stroke="transparent"` become no fill/line; use a color
161 plus alpha when a painted transparent layer must remain represented. Prefer
162 descendant alpha over group opacity when isolated compositing matters (§2.2).
163
164 PPTX import is a user-input boundary, not generated authoring. Tolerant mode
165 retains recognized color semantics, omits only unsupported paint properties,
166 and records the decision in `conversion-report.json`; `--strict` keeps the
167 closed parser checks. See
168 [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
169 ---
170
171 ### 6.3 Gradients and Paint Effects
172
173 | Concern | Contract |
174 |---|---|
175 | Definition | Direct `<linearGradient>` / `<radialGradient>` child of `<defs>` with unique `id` |
176 | Reference | Exact local `url(#id)` |
177 | Stops | ≥2 direct `<stop>` children; explicit color; finite non-decreasing offset in `0..1` or `0%..100%` (ties form hard edges); optional alpha |
178 | Coordinates | `objectBoundingBox` only. Generated values: `0..1`; omitted linear axis = `(0,0) → (1,0)`. Only import-normalized linear projections may reach `-0.105..1.105`; radial values stay in `0..1`, and their effective focus must lie inside the circle centered at `(0.5,0.5)` with radius `0.5` |
179 | Forbidden | External/quoted refs, `href` inheritance, `gradientTransform`, `spreadMethod`, CSS gradients |
180
181 | Target | Contract and fidelity |
182 |---|---|
183 | `<rect>`, `<circle>`, `<ellipse>`, `<path>`, `<polygon>` fill/stroke | Linear `Native-normalized`; radial `Approximate` |
184 | `<line>` / `<polyline>` | Gradient stroke only; linear `Native-normalized`, radial `Approximate` |
185 | `<text>` / non-positional `<tspan>` | Gradient fill only; no gradient text outline |
186 | `<image>` | No gradient paint; use §6.5 overlays |
187
188 Linear export preserves stops/alpha and reduces direction to an angle;
189 coincident endpoints are invalid. Radial export preserves the effective focus
190 (`fx/fy`, otherwise `cx/cy`) as a point-focused circle; its outer center and
191 radius normalize to `0.5`, so distinct outer `cx/cy` and `r` are dropped. A
192 focus outside that canonical circle is invalid because SVG renderers clamp it
193 to the circumference while DrawingML retains the rectangle coordinates;
194 reverse import centers such a source focus and records a diagnostic.
195 Gradient strokes stay editable;
196 reverse import may keep the first stop only. Stop alpha multiplies element opacity.
197 PPTX import normalizes gradients and reports degradation;
198 `--strict` keeps the closed parser contract. See
199 [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
200 Checker/exporter preflight share this validation.
201 Gradient-stop colors are contextual paint values. Keep them coherent with the
202 deck anchors and page intent; they are not required to duplicate existing
203 Default `spec_lock.colors` literals or Quick-resolved anchors.
204
205 **Hard rule — non-degenerate gradient geometry**: an `objectBoundingBox`
206 gradient stroke requires non-zero intrinsic width and height. SVG stroke width
207 does not expand that object bounding box, so a perfectly horizontal or vertical
208 gradient ribbon disappears even when its stroke is thick. Author such a ribbon
209 as a closed shape with gradient `fill`, or use a path whose intrinsic geometry
210 has both dimensions. Checker and exporter reject the degenerate stroke form.
211
212 ```xml
213 <defs>
214 <linearGradient id="flow" x1="0" y1="0" x2="1" y2="0">
215 <stop offset="0%" stop-color="#2563EB"/>
216 <stop offset="100%" stop-color="#10B981" stop-opacity="0.7"/>
217 </linearGradient>
218 </defs>
219 <path d="M100 200 C260 80 420 320 620 180" fill="none"
220 stroke="url(#flow)" stroke-width="12"/>
221 ```
222
223 **Native text picture/texture fill**:
224
225 | Concern | Contract |
226 |---|---|
227 | Target | Direct `fill="url(#id)"` on `<text>` or a non-positional `<tspan>`; the text remains editable |
228 | Definition | Direct `<pattern>` child of `<defs>` with unique `id` and exact `data-pptx-text-image-fill="stretch"` or `"tile"` |
229 | Image | Exactly one direct SVG-namespace `<image>` child; project-local or data-URI source; explicit positive `width` / `height` |
230 | Native result | `stretch` → run-level `a:blipFill/a:stretch`; `tile` → run-level `a:blipFill/a:tile` |
231 | Alpha | Text `fill-opacity` multiplies the native picture-fill alpha |
232 | Forbidden | Preset-pattern attributes; `patternTransform`; additional pattern children; image style/alpha/clip/filter/mask/transform; use outside text; unannotated custom image patterns; multi-image/layer knockout composites |
233
234 Use this registered pattern when the design calls for a photograph, material,
235 or texture inside editable glyphs. The pattern is an authoring carrier for a
236 PowerPoint run picture fill, not a general SVG pattern promise. PowerPoint owns
237 the final run bounding box: `stretch` is `Native-normalized`, while `tile` may
238 normalize tile scale or phase and needs visual review. Forward SVG→PPTX export
239 is native; PPTX→SVG does not reconstruct run-level picture fills yet.
240
241 ```xml
242 <defs>
243 <pattern id="titleTexture" data-pptx-text-image-fill="stretch"
244 patternUnits="objectBoundingBox"
245 patternContentUnits="objectBoundingBox" width="1" height="1">
246 <image href="../images/cloud-texture.png"
247 x="0" y="0" width="1" height="1"
248 preserveAspectRatio="none"/>
249 </pattern>
250 </defs>
251 <text x="96" y="220" fill="url(#titleTexture)" fill-opacity="0.85"
252 font-family="Microsoft YaHei" font-size="72" font-weight="700">
253 国风之美
254 </text>
255 ```
256
257 Preset patterns are a separate PPT interface in [`native-data-interface.md`](./native-data-interface.md).
258
259 ---
260
261 ### 6.4 Shadows, Glow, and Elevation
262
263 Filters are native-effect metadata, not a general pixel-filter surface.
264
265 | Concern | Contract |
266 |---|---|
267 | Definition/reference | Direct `<defs><filter id="...">` child with unique id; direct `filter="url(#id)"` attribute, never inline style |
268 | Public targets | `<rect>`, `<circle>`, `<image>`, `<path>`, `<text>`; one validated compact authored shape-preset `<g>` from [`native-shape-authoring.md`](./native-shape-authoring.md) §4; an exact outer `<g filter>` whose sole visual child is one clipped `<image>` |
269 | Required primitive | `feDropShadow` or `feGaussianBlur` |
270 | Generated glow form | Zero-offset `feDropShadow` with flood paint, or the complete blur + flood + composite + merge graph below; never bare blur |
271 | Required parameters | Explicit `stdDeviation` on either effect primitive; explicit `dx`, `dy`, and `flood-opacity` on `feDropShadow`; explicit `flood-opacity` on `feFlood`; explicit `slope` on linear `feFuncA` |
272 | Accepted helpers | `feOffset`, `feFlood`, `feComposite`, `feMerge`, `feMergeNode`, `feComponentTransfer`, linear `feFuncA` |
273 | Alpha transfer | Linear `feFuncA` maps multiplicative `slope` only; `intercept` is unsupported |
274 | Blur sampling | `feGaussianBlur edgeMode` is unsupported; native effects do not expose the SVG edge-sampling modes |
275 | Primitive coordinates | Omit `primitiveUnits` or use `userSpaceOnUse`; `objectBoundingBox` coordinates are unsupported |
276 | Numeric values | Finite unitless values; non-negative `stdDeviation`; finite `dx` / `dy`; `feFuncA slope` within `0..1`; mapped glow `rad = stdDeviation × 9525`, shadow `blurRad = stdDeviation × 2 × 9525`, and shadow `dist = hypot(dx,dy) × 9525` must round into DrawingML `0..27273042316900` |
277 | Classification | Meaningful non-zero offset → one outer shadow; zero/no offset → one glow |
278 | Fidelity | `Approximate`; one filter becomes one DrawingML effect |
279
280 Flood opacity, linear `feFuncA slope`, and element opacity multiply. The
281 converter-only historical path may also multiply flood-color alpha and
282 ancestor group opacity.
283 Native export does not preserve filter-region, `in/in2/result`, merge order, or
284 composite topology. Other primitives, multiple independent effects, filters on
285 `<tspan>` / ordinary `<g>` / unsupported targets are forbidden; apply the
286 effect to supported objects or use explicit layers.
287 Special `<g filter>` targets are limited to the helper-authored compact shape
288 preset above, the exact single clipped-image form in §6.5, the hash-locked
289 `data-pptx-part="geometry-preview"` transport in §1.4—a direct child of an
290 imported preset object referencing the hidden geometry carrier's filter—and the
291 exact imported picture-crop carrier in §6.5, which keeps the effect outside its
292 viewport. The compact preset applies its filter once to the logical shape; its
293 direct registry paths remain unfiltered. None of these cases authorizes ordinary
294 group filters or creates a second PowerPoint object.
295 PPTX import maps one classifiable shape/connector/picture outer shadow or glow
296 to this contract. Unsupported effects and outer-shadow variants whose scale,
297 skew, alignment, or rotation semantics cannot be retained become import
298 diagnostics instead of a silently simplified authoring surface. See
299 [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary)
300 for tolerant, strict, and release-handling behavior.
301 The quality checker and exporter preflight enforce the same definition,
302 reference, primitive, target, and numeric-value contract. Missing required
303 geometry and malformed values are never replaced by effect defaults during
304 native export.
305
306 ```xml
307 <defs>
308 <filter id="softShadow" x="-15%" y="-20%" width="130%" height="150%">
309 <feDropShadow dx="0" dy="6" stdDeviation="8"
310 flood-color="#000000" flood-opacity="0.10"/>
311 </filter>
312 <filter id="expandedShadow" x="-15%" y="-20%" width="130%" height="150%">
313 <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="b"/>
314 <feOffset in="b" dx="0" dy="6" result="o"/>
315 <feFlood flood-color="#000000" flood-opacity="0.10" result="c"/>
316 <feComposite in="c" in2="o" operator="in" result="s"/>
317 <feMerge><feMergeNode in="s"/><feMergeNode in="SourceGraphic"/></feMerge>
318 </filter>
319 <filter id="titleGlow" x="-30%" y="-30%" width="160%" height="160%">
320 <feGaussianBlur in="SourceAlpha" stdDeviation="6" result="b"/>
321 <feFlood flood-color="#38BDF8" flood-opacity="0.45" result="c"/>
322 <feComposite in="c" in2="b" operator="in" result="g"/>
323 <feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
324 </filter>
325 </defs>
326 ```
327
328 Even `feDropShadow` with `dx="0" dy="0"` becomes glow. Use an existing accent
329 color; black reads as diffuse shadow. Bare `feGaussianBlur` remains compatible
330 input but is never generated: preview blurs the object while export emits glow.
331
332 | Elevation | Use | `dy` | `stdDeviation` | Alpha |
333 |---|---|---:|---:|---:|
334 | Floor | Backgrounds, dividers, equal peers, body containers, decorative lines/icons, single-layer pages | — | — | — |
335 | Resting | Card over photo/panel, secondary callout | 2–4 | 4–8 | 0.06–0.10 |
336 | Raised | Primary CTA, focused card, overlay | 6–10 | 10–16 | 0.12–0.20 |
337 | Glow | Short display text, metric, focus accent | 0 offset | 4–8 | 0.35–0.55 |
338
339 **Default — one light source per page (may override when every affected layer
340 uses one deliberate alternative direction)**: every `feOffset` shadow on one
341 slide shares the same `dx`/`dy` direction (default `dx="0"`,
342 `dy="4"`–`dy="8"`, light from upper front). Contradictory shadow directions
343 make one plane read as several incompatible surfaces. A deliberate upward
344 paper-layer treatment flips every affected layer together; never mix
345 directions on the same plane.
346
347 **Reference — not a constraint**: use no more elevation categories than the
348 hierarchy needs; a page may reuse one category across several related objects.
349 Do not lift every peer card or stack strong shadow, border, gradient, and tint
350 on one container. Same-family colored shadow is reserved for a focal accent.
351 On dark backgrounds, prefer a light hairline or restrained glow; never glow body copy.
352 For older/strict renderers, replace a filter with two or three offset
353 translucent shapes behind the object:
354 alpha `0.03–0.05`, increasing offset/radius, and optional same-family tint near
355 `0.04` (`Native-stable`).
356
357 ---
358
359 ### 6.5 Image Treatments, Overlays, and Glass-like Surfaces
360
361 #### Image Carrier and Crop Contracts
362
363 | Need | Authoring contract | Fidelity |
364 |---|---|---|
365 | Cover/crop | Readable raster dimensions + aligned `slice` | Native `srcRect`; `Native-stable`; otherwise native crop cannot be guaranteed |
366 | Contain/fit | Aligned `meet` | Fitted picture frame; `Native-normalized` |
367 | Stretch | `preserveAspectRatio="none"` | Native stretched frame |
368 | Uniform fade | `<image opacity="...">` | Native picture alpha |
369 | Shaped picture | §1.2 image-only `clip-path` | Preset/custom picture geometry |
370
371 **Hard rule — closed image aspect-ratio grammar**: on `<image>`, omit
372 `preserveAspectRatio` for the default `xMidYMid meet`, use `none` alone for
373 stretch, or use one of the nine case-sensitive alignments (`xMinYMin`,
374 `xMidYMin`, `xMaxYMin`, `xMinYMid`, `xMidYMid`, `xMaxYMid`, `xMinYMax`,
375 `xMidYMax`, `xMaxYMax`) followed by explicit `meet` or `slice`. Generated SVG
376 always includes the mode on an aligned value. An alignment without a mode and
377 values needing whitespace normalization are compatible input and receive a
378 Checker recommendation. Empty values, `defer`, unknown/wrong-case alignments or
379 modes, `none` with a mode, and extra tokens are errors; the converter never
380 guesses a fallback.
381
382 **Hard rule — fit/clip interaction**: a non-trivial clip disables `meet`
383 frame-fit. Match the image box to the source ratio or use `slice`. Put one §6.4
384 filter directly on an unclipped `<image>`. For a clipped picture, keep
385 `clip-path` on the `<image>` and put the filter on an exact outer `<g>` whose
386 sole visual child is that image. Never combine `filter` and `clip-path` on the
387 same `<image>`: SVG would clip the preview effect while PowerPoint would not.
388 The carrier may keep object-local id, role, transform, and
389 `data-pptx-carrier`. It may own `data-pptx-layer="master|layout"` only when
390 the carrier itself is the direct fixed atom. It must not own
391 `data-pptx-placeholder`, `data-pptx-binding`, or chart/table replacement
392 metadata; keep slot ownership on the outer placeholder boundary.
393
394 **Hard rule — picture frames and sources are explicit and decodable**: every
395 SVG `<image>` has explicit positive `width`/`height` and exactly one non-empty
396 `href` or compatible `xlink:href`. A data URI must use a supported `image/*`
397 MIME type, valid strict base64 when marked
398 `base64`, a non-empty payload, and bytes that decode as the declared format.
399 An external asset must resolve, use a supported extension, be non-empty, and
400 decode as that extension. The registered formats are PNG, JPEG, GIF, WebP,
401 BMP, TIFF, SVG, EMF, and WMF. Explicit template substitution tokens may remain
402 unresolved only during template checking; export requires the resolved image.
403 Missing, ambiguous, corrupt, mislabeled, or unsupported sources are errors and
404 must never be dropped or packaged as invalid zero-byte media.
405
406 **Hard rule — nested SVG is picture-crop transport, not a general viewport**:
407 every non-root `<svg>` is the exact wrapper accepted by the shared crop parser:
408
409 | Part | Required form |
410 |---|---|
411 | Outer | Registered `x`, `y`, positive `width`/`height`; four ordinary-decimal unit coordinates in `viewBox`; `preserveAspectRatio="none"`; `overflow="hidden"` |
412 | Child | Exactly one direct empty `<image>` with one non-empty `href`/`xlink:href`, `x="0" y="0" width="1" height="1" preserveAspectRatio="none"` |
413 | Context | Only root SVG / ordinary visual `<g>` ancestors; outer may add `id`, supported `transform`, registered layer/carrier metadata, and `data-pptx-frame`, `data-pptx-object`, `data-pptx-shape-id`, `data-pptx-shape-name`, `data-pptx-shape-scope`; an exact imported picture carrier may hold its one §6.4 filter outside this viewport |
414 | Shape crop | Exact outer `data-pptx-crop="1"`; authored wrappers put the registered, locally resolving image-only clip on the inner image, using `userSpaceOnUse` geometry matching the visible `viewBox`; legacy imported outer clips remain compatible |
415
416 The inner image may add only registered `opacity` and that clip. Quantize the
417 `viewBox` without clamping: every signed crop fits
418 `-2147483648..2147483647`, with `l + r < 100000` and `t + b < 100000`.
419 Retain negative/outside-source crops exactly; write redundant `0 0 1 1` as a
420 plain `<image>`. Extra, indirect, or character content; unknown attributes;
421 malformed or unrepresentable crops; and general nested viewports fail. Checker
422 and converter share this parser.
423
424 #### Image Overlay and Material Techniques
425
426 | Overlay | Construction | Typical stops / alpha |
427 |---|---|---|
428 | Directional scrim | Linear rect, darkest beside text | `0%: 0.88; 55%: 0.30; 100%: 0` |
429 | Bottom title fade | Vertical rect over lower image | black `0 → 0.72` |
430 | Vignette/spotlight | Radial rect; place the hotspot with `fx/fy` or `cx/cy` inside the canonical focus circle; outer center/radius remain approximate | black `0 → 0.58` |
431 | Brand wash | Directional existing brand-color gradient | `0.80 → 0.10` |
432 | Grid scrim | Seamless no-stroke rect cells over one image; vary neighboring alpha narrowly and irregularly | Keep the field subordinate; a regular alternation reads as a checkerboard |
433 | Faux glass | Visible fields + diagonal linear panel (`0,0 → 1,1`) + highlight stroke; optional §6.4 elevation | white `0.38 → 0.12`; stroke about `0.55` |
434
435 Layer in document order: image → scrim/wash → text. True source/backdrop blur is
436 `Bake-required`; faux glass is explicit layering, not blur. Validate contrast
437 against the actual image. All overlay gradients follow §6.3 linear/radial
438 fidelity.
439
440 ---
441
442 ### 6.6 Lines, Connectors, Borders, and Markers
443
444 | Surface | Contract / native result |
445 |---|---|
446 | Solid stroke/width/alpha | `Native-stable` editable line |
447 | `4,4`; `6,3`; `2,2`; `8,4`; `8,4,2,4` (comma or space separators) | `dash`; `dash`; `sysDot`; `lgDash`; `lgDashDot` (`Native-normalized`) |
448 | Canonical custom dash | Exactly two positive finite unitless ordinary decimals (`dash gap`); export scales/quantizes against stroke width; `Native-normalized` |
449 | Compatible custom dash | Three or more positive finite unitless values are accepted but reduce to the first pair with a Checker recommendation; compatible numeric spellings also warn |
450 | `stroke-linecap` | `butt`, `round`, `square`; `Native-stable` |
451 | `stroke-linejoin` | `miter`, `round`, `bevel`; `Native-stable` |
452 | `vector-effect` | Exactly `none` or `non-scaling-stroke`; export resolves the choice into native line width (`Native-normalized`) |
453 | `stroke-dashoffset` | No general line mapping; allowed only as a direct finite unitless ordinary-decimal attribute on a §6.10 thick-circle shorthand (`px` suffix is compatible input and warns) |
454 | Gradient stroke | §6.3; re-import may flatten to first stop |
455 | `marker-start` / `marker-end` | §1.1 native line end; type `Native-normalized`, size `Approximate` (`sm/med/lg`) |
456
457 PPTX import treats unsupported line properties as source diagnostics: tolerant
458 mode retains the object and omits only the unsupported outline; `--strict`
459 retains the closed rejection behavior. See
460 [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
461
462 The dash grammar is closed: exact lowercase `none`, or at least two finite
463 unitless numbers separated by whitespace or one comma. Generated SVG uses
464 ordinary decimal spellings. A leading plus sign, exponent, trailing decimal
465 point, surrounding whitespace, or longer custom list is compatible input and
466 produces a non-blocking normalization recommendation. Unknown units, one-value
467 arrays, empty or repeated comma fields, non-finite values, and negative or zero
468 entries are errors. The only zero exception is a gap declared directly on the
469 §6.10 thick-circle element.
470
471 Generated cap, join, and `vector-effect` values use the exact lowercase tokens
472 in the table. Surrounding whitespace is compatible input and produces a
473 recommendation; every other token is an error.
474
475 **Default — relationship-fit dash rhythm (may override when style calls for
476 another rhythm)**: After §6.1 selects dash, preserve direction markers and
477 branch-owned placeholder patterns.
478
479 | Already-dashed/dotted job (illustrative) | Dash |
480 |---|---|
481 | Separator/boundary | `4,4` |
482 | Subtle dotted border/generic non-image placeholder | `2,2` |
483 | Optional/future timeline/flow connector | `8,4` |
484 | Technical/dimension line | `8,4,2,4` |
485
486 ```xml
487 <!-- General boundary versus quiet non-image placeholder -->
488 <rect x="60" y="60" width="400" height="240" rx="12"
489 fill="none" stroke="#999999" stroke-width="2" stroke-dasharray="4,4"/>
490 <line x1="100" y1="360" x2="1180" y2="360"
491 stroke="#CCCCCC" stroke-width="1" stroke-dasharray="2,2"/>
492
493 <!-- Optional/future timeline connector versus technical dimension line -->
494 <line x1="100" y1="420" x2="500" y2="420"
495 stroke="#1A73E8" stroke-width="2" stroke-dasharray="8,4"/>
496 <line x1="620" y1="420" x2="1020" y2="420"
497 stroke="#555555" stroke-width="2" stroke-dasharray="8,4,2,4"/>
498 ```
499
500 **Default — contour-fit joins (may override when the resolved style calls for
501 another character)**:
502
503 | Contour/job (illustrative) | Join |
504 |---|---|
505 | Smooth polyline/organic form | `round` |
506 | Technical diagram | `bevel` |
507 | Crisp rectangle/arrow | `miter` |
508
509 ```xml
510 <!-- Smooth series: round avoids a miter spike at the turn. -->
511 <polyline points="100,200 200,100 300,200" fill="none"
512 stroke="#1A73E8" stroke-width="3" stroke-linejoin="round"/>
513
514 <!-- Technical corner versus crisp rectangle -->
515 <polyline points="400,200 500,100 600,200" fill="none"
516 stroke="#555555" stroke-width="3" stroke-linejoin="bevel"/>
517 <rect x="740" y="120" width="180" height="160" fill="none"
518 stroke="#333333" stroke-width="3" stroke-linejoin="miter"/>
519 ```
520
521 Match marker paint to the parent stroke using the shape-specific channel from
522 §1.1: fill for closed/oval line ends and stroke for the open arrow. Use markers
523 for connectors and §6.10 calculated geometry for a manual diagonal arrowhead.
524 When exact grid spacing matters, use one multi-subpath path rather than a
525 fixed-density preset pattern:
526
527 ```xml
528 <path d="M40 0V120 M80 0V120 M0 40H120 M0 80H120"
529 fill="none" stroke="#2E6EA8" stroke-width="0.8"/>
530 ```
531
532 ---
533
534 ### 6.7 Advanced Text Treatments
535
536 **Hard rule — closed text property grammar**: generated text uses only the
537 values in the `Canonical authoring` column. Registered compatible input remains
538 convertible and receives a non-blocking normalization recommendation. Every
539 other value is invalid; the converter must not replace it with a default.
540
541 | Property | Canonical authoring | Compatible input | DrawingML mapping / rejection boundary |
542 |---|---|---|---|
543 | `font-weight` | `normal`, `bold`, or an exact integer hundred from `100` through `900` | `medium` → `500`; `semibold` → `600` | `normal` and `100..500` map to regular; `bold` and `600..900` map to `b="1"`; therefore numeric weights are `Native-normalized` |
544 | `font-style` | `normal` or `italic` | None | `italic` maps to `i="1"`; oblique, angle, relative, and CSS-wide values are invalid |
545 | `text-anchor` | `start`, `middle`, or `end` on `<svg>`, `<g>`, or `<text>` | None | Maps to left/center/right paragraph alignment plus normalized frame position; it is invalid on `<tspan>` because run-level anchoring has no mapping |
546 | `text-decoration` | `none`, `underline`, `line-through`, or `underline line-through` | `line-through underline` → canonical order | Maps to the single underline and strike run properties; unknown, repeated, or substring-like tokens are invalid |
547 | `baseline-shift` | Exact direct `super` or `sub` on `<tspan>` | None | Maps to editable ordinary-text `a:rPr@baseline` at `30000` or `-25000`; it does not resize the run, is invalid as inline style or on any other element, and cannot combine with an inline formula marker |
548 | `letter-spacing` | Finite unitless ordinary decimal SVG px | The same ordinary decimal with `px`, `pt`, or `em`; normalize to unitless px | Maps to `a:rPr@spc`; the final value must fit DrawingML `-400000..400000`, and negative tracking must leave every generated DrawingML run with a positive estimated advance and its text frame with a positive extent; keywords, percentages, exponents, leading plus signs, trailing decimal points, non-finite values, and other units are invalid |
549
550 The registered inheritable text properties follow SVG inheritance, including
551 declarations on the root `<svg>`: inline `style` overrides the same element's
552 direct attribute, which overrides its ancestor. `baseline-shift` is the narrow
553 exception: declare it directly on the owning `<tspan>`; nested inline content
554 inherits that run shift, while surrounding text keeps its own baseline.
555 Relative font sizes and `em` tracking resolve against the same effective
556 inherited size in Checker and converter. Every declaration is validated even
557 when a later declaration overrides it, so hidden garbage cannot bypass
558 preflight.
559
560 The DrawingML character-spacing range is necessary but not sufficient for
561 negative tracking. After run assembly, each output run must retain a positive
562 estimated advance using the quantized `sz` and `spc` values that will actually
563 be written; a wider sibling run or paragraph line cannot hide a run whose
564 aggregate advance would reverse or collapse, which can reorder or drop
565 characters across PowerPoint-compatible renderers. The generated text frame
566 must also retain a positive horizontal and vertical extent. Checker rejects
567 directly measurable single-line violations, and the converter revalidates
568 every generated run and text frame before writing OOXML. It must not clamp,
569 take the absolute value of, or otherwise hide a non-positive advance or extent.
570 Adjacent authored runs with identical final DrawingML run properties form one
571 output run before sizing and validation; splitting text across equivalent
572 `<tspan>` nodes is not a tracking escape hatch. Tracking and width estimates
573 count the registered project text clusters rather than raw Unicode code points:
574 combining marks, variation selectors, emoji modifiers and ZWJ sequences,
575 paired regional indicators, and same-script virama conjuncts do not receive
576 internal spacing.
577 An unchanged imported native text body reuses the geometry carrier's positive
578 shape frame and attaches the preserved `txBody` payload instead of regenerating
579 runs or a text frame from the SVG estimate.
580
581 **Hard rule — element-specific text surface**:
582
583 - Inheritable text declarations belong only on `<svg>`, `<g>`, `<text>`, or
584 `<tspan>`; placing them on geometry, image, definition, or reuse elements is
585 an error rather than ignored decoration.
586 - `<text>` accepts `x`, `y`, registered paint/alpha/run properties, the text
587 properties above, `font-family`, `font-size`, direct `filter`, direct
588 `transform`, `xml:space`, `id`, and project `data-*` metadata.
589 - `<tspan>` accepts `x`, `y`, `dx`, `dy`, registered paint/alpha/run
590 properties, `font-family`, `font-size`, `font-weight`, `font-style`,
591 `letter-spacing`, `text-decoration`, direct `baseline-shift`, `xml:space`,
592 `id`, and project `data-*` metadata. It does not accept `text-anchor`,
593 `filter`, or `transform`.
594 - `word-spacing`, `dominant-baseline`, `alignment-baseline`,
595 font shorthand/variant/stretch/feature/variation/synthesis controls,
596 `font-kerning`/`kerning`, `font-size-adjust`, `line-height`, text alignment,
597 indent/shadow/rendering controls, white-space/word-break/hyphenation
598 controls, `writing-mode`, `vertical-align`, `direction`, `unicode-bidi`, and
599 `text-transform` have no registered native mapping and are errors as direct
600 attributes or inline style.
601 - Any other unregistered `font-*` or `text-*` property is also an error; the
602 closed grammar must not grow through an ignored CSS spelling.
603
604 **Hard rule — project text whitespace**:
605
606 - `xml:space` is the project's closed authoring control for significant text
607 whitespace. It is valid only as an exact direct attribute on `<text>` or
608 `<tspan>`, accepts only the case-sensitive values `default` and `preserve`,
609 inherits through the text tree, and may be reset on a child `<tspan>`.
610 - The project maps this control to the visible Chromium/SVG2 behavior used by
611 Live Preview; it does not claim the legacy SVG 1.1 newline-deletion model.
612 XML line endings and tabs become U+0020 SPACE. In `default` mode, contiguous
613 U+0020 characters collapse across inline run boundaries and leading or
614 trailing default-mode spaces in the resulting text chunk are removed. In
615 `preserve` mode, every resulting U+0020 character remains significant.
616 - Only XML whitespace is normalized. NBSP, ideographic space, and other
617 Unicode spacing characters remain literal text and must not be rewritten by
618 a generic Unicode-whitespace regular expression.
619 - Source line breaks do not create PowerPoint paragraphs. Use the registered
620 positioned-`tspan`/paragraph structure for visual lines, and preserve DOM
621 text/tail order plus original style inheritance when normalizing that
622 structure.
623
624 These allowlists are additive to the global structural blacklist and the
625 paint, font-size, opacity, filter, and transform value contracts owned by their
626 respective sections; they do not weaken those contracts.
627
628 | Treatment | SVG surface | Result / boundary |
629 |---|---|---|
630 | Underline / strike / both | `text-decoration="underline"`, `line-through`, or both | `Native-stable`; both emits both run properties |
631 | Mixed runs | Non-positional `<tspan>` | One `Native-normalized` editable frame; §4.2 |
632 | Superscript / subscript | Direct `baseline-shift="super|sub"` on `<tspan>` | Editable ordinary-text run at PowerPoint's native baseline offset; set `font-size` on the same run when a smaller glyph is intended |
633 | Font size | Generated default is a finite unitless SVG px value; compatible `px`, `pt`, `pc`/`pica`, `in`, `cm`, `mm`, `q`, `em`, and `rem` values receive a recommendation warning only | Converted to SVG px, then editable DrawingML point size; unsupported units/percentages error |
634 | Tracking | §6.7 closed `letter-spacing` grammar | `Native-normalized`; compatible units normalize to SVG px before DrawingML conversion |
635 | Transparency | `opacity` / `fill-opacity` on text/run | `Native-normalized` run alpha, not isolated compositing |
636 | Gradient fill | §6.3 gradient on text/run | Editable fill; geometry normalizes |
637 | Outline | Solid `stroke`, `stroke-width`, `stroke-opacity` | `Native-normalized` editable run outline; re-import does not reconstruct it |
638 | Shadow/glow | §6.4 filter on `<text>` only | Shape shadow / run glow; `Approximate` |
639 | Native bullet | Leading `· • ● ▪ ■ ◆ ◇ ◦ ‣` + non-empty content | `·`/`•` → `•`; others unchanged; color/alpha from marker run; font/size follow text |
640
641 **Default — lift key information (may override when uniform treatment is
642 deliberate)**: In prose, lift numerical results, explicit contrasts, and one or
643 two load-bearing nouns per sentence with bold `<tspan>` runs in the locked or
644 Quick-resolved accent. Keep connectives, routine verbs, non-load-bearing nouns,
645 decorative adjectives, and structural copy neutral; reserve green/red for
646 actual polarity.
647
648 ```xml
649 <!-- Uniform: the two results disappear into the sentence. -->
650 <text x="80" y="200" font-size="20" fill="#333333">
651 2024年公司营收同比增长35%达到12亿元创历史新高
652 </text>
653
654 <!-- Lifted: data-bearing runs carry the scan. -->
655 <text x="80" y="200" font-size="20" fill="#333333">
656 2024年公司营收同比<tspan fill="#1A73E8" font-weight="bold">增长35%</tspan>达到<tspan fill="#1A73E8" font-weight="bold">12亿元</tspan>创历史新高
657 </text>
658 ```
659
660 **Default — semantic underline (may override when another cue is clear)**:
661 Reserve it for links, key terms, or local emphasis—not decoration.
662
663 ```xml
664 <!-- Key term -->
665 <text x="100" y="200" font-size="20" fill="#333333"
666 text-decoration="underline">Important Term</text>
667
668 <!-- Link: decorate the linked run, not the whole sentence. -->
669 <text x="100" y="240" font-size="18" fill="#333333">Read <a
670 href="https://example.com"><tspan text-decoration="underline">the guide</tspan></a>.</text>
671 ```
672
673 **Hard rule — generated decorative lettering ownership**: Approved AI
674 decorative lettering is a prepared `<image>` asset under the image contracts,
675 not an advanced native-text treatment. Keep ordinary editable titles and
676 subtitles as normal `<text>`; this contract does not add WordArt, text warp, or
677 text-on-path authoring.
678
679 ```xml
680 <text x="100" y="200" font-size="20" xml:space="preserve">Current <tspan
681 fill="#999999" text-decoration="line-through">old</tspan> value</text>
682 <text x="100" y="240" font-size="20">CO<tspan
683 baseline-shift="sub" font-size="14">2</tspan></text>
684 ```
685
686 Use strikethrough for removed/former values; it is ordinary notation, not a
687 style-exclusive effect. Imported double underline/strike normalizes to single.
688 Bullet detection allows optional leading whitespace, requires non-empty content,
689 and leaves non-leading decorative glyphs as ordinary text.
690 Keep body tracking normal; CJK tracking defaults near/below 2% of font size and
691 above 5% triggers review. Text outline is solid only. `textPath`, masks, blend
692 modes, generated effects, and text-image knockouts are outside editable text.
693
694 ---
695
696 ### 6.8 Transforms, Layering, and Static Reuse
697
698 | Surface | Contract / fidelity |
699 |---|---|
700 | `rotate(angle[, cx, cy])` | Geometry/image/text/ordinary group; `Native-normalized` |
701 | `translate(x y)` | Geometry/image/group; pure translation also safe on text; `Native-normalized` |
702 | Positive scale / negative mirror | Geometry/image or a group/use whose expanded visual subtree is geometry/image only; explicit pivot; `Native-normalized` |
703 | `matrix(a b c d e f)` | Geometry/image or the same geometry/image-only group/use; transformed axes finite, non-zero, orthogonal; excludes rounded rectangles and subtrees containing them; `Native-normalized` |
704 | Source order | Back-to-front PPT z-order; `Native-stable` |
705 | `<g opacity>` | Compatible approximate mapping; generated SVG prefers descendant alpha, §2.2 |
706 | Local `<use>` | §1.3 compile-time reuse; `Native-normalized` |
707
708 **Hard rule — closed transform grammar**: Use only lowercase `translate`,
709 `scale`, `rotate`, and `matrix` with exact finite unitless argument counts:
710 `translate` 1/2, `scale` 1/2, `rotate` 1/3, and `matrix` 6. Separate arguments
711 and operations with whitespace or one comma. Leading/trailing/repeated commas,
712 adjacent operations without a separator, units, unknown functions, and
713 incomplete input fail quality check and export. Generated numeric tokens use
714 ordinary decimals; a supported leading `+`, exponent, or trailing decimal point
715 remains compatible input and receives a non-blocking normalization warning.
716 Model-facing translation values, rotation centers, and matrix `e/f` use at
717 most two decimals under §1.4; angles, scale arguments, and matrix `a/b/c/d`
718 retain the precision required by the transform.
719
720 Set text size/position directly. A text transform is either a translate-only
721 list or one rotate operation; do not scale, matrix-transform, or mix operations
722 on text. A group containing text follows the same translate-only/single-rotate
723 limit. `skewX`, `skewY`, zero/non-orthogonal axes, and shear matrices are
724 forbidden. Native chart/table markers allow translate/scale only. The §6.10
725 thick-circle shortcut does not inherit general transform support. Positive
726 rotation is clockwise and pivoted rotation normalizes the native frame. Every
727 cumulative matrix, including transforms split across ancestors, must remain
728 finite, non-zero, and orthogonal; importer/live-editor matrices do not expand
729 the hand-authored contract.
730 Mirror around vertical pivot `cx` with
731 `translate(cx 0) scale(-1 1) translate(-cx 0)`; use the analogous Y sequence
732 for a horizontal pivot. During mirror materialization, imported PowerPoint
733 groups with an axis flip keep their geometry reflection, while each descendant
734 SVG text node receives the matching counter-reflection so browser previews keep
735 glyphs upright. The tool-side native record retains the source group flip.
736
737 Layer back-to-front: background/image → scrim/shadow → main geometry → labels /
738 icons → top annotation. Finalization and native export independently expand
739 `<use>` into cloned editable primitives; PowerPoint does not retain a symbol /
740 instance graph.
741
742 ---
743
744 ### 6.9 Freeform Shapes and Curves
745
746 | Input | Native normalization | Fidelity |
747 |---|---|---|
748 | `M/L/H/V`, absolute or relative | Absolute `M/L` | `Native-normalized` |
749 | `C` | Cubic Bézier | `Native-normalized` |
750 | `S/Q/T` | Explicit cubic controls | `Native-normalized` |
751 | `A` | Cubic segments of at most 90° | `Approximate` |
752 | `Z`; polygon/polyline | Closed/open freeform | `Native-normalized` |
753
754 **Hard rule — complete freeform grammar**: Generated `path@d` and
755 `polygon` / `polyline@points` use finite unitless ordinary decimals and only
756 the commands registered above. Native export consumes the complete attribute;
757 it never extracts recognizable fragments while ignoring other characters.
758 Finite scientific notation, a leading plus sign, and a trailing decimal point
759 remain read-compatible and receive recommendation warnings; generated SVG does
760 not write them. Unknown commands or characters, misplaced/repeated commas,
761 non-finite numbers, missing attributes, incomplete command groups, and odd
762 point counts are invalid. A path starts with `M` / `m`; `A` radii are
763 non-negative and both arc flags are exactly `0` or `1`. Each registered path
764 command accepts its uppercase absolute and lowercase relative form. Legal
765 separator-free arc flag sequences remain valid and are parsed as individual
766 flag tokens. A polygon has at least three coordinate pairs and a polyline at
767 least two.
768
769 **Validation**: Checker and native export consume the same parser in
770 [`paths.py`](../scripts/svg_to_pptx/drawingml/paths.py); native-object fallback
771 bounds reuse its normalized commands rather than a second path grammar.
772
773 **Reference — not a constraint**: use the fewest curve segments and control
774 points that preserve the intended silhouette. Set endpoints and tangent
775 directions first; use `S` after `C` or `T` after `Q` when reflected controls
776 preserve deliberate tangent continuity.
777
778 ```xml
779 <path d="M80 300 C180 180 300 180 400 300 S620 420 720 300"
780 fill="none" stroke="#2563EB" stroke-width="4" stroke-linecap="round"/>
781 <path d="M80 520 Q240 400 400 520 T720 520"
782 fill="none" stroke="#0F766E" stroke-width="4" stroke-linecap="round"/>
783 ```
784
785 Command identity, relative coordinates, shorthand, arc parameters, and original
786 handles are not retained. Geometry needs non-zero bounds. Before authoring a
787 freeform, apply [`native-shape-authoring.md`](./native-shape-authoring.md):
788 prefer editable primitives and exact Office presets, independently composed
789 when possible; materialize a Boolean only when one contour requires it. Use a
790 closed cubic path only for an organic silhouette those
791 cannot express, polygon/closed path for unmatched ribbons/facets, and an open
792 path only for a required data curve, custom route, or locked or Quick-resolved
793 hand-drawn / organic style. Straight relationships use `<line>`; exact stock bends/curves
794 use an authored native Connector preset. Multi-`M` paths remain available for
795 exact linework, and a [`shared-standards-core.md`](./shared-standards-core.md)
796 §1.2 path clip for unmatched organic pictures. Filled silhouettes end with
797 `Z`; open paths use `fill="none"`. Do not depend on
798 `fill-rule="evenodd"`; build explicit visible geometry or bake an essential
799 knockout.
800 For a fixed background, a background-colored overlay is also valid.
801
802 | Rounded rect input | Result |
803 |---|---|
804 | One positive radius, or `0 < rx == ry <= min(width,height)/2` | `Native-stable` adjustable `roundRect` without distorting transforms; the same short-side limit applies to one-radius input |
805 | `0 < abs(rx-ry) < 0.5px` after scaling | One normalized native radius; `Approximate` |
806 | `abs(rx-ry) >= 0.5px`, either positive | Cubic custom geometry; no radius handle; `Approximate` |
807 | Equal radius above half the short side | Native short-side clamp may differ from SVG; `Approximate` |
808
809 ---
810
811 ### 6.10 Radial Geometry, Donuts, Gauges, Sunbursts, and Diagonal Arrowheads
812
813 For center `(cx,cy)`, radius `r`, and degrees `θ`:
814
815 ```text
816 x = cx + r × cos(θ × π / 180)
817 y = cy + r × sin(θ × π / 180)
818 ```
819
820 For clockwise pie/donut sectors, default to `-90°` only when the chart starts at
821 12 o'clock. A full-circle percentage sector spans `percentage × 360°`;
822 large-arc is `1` above `180°`; outer sweep is `1`, inner return is `0`. Split
823 both outer and inner boundaries of a full ring into at least two arcs each.
824 Calculated endpoints survive subject to EMU rounding; `A` curves remain cubic
825 approximations. Verify all spans plus gaps against the planned sweep.
826 Explicit arc sectors are editable `Approximate` freeforms. Thin circles using a
827 §6.6 preset/two-number dash stay `Native-normalized` ellipse lines.
828
829 ```xml
830 <!-- 75% donut: center 400,400; outer 180; inner 100; -90° → 180°. -->
831 <path d="M400 220 A180 180 0 1 1 220 400
832 L300 400 A100 100 0 1 0 400 300 Z" fill="#2563EB"/>
833 ```
834
835 **Gauge**: require `max > min`, `p = clamp((value-min)/(max-min),0,1)`, and
836 `0 < planned clockwise sweep <= 360°`; value sweep is `p × planned sweep`.
837 `valueEndAngle = startAngle + valueSweep`; large-arc is `1` iff
838 `abs(valueSweep) > 180°`.
839 Omit the value sector at `p=0`. At `p=1` with `360°`, split both boundaries into
840 at least two arcs. Track/value share center, radii, start, and sweep flags.
841
842 **Sunburst — `Approximate`**: one explicit annular sector per node; each depth owns one radius
843 band and child angular intervals partition the parent. Do not use one `evenodd`
844 compound ring.
845
846 **Thick-circle shorthand — `Approximate`, non-position-sensitive only**:
847
848 - One circle per segment; `fill="none"`; the circle may use one `rotate` for its
849 start angle, and ancestor transforms must be translate-only.
850 - Exactly two non-preset finite unitless ordinary-decimal values (`dash gap`);
851 `stroke-dashoffset` is a direct finite unitless ordinary-decimal attribute.
852 - `0 < stroke-width < 2r`, `stroke-width/r >= 0.15`,
853 `0 < dash < 2πr`, `gap >= 0`, and `dash + gap >= 2πr - 1` SVG unit. The
854 one-unit tolerance exists only for integer-rounded circumference values.
855 - Native construction uses only the first dash and re-imports as a freeform.
856 Its native start is 90° counterclockwise from the SVG preview; use explicit
857 arcs whenever start angle, cap, or radial precision matters.
858
859 ```xml
860 <circle cx="400" cy="400" r="140" fill="none" stroke="#2563EB"
861 stroke-width="48" stroke-dasharray="615.75 263.90" stroke-dashoffset="0"/>
862 ```
863
864 **Diagonal polygon arrowhead**: for a non-zero line, calculate rather than use a
865 fixed triangle:
866
867 ```text
868 dx=x2-x1; dy=y2-y1; len=√(dx²+dy²); ux=dx/len; uy=dy/len
869 px=-uy; py=ux
870 tip=(x2,y2)
871 back1=(x2-ux×12+px×5, y2-uy×12+py×5)
872 back2=(x2-ux×12-px×5, y2-uy×12-py×5)
873 ```
874
875 Use §1.1 markers for ordinary connectors; the polygon is for a manually drawn
876 filled `Native-normalized` arrowhead. Example:
877 `<polygon points="370,430 365.6,417.8 358.2,424.6"/>`.
878
879 ---
880
881 ### 6.11 Constructed Technique Recipes
882
883 **Hard rule — explicit construction**: these are supported-layer recipes, not
884 browser-filter permissions.
885
886 **Reference — not a constraint**: use them only when they match the locked or
887 Quick-resolved style. Their curve recipes are explicit exceptions to the
888 Shape-first default above; they do not authorize decorative freeforms in
889 another style.
890
891 | Family | Technique | Use when | Construction / boundary |
892 |---|---|---|---|
893 | Material / depth | Faux glass | Visible field must remain present behind a panel | §6.5 translucent panel + highlight; no backdrop blur; `Native-normalized` |
894 | Material / depth | Paper cut | Ordered layers/openings carry the material language | Organic paths + one §6.4 shadow per layer, never the group; `Approximate` |
895 | Hand / print | Hand-drawn mark | Annotation, underline, or highlighter gesture | Rotated translucent bar + restrained `Q/C` paths + round caps; no roughness filter; `Native-normalized` |
896 | Hand / print | Ink wash | Brush mass or atmosphere | Same-family translucent curves/strokes; no feather/wet edge; `Native-normalized` |
897 | Hand / print | Riso offset | Deliberate print misregistration | Offset duplicate, second ink, lower alpha; no blend mode; `Native-normalized` |
898 | Hand / print | Pixel grid | Sparse hard-cell digital accent | Integer-aligned rect grid; `shape-rendering` preview-only; `Native-stable` |
899 | Hand / print | Halftone | Sparse screen modulation | Calculated circles; `Native-stable`; bake dense screens or use [`native-data-interface.md`](./native-data-interface.md) |
900 | Form / geometry | Faceted or folded form | Isometric object, folded ribbon, dimensional numeral/band | Shared vertices, one light direction, same-hue alternating paint per [`native-shape-authoring.md`](./native-shape-authoring.md) §7.1; no 3D; `Native-normalized` |
901 | Form / geometry | Gradient ribbon | Continuous directional energy, not faceted depth | Cubic gradient stroke or closed gradient-filled band; no mesh gradient; `Native-normalized`, re-import may flatten color |
902 | Data expression | Line plus area | Magnitude context beneath an exact reading edge | Subordinate low-alpha area first, crisp line above; `Native-normalized` |
903
904 **Minimal construction anchors**:
905
906 ```xml
907 <!-- Hand-drawn + ink. -->
908 <rect x="80" y="80" width="240" height="28" fill="#FDE68A"
909 opacity="0.72" transform="rotate(-1,200,94)"/>
910 <path d="M90 150 Q210 142 330 151" fill="none" stroke="#1F2937"
911 stroke-width="3" stroke-linecap="round"/>
912 <path d="M80 220 C160 160 250 180 330 230 Z" fill="#1F2937" opacity="0.16"/>
913 <path d="M90 240 C180 210 250 260 340 220" fill="none" stroke="#1F2937"
914 stroke-width="10" stroke-linecap="round" opacity="0.70"/>
915
916 <!-- Riso, pixel cells, sparse dots. -->
917 <text x="86" y="320" font-family="Arial, sans-serif" font-size="64"
918 fill="#EC4899" opacity="0.85">PRINT</text>
919 <text x="92" y="326" font-family="Arial, sans-serif" font-size="64"
920 fill="#2563EB">PRINT</text>
921 <g id="pixel-cells" shape-rendering="crispEdges" fill="#2563EB">
922 <rect x="400" y="80" width="16" height="16"/><rect x="416" y="80" width="16" height="16"/>
923 </g>
924 <g id="sparse-dots" fill="#EC4899"><circle cx="410" cy="140" r="3"/><circle cx="426" cy="140" r="6"/></g>
925
926 <!-- Isometric facets + line-over-area. -->
927 <g id="isometric-facets" transform="translate(520 160)">
928 <polygon points="0,0 80,-24 160,0 80,24" fill="#60A5FA"/>
929 <polygon points="0,0 0,48 80,72 80,24" fill="#3B82F6"/>
930 <polygon points="80,24 80,72 160,48 160,0" fill="#2563EB"/>
931 </g>
932 <path d="M760 260 L860 220 L960 250 L960 340 L760 340 Z" fill="#2563EB" opacity="0.10"/>
933 <path d="M760 260 L860 220 L960 250" fill="none" stroke="#2563EB" stroke-width="4"/>
934 ```
935
936 **Default — integer pixel grid (may override for deliberate irregular
937 treatment)**: avoid soft scaling; use explicit dots only for sparse editable
938 halftone and route dense full-slide texture to §6.12.
939
940 ---
941
942 ### 6.12 Unsupported Effects and Native-Safe Alternatives
943
944 | Unsupported intent | Do not author | Fidelity | Alternative |
945 |---|---|---|---|
946 | Source/backdrop blur; procedural texture | Plain blur, `feTurbulence`, `feDisplacementMap`, `feColorMatrix`, arbitrary filter graph | `Bake-required` | §6.4 effect, explicit geometry, translucent layers, or baked texture |
947 | Inner shadow, soft edge, reflection | Non-outer-shadow/glow graph | `Bake-required` | Explicit inset/highlight/shadow layers or image |
948 | Per-pixel compositing | Mask, blend mode, knockout, arbitrary alpha composite | `Bake-required` | Direct geometry; §1.2 image clip; otherwise bake |
949 | Exact custom tile | Unannotated `<pattern>` / `patternTransform` | `Bake-required` | Multi-subpath geometry, suitable [`native-data-interface.md`](./native-data-interface.md) preset, or bake |
950 | Sheared object | Skew/shear matrix | `Bake-required` | Pre-transform geometry path; bake text/image |
951
952 **Hard rule — blur semantics**: within §6.4, zero-offset `feGaussianBlur` means
953 glow; it does not blur the object or backdrop. Use a low-alpha raster for dense
954 grain and explicit circles/paths only for sparse editable marks.
955
956 Unsupported source effects remain visible where possible and retain their
957 import diagnostics. Resolve those diagnostics before release export; see
958 [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
959
960 ---
961
962 ### 6.13 Page-Level Composition Recipes
963
964 **Reference — not a quota**: use the planned page skeleton; when images are
965 active, select it through
966 [`image-layout-patterns.md`](./image-layout-patterns.md). Read each recipe
967 back-to-front and omit every layer without a distinct job.
968
969 | Page / deck job | Back-to-front stack | Stop |
970 |---|---|---|
971 | Cover | Hero field → optional scrim/wash → purposeful opening/contour → native title, optionally paired with a prepared decorative-lettering image | Stop when copy is safe and title/field read together |
972 | Divider | Image band or quiet field → restrained wash → recurring geometry → number/title | Reuse deck language; add no effect family |
973 | Text-led explanation | Quiet field → recurring material/contour → native hierarchy → optional local emphasis | Emphasis clarifies the argument, never decorates body copy |
974 | Process / system | Context field → native relation lines → nodes/labels → optional state/direction focus | Every connector stays semantic; atmosphere must not obscure flow |
975 | Evidence / metric | Context field → local contrast → native leaders/labels/metric → optional focus/elevation | Claims stay native; atmosphere must not weaken evidence |
976 | Comparison | Matched planes → optional shared wash/divider → matched labels → one difference marker | Keep crop, elevation, and paint symmetric unless asymmetry is the claim |
977 | Closing / CTA | Receded field → echoed contour/gradient → native action → optional raised accent | Add no effect family or competing image |
978 | Cross-page motif | Reuse contour, gradient direction, line language, texture, or light logic; vary scale, crop, or position by page job | Preserve recognition without copying the page or adding novelty effects |
979
980 ---
981
981 lines MARKDOWN