@hirokisakabe/pom 8.3.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +35 -23
  2. package/dist/autoFit/autoFit.js +1 -1
  3. package/dist/autoFit/autoFit.js.map +1 -1
  4. package/dist/autoFit/strategies/reduceFontSize.js +16 -14
  5. package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
  6. package/dist/autoFit/strategies/reduceGapAndPadding.js +13 -20
  7. package/dist/autoFit/strategies/reduceGapAndPadding.js.map +1 -1
  8. package/dist/autoFit/strategies/reduceTableRowHeight.js +8 -2
  9. package/dist/autoFit/strategies/reduceTableRowHeight.js.map +1 -1
  10. package/dist/autoFit/strategies/uniformScale.js +19 -20
  11. package/dist/autoFit/strategies/uniformScale.js.map +1 -1
  12. package/dist/autoFit/strategyResult.js +15 -0
  13. package/dist/autoFit/strategyResult.js.map +1 -0
  14. package/dist/buildPptx.js +1 -1
  15. package/dist/calcYogaLayout/calcYogaLayout.js +16 -27
  16. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  17. package/dist/diagnostics.d.ts +1 -1
  18. package/dist/diagnostics.d.ts.map +1 -1
  19. package/dist/diagnostics.js.map +1 -1
  20. package/dist/parseXml/coercionRules.js +43 -8
  21. package/dist/parseXml/coercionRules.js.map +1 -1
  22. package/dist/parseXml/parseXml.d.ts +8 -3
  23. package/dist/parseXml/parseXml.d.ts.map +1 -1
  24. package/dist/parseXml/parseXml.js +192 -212
  25. package/dist/parseXml/parseXml.js.map +1 -1
  26. package/dist/parseXml/serializeXml.d.ts.map +1 -1
  27. package/dist/parseXml/serializeXml.js +12 -17
  28. package/dist/parseXml/serializeXml.js.map +1 -1
  29. package/dist/registry/definitions/arrow.js +2 -2
  30. package/dist/registry/definitions/arrow.js.map +1 -1
  31. package/dist/registry/definitions/chart.js +2 -2
  32. package/dist/registry/definitions/chart.js.map +1 -1
  33. package/dist/registry/definitions/compositeNodes.js +7 -12
  34. package/dist/registry/definitions/compositeNodes.js.map +1 -1
  35. package/dist/registry/definitions/icon.js +2 -2
  36. package/dist/registry/definitions/icon.js.map +1 -1
  37. package/dist/registry/definitions/image.js +2 -2
  38. package/dist/registry/definitions/image.js.map +1 -1
  39. package/dist/registry/definitions/layer.js +4 -5
  40. package/dist/registry/definitions/layer.js.map +1 -1
  41. package/dist/registry/definitions/line.js +2 -2
  42. package/dist/registry/definitions/line.js.map +1 -1
  43. package/dist/registry/definitions/list.js +3 -4
  44. package/dist/registry/definitions/list.js.map +1 -1
  45. package/dist/registry/definitions/shape.js +2 -2
  46. package/dist/registry/definitions/shape.js.map +1 -1
  47. package/dist/registry/definitions/stack.js +3 -4
  48. package/dist/registry/definitions/stack.js.map +1 -1
  49. package/dist/registry/definitions/svg.js +2 -2
  50. package/dist/registry/definitions/svg.js.map +1 -1
  51. package/dist/registry/definitions/table.js +2 -2
  52. package/dist/registry/definitions/table.js.map +1 -1
  53. package/dist/registry/definitions/text.js +2 -2
  54. package/dist/registry/definitions/text.js.map +1 -1
  55. package/dist/registry/index.js.map +1 -1
  56. package/dist/registry/nodeMetadata.js +208 -0
  57. package/dist/registry/nodeMetadata.js.map +1 -0
  58. package/dist/registry/nodeRegistry.js +3 -0
  59. package/dist/registry/nodeRegistry.js.map +1 -1
  60. package/dist/registry/xmlChildRules.js +55 -0
  61. package/dist/registry/xmlChildRules.js.map +1 -0
  62. package/dist/renderPptx/nodes/arrow.js +7 -28
  63. package/dist/renderPptx/nodes/arrow.js.map +1 -1
  64. package/dist/renderPptx/nodes/chart.js +2 -7
  65. package/dist/renderPptx/nodes/chart.js.map +1 -1
  66. package/dist/renderPptx/nodes/flow.js +6 -13
  67. package/dist/renderPptx/nodes/flow.js.map +1 -1
  68. package/dist/renderPptx/nodes/icon.js +4 -2
  69. package/dist/renderPptx/nodes/icon.js.map +1 -1
  70. package/dist/renderPptx/nodes/image.js +5 -13
  71. package/dist/renderPptx/nodes/image.js.map +1 -1
  72. package/dist/renderPptx/nodes/line.js +9 -33
  73. package/dist/renderPptx/nodes/line.js.map +1 -1
  74. package/dist/renderPptx/nodes/list.js +8 -20
  75. package/dist/renderPptx/nodes/list.js.map +1 -1
  76. package/dist/renderPptx/nodes/matrix.js +10 -11
  77. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  78. package/dist/renderPptx/nodes/processArrow.js +9 -16
  79. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  80. package/dist/renderPptx/nodes/pyramid.js +5 -7
  81. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  82. package/dist/renderPptx/nodes/shape.js +7 -20
  83. package/dist/renderPptx/nodes/shape.js.map +1 -1
  84. package/dist/renderPptx/nodes/svg.js +2 -5
  85. package/dist/renderPptx/nodes/svg.js.map +1 -1
  86. package/dist/renderPptx/nodes/table.js +2 -5
  87. package/dist/renderPptx/nodes/table.js.map +1 -1
  88. package/dist/renderPptx/nodes/text.js +4 -1
  89. package/dist/renderPptx/nodes/text.js.map +1 -1
  90. package/dist/renderPptx/nodes/timeline.js +20 -22
  91. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  92. package/dist/renderPptx/nodes/tree.js +5 -5
  93. package/dist/renderPptx/nodes/tree.js.map +1 -1
  94. package/dist/renderPptx/renderPptx.js +13 -29
  95. package/dist/renderPptx/renderPptx.js.map +1 -1
  96. package/dist/renderPptx/textOptions.js +32 -8
  97. package/dist/renderPptx/textOptions.js.map +1 -1
  98. package/dist/renderPptx/units.js +11 -1
  99. package/dist/renderPptx/units.js.map +1 -1
  100. package/dist/renderPptx/utils/backgroundBorder.js +103 -57
  101. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  102. package/dist/renderPptx/utils/contentArea.js +26 -9
  103. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  104. package/dist/renderPptx/utils/scaleToFit.js +17 -1
  105. package/dist/renderPptx/utils/scaleToFit.js.map +1 -1
  106. package/dist/renderPptx/utils/straightLine.js +41 -0
  107. package/dist/renderPptx/utils/straightLine.js.map +1 -0
  108. package/dist/renderPptx/utils/visualStyle.js +113 -0
  109. package/dist/renderPptx/utils/visualStyle.js.map +1 -0
  110. package/dist/shared/boxSpacing.js +63 -0
  111. package/dist/shared/boxSpacing.js.map +1 -0
  112. package/dist/shared/walkTree.js +1 -7
  113. package/dist/shared/walkTree.js.map +1 -1
  114. package/dist/toPositioned/toPositioned.js +1 -1
  115. package/dist/toPositioned/toPositioned.js.map +1 -1
  116. package/dist/types.d.ts +1127 -95
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js +47 -17
  119. package/dist/types.js.map +1 -1
  120. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentCenterX),\n y: pxToIn(parentBottomY),\n w: 0,\n h: pxToIn(midY - parentBottomY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(minX),\n y: pxToIn(midY),\n w: pxToIn(maxX - minX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 垂直線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(childCenterX),\n y: pxToIn(midY),\n w: 0,\n h: pxToIn(childTopY - midY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentRightX),\n y: pxToIn(parentCenterY),\n w: pxToIn(midX - parentRightX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(minY),\n w: 0,\n h: pxToIn(maxY - minY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 水平線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(childCenterY),\n w: pxToIn(childLeftX - midX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return ctx.pptx.ShapeType.rect;\n case \"roundRect\":\n return ctx.pptx.ShapeType.roundRect;\n case \"ellipse\":\n return ctx.pptx.ShapeType.ellipse;\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n // ノードの背景\n ctx.slide.addShape(shapeType, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fill: { color },\n line: { color: \"333333\", width: pxToPt(1 * sf) },\n });\n\n // ノードのラベル\n ctx.slide.addText(layoutNode.item.label, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fontSize: pxToPt(12 * sf),\n fontFace: \"Noto Sans JP\",\n color: \"FFFFFF\",\n align: \"center\",\n valign: \"middle\",\n });\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n treeSize.width,\n treeSize.height,\n \"tree\",\n ctx.buildContext.diagnostics,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n const defaultTextColor = stripHash(node.textColor) ?? \"FFFFFF\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentCenterX),\n y: pxToIn(parentBottomY),\n w: 0,\n h: pxToIn(midY - parentBottomY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(minX),\n y: pxToIn(midY),\n w: pxToIn(maxX - minX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 垂直線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(childCenterX),\n y: pxToIn(midY),\n w: 0,\n h: pxToIn(childTopY - midY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentRightX),\n y: pxToIn(parentCenterY),\n w: pxToIn(midX - parentRightX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(minY),\n w: 0,\n h: pxToIn(maxY - minY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 水平線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(childCenterY),\n w: pxToIn(childLeftX - midX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return ctx.pptx.ShapeType.rect;\n case \"roundRect\":\n return ctx.pptx.ShapeType.roundRect;\n case \"ellipse\":\n return ctx.pptx.ShapeType.ellipse;\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n // ノードの背景\n ctx.slide.addShape(shapeType, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fill: { color },\n line: { color: \"333333\", width: pxToPt(1 * sf) },\n });\n\n // ノードのラベル\n ctx.slide.addText(layoutNode.item.label, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fontSize: pxToPt(12 * sf),\n fontFace: \"Noto Sans JP\",\n color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,\n align: \"center\",\n valign: \"middle\",\n });\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n treeSize,\n ctx,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { getImageData } from "../shared/measureImage.js";
2
+ import { resolveBoxSpacing } from "../shared/boxSpacing.js";
2
3
  import { getNodeDef } from "../registry/nodeRegistry.js";
3
4
  import { pxToIn, pxToPt } from "./units.js";
4
5
  import { convertStrike, convertUnderline } from "./textOptions.js";
5
6
  import "../registry/index.js";
6
7
  import { registerBackgroundGradient } from "./gradientFills.js";
7
- import { renderBackgroundAndBorder } from "./utils/backgroundBorder.js";
8
+ import { renderBackgroundAndBorder, renderBorderOnly } from "./utils/backgroundBorder.js";
8
9
  //#region src/renderPptx/renderPptx.ts
9
10
  async function loadPptxGenJS() {
10
11
  const mod = await import("pptxgenjs");
@@ -111,13 +112,15 @@ function defineSlideMasterFromOptions(pptx, master) {
111
112
  else if ("data" in master.background) masterProps.background = { data: master.background.data };
112
113
  else if ("image" in master.background) masterProps.background = { path: master.background.image };
113
114
  }
114
- if (master.margin !== void 0) if (typeof master.margin === "number") masterProps.margin = pxToIn(master.margin);
115
- else masterProps.margin = [
116
- pxToIn(master.margin.top ?? 0),
117
- pxToIn(master.margin.right ?? 0),
118
- pxToIn(master.margin.bottom ?? 0),
119
- pxToIn(master.margin.left ?? 0)
120
- ];
115
+ if (master.margin !== void 0) {
116
+ const margin = resolveBoxSpacing(master.margin);
117
+ masterProps.margin = [
118
+ pxToIn(margin.top),
119
+ pxToIn(margin.right),
120
+ pxToIn(margin.bottom),
121
+ pxToIn(margin.left)
122
+ ];
123
+ }
121
124
  if (master.objects && master.objects.length > 0) masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));
122
125
  if (master.slideNumber) masterProps.slideNumber = {
123
126
  x: pxToIn(master.slideNumber.x),
@@ -177,27 +180,8 @@ async function renderPptx(pages, slidePx, buildContext, master) {
177
180
  * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)
178
181
  */
179
182
  function renderNode(node, isRoot = false) {
180
- if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootBackgroundGradient) && !rootHasOpacity)) {
181
- const { border, borderRadius } = node;
182
- if (Boolean(border && (border.color !== void 0 || border.width !== void 0 || border.dashType !== void 0))) {
183
- const line = {
184
- color: border?.color ?? "000000",
185
- width: border?.width !== void 0 ? pxToPt(border.width) : void 0,
186
- dashType: border?.dashType
187
- };
188
- const shapeType = borderRadius ? ctx.pptx.ShapeType.roundRect : ctx.pptx.ShapeType.rect;
189
- const rectRadius = borderRadius ? Math.min(borderRadius / Math.min(node.w, node.h) * 2, 1) : void 0;
190
- ctx.slide.addShape(shapeType, {
191
- x: pxToIn(node.x),
192
- y: pxToIn(node.y),
193
- w: pxToIn(node.w),
194
- h: pxToIn(node.h),
195
- fill: { type: "none" },
196
- line,
197
- rectRadius
198
- });
199
- }
200
- } else renderBackgroundAndBorder(node, ctx);
183
+ if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootBackgroundGradient) && !rootHasOpacity)) renderBorderOnly(node, ctx);
184
+ else renderBackgroundAndBorder(node, ctx);
201
185
  const def = getNodeDef(node.type);
