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

394
skills/ppt/html2pptx.md Executable file
View File

@@ -0,0 +1,394 @@
# HTML to PowerPoint Guide
Convert HTML slides into PowerPoint presentations using the `html2pptx.js` library with accurate positioning.
---
## Creating HTML Slides
Each HTML slide must include the correct body dimensions:
### Layout Dimensions
- **16:9** (default): `width: 720pt; height: 405pt`
- **4:3**: `width: 720pt; height: 540pt`
- **16:10**: `width: 720pt; height: 450pt`
**CRITICAL — Prevent Overflow**:
- `<body>` must set exact dimensions via inline style: `width: 720pt; height: 405pt; margin: 0; padding: 0; overflow: hidden;`
- All content must fit within these boundaries. If content overflows, split into multiple slides
- Maintain at least **36pt bottom margin** inside the body for safe spacing
**Fill the slide**: Content should occupy the available space well. Avoid designs where text clusters in the top third with the bottom two-thirds empty. Use generous font sizes, spacing, and visual elements to fill the canvas.
### Supported Elements
- `<p>`, `<h1>`-`<h6>` — Styled text
- `<ul>`, `<ol>` — Lists (never use manual bullet symbols like bullet, -, *)
- `<b>`, `<strong>`, `<i>`, `<em>`, `<u>` — Inline formatting
- `<span>` — Inline formatting with CSS styles
- `<br>` — Line breaks
- `<div>` with bg/border — Becomes shapes
- `<img>` — Images
- `class="placeholder"` — Reserves space for charts (returns `{ id, x, y, w, h }`)
### Key Text Rules
**All text must be inside `<p>`, `<h1>`-`<h6>`, `<ul>`, or `<ol>` tags:**
-`<div><p>Text here</p></div>`
-`<div>Text here</div>`**Silently ignored in PowerPoint**
-`<span>Text here</span>`**Silently ignored in PowerPoint**
**Never use manual bullet symbols (bullet, -, *, etc.)** — use `<ul>` or `<ol>` instead.
**v3 Smart Font Mapping (pass-through + safe fallback):**
html2pptx.js v3 no longer maps all fonts to the same one. Strategy:
1. **PPT-safe fonts** (Corbel, Arial, SimHei, Palatino Linotype, etc. 40+) → **Pass through directly**
2. **macOS-exclusive fonts** (PingFang SC, Hiragino Sans) → Mapped to cross-platform equivalents
3. **Web fonts** (Roboto, Montserrat, Inter, etc. 40+) → Mapped to visually closest PPT-safe font
4. **CSS generic names** (sans-serif/serif) → Corbel / Times New Roman
5. **Unknown fonts** → CJK falls back to fontConfig.cjk; Latin passes through directly
**Write PPT font names directly in HTML**:
```css
font-family: "SimHei", "Microsoft YaHei", sans-serif; /* CJK heading */
font-family: "Microsoft YaHei", sans-serif; /* CJK body */
font-family: "Gill Sans MT", "Century Gothic", sans-serif; /* English heading */
```
**fontConfig is still available** (as CJK/Latin ultimate fallback, optional):
```javascript
const fontConfig = { cjk: 'SimHei', latin: 'Gill Sans MT' };
const result = await html2pptx('slide.html', pptx, { fontConfig });
```
### Styles
- Body must use `display: flex; flex-direction: column;` — without it, multiple direct children stack horizontally
- **Do not use `flex-wrap`** — multi-row layouts must use separate flex containers (html2pptx renderer may lose wrapped content)
- `<span>` supports: `font-weight`, `font-style`, `text-decoration`, `color`, `font-size`, `letter-spacing`; color accepts `rgba()` for transparency
- `<span>` does NOT support: `margin`, `padding`
- `text-transform: uppercase / lowercase / capitalize` works on all text elements and `<span>`
- **Rotated text**: `transform: rotate(-30deg)` or `writing-mode: vertical-rl / vertical-lr`
- Use hex colors with `#` prefix in CSS; use `text-align` for alignment hints
### Shape Styles (DIV elements only)
Backgrounds, borders, and shadows **only work on `<div>`**, not on text elements.
- **Background**: `background-color` on `<div>` only
- **Border**: uniform (`border: 2px solid #333`) or partial (`border-left`, `border-right`, etc.)
- **Border radius**: `border-radius: 8pt` for rounded corners; `50%` for circle; percentages relative to smaller dimension
- **Box shadow**: outer shadows only — `box-shadow: 2px 2px 8px rgba(0,0,0,0.3)`; inset shadows ignored
### Typography Guidelines
Choose font sizes that create clear visual hierarchy. Refer to `design-system.md` for suggested ranges.
**Minimum font size**: Don't go below ~11pt for any text. Prefer ≥13pt for body text.
**Hierarchy principle**: Headings should be noticeably larger and bolder than body text.
### Spacing Guidelines
Use consistent spacing throughout the deck. Refer to `design-system.md` for suggested values. Key principles:
- Page margins: ~48pt left/right, ~40pt top, ≥36pt bottom safe zone
- Be generous with whitespace — but fill the slide; avoid large empty areas
### Color Rules
**All colors must come from the current theme's color scale.** Arbitrary grays unrelated to the primary color are forbidden.
After selecting a theme from `themes.md`, use that theme's complete color scale.
### Image Rules (MANDATORY for decks with 6+ slides)
Every deck with 6+ slides must include real photographs. Images create visual richness and professional quality.
**Image sourcing priority**:
1. **Unsplash** (free, high quality) — use theme's **Image Keywords** from `themes.md`:
```bash
curl -L "https://source.unsplash.com/1920x1080/?keyword1,keyword2" -o cover-bg.jpg
```
2. **User-provided images** — local files
3. **Gradient fallback** — if Unsplash fails, generate gradient PNG via Sharp
**Image usage in HTML**:
- **Page background**: `<body style="background-image:url('bg.jpg'); background-size:cover;">`
- **Inline image**: `<img src="photo.jpg" style="width:296pt; height:220pt; object-fit:cover;">`
- **DIV background-image is NOT supported** — only body background-image works
**Mask overlay for photo backgrounds**:
```html
<!-- Background photo on body -->
<body style="width:720pt; height:405pt; margin:0; padding:0; overflow:hidden;
background-image:url('cover-bg.jpg'); background-size:cover;
font-family:'PingFang SC','Microsoft YaHei',sans-serif;
display:flex; flex-direction:column;">
<!-- Semi-transparent mask layer (use theme's Mask Color from themes.md) -->
<div style="position:absolute; top:0; left:0; width:720pt; height:405pt;
background-color:rgba(18,32,64,0.75);"></div>
<!-- Content layer above mask -->
<div style="position:relative; z-index:1; flex:1; display:flex; flex-direction:column;
justify-content:center; align-items:center;">
<h1 style="font-size:34pt; font-weight:bold; color:#FFFFFF;">Title Here</h1>
</div>
</body>
```
**Mask opacity guide**:
- Dark mask (cover/divider): opacity 0.700.85 — text clearly readable
- Light mask (content): opacity 0.600.80 — retains image visibility
### Icons & Gradients
- **CRITICAL: Never use CSS gradients** — pre-rasterize as PNG with Sharp
- **Icons**: rasterize react-icons SVG to PNG; **Gradients**: rasterize SVG to PNG background
```javascript
// Icon to PNG
const { FaHome } = require('react-icons/fa');
const svgString = ReactDOMServer.renderToStaticMarkup(
React.createElement(FaHome, { color: '#4472C4', size: '256' })
);
await sharp(Buffer.from(svgString)).png().toFile('home-icon.png');
// In HTML: <img src="home-icon.png" style="width:40pt;height:40pt;">
// Gradient to PNG
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="562.5">
<defs><linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#COLOR1"/>
<stop offset="100%" style="stop-color:#COLOR2"/>
</linearGradient></defs>
<rect width="100%" height="100%" fill="url(#g)"/>
</svg>`;
await sharp(Buffer.from(svg)).png().toFile('gradient-bg.png');
// In HTML: <body style="background-image: url('gradient-bg.png');">
```
---
## Using the html2pptx Library
### Dependencies
Globally installed: `pptxgenjs`, `playwright`, `sharp`
### Basic Usage
```javascript
const pptxgen = require('pptxgenjs');
const html2pptx = require('./html2pptx');
const pptx = new pptxgen();
pptx.layout = 'LAYOUT_16x9'; // Must match HTML body dimensions
// Optional: custom font configuration (from themes.md)
const fontConfig = { cjk: 'Microsoft YaHei', latin: 'Gill Sans MT' };
const { slide, placeholders, warnings } = await html2pptx('slide1.html', pptx, { fontConfig });
// If warnings is non-empty, fix the HTML and re-run
if (warnings.length > 0) {
console.error('Fix overflow issues before saving:', warnings);
process.exit(1);
}
if (placeholders.length > 0) {
slide.addChart(pptx.charts.LINE, chartData, placeholders[0]);
}
await pptx.writeFile('output.pptx');
```
### API Reference
```javascript
await html2pptx(htmlFile, pres, options)
```
**Parameters:**
- `htmlFile` (string): Path to HTML file
- `pres` (pptxgen): PptxGenJS instance with layout set
- `options.tmpDir` (string): Temp dir (default: `process.env.TMPDIR || '/tmp'`)
- `options.slide` (object): Existing slide to reuse
- `options.fontConfig` (object): Font mapping config `{ cjk: 'Microsoft YaHei', latin: 'Corbel' }`
**Returns:**
```javascript
{
slide: pptxgenSlide,
placeholders: [{ id, x, y, w, h }, ...],
warnings: string[] // Overflow and layout suggestions; empty if none
}
```
### Validation
**Blocking errors** (conversion aborted):
1. CSS gradients — must be pre-rasterized as PNG
2. Backgrounds/borders/shadows on text elements (`<p>`, `<h1>`-`<h6>`, etc.)
3. Unwrapped text directly in `<div>`
4. Manual bullet symbols in text elements
5. Font size below 11pt
**Non-blocking warnings** (conversion succeeds, returned in `warnings`):
1. Element out of bounds — extends beyond slide edges
2. Vertical imbalance — content clusters in top 55%
3. Text overlap — text elements overlap each other
4. Character density — exceeds 350 CJK / 550 Latin chars
5. Body-level overflow
**Blocking issues** (overflow, font < 11pt) must be fixed. **Non-blocking warnings** are suggestions — use your judgment.
### Visual Quality Check
After generating all slides, do a quick quality scan:
- Is there visual variety across the deck? (different layouts, backgrounds, card styles)
- Do real photographs appear? (at least on cover + 1 other slide for 6+ slide decks)
- Is there at least one dramatic "rhythm breaker" page?
- Does every slide have a clear visual focal point?
### Working with Placeholders
```javascript
const { slide, placeholders } = await html2pptx('slide.html', pptx);
slide.addChart(pptx.charts.BAR, data, placeholders[0]);
// By ID:
const chartArea = placeholders.find(p => p.id === 'chart-area');
slide.addChart(pptx.charts.LINE, data, chartArea);
```
---
## Using PptxGenJS
### Critical Rules
**NEVER use `#` prefix** with hex colors in PptxGenJS — causes file corruption.
- ✅ `color: "FF0000"`, `fill: { color: "0066CC" }`
- ❌ `color: "#FF0000"`
### Adding Images
```javascript
const imgWidth = 1860, imgHeight = 1519;
const h = 3, w = h * (imgWidth / imgHeight);
slide.addImage({ path: "chart.png", x: (10 - w) / 2, y: 1.5, w, h });
```
### Adding Shapes
```javascript
slide.addShape(pptx.shapes.RECTANGLE, {
x: 1, y: 1, w: 3, h: 2,
fill: { color: "4472C4" },
line: { color: "2E5DA8", width: 2 },
rectRadius: 0.1 // rounded corners (ROUNDED_RECTANGLE only)
});
```
### Adding Charts
**Time Series Granularity:** `< 30 days` daily | `30-365 days` monthly | `> 365 days` yearly.
#### Bar Chart
```javascript
slide.addChart(pptx.charts.BAR, [{
name: "Sales 2024",
labels: ["Q1", "Q2", "Q3", "Q4"],
values: [4500, 5500, 6200, 7100]
}], {
...placeholders[0],
barDir: 'col',
showTitle: true, title: 'Quarterly Sales',
showLegend: false,
showCatAxisTitle: true, catAxisTitle: 'Quarter',
showValAxisTitle: true, valAxisTitle: 'Sales ($000s)',
valAxisMinVal: 0, valAxisMaxVal: 8000, valAxisMajorUnit: 2000,
chartColors: ["4472C4"]
});
```
#### Line Chart
```javascript
slide.addChart(pptx.charts.LINE, [{
name: "Temperature",
labels: ["Jan", "Feb", "Mar", "Apr"],
values: [32, 35, 42, 55]
}], {
x: 1, y: 1, w: 8, h: 4,
lineSize: 4, lineSmooth: true,
showCatAxisTitle: true, catAxisTitle: 'Month',
showValAxisTitle: true, valAxisTitle: 'Temp (F)',
valAxisMinVal: 0, valAxisMaxVal: 60, valAxisMajorUnit: 20,
chartColors: ["4472C4"]
});
```
#### Pie Chart
```javascript
slide.addChart(pptx.charts.PIE, [{
name: "Market Share",
labels: ["Product A", "Product B", "Other"],
values: [35, 45, 20]
}], {
x: 2, y: 1, w: 6, h: 4,
showPercent: true, showLegend: true, legendPos: 'r',
chartColors: ["4472C4", "ED7D31", "A5A5A5"]
});
```
#### Scatter Chart
```javascript
slide.addChart(pptx.charts.SCATTER, [
{ name: 'X-Axis', values: [10, 15, 20, 12, 18] },
{ name: 'Series 1', values: [20, 25, 30] },
{ name: 'Series 2', values: [18, 22] }
], {
x: 1, y: 1, w: 8, h: 4,
lineSize: 0, lineDataSymbol: 'circle', lineDataSymbolSize: 6,
showCatAxisTitle: true, catAxisTitle: 'X',
showValAxisTitle: true, valAxisTitle: 'Y',
chartColors: ["4472C4", "ED7D31"]
});
```
#### Multiple Series
```javascript
slide.addChart(pptx.charts.LINE, [
{ name: "Product A", labels: ["Q1","Q2","Q3","Q4"], values: [10,20,30,40] },
{ name: "Product B", labels: ["Q1","Q2","Q3","Q4"], values: [15,25,20,35] }
], { x: 1, y: 1, w: 8, h: 4, showCatAxisTitle: true, catAxisTitle: 'Quarter',
showValAxisTitle: true, valAxisTitle: 'Revenue ($M)' });
```
**Chart colors:** no `#` prefix; align with slide palette; strong contrast between adjacent series.
### Adding Tables
```javascript
const tableData = [
[
{ text: "Product", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } },
{ text: "Revenue", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } },
{ text: "Growth", options: { fill: { color: "4472C4" }, color: "FFFFFF", bold: true } }
],
["Product A", "$50M", "+15%"],
["Product B", "$35M", "+22%"]
];
slide.addTable(tableData, {
x: 1, y: 1.5, w: 8, h: 2.5,
colW: [3, 2.5, 2.5], rowH: [0.5, 0.6, 0.6],
border: { pt: 1, color: "CCCCCC" },
align: "center", valign: "middle", fontSize: 14
});
```
**Table options:** `x, y, w, h` | `colW` | `rowH` | `border: { pt, color }` | `fill` | `align` | `valign` | `fontSize` | `autoPage`