@hirokisakabe/pom 8.2.1 → 8.4.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 +37 -25
- package/dist/autoFit/autoFit.js +1 -1
- package/dist/autoFit/autoFit.js.map +1 -1
- package/dist/autoFit/strategies/reduceFontSize.js +16 -14
- package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
- package/dist/autoFit/strategies/reduceGapAndPadding.js +13 -20
- package/dist/autoFit/strategies/reduceGapAndPadding.js.map +1 -1
- package/dist/autoFit/strategies/reduceTableRowHeight.js +8 -2
- package/dist/autoFit/strategies/reduceTableRowHeight.js.map +1 -1
- package/dist/autoFit/strategies/uniformScale.js +19 -20
- package/dist/autoFit/strategies/uniformScale.js.map +1 -1
- package/dist/autoFit/strategyResult.js +15 -0
- package/dist/autoFit/strategyResult.js.map +1 -0
- package/dist/buildContext.js +3 -1
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +5 -1
- package/dist/buildPptx.js.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js +18 -28
- package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
- package/dist/calcYogaLayout/fontLoader.js.map +1 -1
- package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
- package/dist/calcYogaLayout/measureText.js +9 -2
- package/dist/calcYogaLayout/measureText.js.map +1 -1
- package/dist/diagnostics.d.ts +1 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/icons/renderIcon.js.map +1 -1
- package/dist/parseMasterPptx.js.map +1 -1
- package/dist/parseXml/coercionRules.js +48 -9
- package/dist/parseXml/coercionRules.js.map +1 -1
- package/dist/parseXml/parseXml.d.ts +8 -3
- package/dist/parseXml/parseXml.d.ts.map +1 -1
- package/dist/parseXml/parseXml.js +192 -209
- package/dist/parseXml/parseXml.js.map +1 -1
- package/dist/parseXml/serializeXml.d.ts.map +1 -1
- package/dist/parseXml/serializeXml.js +13 -17
- package/dist/parseXml/serializeXml.js.map +1 -1
- package/dist/registry/definitions/arrow.js +2 -2
- package/dist/registry/definitions/arrow.js.map +1 -1
- package/dist/registry/definitions/chart.js +2 -2
- package/dist/registry/definitions/chart.js.map +1 -1
- package/dist/registry/definitions/compositeNodes.js +7 -12
- package/dist/registry/definitions/compositeNodes.js.map +1 -1
- package/dist/registry/definitions/icon.js +2 -2
- package/dist/registry/definitions/icon.js.map +1 -1
- package/dist/registry/definitions/image.js +2 -2
- package/dist/registry/definitions/image.js.map +1 -1
- package/dist/registry/definitions/layer.js +4 -5
- package/dist/registry/definitions/layer.js.map +1 -1
- package/dist/registry/definitions/line.js +2 -2
- package/dist/registry/definitions/line.js.map +1 -1
- package/dist/registry/definitions/list.js +3 -4
- package/dist/registry/definitions/list.js.map +1 -1
- package/dist/registry/definitions/shape.js +2 -2
- package/dist/registry/definitions/shape.js.map +1 -1
- package/dist/registry/definitions/stack.js +3 -4
- package/dist/registry/definitions/stack.js.map +1 -1
- package/dist/registry/definitions/svg.js +2 -2
- package/dist/registry/definitions/svg.js.map +1 -1
- package/dist/registry/definitions/table.js +2 -2
- package/dist/registry/definitions/table.js.map +1 -1
- package/dist/registry/definitions/text.js +5 -3
- package/dist/registry/definitions/text.js.map +1 -1
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/nodeMetadata.js +208 -0
- package/dist/registry/nodeMetadata.js.map +1 -0
- package/dist/registry/nodeRegistry.js +3 -0
- package/dist/registry/nodeRegistry.js.map +1 -1
- package/dist/registry/xmlChildRules.js +55 -0
- package/dist/registry/xmlChildRules.js.map +1 -0
- package/dist/renderPptx/gradientFills.js +139 -0
- package/dist/renderPptx/gradientFills.js.map +1 -0
- package/dist/renderPptx/nodes/arrow.js +7 -28
- package/dist/renderPptx/nodes/arrow.js.map +1 -1
- package/dist/renderPptx/nodes/chart.js +2 -7
- package/dist/renderPptx/nodes/chart.js.map +1 -1
- package/dist/renderPptx/nodes/flow.js +6 -13
- package/dist/renderPptx/nodes/flow.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +4 -2
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/image.js +5 -13
- package/dist/renderPptx/nodes/image.js.map +1 -1
- package/dist/renderPptx/nodes/line.js +9 -33
- package/dist/renderPptx/nodes/line.js.map +1 -1
- package/dist/renderPptx/nodes/list.js +8 -20
- package/dist/renderPptx/nodes/list.js.map +1 -1
- package/dist/renderPptx/nodes/matrix.js +10 -11
- package/dist/renderPptx/nodes/matrix.js.map +1 -1
- package/dist/renderPptx/nodes/processArrow.js +9 -16
- package/dist/renderPptx/nodes/processArrow.js.map +1 -1
- package/dist/renderPptx/nodes/pyramid.js +5 -7
- package/dist/renderPptx/nodes/pyramid.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +7 -20
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/svg.js +2 -5
- package/dist/renderPptx/nodes/svg.js.map +1 -1
- package/dist/renderPptx/nodes/table.js +2 -5
- package/dist/renderPptx/nodes/table.js.map +1 -1
- package/dist/renderPptx/nodes/text.js +22 -15
- package/dist/renderPptx/nodes/text.js.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +20 -22
- package/dist/renderPptx/nodes/timeline.js.map +1 -1
- package/dist/renderPptx/nodes/tree.js +5 -5
- package/dist/renderPptx/nodes/tree.js.map +1 -1
- package/dist/renderPptx/renderPptx.js +18 -30
- package/dist/renderPptx/renderPptx.js.map +1 -1
- package/dist/renderPptx/textOptions.js +34 -9
- package/dist/renderPptx/textOptions.js.map +1 -1
- package/dist/renderPptx/units.js +11 -1
- package/dist/renderPptx/units.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +107 -59
- package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
- package/dist/renderPptx/utils/contentArea.js +26 -9
- package/dist/renderPptx/utils/contentArea.js.map +1 -1
- package/dist/renderPptx/utils/scaleToFit.js +17 -1
- package/dist/renderPptx/utils/scaleToFit.js.map +1 -1
- package/dist/renderPptx/utils/straightLine.js +41 -0
- package/dist/renderPptx/utils/straightLine.js.map +1 -0
- package/dist/renderPptx/utils/visualStyle.js +113 -0
- package/dist/renderPptx/utils/visualStyle.js.map +1 -0
- package/dist/shared/boxSpacing.js +63 -0
- package/dist/shared/boxSpacing.js.map +1 -0
- package/dist/shared/gradient.js +103 -0
- package/dist/shared/gradient.js.map +1 -0
- package/dist/shared/measureImage.js.map +1 -1
- package/dist/shared/tableUtils.js.map +1 -1
- package/dist/shared/walkTree.js +1 -7
- package/dist/shared/walkTree.js.map +1 -1
- package/dist/toPositioned/toPositioned.js +1 -1
- package/dist/toPositioned/toPositioned.js.map +1 -1
- package/dist/types.d.ts +1166 -93
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +54 -18
- package/dist/types.js.map +1 -1
- package/package.json +10 -9
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NODE_METADATA, getNodeMetadata, getNodeMetadataByTag } from "../registry/nodeMetadata.js";
|
|
2
|
+
import { INLINE_BOOLEAN_FORMATS, INLINE_FORMAT_TAGS, INLINE_FORMAT_TAG_LIST, MARK_DEFAULT_HIGHLIGHT_COLOR, formatExpectedTags } from "../registry/xmlChildRules.js";
|
|
2
3
|
import { CHILD_ELEMENT_COERCION_MAP, NODE_COERCION_MAP, coerceFallback, coerceWithRule, getObjectShapeFromRule, resolveMixedNotationShorthand } from "./coercionRules.js";
|
|
3
4
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
4
5
|
//#region src/parseXml/parseXml.ts
|
|
@@ -11,35 +12,7 @@ var ParseXmlError = class extends Error {
|
|
|
11
12
|
this.errors = errors;
|
|
12
13
|
}
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
Text: "text",
|
|
16
|
-
Image: "image",
|
|
17
|
-
Table: "table",
|
|
18
|
-
Shape: "shape",
|
|
19
|
-
Chart: "chart",
|
|
20
|
-
Timeline: "timeline",
|
|
21
|
-
Matrix: "matrix",
|
|
22
|
-
Tree: "tree",
|
|
23
|
-
Flow: "flow",
|
|
24
|
-
ProcessArrow: "processArrow",
|
|
25
|
-
Pyramid: "pyramid",
|
|
26
|
-
Ul: "ul",
|
|
27
|
-
Ol: "ol",
|
|
28
|
-
Line: "line",
|
|
29
|
-
Arrow: "arrow",
|
|
30
|
-
VStack: "vstack",
|
|
31
|
-
HStack: "hstack",
|
|
32
|
-
Layer: "layer",
|
|
33
|
-
Icon: "icon",
|
|
34
|
-
Svg: "svg"
|
|
35
|
-
};
|
|
36
|
-
const TYPE_TO_TAG = Object.fromEntries(Object.entries(TAG_TO_TYPE).map(([tag, type]) => [type, tag]));
|
|
37
|
-
const CONTAINER_TYPES = new Set([
|
|
38
|
-
"vstack",
|
|
39
|
-
"hstack",
|
|
40
|
-
"layer"
|
|
41
|
-
]);
|
|
42
|
-
const TEXT_CONTENT_NODES = new Set(["text", "shape"]);
|
|
15
|
+
Object.fromEntries(NODE_METADATA.map((def) => [def.tagName, def.type]));
|
|
43
16
|
const UNIVERSAL_ATTRS = new Set(["x", "y"]);
|
|
44
17
|
function getKnownAttributes(nodeType) {
|
|
45
18
|
const rules = NODE_COERCION_MAP[nodeType];
|
|
@@ -73,24 +46,6 @@ function getKnownChildAttributes(tagName) {
|
|
|
73
46
|
if (!rules) return [];
|
|
74
47
|
return Object.keys(rules);
|
|
75
48
|
}
|
|
76
|
-
const leafNodeValidationSchemas = {
|
|
77
|
-
text: textNodeSchema,
|
|
78
|
-
image: imageNodeSchema,
|
|
79
|
-
table: tableNodeSchema,
|
|
80
|
-
shape: shapeNodeSchema,
|
|
81
|
-
chart: chartNodeSchema,
|
|
82
|
-
timeline: timelineNodeSchema,
|
|
83
|
-
matrix: matrixNodeSchema,
|
|
84
|
-
tree: treeNodeSchema,
|
|
85
|
-
flow: flowNodeSchema,
|
|
86
|
-
processArrow: processArrowNodeSchema,
|
|
87
|
-
pyramid: pyramidNodeSchema,
|
|
88
|
-
line: lineNodeSchema,
|
|
89
|
-
arrow: arrowNodeSchema,
|
|
90
|
-
ul: ulNodeSchema,
|
|
91
|
-
ol: olNodeSchema,
|
|
92
|
-
icon: iconNodeSchema
|
|
93
|
-
};
|
|
94
49
|
function formatZodIssue(issue, tagName) {
|
|
95
50
|
const path = issue.path;
|
|
96
51
|
if (path.length > 0 && path[0] === "children") return null;
|
|
@@ -120,34 +75,17 @@ function formatZodIssue(issue, tagName) {
|
|
|
120
75
|
if (attrName) return `<${tagName}>: Attribute "${attrName}": ${issue.message}`;
|
|
121
76
|
return `<${tagName}>: ${issue.message}`;
|
|
122
77
|
}
|
|
123
|
-
const CHILD_ELEMENT_PROPS = {
|
|
124
|
-
flow: new Set(["nodes", "connections"]),
|
|
125
|
-
table: new Set(["columns", "rows"]),
|
|
126
|
-
chart: new Set(["data"]),
|
|
127
|
-
timeline: new Set(["items"]),
|
|
128
|
-
matrix: new Set([
|
|
129
|
-
"axes",
|
|
130
|
-
"items",
|
|
131
|
-
"quadrants"
|
|
132
|
-
]),
|
|
133
|
-
processArrow: new Set(["steps"]),
|
|
134
|
-
pyramid: new Set(["levels"]),
|
|
135
|
-
tree: new Set(["data"]),
|
|
136
|
-
ul: new Set(["items"]),
|
|
137
|
-
ol: new Set(["items"]),
|
|
138
|
-
icon: new Set(["name"]),
|
|
139
|
-
svg: new Set(["svgContent"])
|
|
140
|
-
};
|
|
141
78
|
function validateLeafNode(nodeType, result, errors) {
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
144
|
-
const
|
|
145
|
-
const
|
|
79
|
+
const def = getNodeMetadata(nodeType);
|
|
80
|
+
if (def.childPolicy.kind === "pom-children") return;
|
|
81
|
+
const schema = def.schema;
|
|
82
|
+
const tagName = def.tagName;
|
|
83
|
+
const optionalChildProps = new Set(def.childPolicy.kind === "custom" ? def.childPolicy.optionalProperties ?? [] : []);
|
|
146
84
|
const parseResult = schema.safeParse(result);
|
|
147
85
|
if (!parseResult.success) {
|
|
148
86
|
const seen = /* @__PURE__ */ new Set();
|
|
149
87
|
for (const issue of parseResult.error.issues) {
|
|
150
|
-
if (
|
|
88
|
+
if (optionalChildProps.size > 0 && issue.path.length === 1 && optionalChildProps.has(String(issue.path[0])) && issue.code === "invalid_type" && issue.input === void 0) continue;
|
|
151
89
|
if (issue.path.length > 0 && UNIVERSAL_ATTRS.has(String(issue.path[0]))) continue;
|
|
152
90
|
const msg = formatZodIssue(issue, tagName);
|
|
153
91
|
if (msg && !seen.has(msg)) {
|
|
@@ -222,52 +160,46 @@ function getTextContent(node) {
|
|
|
222
160
|
function getRawChildren(node) {
|
|
223
161
|
return node[getTagName(node)] ?? [];
|
|
224
162
|
}
|
|
225
|
-
const INLINE_FORMAT_TAGS = new Set([
|
|
226
|
-
"B",
|
|
227
|
-
"I",
|
|
228
|
-
"A",
|
|
229
|
-
"U",
|
|
230
|
-
"S",
|
|
231
|
-
"Mark",
|
|
232
|
-
"Span"
|
|
233
|
-
]);
|
|
234
163
|
function hasInlineFormatChildren(childElements) {
|
|
235
164
|
return childElements.length > 0 && childElements.every((el) => INLINE_FORMAT_TAGS.has(getTagName(el)));
|
|
236
165
|
}
|
|
237
|
-
function extractTextRuns(children,
|
|
166
|
+
function extractTextRuns(children, inherited = {}) {
|
|
238
167
|
const runs = [];
|
|
239
|
-
for (const child of children)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (inheritHref) run.href = inheritHref;
|
|
248
|
-
if (inheritFontFamily) run.fontFamily = inheritFontFamily;
|
|
249
|
-
runs.push(run);
|
|
250
|
-
} else {
|
|
168
|
+
for (const child of children) {
|
|
169
|
+
if (isTextNode(child)) {
|
|
170
|
+
runs.push({
|
|
171
|
+
text: child["#text"],
|
|
172
|
+
...inherited
|
|
173
|
+
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
251
176
|
const tag = getTagName(child);
|
|
252
177
|
const innerChildren = getRawChildren(child);
|
|
253
|
-
|
|
254
|
-
|
|
178
|
+
const booleanFormat = INLINE_BOOLEAN_FORMATS.find((format) => format.tag === tag);
|
|
179
|
+
if (booleanFormat) runs.push(...extractTextRuns(innerChildren, {
|
|
180
|
+
...inherited,
|
|
181
|
+
[booleanFormat.property]: true
|
|
182
|
+
}));
|
|
255
183
|
else if (tag === "A") {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
184
|
+
const next = { ...inherited };
|
|
185
|
+
const href = getAttributes(child).href;
|
|
186
|
+
if (href) next.href = href;
|
|
187
|
+
else delete next.href;
|
|
188
|
+
runs.push(...extractTextRuns(innerChildren, next));
|
|
189
|
+
} else if (tag === "Mark") {
|
|
261
190
|
const rawColor = getAttributes(child).color;
|
|
262
|
-
const
|
|
263
|
-
runs.push(...extractTextRuns(innerChildren,
|
|
191
|
+
const highlight = rawColor && rawColor.trim() ? rawColor : MARK_DEFAULT_HIGHLIGHT_COLOR;
|
|
192
|
+
runs.push(...extractTextRuns(innerChildren, {
|
|
193
|
+
...inherited,
|
|
194
|
+
highlight
|
|
195
|
+
}));
|
|
264
196
|
} else if (tag === "Span") {
|
|
265
197
|
const spanAttrs = getAttributes(child);
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
runs.push(...extractTextRuns(innerChildren,
|
|
198
|
+
const next = { ...inherited };
|
|
199
|
+
if (spanAttrs.color && spanAttrs.color.trim()) next.color = spanAttrs.color;
|
|
200
|
+
if (spanAttrs.fontFamily && spanAttrs.fontFamily.trim()) next.fontFamily = spanAttrs.fontFamily;
|
|
201
|
+
if (spanAttrs.letterSpacing && spanAttrs.letterSpacing.trim()) next.letterSpacing = Number(spanAttrs.letterSpacing);
|
|
202
|
+
runs.push(...extractTextRuns(innerChildren, next));
|
|
271
203
|
}
|
|
272
204
|
}
|
|
273
205
|
return runs;
|
|
@@ -320,85 +252,78 @@ function coerceChildAttrs(parentTagName, tagName, attrs, errors) {
|
|
|
320
252
|
}
|
|
321
253
|
return result;
|
|
322
254
|
}
|
|
323
|
-
function
|
|
324
|
-
|
|
325
|
-
for (const child of childElements) {
|
|
326
|
-
const tag = getTagName(child);
|
|
327
|
-
if (tag !== "ProcessArrowStep") {
|
|
328
|
-
errors.push(`Unknown child element <${tag}> inside <ProcessArrow>. Expected: <ProcessArrowStep>`);
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
steps.push(coerceChildAttrs("ProcessArrow", tag, getAttributes(child), errors));
|
|
332
|
-
}
|
|
333
|
-
result.steps = steps;
|
|
255
|
+
function unknownChildError(childTag, parentTag, expectedTags) {
|
|
256
|
+
return `Unknown child element <${childTag}> inside <${parentTag}>. Expected: ${formatExpectedTags(expectedTags)}`;
|
|
334
257
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
258
|
+
/** element の text content / インライン装飾を attrs の text / runs へ反映する */
|
|
259
|
+
function applyTextAndRuns(element, attrs) {
|
|
260
|
+
const runsResult = buildRunsAndText(element);
|
|
261
|
+
if (runsResult) {
|
|
262
|
+
attrs.runs = runsResult.runs;
|
|
263
|
+
attrs.text = runsResult.text;
|
|
264
|
+
} else {
|
|
265
|
+
const textContent = getTextContent(element);
|
|
266
|
+
if (textContent !== void 0 && !("text" in attrs)) attrs.text = textContent;
|
|
344
267
|
}
|
|
345
|
-
result.levels = levels;
|
|
346
268
|
}
|
|
347
|
-
|
|
269
|
+
/** RepeatedChildRule (単一種類の child tag の繰り返し → 配列 property) の汎用 converter */
|
|
270
|
+
function convertRepeatedChildren(rule, parentTag, childElements, result, errors) {
|
|
348
271
|
const items = [];
|
|
349
272
|
for (const child of childElements) {
|
|
350
273
|
const tag = getTagName(child);
|
|
351
|
-
if (tag !==
|
|
352
|
-
errors.push(
|
|
274
|
+
if (tag !== rule.childTag) {
|
|
275
|
+
errors.push(unknownChildError(tag, parentTag, [rule.childTag]));
|
|
353
276
|
continue;
|
|
354
277
|
}
|
|
355
|
-
|
|
278
|
+
const attrs = coerceChildAttrs(parentTag, tag, getAttributes(child), errors);
|
|
279
|
+
if (rule.allowsItemText) applyTextAndRuns(child, attrs);
|
|
280
|
+
items.push(attrs);
|
|
356
281
|
}
|
|
357
|
-
result.
|
|
282
|
+
result[rule.property] = items;
|
|
358
283
|
}
|
|
359
|
-
function convertMatrixChildren(childElements, result, errors) {
|
|
284
|
+
function convertMatrixChildren(rule, tagName, childElements, result, errors) {
|
|
360
285
|
const items = [];
|
|
361
286
|
for (const child of childElements) {
|
|
362
287
|
const tag = getTagName(child);
|
|
363
288
|
switch (tag) {
|
|
364
289
|
case "MatrixAxes":
|
|
365
|
-
result.axes = coerceChildAttrs(
|
|
290
|
+
result.axes = coerceChildAttrs(tagName, tag, getAttributes(child), errors);
|
|
366
291
|
break;
|
|
367
292
|
case "MatrixQuadrants":
|
|
368
|
-
result.quadrants = coerceChildAttrs(
|
|
293
|
+
result.quadrants = coerceChildAttrs(tagName, tag, getAttributes(child), errors);
|
|
369
294
|
break;
|
|
370
295
|
case "MatrixItem":
|
|
371
|
-
items.push(coerceChildAttrs(
|
|
296
|
+
items.push(coerceChildAttrs(tagName, tag, getAttributes(child), errors));
|
|
372
297
|
break;
|
|
373
|
-
default: errors.push(
|
|
298
|
+
default: errors.push(unknownChildError(tag, tagName, rule.expectedTags));
|
|
374
299
|
}
|
|
375
300
|
}
|
|
376
301
|
if (items.length > 0) result.items = items;
|
|
377
302
|
}
|
|
378
|
-
function convertFlowChildren(childElements, result, errors) {
|
|
303
|
+
function convertFlowChildren(rule, tagName, childElements, result, errors) {
|
|
379
304
|
const nodes = [];
|
|
380
305
|
const connections = [];
|
|
381
306
|
for (const child of childElements) {
|
|
382
307
|
const tag = getTagName(child);
|
|
383
308
|
switch (tag) {
|
|
384
309
|
case "FlowNode":
|
|
385
|
-
nodes.push(coerceChildAttrs(
|
|
310
|
+
nodes.push(coerceChildAttrs(tagName, tag, getAttributes(child), errors));
|
|
386
311
|
break;
|
|
387
312
|
case "FlowConnection":
|
|
388
|
-
connections.push(coerceChildAttrs(
|
|
313
|
+
connections.push(coerceChildAttrs(tagName, tag, getAttributes(child), errors));
|
|
389
314
|
break;
|
|
390
|
-
default: errors.push(
|
|
315
|
+
default: errors.push(unknownChildError(tag, tagName, rule.expectedTags));
|
|
391
316
|
}
|
|
392
317
|
}
|
|
393
318
|
if (nodes.length > 0) result.nodes = nodes;
|
|
394
319
|
if (connections.length > 0) result.connections = connections;
|
|
395
320
|
}
|
|
396
|
-
function convertChartChildren(childElements, result, errors) {
|
|
321
|
+
function convertChartChildren(rule, tagName, childElements, result, errors) {
|
|
397
322
|
const data = [];
|
|
398
323
|
for (const child of childElements) {
|
|
399
324
|
const tag = getTagName(child);
|
|
400
325
|
if (tag !== "ChartSeries") {
|
|
401
|
-
errors.push(
|
|
326
|
+
errors.push(unknownChildError(tag, tagName, rule.expectedTags));
|
|
402
327
|
continue;
|
|
403
328
|
}
|
|
404
329
|
const attrs = getAttributes(child);
|
|
@@ -410,7 +335,7 @@ function convertChartChildren(childElements, result, errors) {
|
|
|
410
335
|
for (const dp of getChildElements(child)) {
|
|
411
336
|
const dpTag = getTagName(dp);
|
|
412
337
|
if (dpTag !== "ChartDataPoint") {
|
|
413
|
-
errors.push(
|
|
338
|
+
errors.push(unknownChildError(dpTag, "ChartSeries", ["ChartDataPoint"]));
|
|
414
339
|
continue;
|
|
415
340
|
}
|
|
416
341
|
const dpAttrs = getAttributes(dp);
|
|
@@ -429,14 +354,14 @@ function convertChartChildren(childElements, result, errors) {
|
|
|
429
354
|
}
|
|
430
355
|
result.data = data;
|
|
431
356
|
}
|
|
432
|
-
function convertTableChildren(childElements, result, errors) {
|
|
357
|
+
function convertTableChildren(rule, tagName, childElements, result, errors) {
|
|
433
358
|
const columns = [];
|
|
434
359
|
const rows = [];
|
|
435
360
|
for (const child of childElements) {
|
|
436
361
|
const tag = getTagName(child);
|
|
437
362
|
switch (tag) {
|
|
438
363
|
case "Col":
|
|
439
|
-
columns.push(coerceChildAttrs(
|
|
364
|
+
columns.push(coerceChildAttrs(tagName, tag, getAttributes(child), errors));
|
|
440
365
|
break;
|
|
441
366
|
case "Tr": {
|
|
442
367
|
const rowAttrs = getAttributes(child);
|
|
@@ -444,18 +369,11 @@ function convertTableChildren(childElements, result, errors) {
|
|
|
444
369
|
for (const cellEl of getChildElements(child)) {
|
|
445
370
|
const cellTag = getTagName(cellEl);
|
|
446
371
|
if (cellTag !== "Td") {
|
|
447
|
-
errors.push(
|
|
372
|
+
errors.push(unknownChildError(cellTag, "Tr", ["Td"]));
|
|
448
373
|
continue;
|
|
449
374
|
}
|
|
450
375
|
const cellAttrs = coerceChildAttrs("Tr", cellTag, getAttributes(cellEl), errors);
|
|
451
|
-
|
|
452
|
-
if (runsResult) {
|
|
453
|
-
cellAttrs.runs = runsResult.runs;
|
|
454
|
-
cellAttrs.text = runsResult.text;
|
|
455
|
-
} else {
|
|
456
|
-
const cellText = getTextContent(cellEl);
|
|
457
|
-
if (cellText !== void 0 && !("text" in cellAttrs)) cellAttrs.text = cellText;
|
|
458
|
-
}
|
|
376
|
+
applyTextAndRuns(cellEl, cellAttrs);
|
|
459
377
|
cells.push(cellAttrs);
|
|
460
378
|
}
|
|
461
379
|
const row = { cells };
|
|
@@ -467,7 +385,7 @@ function convertTableChildren(childElements, result, errors) {
|
|
|
467
385
|
rows.push(row);
|
|
468
386
|
break;
|
|
469
387
|
}
|
|
470
|
-
default: errors.push(
|
|
388
|
+
default: errors.push(unknownChildError(tag, tagName, rule.expectedTags));
|
|
471
389
|
}
|
|
472
390
|
}
|
|
473
391
|
if (columns.length > 0) result.columns = columns;
|
|
@@ -483,18 +401,19 @@ function convertTreeItem(element, errors) {
|
|
|
483
401
|
const item = {};
|
|
484
402
|
if (attrs.label !== void 0) item.label = attrs.label;
|
|
485
403
|
if (attrs.color !== void 0) item.color = attrs.color;
|
|
404
|
+
if (attrs.textColor !== void 0) item.textColor = attrs.textColor;
|
|
486
405
|
const children = getChildElements(element);
|
|
487
406
|
if (children.length > 0) item.children = children.map((child) => {
|
|
488
407
|
const tag = getTagName(child);
|
|
489
408
|
if (tag !== "TreeItem") {
|
|
490
|
-
errors.push(
|
|
409
|
+
errors.push(unknownChildError(tag, "TreeItem", ["TreeItem"]));
|
|
491
410
|
return null;
|
|
492
411
|
}
|
|
493
412
|
return convertTreeItem(child, errors);
|
|
494
413
|
}).filter((item) => item !== null);
|
|
495
414
|
return item;
|
|
496
415
|
}
|
|
497
|
-
function convertTreeChildren(childElements, result, errors) {
|
|
416
|
+
function convertTreeChildren(rule, tagName, childElements, result, errors) {
|
|
498
417
|
if (childElements.length !== 1) {
|
|
499
418
|
errors.push(`<Tree> must have exactly 1 <TreeItem> child element, but got ${childElements.length}`);
|
|
500
419
|
return;
|
|
@@ -502,32 +421,11 @@ function convertTreeChildren(childElements, result, errors) {
|
|
|
502
421
|
const child = childElements[0];
|
|
503
422
|
const tag = getTagName(child);
|
|
504
423
|
if (tag !== "TreeItem") {
|
|
505
|
-
errors.push(
|
|
424
|
+
errors.push(unknownChildError(tag, tagName, rule.expectedTags));
|
|
506
425
|
return;
|
|
507
426
|
}
|
|
508
427
|
result.data = convertTreeItem(child, errors);
|
|
509
428
|
}
|
|
510
|
-
function convertListChildren(parentTag, childElements, result, errors) {
|
|
511
|
-
const items = [];
|
|
512
|
-
for (const child of childElements) {
|
|
513
|
-
const tag = getTagName(child);
|
|
514
|
-
if (tag !== "Li") {
|
|
515
|
-
errors.push(`Unknown child element <${tag}> inside <${parentTag}>. Expected: <Li>`);
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
const attrs = coerceChildAttrs(parentTag, tag, getAttributes(child), errors);
|
|
519
|
-
const runsResult = buildRunsAndText(child);
|
|
520
|
-
if (runsResult) {
|
|
521
|
-
attrs.runs = runsResult.runs;
|
|
522
|
-
attrs.text = runsResult.text;
|
|
523
|
-
} else {
|
|
524
|
-
const textContent = getTextContent(child);
|
|
525
|
-
if (textContent !== void 0 && !("text" in attrs)) attrs.text = textContent;
|
|
526
|
-
}
|
|
527
|
-
items.push(attrs);
|
|
528
|
-
}
|
|
529
|
-
result.items = items;
|
|
530
|
-
}
|
|
531
429
|
const svgBuilder = new XMLBuilder({
|
|
532
430
|
preserveOrder: true,
|
|
533
431
|
ignoreAttributes: false,
|
|
@@ -536,7 +434,7 @@ const svgBuilder = new XMLBuilder({
|
|
|
536
434
|
function serializeSvgElement(svgElement) {
|
|
537
435
|
return String(svgBuilder.build([svgElement]));
|
|
538
436
|
}
|
|
539
|
-
function convertSvgChildren(childElements, result, errors) {
|
|
437
|
+
function convertSvgChildren(_rule, _tagName, childElements, result, errors) {
|
|
540
438
|
if (childElements.length !== 1) {
|
|
541
439
|
errors.push(`<Svg>: Expected exactly one <svg> child element, but found ${childElements.length} child element(s)`);
|
|
542
440
|
return;
|
|
@@ -549,11 +447,13 @@ function convertSvgChildren(childElements, result, errors) {
|
|
|
549
447
|
}
|
|
550
448
|
result.svgContent = serializeSvgElement(child);
|
|
551
449
|
}
|
|
552
|
-
function
|
|
450
|
+
function convertInlineRunsChildren(tagName, childElements, result, errors, node) {
|
|
553
451
|
for (const el of childElements) {
|
|
554
452
|
const tag = getTagName(el);
|
|
555
453
|
if (!INLINE_FORMAT_TAGS.has(tag)) {
|
|
556
|
-
|
|
454
|
+
const allowedTags = INLINE_FORMAT_TAG_LIST.map((t) => `<${t}>`);
|
|
455
|
+
const allowedList = `${allowedTags.slice(0, -1).join(", ")}, and ${allowedTags[allowedTags.length - 1]}`;
|
|
456
|
+
errors.push(`<${tagName}>: Unexpected child element <${tag}>. Only ${allowedList} are allowed inside <${tagName}>`);
|
|
557
457
|
return;
|
|
558
458
|
}
|
|
559
459
|
}
|
|
@@ -564,13 +464,8 @@ function convertTextInlineChildren(childElements, result, errors, node) {
|
|
|
564
464
|
result.text = runsResult.text;
|
|
565
465
|
}
|
|
566
466
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
ul: (childElements, result, errors) => convertListChildren("Ul", childElements, result, errors),
|
|
570
|
-
ol: (childElements, result, errors) => convertListChildren("Ol", childElements, result, errors),
|
|
571
|
-
processArrow: convertProcessArrowChildren,
|
|
572
|
-
pyramid: convertPyramidChildren,
|
|
573
|
-
timeline: convertTimelineChildren,
|
|
467
|
+
/** NodeSpecificChildRule を持つノードの専用 converter テーブル */
|
|
468
|
+
const NODE_SPECIFIC_CHILD_CONVERTERS = {
|
|
574
469
|
matrix: convertMatrixChildren,
|
|
575
470
|
flow: convertFlowChildren,
|
|
576
471
|
chart: convertChartChildren,
|
|
@@ -578,20 +473,85 @@ const CHILD_ELEMENT_CONVERTERS = {
|
|
|
578
473
|
tree: convertTreeChildren,
|
|
579
474
|
svg: convertSvgChildren
|
|
580
475
|
};
|
|
581
|
-
|
|
476
|
+
/** NodeMetadata の xmlChildRule に従って child element を変換する */
|
|
477
|
+
function applyXmlChildRule(rule, nodeType, tagName, childElements, result, errors, node) {
|
|
478
|
+
if (rule.kind === "inline-runs") {
|
|
479
|
+
convertInlineRunsChildren(tagName, childElements, result, errors, node);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (rule.kind === "repeated") {
|
|
483
|
+
convertRepeatedChildren(rule, tagName, childElements, result, errors);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const converter = NODE_SPECIFIC_CHILD_CONVERTERS[nodeType];
|
|
487
|
+
if (!converter) throw new Error(`No node-specific child converter registered for node type: ${nodeType}`);
|
|
488
|
+
converter(rule, tagName, childElements, result, errors);
|
|
489
|
+
}
|
|
490
|
+
const THEME_TOKEN_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
491
|
+
const THEME_TOKEN_VALUE_PATTERN = /^[0-9A-Fa-f]{6}$/;
|
|
492
|
+
const TOKEN_REF_PATTERN = /^(#?)\$([A-Za-z][A-Za-z0-9_-]*)$/;
|
|
493
|
+
const TOKEN_REF_IN_GRADIENT_PATTERN = /#?\$([A-Za-z][A-Za-z0-9_-]*)/g;
|
|
494
|
+
function parseThemeElement(element, tokens, errors) {
|
|
495
|
+
if (getChildElements(element).length > 0) errors.push(`<Theme>: Child elements are not supported. Declare tokens as attributes (e.g. <Theme accent="1D4ED8" />)`);
|
|
496
|
+
for (const [name, rawValue] of Object.entries(getAttributes(element))) {
|
|
497
|
+
if (!THEME_TOKEN_NAME_PATTERN.test(name)) {
|
|
498
|
+
errors.push(`<Theme>: Invalid token name "${name}". Token names must start with a letter and contain only letters, digits, "_", and "-"`);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const value = rawValue.replace(/^#/, "");
|
|
502
|
+
if (!THEME_TOKEN_VALUE_PATTERN.test(value)) {
|
|
503
|
+
errors.push(`<Theme>: Invalid color value "${rawValue}" for token "${name}". Expected 6-digit hex (e.g. "1D4ED8")`);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
tokens[name] = value;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function isColorKey(key) {
|
|
510
|
+
return /colors?$/i.test(key) || key === "highlight";
|
|
511
|
+
}
|
|
512
|
+
function resolveThemeToken(name, hashPrefix, tokens, themeDeclared, errors) {
|
|
513
|
+
const value = tokens[name];
|
|
514
|
+
if (value !== void 0) return `${hashPrefix}${value}`;
|
|
515
|
+
if (!themeDeclared) errors.push(`Theme token "$${name}" is referenced, but no <Theme> is declared. Add a top-level <Theme ${name}="RRGGBB" /> element`);
|
|
516
|
+
else {
|
|
517
|
+
const suggestion = findClosestMatch(name, Object.keys(tokens));
|
|
518
|
+
errors.push(`Unknown theme token "$${name}"${suggestion ? `. Did you mean "$${suggestion}"?` : ""}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function resolveThemeTokensDeep(value, key, tokens, themeDeclared, errors) {
|
|
522
|
+
if (typeof value === "string") {
|
|
523
|
+
if (key === "backgroundGradient") return value.replace(TOKEN_REF_IN_GRADIENT_PATTERN, (matched, name) => {
|
|
524
|
+
return resolveThemeToken(name, "#", tokens, themeDeclared, errors) ?? matched;
|
|
525
|
+
});
|
|
526
|
+
if (key !== null && isColorKey(key)) {
|
|
527
|
+
const match = TOKEN_REF_PATTERN.exec(value.trim());
|
|
528
|
+
if (match) return resolveThemeToken(match[2], match[1], tokens, themeDeclared, errors) ?? value;
|
|
529
|
+
}
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
if (Array.isArray(value)) return value.map((item) => resolveThemeTokensDeep(item, key, tokens, themeDeclared, errors));
|
|
533
|
+
if (typeof value === "object" && value !== null) {
|
|
534
|
+
const result = {};
|
|
535
|
+
for (const [childKey, childValue] of Object.entries(value)) result[childKey] = resolveThemeTokensDeep(childValue, childKey, tokens, themeDeclared, errors);
|
|
536
|
+
return result;
|
|
537
|
+
}
|
|
538
|
+
return value;
|
|
539
|
+
}
|
|
540
|
+
function convertElement(node, errors, theme) {
|
|
582
541
|
const tagName = getTagName(node);
|
|
583
|
-
const
|
|
542
|
+
const def = getNodeMetadataByTag(tagName);
|
|
584
543
|
const attrs = getAttributes(node);
|
|
585
544
|
const childElements = getChildElements(node);
|
|
586
545
|
const textContent = getTextContent(node);
|
|
587
|
-
if (
|
|
546
|
+
if (def) return convertPomNode(def.type, tagName, attrs, childElements, textContent, errors, theme, node);
|
|
588
547
|
else {
|
|
589
548
|
errors.push(`Unknown tag: <${tagName}>`);
|
|
590
549
|
return null;
|
|
591
550
|
}
|
|
592
551
|
}
|
|
593
|
-
function convertPomNode(nodeType, tagName, attrs, childElements, textContent, errors, xmlNode) {
|
|
552
|
+
function convertPomNode(nodeType, tagName, attrs, childElements, textContent, errors, theme, xmlNode) {
|
|
594
553
|
const result = { type: nodeType };
|
|
554
|
+
const def = getNodeMetadata(nodeType);
|
|
595
555
|
const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);
|
|
596
556
|
for (const [prefix, subAttrs] of Object.entries(dotGroups)) {
|
|
597
557
|
if (prefix === "type") continue;
|
|
@@ -633,14 +593,18 @@ function convertPomNode(nodeType, tagName, attrs, childElements, textContent, er
|
|
|
633
593
|
else errors.push(`<${tagName}>: Unknown attribute "${key}"`);
|
|
634
594
|
}
|
|
635
595
|
}
|
|
636
|
-
if (textContent !== void 0 &&
|
|
637
|
-
if (!(
|
|
596
|
+
if (textContent !== void 0 && def.textContentProperty) {
|
|
597
|
+
if (!(def.textContentProperty in result)) result[def.textContentProperty] = textContent;
|
|
638
598
|
}
|
|
639
|
-
const
|
|
640
|
-
if (
|
|
641
|
-
else if (
|
|
642
|
-
else if (
|
|
643
|
-
|
|
599
|
+
const childRule = def.xmlChildRule;
|
|
600
|
+
if (childRule && childElements.length > 0) applyXmlChildRule(childRule, nodeType, tagName, childElements, result, errors, xmlNode);
|
|
601
|
+
else if (def.childPolicy.kind === "pom-children" && childElements.length > 0) result.children = childElements.map((child) => convertElement(child, errors, theme)).filter((child) => child !== null);
|
|
602
|
+
else if (def.childPolicy.kind !== "pom-children" && !childRule && childElements.length > 0) errors.push(`<${tagName}>: Unexpected child elements. <${tagName}> does not accept child elements`);
|
|
603
|
+
for (const [key, value] of Object.entries(result)) {
|
|
604
|
+
if (key === "type" || key === "children") continue;
|
|
605
|
+
result[key] = resolveThemeTokensDeep(value, key, theme.tokens, theme.declared, errors);
|
|
606
|
+
}
|
|
607
|
+
if (def.childPolicy.kind !== "pom-children") validateLeafNode(nodeType, result, errors);
|
|
644
608
|
if (nodeType === "icon") {
|
|
645
609
|
if (typeof result.color === "string" && !result.color.startsWith("#")) result.color = `#${result.color}`;
|
|
646
610
|
if (typeof result.bgColor === "string" && !result.bgColor.startsWith("#")) result.bgColor = `#${result.bgColor}`;
|
|
@@ -654,9 +618,14 @@ function convertPomNode(nodeType, tagName, attrs, childElements, textContent, er
|
|
|
654
618
|
/**
|
|
655
619
|
* XML 文字列を POMNode 配列に変換する。
|
|
656
620
|
*
|
|
657
|
-
* 最上位は `<Slide>` 要素のみが許容される。各 `<Slide>` が
|
|
658
|
-
*
|
|
659
|
-
*
|
|
621
|
+
* 最上位は `<Slide>` 要素と `<Theme>` 要素のみが許容される。各 `<Slide>` が
|
|
622
|
+
* 1 つのスライドに対応し、その子要素がスライドのルート POMNode となる。
|
|
623
|
+
* 子要素が複数ある場合は暗黙的に VStack でラップされる。
|
|
624
|
+
*
|
|
625
|
+
* `<Theme>` は文書全体に適用されるデザイントークン(配色)の宣言で、最大 1 つ
|
|
626
|
+
* 置ける。属性名がトークン名、属性値が 6 桁 hex の色値となり、各ノードの色属性
|
|
627
|
+
* から `$トークン名` で参照できる。参照は parse 時に解決されるため、返される
|
|
628
|
+
* POMNode には解決済みの hex 値が入る(`<Theme>` 自体はノードにならない)。
|
|
660
629
|
*
|
|
661
630
|
* XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して
|
|
662
631
|
* 適切な型(number, boolean, array, object)に変換される。
|
|
@@ -693,12 +662,26 @@ function parseXml(xmlString) {
|
|
|
693
662
|
if (!parsed || parsed.length === 0) return [];
|
|
694
663
|
const rootChildren = parsed[0]["__root__"] ?? [];
|
|
695
664
|
const errors = [];
|
|
696
|
-
const
|
|
665
|
+
const topLevelElements = rootChildren.filter((child) => !isTextNode(child));
|
|
666
|
+
const theme = {
|
|
667
|
+
tokens: {},
|
|
668
|
+
declared: false
|
|
669
|
+
};
|
|
670
|
+
for (const element of topLevelElements) {
|
|
671
|
+
if (getTagName(element) !== "Theme") continue;
|
|
672
|
+
if (theme.declared) {
|
|
673
|
+
errors.push(`Only one <Theme> element is allowed, but multiple were found`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
theme.declared = true;
|
|
677
|
+
parseThemeElement(element, theme.tokens, errors);
|
|
678
|
+
}
|
|
679
|
+
const slideElements = topLevelElements.filter((element) => getTagName(element) !== "Theme");
|
|
697
680
|
const nodes = [];
|
|
698
681
|
for (const slideEl of slideElements) {
|
|
699
682
|
const tagName = getTagName(slideEl);
|
|
700
683
|
if (tagName !== "Slide") {
|
|
701
|
-
errors.push(`Top-level element must be <Slide>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`);
|
|
684
|
+
errors.push(`Top-level element must be <Slide> or <Theme>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`);
|
|
702
685
|
continue;
|
|
703
686
|
}
|
|
704
687
|
if (Object.keys(getAttributes(slideEl)).length > 0) errors.push(`<Slide>: Attributes are not supported`);
|
|
@@ -707,7 +690,7 @@ function parseXml(xmlString) {
|
|
|
707
690
|
errors.push(`<Slide> must contain at least one child element`);
|
|
708
691
|
continue;
|
|
709
692
|
}
|
|
710
|
-
const converted = slideChildren.map((child) => convertElement(child, errors)).filter((c) => c !== null);
|
|
693
|
+
const converted = slideChildren.map((child) => convertElement(child, errors, theme)).filter((c) => c !== null);
|
|
711
694
|
if (converted.length === 0) continue;
|
|
712
695
|
if (converted.length === 1) nodes.push(converted[0]);
|
|
713
696
|
else nodes.push({
|
|
@@ -719,6 +702,6 @@ function parseXml(xmlString) {
|
|
|
719
702
|
return nodes;
|
|
720
703
|
}
|
|
721
704
|
//#endregion
|
|
722
|
-
export { ParseXmlError,
|
|
705
|
+
export { ParseXmlError, parseXml };
|
|
723
706
|
|
|
724
707
|
//# sourceMappingURL=parseXml.js.map
|