Skip to content

SVG pictures; align and distribute in ppt_arrange; golden p16 (round 3: I1, A5) - #12

Merged
uvrt merged 1 commit into
mainfrom
r3-align-svg-pictures
Oct 11, 2026
Merged

uvrt merged 1 commit into
mainfrom
r3-align-svg-pictures

Conversation

@uvrt

@uvrt uvrt commented Oct 11, 2026

Copy link
Copy Markdown
Owner

Production feedback round 3, items A5 and I1.

I1: SVG pictures

Measured in PowerPoint for Mac 16 (an SVG inserted by script and the deck saved):

  • a:blip r:embed=<png> with a:extLst/a:ext uri="{96DAC541-7B7A-43D3-8B79-37D633B846F1}"/asvg:svgBlip xmlns:asvg="http://schemas.microsoft.com/office/drawing/2016/SVG/main" r:embed=<svg>
  • the SVG is ppt/media/imageN.svg, Default Extension="svg" ContentType="image/svg+xml", behind an ordinary officeDocument/2006/relationships/image relationship. PowerPoint does not write the 2017/06/relationships/svgImage type, so neither do we (both read).
  • the picture is named "Graphic N", with an empty a:picLocks; PowerPoint's own PNG is the SVG at twice its size (192 dpi), and PowerPoint redraws the PNGs when it saves.

API:

  • slide.add_picture(svg_bytes_or_path, left, top, width=None, height=None, *, name=, description=, fallback=None, fallback_dpi=300): the fallback is fallback, else drawn by pptx2svg.png.svg_to_png at the placed size (an ImportError names pptx-agent[png] without the extra); the native size comes from width/height (px, pt, pc, in, cm, mm) or the viewBox, at 96 dpi.
  • picture.vector -> bytes | None, picture.set_vector(svg | None) (one undo step; the PNG stays), picture.replace_image(png, svg=...) or replace_image(svg) (fallback made).
  • The SVG is parsed as untrusted data (fullstate.safe.parse_untrusted: no DTD, no entities, 16 MiB) and must have an svg root in the SVG namespace. The bytes are stored as given, and identical SVGs once.
  • The outline marks picture svg, and insert_outline reads that mark; the geometry shows image.svg. ppt_add_picture takes image/svg+xml blobs to insert and replace, and returns svg: true with the SVG's native size.

Office: our decks open in PowerPoint without a prompt and draw the vector (a picture with a deliberately orange fallback shows the blue icon). A save-as keeps every svgBlip. PowerPoint reports our pictures as shape type graphic, and selecting one shows the Graphics Format tab (Graphic-indeling) with Graphics Fill and Convert to Shape. Clicking Convert by UI script did not work, so the conversion itself was not checked. pptx2svg's SVG embeds the vector. Its PNG drops an SVG whose root size has units (width="72pt"), because resvg-py 0.5 refuses it. That is a renderer follow-up; our fallback rasteriser works around it.

A5: align and distribute

ppt_arrange gains:

  • align, with edge (left, center, right, top, middle, bottom) and to (shapes by default, slide, or content_area).
  • distribute, with axis and within (shapes by default, or content_area), plus an optional gap.

Both run through pptx_agent.edit.arrange. The content area is Slide.content_area: the layout's body placeholders, else the band under the title. Each call is one undo step, returns moved and the boxes, and gets the normal per-edit checks. GUIDANCE.md says a house rule that requires equal spacing should name these actions.

Budgets

  • Core: unchanged, 3,398 estimated (about 5,029 counted, within 5,500).
  • All definitions: 11,113 → 11,309, so the guard rises from 11,200 to 11,400 (note in test_tools.py).
  • Strict mode: ppt_arrange now has 8 optional parameters and no longer fits the 24-optional limit beside the others, so it is sent non-strict (the dispatcher still validates it). ppt_add_table becomes strict in its place: 29 free-text strings (limit 32) and 24 optional parameters. The provider-marked test_strict_tools_keep_every_field_given was not run (no API calls).

Golden p16

The Halden approach slide: four phases, each an SVG icon above its label, grouped, then distributed across the content area and aligned to its middle. 7 calls, 17/17. PowerPoint's PDF export of the output was checked.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WCxhY1fvWQPiWCwz7SYUVh

SVG pictures as PowerPoint 2016+ stores an inserted SVG, measured in PowerPoint
for Mac 16: the PNG in a:blip, the SVG behind its asvg:svgBlip extension
(xmlns:asvg on the element), an image/svg+xml part behind an ordinary image
relationship, the picture named "Graphic N" with its aspect unlocked.
slide.add_picture takes an SVG (fallback= or one drawn by pptx2svg at the placed
size and fallback_dpi; an ImportError names pptx-agent[png] without it; native
size from width/height or the viewBox at 96 dpi). The SVG is parsed as untrusted
data first (the apply_svg parser: no DTD, no entities, 16 MiB) and stored once,
as given. picture.vector, picture.set_vector (one undo step), replace_image(png,
svg=) or an SVG image; the outline marks "picture svg", the geometry image.svg;
ppt_add_picture takes image/svg+xml blobs to insert and replace.

ppt_arrange gains align (edge; to: shapes, slide, content_area) and distribute
(axis; within: shapes, content_area; gap) through pptx_agent.edit.arrange, the
content area being Slide.content_area: one undo step each, the boxes returned.
GUIDANCE.md: a house rule requiring equal spacing should name them.

Every-definition guard 11,200 -> 11,400 (11,309 estimated); the core unchanged
(3,398, about 5,029 counted). ppt_arrange no longer fits the strict optional
parameter limit and goes non-strict; ppt_add_table takes its place (29 free-text
strings, 24 optional parameters).

Golden p16: the Halden approach slide, four phases with SVG icons spaced across
the content area (distribute, align), 7 calls, 17/17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WCxhY1fvWQPiWCwz7SYUVh
@uvrt
uvrt merged commit 606fddc into main Oct 11, 2026
19 checks passed
@uvrt
uvrt deleted the r3-align-svg-pictures branch October 11, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant