| 1 | > See [`shared-standards-core.md`](./shared-standards-core.md) for the mandatory SVG foundation. |
| 2 | |
| 3 | # Native Data Interface |
| 4 | |
| 5 | Conditional interface for preset pattern fills and PowerPoint-native chart/table replacement metadata. Load when either feature appears in the authored SVG. |
| 6 | |
| 7 | ## 1. Pattern Fill — `<pattern>` with PPTX preset annotation |
| 8 | |
| 9 | `<pattern>` requests one fixed DrawingML preset; the converter does not render |
| 10 | the tile's arbitrary geometry. Use this interface only when that preset mapping |
| 11 | is intended. |
| 12 | |
| 13 | `data-pptx-pattern="<preset>"` is the generated default for selecting the |
| 14 | intended preset from the enum below. The converter retains an `ltUpDiag` |
| 15 | fallback when the annotation is absent; the checker reports that fallback as a |
| 16 | non-blocking fidelity warning. Invalid explicit preset names remain errors |
| 17 | because they violate the closed OOXML enum. |
| 18 | |
| 19 | Pattern colors may come from importer metadata (`data-pptx-fg` / |
| 20 | `data-pptx-bg`) or from the pattern's child paint. Without metadata, the first |
| 21 | child `<rect>` fill becomes the background and the first stroke (or other fill) |
| 22 | becomes the foreground. A missing background defaults to white; a missing |
| 23 | foreground means no native pattern fill can be emitted. The child geometry |
| 24 | itself is never used as a repeatable tile. |
| 25 | |
| 26 | **Valid `data-pptx-pattern` values** (OOXML `ST_PresetPatternVal` — closed enum, anything outside makes PowerPoint open with "needs to be repaired"): |
| 27 | |
| 28 | | Category | Values | |
| 29 | |---|---| |
| 30 | | Grids | `smGrid` · `lgGrid` · `dotGrid` *(no `ltGrid` — common typo)* | |
| 31 | | Diagonal lines | `ltUpDiag` · `ltDnDiag` · `dkUpDiag` · `dkDnDiag` · `wdUpDiag` · `wdDnDiag` · `dashUpDiag` · `dashDnDiag` · `diagCross` | |
| 32 | | Horizontal / vertical lines | `horz` · `vert` · `ltHorz` · `ltVert` · `dkHorz` · `dkVert` · `narHorz` · `narVert` · `dashHorz` · `dashVert` · `cross` | |
| 33 | | Percent fills | `pct5` · `pct10` · `pct20` · `pct25` · `pct30` · `pct40` · `pct50` · `pct60` · `pct70` · `pct75` · `pct80` · `pct90` | |
| 34 | | Checks & confetti | `smCheck` · `lgCheck` · `smConfetti` · `lgConfetti` | |
| 35 | | Decorative | `horzBrick` · `diagBrick` · `weave` · `plaid` · `trellis` · `zigZag` · `wave` · `sphere` · `divot` · `shingle` · `solidDmnd` · `openDmnd` · `dotDmnd` | |
| 36 | |
| 37 | `svg_quality_checker.py` warns when a referenced pattern lacks the annotation; |
| 38 | it errors when the pattern uses `patternTransform` or names a preset outside |
| 39 | this enum. |
| 40 | |
| 41 | ## 2. PowerPoint-Native Chart / Table Replacement Markers (Opt-in) |
| 42 | |
| 43 | Native PowerPoint tables and Excel-backed charts activate at export time only. Generated pages prepare dormant replacement metadata for independently selected native-ready objects while keeping hand-authored SVG geometry pixel-stable across PowerPoint / Keynote / LibreOffice / WPS. |
| 44 | |
| 45 | **Hard rule — selected-object authoring**: write the marker and JSON metadata in |
| 46 | the same edit only for a supported chart or pure text-grid table selected as |
| 47 | native-ready under [`executor-chart.md`](./executor-chart.md) §2.2. Default reads |
| 48 | `Native-ready: yes` from §IX (legacy §VII fallback only); Quick makes the |
| 49 | decision in active context before drawing. Default `no`, a Quick decision not to |
| 50 | use the native object model, and incidental microvisuals stay on the SVG |
| 51 | fallback route. Canonical rectangular merged text cells may use the narrow |
| 52 | `row_span` / `col_span` contract below; graphical cells stay unmarked. The |
| 53 | marker group supplies visible SVG fallback children for browser rendering and |
| 54 | JSON metadata for `svg_to_pptx` native export. |
| 55 | |
| 56 | **Hard rule — activation is the opt-in, dormant unless exported with `--native-charts-and-tables`**: A marker only declares that a group is eligible for PowerPoint-native Chart/Table replacement. Normal `svg_to_pptx.py` runs keep the fallback SVG children and convert them into independently editable DrawingML shapes. Pass `--native-charts-and-tables` only when the data source and chart/table-specific object model matter more than cross-renderer layout fidelity: it emits the PowerPoint Chart/Table object and skips the fallback children to avoid duplicates. Native styling preserves the core palette, text, axis, grid, and background colors where possible, but it is still a PowerPoint Chart/Table object rather than a pixel-identical SVG drawing. |
| 57 | |
| 58 | The native route is deliberately data-object-first and may be lossy: marker-local labels, callouts, KPIs, guide lines, custom split/bin semantics, or styling that is absent from the payload may disappear or normalize. Export warns about this route-level risk and any narrower issue it can detect. Loss of visual parity is not grounds to remove an active marker that the emitter can otherwise convert; use the default SVG-fallback export when exact authored artwork matters more than a native data source and object-specific controls. |
| 59 | |
| 60 | | Replacement marker | Native output | Required metadata | |
| 61 | |---|---|---| |
| 62 | | `<g data-pptx-replace-with="table">` | `<p:graphicFrame>` with `<a:tbl>` | bounds + `columns` or `rows` | |
| 63 | | `<g data-pptx-replace-with="chart">` | `<p:graphicFrame>` with `c:chart` / `cx:chart` + chart part + embedded workbook | bounds + `type`, plus chart data | |
| 64 | |
| 65 | **Metadata placement**: Put JSON in one child |
| 66 | `<metadata type="application/json">`. The parent group's |
| 67 | `data-pptx-replace-with` value selects the table or chart schema, so the |
| 68 | metadata child does not repeat an object-kind attribute. Attribute JSON |
| 69 | (`data-pptx-json="..."`) remains read-compatible but is harder to XML-escape |
| 70 | correctly and is not canonical authoring. |
| 71 | |
| 72 | **Bounds**: Provide `x`, `y`, `width`, and `height` in metadata, or as |
| 73 | `data-pptx-x` / `data-pptx-y` / `data-pptx-width` / `data-pptx-height` on the |
| 74 | marker group. If any bound is omitted, the exporter infers the object frame |
| 75 | from the visible fallback geometry; this keeps SVG fallback and native object |
| 76 | placement aligned. Complete explicit bounds are absolute slide coordinates; |
| 77 | marker/ancestor `translate` and `scale` transforms apply only when at least one |
| 78 | bound is inferred. `x`, `y`, `width`, and `height` must be finite and resolve |
| 79 | inside PowerPoint's 32-bit DrawingML coordinate range; `width` and `height` |
| 80 | must resolve to at least one EMU. Native table frames must additionally resolve |
| 81 | to at least one EMU per resolved row and column. |
| 82 | |
| 83 | **Validation**: `svg_quality_checker.py` validates replacement marker kind, JSON |
| 84 | metadata, bounds/fallback availability, table rows/columns, supported chart |
| 85 | type, chart data shape, and any imported fallback baseline before export. |
| 86 | |
| 87 | Imported marker freshness, fallback classification, provenance, and legacy |
| 88 | read compatibility are operational import concerns. Keep generated authoring |
| 89 | free of those attributes; use the exact behavior and field index in |
| 90 | [`conversion.md`](../scripts/docs/conversion.md#native-table-and-chart-import-claims). |
| 91 | |
| 92 | ```xml |
| 93 | <g id="p03-revenue-chart" data-pptx-replace-with="chart"> |
| 94 | <metadata type="application/json"> |
| 95 | { |
| 96 | "x": 120, "y": 150, "width": 520, "height": 320, |
| 97 | "type": "column", |
| 98 | "title": "Revenue by Segment", |
| 99 | "categories": ["Q1", "Q2", "Q3"], |
| 100 | "series": [ |
| 101 | {"name": "Cloud", "values": [12, 15, 19]}, |
| 102 | {"name": "Services", "values": [8, 9, 11]} |
| 103 | ] |
| 104 | } |
| 105 | </metadata> |
| 106 | <!-- Visible SVG fallback for live preview / non-native export goes here. --> |
| 107 | </g> |
| 108 | ``` |
| 109 | |
| 110 | **Table schema**: Native tables are rectangular DrawingML grids. Use `columns` |
| 111 | for the optional header row and `rows` for body rows; shorter rows are padded |
| 112 | with blank cells unless `strict_grid: true` is set. Tables may contain at most |
| 113 | 1000 resolved rows and 1000 resolved columns. Use `column_widths` and |
| 114 | `row_heights` as relative weights. Weight lists must match the resolved grid, |
| 115 | contain finite non-negative numbers, and include at least one positive value. |
| 116 | If present, `header_rows` must be an integer from `0` through the resolved row |
| 117 | count. Write `strict_grid`, `style.band_row`, and cell `bold` as JSON booleans. |
| 118 | Cell objects accept `text`, `fill`, `color`, |
| 119 | `align`, `valign`, `bold`, `font_size`, `padding`, `border_color`, and |
| 120 | `border_width`, plus optional `lang`; the same `padding`, `border_color`, |
| 121 | `border_width`, and `lang` keys may also live under `style` as table defaults. |
| 122 | For multi-paragraph text, replace cell `text` with a non-empty `paragraphs` |
| 123 | list. Each entry is either a string or an object containing optional |
| 124 | `align: "l|ctr|r"` and exactly one of `text` or non-empty `runs`; empty |
| 125 | paragraph strings are preserved, and cell `text` / `paragraphs` are mutually |
| 126 | exclusive. Each run is an object with required string `text` and optional JSON |
| 127 | boolean `bold`, `italic`, `underline`, and `strike`, plus optional `color`, |
| 128 | `font_size`, one-typeface `font_family`, `lang`, and `alt_lang`. Unknown fields, |
| 129 | wrong types, empty run lists, multi-typeface `font_family`, and unsupported |
| 130 | colors fail fast. PPTX import requires exact physical row/grid topology and |
| 131 | normalizes source presentation-only run XML outside this closed schema only |
| 132 | when it contains no non-empty `rPr` / `defRPr` / `endParaRPr` `effectLst` or |
| 133 | `effectDag`. A table-cell run effect follows the blocking effect contract above |
| 134 | instead of entering either the native payload or an effect-free fallback. |
| 135 | Relationship-bearing text, extensions, structural line breaks, fields, tabs, |
| 136 | bullets, malformed run topology, and unsupported text-body structure remain |
| 137 | fallback-only. |
| 138 | Per-side cell borders use `borders.left|right|top|bottom`, where each value is |
| 139 | either `{ "style": "none" }` or |
| 140 | `{ "style": "solid", "color": "#RRGGBB", "width": <positive-px> }`. |
| 141 | Per-side borders are cell-only; legacy uniform `border_color` / `border_width` |
| 142 | remain supported as defaults that an individual side may override. |
| 143 | When `lang` is absent, export derives `zh-CN` for CJK text and `en-US` |
| 144 | otherwise. `style.band_row: false` disables both `<a:tblPr bandRow>` and |
| 145 | materialized alternating row fills. Native table typography mirrors the |
| 146 | visible SVG fallback: put `style.font_family` and `style.font_size` on the |
| 147 | marker from the table text already drawn, then use `style.header_font_size` or |
| 148 | per-cell `font_size` only when the fallback visibly differs. If the fallback |
| 149 | has no explicit table font, Default uses the deck body family and declared body |
| 150 | anchor from `spec_lock.md`; Quick uses its active-context body family and size. |
| 151 | |
| 152 | **Hard rule — table metadata is the native source of truth**: Every row, |
| 153 | summary line, value, and cell-level style that must survive |
| 154 | `--native-charts-and-tables` must be present in `columns` / `rows`. SVG fallback text is |
| 155 | discarded during native export. `svg_quality_checker.py` warns when visible |
| 156 | fallback `<text>` inside a native table marker does not appear in metadata. |
| 157 | For numeric or currency columns, use cell objects with `align: "r"`; SVG |
| 158 | `text-anchor="end"` does not carry into the native table. |
| 159 | |
| 160 | **Merged table cells — canonical rectangular contract only**: Put positive JSON |
| 161 | integer `row_span` / `col_span` values on the merge anchor and keep every |
| 162 | covered grid cell blank. Spans must stay within the resolved rectangular grid |
| 163 | and may not overlap. The exporter emits the canonical DrawingML topology |
| 164 | (`rowSpan` on the top edge, `gridSpan` on the left edge, `hMerge` / `vMerge` on |
| 165 | covered cells). CamelCase aliases, raw OOXML merge fields, top-level merge lists, |
| 166 | nonblank covered cells, invalid spans, and overlaps fail fast. The PPTX importer |
| 167 | activates native reconstruction only for that same explicit rectangular topology |
| 168 | with empty merge-slave text bodies; other merge encodings remain fallback-only |
| 169 | with `unsupported-merge-topology`. |
| 170 | |
| 171 | **Category chart schema**: `column`, `bar`, `line`, `area`, `pie`, |
| 172 | `doughnut`, `pieOfPie`, `barOfPie`, and `radar` use `categories` plus |
| 173 | `series[].values`. Pie-family charts (`pie`, `doughnut`, `pieOfPie`, and |
| 174 | `barOfPie`) must have exactly one series; the exporter assigns per-category |
| 175 | slice colors so single-series charts do not collapse into one solid color. |
| 176 | Column and bar charts may set per-point colors with `series[].point_colors` |
| 177 | or `series[].pointColors`; the list must match `series[].values` length. |
| 178 | Classic category charts may set native PowerPoint data labels with |
| 179 | `data_labels`. Use `data_labels: true` for default value labels, or an object |
| 180 | with `show_value`, `position`, `number_format`, `font_size`, `font_family`, |
| 181 | `bold`, `color`, and optional per-point `colors`. Supported label positions |
| 182 | depend on chart type: clustered column/bar labels may use `outside_end`, |
| 183 | `inside_end`, `inside_base`, or `center`; stacked / percent-stacked column/bar |
| 184 | labels may use `inside_end`, `inside_base`, or `center`; line labels may use |
| 185 | `above`, `center`, or `best_fit`; area labels do not emit a native label |
| 186 | position. To label only selected data points, use `data_labels.points` with |
| 187 | zero-based `idx` plus optional per-point `position`, `number_format`, |
| 188 | `font_size`, `font_family`, `bold`, and `color`. |
| 189 | |
| 190 | **Combo chart schema**: `combo` uses shared `categories` plus either `plots[]` |
| 191 | or typed `series[]`. Each plot supports `type: "column" | "line" | "area"`, |
| 192 | its own `series`, and optional `axis: "secondary"` for a right-side value axis. |
| 193 | When primary and secondary plots genuinely use different category caches, |
| 194 | `plots[]` may also carry its own `categories` and `category_numeric`; the |
| 195 | workbook writer allocates independent category/value ranges. Typed `series[]` |
| 196 | continues to require the shared top-level categories. |
| 197 | Imported `plots[]` may carry `series_indices` so the verified source identity |
| 198 | where each `c:idx` equals its `c:order` survives when physical plot order differs |
| 199 | from legend order. If one plot supplies it, every plot must supply a same-length |
| 200 | list of unique non-negative JSON integers, and the combined values must form one |
| 201 | contiguous `0..N-1` range. Sources whose `idx` and `order` differ stay |
| 202 | fallback-only; typed `series[]` does not accept this plot-scoped field. |
| 203 | Typed `series[]` accepts the same `type` and `axis` fields per series, and |
| 204 | adjacent compatible series are grouped into the same PowerPoint plot. Area |
| 205 | series may set `fill_opacity` / `fillOpacity` as a `0..1` SVG opacity value |
| 206 | when the SVG fallback uses a transparent area fill under an opaque line. A line plot with `area_fill: true` |
| 207 | is exported as a PowerPoint area chart under the hood; `fill_opacity` only sets |
| 208 | the fill style and does not trigger conversion by itself. Combo export layers |
| 209 | area plots below columns and lines while preserving the original series indices. |
| 210 | Line and area series may set `line_width` / `lineWidth` in SVG px units to |
| 211 | match fallback `stroke-width`. |
| 212 | |
| 213 | **Narrow classic-axis schema**: `axes` is a closed object with the roles |
| 214 | `category`, `value`, `secondary_category`, and `secondary_value`. Each role may |
| 215 | set only `kind` (`text`, `date`, or `value`, as appropriate), `position`, |
| 216 | `visible`, `label_position` (`next_to`, `none`, `low`, or `high`), |
| 217 | `number_format`, `minimum`, `maximum`, `major_unit`, `reverse`, and |
| 218 | `major_gridlines`. `major_unit` applies to value axes only. PPTX date-axis |
| 219 | **import** is deliberately narrow: numeric Excel date serials are accepted for |
| 220 | area charts and OHLC stock charts; arbitrary date-axis source families are not. |
| 221 | This contract is not a full `AxisSpec`: logarithmic scales, minor units/gridlines, |
| 222 | crossing values, display units, tick skipping, and other unlisted OOXML semantics |
| 223 | remain unsupported and fail closed on import. |
| 224 | |
| 225 | **Narrow XY-axis schema**: `scatter` and `bubble` may use a closed `axes` object |
| 226 | with only `x` and `y` roles. Both roles have `kind: "value"`; `x.position` |
| 227 | is `bottom` or `top`, while `y.position` is `left` or `right`. Each accepts the |
| 228 | same closed fields above, and `major_unit` is valid on both value axes. PPTX |
| 229 | import requires the plot to reference exactly two mutually cross-linked |
| 230 | `c:valAx` nodes and separately enforces the closed field/topology gates. The |
| 231 | native writer emits and the importer reads back every field in this closed |
| 232 | contract. Scatter import derives the effective `scatter_style` from a uniform |
| 233 | per-series line/marker/smooth state; unsupported or nonuniform states remain |
| 234 | fallback-only. The normalized SVG fallback newly consumes only |
| 235 | `axes.x.major_gridlines` and `axes.y.major_gridlines`; the other fields do not |
| 236 | imply full visual-axis parity. |
| 237 | |
| 238 | **XY chart schema**: `scatter` and `bubble` use `series[].x` + `series[].y`; |
| 239 | `bubble` also requires one `series[].size` / `series[].sizes` value per point. |
| 240 | `series[].points` is also accepted as `[x, y]` / `[x, y, size]` tuples or |
| 241 | `{x, y, size}` objects. |
| 242 | |
| 243 | **Chart typography**: Metadata sizes use the same px-style unit as SVG text |
| 244 | (`1px = 0.75pt`). `style.font_family` and the role-specific |
| 245 | `title_font_size`, `subtitle_font_size`, `axis_font_size`, |
| 246 | `axis_title_font_size`, `legend_font_size`, and `note_font_size` fields are |
| 247 | required only when the native object must preserve typography that cannot be |
| 248 | inferred unambiguously from the visible fallback. |
| 249 | |
| 250 | **Chart chrome metadata**: Text that is visually part of the chart must be in |
| 251 | metadata, not only in SVG fallback children; metadata MUST still match visible |
| 252 | fallback chrome. `title` becomes the native chart title on classic charts; it |
| 253 | is not an object name, so use `name` for semantic object naming. `subtitle` |
| 254 | becomes the second rich-text line of that classic chart title. `title`, |
| 255 | `subtitle`, and axis-title values may be strings or objects with `text`, |
| 256 | `font_size`, `font_family`, and `color` when the fallback uses local role |
| 257 | typography. `svg_quality_checker.py` rejects `title`, `subtitle`, or axis-title |
| 258 | metadata whose text is not visible inside the replacement marker's fallback. Direct |
| 259 | `--native-charts-and-tables` export keeps the chart native but omits that inconsistent |
| 260 | chrome with a warning. chartEx keeps PowerPoint's empty `<cx:title>` and emits |
| 261 | the title / subtitle as companion editable text boxes until chartEx rich titles |
| 262 | are validated. Axis |
| 263 | titles are optional and explicit: use `axis_titles` with |
| 264 | `category`, `value`, `x`, `y`, or `secondary_value` keys, or the root aliases |
| 265 | `category_axis_title`, `value_axis_title`, `x_axis_title`, `y_axis_title`, and |
| 266 | `secondary_value_axis_title`; do not add semantic axis titles that are not |
| 267 | visible in the fallback. Set `show_value_axis_labels: false` when the fallback |
| 268 | keeps category labels but omits numeric value-axis tick labels, such as a radar |
| 269 | chart without radial coordinates. Native legends are metadata-controlled: use |
| 270 | `show_legend: true` and `legend_position` only when the fallback's legend is |
| 271 | meant to be replaced by PowerPoint's native legend. |
| 272 | Companion text such as `caption`, `source`, `note`, `notes`, `footnote`, and |
| 273 | `footnotes` is exported as editable PPT text boxes next to the native chart. A |
| 274 | companion entry may be a string or an object with `text`, `x`, `y`, `width`, |
| 275 | `height`, `font_size`, `color`, `align`, and `bold`; explicit bounds are |
| 276 | recommended so the native export matches the SVG fallback placement. Explicit |
| 277 | companion bounds are slide coordinates, not local coordinates inside a |
| 278 | transformed marker group. Use companion text for chart captions, source notes, |
| 279 | center labels, and freeform annotations; use `data_labels` for values that |
| 280 | belong to chart points. |
| 281 | |
| 282 | **Chart color styling**: For classic native charts, `style.colors` sets series |
| 283 | colors. The exporter also writes explicit chart-area fill, plot-area fill, |
| 284 | axis line, gridline, and label text colors so PowerPoint does not substitute a |
| 285 | white/default-theme chart. If omitted, the exporter infers these colors from |
| 286 | the visible SVG fallback: the largest panel-like `<rect>` becomes the chart |
| 287 | background, fallback text supplies label color, and fallback strokes supply |
| 288 | axis/grid colors. Override any of them explicitly under `style` with |
| 289 | `chart_area_fill`, `plot_area_fill`, `text_color`, `axis_color`, and |
| 290 | `grid_color`; use `"none"` for transparent chart or plot area fill. Generated |
| 291 | payloads default to uppercase `#RRGGBB`. The exporter retains compatibility for |
| 292 | `#RGB`, `rgb(...)` / `rgba(...)`, and common CSS names, normalizing them to |
| 293 | 6-digit OOXML RGB. Bar and column series also disable PowerPoint's negative-value |
| 294 | inversion so negative bars keep the same series fill instead of turning into |
| 295 | white/theme fill. |
| 296 | |
| 297 | For ChartEx native charts, valid payload `style.colors` (or root `colors`) |
| 298 | populate the ChartEx color-style part instead of being replaced by a fixed |
| 299 | accent1–accent6 list. Other ChartEx style semantics remain normalized. |
| 300 | |
| 301 | **PowerPoint chartEx schema**: `treemap`, `sunburst`, `histogram`, `pareto`, |
| 302 | `boxWhisker`, `waterfall`, and `funnel` use Office 2016+ chartEx parts. Use |
| 303 | these input shapes: |
| 304 | |
| 305 | | Type | Required data | |
| 306 | |---|---| |
| 307 | | `treemap`, `sunburst` | `values` plus either `levels` (`levels[level][point]`) or path-style `categories` (`[["Region", "Group", "Leaf"], ...]`) | |
| 308 | | `treemap` display note | Top-level group labels default to `overlapping`; override with `parent_label_layout: "banner" \| "overlapping" \| "none"`. PowerPoint labels only the top level and leaves — intermediate levels group tiles spatially without labels (sunburst shows every ring). | |
| 309 | | `histogram` | `values` | |
| 310 | | `pareto`, `waterfall`, `funnel` | `categories` + `values`; `waterfall` also accepts `subtotals` / `subtotal_indices` point indexes | |
| 311 | | `boxWhisker` | `series[].values`; optional `series[].categories` per value | |
| 312 | |
| 313 | > Note: chartEx files are valid PPTX and editable in PowerPoint; non-Microsoft |
| 314 | > renderers can display a limited subset. |
| 315 | |
| 316 | **Stock chart schema**: `stock` uses numeric Excel date serials in |
| 317 | `categories` or `dates`, plus exactly four series in open / high / low / close |
| 318 | order. Use either `series` with four entries, or top-level `open`, `high`, |
| 319 | `low`, and `close` arrays. PPTX import currently recognizes only canonical OHLC |
| 320 | stock charts with shared numeric date caches, `hiLowLines`, and `upDownBars`. |
| 321 | Safe stock series style may pass the structural gate, but stock series, |
| 322 | `hiLowLines`, and up-down bar local styling can still normalize under the |
| 323 | data-object-first contract. HLC, volume, noncanonical structure, and style XML |
| 324 | outside the safe parsing boundary stay fallback-only. |
| 325 | |
| 326 | **PPTX chart-import boundary**: The importer recognizes conservative classic |
| 327 | single-plot charts plus the verified scatter/bubble XY-axis, column/line/area |
| 328 | combo, area date-axis, canonical OHLC stock, radar, safe `of_pie` `serLines`, |
| 329 | axis/title/legend normalization, and bar/column gap/overlap subsets. Imported |
| 330 | `gapWidth` must be one canonical integer in `0..500`; imported `overlap` must be |
| 331 | one canonical integer in `-100..100`. Both values intentionally normalize to |
| 332 | the native writer contract rather than claiming exact source-style retention. |
| 333 | Malformed, duplicate, or out-of-range values fail closed. |
| 334 | |
| 335 | ChartEx import is closed to seven validated data models: `treemap`, `sunburst`, |
| 336 | `histogram`, `pareto`, `box_whisker`, `waterfall`, and `funnel`. The importer |
| 337 | retains their supported hierarchy/category/value/series/subtotal topology for |
| 338 | native read-back. Numeric cache values must be non-empty and finite, and cache |
| 339 | counts/indexes must be canonical non-negative decimal integers with exact, |
| 340 | contiguous topology; malformed, non-numeric, `NaN`, infinite, sparse, duplicate, |
| 341 | or mismatched caches fail closed. ChartEx style, axis, label, and binning details |
| 342 | outside the payload normalize. Full `AxisSpec`, arbitrary ChartEx families or |
| 343 | presentation fidelity, arbitrary stock variants, and axis/combo/date-axis |
| 344 | semantics outside the closed fields above remain fallback-only. The C4/C5 |
| 345 | import work does not expand the normalized SVG renderer and does not reduce |
| 346 | existing SVG-marker-to-native writer support. |
| 347 | |
| 348 | **Deferred chart types**: Exploded pie / doughnut variants, `map`, `heatmap`, |
| 349 | `bullet`, and `gantt` are intentionally outside the current native-object |
| 350 | support boundary. The exporter fails fast for these types until each mapping is |
| 351 | implemented and validated one by one. |
| 352 | |
| 353 | **Supported chart types**: |
| 354 | |
| 355 | - `column`, `bar`: `clustered`, `stacked`, or `percentStacked` (`grouping`) |
| 356 | - `line`: `standard`, `stacked`, or `percentStacked` (`grouping`); `line` or `lineMarker` (`line_style`, default `line` / no markers) |
| 357 | - `area`: `standard`, `stacked`, or `percentStacked` (`grouping`) |
| 358 | - `pie`: exactly one series, per-slice colors |
| 359 | - `doughnut`: exactly one series, per-slice colors |
| 360 | - `pieOfPie`, `barOfPie`: exactly one series, per-slice colors |
| 361 | - `radar`, `radarMarkers`, `radarFilled` |
| 362 | - `scatter`: `marker` (default), `lineMarker`, `line`, `smoothMarker`, or `smooth` (`scatter_style`) |
| 363 | - `bubble`: x/y/size series |
| 364 | - `combo`: `column`, `line`, and `area` plots, optional secondary value axis |
| 365 | - `treemap`, `sunburst`: hierarchical chartEx charts |
| 366 | - `histogram`, `pareto` |
| 367 | - `boxWhisker` |
| 368 | - `waterfall`, `funnel` |
| 369 | - `stock`: open / high / low / close series |
| 370 | |
| 371 | 3D chart aliases (`3DColumn`, `3DBar`, `3DLine`, `3DArea`, `3DPie`, cone, |
| 372 | cylinder, pyramid variants, and `surface`) are unsupported. |
| 373 | |
| 374 | Native legends are opt-in through `show_legend: true`; `legend_position` |
| 375 | defaults to `bottom` and accepts `top`, `left`, or `right`. |
| 376 | |
| 377 | **Forbidden — replacement marker transforms**: Do not rotate, skew, or matrix-transform table/chart replacement groups. Translate / scale is accepted; complex transforms fail export because PowerPoint-native table/chart frames do not preserve arbitrary SVG transforms. |
| 378 |