You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The architecture-diagram layout engine under-estimates auto-sized box heights, causing
text to overflow the rounded rectangle. Two independent defects compound:
CJK text (affects all templates, including 16:9): the width estimate assumes
halfwidth characters (~5pt average). Fullwidth characters are 11pt (1em), so Japanese description text gets roughly half the lines it needs.
Non-16:9 templates: the estimation constants are burned in for the 16:9 physical
density (1px = 0.5pt). On 4:3 (1px = 0.375pt) the same 11pt text occupies 33% more px.
Measured shortfall of the estimated height vs. required height (box width 240px,
title + description, build_layout direct invocation):
Case
16:9
4:3
Halfwidth title only
-4%
-15%
Halfwidth + description
-5%
-40%
Fullwidth (Japanese) + description
-26%
-51%
This was listed as a known limitation in #283 (workaround: specify box.height
explicitly). The "~25%" figure there under-stated the impact — line wrapping is
discrete, so the two defects multiply.
Root cause
sdpm/sdpm/engine/layout/placement.py:43-49:
char_per_line=max(1, bw//10) # assumes 1 char = 10px = 5pt at 16:9, halfwidth only
...
bh=lines*24+40# assumes 1 line = 24px = 12pt at 16:9
The true calibration in pt (verified against the builder): box text is always 11pt
(layout/model.py), rendered line height 13.2pt (1.2em), padding 20pt, average char
width 5pt halfwidth / 11pt fullwidth. The px constants are these pt values converted
at the 16:9 rate only. Icon-label metrics (len(line) * 8, label heights) have the
same burn-in.
The same 16:9 rate is also hardcoded in the knowledge layer: the width guide in sdpm/references/workflows/slide-json-spec.md teaches fullwidth = pt × 2 × char count
— the «× 2» is 1pt = 2px, true only for 16:9. Agents budget text 33% too optimistically
on 4:3 for every textbox, not just arch diagrams.
Design
ptPerPx — declare the deck's unit relationship in slideSize
Slide JSON mixes two unit systems: layout in px (virtual, width normalized to 1920),
fonts in pt (physical). The exchange rate is ptPerPx = physical width in pt / 1920,
which cannot be derived from the canvas size (4:3 and 16:10 presets share
0.375 despite different aspect ratios — it depends on physical width).
deck.json's slideSize is best understood as the spec's coordinate-system
declaration (like an SVG viewBox) — px coordinates are meaningless without it, and generate already validates the declaration against the template. The pt↔px rate is
part of that declaration:
render_architecture → build_layout → _layout_scale gain a keyword argument.
Estimates are rewritten as pt-true values converted by pt_per_px, with per-character
CJK-aware width (fullwidth = 11pt, halfwidth = 5pt). Default 0.5 is the exact 16:9
value, so all existing callers are unchanged.
Rejected alternatives (recorded for the future):
Passing slide_width_emu: EMU is an infrastructure concept; layout/ currently
imports nothing but stdlib and stays a template-agnostic pure px space
Reusing the converter's conversion_scale ContextVar: layout → converter would
invert the dependency DAG, and implicit global state breaks the pure-function tests
Deriving from canvas height (assume 7.5in): swaps a 16:9 burn-in for a
7.5in burn-in (16:10 presets are 6.25in → 17% error)
Scaling the font instead of the estimate: the engine-wide convention is
"pt is physical, px is normalized layout"; arch text would become inconsistent
with surrounding slide text
Adding text-overflow checks to lint: heuristic width checks are false-positive
prone (fonts, autofit, mixed scripts); the accurate detector already exists at
generate/preview time (LibreOffice renders the real pptx — correct for any
aspect ratio since fix: 4:3 など 16:9 以外のカスタムテンプレートで生成すると、レイアウト崩れ・プレビュー見切れが発生する #208's isotropic measure fix). Budgeting belongs to the
knowledge layer, verification to generate — lint stays context-free
Note: 16:9 output changes too (intentional)
This is a bug fix: 16:9 boxes with halfwidth text grow ~5-10%; Japanese descriptions
gain their missing lines. Existing decks re-generate with slightly different arch
layouts.
Summary
The architecture-diagram layout engine under-estimates auto-sized box heights, causing
text to overflow the rounded rectangle. Two independent defects compound:
halfwidth characters (~5pt average). Fullwidth characters are 11pt (1em), so Japanese
descriptiontext gets roughly half the lines it needs.density (1px = 0.5pt). On 4:3 (1px = 0.375pt) the same 11pt text occupies 33% more px.
Measured shortfall of the estimated height vs. required height (box width 240px,
title + description,
build_layoutdirect invocation):This was listed as a known limitation in #283 (workaround: specify
box.heightexplicitly). The "~25%" figure there under-stated the impact — line wrapping is
discrete, so the two defects multiply.
Root cause
sdpm/sdpm/engine/layout/placement.py:43-49:The true calibration in pt (verified against the builder): box text is always 11pt
(
layout/model.py), rendered line height 13.2pt (1.2em), padding 20pt, average charwidth 5pt halfwidth / 11pt fullwidth. The px constants are these pt values converted
at the 16:9 rate only. Icon-label metrics (
len(line) * 8, label heights) have thesame burn-in.
The same 16:9 rate is also hardcoded in the knowledge layer: the width guide in
sdpm/references/workflows/slide-json-spec.mdteachesfullwidth = pt × 2 × char count— the «× 2» is 1pt = 2px, true only for 16:9. Agents budget text 33% too optimistically
on 4:3 for every textbox, not just arch diagrams.
Design
ptPerPx— declare the deck's unit relationship inslideSizeSlide JSON mixes two unit systems: layout in px (virtual, width normalized to 1920),
fonts in pt (physical). The exchange rate is
ptPerPx = physical width in pt / 1920,which cannot be derived from the canvas size (4:3 and 16:10 presets share
0.375 despite different aspect ratios — it depends on physical width).
deck.json'sslideSizeis best understood as the spec's coordinate-systemdeclaration (like an SVG
viewBox) — px coordinates are meaningless without it, andgeneratealready validates the declaration against the template. The pt↔px rate ispart of that declaration:
Information flow (same transcription pattern as #208 — the value originates from
tools, not hand-maintained constants):
Layout engine: explicit
pt_per_pxargument (default 0.5)render_architecture→build_layout→_layout_scalegain a keyword argument.Estimates are rewritten as pt-true values converted by
pt_per_px, with per-characterCJK-aware width (fullwidth = 11pt, halfwidth = 5pt). Default
0.5is the exact 16:9value, so all existing callers are unchanged.
Rejected alternatives (recorded for the future):
slide_width_emu: EMU is an infrastructure concept;layout/currentlyimports nothing but stdlib and stays a template-agnostic pure px space
conversion_scaleContextVar: layout → converter wouldinvert the dependency DAG, and implicit global state breaks the pure-function tests
7.5in burn-in (16:10 presets are 6.25in → 17% error)
"pt is physical, px is normalized layout"; arch text would become inconsistent
with surrounding slide text
prone (fonts, autofit, mixed scripts); the accurate detector already exists at
generate/preview time (LibreOffice renders the real pptx — correct for any
aspect ratio since fix: 4:3 など 16:9 以外のカスタムテンプレートで生成すると、レイアウト崩れ・プレビュー見切れが発生する #208's isotropic measure fix). Budgeting belongs to the
knowledge layer, verification to generate — lint stays context-free
Note: 16:9 output changes too (intentional)
This is a bug fix: 16:9 boxes with halfwidth text grow ~5-10%; Japanese descriptions
gain their missing lines. Existing decks re-generate with slightly different arch
layouts.
Also fixed in this change
(
analysisJsonin DynamoDB) stores only{theme_colors, layouts}—slide_sizeis dropped. Cache hits on
analyze_templatetherefore return noslide_size,silently defeating fix: 4:3 など 16:9 以外のカスタムテンプレートで生成すると、レイアウト崩れ・プレビュー見切れが発生する #208's transcription flow for uploaded templates. The cache
will store the full analyzer output.
api.init(template=...)has had zero production callers sincePR 🐛 fix(mcp-local-analyze-template-search): use get_templates_dirs() fo… #152 removed the tool-side passthrough (the MCP
init_presentationtool takesno template; remote init explicitly documents "Template is NOT set at init time").
PR fix: support non-16:9 custom templates (4:3 etc.) and fix two pre-existing live preview issues #283 unknowingly extended this dead branch (slideSize write at init) and added
tests for it. The branch, its tests, and stale doc references will be removed;
slideSizewriters reduce to: agent transcription + pptx import.Out of scope
layout/graph.py(layout_graph) is unreachable code with its own 1920×1080hardcode — separate cleanup issue
gaps) — not text-metric derived
Related
sdpm/sdpm/engine/layout/placement.py,render.py,analyzer/__init__.py,converter/pipeline.py,tools/__init__.py,api/index.py,sdpm/references/workflows/slide-json-spec.md,personas/vibe.md