Conflicts resolved onto main's newer shape, and the PR's policy applied to
every alloc_id/add_media/new_slide call site main has grown since: commands
answer bad(cmd, NO_ID_SPACE) (slide.reuse, insert.zoom, SmartArt, photo
album, review.accept, handout/notes masters, master placeholders,
background/artistic/compress pictures, transition sound, ink to shape,
paste special, file.new templates); reid/bring_media/renumber/regenerate
and compare::adopt/accept are fallible; outline import keeps what fit.
Tests use expect() on the now-Option results.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix-ups in the merge (review): uniform-width and highlighter strokes are drawn as one path (on
the slide and in the live preview) so see-through ink doesn't darken where segments overlap;
segment pairs are destructured instead of indexed; tests that build pointer events set the new
pressure field.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
repair() brings each slide side into PowerPoint's 72–4032 pt (as Design › Slide Size allows;
it accepted 1–4000 before), and replaces a zero, negative or non-finite size with 16:9; a bad
notes size gets the default. The native reader takes a null, string or missing slide/notes
size as NaN for repair to fix, so such a file opens instead of failing as damaged (NaN and
infinity are saved as null). The canvas fit and slide show fit treat a zero or non-finite side
as 1 pt, so an in-memory bad size can't make layout infinite.
Co-authored-by: Cursor <cursoragent@cursor.com>
Name and Title Organization Chart with a title box per node (DiagramNode.title, smartArt.setNodes title; written as the title box's presentation point text and read back). Half Circle Org Chart draws two half rings per node. New layouts: Vertical Box List, Trapezoid List, Table List, Accent Process, Continuous Block Process, Basic Bending Process, Linear Venn, Stacked Venn, Nested Target. smartArt.fromText (Convert to SmartArt), smartArt.toText and smartArt.toShapes, with a Convert menu in SmartArt Design.
Co-authored-by: Cursor <cursoragent@cursor.com>
Organization Chart and Half Circle Organization Chart with elbow connectors; assistant nodes (Add Assistant, written as asst points and read back) sit beside their parent on a row of their own. Segmented Cycle, Multidirectional Cycle, Cycle Matrix, Segmented Pyramid and Basic Pie. Venn labels move further out so the third circle's no longer runs into the overlap.
Co-authored-by: Cursor <cursoragent@cursor.com>
Each shape's text shrinks so its longest word fits on a line (the pyramid tip no longer breaks words). Three picture layouts (Vertical Picture List, Picture Caption List, Picture Accent Process) with a picture placeholder per node and smartArt.picture to fill it. Five 3-D SmartArt Styles (Polished, Inset, Cartoon, Powder, Brick Scene) drawn with lit bevels, and Colorful Range and Gradient Range colours; all written to and read from the .pptx quickStyle and colors parts.
Co-authored-by: Cursor <cursoragent@cursor.com>
A pen reading of 1 is the full pen width. A lighter touch draws a thinner
stroke, and a zero reading stays visible at 8% of the pen. A mouse, a
missing or non-finite reading, and a stroke whose samples are all the old
placeholder 0.5 still draw at the pen width. PPTX keeps one width per
stroke: the mean of the samples for a pressured stroke, the pen width for
a legacy one. Tilt is not read.
Co-authored-by: Cursor <cursoragent@cursor.com>
Layouts now include Vertical and Horizontal Bullet List, Basic Block List,
Basic/Chevron/Vertical/Step Up Process, Basic and Block Cycle, Basic Radial,
Basic and Inverted Pyramid (bands drawn as polygons so the slope and the
text stay right), Basic and Titled Matrix, and Hierarchy. Each layout says
which nodes each shape shows, so typed text maps back for any layout.
SmartArt Styles (flat, subtle, intense, white outline) and Change Colors
(colourful or one accent) are kept on the diagram, offered in the SmartArt
Design tab, and written to .pptx as quick style / colour definition ids
(and read back from them).
Co-authored-by: Cursor <cursoragent@cursor.com>
Export: a group with a diagram becomes a graphicFrame naming ppt/diagrams
data, layout, quickStyle and colors parts, plus the group's shapes as the
cached drawing (dsp). The data model is built from the outline (doc, node,
parTrans/sibTrans points and parOf links). A diagram read from a file keeps
its own layout, quick style and colour parts until its layout is switched.
Import: the outline is rebuilt from the data model's parOf tree, so
PowerPoint diagrams get the Text Pane. Their shapes stay the file's drawing
until the first outline edit (typed text isn't mapped back to nodes then).
Co-authored-by: Cursor <cursoragent@cursor.com>
A SmartArt group now keeps its outline (Shape.diagram: layout + nodes with
levels) and its shapes are regenerated from it inside the group's child
rectangle. New commands: smartArt.nodes, setNodes, addNode (after, before,
above, below), removeNode, promote, demote and layout. Sub-points become
bullets in their parent's shape; the hierarchy layout is a real tree with
connector lines. Text typed into a diagram shape is read back first.
UI: a SmartArt Design contextual tab and a Text Pane (Enter adds a line,
Tab/Shift+Tab demote/promote, Backspace on an empty line removes it).
Co-authored-by: Cursor <cursoragent@cursor.com>
An owned corpus opens, saves and reopens SmartArt and OLE kept as XML, audio and video bookmarks and trims, a section with a hidden slide, slide-number and date fields, ink, .glb models and animation repeat, bounce end and triggers. Relationship-bearing SmartArt and OLE, playback options and rewind stay with their open PRs.
View > Guides used to paint two fixed centre lines. Guides now belong
to the presentation (DrawGuide, read from and written to p:guideLst in
viewProps.xml, in eighths of a point), so they survive a save and
reopen in PowerPoint.
- Turning Guides on gives a deck without guides PowerPoint's two
centre guides.
- A guide can be dragged anywhere away from shapes. It moves in whole
points, unless Alt is held or snapping is off, and shows its
distance from the centre while it moves.
- A guide dropped off the slide is deleted.
- The slide context menu's Grid and Guides submenu has Add Vertical
Guide and Add Horizontal Guide.
- While guides are shown, moving and resizing shapes snaps to them.
New commands guide.add, guide.move and guide.delete are undoable;
guide.list is a query.
Co-authored-by: Cursor <cursoragent@cursor.com>
Decks from SharePoint and Microsoft 365 carry customXml items (document
library metadata and content types), docMetadata/LabelInfo.xml (the
Microsoft Purview sensitivity label) and docProps/custom.xml. All were
dropped on save, so a labelled corporate deck came back unlabelled. They
are now kept verbatim with their content types, their own relationships
(itemProps) and the root or presentation relationship linking them. Kept
parts never replace a part the writer makes.
Co-authored-by: Cursor <cursoragent@cursor.com>
An owned corpus of those deck kinds survives open, save and reopen. The
check found the handout master — printed header, footer, date and slide
number — was dropped on save, so import and export now keep that part.
p:custDataLst points at ppt/tags/tagN.xml parts that add-ins such as
think-cell, Mekko Graphics and Office Timeline use to recognise their
decks, slides and shapes. They were dropped on open, so a saved deck lost
its add-in identity. The model now carries name/value tags on the
presentation, slides and shapes; the reader resolves every tags part and
the writer emits one tags part per holder, in the schema's element order.
Co-authored-by: Cursor <cursoragent@cursor.com>
A deck that embeds a TrueType or OpenType face, including one wrapped as Embedded OpenType, now draws with that face and saves it back. MicroType Express subsets stay in the file and still draw in a stand-in.
Co-authored-by: Cursor <cursoragent@cursor.com>
alloc_id bumped next_id with saturating_add: a presentation whose
next_id reached u32::MAX (a hostile file can arrive that way;
fix_next_id imports it) handed out that same id to every new slide,
shape, layout and media - duplicate ids, ambiguous references, silent
corruption on every later insert.
alloc_id now returns Option<u32> via checked_add, and every caller
refuses: the engine's insert/duplicate/media/layout commands answer
with a clear error naming the command, paste and duplicate re-id
helpers propagate it, media addition is fallible, and a fresh slide is
Option. The pptx export's synthesized blank layout is guarded the same
way. Loading keeps such a deck viewable: repair re-ids duplicate
slides while it can, and a duplicate it cannot re-id stays as it is -
growth is refused, and only that duplicate keeps its ambiguous id.
Renumbering the loaded graph was rejected: it needs the
cross-document reference remapping that issue #406 tracks.
Tests: model exhaustion (alloc_id None, fresh media refused, a new
slide refused), engine (slide.new, shape.insert, slide.duplicate and
edit.duplicate all refuse; the document holds no duplicate ids),
format repair (an unre-id-able duplicate stays visible).
Value axes showed raw numbers ("800000") with a fixed ~5-interval scale and
fixed label gutters, deleted axes were still drawn, and data labels ignored
their number format, so Russian business charts read "516600000" where
PowerPoint shows "516 600 000 ₽".
- New deckcraft_model::numfmt: Excel number format codes (sections for
positive/negative/zero, quoted and escaped literals, _x padding, *x fill,
[$sym-lcid] currency, [colour]/conditions dropped, 0/#/? placeholders,
grouping with ',' or the locale's literal space, trailing-comma scaling,
%, General) with digit grouping and decimal mark from the chart's
language (c:lang: ru-RU -> "800 000", "12,5%").
- Reading: each value axis' numFmt, c:delete, scaling min/max and
majorUnit, for the primary and a combo chart's secondary axis; data
labels' numFmt (series, then group, else the source data's formatCode);
the chart's c:lang. The primary value axis is the one on the left
(bottom) and the secondary the one on the right/top or crossing at max,
whatever the plot groups' order.
- Scale (Chart::axis_scale): fixed bounds and unit where given, else the
data plus 5% headroom rounded out to the smallest 1-2-5 unit that leaves
as many intervals as the axis has room for labels, as PowerPoint does
(0-800 000 by 100 000, 0-7 000 by 1 000 on the sample deck).
- Rendering: labels in the axis format, gutters sized to the longest label,
deleted axes draw no labels and take no room, data labels in their own
format.
* Text: Ctrl+arrows, Ctrl+Backspace/Delete and the other text keys work like PowerPoint (#268)
Observed black-box in PowerPoint for Windows on synthetic text:
- Word stops (crates/model/src/words.rs): words, punctuation runs, symbol
runs, tabs, line breaks and each CJK character are tokens; trailing spaces
join the token before. it's, a-b, 3.14, 1,000 are one word; e.g., a_b and
x@y.com are not. Grapheme clusters never split.
- Ctrl+Left/Right and Ctrl+Shift+Left/Right move by those stops, across
paragraphs; with a selection they go on from its start/end. In a
right-to-left paragraph Left and Right swap.
- Ctrl+Backspace deletes to the previous stop; Ctrl+Delete deletes the rest
of the word, or spaces plus the next word, then applies PowerPoint's smart
spacing. With a selection both work from its start.
- Ctrl+Up/Down by paragraph, Ctrl+Home/End to the text's ends.
- End on a wrapped line goes past the trailing space and the caret stays
drawn at that line's end (TextSel::line_end); End again stays, Home
returns to the same line.
- Ctrl+]/[ and Ctrl+Shift+>/< change font size (egui's Period/Comma/
bracket key names now match registry shortcuts).
- Shift+F3 cycles lower -> Title -> UPPER; a caret changes its word.
- Double-click selects the word without trailing spaces; triple-click
includes the paragraph mark.
- Mac keeps Option for words and Cmd for line/text ends (Session::mac_keys).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* rustfmt
* Canvas: one caret, drawn at a wrapped line's end after End
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #133: Selection pane engine: restack inside groups, Show/Hide All, Lock like PowerPoint
- arrange.bringForward/sendBackward/bringToFront/sendToBack and arrange.reorder work on a
group's children, which stay in their group (as PowerPoint's pane arrows and drag do);
steps that change nothing leave no undo step.
- selection.select (pane click: Ctrl toggles, children and top-level objects don't mix,
picking a hidden object shows it), selection.showAll (everything, children too) and
selection.hideAll (top level).
- shape.lock cascades through groups; locked objects can be selected, formatted and typed
in, but not moved, sized, rotated, flipped, reshaped or cropped (commands and canvas).
- PPTX: Lock reads and writes PowerPoint's full locking attribute set per element kind
(observed by saving locked objects from PowerPoint); partial locks aren't Lock.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Front-most first with groups folding; drag a row to restack among its siblings (accent
insertion line); Bring Forward / Send Backward arrows; Show All / Hide All; per-row eye and
padlock; double-click or F2 renames in place; Ctrl+click toggles, Shift+click selects a run;
arrows, Home, End and Esc in the list; right-click menu. Lock/Unlock on the canvas menu,
greyed handles on locked objects. egui_kittest tests drive the pane.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Tables: styles the file defines are read, drawn and kept
PowerPoint writes the definition of every table style a deck uses into
ppt/tableStyles.xml. DeckCraft mapped a handful of GUIDs to its own looks
and drew any other style as Medium Style 2, so themed and light styles came
out in the wrong colours, and dropped the definitions on save.
The reader now keeps each definition (whole table, bands, first/last
rows and columns, corner cells, table background; fills, theme fill and
line references, borders, text colour and bold) on the presentation, and
tables using one keep its GUID. The renderer applies the parts that cover
each cell in the spec's order, a part's edge lines on its region's edges
and its inside lines within it, and paints fills as real fills (gradients
and see-through colours). The writer puts the definitions back as read.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Tables: DeckCraft's own table styles still map back to their ids
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix a brace lost in the rebase
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Theme background pictures (Office 2007-era themes such as Treck) carry an
a:duotone recolour in phClr; DeckCraft read it but drew the picture
unchanged, so those backgrounds came out grey. The painter now resolves the
duotone's colours against the slide's colours and placeholder colour, and
the decoded-image cache applies them (luminance picks a colour between dark
and light) and keys on them.
Layouts and slides can replace the master's colour map
(p:clrMapOvr/a:overrideClrMapping, e.g. dark section layouts mapping bg1 to
dk1). They were read as the master's map; now Layout and Slide carry an
optional colour map, resolve::Ctx uses slide > layout > master, and the
writer keeps them.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An object DeckCraft doesn't model kept its XML, but the parts that XML
points to (diagram data, layout, style and colours; an OLE embedding)
were not kept, so a save wrote it as an empty rectangle or its preview.
Opaque now carries those parts (relationship id, full relationship type,
the part as a media item) and the writer re-adds them under the slide's
new relationship ids. A part with relationships of its own, or an
external link, still falls back to the preview with the existing
warning. A diagram's data part loses its link to the cached drawing,
which is a slide relationship id a save renumbers; only diagrams whose
drawing is missing or empty reach this path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When typing in Kurdish, Arabic or Hebrew in a new document or text shape, default
paragraph direction (props.rtl: None) was resolving to LTR. In an LTR
paragraph, visual reordering pinned newly typed characters and the caret to the
left margin, resetting to the start of the line with every keystroke and jumping on
space characters due to UAX #9 rule L1.
- Add first_strong_is_rtl() in deckcraft-model following UAX #9 rules P2/P3.
- Auto-resolve unassigned paragraph direction to RTL in shape_para layout when the
paragraph starts with an RTL script, defaulting alignment to Align::Right.
- Auto-promote paragraph to RTL (props.rtl = Some(true), props.align = Some(Align::Right))
when typing or inserting text starting with an RTL character.
- Add regression tests in deckcraft-model, deckcraft-text, and deckcraft-engine.
- crates/media: probe (duration, rate/channels, size/fps; WMA/WMV recognised), audio decode to
f32 PCM (MP3, AAC M4A/ADTS, ALAC, WAV, AIFF, CAF, FLAC, Vorbis via symphonia; Opus with our
decoder in Ogg/WebM/MKV), video decode (MP4/MOV/WebM/MKV) with YUV->RGBA, poster frames,
trim/fade/volume, Player mixer + per-voice clock (device-clocked or wall-clocked), VideoFeed.
- Codec crates ported from FilmCraft: bitstream, h264, hevc, vp9, av1, isobmff, matroska, ogg, opus.
- insert.audio/insert.video probe and store duration/size and a poster frame; unplayable media
is embedded with a warning. New media.play/pause/toggle/stop/seek/info/posterFrame.
- UI: MediaHost (cpal output injected by the desktop app), control bar under media, video frames
on canvas and in the show; autoplay, click to play, loop, rewind, across slides, hide, full screen.
- Tests with in-code WAV/AIFF and CC0 self-generated fixtures (make.sh, ATTRIBUTION.md rows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>