@hirokisakabe/pom 1.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -193
- package/dist/buildPptx.d.ts +2 -2
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +3 -1
- package/dist/calcYogaLayout/calcYogaLayout.d.ts.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js +95 -19
- package/dist/calcYogaLayout/fontLoader.d.ts +0 -7
- package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -1
- package/dist/calcYogaLayout/fontLoader.js +1 -1
- package/dist/calcYogaLayout/measureText.d.ts +0 -4
- package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
- package/dist/calcYogaLayout/measureText.js +0 -6
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/{inputSchema.d.ts → parseXml/inputSchema.d.ts} +254 -460
- package/dist/parseXml/inputSchema.d.ts.map +1 -0
- package/dist/{inputSchema.js → parseXml/inputSchema.js} +42 -140
- package/dist/{parseXml.d.ts → parseXml/parseXml.d.ts} +6 -2
- package/dist/parseXml/parseXml.d.ts.map +1 -0
- package/dist/parseXml/parseXml.js +823 -0
- package/dist/renderPptx/nodes/flow.d.ts.map +1 -1
- package/dist/renderPptx/nodes/flow.js +26 -16
- package/dist/renderPptx/nodes/index.d.ts +1 -0
- package/dist/renderPptx/nodes/index.d.ts.map +1 -1
- package/dist/renderPptx/nodes/index.js +1 -0
- package/dist/renderPptx/nodes/list.d.ts +12 -0
- package/dist/renderPptx/nodes/list.d.ts.map +1 -0
- package/dist/renderPptx/nodes/list.js +145 -0
- package/dist/renderPptx/nodes/matrix.d.ts.map +1 -1
- package/dist/renderPptx/nodes/matrix.js +43 -31
- package/dist/renderPptx/nodes/processArrow.d.ts.map +1 -1
- package/dist/renderPptx/nodes/processArrow.js +14 -6
- package/dist/renderPptx/nodes/shape.js +2 -2
- package/dist/renderPptx/nodes/table.d.ts.map +1 -1
- package/dist/renderPptx/nodes/table.js +1 -1
- package/dist/renderPptx/nodes/timeline.d.ts.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +58 -37
- package/dist/renderPptx/nodes/tree.d.ts.map +1 -1
- package/dist/renderPptx/nodes/tree.js +49 -40
- package/dist/renderPptx/renderPptx.d.ts +1 -2
- package/dist/renderPptx/renderPptx.d.ts.map +1 -1
- package/dist/renderPptx/renderPptx.js +8 -4
- package/dist/renderPptx/textOptions.d.ts +1 -29
- package/dist/renderPptx/textOptions.d.ts.map +1 -1
- package/dist/renderPptx/textOptions.js +1 -27
- package/dist/renderPptx/types.d.ts +0 -2
- package/dist/renderPptx/types.d.ts.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +1 -1
- package/dist/renderPptx/utils/scaleToFit.d.ts +8 -0
- package/dist/renderPptx/utils/scaleToFit.d.ts.map +1 -0
- package/dist/renderPptx/utils/scaleToFit.js +19 -0
- package/dist/shared/measureImage.d.ts.map +1 -0
- package/dist/{table/utils.d.ts → shared/tableUtils.d.ts} +1 -3
- package/dist/shared/tableUtils.d.ts.map +1 -0
- package/dist/{table/utils.js → shared/tableUtils.js} +2 -2
- package/dist/toPositioned/toPositioned.d.ts.map +1 -1
- package/dist/toPositioned/toPositioned.js +4 -2
- package/dist/types.d.ts +271 -154
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +67 -33
- package/package.json +3 -6
- package/dist/calcYogaLayout/measureImage.d.ts.map +0 -1
- package/dist/component.d.ts +0 -90
- package/dist/component.d.ts.map +0 -1
- package/dist/component.js +0 -123
- package/dist/inputSchema.d.ts.map +0 -1
- package/dist/parseXml.d.ts.map +0 -1
- package/dist/parseXml.js +0 -332
- package/dist/renderPptx/nodes/box.d.ts +0 -2
- package/dist/renderPptx/nodes/box.d.ts.map +0 -1
- package/dist/renderPptx/nodes/box.js +0 -3
- package/dist/renderPptx/utils/index.d.ts +0 -6
- package/dist/renderPptx/utils/index.d.ts.map +0 -1
- package/dist/renderPptx/utils/index.js +0 -3
- package/dist/renderPptx/utils/shapeDrawing.d.ts +0 -27
- package/dist/renderPptx/utils/shapeDrawing.d.ts.map +0 -1
- package/dist/renderPptx/utils/shapeDrawing.js +0 -36
- package/dist/renderPptx/utils/textDrawing.d.ts +0 -25
- package/dist/renderPptx/utils/textDrawing.d.ts.map +0 -1
- package/dist/renderPptx/utils/textDrawing.js +0 -25
- package/dist/schema.d.ts +0 -23
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -24
- package/dist/table/utils.d.ts.map +0 -1
- /package/dist/{calcYogaLayout → shared}/measureImage.d.ts +0 -0
- /package/dist/{calcYogaLayout → shared}/measureImage.js +0 -0
package/README.md
CHANGED
|
@@ -1,233 +1,198 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">pom</h1>
|
|
2
|
+
<p align="center">
|
|
3
|
+
Declarative PowerPoint generation from XML — built for AI
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/@hirokisakabe/pom"><img src="https://img.shields.io/npm/v/@hirokisakabe/pom.svg" alt="npm version"></a>
|
|
8
|
+
<a href="https://github.com/hirokisakabe/pom/actions/workflows/ci.yml"><img src="https://github.com/hirokisakabe/pom/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://github.com/hirokisakabe/pom/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@hirokisakabe/pom.svg" alt="License"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<b>pom (PowerPoint Object Model)</b> is a TypeScript library that converts XML into PowerPoint files (.pptx).<br>
|
|
14
|
+
Flexbox-style layout powered by <a href="https://www.yogalayout.dev/">yoga-layout</a>, rendered with <a href="https://github.com/nicktomlin/pptxgenjs">pptxgenjs</a>.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://pom-playground.vercel.app/"><b>Try it online — Playground</b></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://pom-playground.vercel.app/">
|
|
23
|
+
<img src="./docs/images/playground.png" alt="Playground" width="800">
|
|
24
|
+
</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
- [Features](#features)
|
|
32
|
+
- [Quick Start](#quick-start)
|
|
33
|
+
- [Available Nodes](#available-nodes)
|
|
34
|
+
- [Node Examples](#node-examples)
|
|
35
|
+
- [Documentation](#documentation)
|
|
36
|
+
- [License](#license)
|
|
2
37
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Requirements
|
|
38
|
+
## Features
|
|
6
39
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
40
|
+
- **AI Friendly** — Simple XML structure designed for LLM code generation. Pair with [LLM Integration guide](./docs/llm-integration.md) for prompt-ready references.
|
|
41
|
+
- **Declarative** — Describe slides as XML. No imperative API calls needed.
|
|
42
|
+
- **Flexible Layout** — Flexbox-style layout with VStack / HStack / Box, powered by yoga-layout.
|
|
43
|
+
- **Rich Nodes** — 15 built-in node types: charts, flowcharts, tables, timelines, org trees, and more.
|
|
44
|
+
- **Schema-validated** — XML input is validated with Zod schemas at runtime with clear error messages.
|
|
45
|
+
- **PowerPoint Native** — Full access to native PowerPoint shape features (roundRect, ellipse, arrows, etc.).
|
|
46
|
+
- **Pixel Units** — Intuitive pixel-based sizing (internally converted to inches at 96 DPI).
|
|
47
|
+
- **Master Slide** — Define headers, footers, and page numbers once — applied to all slides automatically.
|
|
48
|
+
- **Accurate Text Measurement** — Text width measured with opentype.js and bundled Noto Sans JP fonts for consistent layout.
|
|
9
49
|
|
|
10
|
-
|
|
11
|
-
> pom works in both Node.js and browser environments. Text measurement uses opentype.js with bundled Noto Sans JP fonts, ensuring consistent layout across all environments.
|
|
50
|
+
## Quick Start
|
|
12
51
|
|
|
13
|
-
|
|
52
|
+
> Requires Node.js 18+
|
|
14
53
|
|
|
15
54
|
```bash
|
|
16
55
|
npm install @hirokisakabe/pom
|
|
17
56
|
```
|
|
18
57
|
|
|
19
|
-
## Quick Start
|
|
20
|
-
|
|
21
58
|
```typescript
|
|
22
|
-
import { buildPptx
|
|
23
|
-
|
|
24
|
-
const slide: POMNode = {
|
|
25
|
-
type: "vstack",
|
|
26
|
-
w: "100%",
|
|
27
|
-
h: "max",
|
|
28
|
-
padding: 48,
|
|
29
|
-
gap: 24,
|
|
30
|
-
alignItems: "start",
|
|
31
|
-
children: [
|
|
32
|
-
{
|
|
33
|
-
type: "text",
|
|
34
|
-
text: "Presentation Title",
|
|
35
|
-
fontPx: 48,
|
|
36
|
-
bold: true,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: "text",
|
|
40
|
-
text: "Subtitle",
|
|
41
|
-
fontPx: 24,
|
|
42
|
-
color: "666666",
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const pptx = await buildPptx([slide], { w: 1280, h: 720 });
|
|
48
|
-
await pptx.writeFile({ fileName: "presentation.pptx" });
|
|
49
|
-
```
|
|
59
|
+
import { buildPptx } from "@hirokisakabe/pom";
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
const xml = `
|
|
62
|
+
<VStack w="100%" h="max" padding="48" gap="24" alignItems="start">
|
|
63
|
+
<Text fontPx="48" bold="true">Presentation Title</Text>
|
|
64
|
+
<Text fontPx="24" color="666666">Subtitle</Text>
|
|
65
|
+
</VStack>
|
|
66
|
+
`;
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- **Flexible Layout**: Automatic layout with VStack/HStack/Box
|
|
57
|
-
- **Pixel Units**: Intuitive pixel-based sizing (internally converted to inches)
|
|
58
|
-
- **Master Slide**: Automatically insert common headers, footers, and page numbers across all pages
|
|
59
|
-
- **AI Friendly**: Simple structure that makes it easy for LLMs to generate code
|
|
68
|
+
const pptx = await buildPptx(xml, { w: 1280, h: 720 });
|
|
69
|
+
await pptx.writeFile({ fileName: "presentation.pptx" });
|
|
70
|
+
```
|
|
60
71
|
|
|
61
72
|
## Available Nodes
|
|
62
73
|
|
|
63
|
-
| Node | Description
|
|
64
|
-
| ------------ |
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
74
|
+
| Node | Description |
|
|
75
|
+
| ------------ | ---------------------------------------------- |
|
|
76
|
+
| Text | Text with font styling and decoration |
|
|
77
|
+
| Ul | Unordered (bullet) list with Li items |
|
|
78
|
+
| Ol | Ordered (numbered) list with Li items |
|
|
79
|
+
| Image | Images from file path, URL, or base64 |
|
|
80
|
+
| Table | Tables with customizable columns and rows |
|
|
81
|
+
| Shape | PowerPoint shapes (roundRect, ellipse, etc.) |
|
|
82
|
+
| Chart | Charts (bar, line, pie, area, doughnut, radar) |
|
|
83
|
+
| Timeline | Timeline / roadmap visualizations |
|
|
84
|
+
| Matrix | 2x2 positioning maps |
|
|
85
|
+
| Tree | Organization charts and decision trees |
|
|
86
|
+
| Flow | Flowcharts with nodes and edges |
|
|
87
|
+
| ProcessArrow | Chevron-style process diagrams |
|
|
88
|
+
| Line | Horizontal / vertical lines |
|
|
89
|
+
| Layer | Absolute-positioned overlay container |
|
|
90
|
+
| Box | Container for single child with padding |
|
|
91
|
+
| VStack | Vertical stack layout |
|
|
92
|
+
| HStack | Horizontal stack layout |
|
|
78
93
|
|
|
79
94
|
For detailed node documentation, see [Nodes Reference](./docs/nodes.md).
|
|
80
95
|
|
|
81
|
-
##
|
|
96
|
+
## Node Examples
|
|
82
97
|
|
|
83
|
-
|
|
98
|
+
### Chart
|
|
84
99
|
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
theme?: Partial<Theme>; // Theme override
|
|
93
|
-
}>((props) => {
|
|
94
|
-
const t = mergeTheme(props.theme);
|
|
95
|
-
return {
|
|
96
|
-
type: "box",
|
|
97
|
-
padding: t.spacing.md,
|
|
98
|
-
backgroundColor: "FFFFFF",
|
|
99
|
-
border: { color: t.colors.border, width: 1 },
|
|
100
|
-
borderRadius: 8,
|
|
101
|
-
children: {
|
|
102
|
-
type: "vstack",
|
|
103
|
-
gap: t.spacing.sm,
|
|
104
|
-
children: [
|
|
105
|
-
{
|
|
106
|
-
type: "text",
|
|
107
|
-
text: props.title,
|
|
108
|
-
fontPx: t.fontPx.heading,
|
|
109
|
-
bold: true,
|
|
110
|
-
},
|
|
111
|
-
props.content,
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Use the component
|
|
118
|
-
const slide: POMNode = {
|
|
119
|
-
type: "vstack",
|
|
120
|
-
w: 1280,
|
|
121
|
-
h: 720,
|
|
122
|
-
padding: 48,
|
|
123
|
-
gap: 24,
|
|
124
|
-
children: [
|
|
125
|
-
SectionCard({
|
|
126
|
-
title: "Revenue",
|
|
127
|
-
content: { type: "text", text: "$1,000,000" },
|
|
128
|
-
}),
|
|
129
|
-
SectionCard({
|
|
130
|
-
title: "Custom Theme",
|
|
131
|
-
content: { type: "text", text: "Styled card" },
|
|
132
|
-
theme: { colors: { border: "CBD5E1" } },
|
|
133
|
-
}),
|
|
134
|
-
],
|
|
135
|
-
};
|
|
100
|
+
```xml
|
|
101
|
+
<Chart chartType="bar" w="350" h="250" showTitle="true" title="Bar Chart" showLegend="true">
|
|
102
|
+
<Series name="Q1">
|
|
103
|
+
<DataPoint label="Jan" value="30" />
|
|
104
|
+
<DataPoint label="Feb" value="45" />
|
|
105
|
+
</Series>
|
|
106
|
+
</Chart>
|
|
136
107
|
```
|
|
137
108
|
|
|
138
|
-
|
|
109
|
+
<img src="./docs/images/chart.png" alt="Chart example" width="600">
|
|
139
110
|
|
|
140
|
-
|
|
141
|
-
- **Theme**: Use `mergeTheme()` for colors, spacing, and font size overrides
|
|
142
|
-
- **Nesting**: Components can call other components
|
|
143
|
-
- **JSON / LLM**: Use `expandComponents()` to resolve component references in JSON
|
|
111
|
+
### Flow
|
|
144
112
|
|
|
145
|
-
|
|
113
|
+
```xml
|
|
114
|
+
<Flow direction="horizontal" w="100%" h="300">
|
|
115
|
+
<FlowNode id="start" shape="flowChartTerminator" text="Start" color="16A34A" />
|
|
116
|
+
<FlowNode id="process" shape="flowChartProcess" text="Process" color="1D4ED8" />
|
|
117
|
+
<FlowNode id="end" shape="flowChartTerminator" text="End" color="DC2626" />
|
|
118
|
+
<Connection from="start" to="process" />
|
|
119
|
+
<Connection from="process" to="end" />
|
|
120
|
+
</Flow>
|
|
121
|
+
```
|
|
146
122
|
|
|
147
|
-
|
|
123
|
+
<img src="./docs/images/flow.png" alt="Flow example" width="600">
|
|
124
|
+
|
|
125
|
+
### Tree
|
|
126
|
+
|
|
127
|
+
```xml
|
|
128
|
+
<Tree layout="vertical" nodeShape="roundRect" w="100%" h="400">
|
|
129
|
+
<TreeItem label="CEO" color="0F172A">
|
|
130
|
+
<TreeItem label="CTO" color="1D4ED8">
|
|
131
|
+
<TreeItem label="Dev Team" color="0EA5E9" />
|
|
132
|
+
</TreeItem>
|
|
133
|
+
<TreeItem label="CFO" color="16A34A">
|
|
134
|
+
<TreeItem label="Finance" color="0EA5E9" />
|
|
135
|
+
</TreeItem>
|
|
136
|
+
</TreeItem>
|
|
137
|
+
</Tree>
|
|
138
|
+
```
|
|
148
139
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
props.content,
|
|
167
|
-
],
|
|
168
|
-
},
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const registry = { SectionCard };
|
|
173
|
-
|
|
174
|
-
// LLM output (JSON with component references)
|
|
175
|
-
const llmOutput = [
|
|
176
|
-
{
|
|
177
|
-
type: "vstack",
|
|
178
|
-
w: 1280,
|
|
179
|
-
h: 720,
|
|
180
|
-
children: [
|
|
181
|
-
{
|
|
182
|
-
type: "component",
|
|
183
|
-
name: "SectionCard",
|
|
184
|
-
props: { title: "KPI", content: { type: "text", text: "$1M" } },
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
];
|
|
189
|
-
|
|
190
|
-
// Expand → Build
|
|
191
|
-
const slides = expandComponentSlides(llmOutput, registry);
|
|
192
|
-
const pptx = await buildPptx(slides, { w: 1280, h: 720 });
|
|
140
|
+
<img src="./docs/images/tree.png" alt="Tree example" width="600">
|
|
141
|
+
|
|
142
|
+
### Table
|
|
143
|
+
|
|
144
|
+
```xml
|
|
145
|
+
<Table defaultRowHeight="36">
|
|
146
|
+
<Column width="80" />
|
|
147
|
+
<Column width="200" />
|
|
148
|
+
<Row>
|
|
149
|
+
<Cell bold="true" backgroundColor="0F172A" color="FFFFFF">ID</Cell>
|
|
150
|
+
<Cell bold="true" backgroundColor="0F172A" color="FFFFFF">Name</Cell>
|
|
151
|
+
</Row>
|
|
152
|
+
<Row>
|
|
153
|
+
<Cell>001</Cell>
|
|
154
|
+
<Cell>Project Alpha</Cell>
|
|
155
|
+
</Row>
|
|
156
|
+
</Table>
|
|
193
157
|
```
|
|
194
158
|
|
|
195
|
-
|
|
159
|
+
<img src="./docs/images/table.png" alt="Table example" width="600">
|
|
196
160
|
|
|
197
|
-
|
|
161
|
+
### Timeline
|
|
198
162
|
|
|
199
|
-
```
|
|
200
|
-
|
|
163
|
+
```xml
|
|
164
|
+
<Timeline direction="horizontal" w="100%" h="200">
|
|
165
|
+
<TimelineItem date="2024 Q1" title="Phase 1" description="Planning" color="1D4ED8" />
|
|
166
|
+
<TimelineItem date="2024 Q2" title="Phase 2" description="Development" color="16A34A" />
|
|
167
|
+
<TimelineItem date="2024 Q3" title="Phase 3" description="Testing" color="0EA5E9" />
|
|
168
|
+
</Timeline>
|
|
169
|
+
```
|
|
201
170
|
|
|
202
|
-
|
|
203
|
-
<VStack gap="16" padding="32">
|
|
204
|
-
<Text fontPx="32" bold="true">売上レポート</Text>
|
|
205
|
-
<HStack gap="16">
|
|
206
|
-
<Chart chartType="bar" w="400" h="300"
|
|
207
|
-
data='[{ "name": "Q1", "labels": ["1月","2月","3月"], "values": [100,120,90] }]'
|
|
208
|
-
/>
|
|
209
|
-
<Text fontPx="18" color="00AA00">前年比 +15%</Text>
|
|
210
|
-
</HStack>
|
|
211
|
-
</VStack>
|
|
212
|
-
`;
|
|
171
|
+
<img src="./docs/images/timeline.png" alt="Timeline example" width="600">
|
|
213
172
|
|
|
214
|
-
|
|
215
|
-
const pptx = await buildPptx(nodes, { w: 1280, h: 720 });
|
|
216
|
-
```
|
|
173
|
+
### ProcessArrow
|
|
217
174
|
|
|
218
|
-
|
|
175
|
+
```xml
|
|
176
|
+
<ProcessArrow direction="horizontal" w="100%" h="100">
|
|
177
|
+
<Step label="Planning" color="4472C4" />
|
|
178
|
+
<Step label="Design" color="5B9BD5" />
|
|
179
|
+
<Step label="Development" color="70AD47" />
|
|
180
|
+
<Step label="Testing" color="FFC000" />
|
|
181
|
+
<Step label="Release" color="ED7D31" />
|
|
182
|
+
</ProcessArrow>
|
|
183
|
+
```
|
|
219
184
|
|
|
220
|
-
|
|
185
|
+
<img src="./docs/images/processArrow.png" alt="ProcessArrow example" width="600">
|
|
221
186
|
|
|
222
187
|
## Documentation
|
|
223
188
|
|
|
224
|
-
| Document
|
|
225
|
-
|
|
|
226
|
-
| [Nodes Reference](./docs/nodes.md)
|
|
227
|
-
| [Master Slide](./docs/master-slide.md)
|
|
228
|
-
| [Serverless Environments](./docs/serverless.md)
|
|
229
|
-
| [LLM Integration](./docs/llm-integration.md)
|
|
230
|
-
| [
|
|
189
|
+
| Document | Description |
|
|
190
|
+
| ------------------------------------------------ | --------------------------------------- |
|
|
191
|
+
| [Nodes Reference](./docs/nodes.md) | Complete reference for all node types |
|
|
192
|
+
| [Master Slide](./docs/master-slide.md) | Headers, footers, and page numbers |
|
|
193
|
+
| [Serverless Environments](./docs/serverless.md) | Text measurement options for serverless |
|
|
194
|
+
| [LLM Integration](./docs/llm-integration.md) | Compact XML reference for LLM prompts |
|
|
195
|
+
| [Playground](https://pom-playground.vercel.app/) | Try pom XML in the browser |
|
|
231
196
|
|
|
232
197
|
## License
|
|
233
198
|
|
package/dist/buildPptx.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TextMeasurementMode } from "./calcYogaLayout/measureText.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { SlideMasterOptions } from "./types.ts";
|
|
3
3
|
export type { TextMeasurementMode };
|
|
4
|
-
export declare function buildPptx(
|
|
4
|
+
export declare function buildPptx(xml: string, slideSize: {
|
|
5
5
|
w: number;
|
|
6
6
|
h: number;
|
|
7
7
|
}, options?: {
|
package/dist/buildPptx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;AAIzC,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,wCAqBF"}
|
package/dist/buildPptx.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout.js";
|
|
2
2
|
import { setTextMeasurementMode, } from "./calcYogaLayout/measureText.js";
|
|
3
|
+
import { parseXml } from "./parseXml/parseXml.js";
|
|
3
4
|
import { renderPptx } from "./renderPptx/renderPptx.js";
|
|
4
5
|
import { toPositioned } from "./toPositioned/toPositioned.js";
|
|
5
|
-
export async function buildPptx(
|
|
6
|
+
export async function buildPptx(xml, slideSize, options) {
|
|
6
7
|
// テキスト計測モードを設定(デフォルトは auto)
|
|
7
8
|
if (options?.textMeasurement) {
|
|
8
9
|
setTextMeasurementMode(options.textMeasurement);
|
|
@@ -10,6 +11,7 @@ export async function buildPptx(nodes, slideSize, options) {
|
|
|
10
11
|
else {
|
|
11
12
|
setTextMeasurementMode("auto");
|
|
12
13
|
}
|
|
14
|
+
const nodes = parseXml(xml);
|
|
13
15
|
const positionedPages = [];
|
|
14
16
|
for (const node of nodes) {
|
|
15
17
|
await calcYogaLayout(node, slideSize);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,aAAa,CAAC;AA6BnE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,iBAiBpC"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { loadYoga } from "yoga-layout/load";
|
|
2
2
|
import { measureText } from "./measureText.js";
|
|
3
|
-
import { measureImage, prefetchImageSize } from "
|
|
4
|
-
import { calcTableIntrinsicSize } from "../
|
|
3
|
+
import { measureImage, prefetchImageSize } from "../shared/measureImage.js";
|
|
4
|
+
import { calcTableIntrinsicSize } from "../shared/tableUtils.js";
|
|
5
5
|
import { measureProcessArrow, measureTimeline, measureMatrix, measureTree, measureFlow, } from "./measureCompositeNodes.js";
|
|
6
|
+
/**
|
|
7
|
+
* コンポジットノードの最小スケール閾値。
|
|
8
|
+
* renderPptx/utils/scaleToFit.ts の MIN_SCALE_THRESHOLD と同じ値を維持すること。
|
|
9
|
+
*/
|
|
10
|
+
const MIN_SCALE_THRESHOLD = 0.5;
|
|
11
|
+
/** 制約付きサイズを閾値でクランプする */
|
|
12
|
+
function constrainWithMinScale(intrinsicSize, availableSize) {
|
|
13
|
+
const minSize = intrinsicSize * MIN_SCALE_THRESHOLD;
|
|
14
|
+
return Math.max(minSize, Math.min(intrinsicSize, availableSize));
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* POMNode ツリーを Yoga でレイアウト計算する
|
|
8
18
|
* POMNode ツリーの各ノードに yogaNode プロパティがセットされる
|
|
@@ -232,6 +242,37 @@ async function applyStyleToYogaNode(node, yn) {
|
|
|
232
242
|
});
|
|
233
243
|
}
|
|
234
244
|
break;
|
|
245
|
+
case "ul":
|
|
246
|
+
case "ol":
|
|
247
|
+
{
|
|
248
|
+
const combinedText = node.items.map((item) => item.text).join("\n");
|
|
249
|
+
const fontSizePx = node.fontPx ?? 24;
|
|
250
|
+
const fontFamily = "Noto Sans JP";
|
|
251
|
+
const fontWeight = node.bold ? "bold" : "normal";
|
|
252
|
+
const lineHeight = node.lineSpacingMultiple ?? 1.3;
|
|
253
|
+
yn.setMeasureFunc((width, widthMode) => {
|
|
254
|
+
const maxWidthPx = (() => {
|
|
255
|
+
switch (widthMode) {
|
|
256
|
+
case yoga.MEASURE_MODE_UNDEFINED:
|
|
257
|
+
return Number.POSITIVE_INFINITY;
|
|
258
|
+
case yoga.MEASURE_MODE_EXACTLY:
|
|
259
|
+
case yoga.MEASURE_MODE_AT_MOST:
|
|
260
|
+
return width;
|
|
261
|
+
}
|
|
262
|
+
})();
|
|
263
|
+
const { widthPx, heightPx } = measureText(combinedText, maxWidthPx, {
|
|
264
|
+
fontFamily,
|
|
265
|
+
fontSizePx,
|
|
266
|
+
lineHeight,
|
|
267
|
+
fontWeight,
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
width: widthPx,
|
|
271
|
+
height: heightPx,
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
235
276
|
case "image":
|
|
236
277
|
{
|
|
237
278
|
const src = node.src;
|
|
@@ -262,9 +303,9 @@ async function applyStyleToYogaNode(node, yn) {
|
|
|
262
303
|
// テキストがある場合、テキストサイズを測定
|
|
263
304
|
const text = node.text;
|
|
264
305
|
const fontSizePx = node.fontPx ?? 24;
|
|
265
|
-
const fontFamily = "Noto Sans JP";
|
|
306
|
+
const fontFamily = node.fontFamily ?? "Noto Sans JP";
|
|
266
307
|
const fontWeight = node.bold ? "bold" : "normal";
|
|
267
|
-
const lineHeight = 1.3;
|
|
308
|
+
const lineHeight = node.lineSpacingMultiple ?? 1.3;
|
|
268
309
|
yn.setMeasureFunc((width, widthMode) => {
|
|
269
310
|
const maxWidthPx = (() => {
|
|
270
311
|
switch (widthMode) {
|
|
@@ -292,41 +333,76 @@ async function applyStyleToYogaNode(node, yn) {
|
|
|
292
333
|
break;
|
|
293
334
|
case "processArrow":
|
|
294
335
|
{
|
|
295
|
-
yn.setMeasureFunc(() => {
|
|
296
|
-
const
|
|
297
|
-
return {
|
|
336
|
+
yn.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
337
|
+
const intrinsic = measureProcessArrow(node);
|
|
338
|
+
return {
|
|
339
|
+
width: widthMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
340
|
+
? constrainWithMinScale(intrinsic.width, width)
|
|
341
|
+
: intrinsic.width,
|
|
342
|
+
height: heightMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
343
|
+
? constrainWithMinScale(intrinsic.height, height)
|
|
344
|
+
: intrinsic.height,
|
|
345
|
+
};
|
|
298
346
|
});
|
|
299
347
|
}
|
|
300
348
|
break;
|
|
301
349
|
case "timeline":
|
|
302
350
|
{
|
|
303
|
-
yn.setMeasureFunc(() => {
|
|
304
|
-
const
|
|
305
|
-
return {
|
|
351
|
+
yn.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
352
|
+
const intrinsic = measureTimeline(node);
|
|
353
|
+
return {
|
|
354
|
+
width: widthMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
355
|
+
? constrainWithMinScale(intrinsic.width, width)
|
|
356
|
+
: intrinsic.width,
|
|
357
|
+
height: heightMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
358
|
+
? constrainWithMinScale(intrinsic.height, height)
|
|
359
|
+
: intrinsic.height,
|
|
360
|
+
};
|
|
306
361
|
});
|
|
307
362
|
}
|
|
308
363
|
break;
|
|
309
364
|
case "matrix":
|
|
310
365
|
{
|
|
311
|
-
yn.setMeasureFunc(() => {
|
|
312
|
-
const
|
|
313
|
-
return {
|
|
366
|
+
yn.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
367
|
+
const intrinsic = measureMatrix(node);
|
|
368
|
+
return {
|
|
369
|
+
width: widthMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
370
|
+
? constrainWithMinScale(intrinsic.width, width)
|
|
371
|
+
: intrinsic.width,
|
|
372
|
+
height: heightMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
373
|
+
? constrainWithMinScale(intrinsic.height, height)
|
|
374
|
+
: intrinsic.height,
|
|
375
|
+
};
|
|
314
376
|
});
|
|
315
377
|
}
|
|
316
378
|
break;
|
|
317
379
|
case "tree":
|
|
318
380
|
{
|
|
319
|
-
yn.setMeasureFunc(() => {
|
|
320
|
-
const
|
|
321
|
-
return {
|
|
381
|
+
yn.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
382
|
+
const intrinsic = measureTree(node);
|
|
383
|
+
return {
|
|
384
|
+
width: widthMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
385
|
+
? constrainWithMinScale(intrinsic.width, width)
|
|
386
|
+
: intrinsic.width,
|
|
387
|
+
height: heightMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
388
|
+
? constrainWithMinScale(intrinsic.height, height)
|
|
389
|
+
: intrinsic.height,
|
|
390
|
+
};
|
|
322
391
|
});
|
|
323
392
|
}
|
|
324
393
|
break;
|
|
325
394
|
case "flow":
|
|
326
395
|
{
|
|
327
|
-
yn.setMeasureFunc(() => {
|
|
328
|
-
const
|
|
329
|
-
return {
|
|
396
|
+
yn.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
397
|
+
const intrinsic = measureFlow(node);
|
|
398
|
+
return {
|
|
399
|
+
width: widthMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
400
|
+
? constrainWithMinScale(intrinsic.width, width)
|
|
401
|
+
: intrinsic.width,
|
|
402
|
+
height: heightMode !== yoga.MEASURE_MODE_UNDEFINED
|
|
403
|
+
? constrainWithMinScale(intrinsic.height, height)
|
|
404
|
+
: intrinsic.height,
|
|
405
|
+
};
|
|
330
406
|
});
|
|
331
407
|
}
|
|
332
408
|
break;
|
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
* opentype.js を使用したフォント読み込みモジュール
|
|
3
3
|
* Node.js とブラウザ両方で動作する
|
|
4
4
|
*/
|
|
5
|
-
import { Font } from "opentype.js";
|
|
6
|
-
/**
|
|
7
|
-
* フォントを取得する(キャッシュ付き)
|
|
8
|
-
* @param weight フォントウェイト ("normal" or "bold")
|
|
9
|
-
* @returns opentype.js の Font オブジェクト
|
|
10
|
-
*/
|
|
11
|
-
export declare function getFont(weight: "normal" | "bold"): Font;
|
|
12
5
|
/**
|
|
13
6
|
* 指定したテキストの幅を計測する
|
|
14
7
|
* @param text 計測するテキスト
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2DH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,MAAM,CAGR"}
|
|
@@ -30,7 +30,7 @@ function base64ToArrayBuffer(base64) {
|
|
|
30
30
|
* @param weight フォントウェイト ("normal" or "bold")
|
|
31
31
|
* @returns opentype.js の Font オブジェクト
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
function getFont(weight) {
|
|
34
34
|
const cacheKey = weight;
|
|
35
35
|
// キャッシュがあればそれを返す
|
|
36
36
|
const cached = fontCache.get(cacheKey);
|
|
@@ -9,10 +9,6 @@ export type TextMeasurementMode = "opentype" | "fallback" | "auto";
|
|
|
9
9
|
* テキスト計測モードを設定する
|
|
10
10
|
*/
|
|
11
11
|
export declare function setTextMeasurementMode(mode: TextMeasurementMode): void;
|
|
12
|
-
/**
|
|
13
|
-
* 現在のテキスト計測モードを取得する
|
|
14
|
-
*/
|
|
15
|
-
export declare function getTextMeasurementMode(): TextMeasurementMode;
|
|
16
12
|
/**
|
|
17
13
|
* テキストを折り返し付きでレイアウトし、そのサイズを測定する
|
|
18
14
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAoHnE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEtE;
|
|
1
|
+
{"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAoHnE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEtE;AAcD;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,cAAc,GACnB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA"}
|