236 lines
8.8 KiB
Markdown
236 lines
8.8 KiB
Markdown
> See shared-standards.md for common technical constraints.
|
|
|
|
# Image Layout Specification
|
|
|
|
Layout rules for pages where the image is placed **side-by-side with body text** as a container block. Strategist and Executor both follow these rules when the image's narrative intent is *side-by-side*.
|
|
|
|
**Core principle (side-by-side)**: compute container layout from the image's original aspect ratio so the image displays completely — no excess whitespace, no cropping.
|
|
|
|
> **Scope**: this spec applies to *side-by-side* intent only. Other intents (hero / full-bleed, atmosphere / background, accent / inline) use full-bleed placement where ratio alignment is not a constraint and cropping is expected — the ratio→split table below does NOT apply. See `references/strategist.md` §h for intent selection.
|
|
|
|
---
|
|
|
|
## Layout Decision Flow
|
|
|
|
```
|
|
1. Decide narrative intent (hero / atmosphere / side-by-side / accent) — see strategist.md §h
|
|
2. If intent = side-by-side: continue below. Otherwise: compose per narrative; this spec does not apply.
|
|
3. Get image original dimensions → Calculate ratio (width/height)
|
|
4. Select layout type based on ratio
|
|
5. Calculate maximum display size for the image
|
|
6. Allocate remaining space for text area
|
|
7. Fill results into the Design Specification's image resource list
|
|
```
|
|
|
|
**When to run**: if image approach includes "B) User-provided", run the scan and populate the image resource list after the Strategist's Eight Confirmations and before content analysis / outlining.
|
|
|
|
---
|
|
|
|
## Layout Type Selection (side-by-side intent)
|
|
|
|
| Image Ratio | Layout Type | Image Position | Description |
|
|
|-------------|-------------|----------------|-------------|
|
|
| > 2.0 (ultra-wide) | Top-bottom split | Top full-width | Image spans canvas width, height proportional |
|
|
| 1.5-2.0 (wide) | Top-bottom split | Top | Image width = content area width, height proportional |
|
|
| 1.2-1.5 (standard) | Left-right split | Left | Image height-first fit, width proportional |
|
|
| 0.8-1.2 (square) | Left-right split | Left | Image takes content area height, width proportional |
|
|
| < 0.8 (portrait) | Left-right split | Left | Image height = content area height, width proportional |
|
|
|
|
> Boundary ratio (e.g., 1.5): decide by text volume — more text → left-right; less text → top-bottom.
|
|
|
|
---
|
|
|
|
## Dimension Calculation Formulas
|
|
|
|
### Canvas Parameters (All Formats)
|
|
|
|
| Format | Canvas | Margins (L/R, T/B) | Content Area (W x H) | Title Height | Content Start Y |
|
|
|--------|--------|--------------------|-----------------------|-------------|----------------|
|
|
| PPT 16:9 | 1280x720 | 60, 60 | 1160 x 600 | 60px | 80px |
|
|
| PPT 4:3 | 1024x768 | 50, 50 | 924 x 608 | 60px | 70px |
|
|
| Xiaohongshu | 1242x1660 | 60, 80 | 1122 x 1500 | 80px | 100px |
|
|
| WeChat Moments | 1080x1080 | 60, 60 | 960 x 960 | 60px | 80px |
|
|
| Story | 1080x1920 | 60, 120/180 | 960 x 1620 | 80px | 140px |
|
|
| WeChat Article | 900x383 | 40, 40 | 820 x 303 | 40px | 50px |
|
|
|
|
> Below, **W** = content area width, **H** = content area height (excludes title). PPT 16:9 example: W=1160, H=600.
|
|
|
|
### Top-Bottom Layout Calculation
|
|
|
|
```
|
|
Image width = W = 1160 px
|
|
Image height = W / R = 1160 / R px
|
|
Text area height = H - image height - gap(20px)
|
|
|
|
Validation: Text area height >= 150px (at least 3-4 lines of text)
|
|
If not satisfied → Switch to left-right layout
|
|
```
|
|
|
|
### Left-Right Layout Calculation
|
|
|
|
**Method 1 (height-first, suitable for portrait images)**:
|
|
```
|
|
Image height = H = 600 px
|
|
Image width = H x R = 600 x R px
|
|
Text area width = W - image width - gap(20px)
|
|
```
|
|
|
|
**Method 2 (width-constrained, for wide images converted to left-right)**:
|
|
```
|
|
Image width = W x 0.7 = 812 px
|
|
Image height = image width / R
|
|
Text area width = W - image width - gap(20px)
|
|
```
|
|
|
|
**Validation**: Text area width >= 280px; otherwise reduce image area width.
|
|
|
|
---
|
|
|
|
## Layout Examples
|
|
|
|
### Ultra-wide Image (ratio 2.45)
|
|
|
|
```
|
|
Original: 1960x800, R=2.45 → Top-bottom split
|
|
Image: 1160x473, Text area: 1160x147 → 7:3 top-bottom
|
|
```
|
|
|
|
### Standard Landscape (ratio 1.38)
|
|
|
|
```
|
|
Original: 1614x1171, R=1.38 → Left-right split
|
|
Image: 773x560 (left), Text area: 367x560 (right) → 7:3 left-right
|
|
```
|
|
|
|
### Wide Image Edge Case (ratio 1.75)
|
|
|
|
```
|
|
Original: 1820x1040, R=1.75
|
|
Try top-bottom: image height=663, text area=-43 ❌
|
|
Switch to left-right: image 780x446 (left), text area 360x600 (right) → 7:3 left-right
|
|
```
|
|
|
|
---
|
|
|
|
## Portrait Canvas Override
|
|
|
|
Default selection table assumes **landscape or square canvas**. For portrait canvases (height > width), left-right splits leave both columns too narrow — use the override below.
|
|
|
|
| Canvas Orientation | Image Ratio | Recommended Layout | Reason |
|
|
|-------------------|-------------|-------------------|--------|
|
|
| Portrait (Xiaohongshu, Story) | > 1.5 (wide) | Top-bottom | Same as landscape canvas |
|
|
| Portrait (Xiaohongshu, Story) | 1.2-1.5 (standard) | Top-bottom | Left-right too narrow on tall canvas |
|
|
| Portrait (Xiaohongshu, Story) | 0.8-1.2 (square) | Top-bottom | Image fits well in top half |
|
|
| Portrait (Xiaohongshu, Story) | 0.5-0.8 (portrait) | Left-right | Portrait image on tall canvas works |
|
|
| Portrait (Xiaohongshu, Story) | < 0.5 (extreme portrait) | Left-right | Image takes one side, text the other |
|
|
|
|
> Square canvases (WeChat Moments 1:1): use the standard landscape rules.
|
|
|
|
---
|
|
|
|
## Multi-Image Layout
|
|
|
|
For slides with multiple images, divide the content area evenly using the formulas below.
|
|
|
|
### Grid Formulas
|
|
|
|
```
|
|
columns = number of columns
|
|
rows = number of rows
|
|
gap = 20px (PPT formats) or 30px (social formats)
|
|
|
|
cell_width = (W - (columns - 1) * gap) / columns
|
|
cell_height = (H - (rows - 1) * gap) / rows
|
|
```
|
|
|
|
### Common Patterns
|
|
|
|
| Image Count | Layout | Grid | Description |
|
|
|-------------|--------|------|-------------|
|
|
| 2 (both landscape) | Side-by-side | 2x1 | Two equal columns |
|
|
| 2 (both portrait) | Stacked | 1x2 | Two equal rows |
|
|
| 2 (mixed) | 1 large + 1 small | Custom | Landscape top (full-width), portrait right-bottom |
|
|
| 3 | 1 large + 2 small | 1+2 | Left large (50% width), right column with 2 stacked |
|
|
| 4 | Grid | 2x2 | Equal-sized cells |
|
|
|
|
### Example: 2x2 Grid on PPT 16:9
|
|
|
|
```
|
|
W=1160, H=600, gap=20
|
|
cell_width = (1160 - 20) / 2 = 570
|
|
cell_height = (600 - 20) / 2 = 290
|
|
|
|
Image positions:
|
|
(60, 80) 570x290 (650, 80) 570x290
|
|
(60, 390) 570x290 (650, 390) 570x290
|
|
```
|
|
|
|
> Multi-image slides: use `preserveAspectRatio="xMidYMid meet"` on all images for consistent in-cell display.
|
|
|
|
---
|
|
|
|
## Prohibited Practices
|
|
|
|
| Prohibited | Correct Approach |
|
|
|-----------|-----------------|
|
|
| Fixed 50:50 or arbitrary ratios | Dynamic calculation based on image ratio |
|
|
| Forcing wide image into square container | Use top-bottom layout or increase image area width |
|
|
| Placing portrait image in narrow horizontal strip | Use left-right layout, image on left |
|
|
| Image whitespace exceeding 10% | Recalculate layout or choose alternative approach |
|
|
| Cropping key image content | Use `preserveAspectRatio="xMidYMid meet"` |
|
|
| Text area too small to read | Ensure text area >= 150px (top-bottom) or >= 280px (left-right) |
|
|
|
|
---
|
|
|
|
## Handoff Fields
|
|
|
|
This spec only defines layout calculation. Write computed fields into the Image Resource List defined in [`svg-image-embedding.md`](svg-image-embedding.md):
|
|
|
|
| Field | Meaning |
|
|
|-------|---------|
|
|
| `Ratio` | Original image width / height |
|
|
| `Layout plan` | Top-bottom / left-right / grid, including split ratio when relevant |
|
|
| `Image area` | Computed display rectangle size |
|
|
| `Text area` | Computed remaining text area size |
|
|
|
|
For SVG `<image>` syntax, path rules, `preserveAspectRatio`, external refs, and Base64 embedding: see [`svg-image-embedding.md`](svg-image-embedding.md).
|
|
|
|
### SVG Image Embedding Examples
|
|
|
|
Complete display (data charts, side-by-side — must not crop):
|
|
|
|
```xml
|
|
<image href="../images/xxx.png"
|
|
x="60" y="80" width="780" height="446"
|
|
preserveAspectRatio="xMidYMid meet"/>
|
|
```
|
|
|
|
Crop-to-fill (backgrounds and hero images only):
|
|
|
|
```xml
|
|
<image href="../images/bg.png"
|
|
x="0" y="0" width="1280" height="720"
|
|
preserveAspectRatio="xMidYMid slice"/>
|
|
```
|
|
|
|
---
|
|
|
|
## Automation Tool
|
|
|
|
```bash
|
|
python3 scripts/analyze_images.py <project_path>/images # Default: PPT 16:9
|
|
python3 scripts/analyze_images.py <project_path>/images --canvas ppt43 # PPT 4:3
|
|
python3 scripts/analyze_images.py <project_path>/images --canvas xiaohongshu # Xiaohongshu
|
|
```
|
|
|
|
`--canvas` selects target format (default `ppt169`). The tool computes layout type (top-bottom / left-right), image display area, and text area per the formulas above. Output is a Markdown table — paste directly into the image resource list.
|
|
|
|
---
|
|
|
|
## Role Responsibilities
|
|
|
|
| Role | Responsibility |
|
|
|------|---------------|
|
|
| **Strategist** | Run analyze_images.py, calculate layout per this spec, populate image resource list |
|
|
| **Executor** | Strictly follow the layout plan and dimensions in the image resource list when generating SVGs |
|