Use checked arithmetic for ASF header-object bounds and cursor advances so hostile object sizes cannot panic. Keep a synthetic regression for the malformed header.
Addresses #750 (ASF object-size overflow).
Dragging the Interface size slider set the interface scale on every
frame, which grew or shrank the slider under the pointer and ran it to
one of its ends. Keep the value pending in the dialog while the pointer
holds the slider and apply it on release; keyboard changes still apply
at once.
A picture was decoded at the size of its box on screen. A cropped picture
shows only part of the image, magnified, so it was decoded at a fraction of
the resolution it needs and stretched back up: Merge Shapes > Intersect,
which crops the picture to the new smaller box, left it visibly blurred.
Size the decode from the rectangle the whole image occupies instead.
anchorCtr's centreing offset was computed for a block that starts at
the box's left edge and then added to lines that alignment had already
placed within the box. For a right-aligned line the two shifts
stacked: the line ended up starting at the box's centre and ran past
the right edge - on the tdf165341 fixture the sentence was cut off at
"...paragraph rig". The clamp at zero made it worse for content wider
than the box: all the overflow went to the right only.
The origin is now set so the drawn text block's own centre (min/max of
the laid-out line extents) lands on the box's centre: every alignment
stays inside the box when it fits, and overflow centres symmetrically
past both edges, as PowerPoint draws it. Empty lines carry no ink and
stay out of the block; vertical text and multi-column bodies are
unchanged (centring is unimplemented for them, as before).
Tests: right/left-aligned, trailing empty paragraph, mixed alignment,
symmetric overflow, and a no-anchorCtr control - the block's centre
equals the box's centre in every case.
Reported with a public LibreOffice test fixture (tdf165341.pptx);
verified by rendering the slide before (text cut at the right edge)
and after (whole sentence centred).
The Preferences dialog applies its controls as they change (appearance,
interface size, the editing checkboxes, the comment author), and its Cancel
result was discarded, so Cancel did nothing and the window stayed open.
The dialog now remembers the settings it opened with. Cancel, Esc and the
window's close button put them back and close it; Done keeps the changes.
blank_presentation's debug_assert would panic in debug builds (never-crash
rule): a fresh deck that cannot fit its title slide now simply arrives
without it. The exhaustion test also covers insert.smartArt and
insert.textBox.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Union merges of the 2026-10-14 community PR wave stacked header lines and
left near-duplicate rows, bullets and paragraphs with per-PR counts. Each
progress doc now has one status line and one row per fact, with current
numbers: breadth 84% over 191 features (cargo xtask parity, after removing
duplicate Thesaurus/AutoCorrect/Artistic effects/Corrections checklist
rows), 320 engine commands, 41 dialogs, 1,641 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix-ups in the merge (review): zoom targets, sections and titles are looked up with get()
instead of indexing; Hyperlink literals carry both main's sound fields and zoom_return;
duplicated test statements left by the test-file union are removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The landing branch checks that every params doc parses into its keys; #509's
used `;` between keys, which that parser rejects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SmartArt and OLE objects now keep their parts as media items
(ShapeKind::Opaque::parts). media_refs visits them, so slide.reuse copies them
and picture.compress never deletes them; the Opaque arm names every field so a
new one can't be missed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main (#346) shows why a file couldn't be opened as a notice over the start
screen; #488's background open put the error in the status bar instead, which
broke a_file_that_cant_be_opened_says_why. The background open now reports
through the same notice, and the test waits for the background read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The progress window's result was added with add_presentation_file, straight
into the session: not journaled and outside the command panic guard that
file.open runs under. The host now hands the loaded file to
Session::stage_loaded and runs the new internal command file.adopt {path,
token}, which shares file.open's tail (repaired-copy handling, damaged-media
report) and so is journaled and guarded. Without the staged copy (a replayed
journal, a stale token) file.adopt reads path like file.open. Reading still
happens on the background thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shape3d::parse scanned the kept a:scene3d/a:sp3d XML as strings and copied the
camera, light rig, direction, material and bevel names from the file into the
XML it writes back unescaped. It now parses the fragment with the importer's
XML reader (exported from deckcraft-pptx as parse_xml) and keeps a value only
when it is in the format's lists (ST_PresetCameraType, ST_LightRigType,
ST_LightRigDirection, ST_PresetMaterialType, ST_BevelPresetType), else the
default; lengths are clamped and the extrusion/contour colours are
re-serialized (escaped) by the XML writer. The Format Shape pane rows in
ui-parity.md that earlier merges had duplicated are folded into one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
picture.compress decided which originals were still in use by scanning JSON
keys, which missed artistic-effect originals (and the embedded-font shape it
assumed no longer exists), so compressing a picture whose bytes were also
another picture's artistic original deleted media still in use. It now uses
the engine's typed media walker (media_refs::presentation_media).
Decoding is capped: the header's size is read first and pictures over 64 MP
are left alone, and the decoder runs with image::Limits (512 MiB allocation),
instead of decoding up to 400 MP before checking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
slide.reuse indexed src.slides[i] and found media by scanning JSON keys
(media/poster/preview/sound), which missed artistic-effect originals and would
miss any reference stored under another key. New engine module media_refs: one
typed walker over the model (written once, expanded for & and &mut) that visits
every MediaId a presentation, master, layout, slide or shape can hold: pictures
and their artistic originals, picture fills in shapes, lines, runs, table cells
and styles, chart series and points, backgrounds and theme format schemes;
media clips and posters, 3-D models, OLE/SmartArt previews and node pictures,
picture bullets, hyperlink, transition and animation sounds, embedded fonts.
slide.reuse now lists and remaps media through it, uses get() for slides, and
keeps a linked media item's link when copying it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A chart's kept parts were written under the names the source file gave them,
so a relationship to ppt/media/image1.png (or any name the writer generates)
produced a second zip entry with that name on save. Kept parts are now written
as ppt/<folder>/keptN.<ext> with a content-type override each, the chart's and
the kept parts' own relationships are rewritten to the new names (relationships
to parts that were not kept are dropped), and the package writer skips any
second entry under a name it already wrote. externalData/userShapes are dropped
only when their relationship is not written back.
Office 2016 chartex charts (added on main since the PR) capture and write back
their kept parts the same way; the plain-chart fallback carries none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The caret's colour (caret_color) is kept and used at main's caret draw site; the
placeholder-prompt hiding is kept too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
design.browseTheme reads its path with io::read_file, and File › Info sizes the file with
io::file_len (which checks the read root), so commands_do_not_touch_the_file_system_themselves
passes. spell.rs is allow-listed beside recovery.rs: it touches only the app's own dictionary
folders. main's render_slide MCP tool refuses a `path` argument (#371), so the root tests
render to a file through call_app ui.render instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>