@hirokisakabe/pom 8.7.0 → 8.9.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 +15 -3
- package/dist/buildContext.js +4 -2
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +3 -1
- package/dist/buildPptx.js.map +1 -1
- package/dist/parseXml/coercionRules.js +12 -2
- package/dist/parseXml/coercionRules.js.map +1 -1
- package/dist/parseXml/parseXml.d.ts.map +1 -1
- package/dist/parseXml/parseXml.js +3 -3
- package/dist/parseXml/parseXml.js.map +1 -1
- package/dist/renderPptx/glowEffects.js +147 -0
- package/dist/renderPptx/glowEffects.js.map +1 -0
- package/dist/renderPptx/gradientFills.js +36 -8
- package/dist/renderPptx/gradientFills.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +13 -6
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +24 -2
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/text.js +7 -2
- package/dist/renderPptx/nodes/text.js.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +41 -14
- package/dist/renderPptx/nodes/timeline.js.map +1 -1
- package/dist/renderPptx/renderPptx.js +1 -1
- package/dist/renderPptx/units.js +8 -1
- package/dist/renderPptx/units.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +1 -1
- package/dist/shared/gradient.js +185 -19
- package/dist/shared/gradient.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -4
- package/dist/types.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseXml.js","names":[],"sources":["../../src/parseXml/parseXml.ts"],"sourcesContent":["import { XMLBuilder, XMLParser } from \"fast-xml-parser\";\nimport type { z } from \"zod\";\nimport type { POMNode } from \"../types.ts\";\nimport {\n getNodeMetadata,\n getNodeMetadataByTag,\n NODE_METADATA,\n} from \"../registry/nodeMetadata.ts\";\nimport {\n INLINE_BOOLEAN_FORMATS,\n INLINE_FORMAT_TAG_LIST,\n INLINE_FORMAT_TAGS,\n INLINE_LINK_TAG,\n INLINE_MARK_TAG,\n INLINE_SPAN_TAG,\n MARK_DEFAULT_HIGHLIGHT_COLOR,\n formatExpectedTags,\n type NodeSpecificChildRule,\n type RepeatedChildRule,\n type TextRun,\n type XmlChildRule,\n} from \"../registry/xmlChildRules.ts\";\nimport {\n type CoercionRule,\n NODE_COERCION_MAP,\n CHILD_ELEMENT_COERCION_MAP,\n coerceWithRule,\n coerceFallback,\n getObjectShapeFromRule,\n resolveMixedNotationShorthand,\n} from \"./coercionRules.ts\";\n\n// ===== ParseXmlError =====\nexport class ParseXmlError extends Error {\n public readonly errors: string[];\n constructor(errors: string[]) {\n const message = `XML validation failed (${errors.length} error${errors.length > 1 ? \"s\" : \"\"}):\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`;\n super(message);\n this.name = \"ParseXmlError\";\n this.errors = errors;\n }\n}\n\n// ===== Tag name → POM node type mapping =====\nexport const TAG_TO_TYPE: Record<string, string> = Object.fromEntries(\n NODE_METADATA.map((def) => [def.tagName, def.type]),\n);\n// Attributes allowed on any node (e.g., x/y for Layer children positioning)\nconst UNIVERSAL_ATTRS = new Set([\"x\", \"y\"]);\n\n// ===== Validation helpers =====\nfunction getKnownAttributes(nodeType: string): string[] {\n const rules = NODE_COERCION_MAP[nodeType];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array<number>(n + 1).fill(0),\n );\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction findClosestMatch(\n input: string,\n candidates: string[],\n): string | undefined {\n const threshold = Math.max(2, Math.floor(input.length / 2));\n let bestMatch: string | undefined;\n let bestDistance = Infinity;\n for (const candidate of candidates) {\n const dist = levenshteinDistance(\n input.toLowerCase(),\n candidate.toLowerCase(),\n );\n if (dist < bestDistance && dist <= threshold) {\n bestDistance = dist;\n bestMatch = candidate;\n }\n }\n return bestMatch;\n}\n\nfunction getKnownChildAttributes(tagName: string): string[] {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction formatZodIssue(\n issue: z.core.$ZodIssue,\n tagName: string,\n): string | null {\n const path = issue.path;\n // Skip children-related issues (validated recursively)\n if (path.length > 0 && path[0] === \"children\") return null;\n // Skip \"type\" field issues (set internally)\n if (path.length === 1 && path[0] === \"type\") return null;\n\n const attrName = path.length > 0 ? String(path[0]) : undefined;\n\n const code = issue.code;\n\n if (code === \"invalid_type\") {\n // Missing required attribute\n if (issue.input === undefined) {\n if (attrName) {\n return `<${tagName}>: Missing required attribute \"${attrName}\"`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n // Type mismatch\n if (attrName) {\n return `<${tagName}>: Invalid type for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"invalid_value\") {\n if (attrName) {\n const values = (issue as unknown as { values: string[] }).values;\n if (values) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". Expected: ${values.map((v) => `\"${v}\"`).join(\", \")}`;\n }\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"too_small\" || code === \"too_big\") {\n if (attrName) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n // Generic fallback\n if (attrName) {\n return `<${tagName}>: Attribute \"${attrName}\": ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n}\n\n// Properties that may be legitimately absent when using child element notation\n// or when the property is optional in practice (even if required in schema).\nfunction validateLeafNode(\n nodeType: string,\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n if (def.childPolicy.kind === \"pom-children\") return;\n const schema = def.schema;\n const tagName = def.tagName;\n const optionalChildProps = new Set(\n def.childPolicy.kind === \"custom\"\n ? (def.childPolicy.optionalProperties ?? [])\n : [],\n );\n const parseResult = schema.safeParse(result);\n if (!parseResult.success) {\n const seen = new Set<string>();\n for (const issue of parseResult.error.issues) {\n // Skip only top-level missing child-element properties (path.length === 1)\n // Nested issues (e.g., data.children[0].label) must still be reported\n if (\n optionalChildProps.size > 0 &&\n issue.path.length === 1 &&\n optionalChildProps.has(String(issue.path[0])) &&\n issue.code === \"invalid_type\" &&\n issue.input === undefined\n ) {\n continue;\n }\n // Skip issues for universal attributes (x, y)\n if (issue.path.length > 0 && UNIVERSAL_ATTRS.has(String(issue.path[0]))) {\n continue;\n }\n const msg = formatZodIssue(issue, tagName);\n if (msg && !seen.has(msg)) {\n seen.add(msg);\n errors.push(msg);\n }\n }\n }\n}\n\n// ===== Types for XML parser output (preserveOrder mode) =====\ntype XmlNode = XmlElement | XmlTextNode;\ntype XmlTextNode = { \"#text\": string };\ninterface XmlElement {\n [tagName: string]: XmlNode[] | Record<string, string> | undefined;\n \":@\"?: Record<string, string>;\n}\n\n// ===== Coercion rule lookup =====\n\nfunction getCoercionRule(\n nodeType: string,\n propertyName: string,\n): CoercionRule | undefined {\n return NODE_COERCION_MAP[nodeType]?.[propertyName];\n}\n\n// ===== Dot notation helpers =====\n\n// ===== Dot notation expansion =====\nfunction expandDotNotation(attrs: Record<string, string>): {\n regular: Record<string, string>;\n dotGroups: Record<string, Record<string, string>>;\n} {\n const regular: Record<string, string> = {};\n const dotGroups: Record<string, Record<string, string>> = {};\n\n for (const [key, value] of Object.entries(attrs)) {\n const dotIndex = key.indexOf(\".\");\n if (dotIndex > 0) {\n const prefix = key.substring(0, dotIndex);\n const suffix = key.substring(dotIndex + 1);\n if (!dotGroups[prefix]) dotGroups[prefix] = {};\n dotGroups[prefix][suffix] = value;\n } else {\n regular[key] = value;\n }\n }\n\n return { regular, dotGroups };\n}\n\nfunction coerceDotGroup(\n prefix: string,\n subAttrs: Record<string, string>,\n rule: CoercionRule,\n tagName: string,\n errors: string[],\n): Record<string, unknown> {\n const objectShape = getObjectShapeFromRule(rule);\n\n const obj: Record<string, unknown> = {};\n if (objectShape) {\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n if (objectShape[subKey]) {\n const coerced = coerceWithRule(subValue, objectShape[subKey]);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${prefix}.${subKey}: ${coerced.error}`);\n } else {\n obj[subKey] = coerced.value;\n }\n } else {\n const knownSubKeys = Object.keys(objectShape);\n const suggestion = findClosestMatch(subKey, knownSubKeys);\n errors.push(\n `<${tagName}>: Unknown sub-attribute \"${prefix}.${subKey}\"${suggestion ? `. Did you mean \"${prefix}.${suggestion}\"?` : \"\"}`,\n );\n }\n }\n } else {\n errors.push(\n `<${tagName}>: Attribute \"${prefix}\" does not support dot notation`,\n );\n }\n return obj;\n}\n\n// ===== XML node helpers =====\nfunction isTextNode(node: XmlNode): node is XmlTextNode {\n return \"#text\" in node;\n}\n\nfunction getTagName(node: XmlElement): string {\n for (const key of Object.keys(node)) {\n if (key !== \":@\") return key;\n }\n throw new Error(\"No tag name found in XML element\");\n}\n\nfunction getAttributes(node: XmlElement): Record<string, string> {\n const attrs: Record<string, string> = {};\n const rawAttrs = node[\":@\"];\n if (rawAttrs) {\n for (const [key, value] of Object.entries(rawAttrs)) {\n const attrName = key.startsWith(\"@_\") ? key.slice(2) : key;\n attrs[attrName] = value.trim();\n }\n }\n return attrs;\n}\n\nfunction getChildElements(node: XmlElement): XmlElement[] {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return [];\n return children.filter((child): child is XmlElement => !isTextNode(child));\n}\n\nfunction getTextContent(node: XmlElement): string | undefined {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return undefined;\n const textParts: string[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n textParts.push(child[\"#text\"]);\n }\n }\n return textParts.length > 0 ? textParts.join(\"\") : undefined;\n}\n\nfunction getRawChildren(node: XmlElement): XmlNode[] {\n const tagName = getTagName(node);\n return (node[tagName] as XmlNode[] | undefined) ?? [];\n}\n\nfunction hasInlineFormatChildren(childElements: XmlElement[]): boolean {\n return (\n childElements.length > 0 &&\n childElements.every((el) => INLINE_FORMAT_TAGS.has(getTagName(el)))\n );\n}\n\nfunction extractTextRuns(\n children: XmlNode[],\n inherited: Partial<Omit<TextRun, \"text\">> = {},\n): TextRun[] {\n const runs: TextRun[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n runs.push({ text: child[\"#text\"], ...inherited });\n continue;\n }\n const tag = getTagName(child);\n const innerChildren = getRawChildren(child);\n const booleanFormat = INLINE_BOOLEAN_FORMATS.find(\n (format) => format.tag === tag,\n );\n if (booleanFormat) {\n const next: Partial<Omit<TextRun, \"text\">> = {\n ...inherited,\n [booleanFormat.property]: true,\n };\n // subscript と superscript は OOXML の baseline 値として相互排他\n // (同時 true は描画結果が未定義になる)。後で立てた側を優先して反対側を解除する。\n if (booleanFormat.property === \"subscript\") delete next.superscript;\n if (booleanFormat.property === \"superscript\") delete next.subscript;\n runs.push(...extractTextRuns(innerChildren, next));\n } else if (tag === INLINE_LINK_TAG) {\n // href なしの <A> は外側の href を引き継がない(リンク解除として扱う)\n const next = { ...inherited };\n const href = getAttributes(child).href;\n if (href) {\n next.href = href;\n } else {\n delete next.href;\n }\n runs.push(...extractTextRuns(innerChildren, next));\n } else if (tag === INLINE_MARK_TAG) {\n const rawColor = getAttributes(child).color;\n const highlight =\n rawColor && rawColor.trim() ? rawColor : MARK_DEFAULT_HIGHLIGHT_COLOR;\n runs.push(...extractTextRuns(innerChildren, { ...inherited, highlight }));\n } else if (tag === INLINE_SPAN_TAG) {\n const spanAttrs = getAttributes(child);\n const next = { ...inherited };\n if (spanAttrs.color && spanAttrs.color.trim()) {\n next.color = spanAttrs.color;\n }\n if (spanAttrs.fontFamily && spanAttrs.fontFamily.trim()) {\n next.fontFamily = spanAttrs.fontFamily;\n }\n if (spanAttrs.fontSize && spanAttrs.fontSize.trim()) {\n next.fontSize = Number(spanAttrs.fontSize);\n }\n if (spanAttrs.letterSpacing && spanAttrs.letterSpacing.trim()) {\n next.letterSpacing = Number(spanAttrs.letterSpacing);\n }\n runs.push(...extractTextRuns(innerChildren, next));\n }\n }\n return runs;\n}\n\nfunction buildRunsAndText(\n node: XmlElement,\n): { runs: TextRun[]; text: string } | null {\n const rawChildren = getRawChildren(node);\n const childElements = rawChildren.filter(\n (c): c is XmlElement => !isTextNode(c),\n );\n if (!hasInlineFormatChildren(childElements)) return null;\n const runs = extractTextRuns(rawChildren);\n const text = runs.map((r) => r.text).join(\"\");\n return { runs, text };\n}\n\nfunction coerceChildAttrs(\n parentTagName: string,\n tagName: string,\n attrs: Record<string, string>,\n errors: string[],\n): Record<string, unknown> {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n const result: Record<string, unknown> = {};\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n // Process dot-notation attributes\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (rules && rules[prefix]) {\n result[prefix] = coerceDotGroup(\n prefix,\n subAttrs,\n rules[prefix],\n `${parentTagName}.${tagName}`,\n errors,\n );\n } else if (rules) {\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${prefix}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[prefix] = {};\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n (result[prefix] as Record<string, unknown>)[subKey] =\n coerceFallback(subValue);\n }\n }\n }\n\n // Process regular attributes\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key in dotGroups) {\n if (rules && rules[key]) {\n const resolved = resolveMixedNotationShorthand(value, rules[key]);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${parentTagName}>.<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes. Use one or the other, not both`,\n );\n continue;\n }\n if (rules && rules[key]) {\n const coerced = coerceWithRule(value, rules[key]);\n if (coerced.error !== null) {\n errors.push(`<${parentTagName}>.<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (rules) {\n // Unknown attribute on child element\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(key, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${key}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[key] = coerceFallback(value);\n }\n }\n return result;\n}\n\n// ===== Child element converters =====\n\nfunction unknownChildError(\n childTag: string,\n parentTag: string,\n expectedTags: readonly string[],\n): string {\n return `Unknown child element <${childTag}> inside <${parentTag}>. Expected: ${formatExpectedTags(expectedTags)}`;\n}\n\n/** element の text content / インライン装飾を attrs の text / runs へ反映する */\nfunction applyTextAndRuns(\n element: XmlElement,\n attrs: Record<string, unknown>,\n): void {\n const runsResult = buildRunsAndText(element);\n if (runsResult) {\n attrs.runs = runsResult.runs;\n attrs.text = runsResult.text;\n } else {\n const textContent = getTextContent(element);\n if (textContent !== undefined && !(\"text\" in attrs)) {\n attrs.text = textContent;\n }\n }\n}\n\n/** RepeatedChildRule (単一種類の child tag の繰り返し → 配列 property) の汎用 converter */\nfunction convertRepeatedChildren(\n rule: RepeatedChildRule,\n parentTag: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== rule.childTag) {\n errors.push(unknownChildError(tag, parentTag, [rule.childTag]));\n continue;\n }\n const attrs = coerceChildAttrs(\n parentTag,\n tag,\n getAttributes(child),\n errors,\n );\n if (rule.allowsItemText) {\n applyTextAndRuns(child, attrs);\n }\n items.push(attrs);\n }\n result[rule.property] = items;\n}\n\n/** NodeSpecificChildRule を処理する専用 converter のシグネチャ */\ntype NodeSpecificChildConverter = (\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n) => void;\n\nfunction convertMatrixChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"MatrixAxes\":\n result.axes = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixQuadrants\":\n result.quadrants = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixItem\":\n items.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (items.length > 0) {\n result.items = items;\n }\n}\n\nfunction convertFlowChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const nodes: Record<string, unknown>[] = [];\n const connections: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"FlowNode\":\n nodes.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"FlowConnection\":\n connections.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (nodes.length > 0) {\n result.nodes = nodes;\n }\n if (connections.length > 0) {\n result.connections = connections;\n }\n}\n\nfunction convertChartChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const data: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"ChartSeries\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n continue;\n }\n const attrs = getAttributes(child);\n const series: Record<string, unknown> = {\n labels: [],\n values: [],\n };\n if (attrs.name !== undefined) {\n // chartDataSchema.name は z.string().optional() なのでそのまま文字列として使用\n series.name = attrs.name;\n }\n\n for (const dp of getChildElements(child)) {\n const dpTag = getTagName(dp);\n if (dpTag !== \"ChartDataPoint\") {\n errors.push(\n unknownChildError(dpTag, \"ChartSeries\", [\"ChartDataPoint\"]),\n );\n continue;\n }\n const dpAttrs = getAttributes(dp);\n if (dpAttrs.label === undefined) {\n errors.push('<ChartDataPoint> requires a \"label\" attribute');\n }\n if (dpAttrs.value === undefined) {\n errors.push('<ChartDataPoint> requires a \"value\" attribute');\n }\n if (dpAttrs.label === undefined || dpAttrs.value === undefined) {\n continue;\n }\n const numValue = Number(dpAttrs.value);\n if (isNaN(numValue)) {\n errors.push(\n `Cannot convert \"${dpAttrs.value}\" to number in <ChartDataPoint> \"value\" attribute`,\n );\n continue;\n }\n (series.labels as string[]).push(dpAttrs.label);\n (series.values as number[]).push(numValue);\n }\n data.push(series);\n }\n result.data = data;\n}\n\nfunction convertTableChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const columns: Record<string, unknown>[] = [];\n const rows: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"Col\":\n columns.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"Tr\": {\n const rowAttrs = getAttributes(child);\n const cells: Record<string, unknown>[] = [];\n for (const cellEl of getChildElements(child)) {\n const cellTag = getTagName(cellEl);\n if (cellTag !== \"Td\") {\n errors.push(unknownChildError(cellTag, \"Tr\", [\"Td\"]));\n continue;\n }\n const cellAttrs = coerceChildAttrs(\n \"Tr\",\n cellTag,\n getAttributes(cellEl),\n errors,\n );\n applyTextAndRuns(cellEl, cellAttrs);\n cells.push(cellAttrs);\n }\n const row: Record<string, unknown> = { cells };\n if (rowAttrs.height !== undefined) {\n const h = Number(rowAttrs.height);\n if (isNaN(h)) {\n errors.push(\n `Cannot convert \"${rowAttrs.height}\" to number in <Tr> \"height\" attribute`,\n );\n } else {\n row.height = h;\n }\n }\n rows.push(row);\n break;\n }\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (columns.length > 0) {\n result.columns = columns;\n } else if (rows.length > 0) {\n // Col が未指定の場合、行のセル数(colspan 考慮)からデフォルトの columns を自動生成\n const maxCells = Math.max(\n ...rows.map((row) =>\n (row.cells as Record<string, unknown>[]).reduce(\n (sum, cell) => sum + ((cell.colspan as number) ?? 1),\n 0,\n ),\n ),\n );\n result.columns = Array.from({ length: maxCells }, () => ({}));\n }\n if (rows.length > 0) {\n result.rows = rows;\n }\n}\n\nfunction convertTreeItem(\n element: XmlElement,\n errors: string[],\n): Record<string, unknown> {\n const attrs = getAttributes(element);\n if (attrs.label === undefined) {\n errors.push('<TreeItem> requires a \"label\" attribute');\n }\n const item: Record<string, unknown> = {};\n if (attrs.label !== undefined) {\n item.label = attrs.label;\n }\n if (attrs.color !== undefined) {\n item.color = attrs.color;\n }\n if (attrs.textColor !== undefined) {\n item.textColor = attrs.textColor;\n }\n const children = getChildElements(element);\n if (children.length > 0) {\n item.children = children\n .map((child) => {\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, \"TreeItem\", [\"TreeItem\"]));\n return null;\n }\n return convertTreeItem(child, errors);\n })\n .filter((item): item is Record<string, unknown> => item !== null);\n }\n return item;\n}\n\nfunction convertTreeChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Tree> must have exactly 1 <TreeItem> child element, but got ${childElements.length}`,\n );\n return;\n }\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n return;\n }\n result.data = convertTreeItem(child, errors);\n}\n\n// SVG 要素を XML 文字列に再構築する\nconst svgBuilder = new XMLBuilder({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n});\n\nfunction serializeSvgElement(svgElement: XmlElement): string {\n return String(svgBuilder.build([svgElement]));\n}\n\nfunction convertSvgChildren(\n _rule: NodeSpecificChildRule,\n _tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Svg>: Expected exactly one <svg> child element, but found ${childElements.length} child element(s)`,\n );\n return;\n }\n\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"svg\") {\n errors.push(`<Svg>: Expected <svg> child element, but found <${tag}>`);\n return;\n }\n\n result.svgContent = serializeSvgElement(child);\n}\n\nfunction convertInlineRunsChildren(\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n // インラインフォーマットタグ以外の子要素がある場合はエラー\n for (const el of childElements) {\n const tag = getTagName(el);\n if (!INLINE_FORMAT_TAGS.has(tag)) {\n const allowedTags = INLINE_FORMAT_TAG_LIST.map((t) => `<${t}>`);\n const allowedList = `${allowedTags.slice(0, -1).join(\", \")}, and ${allowedTags[allowedTags.length - 1]}`;\n errors.push(\n `<${tagName}>: Unexpected child element <${tag}>. Only ${allowedList} are allowed inside <${tagName}>`,\n );\n return;\n }\n }\n if (!node || childElements.length === 0) return;\n const runsResult = buildRunsAndText(node);\n if (runsResult) {\n result.runs = runsResult.runs;\n result.text = runsResult.text;\n }\n}\n\n/** NodeSpecificChildRule を持つノードの専用 converter テーブル */\nconst NODE_SPECIFIC_CHILD_CONVERTERS: Record<\n string,\n NodeSpecificChildConverter\n> = {\n matrix: convertMatrixChildren,\n flow: convertFlowChildren,\n chart: convertChartChildren,\n table: convertTableChildren,\n tree: convertTreeChildren,\n svg: convertSvgChildren,\n};\n\n/** NodeMetadata の xmlChildRule に従って child element を変換する */\nfunction applyXmlChildRule(\n rule: XmlChildRule,\n nodeType: string,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n if (rule.kind === \"inline-runs\") {\n convertInlineRunsChildren(tagName, childElements, result, errors, node);\n return;\n }\n if (rule.kind === \"repeated\") {\n convertRepeatedChildren(rule, tagName, childElements, result, errors);\n return;\n }\n const converter = NODE_SPECIFIC_CHILD_CONVERTERS[nodeType];\n if (!converter) {\n throw new Error(\n `No node-specific child converter registered for node type: ${nodeType}`,\n );\n }\n converter(rule, tagName, childElements, result, errors);\n}\n\n// ===== Theme tokens =====\nconst THEME_TOKEN_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst THEME_TOKEN_VALUE_PATTERN = /^[0-9A-Fa-f]{6}$/;\n// 値全体が \"$name\"(\"#\" プレフィックスは任意)のときトークン参照とみなす\nconst TOKEN_REF_PATTERN = /^(#?)\\$([A-Za-z][A-Za-z0-9_-]*)$/;\n// backgroundGradient 文字列中に現れる \"$name\" 参照\nconst TOKEN_REF_IN_GRADIENT_PATTERN = /#?\\$([A-Za-z][A-Za-z0-9_-]*)/g;\n\nfunction parseThemeElement(\n element: XmlElement,\n tokens: Record<string, string>,\n errors: string[],\n): void {\n if (getChildElements(element).length > 0) {\n errors.push(\n `<Theme>: Child elements are not supported. Declare tokens as attributes (e.g. <Theme accent=\"1D4ED8\" />)`,\n );\n }\n for (const [name, rawValue] of Object.entries(getAttributes(element))) {\n if (!THEME_TOKEN_NAME_PATTERN.test(name)) {\n errors.push(\n `<Theme>: Invalid token name \"${name}\". Token names must start with a letter and contain only letters, digits, \"_\", and \"-\"`,\n );\n continue;\n }\n const value = rawValue.replace(/^#/, \"\");\n if (!THEME_TOKEN_VALUE_PATTERN.test(value)) {\n errors.push(\n `<Theme>: Invalid color value \"${rawValue}\" for token \"${name}\". Expected 6-digit hex (e.g. \"1D4ED8\")`,\n );\n continue;\n }\n tokens[name] = value;\n }\n}\n\n// トークン参照を解決する対象のキー判定。\n// pom の色属性は \"...Color\" / \"...Colors\" / \"highlight\" に統一されているため、\n// キー名ベースで判定することでテキスト内容(text 等)の \"$...\" を誤置換しない。\nfunction isColorKey(key: string): boolean {\n return /colors?$/i.test(key) || key === \"highlight\";\n}\n\nfunction resolveThemeToken(\n name: string,\n hashPrefix: string,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): string | undefined {\n const value = tokens[name];\n if (value !== undefined) {\n return `${hashPrefix}${value}`;\n }\n if (!themeDeclared) {\n errors.push(\n `Theme token \"$${name}\" is referenced, but no <Theme> is declared. Add a top-level <Theme ${name}=\"RRGGBB\" /> element`,\n );\n } else {\n const suggestion = findClosestMatch(name, Object.keys(tokens));\n errors.push(\n `Unknown theme token \"$${name}\"${suggestion ? `. Did you mean \"$${suggestion}\"?` : \"\"}`,\n );\n }\n return undefined;\n}\n\ninterface ThemeContext {\n tokens: Record<string, string>;\n declared: boolean;\n}\n\nfunction resolveThemeTokensDeep(\n value: unknown,\n key: string | null,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): unknown {\n if (typeof value === \"string\") {\n if (key === \"backgroundGradient\") {\n return value.replace(\n TOKEN_REF_IN_GRADIENT_PATTERN,\n (matched, name: string) => {\n const resolved = resolveThemeToken(\n name,\n \"#\",\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? matched;\n },\n );\n }\n if (key !== null && isColorKey(key)) {\n const match = TOKEN_REF_PATTERN.exec(value.trim());\n if (match) {\n const resolved = resolveThemeToken(\n match[2],\n match[1],\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? value;\n }\n }\n return value;\n }\n if (Array.isArray(value)) {\n // 配列はキー文脈を維持して要素ごとに解決する(chartColors 等の文字列配列に対応)\n return value.map((item) =>\n resolveThemeTokensDeep(item, key, tokens, themeDeclared, errors),\n );\n }\n if (typeof value === \"object\" && value !== null) {\n const result: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value)) {\n result[childKey] = resolveThemeTokensDeep(\n childValue,\n childKey,\n tokens,\n themeDeclared,\n errors,\n );\n }\n return result;\n }\n return value;\n}\n\n// ===== Node conversion =====\nfunction convertElement(\n node: XmlElement,\n errors: string[],\n theme: ThemeContext,\n): Record<string, unknown> | null {\n const tagName = getTagName(node);\n const def = getNodeMetadataByTag(tagName);\n const attrs = getAttributes(node);\n const childElements = getChildElements(node);\n const textContent = getTextContent(node);\n\n if (def) {\n return convertPomNode(\n def.type,\n tagName,\n attrs,\n childElements,\n textContent,\n errors,\n theme,\n node,\n );\n } else {\n errors.push(`Unknown tag: <${tagName}>`);\n return null;\n }\n}\n\nfunction convertPomNode(\n nodeType: string,\n tagName: string,\n attrs: Record<string, string>,\n childElements: XmlElement[],\n textContent: string | undefined,\n errors: string[],\n theme: ThemeContext,\n xmlNode?: XmlElement,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { type: nodeType };\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n\n // Expand dot-notation attributes (e.g., fill.color=\"hex\" → { fill: { color: \"hex\" } })\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (prefix === \"type\") continue;\n const rule = getCoercionRule(nodeType, prefix);\n if (rule) {\n result[prefix] = coerceDotGroup(prefix, subAttrs, rule, tagName, errors);\n } else {\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${prefix}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${prefix}\"`);\n }\n }\n }\n\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key === \"type\") continue;\n // Conflict check: dot-notation and regular attribute for the same key\n if (key in dotGroups) {\n const ruleForConflict = getCoercionRule(nodeType, key);\n if (ruleForConflict) {\n const resolved = resolveMixedNotationShorthand(value, ruleForConflict);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes (e.g., \"${key}.xxx\"). Use one or the other, not both`,\n );\n continue;\n }\n const rule = getCoercionRule(nodeType, key);\n if (rule) {\n const coerced = coerceWithRule(value, rule);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (UNIVERSAL_ATTRS.has(key)) {\n // Allow universal attributes (e.g., x/y for Layer children)\n result[key] = coerceFallback(value);\n } else {\n // Unknown attribute\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(key, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${key}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${key}\"`);\n }\n }\n }\n\n // Text content → text property for nodes that support it\n if (textContent !== undefined && def.textContentProperty) {\n if (!(def.textContentProperty in result)) {\n result[def.textContentProperty] = textContent;\n }\n }\n\n // Child element notation for complex properties\n const childRule = def.xmlChildRule;\n if (childRule && childElements.length > 0) {\n applyXmlChildRule(\n childRule,\n nodeType,\n tagName,\n childElements,\n result,\n errors,\n xmlNode,\n );\n }\n // Children for container nodes\n else if (\n def.childPolicy.kind === \"pom-children\" &&\n childElements.length > 0\n ) {\n const convertedChildren = childElements\n .map((child) => convertElement(child, errors, theme))\n .filter((child): child is Record<string, unknown> => child !== null);\n result.children = convertedChildren;\n }\n // Leaf nodes that shouldn't have child elements\n else if (\n def.childPolicy.kind !== \"pom-children\" &&\n !childRule &&\n childElements.length > 0\n ) {\n errors.push(\n `<${tagName}>: Unexpected child elements. <${tagName}> does not accept child elements`,\n );\n }\n\n // テーマトークン参照(色属性中の \"$name\")を Zod 検証より前に解決する。\n // children は各子ノードの convertPomNode で解決済みのためスキップする。\n for (const [key, value] of Object.entries(result)) {\n if (key === \"type\" || key === \"children\") continue;\n result[key] = resolveThemeTokensDeep(\n value,\n key,\n theme.tokens,\n theme.declared,\n errors,\n );\n }\n\n // Zod validation for leaf nodes\n if (def.childPolicy.kind !== \"pom-children\") {\n validateLeafNode(nodeType, result, errors);\n }\n\n // Icon: normalize color / bgColor\n if (nodeType === \"icon\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (typeof result.bgColor === \"string\" && !result.bgColor.startsWith(\"#\")) {\n result.bgColor = `#${result.bgColor}`;\n }\n }\n\n // Svg: normalize color and validate svgContent\n if (nodeType === \"svg\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (result.svgContent === undefined) {\n errors.push(\"<Svg>: A <svg> child element is required\");\n }\n }\n\n return result;\n}\n\n/**\n * XML 文字列を POMNode 配列に変換する。\n *\n * 最上位は `<Slide>` 要素と `<Theme>` 要素のみが許容される。各 `<Slide>` が\n * 1 つのスライドに対応し、その子要素がスライドのルート POMNode となる。\n * 子要素が複数ある場合は暗黙的に VStack でラップされる。\n *\n * `<Theme>` は文書全体に適用されるデザイントークン(配色)の宣言で、最大 1 つ\n * 置ける。属性名がトークン名、属性値が 6 桁 hex の色値となり、各ノードの色属性\n * から `$トークン名` で参照できる。参照は parse 時に解決されるため、返される\n * POMNode には解決済みの hex 値が入る(`<Theme>` 自体はノードにならない)。\n *\n * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して\n * 適切な型(number, boolean, array, object)に変換される。\n * 未知のタグ名が指定された場合はエラーがスローされる。\n *\n * @example\n * ```typescript\n * import { parseXml, buildPptx } from \"@hirokisakabe/pom\";\n *\n * const xml = `\n * <Slide>\n * <VStack gap=\"16\" padding=\"32\">\n * <Text fontSize=\"32\" bold=\"true\">売上レポート</Text>\n * </VStack>\n * </Slide>\n * `;\n *\n * const nodes = parseXml(xml);\n * const pptx = await buildPptx(nodes, { w: 1280, h: 720 });\n * ```\n */\nexport function parseXml(xmlString: string): POMNode[] {\n if (!xmlString.trim()) return [];\n\n const parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n parseAttributeValue: false,\n parseTagValue: false,\n trimValues: false,\n });\n\n const wrappedXml = `<__root__>${xmlString}</__root__>`;\n const parsed: XmlElement[] = parser.parse(wrappedXml) as XmlElement[];\n\n if (!parsed || parsed.length === 0) return [];\n\n const rootElement = parsed[0];\n const rootChildren = (rootElement[\"__root__\"] ?? []) as XmlNode[];\n\n const errors: string[] = [];\n const topLevelElements = rootChildren.filter(\n (child): child is XmlElement => !isTextNode(child),\n );\n\n // <Theme> はスライドより先に収集する(出現位置によらず文書全体に適用)\n const theme: ThemeContext = { tokens: {}, declared: false };\n for (const element of topLevelElements) {\n if (getTagName(element) !== \"Theme\") continue;\n if (theme.declared) {\n errors.push(\n `Only one <Theme> element is allowed, but multiple were found`,\n );\n continue;\n }\n theme.declared = true;\n parseThemeElement(element, theme.tokens, errors);\n }\n\n const slideElements = topLevelElements.filter(\n (element) => getTagName(element) !== \"Theme\",\n );\n\n const nodes: POMNode[] = [];\n for (const slideEl of slideElements) {\n const tagName = getTagName(slideEl);\n if (tagName !== \"Slide\") {\n errors.push(\n `Top-level element must be <Slide> or <Theme>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`,\n );\n continue;\n }\n if (Object.keys(getAttributes(slideEl)).length > 0) {\n errors.push(`<Slide>: Attributes are not supported`);\n }\n const slideChildren = getChildElements(slideEl);\n if (slideChildren.length === 0) {\n errors.push(`<Slide> must contain at least one child element`);\n continue;\n }\n const converted = slideChildren\n .map((child) => convertElement(child, errors, theme))\n .filter((c): c is Record<string, unknown> => c !== null);\n if (converted.length === 0) continue;\n if (converted.length === 1) {\n nodes.push(converted[0] as POMNode);\n } else {\n nodes.push({\n type: \"vstack\",\n children: converted,\n } as POMNode);\n }\n }\n\n if (errors.length > 0) {\n throw new ParseXmlError(errors);\n }\n\n return nodes;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parseXml.js","names":[],"sources":["../../src/parseXml/parseXml.ts"],"sourcesContent":["import { XMLBuilder, XMLParser } from \"fast-xml-parser\";\nimport type { z } from \"zod\";\nimport type { POMNode } from \"../types.ts\";\nimport {\n getNodeMetadata,\n getNodeMetadataByTag,\n NODE_METADATA,\n} from \"../registry/nodeMetadata.ts\";\nimport {\n INLINE_BOOLEAN_FORMATS,\n INLINE_FORMAT_TAG_LIST,\n INLINE_FORMAT_TAGS,\n INLINE_LINK_TAG,\n INLINE_MARK_TAG,\n INLINE_SPAN_TAG,\n MARK_DEFAULT_HIGHLIGHT_COLOR,\n formatExpectedTags,\n type NodeSpecificChildRule,\n type RepeatedChildRule,\n type TextRun,\n type XmlChildRule,\n} from \"../registry/xmlChildRules.ts\";\nimport {\n type CoercionRule,\n NODE_COERCION_MAP,\n CHILD_ELEMENT_COERCION_MAP,\n coerceWithRule,\n coerceFallback,\n getObjectShapeFromRule,\n resolveMixedNotationShorthand,\n} from \"./coercionRules.ts\";\n\n// ===== ParseXmlError =====\nexport class ParseXmlError extends Error {\n public readonly errors: string[];\n constructor(errors: string[]) {\n const message = `XML validation failed (${errors.length} error${errors.length > 1 ? \"s\" : \"\"}):\\n${errors.map((e) => ` - ${e}`).join(\"\\n\")}`;\n super(message);\n this.name = \"ParseXmlError\";\n this.errors = errors;\n }\n}\n\n// ===== Tag name → POM node type mapping =====\nexport const TAG_TO_TYPE: Record<string, string> = Object.fromEntries(\n NODE_METADATA.map((def) => [def.tagName, def.type]),\n);\n// Attributes allowed on any node (e.g., x/y for Layer children positioning)\nconst UNIVERSAL_ATTRS = new Set([\"x\", \"y\"]);\n\n// ===== Validation helpers =====\nfunction getKnownAttributes(nodeType: string): string[] {\n const rules = NODE_COERCION_MAP[nodeType];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array<number>(n + 1).fill(0),\n );\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] =\n a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1]\n : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n return dp[m][n];\n}\n\nfunction findClosestMatch(\n input: string,\n candidates: string[],\n): string | undefined {\n const threshold = Math.max(2, Math.floor(input.length / 2));\n let bestMatch: string | undefined;\n let bestDistance = Infinity;\n for (const candidate of candidates) {\n const dist = levenshteinDistance(\n input.toLowerCase(),\n candidate.toLowerCase(),\n );\n if (dist < bestDistance && dist <= threshold) {\n bestDistance = dist;\n bestMatch = candidate;\n }\n }\n return bestMatch;\n}\n\nfunction getKnownChildAttributes(tagName: string): string[] {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n if (!rules) return [];\n return Object.keys(rules);\n}\n\nfunction formatZodIssue(\n issue: z.core.$ZodIssue,\n tagName: string,\n): string | null {\n const path = issue.path;\n // Skip children-related issues (validated recursively)\n if (path.length > 0 && path[0] === \"children\") return null;\n // Skip \"type\" field issues (set internally)\n if (path.length === 1 && path[0] === \"type\") return null;\n\n const attrName = path.length > 0 ? String(path[0]) : undefined;\n\n const code = issue.code;\n\n if (code === \"invalid_type\") {\n // Missing required attribute\n if (issue.input === undefined) {\n if (attrName) {\n return `<${tagName}>: Missing required attribute \"${attrName}\"`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n // Type mismatch\n if (attrName) {\n return `<${tagName}>: Invalid type for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"invalid_value\") {\n if (attrName) {\n const values = (issue as unknown as { values: string[] }).values;\n if (values) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". Expected: ${values.map((v) => `\"${v}\"`).join(\", \")}`;\n }\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n if (code === \"too_small\" || code === \"too_big\") {\n if (attrName) {\n return `<${tagName}>: Invalid value for attribute \"${attrName}\". ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n }\n\n // Generic fallback\n if (attrName) {\n return `<${tagName}>: Attribute \"${attrName}\": ${issue.message}`;\n }\n return `<${tagName}>: ${issue.message}`;\n}\n\n// Properties that may be legitimately absent when using child element notation\n// or when the property is optional in practice (even if required in schema).\nfunction validateLeafNode(\n nodeType: string,\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n if (def.childPolicy.kind === \"pom-children\") return;\n const schema = def.schema;\n const tagName = def.tagName;\n const optionalChildProps = new Set(\n def.childPolicy.kind === \"custom\"\n ? (def.childPolicy.optionalProperties ?? [])\n : [],\n );\n const parseResult = schema.safeParse(result);\n if (!parseResult.success) {\n const seen = new Set<string>();\n for (const issue of parseResult.error.issues) {\n // Skip only top-level missing child-element properties (path.length === 1)\n // Nested issues (e.g., data.children[0].label) must still be reported\n if (\n optionalChildProps.size > 0 &&\n issue.path.length === 1 &&\n optionalChildProps.has(String(issue.path[0])) &&\n issue.code === \"invalid_type\" &&\n issue.input === undefined\n ) {\n continue;\n }\n // Skip issues for universal attributes (x, y)\n if (issue.path.length > 0 && UNIVERSAL_ATTRS.has(String(issue.path[0]))) {\n continue;\n }\n const msg = formatZodIssue(issue, tagName);\n if (msg && !seen.has(msg)) {\n seen.add(msg);\n errors.push(msg);\n }\n }\n }\n}\n\n// ===== Types for XML parser output (preserveOrder mode) =====\ntype XmlNode = XmlElement | XmlTextNode;\ntype XmlTextNode = { \"#text\": string };\ninterface XmlElement {\n [tagName: string]: XmlNode[] | Record<string, string> | undefined;\n \":@\"?: Record<string, string>;\n}\n\n// ===== Coercion rule lookup =====\n\nfunction getCoercionRule(\n nodeType: string,\n propertyName: string,\n): CoercionRule | undefined {\n return NODE_COERCION_MAP[nodeType]?.[propertyName];\n}\n\n// ===== Dot notation helpers =====\n\n// ===== Dot notation expansion =====\nfunction expandDotNotation(attrs: Record<string, string>): {\n regular: Record<string, string>;\n dotGroups: Record<string, Record<string, string>>;\n} {\n const regular: Record<string, string> = {};\n const dotGroups: Record<string, Record<string, string>> = {};\n\n for (const [key, value] of Object.entries(attrs)) {\n const dotIndex = key.indexOf(\".\");\n if (dotIndex > 0) {\n const prefix = key.substring(0, dotIndex);\n const suffix = key.substring(dotIndex + 1);\n if (!dotGroups[prefix]) dotGroups[prefix] = {};\n dotGroups[prefix][suffix] = value;\n } else {\n regular[key] = value;\n }\n }\n\n return { regular, dotGroups };\n}\n\nfunction coerceDotGroup(\n prefix: string,\n subAttrs: Record<string, string>,\n rule: CoercionRule,\n tagName: string,\n errors: string[],\n): Record<string, unknown> {\n const objectShape = getObjectShapeFromRule(rule);\n\n const obj: Record<string, unknown> = {};\n if (objectShape) {\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n if (objectShape[subKey]) {\n const coerced = coerceWithRule(subValue, objectShape[subKey]);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${prefix}.${subKey}: ${coerced.error}`);\n } else {\n obj[subKey] = coerced.value;\n }\n } else {\n const knownSubKeys = Object.keys(objectShape);\n const suggestion = findClosestMatch(subKey, knownSubKeys);\n errors.push(\n `<${tagName}>: Unknown sub-attribute \"${prefix}.${subKey}\"${suggestion ? `. Did you mean \"${prefix}.${suggestion}\"?` : \"\"}`,\n );\n }\n }\n } else {\n errors.push(\n `<${tagName}>: Attribute \"${prefix}\" does not support dot notation`,\n );\n }\n return obj;\n}\n\n// ===== XML node helpers =====\nfunction isTextNode(node: XmlNode): node is XmlTextNode {\n return \"#text\" in node;\n}\n\nfunction getTagName(node: XmlElement): string {\n for (const key of Object.keys(node)) {\n if (key !== \":@\") return key;\n }\n throw new Error(\"No tag name found in XML element\");\n}\n\nfunction getAttributes(node: XmlElement): Record<string, string> {\n const attrs: Record<string, string> = {};\n const rawAttrs = node[\":@\"];\n if (rawAttrs) {\n for (const [key, value] of Object.entries(rawAttrs)) {\n const attrName = key.startsWith(\"@_\") ? key.slice(2) : key;\n attrs[attrName] = value.trim();\n }\n }\n return attrs;\n}\n\nfunction getChildElements(node: XmlElement): XmlElement[] {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return [];\n return children.filter((child): child is XmlElement => !isTextNode(child));\n}\n\nfunction getTextContent(node: XmlElement): string | undefined {\n const tagName = getTagName(node);\n const children = node[tagName] as XmlNode[] | undefined;\n if (!children) return undefined;\n const textParts: string[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n textParts.push(child[\"#text\"]);\n }\n }\n return textParts.length > 0 ? textParts.join(\"\") : undefined;\n}\n\nfunction getRawChildren(node: XmlElement): XmlNode[] {\n const tagName = getTagName(node);\n return (node[tagName] as XmlNode[] | undefined) ?? [];\n}\n\nfunction hasInlineFormatChildren(childElements: XmlElement[]): boolean {\n return (\n childElements.length > 0 &&\n childElements.every((el) => INLINE_FORMAT_TAGS.has(getTagName(el)))\n );\n}\n\nfunction extractTextRuns(\n children: XmlNode[],\n inherited: Partial<Omit<TextRun, \"text\">> = {},\n): TextRun[] {\n const runs: TextRun[] = [];\n for (const child of children) {\n if (isTextNode(child)) {\n runs.push({ text: child[\"#text\"], ...inherited });\n continue;\n }\n const tag = getTagName(child);\n const innerChildren = getRawChildren(child);\n const booleanFormat = INLINE_BOOLEAN_FORMATS.find(\n (format) => format.tag === tag,\n );\n if (booleanFormat) {\n const next: Partial<Omit<TextRun, \"text\">> = {\n ...inherited,\n [booleanFormat.property]: true,\n };\n // subscript と superscript は OOXML の baseline 値として相互排他\n // (同時 true は描画結果が未定義になる)。後で立てた側を優先して反対側を解除する。\n if (booleanFormat.property === \"subscript\") delete next.superscript;\n if (booleanFormat.property === \"superscript\") delete next.subscript;\n runs.push(...extractTextRuns(innerChildren, next));\n } else if (tag === INLINE_LINK_TAG) {\n // href なしの <A> は外側の href を引き継がない(リンク解除として扱う)\n const next = { ...inherited };\n const href = getAttributes(child).href;\n if (href) {\n next.href = href;\n } else {\n delete next.href;\n }\n runs.push(...extractTextRuns(innerChildren, next));\n } else if (tag === INLINE_MARK_TAG) {\n const rawColor = getAttributes(child).color;\n const highlight =\n rawColor && rawColor.trim() ? rawColor : MARK_DEFAULT_HIGHLIGHT_COLOR;\n runs.push(...extractTextRuns(innerChildren, { ...inherited, highlight }));\n } else if (tag === INLINE_SPAN_TAG) {\n const spanAttrs = getAttributes(child);\n const next = { ...inherited };\n if (spanAttrs.color && spanAttrs.color.trim()) {\n next.color = spanAttrs.color;\n }\n if (spanAttrs.fontFamily && spanAttrs.fontFamily.trim()) {\n next.fontFamily = spanAttrs.fontFamily;\n }\n if (spanAttrs.fontSize && spanAttrs.fontSize.trim()) {\n next.fontSize = Number(spanAttrs.fontSize);\n }\n if (spanAttrs.letterSpacing && spanAttrs.letterSpacing.trim()) {\n next.letterSpacing = Number(spanAttrs.letterSpacing);\n }\n runs.push(...extractTextRuns(innerChildren, next));\n }\n }\n return runs;\n}\n\nfunction buildRunsAndText(\n node: XmlElement,\n): { runs: TextRun[]; text: string } | null {\n const rawChildren = getRawChildren(node);\n const childElements = rawChildren.filter(\n (c): c is XmlElement => !isTextNode(c),\n );\n if (!hasInlineFormatChildren(childElements)) return null;\n const runs = extractTextRuns(rawChildren);\n const text = runs.map((r) => r.text).join(\"\");\n return { runs, text };\n}\n\nfunction coerceChildAttrs(\n parentTagName: string,\n tagName: string,\n attrs: Record<string, string>,\n errors: string[],\n): Record<string, unknown> {\n const rules = CHILD_ELEMENT_COERCION_MAP[tagName];\n const result: Record<string, unknown> = {};\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n // Process dot-notation attributes\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (rules && rules[prefix]) {\n result[prefix] = coerceDotGroup(\n prefix,\n subAttrs,\n rules[prefix],\n `${parentTagName}.${tagName}`,\n errors,\n );\n } else if (rules) {\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${prefix}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[prefix] = {};\n for (const [subKey, subValue] of Object.entries(subAttrs)) {\n (result[prefix] as Record<string, unknown>)[subKey] =\n coerceFallback(subValue);\n }\n }\n }\n\n // Process regular attributes\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key in dotGroups) {\n if (rules && rules[key]) {\n const resolved = resolveMixedNotationShorthand(value, rules[key]);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${parentTagName}>.<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes. Use one or the other, not both`,\n );\n continue;\n }\n if (rules && rules[key]) {\n const coerced = coerceWithRule(value, rules[key]);\n if (coerced.error !== null) {\n errors.push(`<${parentTagName}>.<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (rules) {\n // Unknown attribute on child element\n const knownAttrs = getKnownChildAttributes(tagName);\n const suggestion = findClosestMatch(key, knownAttrs);\n errors.push(\n `<${parentTagName}>.<${tagName}>: Unknown attribute \"${key}\"${suggestion ? `. Did you mean \"${suggestion}\"?` : \"\"}`,\n );\n } else {\n result[key] = coerceFallback(value);\n }\n }\n return result;\n}\n\n// ===== Child element converters =====\n\nfunction unknownChildError(\n childTag: string,\n parentTag: string,\n expectedTags: readonly string[],\n): string {\n return `Unknown child element <${childTag}> inside <${parentTag}>. Expected: ${formatExpectedTags(expectedTags)}`;\n}\n\n/** element の text content / インライン装飾を attrs の text / runs へ反映する */\nfunction applyTextAndRuns(\n element: XmlElement,\n attrs: Record<string, unknown>,\n): void {\n const runsResult = buildRunsAndText(element);\n if (runsResult) {\n attrs.runs = runsResult.runs;\n attrs.text = runsResult.text;\n } else {\n const textContent = getTextContent(element);\n if (textContent !== undefined && !(\"text\" in attrs)) {\n attrs.text = textContent;\n }\n }\n}\n\n/** RepeatedChildRule (単一種類の child tag の繰り返し → 配列 property) の汎用 converter */\nfunction convertRepeatedChildren(\n rule: RepeatedChildRule,\n parentTag: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== rule.childTag) {\n errors.push(unknownChildError(tag, parentTag, [rule.childTag]));\n continue;\n }\n const attrs = coerceChildAttrs(\n parentTag,\n tag,\n getAttributes(child),\n errors,\n );\n if (rule.allowsItemText) {\n applyTextAndRuns(child, attrs);\n }\n items.push(attrs);\n }\n result[rule.property] = items;\n}\n\n/** NodeSpecificChildRule を処理する専用 converter のシグネチャ */\ntype NodeSpecificChildConverter = (\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n) => void;\n\nfunction convertMatrixChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const items: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"MatrixAxes\":\n result.axes = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixQuadrants\":\n result.quadrants = coerceChildAttrs(\n tagName,\n tag,\n getAttributes(child),\n errors,\n );\n break;\n case \"MatrixItem\":\n items.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (items.length > 0) {\n result.items = items;\n }\n}\n\nfunction convertFlowChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const nodes: Record<string, unknown>[] = [];\n const connections: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"FlowNode\":\n nodes.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"FlowConnection\":\n connections.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (nodes.length > 0) {\n result.nodes = nodes;\n }\n if (connections.length > 0) {\n result.connections = connections;\n }\n}\n\nfunction convertChartChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const data: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n if (tag !== \"ChartSeries\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n continue;\n }\n const attrs = getAttributes(child);\n const series: Record<string, unknown> = {\n labels: [],\n values: [],\n };\n if (attrs.name !== undefined) {\n // chartDataSchema.name は z.string().optional() なのでそのまま文字列として使用\n series.name = attrs.name;\n }\n\n for (const dp of getChildElements(child)) {\n const dpTag = getTagName(dp);\n if (dpTag !== \"ChartDataPoint\") {\n errors.push(\n unknownChildError(dpTag, \"ChartSeries\", [\"ChartDataPoint\"]),\n );\n continue;\n }\n const dpAttrs = getAttributes(dp);\n if (dpAttrs.label === undefined) {\n errors.push('<ChartDataPoint> requires a \"label\" attribute');\n }\n if (dpAttrs.value === undefined) {\n errors.push('<ChartDataPoint> requires a \"value\" attribute');\n }\n if (dpAttrs.label === undefined || dpAttrs.value === undefined) {\n continue;\n }\n const numValue = Number(dpAttrs.value);\n if (isNaN(numValue)) {\n errors.push(\n `Cannot convert \"${dpAttrs.value}\" to number in <ChartDataPoint> \"value\" attribute`,\n );\n continue;\n }\n (series.labels as string[]).push(dpAttrs.label);\n (series.values as number[]).push(numValue);\n }\n data.push(series);\n }\n result.data = data;\n}\n\nfunction convertTableChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n const columns: Record<string, unknown>[] = [];\n const rows: Record<string, unknown>[] = [];\n for (const child of childElements) {\n const tag = getTagName(child);\n switch (tag) {\n case \"Col\":\n columns.push(\n coerceChildAttrs(tagName, tag, getAttributes(child), errors),\n );\n break;\n case \"Tr\": {\n const rowAttrs = getAttributes(child);\n const cells: Record<string, unknown>[] = [];\n for (const cellEl of getChildElements(child)) {\n const cellTag = getTagName(cellEl);\n if (cellTag !== \"Td\") {\n errors.push(unknownChildError(cellTag, \"Tr\", [\"Td\"]));\n continue;\n }\n const cellAttrs = coerceChildAttrs(\n \"Tr\",\n cellTag,\n getAttributes(cellEl),\n errors,\n );\n applyTextAndRuns(cellEl, cellAttrs);\n cells.push(cellAttrs);\n }\n const row: Record<string, unknown> = { cells };\n if (rowAttrs.height !== undefined) {\n const h = Number(rowAttrs.height);\n if (isNaN(h)) {\n errors.push(\n `Cannot convert \"${rowAttrs.height}\" to number in <Tr> \"height\" attribute`,\n );\n } else {\n row.height = h;\n }\n }\n rows.push(row);\n break;\n }\n default:\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n }\n }\n if (columns.length > 0) {\n result.columns = columns;\n } else if (rows.length > 0) {\n // Col が未指定の場合、行のセル数(colspan 考慮)からデフォルトの columns を自動生成\n const maxCells = Math.max(\n ...rows.map((row) =>\n (row.cells as Record<string, unknown>[]).reduce(\n (sum, cell) => sum + ((cell.colspan as number) ?? 1),\n 0,\n ),\n ),\n );\n result.columns = Array.from({ length: maxCells }, () => ({}));\n }\n if (rows.length > 0) {\n result.rows = rows;\n }\n}\n\nfunction convertTreeItem(\n element: XmlElement,\n errors: string[],\n): Record<string, unknown> {\n const attrs = getAttributes(element);\n if (attrs.label === undefined) {\n errors.push('<TreeItem> requires a \"label\" attribute');\n }\n const item: Record<string, unknown> = {};\n if (attrs.label !== undefined) {\n item.label = attrs.label;\n }\n if (attrs.color !== undefined) {\n item.color = attrs.color;\n }\n if (attrs.textColor !== undefined) {\n item.textColor = attrs.textColor;\n }\n const children = getChildElements(element);\n if (children.length > 0) {\n item.children = children\n .map((child) => {\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, \"TreeItem\", [\"TreeItem\"]));\n return null;\n }\n return convertTreeItem(child, errors);\n })\n .filter((item): item is Record<string, unknown> => item !== null);\n }\n return item;\n}\n\nfunction convertTreeChildren(\n rule: NodeSpecificChildRule,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Tree> must have exactly 1 <TreeItem> child element, but got ${childElements.length}`,\n );\n return;\n }\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"TreeItem\") {\n errors.push(unknownChildError(tag, tagName, rule.expectedTags));\n return;\n }\n result.data = convertTreeItem(child, errors);\n}\n\n// SVG 要素を XML 文字列に再構築する\nconst svgBuilder = new XMLBuilder({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n});\n\nfunction serializeSvgElement(svgElement: XmlElement): string {\n return String(svgBuilder.build([svgElement]));\n}\n\nfunction convertSvgChildren(\n _rule: NodeSpecificChildRule,\n _tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n): void {\n if (childElements.length !== 1) {\n errors.push(\n `<Svg>: Expected exactly one <svg> child element, but found ${childElements.length} child element(s)`,\n );\n return;\n }\n\n const child = childElements[0];\n const tag = getTagName(child);\n if (tag !== \"svg\") {\n errors.push(`<Svg>: Expected <svg> child element, but found <${tag}>`);\n return;\n }\n\n result.svgContent = serializeSvgElement(child);\n}\n\nfunction convertInlineRunsChildren(\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n // インラインフォーマットタグ以外の子要素がある場合はエラー\n for (const el of childElements) {\n const tag = getTagName(el);\n if (!INLINE_FORMAT_TAGS.has(tag)) {\n const allowedTags = INLINE_FORMAT_TAG_LIST.map((t) => `<${t}>`);\n const allowedList = `${allowedTags.slice(0, -1).join(\", \")}, and ${allowedTags[allowedTags.length - 1]}`;\n errors.push(\n `<${tagName}>: Unexpected child element <${tag}>. Only ${allowedList} are allowed inside <${tagName}>`,\n );\n return;\n }\n }\n if (!node || childElements.length === 0) return;\n const runsResult = buildRunsAndText(node);\n if (runsResult) {\n result.runs = runsResult.runs;\n result.text = runsResult.text;\n }\n}\n\n/** NodeSpecificChildRule を持つノードの専用 converter テーブル */\nconst NODE_SPECIFIC_CHILD_CONVERTERS: Record<\n string,\n NodeSpecificChildConverter\n> = {\n matrix: convertMatrixChildren,\n flow: convertFlowChildren,\n chart: convertChartChildren,\n table: convertTableChildren,\n tree: convertTreeChildren,\n svg: convertSvgChildren,\n};\n\n/** NodeMetadata の xmlChildRule に従って child element を変換する */\nfunction applyXmlChildRule(\n rule: XmlChildRule,\n nodeType: string,\n tagName: string,\n childElements: XmlElement[],\n result: Record<string, unknown>,\n errors: string[],\n node?: XmlElement,\n): void {\n if (rule.kind === \"inline-runs\") {\n convertInlineRunsChildren(tagName, childElements, result, errors, node);\n return;\n }\n if (rule.kind === \"repeated\") {\n convertRepeatedChildren(rule, tagName, childElements, result, errors);\n return;\n }\n const converter = NODE_SPECIFIC_CHILD_CONVERTERS[nodeType];\n if (!converter) {\n throw new Error(\n `No node-specific child converter registered for node type: ${nodeType}`,\n );\n }\n converter(rule, tagName, childElements, result, errors);\n}\n\n// ===== Theme tokens =====\nconst THEME_TOKEN_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst THEME_TOKEN_VALUE_PATTERN = /^[0-9A-Fa-f]{6}$/;\n// 値全体が \"$name\"(\"#\" プレフィックスは任意)のときトークン参照とみなす\nconst TOKEN_REF_PATTERN = /^(#?)\\$([A-Za-z][A-Za-z0-9_-]*)$/;\n// backgroundGradient / textGradient 文字列中に現れる \"$name\" 参照\nconst TOKEN_REF_IN_GRADIENT_PATTERN = /#?\\$([A-Za-z][A-Za-z0-9_-]*)/g;\n\nfunction parseThemeElement(\n element: XmlElement,\n tokens: Record<string, string>,\n errors: string[],\n): void {\n if (getChildElements(element).length > 0) {\n errors.push(\n `<Theme>: Child elements are not supported. Declare tokens as attributes (e.g. <Theme accent=\"1D4ED8\" />)`,\n );\n }\n for (const [name, rawValue] of Object.entries(getAttributes(element))) {\n if (!THEME_TOKEN_NAME_PATTERN.test(name)) {\n errors.push(\n `<Theme>: Invalid token name \"${name}\". Token names must start with a letter and contain only letters, digits, \"_\", and \"-\"`,\n );\n continue;\n }\n const value = rawValue.replace(/^#/, \"\");\n if (!THEME_TOKEN_VALUE_PATTERN.test(value)) {\n errors.push(\n `<Theme>: Invalid color value \"${rawValue}\" for token \"${name}\". Expected 6-digit hex (e.g. \"1D4ED8\")`,\n );\n continue;\n }\n tokens[name] = value;\n }\n}\n\n// トークン参照を解決する対象のキー判定。\n// pom の色属性は \"...Color\" / \"...Colors\" / \"highlight\" に統一されているため、\n// キー名ベースで判定することでテキスト内容(text 等)の \"$...\" を誤置換しない。\nfunction isColorKey(key: string): boolean {\n return /colors?$/i.test(key) || key === \"highlight\";\n}\n\nfunction resolveThemeToken(\n name: string,\n hashPrefix: string,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): string | undefined {\n const value = tokens[name];\n if (value !== undefined) {\n return `${hashPrefix}${value}`;\n }\n if (!themeDeclared) {\n errors.push(\n `Theme token \"$${name}\" is referenced, but no <Theme> is declared. Add a top-level <Theme ${name}=\"RRGGBB\" /> element`,\n );\n } else {\n const suggestion = findClosestMatch(name, Object.keys(tokens));\n errors.push(\n `Unknown theme token \"$${name}\"${suggestion ? `. Did you mean \"$${suggestion}\"?` : \"\"}`,\n );\n }\n return undefined;\n}\n\ninterface ThemeContext {\n tokens: Record<string, string>;\n declared: boolean;\n}\n\nfunction resolveThemeTokensDeep(\n value: unknown,\n key: string | null,\n tokens: Record<string, string>,\n themeDeclared: boolean,\n errors: string[],\n): unknown {\n if (typeof value === \"string\") {\n if (key === \"backgroundGradient\" || key === \"textGradient\") {\n return value.replace(\n TOKEN_REF_IN_GRADIENT_PATTERN,\n (matched, name: string) => {\n const resolved = resolveThemeToken(\n name,\n \"#\",\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? matched;\n },\n );\n }\n if (key !== null && isColorKey(key)) {\n const match = TOKEN_REF_PATTERN.exec(value.trim());\n if (match) {\n const resolved = resolveThemeToken(\n match[2],\n match[1],\n tokens,\n themeDeclared,\n errors,\n );\n return resolved ?? value;\n }\n }\n return value;\n }\n if (Array.isArray(value)) {\n // 配列はキー文脈を維持して要素ごとに解決する(chartColors 等の文字列配列に対応)\n return value.map((item) =>\n resolveThemeTokensDeep(item, key, tokens, themeDeclared, errors),\n );\n }\n if (typeof value === \"object\" && value !== null) {\n const result: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value)) {\n result[childKey] = resolveThemeTokensDeep(\n childValue,\n childKey,\n tokens,\n themeDeclared,\n errors,\n );\n }\n return result;\n }\n return value;\n}\n\n// ===== Node conversion =====\nfunction convertElement(\n node: XmlElement,\n errors: string[],\n theme: ThemeContext,\n): Record<string, unknown> | null {\n const tagName = getTagName(node);\n const def = getNodeMetadataByTag(tagName);\n const attrs = getAttributes(node);\n const childElements = getChildElements(node);\n const textContent = getTextContent(node);\n\n if (def) {\n return convertPomNode(\n def.type,\n tagName,\n attrs,\n childElements,\n textContent,\n errors,\n theme,\n node,\n );\n } else {\n errors.push(`Unknown tag: <${tagName}>`);\n return null;\n }\n}\n\nfunction convertPomNode(\n nodeType: string,\n tagName: string,\n attrs: Record<string, string>,\n childElements: XmlElement[],\n textContent: string | undefined,\n errors: string[],\n theme: ThemeContext,\n xmlNode?: XmlElement,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { type: nodeType };\n const def = getNodeMetadata(nodeType as POMNode[\"type\"]);\n\n // Expand dot-notation attributes (e.g., fill.color=\"hex\" → { fill: { color: \"hex\" } })\n const { regular: regularAttrs, dotGroups } = expandDotNotation(attrs);\n\n for (const [prefix, subAttrs] of Object.entries(dotGroups)) {\n if (prefix === \"type\") continue;\n const rule = getCoercionRule(nodeType, prefix);\n if (rule) {\n result[prefix] = coerceDotGroup(prefix, subAttrs, rule, tagName, errors);\n } else {\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(prefix, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${prefix}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${prefix}\"`);\n }\n }\n }\n\n for (const [key, value] of Object.entries(regularAttrs)) {\n if (key === \"type\") continue;\n // Conflict check: dot-notation and regular attribute for the same key\n if (key in dotGroups) {\n const ruleForConflict = getCoercionRule(nodeType, key);\n if (ruleForConflict) {\n const resolved = resolveMixedNotationShorthand(value, ruleForConflict);\n if (resolved.mode === \"ignore\") {\n continue;\n }\n if (resolved.mode === \"merge\") {\n result[key] = {\n ...resolved.value,\n ...(result[key] as Record<string, unknown>),\n };\n continue;\n }\n }\n errors.push(\n `<${tagName}>: Attribute \"${key}\" conflicts with dot-notation attributes (e.g., \"${key}.xxx\"). Use one or the other, not both`,\n );\n continue;\n }\n const rule = getCoercionRule(nodeType, key);\n if (rule) {\n const coerced = coerceWithRule(value, rule);\n if (coerced.error !== null) {\n errors.push(`<${tagName}>: ${coerced.error}`);\n } else {\n result[key] = coerced.value;\n }\n } else if (UNIVERSAL_ATTRS.has(key)) {\n // Allow universal attributes (e.g., x/y for Layer children)\n result[key] = coerceFallback(value);\n } else {\n // Unknown attribute\n const knownAttrs = getKnownAttributes(nodeType);\n const suggestion = findClosestMatch(key, knownAttrs);\n if (suggestion) {\n errors.push(\n `<${tagName}>: Unknown attribute \"${key}\". Did you mean \"${suggestion}\"?`,\n );\n } else {\n errors.push(`<${tagName}>: Unknown attribute \"${key}\"`);\n }\n }\n }\n\n // Text content → text property for nodes that support it\n if (textContent !== undefined && def.textContentProperty) {\n if (!(def.textContentProperty in result)) {\n result[def.textContentProperty] = textContent;\n }\n }\n\n // Child element notation for complex properties\n const childRule = def.xmlChildRule;\n if (childRule && childElements.length > 0) {\n applyXmlChildRule(\n childRule,\n nodeType,\n tagName,\n childElements,\n result,\n errors,\n xmlNode,\n );\n }\n // Children for container nodes\n else if (def.childPolicy.kind === \"pom-children\") {\n const convertedChildren = childElements\n .map((child) => convertElement(child, errors, theme))\n .filter((child): child is Record<string, unknown> => child !== null);\n result.children = convertedChildren;\n }\n // Leaf nodes that shouldn't have child elements\n else if (!childRule && childElements.length > 0) {\n errors.push(\n `<${tagName}>: Unexpected child elements. <${tagName}> does not accept child elements`,\n );\n }\n\n // テーマトークン参照(色属性中の \"$name\")を Zod 検証より前に解決する。\n // children は各子ノードの convertPomNode で解決済みのためスキップする。\n for (const [key, value] of Object.entries(result)) {\n if (key === \"type\" || key === \"children\") continue;\n result[key] = resolveThemeTokensDeep(\n value,\n key,\n theme.tokens,\n theme.declared,\n errors,\n );\n }\n\n // Zod validation for leaf nodes\n if (def.childPolicy.kind !== \"pom-children\") {\n validateLeafNode(nodeType, result, errors);\n }\n\n // Icon: normalize color / bgColor\n if (nodeType === \"icon\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (typeof result.bgColor === \"string\" && !result.bgColor.startsWith(\"#\")) {\n result.bgColor = `#${result.bgColor}`;\n }\n }\n\n // Svg: normalize color and validate svgContent\n if (nodeType === \"svg\") {\n if (typeof result.color === \"string\" && !result.color.startsWith(\"#\")) {\n result.color = `#${result.color}`;\n }\n if (result.svgContent === undefined) {\n errors.push(\"<Svg>: A <svg> child element is required\");\n }\n }\n\n return result;\n}\n\n/**\n * XML 文字列を POMNode 配列に変換する。\n *\n * 最上位は `<Slide>` 要素と `<Theme>` 要素のみが許容される。各 `<Slide>` が\n * 1 つのスライドに対応し、その子要素がスライドのルート POMNode となる。\n * 子要素が複数ある場合は暗黙的に VStack でラップされる。\n *\n * `<Theme>` は文書全体に適用されるデザイントークン(配色)の宣言で、最大 1 つ\n * 置ける。属性名がトークン名、属性値が 6 桁 hex の色値となり、各ノードの色属性\n * から `$トークン名` で参照できる。参照は parse 時に解決されるため、返される\n * POMNode には解決済みの hex 値が入る(`<Theme>` 自体はノードにならない)。\n *\n * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して\n * 適切な型(number, boolean, array, object)に変換される。\n * 未知のタグ名が指定された場合はエラーがスローされる。\n *\n * @example\n * ```typescript\n * import { parseXml, buildPptx } from \"@hirokisakabe/pom\";\n *\n * const xml = `\n * <Slide>\n * <VStack gap=\"16\" padding=\"32\">\n * <Text fontSize=\"32\" bold=\"true\">売上レポート</Text>\n * </VStack>\n * </Slide>\n * `;\n *\n * const nodes = parseXml(xml);\n * const pptx = await buildPptx(nodes, { w: 1280, h: 720 });\n * ```\n */\nexport function parseXml(xmlString: string): POMNode[] {\n if (!xmlString.trim()) return [];\n\n const parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n parseAttributeValue: false,\n parseTagValue: false,\n trimValues: false,\n });\n\n const wrappedXml = `<__root__>${xmlString}</__root__>`;\n const parsed: XmlElement[] = parser.parse(wrappedXml) as XmlElement[];\n\n if (!parsed || parsed.length === 0) return [];\n\n const rootElement = parsed[0];\n const rootChildren = (rootElement[\"__root__\"] ?? []) as XmlNode[];\n\n const errors: string[] = [];\n const topLevelElements = rootChildren.filter(\n (child): child is XmlElement => !isTextNode(child),\n );\n\n // <Theme> はスライドより先に収集する(出現位置によらず文書全体に適用)\n const theme: ThemeContext = { tokens: {}, declared: false };\n for (const element of topLevelElements) {\n if (getTagName(element) !== \"Theme\") continue;\n if (theme.declared) {\n errors.push(\n `Only one <Theme> element is allowed, but multiple were found`,\n );\n continue;\n }\n theme.declared = true;\n parseThemeElement(element, theme.tokens, errors);\n }\n\n const slideElements = topLevelElements.filter(\n (element) => getTagName(element) !== \"Theme\",\n );\n\n const nodes: POMNode[] = [];\n for (const slideEl of slideElements) {\n const tagName = getTagName(slideEl);\n if (tagName !== \"Slide\") {\n errors.push(\n `Top-level element must be <Slide> or <Theme>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`,\n );\n continue;\n }\n if (Object.keys(getAttributes(slideEl)).length > 0) {\n errors.push(`<Slide>: Attributes are not supported`);\n }\n const slideChildren = getChildElements(slideEl);\n if (slideChildren.length === 0) {\n errors.push(`<Slide> must contain at least one child element`);\n continue;\n }\n const converted = slideChildren\n .map((child) => convertElement(child, errors, theme))\n .filter((c): c is Record<string, unknown> => c !== null);\n if (converted.length === 0) continue;\n if (converted.length === 1) {\n nodes.push(converted[0] as POMNode);\n } else {\n nodes.push({\n type: \"vstack\",\n children: converted,\n } as POMNode);\n }\n }\n\n if (errors.length > 0) {\n throw new ParseXmlError(errors);\n }\n\n return nodes;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { pxToEmu } from "./units.js";
|
|
2
|
+
//#region src/renderPptx/glowEffects.ts
|
|
3
|
+
async function loadJSZip() {
|
|
4
|
+
const mod = await import("jszip");
|
|
5
|
+
return mod.default ?? mod;
|
|
6
|
+
}
|
|
7
|
+
const MARKER_PREFIX = "pom-glow:";
|
|
8
|
+
/**
|
|
9
|
+
* glow 仕様を一意なマーカー名にマップし、後処理で
|
|
10
|
+
* `<a:effectLst><a:glow>` 要素として挿入できるようにするレジストリ
|
|
11
|
+
*/
|
|
12
|
+
var GlowEffectRegistry = class {
|
|
13
|
+
markerBySpec = /* @__PURE__ */ new Map();
|
|
14
|
+
registered = [];
|
|
15
|
+
/**
|
|
16
|
+
* glow を登録し、`objectName` に渡すマーカー文字列を返す。
|
|
17
|
+
* 同じ仕様の glow は同じマーカーを返す。
|
|
18
|
+
*/
|
|
19
|
+
register(glow) {
|
|
20
|
+
const sizePx = glow.size ?? 8;
|
|
21
|
+
const opacity = glow.opacity ?? .75;
|
|
22
|
+
const color = (glow.color ?? "FFFFFF").replace(/^#/, "").toUpperCase();
|
|
23
|
+
const specKey = JSON.stringify({
|
|
24
|
+
sizePx,
|
|
25
|
+
opacity,
|
|
26
|
+
color
|
|
27
|
+
});
|
|
28
|
+
const existing = this.markerBySpec.get(specKey);
|
|
29
|
+
if (existing) return existing;
|
|
30
|
+
const marker = `${MARKER_PREFIX}${this.registered.length}`;
|
|
31
|
+
this.markerBySpec.set(specKey, marker);
|
|
32
|
+
this.registered.push({
|
|
33
|
+
marker,
|
|
34
|
+
sizePx,
|
|
35
|
+
opacity,
|
|
36
|
+
color
|
|
37
|
+
});
|
|
38
|
+
return marker;
|
|
39
|
+
}
|
|
40
|
+
get isEmpty() {
|
|
41
|
+
return this.registered.length === 0;
|
|
42
|
+
}
|
|
43
|
+
get entries() {
|
|
44
|
+
return this.registered;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* DrawingML の `<a:glow>` 要素を構築する (effectLst で囲まない、内側だけ)
|
|
49
|
+
*
|
|
50
|
+
* - rad (光彩半径): px → EMU
|
|
51
|
+
* - alpha (透明度): 0-1 → 1/1000 % (0-100000)
|
|
52
|
+
*/
|
|
53
|
+
function buildGlowXml(entry) {
|
|
54
|
+
const rad = Math.round(pxToEmu(entry.sizePx));
|
|
55
|
+
const alpha = Math.round(entry.opacity * 1e5);
|
|
56
|
+
return `<a:glow rad="${rad}"><a:srgbClr val="${entry.color}"><a:alpha val="${alpha}"/></a:srgbClr></a:glow>`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 1 つの slide XML を受け取り、登録された glow を該当 shape に適用した XML を返す
|
|
60
|
+
*
|
|
61
|
+
* `<p:cNvPr ... name="pom-glow:N">` を含む `<p:sp>` ブロックを正規表現で検出し、
|
|
62
|
+
* 以下のいずれかで `<a:glow>` を挿入する:
|
|
63
|
+
*
|
|
64
|
+
* 1. 既に `<a:effectLst>...</a:effectLst>` が存在する場合
|
|
65
|
+
* (= pptxgenjs が shape の `shadow` 等で同要素を生成済み):
|
|
66
|
+
* その `effectLst` 内側の末尾 (`</a:effectLst>` の直前) に `<a:glow>` を追加する。
|
|
67
|
+
* OOXML スキーマ上 `<p:spPr>` 内の `<a:effectLst>` は最大 1 個までで、二重に
|
|
68
|
+
* 並べると不正になるため。
|
|
69
|
+
* 2. 既存 `effectLst` が無い場合:
|
|
70
|
+
* `</p:spPr>` の直前に新規 `<a:effectLst><a:glow/></a:effectLst>` を挿入する。
|
|
71
|
+
*
|
|
72
|
+
* pptxgenjs は cNvPr を `<p:cNvPr.../>` (self-closing) と
|
|
73
|
+
* `<p:cNvPr...></p:cNvPr>` (open+close) のどちらの形式でも出力し得るため、
|
|
74
|
+
* cNvPr の閉じ形式に依存せず name 属性のあとから lazy に進める。
|
|
75
|
+
*/
|
|
76
|
+
function applyGlowToXml(xml, registry) {
|
|
77
|
+
let result = xml;
|
|
78
|
+
for (const entry of registry.entries) {
|
|
79
|
+
const glowXml = buildGlowXml(entry);
|
|
80
|
+
const re = new RegExp(`(<p:cNvPr[^>]*name="${entry.marker}"[\\s\\S]*?)(</p:spPr>)`, "g");
|
|
81
|
+
result = result.replace(re, (_match, prefix, suffix) => {
|
|
82
|
+
const block = prefix;
|
|
83
|
+
const closingIdx = block.lastIndexOf("</a:effectLst>");
|
|
84
|
+
if (closingIdx >= 0) return `${block.substring(0, closingIdx)}${glowXml}${block.substring(closingIdx)}${suffix}`;
|
|
85
|
+
return `${block}<a:effectLst>${glowXml}</a:effectLst>${suffix}`;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 出力 zip 内のスライド XML に glow 効果を挿入する
|
|
92
|
+
*/
|
|
93
|
+
async function applyGlowEffects(data, registry) {
|
|
94
|
+
const zip = await (await loadJSZip()).loadAsync(data);
|
|
95
|
+
const slidePaths = Object.keys(zip.files).filter((path) => /^ppt\/slides\/slide\d+\.xml$/.test(path));
|
|
96
|
+
for (const path of slidePaths) {
|
|
97
|
+
const file = zip.file(path);
|
|
98
|
+
if (!file) continue;
|
|
99
|
+
const original = await file.async("text");
|
|
100
|
+
const replaced = applyGlowToXml(original, registry);
|
|
101
|
+
if (replaced !== original) zip.file(path, replaced);
|
|
102
|
+
}
|
|
103
|
+
return zip;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* pptx インスタンスの write / writeFile をラップし、出力時に glow 後処理を適用する。
|
|
107
|
+
*
|
|
108
|
+
* 既に他の後処理 (gradientFills 等) によりラップされている場合に備え、
|
|
109
|
+
* 元の関数を保存してチェーン可能にしている。
|
|
110
|
+
*/
|
|
111
|
+
function patchPptxWriteForGlowEffects(pptx, registry) {
|
|
112
|
+
if (registry.isEmpty) return;
|
|
113
|
+
const originalWrite = pptx.write.bind(pptx);
|
|
114
|
+
const originalWriteFile = pptx.writeFile.bind(pptx);
|
|
115
|
+
const patchedWrite = async (rawProps) => {
|
|
116
|
+
const props = typeof rawProps === "string" ? { outputType: rawProps } : rawProps;
|
|
117
|
+
const zip = await applyGlowEffects(await originalWrite({ outputType: "uint8array" }), registry);
|
|
118
|
+
const outputType = props?.outputType;
|
|
119
|
+
if (outputType === "STREAM") return zip.generateAsync({
|
|
120
|
+
type: "nodebuffer",
|
|
121
|
+
compression: props?.compression ? "DEFLATE" : "STORE"
|
|
122
|
+
});
|
|
123
|
+
if (outputType) return zip.generateAsync({ type: outputType });
|
|
124
|
+
return zip.generateAsync({
|
|
125
|
+
type: "blob",
|
|
126
|
+
compression: props?.compression ? "DEFLATE" : "STORE"
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
pptx.write = patchedWrite;
|
|
130
|
+
const patchedWriteFile = async (rawProps) => {
|
|
131
|
+
const props = typeof rawProps === "string" ? { fileName: rawProps } : rawProps;
|
|
132
|
+
if (!(typeof process !== "undefined" && Boolean(process.versions?.node))) return originalWriteFile(props);
|
|
133
|
+
const rawName = props?.fileName ?? "Presentation.pptx";
|
|
134
|
+
const fileName = rawName.toLowerCase().endsWith(".pptx") ? rawName : `${rawName}.pptx`;
|
|
135
|
+
const buffer = await patchedWrite({
|
|
136
|
+
outputType: "nodebuffer",
|
|
137
|
+
compression: props?.compression
|
|
138
|
+
});
|
|
139
|
+
await (await import("fs")).promises.writeFile(fileName, buffer);
|
|
140
|
+
return fileName;
|
|
141
|
+
};
|
|
142
|
+
pptx.writeFile = patchedWriteFile;
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
export { GlowEffectRegistry, patchPptxWriteForGlowEffects };
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=glowEffects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glowEffects.js","names":[],"sources":["../../src/renderPptx/glowEffects.ts"],"sourcesContent":["/**\n * Shape / Icon の glow (光彩) 効果の実現\n *\n * pptxgenjs の Shape options には glow 相当のネイティブサポートがないため\n * (TextPropsOptions の glow は文字グリフ用)、以下の方式で実現する:\n *\n * 1. レンダリング時: glow 指定のある shape に一意なマーカー名を `objectName`\n * として埋め込み、レジストリに登録する。マーカー名は `<p:cNvPr name=\"...\"/>`\n * として PPTX 出力 XML に書き込まれる。\n * 2. 出力時: pptx.write() / writeFile() をラップし、出力 zip 内のスライド XML\n * の該当 `<p:sp>` の `<p:spPr>` 末尾に DrawingML ネイティブの\n * `<a:effectLst><a:glow>...</a:glow></a:effectLst>` を挿入する。\n *\n * 文字列置換採用理由 / 注意点は gradientFills.ts と同様。\n */\nimport type { TextGlow } from \"../types.ts\";\nimport { pxToEmu } from \"./units.ts\";\n\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\ntype WriteProps = NonNullable<Parameters<PptxGenJSInstance[\"write\"]>[0]>;\ntype WriteFileProps = NonNullable<\n Parameters<PptxGenJSInstance[\"writeFile\"]>[0]\n>;\n\nasync function loadJSZip(): Promise<typeof import(\"jszip\")> {\n const mod = await import(\"jszip\");\n /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n return (mod as any).default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\n\ninterface RegisteredGlow {\n /** objectName に埋め込むマーカー文字列 */\n marker: string;\n /** size (px) — XML 出力時に EMU に変換する */\n sizePx: number;\n /** opacity 0-1 */\n opacity: number;\n /** 6桁 HEX (# なし、大文字) */\n color: string;\n}\n\nconst MARKER_PREFIX = \"pom-glow:\";\n\n/**\n * glow 仕様を一意なマーカー名にマップし、後処理で\n * `<a:effectLst><a:glow>` 要素として挿入できるようにするレジストリ\n */\nexport class GlowEffectRegistry {\n private readonly markerBySpec = new Map<string, string>();\n private readonly registered: RegisteredGlow[] = [];\n\n /**\n * glow を登録し、`objectName` に渡すマーカー文字列を返す。\n * 同じ仕様の glow は同じマーカーを返す。\n */\n register(glow: TextGlow): string {\n const sizePx = glow.size ?? 8;\n const opacity = glow.opacity ?? 0.75;\n const color = (glow.color ?? \"FFFFFF\").replace(/^#/, \"\").toUpperCase();\n const specKey = JSON.stringify({ sizePx, opacity, color });\n const existing = this.markerBySpec.get(specKey);\n if (existing) return existing;\n\n const index = this.registered.length;\n const marker = `${MARKER_PREFIX}${index}`;\n this.markerBySpec.set(specKey, marker);\n this.registered.push({ marker, sizePx, opacity, color });\n return marker;\n }\n\n get isEmpty(): boolean {\n return this.registered.length === 0;\n }\n\n get entries(): readonly RegisteredGlow[] {\n return this.registered;\n }\n}\n\n/**\n * DrawingML の `<a:glow>` 要素を構築する (effectLst で囲まない、内側だけ)\n *\n * - rad (光彩半径): px → EMU\n * - alpha (透明度): 0-1 → 1/1000 % (0-100000)\n */\nfunction buildGlowXml(entry: RegisteredGlow): string {\n const rad = Math.round(pxToEmu(entry.sizePx));\n const alpha = Math.round(entry.opacity * 100000);\n return `<a:glow rad=\"${rad}\"><a:srgbClr val=\"${entry.color}\"><a:alpha val=\"${alpha}\"/></a:srgbClr></a:glow>`;\n}\n\n/**\n * 1 つの slide XML を受け取り、登録された glow を該当 shape に適用した XML を返す\n *\n * `<p:cNvPr ... name=\"pom-glow:N\">` を含む `<p:sp>` ブロックを正規表現で検出し、\n * 以下のいずれかで `<a:glow>` を挿入する:\n *\n * 1. 既に `<a:effectLst>...</a:effectLst>` が存在する場合\n * (= pptxgenjs が shape の `shadow` 等で同要素を生成済み):\n * その `effectLst` 内側の末尾 (`</a:effectLst>` の直前) に `<a:glow>` を追加する。\n * OOXML スキーマ上 `<p:spPr>` 内の `<a:effectLst>` は最大 1 個までで、二重に\n * 並べると不正になるため。\n * 2. 既存 `effectLst` が無い場合:\n * `</p:spPr>` の直前に新規 `<a:effectLst><a:glow/></a:effectLst>` を挿入する。\n *\n * pptxgenjs は cNvPr を `<p:cNvPr.../>` (self-closing) と\n * `<p:cNvPr...></p:cNvPr>` (open+close) のどちらの形式でも出力し得るため、\n * cNvPr の閉じ形式に依存せず name 属性のあとから lazy に進める。\n */\nfunction applyGlowToXml(xml: string, registry: GlowEffectRegistry): string {\n let result = xml;\n for (const entry of registry.entries) {\n const glowXml = buildGlowXml(entry);\n const re = new RegExp(\n `(<p:cNvPr[^>]*name=\"${entry.marker}\"[\\\\s\\\\S]*?)(</p:spPr>)`,\n \"g\",\n );\n result = result.replace(re, (_match, prefix, suffix) => {\n const block = prefix as string;\n // shape ブロック内に既存の effectLst があるかを `</a:effectLst>` の有無で判定する。\n // 別 shape の effectLst を誤って書き換えないよう、検索範囲は prefix\n // (= 該当 cNvPr 〜 直後の </p:spPr> の手前まで) に限定している。\n const closingIdx = block.lastIndexOf(\"</a:effectLst>\");\n if (closingIdx >= 0) {\n const before = block.substring(0, closingIdx);\n const after = block.substring(closingIdx);\n return `${before}${glowXml}${after}${suffix as string}`;\n }\n return `${block}<a:effectLst>${glowXml}</a:effectLst>${suffix as string}`;\n });\n }\n return result;\n}\n\n/**\n * 出力 zip 内のスライド XML に glow 効果を挿入する\n */\nexport async function applyGlowEffects(\n data: Uint8Array | ArrayBuffer,\n registry: GlowEffectRegistry,\n): Promise<import(\"jszip\")> {\n const JSZip = await loadJSZip();\n const zip = await JSZip.loadAsync(data);\n\n const slidePaths = Object.keys(zip.files).filter((path) =>\n /^ppt\\/slides\\/slide\\d+\\.xml$/.test(path),\n );\n for (const path of slidePaths) {\n const file = zip.file(path);\n if (!file) continue;\n const original = await file.async(\"text\");\n const replaced = applyGlowToXml(original, registry);\n if (replaced !== original) {\n zip.file(path, replaced);\n }\n }\n return zip;\n}\n\n/**\n * pptx インスタンスの write / writeFile をラップし、出力時に glow 後処理を適用する。\n *\n * 既に他の後処理 (gradientFills 等) によりラップされている場合に備え、\n * 元の関数を保存してチェーン可能にしている。\n */\nexport function patchPptxWriteForGlowEffects(\n pptx: PptxGenJSInstance,\n registry: GlowEffectRegistry,\n): void {\n if (registry.isEmpty) return;\n\n const originalWrite = pptx.write.bind(pptx);\n const originalWriteFile = pptx.writeFile.bind(pptx);\n\n const patchedWrite = async (rawProps?: WriteProps | string) => {\n const props: WriteProps | undefined =\n typeof rawProps === \"string\"\n ? ({ outputType: rawProps } as WriteProps)\n : rawProps;\n const data = (await originalWrite({\n outputType: \"uint8array\",\n })) as Uint8Array;\n const zip = await applyGlowEffects(data, registry);\n\n const outputType = props?.outputType;\n if (outputType === \"STREAM\") {\n return zip.generateAsync({\n type: \"nodebuffer\",\n compression: props?.compression ? \"DEFLATE\" : \"STORE\",\n });\n }\n if (outputType) {\n return zip.generateAsync({ type: outputType });\n }\n return zip.generateAsync({\n type: \"blob\",\n compression: props?.compression ? \"DEFLATE\" : \"STORE\",\n });\n };\n pptx.write = patchedWrite;\n\n const patchedWriteFile = async (rawProps?: WriteFileProps | string) => {\n const props: WriteFileProps | undefined =\n typeof rawProps === \"string\" ? { fileName: rawProps } : rawProps;\n const isNode =\n typeof process !== \"undefined\" && Boolean(process.versions?.node);\n if (!isNode) {\n return originalWriteFile(props);\n }\n const rawName = props?.fileName ?? \"Presentation.pptx\";\n const fileName = rawName.toLowerCase().endsWith(\".pptx\")\n ? rawName\n : `${rawName}.pptx`;\n const buffer = (await patchedWrite({\n outputType: \"nodebuffer\",\n compression: props?.compression,\n })) as Buffer;\n const fs = await import(\"fs\");\n await fs.promises.writeFile(fileName, buffer);\n return fileName;\n };\n pptx.writeFile = patchedWriteFile;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseLinearGradient } from "../shared/gradient.js";
|
|
1
|
+
import { parseGradient, parseLinearGradient } from "../shared/gradient.js";
|
|
2
2
|
//#region src/renderPptx/gradientFills.ts
|
|
3
3
|
async function loadJSZip() {
|
|
4
4
|
const mod = await import("jszip");
|
|
@@ -50,26 +50,54 @@ var GradientFillRegistry = class {
|
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
52
52
|
* backgroundGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。
|
|
53
|
+
* linear-gradient / radial-gradient の両方に対応。
|
|
53
54
|
* パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。
|
|
54
55
|
*/
|
|
55
56
|
function registerBackgroundGradient(value, opacity, registry) {
|
|
56
|
-
const gradient =
|
|
57
|
+
const gradient = parseGradient(value);
|
|
57
58
|
if (!gradient) return void 0;
|
|
58
59
|
return registry.register(gradient, opacity);
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
61
|
-
*
|
|
62
|
+
* textGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。
|
|
63
|
+
* 戻り値のマーカー色を text run の color に渡すと、pptxgenjs が出力する
|
|
64
|
+
* `<a:rPr><a:solidFill><a:srgbClr val="マーカー色"/></a:solidFill></a:rPr>` が
|
|
65
|
+
* 後処理で gradFill に置換され、PowerPoint 上ネイティブの文字グラデーションとして
|
|
66
|
+
* 表示・編集可能になる。
|
|
67
|
+
*
|
|
68
|
+
* textGradient は radial-gradient を受け付けない (linear-gradient のみ)。
|
|
69
|
+
* パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。
|
|
70
|
+
*/
|
|
71
|
+
function registerTextGradient(value, registry) {
|
|
72
|
+
const linear = parseLinearGradient(value);
|
|
73
|
+
if (!linear) return void 0;
|
|
74
|
+
return registry.register({
|
|
75
|
+
kind: "linear",
|
|
76
|
+
value: linear
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Gradient を DrawingML の `<a:gradFill>` 要素に変換する
|
|
62
81
|
*
|
|
63
82
|
* - カラーストップ位置: % → 1/1000 % (0-100000)
|
|
64
|
-
* -
|
|
83
|
+
* - 角度 (linear): CSS 基準 (0deg = 上向き) → DrawingML 基準 (0 = 右向き、1/60000 度)
|
|
84
|
+
* - 中心位置 (radial): CSS 風 % → DrawingML `<a:fillToRect l/t/r/b>` (1/1000 %)。
|
|
85
|
+
* fillToRect は焦点を表す矩形で、中心位置 (cx, cy) に対し
|
|
86
|
+
* l=cx*1000 / t=cy*1000 / r=(100-cx)*1000 / b=(100-cy)*1000 とする。
|
|
87
|
+
* PowerPoint の radial fill は path="circle" 1 種類で、shape (circle / ellipse) や
|
|
88
|
+
* size キーワードを描画上区別しない。要素の縦横比に応じて自動で楕円状になる。
|
|
65
89
|
*/
|
|
66
90
|
function buildGradFillXml(gradient, opacity) {
|
|
67
91
|
const alphaXml = opacity !== void 0 ? `<a:alpha val="${Math.round(opacity * 1e5)}"/>` : "";
|
|
68
|
-
const gsXml = gradient.stops.map((stop) => {
|
|
92
|
+
const gsXml = gradient.value.stops.map((stop) => {
|
|
69
93
|
return `<a:gs pos="${Math.round(stop.position * 1e3)}">${alphaXml ? `<a:srgbClr val="${stop.color}">${alphaXml}</a:srgbClr>` : `<a:srgbClr val="${stop.color}"/>`}</a:gs>`;
|
|
70
94
|
}).join("");
|
|
71
|
-
|
|
72
|
-
|
|
95
|
+
if (gradient.kind === "linear") {
|
|
96
|
+
const dmlAngle = ((gradient.value.angle - 90) % 360 + 360) % 360;
|
|
97
|
+
return `<a:gradFill flip="none" rotWithShape="1"><a:gsLst>${gsXml}</a:gsLst><a:lin ang="${Math.round(dmlAngle * 6e4)}" scaled="0"/></a:gradFill>`;
|
|
98
|
+
}
|
|
99
|
+
const { centerX, centerY } = gradient.value;
|
|
100
|
+
return `<a:gradFill flip="none" rotWithShape="1"><a:gsLst>${gsXml}</a:gsLst><a:path path="circle"><a:fillToRect l="${Math.round(centerX * 1e3)}" t="${Math.round(centerY * 1e3)}" r="${Math.round((100 - centerX) * 1e3)}" b="${Math.round((100 - centerY) * 1e3)}"/></a:path></a:gradFill>`;
|
|
73
101
|
}
|
|
74
102
|
/**
|
|
75
103
|
* 出力 zip 内のスライド XML のマーカー solidFill を gradFill に置換する
|
|
@@ -134,6 +162,6 @@ function patchPptxWriteForGradientFills(pptx, registry) {
|
|
|
134
162
|
pptx.writeFile = patchedWriteFile;
|
|
135
163
|
}
|
|
136
164
|
//#endregion
|
|
137
|
-
export { GradientFillRegistry, patchPptxWriteForGradientFills, registerBackgroundGradient };
|
|
165
|
+
export { GradientFillRegistry, patchPptxWriteForGradientFills, registerBackgroundGradient, registerTextGradient };
|
|
138
166
|
|
|
139
167
|
//# sourceMappingURL=gradientFills.js.map
|