Files
craft-libs/crates/drawingml
Dylan M. Taylor 5ebc719ca2 craft-drawingml: model types for fills, lines, text, tables, charts and diagrams (#901)
* Add craft-drawingml: units, colours, preset shapes, custom geometry and text warps

The parts of DrawingML that need no document model, moved from DeckCraft
(60f91f78): crates/color, crates/geom (preset, warp, Xfrm and the EMU
conversions) and crates/pptx/src/custgeom.rs with CustomPath from
crates/model.

The code is unchanged except that custgeom reads craft-xml trees and writes
to a craft-xml Writer instead of DeckCraft's own XML helper, and matches
elements by local name. The 59 tests of the moved code come with it; six
more cover the custom geometry limits.

* drawingml: evaluate a custom geometry's guide lists once per coordinate space

read_cust_geom rebuilt Guides and evaluated avLst and gdLst again for every
path: up to 1,000 paths times up to 10,000 guides, each name copied and
hashed.

Guides are now cached per path coordinate space (w, h). At most 16 spaces
and 4 MiB of guide names are kept per geometry; paths in further spaces are
left out. The first space is always kept, and a geometry without guides is
not capped.

Ported from storytold/deckcraft#1789. A list counts as having guides only
when it has gd children, as that PR's review asked.

* drawingml: the model types for fills, lines, text, tables, charts and diagrams

Moved from DeckCraft (60f91f78) crates/model/src: style.rs, text.rs,
table.rs, chart.rs, numfmt.rs and trendline.rs, plus Diagram, DiagramNode
and the MediaId and SlideId newtypes from lib.rs.

The files are unchanged except that deckcraft_color:: is crate::color:: and
Fill and TableBorder allow clippy::large_enum_variant. Their 60 tests come
with them. Readers and writers follow.
2026-11-11 18:51:44 -05:00
..

craft-drawingml

The parts of DrawingML (ECMA-376 part 1, chapter 20) that presentations, word-processing documents and spreadsheets share. A .pptx slide, a .docx drawing and an .xlsx drawing all hold the same a: elements; this crate is the one place that knows what they mean.

Nothing here opens a package. Readers take a craft_xml::El and writers a craft_xml::Writer, so the caller decides which part the markup comes from. Lengths are points (1/72 inch) as f64; files store EMU (12 700 per point).

Module What Markup
crate root emu_to_pt, pt_to_emu, Xfrm (offset, extent, rotation, flips, as an affine), group_child_affine a:xfrm, a:chOff, a:chExt
color Rgba, SchemeSlot and ColorScheme (the twelve theme colours and their bg1/tx1 aliases), ColorTransform with apply, the preset and system colour names a:srgbClr, a:schemeClr, a:prstClr, a:sysClr, a:lumMod, a:tint, a:alpha …
preset build(name, w, h, adjusts): the outline of a preset shape as fill and stroke sub-paths, its text rectangle, adjust handles and connection sites; CATALOG with labels, categories and default adjusts a:prstGeom
custgeom read_cust_geom / write_cust_geom between a:custGeom and CustomPath (an M L C Q Z path string per sub-path), and Guides, the shape guide formula evaluator a:custGeom, a:gd
warp warp(name, adjusts, rect): the bands and guide curves of a preset text warp, and the mapping of laid-out lines onto them a:prstTxWarp

The model

These modules hold types only; their readers and writers are not here yet.

Module Types Markup they stand for
style ColorRef (a base colour plus transforms, resolved against a ColorScheme), Fill, Gradient, PictureFill, Line, Effects, ShapeStyle a:solidFill, a:gradFill, a:blipFill, a:pattFill, a:ln, a:effectLst, p:style
text TextBody, Paragraph, ParaProps, Run, RunProps, ListStyle, hyperlinks and actions a:txBody, a:bodyPr, a:lstStyle, a:p, a:r
table Table, Row, Cell, TableStyleDef and the built-in table styles a:tbl, a:tblStyleLst
chart Chart, Series, axes, data labels, trendlines and error bars, with the values they compute c:chartSpace, cx:chartSpace
numfmt format(code, value, lang): number format codes as charts and cells use them c:numFmt
diagram Diagram, DiagramNode dgm:dataModel

A picture or sound is a MediaId; whoever owns the package keeps the bytes. Themes are not here yet.

Preset shapes and text warps

Preset names are the file's (roundRect, rightArrow, textArchUp) so files round-trip. The outlines are constructed in Rust from what each shape looks like and from the meaning of its adjust values; no geometry table is copied. Adjust values use the file convention: ratios in 1/100 000 of the shorter side (or of the width or height), angles in 1/60 000 degree.

let g = craft_drawingml::preset::build("roundRect", 200.0, 100.0, &[25_000.0]).unwrap();
assert!(!g.paths.is_empty());
assert!(g.text_rect.width() < 200.0);

Hostile input

read_cust_geom reads at most 1 000 paths per geometry, 100 000 commands per path and 5 000 guides per list, keeps at most 10 000 guide values, and evaluates the guide lists once per coordinate space, for at most 16 spaces and 4 MiB of guide names. Every evaluated value is finite and within ±1e15; a division by zero is 0. An arc that sweeps a full turn or more draws one turn. preset::build and warp::warp take any f64 and return None or a finite outline. Each limit has a test.

The types derive serde traits with the field names the apps' own document files already use.