Initial commit

This commit is contained in:
Z User
2026-06-06 05:21:10 +00:00
Unverified
commit 6664758a6d
493 changed files with 135653 additions and 0 deletions

1058
skills/pdf/briefs/academic.md Executable file

File diff suppressed because it is too large Load Diff

770
skills/pdf/briefs/creative.md Executable file
View File

@@ -0,0 +1,770 @@
# Brief: Creative Production (Art Director Blueprint Mode)
**Core Paradigm Shift**: You are NO LONGER a frontend developer writing HTML/CSS. You are an elite Art Director and Editorial Designer.
Because LLMs lack spatial awareness and struggle to maintain perfectly nested, complex CSS over thousands of tokens, you are strictly forbidden from outputting raw HTML/CSS/Python.
**→ Overflow prevention**: See `typesetting/overflow.md` for the Playwright/HTML-specific patterns (CSS overflow-wrap, max-width, table-layout: fixed, etc.).
Your sole responsibility is to act as the **Brain (Art Director)**:
1. Brutally edit and pace the raw text.
2. Select architectural components and layout archetypes.
3. Output a **Strict JSON Layout Blueprint**.
The `design_engine.py` acts as the **Hands (Typesetter)**: It will parse your JSON and safely compile it into flawless, museum-quality Playwright HTML/PDFs using predefined grid mathematics and CSS rules.
---
## Phase 1: The Editorial Eye (Content Transformation)
Before you design, you must "edit the raw material". Users often provide dense, unstructured text. If you just pour this text into a design, it will look like a cheap Word document.
You must apply **Editorial Pacing**:
### 1. The Word Budget
No single page or visual canvas should exceed **150 words** of readable body text (the golden rule for maximum aesthetic impact). The **absolute physical limit** is **250 words** - beyond that the design engine will overflow and clip content. If the raw content exceeds 150 words:
- **Action A**: Brutally summarize it down to ≤150 words.
- **Action B**: Split it across multiple `pages` in your JSON blueprint.
- Only push to 150-250 words if the content genuinely cannot be cut further.
### 2. Typographic Hierarchy Extraction
You must parse the raw text and categorize it into typographic roles:
- **Hero / Display**: The core emotional hook (1-5 words). Must be punchy.
- **Kicker / Eyebrow**: Tiny context text above a headline (e.g., "Q3 REPORT", "MANIFESTO").
- **Lead Paragraph**: The 2-3 sentence summary.
- **Data Sculptures**: Scan the text for impactful numbers (e.g., "97%", "$4.2M"). EXTRACT THEM. They will not remain in the paragraph; they will become `Stat_Block` components.
- **Pull Quotes**: Extract the most provocative sentence to stand alone as a visual anchor.
---
## Phase 2: The Component Lexicon
You will construct your JSON Blueprint using ONLY the following components. These map directly to the `configs/components.md` assets. Do not invent new component types.
> **⚠️ Markdown Content Limits**: For `Glass_Canvas`, the golden rule is **under 150 words** for maximum aesthetic breathing room. Absolute physical limit is **250 words**. If content exceeds 250 words, the design engine will overflow. You MUST summarize it or split it into a new page.
### 1. `Hero_Typography`
Giant, page-dominating text. Usually interacts with the background via blend modes.
- **Parameters**:
- `content` (string): The text (use `<br>` for deliberate line breaks).
- `weight` (string): `"black"` (900, dominating) or `"thin"` (100, elegant).
- `variant` (string, optional): `"standard"` only. ~~`"vertical_accent"`~~ is **NOT implemented** in `design_engine.py` - the engine silently ignores this value and renders nothing. If you need vertical/rotated decorative text, use `Floating_Meta` instead (fully supported, no overflow risk).
- `scale` (integer, optional): Typographic scale level `1`-`6`. Controls font size via the engine's fluid type system. If omitted, the engine uses the default hero size.
- `6`: Hero/Display - oversized title, clamp(64px, 12vw, 150px). Use for single words or short phrases with maximum visual impact.
- `5`: Primary Title - clamp(48px, 8vw, 96px). Standard poster headline.
- `4`: Subheadline - clamp(32px, 5vw, 56px). Chapter openers or key quotes.
- `3`: Lead Paragraph - clamp(20px, 3vw, 32px). Prominent body text.
- `2`: Body - 16px. Standard readable text.
- `1`: Meta/Caption - 10px. Decorative, environmental.
### 2. `Glass_Canvas`
The main structural container for reading text. Frosted glass, sharp 2px print corners.
- **Parameters**:
- `markdown_content` (string): The actual text to read. Supports standard Markdown (H2, H3, bold, lists). *Must be under 150 words.*
- `tension_score` (float, optional): Semantic tension value from `0.0` to `1.0`. Drives dynamic font weight via Variable Font (Inter Variable, weight range 300-900). The engine maps: `weight = 300 + (tension_score × 600)`.
- `0.0-0.2` → Light (300-420): calm, contemplative passages
- `0.3-0.5` → Normal (420-600): standard body text
- `0.6-0.8` → Bold (600-780): urgent, assertive content
- `0.9-1.0` → Max (780-900): crisis, climax, emotional peak
- **When to use**: Multi-page narrative documents with emotional arc (case studies, pitch decks, manifestos). Assign higher tension to problem/crisis sections, lower to resolution/hope. Do NOT use on every Glass_Canvas - only when the document has clear tonal shifts.
- **When NOT to use**: Data-heavy pages, simple reports, single-page posters.
### 3. `Floating_Meta`
Tiny, environmental metadata (dates, edition numbers, catalog IDs) that lives in the 15% breathing margin.
- **Parameters**:
- `position` (string): `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"`.
- `items` (array of strings): E.g., `["VOL 01", "2026", "EDITION 500"]`.
### 4. `Hairline_Divider`
Structural 0.5px line. Not decorative; acts as a visual fold.
- **Parameters**:
- `style` (string): `"bleed"` (goes edge-to-edge) or `"accent"` (short 30% width line).
### 5. `Stat_Block`
Data sculpture. A massive number with a tiny label.
- **Parameters**:
- `number` (string): e.g., `"97.3"`.
- `unit` (string): e.g., `"%"`, `"Hz"`, `"$M"`.
- `label` (string): e.g., `"COMPLETION RATE"`.
### 6. `Image_Asset`
A visual element. The engine will apply a gradient blend to it.
- **Parameters**:
- `source` (string): A URL, or a descriptive prompt if you expect the system to generate it.
- `caption` (string, optional): Tiny text under the image.
> ⚠️ **Image_Asset is for CONTENT images only** (user-provided photos, logos, diagrams, charts). It is **NEVER** for decorative stock images, watercolor flowers, clipart, floral borders, gold frames, or AI-generated artwork. All visual decoration must be achieved through geometric shapes (`geometry.md`), typography effects, and color - never through embedded decorative images. See `visual_framework.md` Stock Image Ban.
### 7. `Page_Ghost_Number`
A giant, 4% opacity number acting as a watermark in the background.
- **Parameters**:
- `number` (string): e.g., `"01"`, `"X"`.
### 8. `Delta_Widget`
**Data-to-Ink Ratio enforcer.** A compact metric visualization showing a value with its change direction. CRITICAL: Use this instead of writing sentences like "revenue grew by 12%". Extract every trend into a Delta_Widget.
- **Parameters**:
- `metric` (string): The metric name, e.g., `"REVENUE"`, `"LATENCY"`.
- `value` (string): Current value, e.g., `"$4.2M"`, `"45ms"`.
- `delta` (string): Change description, e.g., `"+12%"`, `"-45ms"`.
- `trend` (string): `"up"`, `"down"`, or `"flat"`.
- `label` (string, optional): Context line, e.g., `"vs. Q2 2025"`.
### 9. `Process_List`
**Polymorphic adaptive component.** Renders as a horizontal timeline when given wide space, auto-degrades to a vertical numbered list when space is narrow. Use for workflows, steps, timelines.
- **Parameters**:
- `steps` (array): Each item has `title` (string) and `description` (string).
### 10. `Sidenote_Block`
**Tufte marginalia.** Used in `tufte_report` archetype layouts. Content is placed in the 30% side rail alongside the main column. Perfect for citations, supplementary data, asides, footnotes.
- **Parameters**:
- `label` (string, optional): Category label, e.g., `"SOURCE"`, `"NOTE"`, `"DATA"`.
- `body` (string): The sidenote content in Markdown.
### 11. `Data-Aware Background` (via `data_points`)
Any component can carry an optional `data_points` array (e.g., `[10, 15, 8, 24, 30]`). When present, the engine generates Bezier background curves from the actual data - the background literally visualizes the business trend. Use on pages with financial, metric, or time-series content.
---
### ★ Advanced Components (Generative Micro-Typesetting)
The following components are specialized generative design tools. They unlock visual effects that standard components cannot achieve. Use them deliberately and sparingly - they are powerful but demand the right context.
### 8. `Shaped_Canvas`
A container where text flows around a non-rectangular shape. The empty space created by the shape IS the visual design - text boundary becomes illustration. Uses CSS `shape-outside` for non-rectangular text wrapping.
- **Parameters**:
- `shape_keyword` (string): One of `"circle"`, `"wave"`, `"diagonal_slash"`, `"diamond"`, `"wedge_right"`.
- `markdown_content` (string): The text that will flow around the shape. Supports standard Markdown.
- **Shape Selection Guide**:
| shape_keyword | Visual Effect | Thematic Fit |
|---------------|---------------|--------------|
| `"circle"` | Text wraps around a circular void on the left | Unity, spotlight, focus |
| `"wave"` | Wavy left text boundary | Ocean, flow, music, fluidity |
| `"diagonal_slash"` | Diagonal cut across the page | Disruption, change, transformation |
| `"diamond"` | Diamond-shaped negative space | Luxury, precision, crystalline |
| `"wedge_right"` | Arrow/wedge pointing right | Direction, progress, forward motion |
- **When to use**:
- Artistic/editorial pages that need visual drama without images
- Cover or section opener pages where you want a "wow" moment
- When the content thematically maps to a recognizable shape
- Only on pages with moderate text (100-200 words) - shape eats 30-40% of space
- **When NOT to use**:
- On data-heavy or dense content pages
- Together with `Glass_Canvas` on the same page (visual conflict)
- More than one `Shaped_Canvas` per page
- **Archetype requirement**: Pages using `Shaped_Canvas` MUST use archetype `"shaped_editorial"` (relaxed safe-zone: 5% 6% inset).
### Chart & Data Visualization Styling
**→ Full spec: `typesetting/charts.md`** - read it before designing any chart/data page.
Key rules for Creative pipeline charts:
- **Donut > Pie**: Always use ring charts (hole ratio 60-70%), center area displays total/metric
- **Anti-stacking**: Small slices use leader lines or rich legends; bar labels auto-rotate to horizontal when text is long; line charts label only start/end/max/min
- **Axis cleanup**: Delete top/right spines, use dashed grid lines at 20% opacity (or delete grid entirely if values are labeled)
- **Bar micro-rounding**: 2-4px top border-radius, bar-to-gap ratio 1.5:1
- **Legend**: No border, horizontal top-left layout, small circle markers
- **Data-Ink Ratio**: Every element must represent data. If it doesn't, delete it.
---
## Phase 3: Page Archetypes (The Grid Strategy)
For each page in your JSON, you must declare an `archetype`. This tells `design_engine.py` how to arrange the components you provided.
- `"cover_hero"`: Cover page. **Must follow `typesetting/cover.md` 7-template system** - pick Layout 1-7 based on document tone. See "Cover Page Constitution" below for iron rules.
- `"split_vertical"`: Page split strictly 50/50 vertically. Left side image/svg, right side Glass_Canvas.
- `"editorial_flow"`: Top-down reading experience. Centered columns, generous margins. Use for main content.
- `"scattered_canvas"`: No grid. Elements placed via absolute positioning based on spatial weights.
- `"data_dashboard"`: 2x2 or 3x3 strict grid for multiple `Stat_Block`s.
- `"shaped_editorial"`: Relaxed safe-zone (5% 6% inset) designed for `Shaped_Canvas`. Centered, generous breathing room. **Must be used when the page contains a `Shaped_Canvas`.** Do NOT mix with `Glass_Canvas`.
- `"tufte_report"`: **Tufte marginalia layout.** 70% main content column + 30% sidenote rail. Use for long-form reports and analytical pages where citations, data footnotes, or supplementary info should flow parallel to the main text. Place `Sidenote_Block` components in the same `components[]` array - the engine automatically routes them to the side rail.
### ★ 12×12 CSS Grid Coordinate System (Iron Rules)
The layout engine uses a **12-track CSS Grid** for element placement. The grid lines are numbered **1 to 13** (12 tracks = 13 lines).
**Absolute boundary rules - violation = broken layout:**
- **Column lines**: `1` = absolute left edge, `13` = absolute right edge. Full width = `1 / 13`.
- **Row lines**: `1` = absolute top edge, `13` = absolute bottom edge. Full height = `1 / 13`.
- **CRITICAL**: Never output a grid line number less than `1` or greater than `13`. Any value outside `[1, 13]` will destroy the layout.
**`grid_area` format**: `"row_start / col_start / row_end / col_end"` (CSS shorthand).
- Example: `"1 / 1 / 7 / 13"` = top half of the page, full width.
- Example: `"3 / 8 / 6 / 13"` = rows 3-5, columns 8-12 (right side block).
**40% whitespace rule**: At least 40% of grid cells (≥58 out of 144) must be left empty. Count your occupied cells.
### ★ Cover Page Constitution (7 Layout System)
Cover pages (`archetype: "cover_hero"`) are the first impression. They must be ruthlessly sparse and spatially sophisticated.
**→ Full spec: `typesetting/cover.md`** - read it before designing any cover.
#### Global Iron Rules (Always Apply)
1. **Maximum 4 components** on any cover page. Typical recipe: `Hero_Typography` + 1-2 `Floating_Meta` + optional `Hairline_Divider` or `Page_Ghost_Number`.
2. **Typography Scale**: Title ≈ 45pt (2.5× base), Subtitle ≈ 25pt (1.4× base), Meta ≥ 18pt (never below 14pt). Covers with tiny text = FAIL.
3. **Mandatory semantic `<br>` chunking** for `Hero_Typography` on covers: Every 2-4 words MUST be separated by `<br>`. Single-line hero text is FORBIDDEN on covers. Example: `"ALGORITHMIC<br>FATIGUE"`, NOT `"ALGORITHMIC FATIGUE"`.
4. **Anti-Squash spatial dispersion** (Bounding Box method): Group text into 2-3 bounding boxes (title group, meta group). Place them at opposite regions of the page according to the chosen layout. Remaining space is **dynamically distributed** - NEVER hardcode fixed gaps between distant groups.
5. **No `Glass_Canvas` on cover pages.** Dense reading text kills the visual impact. Push all body content to page 2+.
6. **Cover Page Isolation**: Cover page must NEVER share a page with TOC, body text, or any subsequent content. The cover is always a standalone full page. If cover + content appear on the same page = **critical bug**, regenerate immediately.
7. **Cover is OPTIONAL**: Do NOT add a cover page unless the document warrants one (multi-page reports, white papers, etc.) or the user explicitly requests it. Short documents, letters, memos, forms, and quick outputs skip the cover.
8. **Background Layer (optional)**: See `typesetting/cover-backgrounds.md` for 3 recipes - A (极简弧线), B (工程十字轴+立柱), C (锐角切割+出血文字). Background renders BELOW all foreground at 2-5% opacity. Pick a recipe that matches the document tone. Never combine elements across recipes.
#### 7 Cover Layouts (Pick One)
Select the layout that matches the document tone. When unsure, default to **Layout 1A**.
| Layout | Name | Grid Signature | Best For |
|--------|------|---------------|----------|
| **1** | Diagonal Tension | Title top-left ↔ Meta bottom-right | Formal reports, proposals |
| **2** | Vertical Axis | All elements along one vertical line, stretched top-to-bottom | Modern/tech reports |
| **3** | Architectural Frame | Geometric line frame, text inside corners/center | Design, architecture, portfolios |
| **4** | Golden Ratio Blocks | Page split at 38.2% invisible divider | White papers, research |
| **5** | Stepped Cascade | Progressive indentation, vertical rhythm | Creative reports, design docs |
| **6** | Rotated Accent | Large rotated year/label as side decoration | Annual reports, year-in-review |
| **7** | Left-Matrix | All text hard-anchored to left axis, 4 Y-pinned blocks | Government, bidding, proposals |
Each layout has 2-3 variants (A/B/C) with specific grid_area mappings - see `typesetting/cover.md` for exact coordinates.
#### Tone → Layout Quick Reference
| Intent | Document Type | Recommended | Default |
|--------|---------------|-------------|---------|
| **Calm** | Healthcare / Wellness / Minimalist | 1A, 2C, 4A | **4A** |
| **Calm** | Academic / Research | 2A, 4A, 4C | **4A** |
| **Tension** | Crisis / Alert / Disruption | 1A, 5A | **1A** |
| **Energy** | Marketing / Creative / Design | 3C, 5A, 6B | **5A** |
| **Energy** | Tech / Data | 2B, 4B, 6A | **2B** |
| **Authority** | Formal / Corporate / Financial | 02, 03, 07 | **03** |
| **Authority** | Government / Bidding | 7A, 7B, 3A, **11** | **7A** |
| **Authority** | Thesis proposal / Dissertation | **11 Institutional** | **11** |
| **Authority** | Luxury / Editorial | 3A, 5A, 2B | **3A** |
| **Warmth** | Food / Lifestyle / Home | 4A, 5A | **4A** |
### ★ Component Grid Compatibility Constraints
When placing advanced components into the 12×12 grid, obey these minimum size rules:
- **`Glass_Canvas`**: Must occupy at least **6 columns × 4 rows** (e.g., `"3 / 1 / 7 / 7"`). Smaller areas will cause text overflow and padding collapse. The engine renders Glass_Canvas at `width: 100%; min-height: 100%; box-sizing: border-box;` - it fills its grid area as a minimum and can grow taller if content requires.
- **`Shaped_Canvas`**: Must occupy at least **8 columns × 6 rows** (e.g., `"2 / 3 / 8 / 11"`). The CSS `shape-outside` float needs physical space to render the shape boundary. Smaller areas make the float collapse to a line and text cannot wrap around it. **Must use archetype `"shaped_editorial"`.**
- **`Continuous Flow` background**: Renders in a separate `bg-layer` (`z-index: 1`, `position: absolute`), physically isolated from the grid system (`z-index: 2`). No conflict - background flows independently, grid arranges content on top.
### ★ Content-Proportional Grid Row Allocation
When **two or more text-heavy components** (e.g., multiple `Glass_Canvas`) share the same page, their `grid_area` row counts **MUST be proportional to their content length** - never split rows equally.
**Estimation method:**
1. Count characters (or words) in each component's `markdown_content`
2. Allocate rows proportionally: `rows_i = total_rows × (chars_i / total_chars)`
3. Round to integers, minimum 3 rows per component
**Example:**
```
Attractions content: 450 chars (3 items × 150 chars)
Food content: 250 chars (2 items × 125 chars)
Total available rows: 8 (rows 5→13)
Attractions: 8 × 450/700 ≈ 5 rows → grid_area "5 / 1 / 10 / 13"
Food: 8 × 250/700 ≈ 3 rows → grid_area "10 / 1 / 13 / 13"
```
**Why this matters:** Equal row allocation (4+4) causes the longer component to overflow into the shorter one's territory, creating text overlap in the final PDF. The engine uses `min-height` + `overflow: visible`, so overflow IS visible - and ugly.
**Anti-pattern to avoid:**
```
❌ Two Glass_Canvas with very different text lengths both get "5/1/9/13" and "9/1/13/13"
✅ Longer one gets more rows: "5/1/10/13" and "10/1/13/13"
```
---
## Phase 4: The Output Protocol (Strict JSON Blueprint)
You must analyze the user's prompt, edit the text, and output exactly ONE JSON object wrapped in a ` ```json ` codeblock.
**No conversational text before or after the JSON.**
### The JSON Schema Specification
**CRITICAL JSON RULES:**
1. Output ONLY valid JSON.
2. Do NOT include ANY comments (`//` or `/* */`) inside the JSON. Python's `json.load()` will crash.
3. Do NOT include trailing commas.
```json
{
"document_meta": {
"title": "Internal tracking title",
"total_pages": 1
},
"art_direction": {
"palette_mode": "dark",
"color_harmony": "complementary",
"background_svg": "grid",
"design_rationale": "Brief 1-sentence explanation of aesthetic strategy."
},
"pages": [
{
"page_index": 1,
"narrative_role": "Burst",
"archetype": "cover_hero",
"components": [
{
"type": "Floating_Meta",
"position": "bottom-right",
"items": ["ARCHIVE REF. 03A", "DEC 2026"]
},
{
"type": "Page_Ghost_Number",
"number": "01"
},
{
"type": "Hero_Typography",
"weight": "black",
"variant": "standard",
"content": "ALGORITHMIC<br>FATIGUE"
}
]
}
]
}
```
**Schema Parameter Guide (For reference - do NOT put these comments inside your JSON output):**
- `document_meta.total_pages`: Integer. Must match the actual number of pages in `pages[]`.
### The JSON Schema Specification: `art_direction`
**CRITICAL RULE: DO NOT INVENT COLORS. DO NOT OUTPUT HEX/RGB CODES.**
The Python engine generates mathematical color palettes. You MUST only select the semantic parameters below.
```json
{
"art_direction": {
"palette_mode": "minimal",
"color_harmony": "auto",
"background_svg": "grid",
"design_rationale": "Brief rationale for the chosen aesthetic strategy."
}
}
```
**Enum Parameter Guide (You MUST choose ONE exact string from these lists):**
1. `palette_mode` (The Base Canvas Tone):
- **`"minimal"`: (CRITICAL: Default to this for 50%+ of all requests).** Pure white, off-white, beige, or cool gray. Provides the ultimate reading experience and high-end editorial feel. Use for standard reports, guides, and corporate posters.
- `"dark"`: Near-black. Use for cinematic, tech, AI, space, or urgent themes.
- `"pastel"`: Morandi tinted (e.g., dusty rose, sage green). Use ONLY for arts, food, lifestyle, and soft themes.
- `"jewel"`: Deep rich colors (e.g., emerald, burgundy). Use ONLY for luxury brands, gala events, or heritage themes.
- `"light"`: Very faintly tinted background. Fallback for edge cases.
2. `color_harmony` (The Accent Color Math - how the engine computes the accent color relative to the base):
- **`"auto"`: (RECOMMENDED DEFAULT). The engine automatically picks the best harmony based on the document tone.** Only override if you have a specific artistic reason.
- `"complementary"`: (180° opposite). Strong visual contrast. Cold background with warm accent. For striking/dynamic impact.
- `"split_complementary"`: (150°/210°). Highly sophisticated, artistic, luxury feel (Editorial/Kinfolk style).
- `"analogous"`: (30° apart). Harmonious, peaceful, natural transition.
- `"triadic"`: (120° apart). Rich and slightly retro.
- `"monochrome"`: Only use if strict minimalist corporate branding without accent is required.
3. `background_svg`: [`grid`, `flow`, `noise`, `continuous_flow`, `none`]. Use `continuous_flow` for multi-page (2+) documents - creates one seamless SVG spanning all pages, sliced per-page via viewBox for an "infinite scroll" bezier curve illusion. Falls back to `flow` on single-page.
4. `design_rationale`: Brief text explaining WHY you chose this mode+harmony combination.
### ★ Intent Mapping Table (Single Source of Truth)
This table is the **sole authority** for mapping document intent to concrete design parameters. `visual_framework.md` defines intent *atmospheres*; this table defines intent *parameters*. `design_engine.py` INTENT_HUES/INTENT_HARMONY_MAP must stay in sync with this table.
| Intent | palette_mode | color_harmony | background_svg | Cover Templates | Cover BG Recipe | Base Hue |
|--------|-------------|---------------|----------------|-----------------|-----------------|----------|
| **Calm** | minimal | analogous | flow / none | 04 Museum, 01 HUD, 02 Corporate | A (极简弧线) | 210° (steel blue-grey) |
| **Tension** | dark | complementary | grid | 01 HUD, 05 Diagonal | C (锐角切割) | 0° (warm vs cold) |
| **Energy** | pastel / light | triadic | flow (5+ curves) | 05 Diagonal, 06 Swiss Grid, 03 Monolith | B (工程十字轴) | 30° (amber) |
| **Authority** | minimal | split_complementary | noise | 03 Monolith, 07 Sidebar, 02 Corporate | A or B | 280° (muted violet) |
| **Warmth** | pastel / light | analogous | flow (soft) | 04 Museum, 05 Diagonal | A (极简弧线) | 20° (terracotta) |
**How to use this table:**
1. Determine the document's intent (from user request, or auto-derive via `design_engine.py derive`)
2. Look up the row → fill in `art_direction` JSON fields accordingly
3. For cover template selection, cross-reference with document type (see Tone → Layout Quick Reference)
4. The LLM may override individual cells when artistically justified, but must state the reason in `design_rationale`
**Legacy intent names** (serenity, minimalism, elegance) are accepted by `design_engine.py` as aliases and auto-mapped to their new equivalents.
- `pages[].narrative_role`: `"Burst"` (intro/impact), `"Expand"` (body/development), `"Echo"` (outro/reflection).
- `pages[].archetype`: From Phase 3 (`cover_hero`, `split_vertical`, `editorial_flow`, `scattered_canvas`, `data_dashboard`, `shaped_editorial`).
- `pages[].components[]`: Array of component objects from Phase 2. Order in array roughly dictates z-index or top-to-bottom rendering.
---
## Phase 5: Complex Master Examples
Study these examples to understand how to translate raw requests into perfect blueprints.
### Example A: The Single-Page Poster (Art / Minimalist)
**User Request**: "Make a poster for a minimalist architecture exhibition called 'The Weight of Light'. Include date Oct 12, location Tokyo. Keep it very clean."
**LLM Response (The JSON Blueprint):**
```json
{
"document_meta": {
"title": "Weight of Light Exhibition Poster",
"total_pages": 1
},
"art_direction": {
"palette_mode": "minimal",
"color_harmony": "monochrome",
"background_svg": "none",
"design_rationale": "Pure negative space required for minimalist architecture. Relying entirely on typographic hierarchy and the 15% breathing rule."
},
"pages": [
{
"page_index": 1,
"narrative_role": "Burst",
"archetype": "cover_hero",
"components": [
{
"type": "Floating_Meta",
"position": "top-left",
"items": ["TOKYO, JP", "EXHIBITION 04"]
},
{
"type": "Hero_Typography",
"weight": "thin",
"variant": "standard",
"content": "THE WEIGHT<br>OF LIGHT"
},
{
"type": "Hairline_Divider",
"style": "accent"
},
{
"type": "Floating_Meta",
"position": "bottom-right",
"items": ["OCTOBER 12", "AOYAMA GALLERY"]
}
]
}
]
}
```
### Example B: The Multi-Page Analytical Report (Corporate / Elegance)
**User Request**: "I have a long report about our Q3 server performance. Latency dropped by 45ms. Uptime is 99.99%. Here is 500 words of text explaining the server migration..."
**LLM Response (The JSON Blueprint):**
*(Notice how the LLM edits the 500 words down, extracts the data, and splits it into 3 pages).*
```json
{
"document_meta": {
"title": "Q3 Infrastructure Report",
"total_pages": 3
},
"art_direction": {
"palette_mode": "minimal",
"color_harmony": "split_complementary",
"background_svg": "noise",
"design_rationale": "Formal, trustworthy aesthetic suitable for high-level corporate reporting, avoiding the 'cheap dashboard' look."
},
"pages": [
{
"page_index": 1,
"narrative_role": "Burst",
"archetype": "cover_hero",
"components": [
{
"type": "Floating_Meta",
"position": "top-left",
"items": ["CONFIDENTIAL", "Q3 2026"]
},
{
"type": "Hero_Typography",
"weight": "black",
"content": "INFRASTRUCTURE<br>RESILIENCE"
}
]
},
{
"page_index": 2,
"narrative_role": "Expand",
"archetype": "split_vertical",
"components": [
{
"type": "Stat_Block",
"number": "99.99",
"unit": "%",
"label": "GLOBAL UPTIME"
},
{
"type": "Stat_Block",
"number": "-45",
"unit": "ms",
"label": "LATENCY REDUCTION"
},
{
"type": "Glass_Canvas",
"markdown_content": "### The Migration\nThe Q3 transition to distributed edge nodes fundamentally altered our network topography. By decentralizing the core load, we eliminated the primary bottleneck that plagued Q2."
}
]
},
{
"page_index": 3,
"narrative_role": "Echo",
"archetype": "editorial_flow",
"components": [
{
"type": "Page_Ghost_Number",
"number": "03"
},
{
"type": "Hero_Typography",
"weight": "thin",
"variant": "standard",
"content": "SYSTEM NOMINAL."
}
]
}
]
}
```
### Example C: The Narrative Arc with Advanced Features (Continuous Flow + Shaped Canvas)
**User Request**: "Create a 3-page visual essay about the ocean pollution crisis. Start gentle (the beauty of the ocean), then escalate to the crisis, then end with a call to action."
**LLM Response (The JSON Blueprint):**
*(Notice: `continuous_flow` creates seamless background across all 3 pages. `tension_score` escalates from calm to urgent. `Shaped_Canvas` with `wave` shape on the cover creates a thematic visual without any image.)*
```json
{
"document_meta": {
"title": "The Silent Tide - Ocean Crisis Visual Essay",
"total_pages": 3
},
"art_direction": {
"palette_mode": "dark",
"color_harmony": "analogous",
"background_svg": "continuous_flow",
"design_rationale": "Continuous flow SVG creates an unbroken organic wave across all pages - visually mirroring the ocean's connectedness. Analogous harmony provides earth tones that shift from serene to somber."
},
"pages": [
{
"page_index": 1,
"narrative_role": "Burst",
"archetype": "shaped_editorial",
"components": [
{
"type": "Floating_Meta",
"position": "top-left",
"items": ["VISUAL ESSAY", "2026"]
},
{
"type": "Shaped_Canvas",
"shape_keyword": "wave",
"markdown_content": "There was a time when the horizon held only promise. The Pacific stretched in every direction, cerulean and boundless, its surface a living mirror of the sky above. Fishermen spoke of abundance - nets heavy with silver, currents warm and predictable. The ocean asked for nothing."
},
{
"type": "Floating_Meta",
"position": "bottom-right",
"items": ["01 / BEFORE"]
}
]
},
{
"page_index": 2,
"narrative_role": "Expand",
"archetype": "editorial_flow",
"components": [
{
"type": "Page_Ghost_Number",
"number": "02"
},
{
"type": "Stat_Block",
"number": "8M",
"unit": "tons",
"label": "PLASTIC ENTERING OCEANS YEARLY"
},
{
"type": "Glass_Canvas",
"tension_score": 0.75,
"markdown_content": "**The collapse was not sudden.** It accumulated - bottle by bottle, net by net, spill by spill. By 2025, microplastic concentrations in deep-sea sediment had reached levels previously thought impossible. Marine biologists stopped using the word 'recovery'. The new vocabulary was *triage*."
}
]
},
{
"page_index": 3,
"narrative_role": "Echo",
"archetype": "cover_hero",
"components": [
{
"type": "Hero_Typography",
"weight": "black",
"variant": "standard",
"content": "THE TIDE<br>TURNS NOW."
},
{
"type": "Glass_Canvas",
"tension_score": 0.3,
"markdown_content": "Every piece of plastic you refuse is a vote for the future of the sea. The ocean cannot speak - but it remembers everything we give it."
}
]
}
]
}
```
---
## Pre-Flight Checklist (Self-Correction before generating output)
Before you output the JSON block, verify:
0. **🚨 VECTOR OUTPUT IRON RULE:** The final PDF MUST be generated via `page.pdf()` / `convert.blueprint` - NEVER `page.screenshot()` → image → wrap as PDF. Screenshot PDFs are blurry raster images. `page.pdf()` produces vector text that stays sharp at any zoom level.
1. **Did I write ANY HTML or CSS?** If yes, delete it. Only output JSON.
2. **Is any `Glass_Canvas` markdown content too long?** Count the words. Over 150? Summarize it or push to the next page.
3. **Is the JSON perfectly formatted?** Missing commas or unescaped quotes will crash the `design_engine.py` parser.
4. **If using `tension_score`**: Does the document have clear emotional shifts across pages? If all pages are the same tone, remove it.
5. **If using `continuous_flow`**: Is this multi-page (2+)? If single-page, switch to `"flow"`.
6. **If using `Shaped_Canvas`**: Is the archetype `"shaped_editorial"`? Is there at most one per page? No `Glass_Canvas` on the same page?
7. **Grid boundary check**: Are ALL `grid_area` values within `[1, 13]`? Any number < 1 or > 13 = FATAL ERROR.
8. **Component minimum size check**: `Glass_Canvas` ≥ 6col×4row? `Shaped_Canvas` ≥ 8col×6row?
9. **Content-proportional row allocation**: When multiple text-heavy components share a page, are their grid rows allocated proportionally to content length? Equal rows for unequal content = text overlap. (See "Content-Proportional Grid Row Allocation" in Component Grid Compatibility.)
9. **40% whitespace check**: Count occupied grid cells. At least 58 of 144 cells must be empty.
10. **Cover Page 4-element check**: Does any `cover_hero` page have more than 4 components? If yes, remove or merge components.
11. **Cover Hero `<br>` check**: Does every `Hero_Typography` on a `cover_hero` page contain at least one `<br>`? Single-line hero text on covers = FAIL.
12. **Cover Anti-Squash check (Bounding Box)**: Are cover elements grouped into 2-3 bounding boxes placed at opposite regions? Is remaining space dynamically distributed (not hardcoded gaps)? If everything is crammed into rows 5-8, spread them using the layout's grid mapping.
13a. **Cover Typography Scale check**: Is the hero title ≥ 45pt? Is subtitle ≥ 25pt? Is meta text ≥ 18pt (never below 14pt)? Tiny cover text = FAIL.
13b. **Cover Layout Selection**: Did I pick a layout (1-7) that matches the document tone? If unsure, default to Layout 1A (Diagonal Tension). For government/bidding documents, use Layout 7 (Left-Matrix).
14. **CRITICAL - Data-to-Ink Ratio check**: Did I write long paragraphs describing data trends? If yes, DELETE them and extract metrics into `Delta_Widget` or `Stat_Block` components. Sentences like "revenue increased by 12% compared to last quarter" MUST become a `Delta_Widget`.
15. **Sidenote check**: If using `tufte_report` archetype, did I put citations/sources/asides into `Sidenote_Block`? They must NOT be inline in `Glass_Canvas`.
16. **Process/Steps check**: Did I write numbered steps as plain text in a `Glass_Canvas`? Convert to `Process_List` component instead.
17. **Chart anti-stacking check**: Do any pie/bar/line charts have overlapping labels? Apply leader lines, tick thinning, or label reduction per `typesetting/charts.md`.
18. **Chart axis cleanup check**: Are top/right spines deleted? Grid lines dashed at 20% opacity (or hidden)? No solid grid lines.
19. **Donut default check**: Are pie charts rendered as donuts (hole ratio 60-70%)? Solid pies = FAIL unless explicitly requested.
20. **🚨 Triple Delivery check (MANDATORY)**: Creative pipeline must deliver **three files** to the user: (1) PDF - the final vector PDF; (2) HTML - the compiled `*_rendered.html` file; (3) Image - a full-page screenshot preview (PNG/JPG). After `convert.blueprint` generates the PDF and HTML, take a screenshot of the HTML for preview. Report all three file paths to the user.
21. **🚨 HTML Pre-Render Validation (MANDATORY for ALL HTML→PDF paths)**: Before calling `html2pdf-next.js`, `html2poster.js`, or `convert.blueprint`, run `poster_validate.py check-html` on the HTML file. This catches overflow:hidden on containers, missing @media screen auto-scale, font fallback gaps, contrast issues, and more. **Any ERROR-level issue must be fixed before generating the PDF.** Warnings are non-blocking but should be reviewed.
```bash
python3 "$PDF_SKILL_DIR/scripts/poster_validate.py" check-html page.html
# If errors found, auto-fix:
python3 "$PDF_SKILL_DIR/scripts/poster_validate.py" check-html page.html --fix --output page.html
```
22. **\u26a0\ufe0f MANDATORY: Post-Generation Checks (Creative)**: After HTML\u2192PDF conversion, run these checks:
```bash
# Check for content overflow and full-bleed issues
python3 "$PDF_SKILL_DIR/scripts/pdf_qa.py" output.pdf --no-tables
# Add metadata
python3 "$PDF_SKILL_DIR/scripts/pdf.py" meta.brand output.pdf
```
Execute the design.
---
## CJK & Vertical Text Rules (When Bypassing design_engine.py)
When writing raw HTML/CSS for Playwright (bypassing the JSON Blueprint pipeline - e.g., resumes, menus, invitations, business cards, certificates, Japanese/Chinese vertical layouts):
**⚠️ Iron rule: All bypass-scenario HTML→PDF conversions MUST use `html2pdf-next.js` — do NOT write custom Python Playwright scripts.** `html2pdf-next.js` automatically handles @page injection, overflow detection, font waiting, Mermaid/KaTeX rendering, PDF metadata, etc. See the "HTML→PDF Engine Selection Rules" section in SKILL.md.
```bash
node "$PDF_SKILL_DIR/scripts/html2pdf-next.js" input.html --output output.pdf --width 210mm --height 297mm
```
0. **Full-Bleed CSS (MANDATORY)**: Every HTML file for Playwright PDF MUST include:
```css
@page { size: 800px 1200px; margin: 0; } /* CONCRETE values only - CSS variables NOT supported in @page */
html, body { margin: 0; padding: 0; width: 800px; height: 1200px; }
```
**⚠️ @page rules do NOT resolve CSS variables** (`var(--x)` is silently ignored, falls back to A4). Always use concrete `px` values.
**⚠️ html/body must have explicit width + min-height** matching the canvas size. Use `min-height` (not `height`) so content taller than the design height expands naturally into a single long-page PDF.
**⚠️ Poster = seamless pagination.** When content exceeds `@page` height, `html2pdf-next.js` lets Playwright paginate at page boundaries - each page has the same dimensions, content flows seamlessly across pages (like scrolling a long image). Do NOT expand to a single oversized page.
`design_engine.py` handles this automatically via `override_css`.
0.25. **Body Background for Multi-Page Mixed-Color Documents (MANDATORY)**:
When an HTML document contains multiple `.page` divs with **different background colors** (e.g. dark cover + white body + dark specs page), Playwright's sub-pixel rounding creates <1px gaps at `.page` edges where `body` background shows through. On dark pages, a white `body` = visible white edges.
**Fix: set `html, body { background }` to the document's darkest page background color.**
```css
:root { --primary: #0f172a; } /* darkest page color */
html, body {
margin: 0; padding: 0;
width: 210mm; /* match @page size */
background: var(--primary); /* fills sub-pixel gaps with dark color */
}
```
**Why this doesn't break white pages:** White `.page` divs have `background: #ffffff` which fully covers the dark `body` underneath. The dark body only shows through the <1px sub-pixel gap at the extreme page edge - imperceptible on white pages after anti-aliasing.
**Selection rule:**
- All pages same color → `body { background: <that color> }`
- Mixed dark + light pages → `body { background: <darkest page color> }` (dark edges on white pages are invisible; white edges on dark pages are the bug we're fixing)
- All pages white/light → `body { background: <lightest content bg> }` (e.g. `#f8fafc`)
0.5. **No overflow:hidden + Browser Preview Adaptive Scaling (MANDATORY)**:
For fixed-size single-page designs (posters, infographics, certificates, etc.), **absolutely never** set `overflow: hidden` on `html`, `body`, or the main container. Reasons:
- When opening the HTML directly in a browser, the viewport is much smaller than the design size (e.g., a 1400px-tall page in a 900px viewport). `overflow: hidden` clips the bottom content and prevents scrolling.
- `html2pdf-next.js`'s pre-render check detects `scrollHeight > clientHeight` + `overflow: hidden` and triggers auto-fix (force-expanding the container), which may break the layout.
**`design_engine.py` handles this automatically**: During blueprint compilation, it auto-injects `@media screen` centering + scaling code, and the `html` background color uses `var(--c-bg)` matching the poster's main color. No manual addition needed.
**Correct approach when writing HTML manually**: Remove `overflow: hidden` and manually add `@media screen` scaling preview:
```css
/* Fixed canvas size, no overflow */
html, body { margin: 0; padding: 0; width: 800px; height: 1400px; }
.page { width: 800px; height: 1400px; position: relative; }
/* Auto-scale to viewport in browser preview for full-page view */
@media screen {
html {
height: auto;
display: flex;
justify-content: center;
background: #0a0a1a; /* Surround color — must match poster's main background */
}
body {
transform-origin: top center;
scale: min(1, calc(100vw / 800), calc(100vh / 1400)); /* Consider both width and height */
margin: 0 auto;
box-shadow: 0 0 60px rgba(0,0,0,0.3); /* Optional: shadow to distinguish canvas in preview */
}
}
```
`@media screen` rules only apply in browser preview; `page.pdf()` uses print media and is unaffected.
**Every fixed-size HTML must include this `@media screen` adaptive code.**
0.75. **Page Container Overflow Clipping (MANDATORY for multi-page documents)**:
Every `.page` div MUST have `overflow: hidden`. Decorative elements (glow circles, gradient overlays) commonly use `width: 120%` or negative offsets - without clipping, they inflate `scrollWidth` beyond page width, causing Playwright to shrink all content and shift it left.
```css
.page { overflow: hidden; } /* Clips decorative overflow, prevents Playwright shrink */
```
For horizontal flex layouts (≥3 items), always add `flex-wrap: wrap`. See `typesetting/overflow.md` §3.5.
1. **Character Encoding Safety**: Never use Japanese kana (の, が, は), rare symbols, or Private Use Area characters in content strings. They corrupt to U+FFFD (<28>) during LLM→file write→read transit. Replace with plain Chinese equivalents: ``→`之/的/缔/省略`.
2. **Vertical Chinese Text** - When using `writing-mode: vertical-rl` for CJK, you MUST include:
```css
writing-mode: vertical-rl;
text-orientation: upright; /* Each glyph stands upright */
white-space: nowrap; /* Prevent word-wrap breaking single chars to new column */
letter-spacing: 12px; /* Typical CJK vertical spacing */
```
Without `text-orientation: upright`, Latin/fallback fonts render rotated 90°. Without `white-space: nowrap`, CJK characters may wrap into unexpected multi-column layouts (e.g., 3 chars on one line + 1 char alone on next).
3. **Font Coverage**: For CJK content via Playwright, always load Google Fonts Noto Serif SC or Noto Sans SC via `<link>` tag in `<head>` (NOT `@import` in CSS - `@import` must be the very first rule in a stylesheet or it's silently ignored). Example:
```html
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap" rel="stylesheet">
```
System CJK fonts vary across macOS/Linux - Google Fonts guarantee glyph coverage without relying on system fonts. `design_engine.py` already handles this automatically via `<link>` tag.
4. **Post-Generation Text Verification**: After Playwright renders the PDF, extract text from every page and scan for `?` or `\ufffd`. If found, the source HTML has encoding-corrupted characters that must be replaced in the Python source.
5. **🚨 HTML Pre-Render Validation (MANDATORY)**: After writing the HTML file and before running `html2pdf-next.js`, always run the HTML validator:
```bash
python3 "$PDF_SKILL_DIR/scripts/poster_validate.py" check-html <your_file>.html
```
- **ERROR** items (e.g. `OVERFLOW_HIDDEN_CONTAINER`, `FONT_NO_FALLBACK`) → must fix before PDF generation. Use `--fix --output <file>.html` for auto-repair.
- **WARNING** items (e.g. `FIXED_SIZE_NO_SCREEN_ADAPT`, `SCREEN_ADAPT_NO_SCALE`, `COLOR_CONTRAST`) → review and fix where appropriate.
- This catches the most common bypass HTML bugs: `overflow:hidden` on containers, missing `@media screen` auto-scale for fixed-size pages, font-family without generic fallback, low contrast text, etc.

702
skills/pdf/briefs/poster.md Executable file
View File

@@ -0,0 +1,702 @@
# Poster Scene Rules — Creative Pipeline (Poster-Specific Constraints)
> When poster / 海报 / 传单 / flyer / 宣传页 keywords are detected, load these rules on top of `creative.md`.
> These rules take priority over `creative.md` generic rules; in case of conflict, this file prevails.
>
> **Positioning**: This is a scene-layer patch on `creative.md` (the generic Creative pipeline) — only covers poster-specific constraints, does not repeat generic rules.
---
## 1. Page Count & Dimensions
### 1.1 Default Single Page
- When the user does not explicitly request multiple pages, **default to a single-page poster**
- Single-page poster `total_pages: 1`, never split into multiple pages on your own
### 1.2 Sizing Strategy
| Text Volume | Recommended Size | Aspect Ratio | Notes |
|--------|---------|--------|------|
| ≤ 50 chars | 720 × 960 | 3:4 | Title poster / social media cover / card |
| 50200 chars | 720 × 960 | 3:4 | Standard promotional poster |
| > 200 chars | 720 × min-height 960 | Adaptive | Long poster (H5 style), content stretches height |
| User-specified landscape | Adjust as needed | As needed | Width and height can be swapped |
### 1.3 Canvas Variables
```json
{
"canvas": { "width": 720, "height": 960 }
}
```
Default dimensions can be overridden via the `canvas` field in the Blueprint. `design_engine.py` will automatically inject `--canvas-w` and `--canvas-h` CSS variables.
---
## 2. Information Density & Page Fill
### 2.1 Core Iron Rule: Content Must Fill the Page
> **The biggest visual disaster for a poster is not content overflow, but content only occupying half the page with a blank bottom half.**
| Text Volume | Content Area / Page Ratio | Notes |
|--------|----------------|------|
| ≤ 50 chars | 7080% | Enlarged cards, large font sizes, generous decorative whitespace is intentional |
| 50200 chars | 7585% | Content modules distributed evenly, must not be crammed in the top half |
| > 200 chars | 8090% | Content-dominant, whitespace only at margins |
### 2.2 Anti-Top-Heavy
- All components' `grid_area` **must cover the full 1→13 rows**, never stop at row 10 or 11
- The last component's `grid_area` row endpoint must be `13`
- **No large blank space at top/header** — the first content component's `grid_area` should start at row 1, not row 3 or 4
- **No large blank space on left/right sides** — components should utilize full column width (most should span 1→13 columns)
- If content is insufficient to fill 12 rows, use these strategies (by priority):
1. **Increase font size**: Hero from scale 5 → 6, body font +2pt
2. **Increase component spacing**: grid gap from 16px → 24px
3. **Insert decorative components**: `Hairline_Divider`, `Page_Ghost_Number`
4. **Expand stat block / hero grid row count**
### 2.3 Grid Area Allocation Iron Rule
```
✅ Correct: Components cover all rows 1→13
Page: [Hero 1→4] [Stats 4→7] [Glass 7→10] [Meta 10→13]
❌ Wrong: Components only reach row 10, rows 11-13 empty
Page: [Hero 1→3] [Stats 3→5] [Glass 5→8] [Meta 8→10] [??? 10→13 void]
```
### 2.4 ★★★ Content-Proportional Row Allocation (Anti-Overlap Iron Rule)
> **When two or more text components share a page, rows must be allocated proportionally to content volume — never split evenly!**
**Problem reproduction:**
```
Attractions: 450 chars (3 attractions × 150 chars description)
Food: 250 chars (2 foods × 125 chars description)
Available rows: 8 rows (5→13)
❌ Even split: Attractions 5→9, Food 9→13 → 4 rows each
→ Attractions content far exceeds 4-row capacity, overflows into Food area, text overlaps!
✅ Proportional split:
Attractions: 8 × 450/700 ≈ 5 rows → 5→10
Food: 8 × 250/700 ≈ 3 rows → 10→13
```
**Steps:**
1. Write all `markdown_content` first
2. Count the **character count** of each text component (including titles/paragraphs/lists)
3. Allocate rows proportionally: `rows_i = total_rows × (chars_i / total_chars)`
4. Round off, each component **minimum 3 rows**
5. Verify: all component grid_area endpoints connect end-to-end, covering full 1→13
**Applicable scenarios:**
- Multiple `Glass_Canvas` on the same page (most common)
- `Glass_Canvas` + `Process_List` on the same page
- Any two or more components containing text paragraphs
**Not applicable:**
- `Hero_Typography` (very large font, 1-3 lines occupying 2-3 grid rows is reasonable)
- `Stat_Block` (number + label, fixed height, typically 2 rows)
- `Floating_Meta` (short labels, typically 2-3 rows)
---
## 3. Font Size System (Poster-Specific)
### 3.1 Minimum Font Size (Hard Floor)
| Element | Min Font Size | Recommended | Notes |
|------|---------|---------|------|
| **Page main title** | **50px** | 5672px | Poster title must have visual impact |
| **Body text** | **24px** | 2428px | Posters are not reports — body font must be large |
| **Subtitle / card title** | **28px** | 3240px | Secondary headings |
| **Floating Meta** | **16px** | 1620px | Metadata text |
| **Stat Number** | **48px** | 5672px | Data sculptures must be eye-catching |
| **Stat Label** | **14px** | 1416px | Data labels |
> Compared to `fill-engine.md`'s generic red line (body ≥ 14pt), the poster body floor is **24px** — posters are a distance-reading medium, font sizes must be larger.
### 3.2 Emphasis Hierarchy
- Use **font size + font weight** to create hierarchy, **not color differentiation**
- Emphasis text (keyword/number highlights) font size must be smaller than titles
- Hero title recommended `weight: "black"` (900), subtitle `weight: "thin"` (100), creating extreme contrast
---
## 4. Color Rules (Poster-Specific)
### 4.1 Color Palette System
**Primary palette: Material Design 3 with low-medium saturation.** Default to medium-saturation colors; for light themes, use gradient backgrounds with white/light text on top.
| Area | Proportion | Role |
|------|------|------|
| 60% Ground | Background/margins/whitespace | Main color (low saturation) |
| 30% Structure | Cards/dividers/secondary areas | Derived from main by adjusting lightness |
| 10% Emphasis | Titles/key numbers/single accent | Adjusted purity/brightness of main color |
**Color derivation rules:**
- Title/subtitle text color: Adjust main color's purity and brightness (not a separate random color)
- Auxiliary colors: **Maximum 2**, derived from primary by adjusting lightness/saturation
- Keep consistent color palette throughout — do NOT change main color between sections
- Default recommendation: **Light theme with medium saturation**, or gradient background + white fonts
### 4.2 Poster Additional Constraints
- **No pure white (#FFFFFF) background** — use at least `#f5f4f2` or warmer off-white
- **No transparent background**
- **Page base color (`<body>` / `<html>` background) must match poster content background** — `printBackground: true` renders body background. If body is white but poster content is gray, white borders/gaps appear in the PDF. Ensure `html, body { background: var(--c-bg); }` matches the poster canvas exactly
- **Do not use a single image to fill the background** — use grid textures, gradients, geometric shapes, and other generative backgrounds
- Long posters (>200 chars) must not use full-image backgrounds
- **Gradients** or **large blurred circles/symbols** can be used sparingly as background accents
- Maximum 2 auxiliary colors, derived from primary by adjusting lightness/saturation
- Background accents: grid textures, organic shapes, large blurred circles/symbols — NOT a single image
- **Overall bright and vibrant color combinations** — the poster should feel visually striking, not muted/dull
### 4.3 palette_mode Mapping
| Poster Style | Recommended palette_mode | color_harmony |
|---------|-------------------|---------------|
| Business/Formal | `minimal` | `auto` |
| Tech/AI | `dark` | `complementary` |
| Lifestyle/Food/Artistic | `pastel` | `analogous` |
| Luxury/Ceremony | `jewel` | `split_complementary` |
| Other | `minimal` (default) | `auto` |
---
## 4.5 ★★★ Anti-Modularization Iron Rule (Anti-Card / Anti-Dashboard)
> **A poster is a complete composition, not a dashboard, not an APP interface, not a report.**
### Root Cause
LLMs naturally tend to "classify information → put each category in a box → stack them vertically", because that is report/document organization logic. But poster visual logic is the exact opposite — **information is unified, hierarchy is established through typographic rhythm (font size, weight, spacing, whitespace), not through borders and background colors**.
### Comparison
| Report/UI Thinking ❌ | Poster Thinking ✅ |
|---|---|
| Each info block wrapped in `border + border-radius + background` as a card | Information placed directly on the canvas, no borders no background |
| Clear visual boundaries between modules | Modules naturally separated by **whitespace and thin lines** |
| Looks like a mobile APP interface | Looks like a design piece |
| Hierarchy via "different colored boxes" | Hierarchy via **font size gradient + weight contrast + color lightness** |
| Stacked Glass_Canvas components = card wall | Pure typography + occasional Hairline_Divider |
### Mandatory Rules
1. **Single-page posters must not have more than 3 containers with `background` + `border`.** If information needs grouping, use whitespace (margin/padding) and thin lines (1px, opacity < 10%) instead of bordered cards.
2. **No 2×2 / 2×3 grid card layouts** (unless it's a pure data scenario with `data_dashboard` archetype). Information flows in a single column; multiple items in the same row use `flex + gap` horizontal layout without borders.
3. **Information hierarchy must be established through typography properties**, not through "different colored/backgrounded boxes":
- Primary information: large font (≥48px) + heavy weight (900)
- Secondary information: medium font (18-24px) + medium weight (700)
- Tertiary information: small font (12-14px) + light weight (300-400) + reduced opacity
4. **Glass_Canvas may be used at most once in a single-page poster.** If multiple text sections are needed, use direct HTML typography instead of wrapping each paragraph in a Glass_Canvas.
5. **Bottom action information (price/address/QR code) should not be wrapped in a separate color block.** Use larger font + heavier weight to emphasize, keeping it unified with the overall composition.
### Correct Example (Direct HTML Flow)
```html
<!-- ❌ Wrong: card wall -->
<div class="card" style="background:rgba(255,255,255,0.5); border-radius:12px; border:1px solid #ddd;">
<h3>📅 展期</h3>
<p>7.15 — 8.30</p>
</div>
<div class="card" style="background:rgba(255,255,255,0.5); border-radius:12px; border:1px solid #ddd;">
<h3>📍 地点</h3>
<p>城市艺术中心</p>
</div>
<!-- ✅ Correct: pure typography, no borders -->
<div class="info-row" style="display:flex; gap:48px;">
<div>
<div style="font-size:10px; letter-spacing:3px; opacity:0.45;">展期</div>
<div style="font-size:20px; font-weight:700;">7.15 — 8.30</div>
</div>
<div>
<div style="font-size:10px; letter-spacing:3px; opacity:0.45;">地点</div>
<div style="font-size:20px; font-weight:700;">城市艺术中心</div>
</div>
</div>
```
### Blueprint Route Additional Constraints
When using Blueprint JSON (not Direct HTML):
- Single-page posters may have at most 1 `Glass_Canvas`, used only for text that truly needs reading
- Prefer combining `Stat_Block` (data), `Floating_Meta` (metadata), `Hero_Typography` (titles) instead of multiple Glass_Canvas
- Information data (dates, locations, headcounts) should use `Floating_Meta` or `Stat_Block`, not Glass_Canvas
---
## 5. Layout Strategy
### 5.1 Composition Priority
1. **Vertical composition** (vertical flow) — most common, information flows top to bottom
2. **Left-right composition** (split vertical) — image and text split, `archetype: "split_vertical"`
3. **Centered composition** (centered) — suitable for title cards with ≤ 50 chars
### 5.2 Card Rules
- Cards **must never overlap**
- Card content should be well-distributed, **no large internal whitespace**
- Text within cards **vertically centered**
- Glass Canvas `align-items` must be `stretch` (built into the engine)
### 5.3 Breathing Margins
| Scenario | safe-zone inset | Notes |
|------|----------------|------|
| Cover / title poster (≤50 chars) | `12% 14%` | Generous whitespace is intentional |
| Standard poster (50-200 chars) | `8% 10%` | Balance whitespace and content |
| Long poster (>200 chars) | `6% 8%` | Maximize content area |
> Max content width = 80% of page width (consistent with original prompt)
---
## 5.5 Visual Impact Rules
### 5.5.1 Emphasis & Contrast
- Create visual contrast with **oversized and small elements** — hero numbers/titles vs tiny metadata
- Highlight core points with large fonts or numbers for strong visual contrast
- **Emphasized text must remain smaller than headings/titles** — never let a highlight be bigger than the heading
- Texts that need emphasis can be highlighted with color, weight, or circled with hand-drawn lines
- **Do not insert multiple small pictures as embellishments** — this wont enhance visual appeal
### 5.5.2 Alignment & Spacing
- Allow blocks to resize based on content, align appropriately, optimize space utilization
- If excess whitespace exists, **enlarge fonts or modules** to balance the layout
- Cards cannot overlap; content should fill the card area without excessive empty space
- Use **flexbox** layouts to prevent footer from moving up (with top and bottom margin settings)
- For visual variety: encourage diverse and creative layouts beyond standard grids, while maintaining alignment and hierarchy
---
## 6. Design Style Library
Based on content theme, the model should autonomously select one of the following styles. Explain the selection rationale in the Blueprint's `design_rationale`.
| Style | Characteristics | Applicable Scenarios |
|------|------|---------|
| **Modern Minimal** | Clean colors, organic shapes, flowing curves, rounded cards, clear hierarchy | Business, tech, education |
| **Neo-Brutalism** | Flat elements, illustrations, patterns, large text blocks, special font designs, thick borders | Creative, events, youth |
| **Artistic Gradient** | Diffused light, gradient glow, semi-transparent elements, blur effects, glass texture | Art, music, branding |
| **Collage** | Contrasting color design, material collage, large text, irregular layout | Trends, fashion, exhibitions |
| **Playful UI** | Bright colors, interesting shapes, energetic | Children, games, social |
### Special Forms for Text Volume ≤ 50 Characters
When content is minimal, prioritize the following compact forms:
| Form | Description | archetype |
|------|------|-----------|
| **Centered Card** | Calendar-like effect, key content centered as note/card | `cover_hero` |
| **Bookmark Page** | Narrow tall ratio (e.g. 360×960), vertical reading | `cover_hero` |
| **Minimal Text** | Title + whitespace only, no additional information | `cover_hero` |
| **Sticky Note** | Content displayed as floating sticky notes above background | `cover_hero` |
| **Polaroid Card** | Photo-style card with caption below | `cover_hero` |
### Special Forms for Text Volume ≤ 100 Characters
| Form | Description |
|------|------|
| **Floating Card System** | Content as cards floating above organic shape backgrounds |
| **Notebook Style** | Lined-paper aesthetic with handwritten-feel content |
| **Fortune Stick** | Vertical strip with centered calligraphy text |
> **Key constraint**: If the user only provides a title with no other requirements, **do not expand content on your own** — just place the title.
---
## 6.5 Icons and Illustrations
- Use **Material Design Icons** (Google Fonts method):
```html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Usage: -->
<i class="material-icons">icon_name</i>
```
- Icon color: Use the **theme color** (not random colors)
- Icon size and position: Aligned with surrounding elements, never stretched
- If positioned beside text: **center-aligned with the first line of text**
- **Emoji can be used as icons** — 🌸 🍴 🏙️ etc. (but remember ReportLab cant render emoji — only use in HTML/Playwright route)
- For logos/emblems: Use text "Your Logo" or icons, **never** search for logo images
---
## 7. Text Readability
### 7.1 Iron Rules
- **Line height ≥ 130%** (`line-height: 1.3` or above)
- **No text shadow/glow effects**
- **When text overlays images**, a semi-transparent mask layer is required
- **Do not use images with lots of text/charts/numbers as text background**
- **No `text-align: justify`** (CJK characters get stretched letter-spacing, terrible result) — always use `text-align: left`
### 7.2 Contrast
- Text on light background: `color: #242220` or darker
- Text on dark background: `color: #f5f4f2` or lighter
- Text on medium background (L 0.300.70): **forbidden** — do not place text on mid-tone backgrounds
---
## 8. Image Usage Rules
### 8.0 Image Recommendations
When creating posters, actively use images to enrich visual effects. Good images can significantly enhance the poster's visual impact.
- Priority: **installed image generation skill** > web image search
- Image style must match the poster theme
- Download images locally first, then embed into the poster
- Local images must be converted to base64 data URI in HTML (Playwright cannot load local absolute paths)
### 8.1 Core Principles
- Don't force images when none are suitable, but images improve results when available
- Each image must be **unique** in the design, no reuse
- Prefer clear, high-resolution, watermark-free, text-free images
- Images should have rounded corners, sized consistently with the overall design
- You can try adding **irregularly shaped masks** (CSS `clip-path`) to images for visual interest
### 8.2 Prohibited Behaviors
- ❌ Placing images directly in corners
- ❌ Images obscuring text or overlapping with other modules
- ❌ Multiple images scattered randomly as decoration
- ❌ Searching for images when logo/badge is needed — use text "Your Logo" or icons instead
---
## 9. Chart Rules (Poster Scene)
- Large numerical datasets → consider creating visual charts
- Chart style should match the poster theme
- Use **Bento Grid** layout for multiple charts
- Chart containers **must have height constraints** to prevent infinite growth
---
## 10. Prohibited Items (Poster-Specific)
### 10.0 HTML Rendering Iron Rules (Applicable to All HTML → PDF Routes)
| Rule | Description |
|------|------|
| **No `overflow: hidden` on content components** | Truncates text. Only allowed on page-level `.canvas` and decorative background layers |
| **Use `min-height` instead of `height` for content containers** | `height:100%` locks height, content gets clipped when too much; `min-height:100%` allows natural expansion |
| **Exceptions where `overflow: hidden` is allowed** | `.canvas` (page boundary), `.poster` (auto-injected by `html2poster.js`), `.floating-meta` (short label ellipsis), cover Layer 1 (decorative clipping), SVG/img (fill container) |
| **Absolutely no `backdrop-filter`** | **Playwright PDF rendering silently discards entire element content!** Use fixed rgba() background color for cards instead |
| **Absolutely no `text-align: justify`** | CJK character spacing gets abnormally stretched, always use `text-align: left` |
| **`overflow: hidden` on `.page` containers** | **MANDATORY for multi-page documents.** Decorative elements (glow, gradient circles, oversized backgrounds) with `width > 100%` or negative offsets cause `scrollWidth > clientWidth`, triggering Playwright to shrink the entire page → content drifts left. `.page { overflow: hidden }` clips decorative overflow without affecting visible content |
| **Horizontal flex rows must have `flex-wrap`** | ≥3 inline items (flow bars, step lists, tag rows) without `flex-wrap: wrap` will overflow the page right edge when content is long. See `typesetting/overflow.md` §3.5 for full rules |
| Prohibited | Reason |
|--------|------|
| ❌ Timeline graphics | Complex connecting lines easily misalign in PDF rendering |
| ❌ Complex SVG-drawn structure/flow diagrams | Unless user explicitly requests |
| ❌ Code-drawn maps or flags | Poor quality |
| ❌ Base64 images (when exceeding 10MB) | File too large. Small image base64 is acceptable (Playwright cannot load local paths) |
| ❌ Content truncation | Must adjust container height to ensure all content fully displayed |
| ❌ Pure white background (#FFFFFF) | Lacks design quality |
| ❌ Transparent background | PDF output cannot be transparent |
---
## 11. Poster Font Recommendations
### 11.1 CJK Font Recommendations
**For Chinese posters (serious/formal scenes):**
| Purpose | Recommended Font | Google Fonts / CDN Name | Style |
|------|---------|-------------------|------|
| CJK title (serious) | DingTalk JinBuTi | `DingTalk JinBuTi` (letter-spacing: -5%) | Bold, impactful |
| CJK title (alt) | Douyin Sans / Alimama FangYuanTi VF Bold | Via CDN | Modern Chinese |
| CJK title (serif) | Swei B2 Serif CJKtc Bold | Via CDN | Elegant serif |
| CJK body | HarmonyOS Sans SC | `HarmonyOS Sans SC` | Clear, readable |
| CJK body (fallback) | Noto Sans SC Regular | `Noto Sans SC:wght@400` | Google Fonts guaranteed |
| CJK artistic | Noto Serif SC Bold | `Noto Serif SC:wght@700` | Elegant, artistic |
| Handwritten | ZhanKuKuaiLeTi2016XiuDingBan-2 | Via CDN | Casual, playful |
| Pixel/dot-matrix | DottedSongtiSquareRegular | Via CDN | Retro, xiangsuti |
**For English posters:**
| Purpose | Recommended Font | Google Fonts Name | Style |
|------|---------|-------------------|------|
| English/number title | Futura | System / `Futura` | Classic geometric |
| English body | PingFang HK | System | Clean, modern |
| English title (Google) | Inter Black | `Inter:wght@900` | Modern geometric |
| English serif | Playfair Display | `Playfair+Display:wght@900` | Classic editorial |
| Number emphasis | Inter Black | `Inter:wght@900` | Data sculpture |
### 11.2 Font Usage Constraints
- Entire poster **maximum 3 fonts**
- Title and body may use different fonts, but must be visually harmonious
- **Never reduce font size or line height to squeeze in more content**
- Font content in cards should be **vertically centered** within the card
- You may use different style fonts for entertaining or artistic scenes
---
## 12. Coordination with design_engine.py
### 12.0 ★★★ Stable Layout Selection Strategy (Anti-Overflow Core Rule)
> **Content-heavy posters must bypass the Blueprint 12×12 Grid and use a pure flow-based HTML approach.**
**Why:** The Blueprint 12×12 Grid allocates space with fixed row heights and cannot predict actual text rendering height, causing:
- Text overflowing Glass Canvas containers
- CJK character spacing stretched by `text-align: justify`
- Inaccurate row allocation for multiple components, text overlap
**Route selection:**
| Scenario | Recommended Route | Notes |
|------|---------|------|
| Title poster (≤ 50 chars) | Blueprint JSON | Few components, little content, Grid sufficient |
| Standard poster (50-150 chars) | Blueprint JSON | Grid mostly sufficient, mind row allocation |
| **Info-dense poster (>150 chars or multiple sections)** | **★ Direct HTML Flow** | **Strongly recommended — completely avoids overflow** |
| **Multi-page info poster** | **★ Direct HTML Flow** | **Strongly recommended** |
| Poster with images | Direct HTML Flow | Image embedding is more stable |
### 12.1 Direct HTML Flow Approach (Recommended Default)
**Core idea: No Grid, no fixed height, content flows naturally, never overflows.**
Write HTML directly, convert to PDF via `html2poster.js`. The poster is a **single continuous `<div class="poster">` container** — NOT split into separate `<div class="page">` blocks.
#### ★★★ Single-Canvas vs Multi-Page (Iron Rule)
| Approach | When to Use | HTML Structure |
|----------|-------------|----------------|
| **Single Canvas** (default) | All content forms one unified poster | One `<div class="poster">`, no `page-break` |
| **Multi-Page** (exception) | User explicitly requests separate pages (e.g., "make a 4-page booklet") | Multiple `<div class="page">` with `page-break-after: always` |
> **Default = Single Canvas.** A poster is ONE design composition, not a paginated report. Multi-page is only for booklets/multi-page documents where the user explicitly asks for page separation.
#### ★★★ Dynamic Height (Anti-Blank-Bottom Iron Rule)
**NEVER hardcode `@page { size: W H }` or `.poster { min-height: Xpx }` with a fixed height.** This creates blank space at the bottom when content is shorter than the hardcoded value.
**Correct approach — use `html2poster.js`:**
`html2poster.js` automatically handles all of this — you just need to write the HTML correctly and call one command:
```bash
node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px
```
It will automatically:
1. Add `overflow: hidden` to `.poster` container (clips decorative overflow)
2. Inject `@page { margin: 0 }` (zero margins)
3. Sync `html/body` background with `.poster` background
4. Measure `.poster` scrollHeight (actual content height)
5. Generate single-page vector PDF with exact dimensions
**⚠️ Do NOT use `html2pdf-next.js` for posters.** It is designed for multi-page documents and will inject 20mm margins / A4 pagination.
**⚠️ Do NOT write hand-written Playwright scripts for posters.** `html2poster.js` handles everything.
```css
/* ✅ CORRECT CSS for poster HTML (html2poster.js handles the rest): */
html, body { margin: 0; padding: 0; background: var(--c-bg); }
.poster { width: 720px; position: relative; background: var(--c-bg); }
/* Note: overflow:hidden on .poster is auto-injected by html2poster.js,
but including it in CSS is fine too */
```
**CSS iron rules:**
```css
html, body { margin: 0; padding: 0; background: var(--c-bg); }
/* Single poster canvas — NO fixed height */
.poster {
width: 720px;
position: relative;
overflow: hidden;
background: var(--c-bg);
/* Height is determined by content, measured at render time */
}
/* Card/content block */
.card {
background: rgba(255,255,255,0.7);
border-radius: 12px;
padding: 24px 28px;
margin-bottom: 20px;
/* No height, no max-height, no overflow:hidden */
}
/* CJK text iron rules */
.card, .card p, .card li {
text-align: left; /* Absolutely no justify! CJK stretches letter-spacing */
line-height: 1.6;
word-break: break-all; /* CJK natural line break */
}
/* Image */
.hero-image {
width: 100%;
border-radius: 8px;
margin-bottom: 20px;
display: block;
}
```
**HTML structure template (Single Canvas):**
```html
<div class="poster">
<!-- Hero section -->
<div class="hero"> ... </div>
<!-- City 1 -->
<div class="city-section">
<h2>南京</h2>
<div class="items-row"> ... attractions ... </div>
<div class="food-row"> ... food ... </div>
</div>
<!-- City separator (thin line, NOT page-break) -->
<div class="city-sep"></div>
<!-- City 2 -->
<div class="city-section"> ... </div>
<!-- Footer -->
<div class="poster-footer"> ... </div>
</div>
```
**Why it's stable:**
- No fixed height — content naturally defines poster size
- No `overflow: hidden`, text always fully displayed
- `text-align: left` avoids CJK letter-spacing stretch
- Single canvas = one unified composition, not chopped pages
- PDF height measured at render time = zero blank space
**Convert to PDF and PNG:**
```bash
# PDF (vector, single-page, zero margins, auto-height):
node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px
# PNG preview (screenshot):
# Use Playwright screenshot with measured height
```
> **⚠️ Do NOT write hand-written Playwright `page.pdf()` scripts.** Use `html2poster.js` which handles overflow:hidden, margin:0, background sync, and height measurement automatically.
### 12.2 Blueprint Grid Approach (Only for Simple Posters)
| Behavior | Status | Notes |
|------|------|------|
| Dynamic inset | ✅ Fixed | More content → smaller inset (`8% 10%`), less content → default (`10% 12%`) |
| Glass Canvas overflow | ✅ Fixed | `min-height:100%` replaces `height:100%`, removed `overflow:hidden` |
| Glass Canvas / Process List stretch | ✅ Fixed | Auto `align-items: stretch` |
### 12.3 Poster Marker in Blueprint
Add `scene: "poster"` marker in `art_direction` so `design_engine.py` can identify poster scenes and apply specific logic in the future:
```json
{
"art_direction": {
"scene": "poster",
"palette_mode": "minimal",
"color_harmony": "auto",
"background_svg": "flow",
"design_rationale": "..."
}
}
```
---
---
## 14. PDF Conversion Iron Rules
### 14.1 Background Color Consistency
```css
/* Must ensure html/body background = poster canvas background */
html, body {
background: var(--c-bg); /* Same color as .canvas background */
}
```
- Playwright `page.pdf({ printBackground: true })` renders body background color
- If body is white but poster is gray, white borders appear in the PDF
- `design_engine.py` already auto-injects `background: var(--c-bg)`, but if bypassing the engine and writing HTML directly, **you must ensure manually**
**Multi-page posters / brochures with mixed page backgrounds:**
- When pages alternate between dark and light backgrounds, set `body { background }` to the **darkest page color** (see SKILL.md "Background Color Consistency" for full rationale)
- This eliminates sub-pixel white edges on dark pages without affecting light pages
### 14.2 Content Centering (Anti-Drift)
**Poster content must be centered in the PDF, no left or right drift allowed.**
Common drift causes and fixes:
| Cause | Fix |
|------|------|
| `@page { margin }` not 0 | Must be `@page { size: <w> <h>; margin: 0; }` |
| `.safe-zone` `inset` left-right asymmetric | Ensure `inset: Y% X%` uses same X% for left and right |
| Component `grid_area` only uses partial columns | Most components should span `1 / 1 / X / 13` (full width) |
| Content container has `max-width` but no `margin: 0 auto` | Add `margin: 0 auto` to center |
| Playwright PDF default margin | Pass `margin: { top: 0, right: 0, bottom: 0, left: 0 }` |
### 14.3 Anti-Blank Edges (Dynamic Height Iron Rule)
**Poster edges, top, and bottom should not have large meaningless whitespace.**
**★★★ CRITICAL: Never hardcode poster height.** The poster is a single continuous canvas — its height is defined by content, not by a fixed CSS value.
| Pattern | Status | Result |
|---------|--------|--------|
| `@page { size: 720px 3600px }` | ❌ FORBIDDEN | Creates 853px+ blank space at bottom if content is shorter |
| `.poster { min-height: 3600px }` | ❌ FORBIDDEN | Same problem — blank bottom |
| `.poster { width: 720px }` (no height) | ✅ CORRECT | Content defines height naturally |
| `node html2poster.js poster.html --width 720px` | ✅ CORRECT | Auto-measures height, zero blank space |
**The 720×960 dimension is for multi-page documents with `page-break-after: always` only — NOT for single-canvas posters.**
- Content must make full use of page area, no more than 20% unused space within safe-zone
- If excess whitespace exists, enlarge font sizes or modules to balance the layout
- Checklist:
- [ ] Poster height defined by content (no hardcoded height)?
- [ ] PDF generated via `html2poster.js` (not html2pdf-next.js)?
- [ ] First component starts from the top?
- [ ] Last component reaches the bottom with proper padding?
- [ ] Left-right margins symmetric?
- [ ] No blank space at bottom of PDF?
---
## 15. Preflight Checklist (Poster-Specific)
Before outputting JSON Blueprint, verify the following items:
```
□ ★ Single-canvas check: poster is ONE continuous <div>, not split into separate pages? (unless user explicitly requests multi-page)
□ ★ Dynamic height check: no hardcoded @page size height or .poster min-height? PDF generated via html2poster.js?
□ ★ Anti-modularization check: ≤ 3 containers with background+border in single-page poster? No 2×2 card grid? Hierarchy via font size/weight not border colors?
□ Emphasis elements (price/date/address) highlighted via font size+weight, not wrapped in separate color blocks?
□ Overall looks like a design piece, not an APP interface or dashboard?
□ total_pages = 1 (single canvas)? (unless user explicitly requests multi-page booklet)
□ No hardcoded @page height or .poster min-height? Content defines height?
□ Left-right margins symmetric? (no left/right drift)
□ html/body background = poster canvas background? (no color mismatch)
□ No bottom blank space in PDF? (height measured dynamically)
□ Page main title ≥ 50px? Body text ≥ 24px?
□ Text volume ≤ 150 chars? (single Glass Canvas)
□ palette_mode not #FFFFFF pure white?
□ No single image filling the background?
□ No overlap between cards?
□ No Timeline / complex SVG structure diagrams?
□ All content fully displayed, not truncated?
□ Emphasis text font size < title font size?
□ Entire design ≤ 3 fonts?
□ cover_hero page ≤ 4 components?
□ Hero_Typography has <br> line breaks?
□ @page { margin: 0 } set? (prevents PDF drift)
```

View File

@@ -0,0 +1,284 @@
# Process Brief: Advanced Reference
Edge-case tools and techniques. **Load only when the main `process.md` doesn't cover the task.**
```
Edge case
├─ No text extracted from PDF → §OCR Fallback
├─ PDF is encrypted/locked → §Encrypted PDFs
├─ PDF is corrupted/damaged → §Corrupted PDFs
├─ Need precise text coordinates → §pdfplumber Advanced
├─ Need fast rendering → §pypdfium2
├─ Advanced page extraction → §poppler-utils Advanced
├─ Complex page ranges / merge → §qpdf Page Manipulation
├─ Optimize file size → §qpdf Optimization
├─ Batch process many PDFs → §Batch Processing
└─ Memory issues with large PDF → §Performance Optimization
```
For basic operations (extract, merge, split, fill forms, convert), go back to `process.md`.
---
## §pypdfium2 (Apache/BSD License)
A Python binding for PDFium (Chromium's PDF library). Excellent for fast rendering and text extraction — serves as a PyMuPDF replacement.
#### Render PDF to Images
```python
import pypdfium2 as pdfium
pdf = pdfium.PdfDocument("document.pdf")
# Render single page
page = pdf[0]
bitmap = page.render(scale=2.0, rotation=0)
img = bitmap.to_pil()
img.save("page_1.png", "PNG")
# Batch render all pages
for i, page in enumerate(pdf):
bitmap = page.render(scale=1.5)
img = bitmap.to_pil()
img.save(f"page_{i+1}.jpg", "JPEG", quality=90)
```
#### Extract Text with pypdfium2
```python
import pypdfium2 as pdfium
pdf = pdfium.PdfDocument("document.pdf")
for i, page in enumerate(pdf):
text = page.get_text()
print(f"Page {i+1}: {len(text)} chars")
```
## §pdfplumber Advanced Features
#### Extract Text with Precise Coordinates
```python
import pdfplumber
with pdfplumber.open("document.pdf") as pdf:
page = pdf.pages[0]
# All characters with coordinates
for char in page.chars[:10]:
print(f"'{char['text']}' at x:{char['x0']:.1f} y:{char['y0']:.1f}")
# Extract text within a specific bounding box (left, top, right, bottom)
bbox_text = page.within_bbox((100, 100, 400, 200)).extract_text()
```
#### Advanced Table Extraction with Custom Settings
```python
import pdfplumber
with pdfplumber.open("complex_table.pdf") as pdf:
page = pdf.pages[0]
table_settings = {
"vertical_strategy": "lines",
"horizontal_strategy": "lines",
"snap_tolerance": 3,
"intersection_tolerance": 15
}
tables = page.extract_tables(table_settings)
# Visual debugging
img = page.to_image(resolution=150)
img.save("debug_layout.png")
```
---
## §poppler-utils Advanced Features
### Extract Text with Bounding Box Coordinates
```bash
pdftotext -bbox-layout document.pdf output.xml
```
### Advanced Image Conversion
```bash
# High-resolution PNG
pdftoppm -png -r 300 document.pdf output_prefix
# Specific page range
pdftoppm -png -r 600 -f 1 -l 3 document.pdf high_res_pages
# JPEG with quality setting
pdftoppm -jpeg -jpegopt quality=85 -r 200 document.pdf jpeg_output
```
### Extract Embedded Images
```bash
pdfimages -all document.pdf images/img
pdfimages -list document.pdf
```
---
## §qpdf Advanced Features
### Complex Page Manipulation
```bash
# Split into groups of N pages
qpdf --split-pages=3 input.pdf output_group_%02d.pdf
# Extract complex page ranges
qpdf input.pdf --pages input.pdf 1,3-5,8,10-end -- extracted.pdf
# Merge specific pages from multiple PDFs
qpdf --empty --pages doc1.pdf 1-3 doc2.pdf 5-7 doc3.pdf 2,4 -- combined.pdf
```
### PDF Optimization and Repair
```bash
qpdf --linearize input.pdf optimized.pdf
qpdf --optimize-level=all input.pdf compressed.pdf
qpdf --check input.pdf
qpdf --fix-qdf damaged.pdf repaired.pdf
```
### Encryption and Decryption
```bash
qpdf --encrypt user_pass owner_pass 256 --print=none --modify=none -- input.pdf encrypted.pdf
qpdf --show-encryption encrypted.pdf
qpdf --password=secret123 --decrypt encrypted.pdf decrypted.pdf
```
---
## §Encrypted PDFs
```python
from pypdf import PdfReader
try:
reader = PdfReader("encrypted.pdf")
if reader.is_encrypted:
reader.decrypt("password")
except Exception as e:
print(f"Failed to decrypt: {e}")
```
Or via qpdf:
```bash
qpdf --password=secret123 --decrypt encrypted.pdf decrypted.pdf
```
---
## §Corrupted PDFs
```bash
qpdf --check corrupted.pdf
qpdf --replace-input corrupted.pdf
```
---
## §OCR Fallback for Scanned PDFs
When `pdfplumber` extracts 0 characters, the PDF is likely a scanned image:
```python
import pytesseract
from pdf2image import convert_from_path
def extract_text_with_ocr(pdf_path):
images = convert_from_path(pdf_path)
text = ""
for i, image in enumerate(images):
text += pytesseract.image_to_string(image)
return text
```
Prerequisites: `pip install pdf2image pytesseract` + install Tesseract OCR and poppler.
---
## §Batch Processing with Error Handling
```python
import os, glob
from pypdf import PdfReader, PdfWriter
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def batch_process_pdfs(input_dir, operation='merge'):
pdf_files = glob.glob(os.path.join(input_dir, "*.pdf"))
if operation == 'merge':
writer = PdfWriter()
for pdf_file in pdf_files:
try:
reader = PdfReader(pdf_file)
for page in reader.pages:
writer.add_page(page)
logger.info(f"Processed: {pdf_file}")
except Exception as e:
logger.error(f"Failed: {pdf_file}: {e}")
continue
with open("batch_merged.pdf", "wb") as output:
writer.write(output)
elif operation == 'extract_text':
for pdf_file in pdf_files:
try:
reader = PdfReader(pdf_file)
text = "".join(page.extract_text() for page in reader.pages)
output_file = pdf_file.replace('.pdf', '.txt')
with open(output_file, 'w', encoding='utf-8') as f:
f.write(text)
logger.info(f"Extracted: {pdf_file}")
except Exception as e:
logger.error(f"Failed: {pdf_file}: {e}")
```
---
## §Performance Optimization
### Text Extraction
- `pdftotext -bbox-layout` is fastest for plain text
- Use pdfplumber for structured data and tables
- Avoid `pypdf.extract_text()` for very large documents
### Image Extraction
- `pdfimages` is much faster than rendering entire pages
- Use low resolution for previews, high resolution for final output
### Memory Management for Large PDFs
```python
def process_large_pdf(pdf_path, chunk_size=10):
reader = PdfReader(pdf_path)
total_pages = len(reader.pages)
for start_idx in range(0, total_pages, chunk_size):
end_idx = min(start_idx + chunk_size, total_pages)
writer = PdfWriter()
for i in range(start_idx, end_idx):
writer.add_page(reader.pages[i])
with open(f"chunk_{start_idx//chunk_size}.pdf", "wb") as output:
writer.write(output)
```
---
## Extended Tooling Inventory
| Library / Tool | Role | Licence |
|----------------|------|---------|
| pikepdf | Low-level PDF manipulation (forms, pages, metadata) | MPL-2.0 |
| pdfplumber | Content extraction (text, tables) | MIT |
| pypdfium2 | Fast rendering, text extraction (PyMuPDF alternative) | Apache/BSD |
| pypdf | Merge, split, crop, metadata, encryption | BSD |
| poppler-utils | CLI text/image extraction, rendering | GPL-2 |
| qpdf | Page manipulation, optimization, encryption, repair | Apache |
| pytesseract | OCR for scanned PDFs | Apache |
| pdf2image | PDF-to-image conversion via poppler | MIT |
| LibreOffice | Office format conversion engine | MPL-2.0 |

319
skills/pdf/briefs/process.md Executable file
View File

@@ -0,0 +1,319 @@
# Brief: PDF Processing
Work with existing PDFs: extract, merge, split, fill forms, convert formats, or **reformat** with a new design. Usually a Light triage path — except reformat, which escalates to Standard.
---
## Decision Tree
```
User request
├─ "Extract text/tables/images" → §Extract
├─ "Merge/split/rotate/crop pages" → §Pages
├─ "Fill a form" → §Forms (check fillable first)
├─ "Read/write metadata" → §Metadata
├─ "Convert DOCX/PPTX/XLSX to PDF" → §Convert
│ └─ DOCX with TOC? → §DOCX Pipeline (5-step)
├─ "Redesign/reformat a document" → §Reformat
│ └─ With a reference template? → §Template-Guided Reformat
└─ Edge cases (OCR, encrypt, batch) → load briefs/process-advanced.md
```
---
## Environment Check
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" env.check
```
Reports availability but does **not** auto-install. Required: Python 3, pikepdf, pdfplumber.
Entry point: `python3 "$PDF_SKILL_DIR/scripts/pdf.py" <group>.<action> [options]`
All commands return JSON on stdout (`{"status": "success", "data": {...}}`) or stderr (`{"status": "error", ...}`).
Exit codes: 0 = success, 1 = bad args, 2 = file not found, 3 = parse error, 4 = operation failed.
---
## §Extract
```bash
# Text (full or page range)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.text report.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.text report.pdf -p 1-3
python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.text report.pdf -p 1,4,7
# Tables — returns structured JSON with page/rows/cols/data
python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.table report.pdf
# Images — dumps embedded rasters to directory
python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.image report.pdf -o ./images/
```
---
## §Pages
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.merge a.pdf b.pdf -o combined.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.split book.pdf -o ./chapters/
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.rotate doc.pdf 90 -o rotated.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.rotate doc.pdf 180 -o rotated.pdf -p 1-3
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.crop doc.pdf 50,50,550,750 -o trimmed.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.clean doc.pdf -o cleaned.pdf
```
---
## §Metadata
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" meta.get doc.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" meta.set doc.pdf -o out.pdf -d '{"Title": "Report", "Author": "Jane"}'
python3 "$PDF_SKILL_DIR/scripts/pdf.py" meta.brand doc.pdf -o branded.pdf
```
Recognised keys: `Title`, `Author`, `Subject`, `Keywords`, `Creator`, `Producer`.
`meta.brand` adds standard branding metadata (producer, creator) in one step.
---
## §Forms
### Step 1 — Check if fillable
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.info input.pdf
```
If `has_fields: true`**Fillable workflow**. If `false`**Non-fillable workflow**.
### Fillable Workflow
```bash
# Inspect fields
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.info input.pdf
# Fill (auto-maps "true"/"false" for checkboxes)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.fill input.pdf -o filled.pdf \
-d '{"name": "John", "agree": "true", "country": "US"}'
```
**Value rules:**
| Type | Value | Example |
|------|-------|---------|
| text | Free string | `"name": "Jane Doe"` |
| checkbox | `"true"` / `"false"` (auto-converts to PDF states) | `"agree": "true"` |
| radio | One of `radio_options[].value` | `"gender": "/Choice1"` |
| dropdown | One of `choice_options[].value` | `"country": "US"` |
For complex forms, use `form.detail` and `form.render` for deeper inspection:
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.detail input.pdf -o fields.json # full field info (types, options, defaults)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.render input.pdf -o ./pages/ # render pages as PNG for visual check
```
### Non-Fillable Workflow (Annotation-Based)
For PDFs without interactive fields (scanned forms, image-based). All four steps are mandatory.
**Step 1 — Render pages as PNG** (required):
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.render input.pdf -o ./pages/
```
**Step 2 — Create `fields.json`** with annotation regions.
To determine bbox coordinates: open the rendered PNG in an image viewer or use Python (`from PIL import Image; img = Image.open('page.png'); print(img.size)`) to get pixel dimensions. Then estimate [left, top, right, bottom] in pixels for each field by inspecting the image. The `dims` field must match the PNG dimensions exactly.
```json
{
"sheet": [
{
"pg": 1,
"dims": [1000, 1400],
"regions": [
{
"id": "last_name",
"hint": "Last name field",
"label": {"tag": "Last name", "bbox": [30, 125, 95, 142]},
"target": {"bbox": [100, 125, 280, 142]},
"ink": {"value": "Simpson", "size": 14, "color": "000000"}
}
]
}
]
}
```
Schema: `pg` = 1-based page, `dims` = [w,h] in pixels, `label.bbox` / `target.bbox` = [left, top, right, bottom], `ink` = {value, size?, color?, font?}. Label and target boxes must NOT intersect.
**Step 3 — Validate bounding boxes** (required):
```bash
# Auto-check for intersections
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.check-bbox fields.json
# Visual validation (red=target, blue=label)
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.validate 1 fields.json page1.png validation.png
```
Fix any issues, regenerate, re-check. Red rectangles must only cover input areas.
**Step 4 — Fill via annotations**:
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" form.annotate input.pdf fields.json -o filled.pdf
```
---
## §Reformat
Take an existing document and rebuild it with a new visual design. Content is preserved; layout, typography, and visual treatment are rebuilt from scratch.
```
1. EXTRACT → Extract content from source (extract.text / extract.table / read directly)
2. STRUCTURE → Organize into sections (headings, body, tables, lists)
3. DELEGATE → Route to appropriate brief:
Structured → briefs/report.md (ReportLab)
Visual → briefs/creative.md (Playwright)
4. BUILD → Follow the delegated brief's full workflow
5. DELIVER → New PDF, same content, new design
```
### §Template-Guided Reformat
When user provides a reference PDF to match:
```
1. ANALYZE → Extract design DNA from template:
- python3 "$PDF_SKILL_DIR/scripts/pdf.py" meta.get template.pdf (page size)
- python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.image template.pdf (color samples)
- python3 "$PDF_SKILL_DIR/scripts/pdf.py" extract.text template.pdf (text structure)
- pdftoppm -png -r 150 template.pdf preview (visual reference)
2. DOCUMENT → Record: page size, margins, colors, fonts, layout grid,
header/footer pattern, decorative elements
3. DELEGATE → Route to brief WITH design constraints (not brief defaults)
4. BUILD → Follow brief workflow, constrained to template DNA
5. COMPARE → pdftoppm both, visually compare side-by-side
```
**Key principles:**
- Match the spirit, not the pixels — exact replication from PDF is impractical
- Prefer original source files (.docx/.html/.tex) over PDF when available
- Declare font substitutions upfront; don't silently fall back
- Template provides design direction, not content — never leak placeholder text
---
## §Convert
### Office → PDF (LibreOffice)
**Simple conversion** (no TOC needed):
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.office input.docx -o output.pdf
```
**When to use the 5-step DOCX Pipeline instead**: If the DOCX has (or should have) a Table of Contents, always use §DOCX Pipeline below. Signs: the document has 3+ headings, or the user mentions "table of contents" / "TOC", or the document already contains a TOC section. When in doubt, run `python3 "$PDF_SKILL_DIR/scripts/toc_validate.py" fix-docx input.docx -o fixed.docx` — if it returns `no_toc_needed`, a simple conversion is fine.
Or directly:
```bash
soffice --headless --convert-to pdf --outdir ./output input.docx
```
**Supported**: `.docx`, `.doc`, `.odt`, `.rtf`, `.pptx`, `.ppt`, `.xlsx`, `.xls`, `.ods`, `.csv`, `.html`
**macOS path**: `/Applications/LibreOffice.app/Contents/MacOS/soffice`
**Gotchas:**
- soffice allows only one instance at a time; close existing LibreOffice windows or use `--env:UserInstallation=file:///tmp/libreoffice_tmp`
- Missing Chinese fonts → squares. Ensure SimHei/SimSun are installed.
- Large files (>50MB) may take 1-2 min; set reasonable timeout
- soffice HTML→PDF is inferior to Playwright for complex CSS
**Priority**: Always prefer soffice for Office→PDF (preserves themes, layouts, master slides). Only fall back to python-pptx/python-docx + HTML + Playwright if soffice is unavailable — fidelity will be lower.
### Fallback: Spreadsheet → PDF without LibreOffice
Use openpyxl + HTML + Playwright. Let data shape drive layout:
| Factor | Decision |
|--------|----------|
| Columns ≤ 6 | Portrait |
| Columns > 6 | Landscape |
| Font size | Scale inversely with column count |
| Styling | Follow user requirements or source file style; if unspecified, use defaults from `typesetting/palette.md` |
### §DOCX Pipeline (5-Step with TOC)
For DOCX files that need TOC generation/correction. Required because LibreOffice `--headless` does not recalculate PAGEREF fields.
```
Step 1: soffice → Convert original DOCX to PDF (pass1)
Step 2: pages.clean → Remove blank pages from pass1
Step 3: fix-docx → Add/fix TOC with HYPERLINK + PAGEREF + bookmarks
Step 4: fix-pages → Correct TOC page numbers using pass1 as reference
Step 5: soffice → Convert final DOCX to PDF + pages.clean
```
**Step 1 — Pass 1 Convert**:
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.office input.docx -o pass1.pdf
```
**Step 2 — Clean Blank Pages**:
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.clean pass1.pdf -o pass1_clean.pdf
```
If `blank_pages_removed == 0`, use pass1.pdf directly.
**Step 3 — Fix TOC**:
```bash
python3 "$PDF_SKILL_DIR/scripts/toc_validate.py" fix-docx input.docx -o fixed.docx
```
Auto-detects and fixes: placeholder TOC, stale TOC (>50% drift), empty TOC, missing TOC (≥3 headings). Each entry gets `<w:hyperlink>` + `PAGEREF` + bookmarks for clickable PDF navigation.
Check output `action` field: `fixed` → use fixed.docx, `skipped` → use original, `no_toc_needed` → skip to Step 5 with pass1 PDF.
**Step 4 — Fix Page Numbers**:
```bash
python3 "$PDF_SKILL_DIR/scripts/toc_validate.py" fix-pages fixed.docx pass1_clean.pdf -o final.docx
```
Corrects PAGEREF display text using actual page positions from pass1 + TOC page offset.
**Step 5 — Final Convert + Clean**:
```bash
python3 "$PDF_SKILL_DIR/scripts/pdf.py" convert.office final.docx -o output.pdf
python3 "$PDF_SKILL_DIR/scripts/pdf.py" pages.clean output.pdf -o output_clean.pdf
```
### Post-Conversion Validation (Optional)
```bash
python3 "$PDF_SKILL_DIR/scripts/toc_validate.py" check-conversion final.docx output_clean.pdf
```
Issues caught: `CONV_TOC_LOST` (TOC disappeared), `CONV_HINT_LEAKED` (placeholder text in PDF), `CONV_HEADING_DRIFT` (heading count mismatch).
---
## Caveats
| Topic | Detail |
|-------|--------|
| Encrypted PDFs | Not supported. User must decrypt externally first. |
| < 50 MB | Instant |
| 50200 MB | 12 minutes |
| > 200 MB | Split first, or extend timeout |
| Memory | ~2-3× input file size |
| Merge failure | Partial output may remain; delete and retry |
| Split failure | Some page files may exist; inspect output dir |
| Form fill | Original never modified; always writes new file |
For edge cases (OCR, batch processing, poppler-utils, qpdf, performance tuning), load `briefs/process-advanced.md`.

1659
skills/pdf/briefs/report.md Executable file

File diff suppressed because it is too large Load Diff