202
186
  switch (def.category) {
203
187
  case "leaf":
@@ -1 +1 @@
1
- {"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["// pptxgenjs の型定義\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\n\n// pptxgenjs は CJS パッケージのため動的 import で読み込む\nasync function loadPptxGenJS(): Promise<new () => PptxGenJSInstance> {\n const pptxModule = await import(\"pptxgenjs\");\n // CJS default export の解決: module.default.default (ESM wrapper) または module.default\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n const mod = pptxModule as any;\n return mod.default?.default ?? mod.default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\ntype SlideMasterProps = Parameters<PptxGenJSInstance[\"defineSlideMaster\"]>[0];\ntype ImageProps = {\n x: number;\n y: number;\n w: number;\n h: number;\n path?: string;\n data?: string;\n};\nimport type {\n PositionedNode,\n SlideMasterOptions,\n MasterObject,\n} from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { convertUnderline, convertStrike } from \"./textOptions.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { renderBackgroundAndBorder } from \"./utils/backgroundBorder.ts\";\nimport { registerBackgroundGradient } from \"./gradientFills.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nconst DEFAULT_MASTER_NAME = \"POM_MASTER\";\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\n/**\n * MasterObject を pptxgenjs の objects 形式に変換する\n */\nfunction convertMasterObject(\n obj: MasterObject,\n): SlideMasterProps[\"objects\"] extends (infer T)[] | undefined ? T : never {\n switch (obj.type) {\n case \"text\":\n return {\n text: {\n text: obj.text,\n options: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fontSize: obj.fontSize ? pxToPt(obj.fontSize) : undefined,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: convertUnderline(obj.underline),\n strike: convertStrike(obj.strike),\n highlight: obj.highlight,\n align: obj.textAlign,\n },\n },\n };\n case \"image\": {\n const imageProps: ImageProps = {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n };\n // src が data URI かパスかを判定\n if (obj.src.startsWith(\"data:\")) {\n imageProps.data = obj.src;\n } else {\n imageProps.path = obj.src;\n }\n return { image: imageProps };\n }\n case \"rect\":\n return {\n rect: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fill: obj.fill\n ? { color: obj.fill.color, transparency: obj.fill.transparency }\n : undefined,\n line: obj.border\n ? {\n color: obj.border.color,\n width: obj.border.width,\n dashType: obj.border.dashType,\n }\n : undefined,\n },\n };\n case \"line\":\n return {\n line: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n line: obj.line\n ? {\n color: obj.line.color,\n width: obj.line.width,\n dashType: obj.line.dashType,\n }\n : { color: \"000000\", width: 1 },\n },\n };\n }\n}\n\n/**\n * SlideMasterOptions から pptxgenjs の defineSlideMaster を呼び出す\n */\nfunction defineSlideMasterFromOptions(\n pptx: PptxGenJSInstance,\n master: SlideMasterOptions,\n): string {\n const masterName = master.title || DEFAULT_MASTER_NAME;\n\n const masterProps: SlideMasterProps = {\n title: masterName,\n };\n\n // background の変換\n if (master.background) {\n if (\"color\" in master.background) {\n masterProps.background = { color: master.background.color };\n } else if (\"path\" in master.background) {\n masterProps.background = { path: master.background.path };\n } else if (\"data\" in master.background) {\n masterProps.background = { data: master.background.data };\n } else if (\"image\" in master.background) {\n masterProps.background = { path: master.background.image };\n }\n }\n\n // margin の変換 (px -> inches)\n if (master.margin !== undefined) {\n if (typeof master.margin === \"number\") {\n masterProps.margin = pxToIn(master.margin);\n } else {\n masterProps.margin = [\n pxToIn(master.margin.top ?? 0),\n pxToIn(master.margin.right ?? 0),\n pxToIn(master.margin.bottom ?? 0),\n pxToIn(master.margin.left ?? 0),\n ];\n }\n }\n\n // objects の変換\n if (master.objects && master.objects.length > 0) {\n masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));\n }\n\n // slideNumber の変換\n if (master.slideNumber) {\n masterProps.slideNumber = {\n x: pxToIn(master.slideNumber.x),\n y: pxToIn(master.slideNumber.y),\n w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : undefined,\n h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : undefined,\n fontSize: master.slideNumber.fontSize\n ? pxToPt(master.slideNumber.fontSize)\n : undefined,\n fontFace: master.slideNumber.fontFamily,\n color: master.slideNumber.color,\n };\n }\n\n pptx.defineSlideMaster(masterProps);\n return masterName;\n}\n\n/**\n * PositionedNode ツリーを PptxGenJS スライドに変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PptxGenJS インスタンス\n */\nexport async function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const slideIn = { w: pxToIn(slidePx.w), h: pxToIn(slidePx.h) }; // layout(=px) → PptxGenJS(=inch) への最終変換\n\n const PptxGenJS = await loadPptxGenJS();\n const pptx = new PptxGenJS();\n\n pptx.defineLayout({ name: \"custom\", width: slideIn.w, height: slideIn.h });\n pptx.layout = \"custom\";\n\n // マスターが指定されている場合、defineSlideMaster を呼び出す\n const masterName = master\n ? defineSlideMasterFromOptions(pptx, master)\n : undefined;\n\n for (const data of pages) {\n // マスターが指定されている場合は masterName を使用\n const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { slide, pptx, buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n // backgroundGradient はマーカー色で slide.background に適用し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)\n const rootGradientMarker =\n rootBackgroundGradient && !rootHasOpacity\n ? registerBackgroundGradient(\n rootBackgroundGradient,\n undefined,\n buildContext.gradientFills,\n )\n : undefined;\n if (rootGradientMarker) {\n slide.background = { color: rootGradientMarker };\n } else if (rootBackgroundColor && !rootHasOpacity) {\n slide.background = { color: rootBackgroundColor };\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n if (cachedData) {\n slide.background = { data: cachedData };\n } else {\n slide.background = { path: rootBackgroundImage.src };\n }\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootBackgroundGradient) &&\n !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n const { border, borderRadius } = node;\n const hasBorder = Boolean(\n border &&\n (border.color !== undefined ||\n border.width !== undefined ||\n border.dashType !== undefined),\n );\n if (hasBorder) {\n const line = {\n color: border?.color ?? \"000000\",\n width:\n border?.width !== undefined ? pxToPt(border.width) : undefined,\n dashType: border?.dashType,\n };\n const shapeType = borderRadius\n ? ctx.pptx.ShapeType.roundRect\n : ctx.pptx.ShapeType.rect;\n const rectRadius = borderRadius\n ? Math.min((borderRadius / Math.min(node.w, node.h)) * 2, 1)\n : undefined;\n ctx.slide.addShape(shapeType, {\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n fill: { type: \"none\" },\n line,\n rectRadius,\n });\n }\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return pptx;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["// pptxgenjs の型定義\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\n\n// pptxgenjs は CJS パッケージのため動的 import で読み込む\nasync function loadPptxGenJS(): Promise<new () => PptxGenJSInstance> {\n const pptxModule = await import(\"pptxgenjs\");\n // CJS default export の解決: module.default.default (ESM wrapper) または module.default\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n const mod = pptxModule as any;\n return mod.default?.default ?? mod.default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\ntype SlideMasterProps = Parameters<PptxGenJSInstance[\"defineSlideMaster\"]>[0];\ntype ImageProps = {\n x: number;\n y: number;\n w: number;\n h: number;\n path?: string;\n data?: string;\n};\nimport type {\n PositionedNode,\n SlideMasterOptions,\n MasterObject,\n} from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { convertUnderline, convertStrike } from \"./textOptions.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport {\n renderBackgroundAndBorder,\n renderBorderOnly,\n} from \"./utils/backgroundBorder.ts\";\nimport { registerBackgroundGradient } from \"./gradientFills.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nconst DEFAULT_MASTER_NAME = \"POM_MASTER\";\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\n/**\n * MasterObject を pptxgenjs の objects 形式に変換する\n */\nfunction convertMasterObject(\n obj: MasterObject,\n): SlideMasterProps[\"objects\"] extends (infer T)[] | undefined ? T : never {\n switch (obj.type) {\n case \"text\":\n return {\n text: {\n text: obj.text,\n options: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fontSize: obj.fontSize ? pxToPt(obj.fontSize) : undefined,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: convertUnderline(obj.underline),\n strike: convertStrike(obj.strike),\n highlight: obj.highlight,\n align: obj.textAlign,\n },\n },\n };\n case \"image\": {\n const imageProps: ImageProps = {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n };\n // src が data URI かパスかを判定\n if (obj.src.startsWith(\"data:\")) {\n imageProps.data = obj.src;\n } else {\n imageProps.path = obj.src;\n }\n return { image: imageProps };\n }\n case \"rect\":\n return {\n rect: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fill: obj.fill\n ? { color: obj.fill.color, transparency: obj.fill.transparency }\n : undefined,\n line: obj.border\n ? {\n color: obj.border.color,\n width: obj.border.width,\n dashType: obj.border.dashType,\n }\n : undefined,\n },\n };\n case \"line\":\n return {\n line: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n line: obj.line\n ? {\n color: obj.line.color,\n width: obj.line.width,\n dashType: obj.line.dashType,\n }\n : { color: \"000000\", width: 1 },\n },\n };\n }\n}\n\n/**\n * SlideMasterOptions から pptxgenjs の defineSlideMaster を呼び出す\n */\nfunction defineSlideMasterFromOptions(\n pptx: PptxGenJSInstance,\n master: SlideMasterOptions,\n): string {\n const masterName = master.title || DEFAULT_MASTER_NAME;\n\n const masterProps: SlideMasterProps = {\n title: masterName,\n };\n\n // background の変換\n if (master.background) {\n if (\"color\" in master.background) {\n masterProps.background = { color: master.background.color };\n } else if (\"path\" in master.background) {\n masterProps.background = { path: master.background.path };\n } else if (\"data\" in master.background) {\n masterProps.background = { data: master.background.data };\n } else if (\"image\" in master.background) {\n masterProps.background = { path: master.background.image };\n }\n }\n\n // margin の変換 (px -> inches)\n if (master.margin !== undefined) {\n const margin = resolveBoxSpacing(master.margin);\n masterProps.margin = [\n pxToIn(margin.top),\n pxToIn(margin.right),\n pxToIn(margin.bottom),\n pxToIn(margin.left),\n ];\n }\n\n // objects の変換\n if (master.objects && master.objects.length > 0) {\n masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));\n }\n\n // slideNumber の変換\n if (master.slideNumber) {\n masterProps.slideNumber = {\n x: pxToIn(master.slideNumber.x),\n y: pxToIn(master.slideNumber.y),\n w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : undefined,\n h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : undefined,\n fontSize: master.slideNumber.fontSize\n ? pxToPt(master.slideNumber.fontSize)\n : undefined,\n fontFace: master.slideNumber.fontFamily,\n color: master.slideNumber.color,\n };\n }\n\n pptx.defineSlideMaster(masterProps);\n return masterName;\n}\n\n/**\n * PositionedNode ツリーを PptxGenJS スライドに変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PptxGenJS インスタンス\n */\nexport async function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const slideIn = { w: pxToIn(slidePx.w), h: pxToIn(slidePx.h) }; // layout(=px) → PptxGenJS(=inch) への最終変換\n\n const PptxGenJS = await loadPptxGenJS();\n const pptx = new PptxGenJS();\n\n pptx.defineLayout({ name: \"custom\", width: slideIn.w, height: slideIn.h });\n pptx.layout = \"custom\";\n\n // マスターが指定されている場合、defineSlideMaster を呼び出す\n const masterName = master\n ? defineSlideMasterFromOptions(pptx, master)\n : undefined;\n\n for (const data of pages) {\n // マスターが指定されている場合は masterName を使用\n const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { slide, pptx, buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n // backgroundGradient はマーカー色で slide.background に適用し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)\n const rootGradientMarker =\n rootBackgroundGradient && !rootHasOpacity\n ? registerBackgroundGradient(\n rootBackgroundGradient,\n undefined,\n buildContext.gradientFills,\n )\n : undefined;\n if (rootGradientMarker) {\n slide.background = { color: rootGradientMarker };\n } else if (rootBackgroundColor && !rootHasOpacity) {\n slide.background = { color: rootBackgroundColor };\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n if (cachedData) {\n slide.background = { data: cachedData };\n } else {\n slide.background = { path: rootBackgroundImage.src };\n }\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootBackgroundGradient) &&\n !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n renderBorderOnly(node, ctx);\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return pptx;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { pxToIn, pxToPt } from "./units.js";
2
- import { getContentArea } from "./utils/contentArea.js";
1
+ import { pxToPt } from "./units.js";
2
+ import { getContentAreaIn } from "./utils/contentArea.js";
3
3
  //#region src/renderPptx/textOptions.ts
4
4
  /**
5
5
  * underline プロパティを pptxgenjs 形式に変換する
@@ -19,32 +19,56 @@ function convertUnderline(underline) {
19
19
  function convertStrike(strike) {
20
20
  if (strike) return "sngStrike";
21
21
  }
22
+ /**
23
+ * glow プロパティを pptxgenjs 形式に変換する
24
+ * size はユーザー入力 px、pptxgenjs の glow.size は pt。
25
+ * pptxgenjs は省略時デフォルトを Object.assign で合成するため undefined を
26
+ * 渡すとデフォルトが消える。ここで pom 側のデフォルトを確定させる。
27
+ */
28
+ function convertGlow(glow) {
29
+ if (glow === void 0) return void 0;
30
+ return {
31
+ size: pxToPt(glow.size ?? 8),
32
+ opacity: glow.opacity ?? .75,
33
+ color: glow.color ?? "FFFFFF"
34
+ };
35
+ }
36
+ /**
37
+ * outline プロパティを pptxgenjs 形式に変換する
38
+ * size はユーザー入力 px、pptxgenjs の outline.size は pt
39
+ */
40
+ function convertOutline(outline) {
41
+ if (outline === void 0) return void 0;
42
+ return {
43
+ size: pxToPt(outline.size ?? 1),
44
+ color: outline.color ?? "FFFFFF"
45
+ };
46
+ }
22
47
  function createTextOptions(node) {
23
48
  const fontSizePx = node.fontSize ?? 24;
24
49
  const fontFamily = node.fontFamily ?? "Noto Sans JP";
25
50
  const lineHeight = node.lineHeight ?? 1.3;
26
- const content = getContentArea(node);
27
51
  return {
28
- x: pxToIn(content.x),
29
- y: pxToIn(content.y),
30
- w: pxToIn(content.w),
31
- h: pxToIn(content.h),
52
+ ...getContentAreaIn(node),
32
53
  fontSize: pxToPt(fontSizePx),
33
54
  fontFace: fontFamily,
34
55
  align: node.textAlign ?? "left",
35
56
  valign: "top",
36
57
  margin: 0,
37
58
  lineSpacingMultiple: lineHeight,
59
+ rotate: node.rotate,
38
60
  color: node.color,
39
61
  bold: node.bold,
40
62
  italic: node.italic,
41
63
  underline: convertUnderline(node.underline),
42
64
  strike: convertStrike(node.strike),
43
65
  highlight: node.highlight,
66
+ glow: convertGlow(node.glow),
67
+ outline: convertOutline(node.outline),
44
68
  charSpacing: node.letterSpacing !== void 0 ? pxToPt(node.letterSpacing) : void 0
45
69
  };
46
70
  }
47
71
  //#endregion
48
- export { convertStrike, convertUnderline, createTextOptions };
72
+ export { convertGlow, convertOutline, convertStrike, convertUnderline, createTextOptions };
49
73
 
50
74
  //# sourceMappingURL=textOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type { PositionedNode, Underline, UnderlineStyle } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { getContentArea } from \"./utils/contentArea.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline プロパティを pptxgenjs 形式に変換する\n */\nexport function convertUnderline(\n underline: Underline | undefined,\n): { style?: UnderlineStyle; color?: string } | undefined {\n if (underline === undefined) return undefined;\n if (underline === false) return undefined;\n if (underline === true) return { style: \"sng\" };\n return {\n style: underline.style,\n color: underline.color,\n };\n}\n\n/**\n * strike プロパティを pptxgenjs 形式に変換する\n */\nexport function convertStrike(\n strike: boolean | undefined,\n): \"sngStrike\" | undefined {\n if (strike) return \"sngStrike\";\n return undefined;\n}\n\nexport function createTextOptions(node: TextNode) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n const content = getContentArea(node);\n\n return {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n // letterSpacing はユーザー入力 px、pptxgenjs の charSpacing は pt\n charSpacing:\n node.letterSpacing !== undefined ? pxToPt(node.letterSpacing) : undefined,\n };\n}\n"],"mappings":";;;;;;AASA,SAAgB,iBACd,WACwD;CACxD,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;CACpC,IAAI,cAAc,OAAO,OAAO,KAAA;CAChC,IAAI,cAAc,MAAM,OAAO,EAAE,OAAO,MAAM;CAC9C,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,UAAU;CACnB;AACF;;;;AAKA,SAAgB,cACd,QACyB;CACzB,IAAI,QAAQ,OAAO;AAErB;AAEA,SAAgB,kBAAkB,MAAgB;CAChD,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,UAAU,eAAe,IAAI;CAEnC,OAAO;EACL,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,UAAU,OAAO,UAAU;EAC3B,UAAU;EACV,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,QAAQ;EACR,qBAAqB;EACrB,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAEhB,aACE,KAAK,kBAAkB,KAAA,IAAY,OAAO,KAAK,aAAa,IAAI,KAAA;CACpE;AACF"}
1
+ {"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type {\n PositionedNode,\n TextGlow,\n TextOutline,\n Underline,\n UnderlineStyle,\n} from \"../types.ts\";\nimport { pxToPt } from \"./units.ts\";\nimport { getContentAreaIn } from \"./utils/contentArea.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline プロパティを pptxgenjs 形式に変換する\n */\nexport function convertUnderline(\n underline: Underline | undefined,\n): { style?: UnderlineStyle; color?: string } | undefined {\n if (underline === undefined) return undefined;\n if (underline === false) return undefined;\n if (underline === true) return { style: \"sng\" };\n return {\n style: underline.style,\n color: underline.color,\n };\n}\n\n/**\n * strike プロパティを pptxgenjs 形式に変換する\n */\nexport function convertStrike(\n strike: boolean | undefined,\n): \"sngStrike\" | undefined {\n if (strike) return \"sngStrike\";\n return undefined;\n}\n\n/**\n * glow プロパティを pptxgenjs 形式に変換する\n * size はユーザー入力 px、pptxgenjs の glow.size は pt。\n * pptxgenjs は省略時デフォルトを Object.assign で合成するため undefined を\n * 渡すとデフォルトが消える。ここで pom 側のデフォルトを確定させる。\n */\nexport function convertGlow(\n glow: TextGlow | undefined,\n): { size: number; opacity: number; color: string } | undefined {\n if (glow === undefined) return undefined;\n return {\n size: pxToPt(glow.size ?? 8),\n opacity: glow.opacity ?? 0.75,\n color: glow.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * outline プロパティを pptxgenjs 形式に変換する\n * size はユーザー入力 px、pptxgenjs の outline.size は pt\n */\nexport function convertOutline(\n outline: TextOutline | undefined,\n): { size: number; color: string } | undefined {\n if (outline === undefined) return undefined;\n return {\n size: pxToPt(outline.size ?? 1),\n color: outline.color ?? \"FFFFFF\",\n };\n}\n\nexport function createTextOptions(node: TextNode) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n\n return {\n ...getContentAreaIn(node),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n rotate: node.rotate,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\n // letterSpacing はユーザー入力 px、pptxgenjs の charSpacing は pt\n charSpacing:\n node.letterSpacing !== undefined ? pxToPt(node.letterSpacing) : undefined,\n };\n}\n"],"mappings":";;;;;;AAeA,SAAgB,iBACd,WACwD;CACxD,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;CACpC,IAAI,cAAc,OAAO,OAAO,KAAA;CAChC,IAAI,cAAc,MAAM,OAAO,EAAE,OAAO,MAAM;CAC9C,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,UAAU;CACnB;AACF;;;;AAKA,SAAgB,cACd,QACyB;CACzB,IAAI,QAAQ,OAAO;AAErB;;;;;;;AAQA,SAAgB,YACd,MAC8D;CAC9D,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO;EACL,MAAM,OAAO,KAAK,QAAQ,CAAC;EAC3B,SAAS,KAAK,WAAW;EACzB,OAAO,KAAK,SAAS;CACvB;AACF;;;;;AAMA,SAAgB,eACd,SAC6C;CAC7C,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,OAAO;EACL,MAAM,OAAO,QAAQ,QAAQ,CAAC;EAC9B,OAAO,QAAQ,SAAS;CAC1B;AACF;AAEA,SAAgB,kBAAkB,MAAgB;CAChD,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,cAAc;CAEtC,OAAO;EACL,GAAG,iBAAiB,IAAI;EACxB,UAAU,OAAO,UAAU;EAC3B,UAAU;EACV,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,QAAQ;EACR,qBAAqB;EACrB,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,MAAM,YAAY,KAAK,IAAI;EAC3B,SAAS,eAAe,KAAK,OAAO;EAEpC,aACE,KAAK,kBAAkB,KAAA,IAAY,OAAO,KAAK,aAAa,IAAI,KAAA;CACpE;AACF"}
@@ -1,6 +1,16 @@
1
1
  const pxToIn = (px) => px / 96;
2
2
  const pxToPt = (px) => px * 72 / 96;
3
+ /**
4
+ * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に
5
+ * まとめて変換する。addShape / addText 等のオプションへ spread して使う。
6
+ */
7
+ const rectPxToIn = (rect) => ({
8
+ x: pxToIn(rect.x),
9
+ y: pxToIn(rect.y),
10
+ w: pxToIn(rect.w),
11
+ h: pxToIn(rect.h)
12
+ });
3
13
  //#endregion
4
- export { pxToIn, pxToPt };
14
+ export { pxToIn, pxToPt, rectPxToIn };
5
15
 
6
16
  //# sourceMappingURL=units.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n"],"mappings":"AAEA,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA"}
1
+ {"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。addShape / addText 等のオプションへ spread して使う。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"AAEA,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA;;;;;AAM5C,MAAa,cAAc,UAKpB;CACL,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;AAClB"}
@@ -1,17 +1,8 @@
1
1
  import { getImageData } from "../../shared/measureImage.js";
2
- import { pxToIn, pxToPt } from "../units.js";
2
+ import { pxToIn, rectPxToIn } from "../units.js";
3
+ import { BORDER_SIDES, convertBorderLine, convertShadow, hasVisibleBorder, resolveBackgroundFill, resolvePerSideBorders, resolveRectRadius } from "./visualStyle.js";
3
4
  import { registerBackgroundGradient } from "../gradientFills.js";
4
5
  //#region src/renderPptx/utils/backgroundBorder.ts
5
- function convertShadow(shadow) {
6
- return {
7
- type: shadow.type ?? "outer",
8
- opacity: shadow.opacity,
9
- blur: shadow.blur,
10
- angle: shadow.angle,
11
- offset: shadow.offset,
12
- color: shadow.color
13
- };
14
- }
15
6
  /**
16
7
  * ノードの背景色・背景画像・ボーダー・影を描画する
17
8
  * 全ノードタイプで最初に呼び出される共通処理
@@ -21,54 +12,39 @@ function convertShadow(shadow) {
21
12
  function renderBackgroundAndBorder(node, ctx) {
22
13
  const { backgroundColor, backgroundGradient, backgroundImage, border, borderRadius, shadow } = node;
23
14
  const gradientMarker = backgroundGradient ? registerBackgroundGradient(backgroundGradient, node.opacity, ctx.buildContext.gradientFills) : void 0;
15
+ const perSideBorders = resolveEffectivePerSideBorders(node, ctx);
24
16
  const hasBackground = Boolean(backgroundColor) || Boolean(gradientMarker);
25
17
  const hasBackgroundImage = Boolean(backgroundImage);
26
- const hasBorder = Boolean(border && (border.color !== void 0 || border.width !== void 0 || border.dashType !== void 0));
18
+ const hasUniformBorder = !perSideBorders && hasVisibleBorder(border);
27
19
  const hasShadow = Boolean(shadow);
28
- if (!hasBackground && !hasBackgroundImage && !hasBorder && !hasShadow) return;
20
+ if (!hasBackground && !hasBackgroundImage && !hasUniformBorder && !perSideBorders && !hasShadow) return;
29
21
  const shapeType = borderRadius ? ctx.pptx.ShapeType.roundRect : ctx.pptx.ShapeType.rect;
30
- const rectRadius = borderRadius ? Math.min(borderRadius / Math.min(node.w, node.h) * 2, 1) : void 0;
22
+ const rectRadius = resolveRectRadius(borderRadius, node.w, node.h);
31
23
  if (!hasBackgroundImage) {
32
- const fill = hasBackground ? gradientMarker ? { color: gradientMarker } : {
33
- color: backgroundColor,
34
- transparency: node.opacity !== void 0 ? (1 - node.opacity) * 100 : void 0
35
- } : { type: "none" };
36
- const line = hasBorder ? {
37
- color: border?.color ?? "000000",
38
- width: border?.width !== void 0 ? pxToPt(border.width) : void 0,
39
- dashType: border?.dashType
40
- } : { type: "none" };
41
- ctx.slide.addShape(shapeType, {
42
- x: pxToIn(node.x),
43
- y: pxToIn(node.y),
44
- w: pxToIn(node.w),
45
- h: pxToIn(node.h),
46
- fill,
47
- line,
48
- rectRadius,
49
- shadow: shadow ? convertShadow(shadow) : void 0
50
- });
24
+ if (hasBackground || hasUniformBorder || hasShadow) {
25
+ const fill = hasBackground ? resolveBackgroundFill(backgroundColor, node.opacity, gradientMarker) : { type: "none" };
26
+ const line = hasUniformBorder ? convertBorderLine(border, "000000") : { type: "none" };
27
+ ctx.slide.addShape(shapeType, {
28
+ ...rectPxToIn(node),
29
+ fill,
30
+ line,
31
+ rectRadius,
32
+ shadow: convertShadow(shadow)
33
+ });
34
+ }
35
+ renderPerSideBorderLines(node, perSideBorders, ctx);
51
36
  return;
52
37
  }
53
38
  if (hasBackground) ctx.slide.addShape(shapeType, {
54
- x: pxToIn(node.x),
55
- y: pxToIn(node.y),
56
- w: pxToIn(node.w),
57
- h: pxToIn(node.h),
58
- fill: gradientMarker ? { color: gradientMarker } : {
59
- color: backgroundColor,
60
- transparency: node.opacity !== void 0 ? (1 - node.opacity) * 100 : void 0
61
- },
39
+ ...rectPxToIn(node),
40
+ fill: resolveBackgroundFill(backgroundColor, node.opacity, gradientMarker),
62
41
  line: { type: "none" },
63
42
  rectRadius
64
43
  });
65
44
  if (backgroundImage) {
66
45
  const sizing = backgroundImage.sizing ?? "cover";
67
46
  const imageOptions = {
68
- x: pxToIn(node.x),
69
- y: pxToIn(node.y),
70
- w: pxToIn(node.w),
71
- h: pxToIn(node.h),
47
+ ...rectPxToIn(node),
72
48
  sizing: {
73
49
  type: sizing,
74
50
  w: pxToIn(node.w),
@@ -85,22 +61,92 @@ function renderBackgroundAndBorder(node, ctx) {
85
61
  path: backgroundImage.src
86
62
  });
87
63
  }
88
- if (hasBorder || hasShadow) ctx.slide.addShape(shapeType, {
89
- x: pxToIn(node.x),
90
- y: pxToIn(node.y),
91
- w: pxToIn(node.w),
92
- h: pxToIn(node.h),
64
+ if (hasUniformBorder || hasShadow) ctx.slide.addShape(shapeType, {
65
+ ...rectPxToIn(node),
93
66
  fill: { type: "none" },
94
- line: hasBorder ? {
95
- color: border?.color ?? "000000",
96
- width: border?.width !== void 0 ? pxToPt(border.width) : void 0,
97
- dashType: border?.dashType
98
- } : { type: "none" },
67
+ line: hasUniformBorder ? convertBorderLine(border, "000000") : { type: "none" },
99
68
  rectRadius,
100
- shadow: shadow ? convertShadow(shadow) : void 0
69
+ shadow: convertShadow(shadow)
70
+ });
71
+ renderPerSideBorderLines(node, perSideBorders, ctx);
72
+ }
73
+ /**
74
+ * 辺ごとの border 指定を解決する。borderRadius との併用は角の接続処理が
75
+ * 複雑になるためサポートせず、警告を発して 4 辺一律の border に
76
+ * フォールバックする
77
+ */
78
+ function resolveEffectivePerSideBorders(node, ctx) {
79
+ const perSideBorders = resolvePerSideBorders(node);
80
+ if (perSideBorders && node.borderRadius !== void 0) {
81
+ ctx.buildContext.diagnostics.add("PER_SIDE_BORDER_WITH_RADIUS", "borderTop / borderRight / borderBottom / borderLeft cannot be combined with borderRadius — falling back to the uniform \"border\" style");
82
+ return;
83
+ }
84
+ return perSideBorders;
85
+ }
86
+ /**
87
+ * ノードの border のみを描画する (背景・影は描画しない)。
88
+ * ルートノードの backgroundColor / backgroundImage を slide.background に
89
+ * 適用した後、border だけを個別に描画するパス用
90
+ */
91
+ function renderBorderOnly(node, ctx) {
92
+ const { border, borderRadius } = node;
93
+ const perSideBorders = resolveEffectivePerSideBorders(node, ctx);
94
+ if (perSideBorders) {
95
+ renderPerSideBorderLines(node, perSideBorders, ctx);
96
+ return;
97
+ }
98
+ if (!hasVisibleBorder(border)) return;
99
+ const shapeType = borderRadius ? ctx.pptx.ShapeType.roundRect : ctx.pptx.ShapeType.rect;
100
+ ctx.slide.addShape(shapeType, {
101
+ ...rectPxToIn(node),
102
+ fill: { type: "none" },
103
+ line: convertBorderLine(border, "000000"),
104
+ rectRadius: resolveRectRadius(borderRadius, node.w, node.h)
101
105
  });
102
106
  }
107
+ /**
108
+ * 辺ごとの border をノードの各辺に沿った line shape として描画する。
109
+ * shape の line オプション (4 辺一律) では表現できないため、辺ごとに
110
+ * 独立した line shape を追加する
111
+ */
112
+ function renderPerSideBorderLines(node, perSideBorders, ctx) {
113
+ if (!perSideBorders) return;
114
+ const edges = {
115
+ top: {
116
+ x: node.x,
117
+ y: node.y,
118
+ w: node.w,
119
+ h: 0
120
+ },
121
+ right: {
122
+ x: node.x + node.w,
123
+ y: node.y,
124
+ w: 0,
125
+ h: node.h
126
+ },
127
+ bottom: {
128
+ x: node.x,
129
+ y: node.y + node.h,
130
+ w: node.w,
131
+ h: 0
132
+ },
133
+ left: {
134
+ x: node.x,
135
+ y: node.y,
136
+ w: 0,
137
+ h: node.h
138
+ }
139
+ };
140
+ for (const side of BORDER_SIDES) {
141
+ const style = perSideBorders[side];
142
+ if (!style) continue;
143
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
144
+ ...rectPxToIn(edges[side]),
145
+ line: convertBorderLine(style, "000000")
146
+ });
147
+ }
148
+ }
103
149
  //#endregion
