| 1 | > See [`shared-standards-core.md`](./shared-standards-core.md) §§1.4–1.5 for the native-shape metadata and validation contracts. |
| 2 | |
| 3 | # Native Shape Authoring Reference |
| 4 | |
| 5 | Use this reference during Executor SVG construction or project-owned canonical |
| 6 | template maintenance when basic primitives, one standard PowerPoint shape, or |
| 7 | supported shape/text operands can express the intended object. Prefer, in order: |
| 8 | editable basic primitives, one exact Office preset, then a PowerPoint-style |
| 9 | Boolean result from closed shapes and/or resolvable text. Hand-authored freeform |
| 10 | geometry is allowed only when those |
| 11 | constructions cannot faithfully express the object. Neither helper writes a |
| 12 | page. The preset helper does not create the shape's own `p:txBody`; keep visible |
| 13 | text outside the atomic fragment. |
| 14 | |
| 15 | ## 1. Selection Gate |
| 16 | |
| 17 | Apply this decision order before drawing any new geometric contour. |
| 18 | |
| 19 | > This gate is for picking the **highest-level faithful native construction**. |
| 20 | > Do not hand-author a freeform merely because an SVG path is convenient. |
| 21 | |
| 22 | | Condition | Action | |
| 23 | |---|---| |
| 24 | | Plain rectangle, symmetric rounded rectangle, circle, or ellipse | Write the ordinary SVG primitive; the exporter already emits an editable native shape. | |
| 25 | | Straight relationship, divider, or leader | Write `<line>`; use a registered marker only when direction is meaningful. | |
| 26 | | One DrawingML preset exactly expresses the intended object | Run `preset_shape_svg.py render`, then insert its complete stdout fragment into the hand-authored page or canonical template. | |
| 27 | | A stock `bentConnector*` / `curvedConnector*` contour exactly expresses a bent or curved relationship and endpoint attachment is not required | Run `preset_shape_svg.py render --object-kind connector`; the result is an unconnected native Connector shape. | |
| 28 | | Two or more supported closed-shape / resolvable-text operands require Union, Combine, Fragment, Intersect, or Subtract | Run `shape_boolean_svg.py render`, then replace the operands with every stdout path; the result remains ordinary editable custom geometry. | |
| 29 | | Basic primitives, one preset, and Boolean materialization cannot faithfully express the visual meaning or contour | Write ordinary `<path>` / `<polygon>` geometry; export keeps it as editable custom geometry. | |
| 30 | | The shape only resembles a preset | Never infer a preset; continue to the Boolean gate, then use freeform only if no faithful construction exists. | |
| 31 | | Mirror/preserve input already owns native-shape metadata | Keep the existing object and metadata; never reselect its preset. | |
| 32 | |
| 33 | **Hard rule**: `preset_shape_svg.py` is the only authoring entry for |
| 34 | `data-pptx-authoring="preset"`. Never add `data-pptx-prst`, frame, adjustment, |
| 35 | or registry path data by hand. Insert the helper's complete compact `<g>` and |
| 36 | rerun the helper whenever its geometry or paint changes. |
| 37 | |
| 38 | --- |
| 39 | |
| 40 | ## 2. Semantic Preset Candidate Guide |
| 41 | |
| 42 | Use the table below as the **go-to menu**: match the page's visual intent to a |
| 43 | candidate preset *before* defaulting to a plain rect or path. Reaching here |
| 44 | first is exactly how presets get used instead of forgotten. |
| 45 | |
| 46 | "Automatic" means the Executor independently applies this semantic decision |
| 47 | gate before drawing a new object. It does not scan existing SVG, classify |
| 48 | paths or contours, or upgrade ordinary SVG during export. |
| 49 | |
| 50 | | Visual intent | Candidate presets | Boundary | |
| 51 | |---|---|---| |
| 52 | | Literal geometric body | `triangle`, `diamond`, `pentagon`, `hexagon`, `octagon`, `star5` | Use only when the named geometry itself is the intent. | |
| 53 | | Solid block direction | `rightArrow`, `leftArrow`, `upArrow`, `downArrow`, `leftRightArrow`, `upDownArrow`, `chevron` | Use `<line>` for a thin straight relationship; do not fake a solid directional object with a stroked path. | |
| 54 | | Standard flowchart node | `flowChartProcess`, `flowChartDecision`, `flowChartInputOutput`, `flowChartTerminator`, `flowChartDocument` | Use only for an actual flowchart; ordinary content cards remain cards. | |
| 55 | | Stock bent / curved relationship contour | `bentConnector*`, `curvedConnector*` | Prefer when the contour fits and endpoint attachment is not required. The authored object is an unconnected native Connector, so moving nodes does not reroute it. | |
| 56 | | Stock callout | `wedgeRectCallout`, `wedgeRoundRectCallout`, `wedgeEllipseCallout`, `cloudCallout` | For a brand-specific or custom tail, continue through the Boolean gate; use freeform only if the result still cannot be expressed faithfully. | |
| 57 | | Stock ribbon or scroll | `ribbon*`, `ellipseRibbon*`, `verticalScroll`, `horizontalScroll` | Select only when the stock contour is visually acceptable. | |
| 58 | | Standalone math symbol | `mathPlus`, `mathMinus`, `mathMultiply`, `mathDivide`, `mathEqual`, `mathNotEqual` | Inline formulas and prose symbols remain text/formula assets. | |
| 59 | | Literal Office symbol | `heart`, `sun`, `moon`, `lightningBolt`, `gear6`, `gear9` | Never replace an icon required by `spec_lock.icons`. | |
| 60 | |
| 61 | Use registry search for a less common literal shape: |
| 62 | |
| 63 | ```bash |
| 64 | python3 ${SKILL_DIR}/scripts/preset_shape_svg.py list --search arrow |
| 65 | python3 ${SKILL_DIR}/scripts/preset_shape_svg.py describe rightArrow |
| 66 | ``` |
| 67 | |
| 68 | **Shape-first diagram rule**: use `<line>` for straight thin relationships; |
| 69 | use an exact connector-family preset for a stock bent or curved contour; use a |
| 70 | block-arrow / chevron preset for a solid direction. Resort to an open freeform |
| 71 | path only when those native constructions cannot faithfully express the |
| 72 | relationship, data geometry, or locked hand-drawn / organic style. Newly |
| 73 | authored connector-family presets remain unconnected and do not gain attachment |
| 74 | semantics. Existing Connector topology imported from a source PPTX remains |
| 75 | owned by the preserve/mirror round-trip contract. |
| 76 | |
| 77 | **Forbidden — false native semantics**: |
| 78 | |
| 79 | - `actionButton*` when navigation or trigger behavior is expected; the helper |
| 80 | maps its visual preset geometry only and never creates an action or hyperlink; |
| 81 | - `chartX`, `chartStar`, or `chartPlus` as a substitute for native charts; |
| 82 | - logo, icon glyph, illustration, brand contour, or data-chart marks. |
| 83 | |
| 84 | --- |
| 85 | |
| 86 | ## 3. Fragment Generation |
| 87 | |
| 88 | Run one command for one selected object. Generated project pages choose the |
| 89 | object's solid paint from the current page context, using `spec_lock.md` roles as |
| 90 | reusable anchors rather than an exhaustive palette; `create-template` takes colors |
| 91 | from the confirmed brief and template `design_spec.md`. Mirror/preserve input |
| 92 | keeps the source object's paint instead of regenerating this authored form. |
| 93 | |
| 94 | ```bash |
| 95 | python3 ${SKILL_DIR}/scripts/preset_shape_svg.py render rightArrow \ |
| 96 | --id p03-growth-arrow \ |
| 97 | --frame 160 210 320 112 \ |
| 98 | --fill "#2563EB" \ |
| 99 | --stroke none \ |
| 100 | --adjust "adj1=val 50000" |
| 101 | ``` |
| 102 | |
| 103 | For a stock bent / curved contour that does not require endpoint attachment: |
| 104 | |
| 105 | ```bash |
| 106 | python3 ${SKILL_DIR}/scripts/preset_shape_svg.py render bentConnector3 \ |
| 107 | --id p03-flow-connector \ |
| 108 | --object-kind connector \ |
| 109 | --frame 420 180 220 140 \ |
| 110 | --fill none \ |
| 111 | --stroke "#475569" \ |
| 112 | --stroke-width 2 |
| 113 | ``` |
| 114 | |
| 115 | Every connector-family preset requires `--object-kind connector`, `--fill none`, |
| 116 | and a visible stroke. It exports as an unconnected `p:cxnSp`; a connector |
| 117 | preset can never be authored as an ordinary `shape`. |
| 118 | |
| 119 | **Hard rule — stdout-only exception**: the helper prints one deterministic |
| 120 | `<g>` fragment. Read that output and insert it with the normal page/template |
| 121 | `apply_patch` edit. Do not redirect it into `svg_output/`, loop over pages or |
| 122 | templates, batch shapes, or let it choose layout. The main Agent still authors |
| 123 | every complete SVG page sequentially and maintains each reusable template |
| 124 | explicitly. |
| 125 | |
| 126 | --- |
| 127 | |
| 128 | ## 4. Atomic Fragment Contract |
| 129 | |
| 130 | The helper emits one compact logical group. Metadata and base paint are written |
| 131 | once on the group; its direct children are the visible paths regenerated from |
| 132 | the locked preset registry. |
| 133 | |
| 134 | | Component | Ownership | |
| 135 | |---|---| |
| 136 | | Logical `<g data-pptx-authoring="preset">` | Stable id, object kind, preset, frame, adjustments, and explicit local base paint. | |
| 137 | | Direct `<path>` children | Ordered browser-visible registry layers. A child writes only a path-specific fill/stroke override when the preset requires one. | |
| 138 | | Deliberately absent transport fields | No hidden carrier, preview wrapper, `data-pptx-part`, or stored fingerprint belongs in project-authored SVG. Those fields remain part of expanded PPTX import/round-trip transport. | |
| 139 | |
| 140 | **Hard rule**: treat the returned group as atomic. Keep it as the content group |
| 141 | when it stands alone. When it needs labels, icons, or other decorations, put |
| 142 | the preset and those siblings in a separate parent content group; never put |
| 143 | them inside the preset group itself. Do not edit the direct paths; they are |
| 144 | validation evidence generated from the registry, not a freehand contour |
| 145 | surface. |
| 146 | |
| 147 | Canonical page/template authoring also keeps paint and opacity off ancestor |
| 148 | groups that contain the preset. Compatible ancestor paint still exports under |
| 149 | the general SVG composition rules, but the checker warns because the atom is no |
| 150 | longer paint-self-contained; rerun the helper with channel alpha instead. |
| 151 | |
| 152 | On a structured template, a validated authored-preset group is one semantic |
| 153 | atom. It may be Slide-local, the single carrier of an `object` slot, or a direct |
| 154 | Master/Layout fixed atom. This narrow exception does not permit ordinary nested |
| 155 | `<g>` structures in Master/Layout layers or placeholder carriers. The template |
| 156 | workflow may add the registered structural ownership attributes to the complete |
| 157 | helper group; it still must not alter preset metadata, paint, or direct paths. |
| 158 | |
| 159 | **Frame coordinate space**: `--frame x y w h` is expressed in the coordinate |
| 160 | space where you insert the fragment. At the page root that is page coordinates; |
| 161 | inside a `<g transform="translate(…)">` use **group-local** coordinates — the |
| 162 | ancestor transform stacks on top, so page-absolute values would double-offset |
| 163 | the shape off-canvas. Keep the helper's exact space-separated ordinary-decimal |
| 164 | `data-pptx-frame` spelling; compact authoring does not accept alternate numeric |
| 165 | spellings. |
| 166 | |
| 167 | **Regeneration rule**: rerun the helper when preset, frame, adjustment, fill, |
| 168 | stroke, or stroke width changes. Moving, scaling, rotating, or flipping the |
| 169 | complete logical group is allowed; zero-scale transforms and shear/skew are |
| 170 | forbidden, and the transformed frame must remain inside DrawingML's coordinate |
| 171 | range. Stroke width must remain inside DrawingML's line-width range. To freely |
| 172 | edit the contour, replace the whole fragment with ordinary SVG rather than |
| 173 | modifying a generated direct path. |
| 174 | |
| 175 | For a canonical reusable template, the complete helper fragment may remain as |
| 176 | an executable exemplar. A final-page adaptation may copy it unchanged only |
| 177 | when all registry metadata, frame, adjustments, and paint remain unchanged; |
| 178 | otherwise regenerate the complete compact group. |
| 179 | |
| 180 | --- |
| 181 | |
| 182 | ## 5. Boundaries |
| 183 | |
| 184 | | Concern | Behavior | |
| 185 | |---|---| |
| 186 | | Shape text | Keep visible SVG `<text>` outside the atomic fragment. It remains editable but may export as a grouped text box rather than the preset's own `p:txBody`. | |
| 187 | | Connector attachment | Authoring helper v1 creates an unconnected `p:cxnSp` and does not accept endpoint/site metadata. Do not hand-add it. The imported-shape contract may preserve an attachment that already exists in a source PPTX; creating a new attached connector is currently unsupported. | |
| 188 | | Action button behavior | `actionButton*` presets map visual geometry only. No action, navigation target, or hyperlink is created automatically. | |
| 189 | | Gradient/pattern paint | Authoring helper v1 accepts solid HEX paint only. Use ordinary SVG when a complex paint treatment is essential. | |
| 190 | | Multi-path darken/lighten | Direct visible layers use the shared normalized paint behavior from the PPTX importer. Their registry-derived HEX values are authorized derivatives of the selected base color and need no separate lock row. | |
| 191 | | Expanded compatibility | Existing helper-authored carrier/preview fragments remain readable as ordinary Slide-local input and receive a non-blocking migration warning; they do not become structured fixed atoms or object-slot carriers. Imported expanded fragments remain the lossless mirror/preserve form. | |
| 192 | | External edits | Any registry-path, style, or semantic mismatch fails quality check and export; regenerate the fragment. | |
| 193 | |
| 194 | **Validation**: `svg_quality_checker.py` independently rerenders every compact |
| 195 | authored preset from registry metadata and compares its direct visible paths |
| 196 | and paint. The exporter performs the same validation, then expands the compact |
| 197 | group only in memory to reuse the lossless native-shape conversion path. |
| 198 | Compatible expanded authored input remains under its separate carrier/preview |
| 199 | freshness contract. |
| 200 | |
| 201 | --- |
| 202 | |
| 203 | ## 6. Shape Boolean Materialization |
| 204 | |
| 205 | **Trigger**: Current page construction has two or more supported shape/text operands |
| 206 | whose faithful result calls for PowerPoint-style Union, Combine, Fragment, |
| 207 | Intersect, or Subtract. A §IX `Native shape suggestion` is a semantic candidate, |
| 208 | not a prerequisite or tool command; Executor may adopt, adapt, or decline it |
| 209 | from the actual content and explicit user/template constraints. |
| 210 | |
| 211 | ```bash |
| 212 | python3 ${SKILL_DIR}/scripts/shape_boolean_svg.py render <svg-file> \ |
| 213 | --operation subtract \ |
| 214 | --source body \ |
| 215 | --source cutout \ |
| 216 | --id result |
| 217 | ``` |
| 218 | |
| 219 | | Concern | Contract | |
| 220 | |---|---| |
| 221 | | Sources | Closed `path`, `polygon`, `rect`, `circle`, `ellipse`, one validated compact authored shape preset, or supported horizontal implicit-LTR direct `<text>` with a resolvable exact OpenType weight/style (`--font-dir` adds search roots). Text becomes glyph geometry and is no longer editable text. Open geometry, groups, nested text, images, definitions, and nested SVG viewports fail closed. | |
| 222 | | Primary shape | The first `--source` supplies result paint. For `subtract`, all later operands are removed from that primary geometry. Explicit paint flags override only their named channels. | |
| 223 | | Coordinates | Ancestor and local transforms are baked into SVG-root coordinate space. Place stdout in the primary operand's z-order with no additional transform; never reinsert it under an original transformed ancestor. Root-coordinate space does not require each result path to be a direct `<svg>` child. | |
| 224 | | Placement | Ordinary Slide-local results belong in the applicable untransformed direct-root semantic `<g>` with its normal `id` / `data-pptx-bounds`. Master/Layout results remain direct-root path atoms and redeclare `data-pptx-layer`. One non-fragment result may be the direct `data-pptx-carrier="true"` child of an `object` slot. | |
| 225 | | Fragment roles | Fragment paths may share one ordinary Slide-local semantic group, but remain separate shapes and cannot collectively claim one carrier or one Master/Layout atom. Helper output inherits no structural role metadata from its operands; redeclare only the final layer/carrier/role contract. | |
| 226 | | Result | `union`, `combine`, `intersect`, and `subtract` emit one ordinary `<path>`. `fragment` emits stable sibling paths named `<id>-1`, `<id>-2`, ... in top/left/bottom/right/area order. | |
| 227 | | Winding | Results use explicit nonzero contour direction and never emit `fill-rule`, `clip-rule`, `clip-path`, `mask`, or Merge Shapes metadata. Operands that depend on even-odd fill, clipping, or masking fail closed. | |
| 228 | | Preservation | This helper authors new geometry only. Never use it to merge or split mirror/preserve source structure. | |
| 229 | |
| 230 | Operation semantics match PowerPoint's visible Merge Shapes result: `union` |
| 231 | keeps every covered region, `combine` keeps the symmetric difference, |
| 232 | `intersect` keeps only common coverage, `subtract` removes every later source |
| 233 | from the primary, and `fragment` returns each atomic filled region. The PPTX |
| 234 | stores the materialized freeform geometry, not replayable operation history. |
| 235 | |
| 236 | **Hard rule — stdout-only replacement**: The helper never writes the source |
| 237 | page. In one normal `apply_patch` edit, remove every selected operand and insert |
| 238 | every returned path in root coordinate space at the primary operand's z-order, |
| 239 | using the placement contract above. Fragment paths remain separate shapes; an |
| 240 | ordinary semantic group does not turn them into one structural atom. |
| 241 | |
| 242 | --- |
| 243 | |
| 244 | ## 7. Shape-Only Modelling Techniques |
| 245 | |
| 246 | Applies to any page built from shapes, **with or without images** — a text-only, |
| 247 | data-only, or icon-only deck reaches these the same way. Each technique below is |
| 248 | plain geometry plus gradient paint, so all of it survives native export. |
| 249 | |
| 250 | ### 7.1 Alternating light/dark gradient = dimensional form |
| 251 | |
| 252 | The single highest-yield shape technique. A cylinder, metallic band, dimensional |
| 253 | numeral, or curved panel is produced by one gradient whose stops **alternate |
| 254 | light and dark** across the shape — light · dark · light for a three-stop ramp, |
| 255 | or light · dark · light · dark · light for a five-stop one. The alternation |
| 256 | imitates a curved surface catching light twice; a plain two-stop ramp always |
| 257 | reads flat no matter how strong the contrast. |
| 258 | |
| 259 | Keep every stop on one hue and vary only lightness, hold one light direction for |
| 260 | the whole page, and remove strokes so adjacent facets meet cleanly. For a |
| 261 | cylinder, apply the alternating ramp across the body and cap it with an ellipse |
| 262 | carrying its own shallower ramp. The same light logic applies across separate |
| 263 | facets of any folded form. |
| 264 | |
| 265 | ### 7.2 Reflection without a reflection effect |
| 266 | |
| 267 | Native reflection is `Bake-required` ([`svg-effects.md`](./svg-effects.md) §6.12), |
| 268 | so build it from geometry instead: |
| 269 | |
| 270 | 1. Duplicate the object and flip it with `transform="translate(0, 2·y_bottom) scale(1, -1)"`. |
| 271 | 2. Keep only the top **10–25 %** of the flipped copy — that is all a reflection |
| 272 | ever shows. |
| 273 | 3. Lay a rectangle over it filled with a gradient running from fully transparent |
| 274 | at the object's base to the page background color at the cut line, so the |
| 275 | copy dissolves into the page. |
| 276 | 4. Drop the whole reflection to roughly **60–70 %** opacity. |
| 277 | |
| 278 | Seat rows of certificates, product shots, logo tiles, and cylinders this way. Do |
| 279 | not add a blur — it will not survive export, and a short gradient fade already |
| 280 | reads correctly at slide scale. |
| 281 | |
| 282 | ### 7.3 Fragment as a modelling tool, not just a boolean |
| 283 | |
| 284 | `fragment` (§6) is the fastest way to build layered diagrams from one silhouette: |
| 285 | lay evenly distributed bars across a triangle and fragment it into pyramid tiers; |
| 286 | cross a circle with two bars for a quadrant wheel; slice an annulus radially for |
| 287 | ring segments. Every piece inherits the parent contour, so the assembly stays |
| 288 | perfectly registered — impossible to achieve by drawing the tiers separately. |
| 289 | |
| 290 | Distribute the cutting bars with a constant step before fragmenting; uneven tiers |
| 291 | read as a mistake rather than a hierarchy. Paint the resulting pieces with one |
| 292 | gradient family per §7.1 so the stack reads as a single solid. |
| 293 | |
| 294 | ### 7.4 Soft edges without the soft-edge effect |
| 295 | |
| 296 | Feathered edges are `Bake-required` ([`svg-effects.md`](./svg-effects.md) §6.12), |
| 297 | but the four jobs they normally do are all reachable with gradients: |
| 298 | |
| 299 | | Intent | Build instead | |
| 300 | |---|---| |
| 301 | | Contact shadow under an object | Ellipse filled with a `radialGradient` from dark-transparent at the centre to fully transparent at the rim | |
| 302 | | Spotlight / stage pool | Cone or ellipse filled with a gradient fading to transparent at its far end, at low opacity over the scene | |
| 303 | | Object dissolving into the page | Overlay a rectangle whose gradient runs from transparent to the exact page background hex | |
| 304 | | Hiding an object while keeping it live | Full transparency, or a background-registered fill ([`image-layout-patterns.md`](./image-layout-patterns.md) `#M1-08`) | |
| 305 | |
| 306 | A radial or linear alpha ramp reads the same as a feathered edge at slide scale |
| 307 | and, unlike a filter, exports intact. Never approximate a soft edge with a stack |
| 308 | of stroked outlines — the banding is visible on projection. |
| 309 | |
| 310 | ### 7.5 Ground plane and staging |
| 311 | |
| 312 | An object floating in empty canvas looks pasted on. Give it a surface: a wide |
| 313 | shallow ellipse or trapezoid beneath it, filled with a gradient that fades to the |
| 314 | background at its edges, optionally with a soft dark ellipse directly under the |
| 315 | object as contact shadow. A trapezoid narrowing away from the viewer reads as a |
| 316 | receding floor; a cylinder or slab reads as a pedestal. |
| 317 | |
| 318 | Keep the plane low-contrast — it is staging, not content. This is what makes |
| 319 | certificate rows, product hero shots, and trophy/award pages look composed |
| 320 | rather than floating, and it costs two shapes. |
| 321 |