104
- export { renderBackgroundAndBorder };
150
+ export { renderBackgroundAndBorder, renderBorderOnly };
105
151
 
106
152
  //# sourceMappingURL=backgroundBorder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backgroundBorder.js","names":[],"sources":["../../../src/renderPptx/utils/backgroundBorder.ts"],"sourcesContent":["import type { PositionedNode, ShadowStyle } from \"../../types.ts\";\nimport { getImageData } from \"../../shared/measureImage.ts\";\nimport { registerBackgroundGradient } from \"../gradientFills.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\n\nfunction convertShadow(shadow: ShadowStyle) {\n return {\n type: shadow.type ?? (\"outer\" as const),\n opacity: shadow.opacity,\n blur: shadow.blur,\n angle: shadow.angle,\n offset: shadow.offset,\n color: shadow.color,\n };\n}\n\n/**\n * ノードの背景色・背景画像・ボーダー・影を描画する\n * 全ノードタイプで最初に呼び出される共通処理\n *\n * 描画順序: 背景色 → 背景画像 → ボーダー\n */\nexport function renderBackgroundAndBorder(\n node: PositionedNode,\n ctx: RenderContext,\n): void {\n const {\n backgroundColor,\n backgroundGradient,\n backgroundImage,\n border,\n borderRadius,\n shadow,\n } = node;\n\n // backgroundGradient はマーカー色の solidFill として描画し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)。\n // opacity はマーカー側ではなく gradFill のカラーストップの alpha で表現する\n const gradientMarker = backgroundGradient\n ? registerBackgroundGradient(\n backgroundGradient,\n node.opacity,\n ctx.buildContext.gradientFills,\n )\n : undefined;\n\n const hasBackground = Boolean(backgroundColor) || Boolean(gradientMarker);\n const hasBackgroundImage = Boolean(backgroundImage);\n const hasBorder = Boolean(\n border &&\n (border.color !== undefined ||\n border.width !== undefined ||\n border.dashType !== undefined),\n );\n const hasShadow = Boolean(shadow);\n\n if (!hasBackground && !hasBackgroundImage && !hasBorder && !hasShadow) {\n return;\n }\n\n // borderRadius がある場合は roundRect を使用し、rectRadius を計算\n const shapeType = borderRadius\n ? ctx.pptx.ShapeType.roundRect\n : ctx.pptx.ShapeType.rect;\n\n // px を 0-1 の正規化値に変換\n const rectRadius = borderRadius\n ? Math.min((borderRadius / Math.min(node.w, node.h)) * 2, 1)\n : undefined;\n\n // backgroundImage がない場合は従来通り1回の addShape で処理\n if (!hasBackgroundImage) {\n const fill = hasBackground\n ? gradientMarker\n ? { color: gradientMarker }\n : {\n color: backgroundColor,\n transparency:\n node.opacity !== undefined ? (1 - node.opacity) * 100 : undefined,\n }\n : { type: \"none\" as const };\n\n const line = hasBorder\n ? {\n color: border?.color ?? \"000000\",\n width: border?.width !== undefined ? pxToPt(border.width) : undefined,\n dashType: border?.dashType,\n }\n : { type: \"none\" as const };\n\n ctx.slide.addShape(shapeType, {\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n fill,\n line,\n rectRadius,\n shadow: shadow ? convertShadow(shadow) : undefined,\n });\n return;\n }\n\n // backgroundImage がある場合は分割描画: 背景色 → 背景画像 → ボーダー\n\n // 1. 背景色\n if (hasBackground) {\n ctx.slide.addShape(shapeType, {\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n fill: gradientMarker\n ? { color: gradientMarker }\n : {\n color: backgroundColor,\n transparency:\n node.opacity !== undefined ? (1 - node.opacity) * 100 : undefined,\n },\n line: { type: \"none\" as const },\n rectRadius,\n });\n }\n\n // 2. 背景画像\n if (backgroundImage) {\n const sizing = backgroundImage.sizing ?? \"cover\";\n const imageOptions: Record<string, unknown> = {\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n sizing: {\n type: sizing,\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n },\n };\n\n const cachedData = getImageData(\n backgroundImage.src,\n ctx.buildContext.imageDataCache,\n );\n if (cachedData) {\n ctx.slide.addImage({ ...imageOptions, data: cachedData });\n } else {\n ctx.slide.addImage({ ...imageOptions, path: backgroundImage.src });\n }\n }\n\n // 3. ボーダー\n if (hasBorder || hasShadow) {\n ctx.slide.addShape(shapeType, {\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n fill: { type: \"none\" as const },\n line: hasBorder\n ? {\n color: border?.color ?? \"000000\",\n width:\n border?.width !== undefined ? pxToPt(border.width) : undefined,\n dashType: border?.dashType,\n }\n : { type: \"none\" as const },\n rectRadius,\n shadow: shadow ? convertShadow(shadow) : undefined,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"backgroundBorder.js","names":[],"sources":["../../../src/renderPptx/utils/backgroundBorder.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport { getImageData } from \"../../shared/measureImage.ts\";\nimport { registerBackgroundGradient } from \"../gradientFills.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, rectPxToIn } from \"../units.ts\";\nimport {\n BORDER_SIDES,\n convertShadow,\n convertBorderLine,\n hasVisibleBorder,\n resolveBackgroundFill,\n resolvePerSideBorders,\n resolveRectRadius,\n type PerSideBorders,\n} from \"./visualStyle.ts\";\n\n/**\n * ノードの背景色・背景画像・ボーダー・影を描画する\n * 全ノードタイプで最初に呼び出される共通処理\n *\n * 描画順序: 背景色 → 背景画像 → ボーダー\n */\nexport function renderBackgroundAndBorder(\n node: PositionedNode,\n ctx: RenderContext,\n): void {\n const {\n backgroundColor,\n backgroundGradient,\n backgroundImage,\n border,\n borderRadius,\n shadow,\n } = node;\n\n // backgroundGradient はマーカー色の solidFill として描画し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)。\n // opacity はマーカー側ではなく gradFill のカラーストップの alpha で表現する\n const gradientMarker = backgroundGradient\n ? registerBackgroundGradient(\n backgroundGradient,\n node.opacity,\n ctx.buildContext.gradientFills,\n )\n : undefined;\n\n const perSideBorders = resolveEffectivePerSideBorders(node, ctx);\n\n const hasBackground = Boolean(backgroundColor) || Boolean(gradientMarker);\n const hasBackgroundImage = Boolean(backgroundImage);\n // 辺ごとの指定がある場合、一律 border は各辺へのマージで反映済みのため\n // shape line としては描画しない\n const hasUniformBorder = !perSideBorders && hasVisibleBorder(border);\n const hasShadow = Boolean(shadow);\n\n if (\n !hasBackground &&\n !hasBackgroundImage &&\n !hasUniformBorder &&\n !perSideBorders &&\n !hasShadow\n ) {\n return;\n }\n\n // borderRadius がある場合は roundRect を使用し、rectRadius を計算\n const shapeType = borderRadius\n ? ctx.pptx.ShapeType.roundRect\n : ctx.pptx.ShapeType.rect;\n\n const rectRadius = resolveRectRadius(borderRadius, node.w, node.h);\n\n // backgroundImage がない場合は従来通り1回の addShape で処理\n if (!hasBackgroundImage) {\n if (hasBackground || hasUniformBorder || hasShadow) {\n const fill = hasBackground\n ? resolveBackgroundFill(backgroundColor, node.opacity, gradientMarker)\n : { type: \"none\" as const };\n\n const line = hasUniformBorder\n ? convertBorderLine(border, \"000000\")\n : { type: \"none\" as const };\n\n ctx.slide.addShape(shapeType, {\n ...rectPxToIn(node),\n fill,\n line,\n rectRadius,\n shadow: convertShadow(shadow),\n });\n }\n\n renderPerSideBorderLines(node, perSideBorders, ctx);\n return;\n }\n\n // backgroundImage がある場合は分割描画: 背景色 → 背景画像 → ボーダー\n\n // 1. 背景色\n if (hasBackground) {\n ctx.slide.addShape(shapeType, {\n ...rectPxToIn(node),\n fill: resolveBackgroundFill(\n backgroundColor,\n node.opacity,\n gradientMarker,\n ),\n line: { type: \"none\" as const },\n rectRadius,\n });\n }\n\n // 2. 背景画像\n if (backgroundImage) {\n const sizing = backgroundImage.sizing ?? \"cover\";\n const imageOptions: Record<string, unknown> = {\n ...rectPxToIn(node),\n sizing: {\n type: sizing,\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n },\n };\n\n const cachedData = getImageData(\n backgroundImage.src,\n ctx.buildContext.imageDataCache,\n );\n if (cachedData) {\n ctx.slide.addImage({ ...imageOptions, data: cachedData });\n } else {\n ctx.slide.addImage({ ...imageOptions, path: backgroundImage.src });\n }\n }\n\n // 3. ボーダー\n if (hasUniformBorder || hasShadow) {\n ctx.slide.addShape(shapeType, {\n ...rectPxToIn(node),\n fill: { type: \"none\" as const },\n line: hasUniformBorder\n ? convertBorderLine(border, \"000000\")\n : { type: \"none\" as const },\n rectRadius,\n shadow: convertShadow(shadow),\n });\n }\n\n renderPerSideBorderLines(node, perSideBorders, ctx);\n}\n\n/**\n * 辺ごとの border 指定を解決する。borderRadius との併用は角の接続処理が\n * 複雑になるためサポートせず、警告を発して 4 辺一律の border に\n * フォールバックする\n */\nfunction resolveEffectivePerSideBorders(\n node: PositionedNode,\n ctx: RenderContext,\n): PerSideBorders | undefined {\n const perSideBorders = resolvePerSideBorders(node);\n if (perSideBorders && node.borderRadius !== undefined) {\n ctx.buildContext.diagnostics.add(\n \"PER_SIDE_BORDER_WITH_RADIUS\",\n 'borderTop / borderRight / borderBottom / borderLeft cannot be combined with borderRadius — falling back to the uniform \"border\" style',\n );\n return undefined;\n }\n return perSideBorders;\n}\n\n/**\n * ノードの border のみを描画する (背景・影は描画しない)。\n * ルートノードの backgroundColor / backgroundImage を slide.background に\n * 適用した後、border だけを個別に描画するパス用\n */\nexport function renderBorderOnly(\n node: PositionedNode,\n ctx: RenderContext,\n): void {\n const { border, borderRadius } = node;\n\n const perSideBorders = resolveEffectivePerSideBorders(node, ctx);\n if (perSideBorders) {\n renderPerSideBorderLines(node, perSideBorders, ctx);\n return;\n }\n\n if (!hasVisibleBorder(border)) return;\n\n const shapeType = borderRadius\n ? ctx.pptx.ShapeType.roundRect\n : ctx.pptx.ShapeType.rect;\n\n ctx.slide.addShape(shapeType, {\n ...rectPxToIn(node),\n fill: { type: \"none\" as const },\n line: convertBorderLine(border, \"000000\"),\n rectRadius: resolveRectRadius(borderRadius, node.w, node.h),\n });\n}\n\n/**\n * 辺ごとの border をノードの各辺に沿った line shape として描画する。\n * shape の line オプション (4 辺一律) では表現できないため、辺ごとに\n * 独立した line shape を追加する\n */\nfunction renderPerSideBorderLines(\n node: PositionedNode,\n perSideBorders: PerSideBorders | undefined,\n ctx: RenderContext,\n): void {\n if (!perSideBorders) return;\n\n const edges = {\n top: { x: node.x, y: node.y, w: node.w, h: 0 },\n right: { x: node.x + node.w, y: node.y, w: 0, h: node.h },\n bottom: { x: node.x, y: node.y + node.h, w: node.w, h: 0 },\n left: { x: node.x, y: node.y, w: 0, h: node.h },\n } as const;\n\n for (const side of BORDER_SIDES) {\n const style = perSideBorders[side];\n if (!style) continue;\n\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n ...rectPxToIn(edges[side]),\n line: convertBorderLine(style, \"000000\"),\n });\n }\n}\n"],"mappings":"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"}