@hirokisakabe/pom 8.3.0 → 8.5.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 (124) 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.d.ts.map +1 -1
  15. package/dist/buildPptx.js +4 -2
  16. package/dist/buildPptx.js.map +1 -1
  17. package/dist/calcYogaLayout/calcYogaLayout.js +16 -27
  18. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  19. package/dist/diagnostics.d.ts +1 -1
  20. package/dist/diagnostics.d.ts.map +1 -1
  21. package/dist/diagnostics.js.map +1 -1
  22. package/dist/parseXml/coercionRules.js +43 -8
  23. package/dist/parseXml/coercionRules.js.map +1 -1
  24. package/dist/parseXml/parseXml.d.ts +8 -3
  25. package/dist/parseXml/parseXml.d.ts.map +1 -1
  26. package/dist/parseXml/parseXml.js +192 -212
  27. package/dist/parseXml/parseXml.js.map +1 -1
  28. package/dist/parseXml/serializeXml.d.ts.map +1 -1
  29. package/dist/parseXml/serializeXml.js +12 -17
  30. package/dist/parseXml/serializeXml.js.map +1 -1
  31. package/dist/registry/definitions/arrow.js +2 -2
  32. package/dist/registry/definitions/arrow.js.map +1 -1
  33. package/dist/registry/definitions/chart.js +2 -2
  34. package/dist/registry/definitions/chart.js.map +1 -1
  35. package/dist/registry/definitions/compositeNodes.js +7 -12
  36. package/dist/registry/definitions/compositeNodes.js.map +1 -1
  37. package/dist/registry/definitions/icon.js +2 -2
  38. package/dist/registry/definitions/icon.js.map +1 -1
  39. package/dist/registry/definitions/image.js +2 -2
  40. package/dist/registry/definitions/image.js.map +1 -1
  41. package/dist/registry/definitions/layer.js +4 -5
  42. package/dist/registry/definitions/layer.js.map +1 -1
  43. package/dist/registry/definitions/line.js +2 -2
  44. package/dist/registry/definitions/line.js.map +1 -1
  45. package/dist/registry/definitions/list.js +3 -4
  46. package/dist/registry/definitions/list.js.map +1 -1
  47. package/dist/registry/definitions/shape.js +2 -2
  48. package/dist/registry/definitions/shape.js.map +1 -1
  49. package/dist/registry/definitions/stack.js +3 -4
  50. package/dist/registry/definitions/stack.js.map +1 -1
  51. package/dist/registry/definitions/svg.js +2 -2
  52. package/dist/registry/definitions/svg.js.map +1 -1
  53. package/dist/registry/definitions/table.js +2 -2
  54. package/dist/registry/definitions/table.js.map +1 -1
  55. package/dist/registry/definitions/text.js +2 -2
  56. package/dist/registry/definitions/text.js.map +1 -1
  57. package/dist/registry/index.js.map +1 -1
  58. package/dist/registry/nodeMetadata.js +208 -0
  59. package/dist/registry/nodeMetadata.js.map +1 -0
  60. package/dist/registry/nodeRegistry.js +3 -0
  61. package/dist/registry/nodeRegistry.js.map +1 -1
  62. package/dist/registry/xmlChildRules.js +55 -0
  63. package/dist/registry/xmlChildRules.js.map +1 -0
  64. package/dist/renderPptx/nodes/arrow.js +7 -28
  65. package/dist/renderPptx/nodes/arrow.js.map +1 -1
  66. package/dist/renderPptx/nodes/chart.js +2 -7
  67. package/dist/renderPptx/nodes/chart.js.map +1 -1
  68. package/dist/renderPptx/nodes/flow.js +6 -13
  69. package/dist/renderPptx/nodes/flow.js.map +1 -1
  70. package/dist/renderPptx/nodes/icon.js +4 -2
  71. package/dist/renderPptx/nodes/icon.js.map +1 -1
  72. package/dist/renderPptx/nodes/image.js +5 -13
  73. package/dist/renderPptx/nodes/image.js.map +1 -1
  74. package/dist/renderPptx/nodes/line.js +9 -33
  75. package/dist/renderPptx/nodes/line.js.map +1 -1
  76. package/dist/renderPptx/nodes/list.js +8 -20
  77. package/dist/renderPptx/nodes/list.js.map +1 -1
  78. package/dist/renderPptx/nodes/matrix.js +10 -11
  79. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  80. package/dist/renderPptx/nodes/processArrow.js +9 -16
  81. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  82. package/dist/renderPptx/nodes/pyramid.js +5 -7
  83. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  84. package/dist/renderPptx/nodes/shape.js +7 -20
  85. package/dist/renderPptx/nodes/shape.js.map +1 -1
  86. package/dist/renderPptx/nodes/svg.js +2 -5
  87. package/dist/renderPptx/nodes/svg.js.map +1 -1
  88. package/dist/renderPptx/nodes/table.js +2 -5
  89. package/dist/renderPptx/nodes/table.js.map +1 -1
  90. package/dist/renderPptx/nodes/text.js +4 -1
  91. package/dist/renderPptx/nodes/text.js.map +1 -1
  92. package/dist/renderPptx/nodes/timeline.js +20 -22
  93. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  94. package/dist/renderPptx/nodes/tree.js +5 -5
  95. package/dist/renderPptx/nodes/tree.js.map +1 -1
  96. package/dist/renderPptx/renderPptx.js +13 -29
  97. package/dist/renderPptx/renderPptx.js.map +1 -1
  98. package/dist/renderPptx/textOptions.js +32 -8
  99. package/dist/renderPptx/textOptions.js.map +1 -1
  100. package/dist/renderPptx/units.js +11 -1
  101. package/dist/renderPptx/units.js.map +1 -1
  102. package/dist/renderPptx/utils/backgroundBorder.js +103 -57
  103. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  104. package/dist/renderPptx/utils/contentArea.js +26 -9
  105. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  106. package/dist/renderPptx/utils/scaleToFit.js +17 -1
  107. package/dist/renderPptx/utils/scaleToFit.js.map +1 -1
  108. package/dist/renderPptx/utils/straightLine.js +41 -0
  109. package/dist/renderPptx/utils/straightLine.js.map +1 -0
  110. package/dist/renderPptx/utils/visualStyle.js +113 -0
  111. package/dist/renderPptx/utils/visualStyle.js.map +1 -0
  112. package/dist/shared/boxSpacing.js +63 -0
  113. package/dist/shared/boxSpacing.js.map +1 -0
  114. package/dist/shared/walkTree.js +1 -7
  115. package/dist/shared/walkTree.js.map +1 -1
  116. package/dist/toPositioned/toPositioned.js +1 -1
  117. package/dist/toPositioned/toPositioned.js.map +1 -1
  118. package/dist/types.d.ts +1127 -95
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/types.js +47 -17
  121. package/dist/types.js.map +1 -1
  122. package/dist/validatePositioned/validatePositioned.js +92 -0
  123. package/dist/validatePositioned/validatePositioned.js.map +1 -0
  124. package/package.json +4 -3
@@ -1,9 +1,10 @@
1
1
  import { pxToIn, pxToPt } from "../units.js";
2
- import { getContentArea } from "../utils/contentArea.js";
2
+ import { withContentBounds } from "../utils/contentArea.js";
3
3
  import { convertStrike, convertUnderline } from "../textOptions.js";
4
+ import { stripHash } from "../utils/visualStyle.js";
4
5
  import { ARROW_DEPTH_RATIO } from "../../shared/processArrowConstants.js";
5
6
  import { measureProcessArrow } from "../../calcYogaLayout/measureCompositeNodes.js";
6
- import { calcScaleFactor } from "../utils/scaleToFit.js";
7
+ import { resolveScaledContentArea } from "../utils/scaleToFit.js";
7
8
  //#region src/renderPptx/nodes/processArrow.ts
8
9
  function renderProcessArrowNode(node, ctx) {
9
10
  const direction = node.direction ?? "horizontal";
@@ -16,20 +17,12 @@ function renderProcessArrowNode(node, ctx) {
16
17
  const itemHeight = node.itemHeight ?? 80;
17
18
  const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;
18
19
  const gap = node.gap ?? -arrowDepth;
19
- const content = getContentArea(node);
20
- const intrinsic = measureProcessArrow(node);
21
- const scaleFactor = calcScaleFactor(content.w, content.h, intrinsic.width, intrinsic.height, "processArrow", ctx.buildContext.diagnostics);
20
+ const { content, scaleFactor } = resolveScaledContentArea(node, measureProcessArrow(node), ctx);
22
21
  const scaledItemWidth = itemWidth * scaleFactor;
23
22
  const scaledItemHeight = itemHeight * scaleFactor;
24
23
  const scaledGap = gap * scaleFactor;
25
24
  const scaledArrowDepth = arrowDepth * scaleFactor;
26
- const contentNode = {
27
- ...node,
28
- x: content.x,
29
- y: content.y,
30
- w: content.w,
31
- h: content.h
32
- };
25
+ const contentNode = withContentBounds(node, content);
33
26
  if (direction === "horizontal") renderHorizontalProcessArrow(contentNode, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, scaledArrowDepth, defaultColor, defaultTextColor, scaleFactor);
34
27
  else renderVerticalProcessArrow(contentNode, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, scaledArrowDepth, defaultColor, defaultTextColor, scaleFactor);
35
28
  }
@@ -40,8 +33,8 @@ function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, it
40
33
  steps.forEach((step, index) => {
41
34
  const stepX = startX + index * (itemWidth + gap);
42
35
  const stepY = centerY - itemHeight / 2;
43
- const fillColor = step.color?.replace("#", "") ?? defaultColor;
44
- const textColor = step.textColor?.replace("#", "") ?? defaultTextColor;
36
+ const fillColor = stripHash(step.color) ?? defaultColor;
37
+ const textColor = stripHash(step.textColor) ?? defaultTextColor;
45
38
  const isFirst = index === 0;
46
39
  const points = isFirst ? [
47
40
  {
@@ -128,8 +121,8 @@ function renderVerticalProcessArrow(node, ctx, steps, stepCount, itemWidth, item
128
121
  steps.forEach((step, index) => {
129
122
  const stepX = centerX - itemWidth / 2;
130
123
  const stepY = startY + index * (itemHeight + gap);
131
- const fillColor = step.color?.replace("#", "") ?? defaultColor;
132
- const textColor = step.textColor?.replace("#", "") ?? defaultTextColor;
124
+ const fillColor = stripHash(step.color) ?? defaultColor;
125
+ const textColor = stripHash(step.textColor) ?? defaultTextColor;
133
126
  const isFirst = index === 0;
134
127
  const points = isFirst ? [
135
128
  {
@@ -1 +1 @@
1
- {"version":3,"file":"processArrow.js","names":[],"sources":["../../../src/renderPptx/nodes/processArrow.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { measureProcessArrow } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport {\n ARROW_DEPTH_RATIO,\n DEFAULT_PROCESS_ARROW_ITEM_WIDTH,\n DEFAULT_PROCESS_ARROW_ITEM_HEIGHT,\n} from \"../../shared/processArrowConstants.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype ProcessArrowPositionedNode = Extract<\n PositionedNode,\n { type: \"processArrow\" }\n>;\n\nexport function renderProcessArrowNode(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const steps = node.steps;\n const stepCount = steps.length;\n\n if (stepCount === 0) return;\n\n const defaultColor = \"4472C4\"; // PowerPoint標準の青\n const defaultTextColor = \"FFFFFF\";\n const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;\n const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;\n const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;\n const gap = node.gap ?? -arrowDepth;\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const intrinsic = measureProcessArrow(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n intrinsic.width,\n intrinsic.height,\n \"processArrow\",\n ctx.buildContext.diagnostics,\n );\n\n const scaledItemWidth = itemWidth * scaleFactor;\n const scaledItemHeight = itemHeight * scaleFactor;\n const scaledGap = gap * scaleFactor;\n const scaledArrowDepth = arrowDepth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = {\n ...node,\n x: content.x,\n y: content.y,\n w: content.w,\n h: content.h,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n } else {\n renderVerticalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n }\n}\n\nfunction renderHorizontalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalWidth = stepCount * itemWidth + (stepCount - 1) * gap;\n const startX = node.x + (node.w - totalWidth) / 2;\n const centerY = node.y + node.h / 2;\n\n steps.forEach((step, index) => {\n const stepX = startX + index * (itemWidth + gap);\n const stepY = centerY - itemHeight / 2;\n const fillColor = step.color?.replace(\"#\", \"\") ?? defaultColor;\n const textColor = step.textColor?.replace(\"#\", \"\") ?? defaultTextColor;\n\n // custGeom でシェブロン形状を描画\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // homePlate 風: 左辺フラット、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { close: true as const },\n ]\n : [\n // chevron 風: 左辺に切り欠き、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { x: pxToIn(arrowDepth), y: pxToIn(itemHeight / 2) },\n { close: true as const },\n ];\n\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(stepX),\n y: pxToIn(stepY),\n w: pxToIn(itemWidth),\n h: pxToIn(itemHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetLeft = isFirst ? 0 : arrowDepth;\n const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);\n\n ctx.slide.addText(step.label, {\n x: pxToIn(stepX + textOffsetLeft),\n y: pxToIn(stepY),\n w: pxToIn(textWidth),\n h: pxToIn(itemHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n });\n });\n}\n\nfunction renderVerticalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalHeight = stepCount * itemHeight + (stepCount - 1) * gap;\n const startY = node.y + (node.h - totalHeight) / 2;\n const centerX = node.x + node.w / 2;\n\n steps.forEach((step, index) => {\n const stepX = centerX - itemWidth / 2;\n const stepY = startY + index * (itemHeight + gap);\n const fillColor = step.color?.replace(\"#\", \"\") ?? defaultColor;\n const textColor = step.textColor?.replace(\"#\", \"\") ?? defaultTextColor;\n\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // rect 風: 上辺フラット、下辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { close: true as const },\n ]\n : [\n // pentagon 風: 上辺に切り欠き、下辺が矢印\n { x: pxToIn(itemWidth / 2), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(arrowDepth) },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { x: 0, y: pxToIn(arrowDepth) },\n { close: true as const },\n ];\n\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(stepX),\n y: pxToIn(stepY),\n w: pxToIn(itemWidth),\n h: pxToIn(itemHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetTop = isFirst ? 0 : arrowDepth;\n const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);\n\n ctx.slide.addText(step.label, {\n x: pxToIn(stepX),\n y: pxToIn(stepY + textOffsetTop),\n w: pxToIn(itemWidth),\n h: pxToIn(textHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"processArrow.js","names":[],"sources":["../../../src/renderPptx/nodes/processArrow.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { measureProcessArrow } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n ARROW_DEPTH_RATIO,\n DEFAULT_PROCESS_ARROW_ITEM_WIDTH,\n DEFAULT_PROCESS_ARROW_ITEM_HEIGHT,\n} from \"../../shared/processArrowConstants.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\n\ntype ProcessArrowPositionedNode = Extract<\n PositionedNode,\n { type: \"processArrow\" }\n>;\n\nexport function renderProcessArrowNode(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const steps = node.steps;\n const stepCount = steps.length;\n\n if (stepCount === 0) return;\n\n const defaultColor = \"4472C4\"; // PowerPoint標準の青\n const defaultTextColor = \"FFFFFF\";\n const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;\n const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;\n const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;\n const gap = node.gap ?? -arrowDepth;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureProcessArrow(node),\n ctx,\n );\n\n const scaledItemWidth = itemWidth * scaleFactor;\n const scaledItemHeight = itemHeight * scaleFactor;\n const scaledGap = gap * scaleFactor;\n const scaledArrowDepth = arrowDepth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n if (direction === \"horizontal\") {\n renderHorizontalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n } else {\n renderVerticalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n }\n}\n\nfunction renderHorizontalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalWidth = stepCount * itemWidth + (stepCount - 1) * gap;\n const startX = node.x + (node.w - totalWidth) / 2;\n const centerY = node.y + node.h / 2;\n\n steps.forEach((step, index) => {\n const stepX = startX + index * (itemWidth + gap);\n const stepY = centerY - itemHeight / 2;\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n // custGeom でシェブロン形状を描画\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // homePlate 風: 左辺フラット、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { close: true as const },\n ]\n : [\n // chevron 風: 左辺に切り欠き、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { x: pxToIn(arrowDepth), y: pxToIn(itemHeight / 2) },\n { close: true as const },\n ];\n\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(stepX),\n y: pxToIn(stepY),\n w: pxToIn(itemWidth),\n h: pxToIn(itemHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetLeft = isFirst ? 0 : arrowDepth;\n const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);\n\n ctx.slide.addText(step.label, {\n x: pxToIn(stepX + textOffsetLeft),\n y: pxToIn(stepY),\n w: pxToIn(textWidth),\n h: pxToIn(itemHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n });\n });\n}\n\nfunction renderVerticalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalHeight = stepCount * itemHeight + (stepCount - 1) * gap;\n const startY = node.y + (node.h - totalHeight) / 2;\n const centerX = node.x + node.w / 2;\n\n steps.forEach((step, index) => {\n const stepX = centerX - itemWidth / 2;\n const stepY = startY + index * (itemHeight + gap);\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // rect 風: 上辺フラット、下辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { close: true as const },\n ]\n : [\n // pentagon 風: 上辺に切り欠き、下辺が矢印\n { x: pxToIn(itemWidth / 2), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(arrowDepth) },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { x: 0, y: pxToIn(arrowDepth) },\n { close: true as const },\n ];\n\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(stepX),\n y: pxToIn(stepY),\n w: pxToIn(itemWidth),\n h: pxToIn(itemHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetTop = isFirst ? 0 : arrowDepth;\n const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);\n\n ctx.slide.addText(step.label, {\n x: pxToIn(stepX),\n y: pxToIn(stepY + textOffsetTop),\n w: pxToIn(itemWidth),\n h: pxToIn(textHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n });\n });\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { pxToIn, pxToPt } from "../units.js";
2
- import { getContentArea } from "../utils/contentArea.js";
2
+ import { stripHash } from "../utils/visualStyle.js";
3
3
  import { measurePyramid } from "../../calcYogaLayout/measureCompositeNodes.js";
4
- import { calcScaleFactor } from "../utils/scaleToFit.js";
4
+ import { resolveScaledContentArea } from "../utils/scaleToFit.js";
5
5
  //#region src/renderPptx/nodes/pyramid.ts
6
6
  function renderPyramidNode(node, ctx) {
7
7
  const direction = node.direction ?? "up";
@@ -10,9 +10,7 @@ function renderPyramidNode(node, ctx) {
10
10
  if (levelCount === 0) return;
11
11
  const defaultColor = "4472C4";
12
12
  const defaultTextColor = "FFFFFF";
13
- const content = getContentArea(node);
14
- const intrinsic = measurePyramid(node);
15
- const scaleFactor = calcScaleFactor(content.w, content.h, intrinsic.width, intrinsic.height, "pyramid", ctx.buildContext.diagnostics);
13
+ const { content, scaleFactor } = resolveScaledContentArea(node, measurePyramid(node), ctx);
16
14
  const baseWidth = 400 * scaleFactor;
17
15
  const layerHeight = 50 * scaleFactor;
18
16
  const gap = 2 * scaleFactor;
@@ -21,8 +19,8 @@ function renderPyramidNode(node, ctx) {
21
19
  const startY = content.y + (content.h - totalHeight) / 2;
22
20
  for (let i = 0; i < levelCount; i++) {
23
21
  const level = levels[i];
24
- const fillColor = level.color?.replace("#", "") ?? defaultColor;
25
- const textColor = level.textColor?.replace("#", "") ?? defaultTextColor;
22
+ const fillColor = stripHash(level.color) ?? defaultColor;
23
+ const textColor = stripHash(level.textColor) ?? defaultTextColor;
26
24
  const layerY = startY + i * (layerHeight + gap);
27
25
  let topWidthRatio;
28
26
  let bottomWidthRatio;
@@ -1 +1 @@
1
- {"version":3,"file":"pyramid.js","names":[],"sources":["../../../src/renderPptx/nodes/pyramid.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measurePyramid } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype PyramidPositionedNode = Extract<PositionedNode, { type: \"pyramid\" }>;\n\nexport function renderPyramidNode(\n node: PyramidPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"up\";\n const levels = node.levels;\n const levelCount = levels.length;\n\n if (levelCount === 0) return;\n\n const defaultColor = \"4472C4\";\n const defaultTextColor = \"FFFFFF\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const intrinsic = measurePyramid(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n intrinsic.width,\n intrinsic.height,\n \"pyramid\",\n ctx.buildContext.diagnostics,\n );\n\n const baseWidth = 400 * scaleFactor;\n const layerHeight = 50 * scaleFactor;\n const gap = 2 * scaleFactor;\n\n const totalHeight = levelCount * layerHeight + (levelCount - 1) * gap;\n const startX = content.x + (content.w - baseWidth) / 2;\n const startY = content.y + (content.h - totalHeight) / 2;\n\n for (let i = 0; i < levelCount; i++) {\n const level = levels[i];\n const fillColor = level.color?.replace(\"#\", \"\") ?? defaultColor;\n const textColor = level.textColor?.replace(\"#\", \"\") ?? defaultTextColor;\n\n const layerY = startY + i * (layerHeight + gap);\n\n // direction=\"up\": i=0 が最上段(最も狭い=三角形)、i=levelCount-1 が最下段(最も広い)\n // direction=\"down\": i=0 が最上段(最も広い)、i=levelCount-1 が最下段(最も狭い=三角形)\n // 頂点層の上辺は幅0(三角形)、それ以外は台形\n let topWidthRatio: number;\n let bottomWidthRatio: number;\n\n if (direction === \"up\") {\n topWidthRatio = i / levelCount;\n bottomWidthRatio = (i + 1) / levelCount;\n } else {\n topWidthRatio = (levelCount - i) / levelCount;\n bottomWidthRatio = (levelCount - i - 1) / levelCount;\n }\n\n const topWidth = baseWidth * topWidthRatio;\n const bottomWidth = baseWidth * bottomWidthRatio;\n\n const topLeftX = startX + (baseWidth - topWidth) / 2;\n const topRightX = topLeftX + topWidth;\n const bottomLeftX = startX + (baseWidth - bottomWidth) / 2;\n const bottomRightX = bottomLeftX + bottomWidth;\n\n // custGeom のバウンディングボックス\n const bboxX = Math.min(topLeftX, bottomLeftX);\n const bboxW = Math.max(topRightX, bottomRightX) - bboxX;\n\n // points はバウンディングボックス内の相対インチ座標\n const points = [\n { x: pxToIn(topLeftX - bboxX), y: 0 },\n { x: pxToIn(topRightX - bboxX), y: 0 },\n { x: pxToIn(bottomRightX - bboxX), y: pxToIn(layerHeight) },\n { x: pxToIn(bottomLeftX - bboxX), y: pxToIn(layerHeight) },\n { close: true as const },\n ];\n\n // 図形を描画(頂点層は三角形、それ以外は台形)\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(bboxX),\n y: pxToIn(layerY),\n w: pxToIn(bboxW),\n h: pxToIn(layerHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央に重ねて描画\n ctx.slide.addText(level.label, {\n x: pxToIn(bboxX),\n y: pxToIn(layerY),\n w: pxToIn(bboxW),\n h: pxToIn(layerHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n align: \"center\",\n valign: \"middle\",\n autoFit: true,\n });\n }\n}\n"],"mappings":";;;;;AASA,SAAgB,kBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CACpB,MAAM,aAAa,OAAO;CAE1B,IAAI,eAAe,GAAG;CAEtB,MAAM,eAAe;CACrB,MAAM,mBAAmB;CAGzB,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,YAAY,eAAe,IAAI;CACrC,MAAM,cAAc,gBAClB,QAAQ,GACR,QAAQ,GACR,UAAU,OACV,UAAU,QACV,WACA,IAAI,aAAa,WACnB;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,MAAM,IAAI;CAEhB,MAAM,cAAc,aAAa,eAAe,aAAa,KAAK;CAClE,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,aAAa;CACrD,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,eAAe;CAEvD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,MAAM,QAAQ,OAAO;EACrB,MAAM,YAAY,MAAM,OAAO,QAAQ,KAAK,EAAE,KAAK;EACnD,MAAM,YAAY,MAAM,WAAW,QAAQ,KAAK,EAAE,KAAK;EAEvD,MAAM,SAAS,SAAS,KAAK,cAAc;EAK3C,IAAI;EACJ,IAAI;EAEJ,IAAI,cAAc,MAAM;GACtB,gBAAgB,IAAI;GACpB,oBAAoB,IAAI,KAAK;EAC/B,OAAO;GACL,iBAAiB,aAAa,KAAK;GACnC,oBAAoB,aAAa,IAAI,KAAK;EAC5C;EAEA,MAAM,WAAW,YAAY;EAC7B,MAAM,cAAc,YAAY;EAEhC,MAAM,WAAW,UAAU,YAAY,YAAY;EACnD,MAAM,YAAY,WAAW;EAC7B,MAAM,cAAc,UAAU,YAAY,eAAe;EACzD,MAAM,eAAe,cAAc;EAGnC,MAAM,QAAQ,KAAK,IAAI,UAAU,WAAW;EAC5C,MAAM,QAAQ,KAAK,IAAI,WAAW,YAAY,IAAI;EAGlD,MAAM,SAAS;GACb;IAAE,GAAG,OAAO,WAAW,KAAK;IAAG,GAAG;GAAE;GACpC;IAAE,GAAG,OAAO,YAAY,KAAK;IAAG,GAAG;GAAE;GACrC;IAAE,GAAG,OAAO,eAAe,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GAC1D;IAAE,GAAG,OAAO,cAAc,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GACzD,EAAE,OAAO,KAAc;EACzB;EAGA,IAAI,MAAM,SAAS,YAAqB;GACtC,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,WAAW;GACrB;GACA,MAAM,EAAE,OAAO,UAAU;GACzB,MAAM,EAAE,MAAM,OAAgB;EAChC,CAAC;EAGD,IAAI,MAAM,QAAQ,MAAM,OAAO;GAC7B,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,WAAW;GACrB,UAAU,QAAQ,KAAK,YAAY,MAAM,WAAW;GACpD,UAAU,KAAK,cAAc;GAC7B,OAAO;GACP,MAAM,KAAK,QAAQ;GACnB,OAAO;GACP,QAAQ;GACR,SAAS;EACX,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"pyramid.js","names":[],"sources":["../../../src/renderPptx/nodes/pyramid.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measurePyramid } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\n\ntype PyramidPositionedNode = Extract<PositionedNode, { type: \"pyramid\" }>;\n\nexport function renderPyramidNode(\n node: PyramidPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"up\";\n const levels = node.levels;\n const levelCount = levels.length;\n\n if (levelCount === 0) return;\n\n const defaultColor = \"4472C4\";\n const defaultTextColor = \"FFFFFF\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measurePyramid(node),\n ctx,\n );\n\n const baseWidth = 400 * scaleFactor;\n const layerHeight = 50 * scaleFactor;\n const gap = 2 * scaleFactor;\n\n const totalHeight = levelCount * layerHeight + (levelCount - 1) * gap;\n const startX = content.x + (content.w - baseWidth) / 2;\n const startY = content.y + (content.h - totalHeight) / 2;\n\n for (let i = 0; i < levelCount; i++) {\n const level = levels[i];\n const fillColor = stripHash(level.color) ?? defaultColor;\n const textColor = stripHash(level.textColor) ?? defaultTextColor;\n\n const layerY = startY + i * (layerHeight + gap);\n\n // direction=\"up\": i=0 が最上段(最も狭い=三角形)、i=levelCount-1 が最下段(最も広い)\n // direction=\"down\": i=0 が最上段(最も広い)、i=levelCount-1 が最下段(最も狭い=三角形)\n // 頂点層の上辺は幅0(三角形)、それ以外は台形\n let topWidthRatio: number;\n let bottomWidthRatio: number;\n\n if (direction === \"up\") {\n topWidthRatio = i / levelCount;\n bottomWidthRatio = (i + 1) / levelCount;\n } else {\n topWidthRatio = (levelCount - i) / levelCount;\n bottomWidthRatio = (levelCount - i - 1) / levelCount;\n }\n\n const topWidth = baseWidth * topWidthRatio;\n const bottomWidth = baseWidth * bottomWidthRatio;\n\n const topLeftX = startX + (baseWidth - topWidth) / 2;\n const topRightX = topLeftX + topWidth;\n const bottomLeftX = startX + (baseWidth - bottomWidth) / 2;\n const bottomRightX = bottomLeftX + bottomWidth;\n\n // custGeom のバウンディングボックス\n const bboxX = Math.min(topLeftX, bottomLeftX);\n const bboxW = Math.max(topRightX, bottomRightX) - bboxX;\n\n // points はバウンディングボックス内の相対インチ座標\n const points = [\n { x: pxToIn(topLeftX - bboxX), y: 0 },\n { x: pxToIn(topRightX - bboxX), y: 0 },\n { x: pxToIn(bottomRightX - bboxX), y: pxToIn(layerHeight) },\n { x: pxToIn(bottomLeftX - bboxX), y: pxToIn(layerHeight) },\n { close: true as const },\n ];\n\n // 図形を描画(頂点層は三角形、それ以外は台形)\n ctx.slide.addShape(\"custGeom\" as never, {\n x: pxToIn(bboxX),\n y: pxToIn(layerY),\n w: pxToIn(bboxW),\n h: pxToIn(layerHeight),\n points,\n fill: { color: fillColor },\n line: { type: \"none\" as const },\n });\n\n // テキストを図形の中央に重ねて描画\n ctx.slide.addText(level.label, {\n x: pxToIn(bboxX),\n y: pxToIn(layerY),\n w: pxToIn(bboxW),\n h: pxToIn(layerHeight),\n fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n align: \"center\",\n valign: \"middle\",\n autoFit: true,\n });\n }\n}\n"],"mappings":";;;;;AASA,SAAgB,kBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CACpB,MAAM,aAAa,OAAO;CAE1B,IAAI,eAAe,GAAG;CAEtB,MAAM,eAAe;CACrB,MAAM,mBAAmB;CAGzB,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,eAAe,IAAI,GACnB,GACF;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,MAAM,IAAI;CAEhB,MAAM,cAAc,aAAa,eAAe,aAAa,KAAK;CAClE,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,aAAa;CACrD,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,eAAe;CAEvD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,MAAM,QAAQ,OAAO;EACrB,MAAM,YAAY,UAAU,MAAM,KAAK,KAAK;EAC5C,MAAM,YAAY,UAAU,MAAM,SAAS,KAAK;EAEhD,MAAM,SAAS,SAAS,KAAK,cAAc;EAK3C,IAAI;EACJ,IAAI;EAEJ,IAAI,cAAc,MAAM;GACtB,gBAAgB,IAAI;GACpB,oBAAoB,IAAI,KAAK;EAC/B,OAAO;GACL,iBAAiB,aAAa,KAAK;GACnC,oBAAoB,aAAa,IAAI,KAAK;EAC5C;EAEA,MAAM,WAAW,YAAY;EAC7B,MAAM,cAAc,YAAY;EAEhC,MAAM,WAAW,UAAU,YAAY,YAAY;EACnD,MAAM,YAAY,WAAW;EAC7B,MAAM,cAAc,UAAU,YAAY,eAAe;EACzD,MAAM,eAAe,cAAc;EAGnC,MAAM,QAAQ,KAAK,IAAI,UAAU,WAAW;EAC5C,MAAM,QAAQ,KAAK,IAAI,WAAW,YAAY,IAAI;EAGlD,MAAM,SAAS;GACb;IAAE,GAAG,OAAO,WAAW,KAAK;IAAG,GAAG;GAAE;GACpC;IAAE,GAAG,OAAO,YAAY,KAAK;IAAG,GAAG;GAAE;GACrC;IAAE,GAAG,OAAO,eAAe,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GAC1D;IAAE,GAAG,OAAO,cAAc,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GACzD,EAAE,OAAO,KAAc;EACzB;EAGA,IAAI,MAAM,SAAS,YAAqB;GACtC,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,WAAW;GACrB;GACA,MAAM,EAAE,OAAO,UAAU;GACzB,MAAM,EAAE,MAAM,OAAgB;EAChC,CAAC;EAGD,IAAI,MAAM,QAAQ,MAAM,OAAO;GAC7B,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,KAAK;GACf,GAAG,OAAO,WAAW;GACrB,UAAU,QAAQ,KAAK,YAAY,MAAM,WAAW;GACpD,UAAU,KAAK,cAAc;GAC7B,OAAO;GACP,MAAM,KAAK,QAAQ;GACnB,OAAO;GACP,QAAQ;GACR,SAAS;EACX,CAAC;CACH;AACF"}
@@ -1,31 +1,18 @@
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
  import { convertStrike, convertUnderline } from "../textOptions.js";
4
+ import { convertBorderLine, convertShadow } from "../utils/visualStyle.js";
4
5
  //#region src/renderPptx/nodes/shape.ts
5
6
  function renderShapeNode(node, ctx) {
6
- const content = getContentArea(node);
7
7
  const shapeOptions = {
8
- x: pxToIn(content.x),
9
- y: pxToIn(content.y),
10
- w: pxToIn(content.w),
11
- h: pxToIn(content.h),
8
+ ...getContentAreaIn(node),
12
9
  fill: node.fill ? {
13
10
  color: node.fill.color,
14
11
  transparency: node.fill.transparency
15
12
  } : void 0,
16
- line: node.line ? {
17
- color: node.line.color,
18
- width: node.line.width !== void 0 ? pxToPt(node.line.width) : void 0,
19
- dashType: node.line.dashType
20
- } : void 0,
21
- shadow: node.shadow ? {
22
- type: node.shadow.type ?? "outer",
23
- opacity: node.shadow.opacity,
24
- blur: node.shadow.blur,
25
- angle: node.shadow.angle,
26
- offset: node.shadow.offset,
27
- color: node.shadow.color
28
- } : void 0
13
+ line: node.line ? convertBorderLine(node.line) : void 0,
14
+ shadow: convertShadow(node.shadow),
15
+ rotate: node.rotate
29
16
  };
30
17
  if (node.text) ctx.slide.addText(node.text, {
31
18
  ...shapeOptions,
@@ -1 +1 @@
1
- {"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const content = getContentArea(node);\n const shapeOptions = {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fill: node.fill\n ? {\n color: node.fill.color,\n transparency: node.fill.transparency,\n }\n : undefined,\n line: node.line\n ? {\n color: node.line.color,\n width:\n node.line.width !== undefined ? pxToPt(node.line.width) : undefined,\n dashType: node.line.dashType,\n }\n : undefined,\n shadow: node.shadow\n ? {\n type: node.shadow.type ?? (\"outer\" as const),\n opacity: node.shadow.opacity,\n blur: node.shadow.blur,\n angle: node.shadow.angle,\n offset: node.shadow.offset,\n color: node.shadow.color,\n }\n : undefined,\n };\n\n if (node.text) {\n // テキストがある場合:addTextでshapeを指定\n ctx.slide.addText(node.text, {\n ...shapeOptions,\n shape: node.shapeType,\n fontSize: pxToPt(node.fontSize ?? 24),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\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 align: node.textAlign ?? \"center\",\n valign: \"middle\" as const,\n lineSpacingMultiple: node.lineHeight ?? 1.3,\n });\n } else {\n // テキストがない場合:addShapeを使用\n ctx.slide.addShape(node.shapeType, shapeOptions);\n }\n}\n"],"mappings":";;;;AAQA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAe;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,MAAM,KAAK,OACP;GACE,OAAO,KAAK,KAAK;GACjB,cAAc,KAAK,KAAK;EAC1B,IACA,KAAA;EACJ,MAAM,KAAK,OACP;GACE,OAAO,KAAK,KAAK;GACjB,OACE,KAAK,KAAK,UAAU,KAAA,IAAY,OAAO,KAAK,KAAK,KAAK,IAAI,KAAA;GAC5D,UAAU,KAAK,KAAK;EACtB,IACA,KAAA;EACJ,QAAQ,KAAK,SACT;GACE,MAAM,KAAK,OAAO,QAAS;GAC3B,SAAS,KAAK,OAAO;GACrB,MAAM,KAAK,OAAO;GAClB,OAAO,KAAK,OAAO;GACnB,QAAQ,KAAK,OAAO;GACpB,OAAO,KAAK,OAAO;EACrB,IACA,KAAA;CACN;CAEA,IAAI,KAAK,MAEP,IAAI,MAAM,QAAQ,KAAK,MAAM;EAC3B,GAAG;EACH,OAAO,KAAK;EACZ,UAAU,OAAO,KAAK,YAAY,EAAE;EACpC,UAAU,KAAK,cAAc;EAC7B,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,qBAAqB,KAAK,cAAc;CAC1C,CAAC;MAGD,IAAI,MAAM,SAAS,KAAK,WAAW,YAAY;AAEnD"}
1
+ {"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentAreaIn } from \"../utils/contentArea.ts\";\nimport { convertBorderLine, convertShadow } from \"../utils/visualStyle.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const shapeOptions = {\n ...getContentAreaIn(node),\n fill: node.fill\n ? {\n color: node.fill.color,\n transparency: node.fill.transparency,\n }\n : undefined,\n line: node.line ? convertBorderLine(node.line) : undefined,\n shadow: convertShadow(node.shadow),\n rotate: node.rotate,\n };\n\n if (node.text) {\n // テキストがある場合:addTextでshapeを指定\n ctx.slide.addText(node.text, {\n ...shapeOptions,\n shape: node.shapeType,\n fontSize: pxToPt(node.fontSize ?? 24),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\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 align: node.textAlign ?? \"center\",\n valign: \"middle\" as const,\n lineSpacingMultiple: node.lineHeight ?? 1.3,\n });\n } else {\n // テキストがない場合:addShapeを使用\n ctx.slide.addShape(node.shapeType, shapeOptions);\n }\n}\n"],"mappings":";;;;;AASA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,eAAe;EACnB,GAAG,iBAAiB,IAAI;EACxB,MAAM,KAAK,OACP;GACE,OAAO,KAAK,KAAK;GACjB,cAAc,KAAK,KAAK;EAC1B,IACA,KAAA;EACJ,MAAM,KAAK,OAAO,kBAAkB,KAAK,IAAI,IAAI,KAAA;EACjD,QAAQ,cAAc,KAAK,MAAM;EACjC,QAAQ,KAAK;CACf;CAEA,IAAI,KAAK,MAEP,IAAI,MAAM,QAAQ,KAAK,MAAM;EAC3B,GAAG;EACH,OAAO,KAAK;EACZ,UAAU,OAAO,KAAK,YAAY,EAAE;EACpC,UAAU,KAAK,cAAc;EAC7B,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,qBAAqB,KAAK,cAAc;CAC1C,CAAC;MAGD,IAAI,MAAM,SAAS,KAAK,WAAW,YAAY;AAEnD"}
@@ -1,12 +1,9 @@
1
- import { pxToIn } from "../units.js";
1
+ import { rectPxToIn } from "../units.js";
2
2
  //#region src/renderPptx/nodes/svg.ts
3
3
  function renderSvgNode(node, ctx) {
4
4
  ctx.slide.addImage({
5
5
  data: node.iconImageData,
6
- x: pxToIn(node.x),
7
- y: pxToIn(node.y),
8
- w: pxToIn(node.w),
9
- h: pxToIn(node.h)
6
+ ...rectPxToIn(node)
10
7
  });
11
8
  }
12
9
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"svg.js","names":[],"sources":["../../../src/renderPptx/nodes/svg.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn } from \"../units.ts\";\n\ntype SvgPositionedNode = Extract<PositionedNode, { type: \"svg\" }>;\n\nexport function renderSvgNode(\n node: SvgPositionedNode,\n ctx: RenderContext,\n): void {\n ctx.slide.addImage({\n data: node.iconImageData,\n x: pxToIn(node.x),\n y: pxToIn(node.y),\n w: pxToIn(node.w),\n h: pxToIn(node.h),\n });\n}\n"],"mappings":";;AAMA,SAAgB,cACd,MACA,KACM;CACN,IAAI,MAAM,SAAS;EACjB,MAAM,KAAK;EACX,GAAG,OAAO,KAAK,CAAC;EAChB,GAAG,OAAO,KAAK,CAAC;EAChB,GAAG,OAAO,KAAK,CAAC;EAChB,GAAG,OAAO,KAAK,CAAC;CAClB,CAAC;AACH"}
1
+ {"version":3,"file":"svg.js","names":[],"sources":["../../../src/renderPptx/nodes/svg.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { rectPxToIn } from \"../units.ts\";\n\ntype SvgPositionedNode = Extract<PositionedNode, { type: \"svg\" }>;\n\nexport function renderSvgNode(\n node: SvgPositionedNode,\n ctx: RenderContext,\n): void {\n ctx.slide.addImage({\n data: node.iconImageData,\n ...rectPxToIn(node),\n });\n}\n"],"mappings":";;AAMA,SAAgB,cACd,MACA,KACM;CACN,IAAI,MAAM,SAAS;EACjB,MAAM,KAAK;EACX,GAAG,WAAW,IAAI;CACpB,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- import { pxToIn, pxToPt } from "../units.js";
1
+ import { pxToIn, pxToPt, rectPxToIn } from "../units.js";
2
2
  import { getContentArea } from "../utils/contentArea.js";
3
3
  import { convertStrike, convertUnderline } from "../textOptions.js";
4
4
  import { resolveColumnWidths, resolveRowHeights } from "../../shared/tableUtils.js";
@@ -49,10 +49,7 @@ function renderTableNode(node, ctx) {
49
49
  }));
50
50
  const content = getContentArea(node);
51
51
  const tableOptions = {
52
- x: pxToIn(content.x),
53
- y: pxToIn(content.y),
54
- w: pxToIn(content.w),
55
- h: pxToIn(content.h),
52
+ ...rectPxToIn(content),
56
53
  colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),
57
54
  rowH: resolveRowHeights(node).map((height) => pxToIn(height)),
58
55
  margin: 0
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const tableRows = node.rows.map((row) =>\n row.cells.map((cell) => {\n const cellFontFace = cell.fontFamily;\n const cellOptions: Record<string, unknown> = {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: cellFontFace,\n color: cell.color,\n bold: cell.bold,\n italic: cell.italic,\n underline: convertUnderline(cell.underline),\n strike: convertStrike(cell.strike),\n highlight: cell.highlight,\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n\n if (cell.runs && cell.runs.length > 0) {\n const textItems = cell.runs.map((run) => ({\n text: run.text,\n options: {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: run.fontFamily ?? cellFontFace,\n color: run.color ?? cell.color,\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline: convertUnderline(run.underline ?? cell.underline),\n strike: convertStrike(run.strike ?? cell.strike),\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n }));\n return {\n text: textItems,\n options: {\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n },\n };\n }\n\n return {\n text: cell.text,\n options: cellOptions,\n };\n }),\n );\n\n const content = getContentArea(node);\n const tableOptions: Record<string, unknown> = {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),\n rowH: resolveRowHeights(node).map((height) => pxToIn(height)),\n margin: 0,\n };\n\n if (node.cellBorder) {\n tableOptions.border = {\n color: node.cellBorder.color ?? \"000000\",\n pt:\n node.cellBorder.width !== undefined ? pxToPt(node.cellBorder.width) : 1,\n type: node.cellBorder.dashType ?? \"solid\",\n };\n }\n\n ctx.slide.addTable(tableRows, tableOptions);\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,QAC/B,IAAI,MAAM,KAAK,SAAS;EACtB,MAAM,eAAe,KAAK;EAC1B,MAAM,cAAuC;GAC3C,UAAU,OAAO,KAAK,YAAY,EAAE;GACpC,UAAU;GACV,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;GACJ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EAEA,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAelC,OAAO;GACL,MAfgB,KAAK,KAAK,KAAK,SAAS;IACxC,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,KAAK,YAAY,EAAE;KACpC,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF,EAEgB;GACd,SAAS;IACP,OAAO,KAAK,aAAa;IACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;IACJ,SAAS,KAAK;IACd,SAAS,KAAK;GAChB;EACF;EAGF,OAAO;GACL,MAAM,KAAK;GACX,SAAS;EACX;CACF,CAAC,CACH;CAEA,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAwC;EAC5C,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,MAAM,oBAAoB,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,OAAO,KAAK,CAAC;EACvE,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,WAAW,OAAO,MAAM,CAAC;EAC5D,QAAQ;CACV;CAEA,IAAI,KAAK,YACP,aAAa,SAAS;EACpB,OAAO,KAAK,WAAW,SAAS;EAChC,IACE,KAAK,WAAW,UAAU,KAAA,IAAY,OAAO,KAAK,WAAW,KAAK,IAAI;EACxE,MAAM,KAAK,WAAW,YAAY;CACpC;CAGF,IAAI,MAAM,SAAS,WAAW,YAAY;AAC5C"}
1
+ {"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport { pxToIn, pxToPt, rectPxToIn } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const tableRows = node.rows.map((row) =>\n row.cells.map((cell) => {\n const cellFontFace = cell.fontFamily;\n const cellOptions: Record<string, unknown> = {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: cellFontFace,\n color: cell.color,\n bold: cell.bold,\n italic: cell.italic,\n underline: convertUnderline(cell.underline),\n strike: convertStrike(cell.strike),\n highlight: cell.highlight,\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n\n if (cell.runs && cell.runs.length > 0) {\n const textItems = cell.runs.map((run) => ({\n text: run.text,\n options: {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: run.fontFamily ?? cellFontFace,\n color: run.color ?? cell.color,\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline: convertUnderline(run.underline ?? cell.underline),\n strike: convertStrike(run.strike ?? cell.strike),\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n }));\n return {\n text: textItems,\n options: {\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n },\n };\n }\n\n return {\n text: cell.text,\n options: cellOptions,\n };\n }),\n );\n\n const content = getContentArea(node);\n const tableOptions: Record<string, unknown> = {\n ...rectPxToIn(content),\n colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),\n rowH: resolveRowHeights(node).map((height) => pxToIn(height)),\n margin: 0,\n };\n\n if (node.cellBorder) {\n tableOptions.border = {\n color: node.cellBorder.color ?? \"000000\",\n pt:\n node.cellBorder.width !== undefined ? pxToPt(node.cellBorder.width) : 1,\n type: node.cellBorder.dashType ?? \"solid\",\n };\n }\n\n ctx.slide.addTable(tableRows, tableOptions);\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,QAC/B,IAAI,MAAM,KAAK,SAAS;EACtB,MAAM,eAAe,KAAK;EAC1B,MAAM,cAAuC;GAC3C,UAAU,OAAO,KAAK,YAAY,EAAE;GACpC,UAAU;GACV,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;GACJ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EAEA,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAelC,OAAO;GACL,MAfgB,KAAK,KAAK,KAAK,SAAS;IACxC,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,KAAK,YAAY,EAAE;KACpC,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF,EAEgB;GACd,SAAS;IACP,OAAO,KAAK,aAAa;IACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;IACJ,SAAS,KAAK;IACd,SAAS,KAAK;GAChB;EACF;EAGF,OAAO;GACL,MAAM,KAAK;GACX,SAAS;EACX;CACF,CAAC,CACH;CAEA,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAwC;EAC5C,GAAG,WAAW,OAAO;EACrB,MAAM,oBAAoB,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,OAAO,KAAK,CAAC;EACvE,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,WAAW,OAAO,MAAM,CAAC;EAC5D,QAAQ;CACV;CAEA,IAAI,KAAK,YACP,aAAa,SAAS;EACpB,OAAO,KAAK,WAAW,SAAS;EAChC,IACE,KAAK,WAAW,UAAU,KAAA,IAAY,OAAO,KAAK,WAAW,KAAK,IAAI;EACxE,MAAM,KAAK,WAAW,YAAY;CACpC;CAGF,IAAI,MAAM,SAAS,WAAW,YAAY;AAC5C"}
@@ -1,5 +1,5 @@
1
1
  import { pxToPt } from "../units.js";
2
- import { convertStrike, convertUnderline, createTextOptions } from "../textOptions.js";
2
+ import { convertGlow, convertOutline, convertStrike, convertUnderline, createTextOptions } from "../textOptions.js";
3
3
  //#region src/renderPptx/nodes/text.ts
4
4
  function renderTextNode(node, ctx) {
5
5
  const textOptions = createTextOptions(node);
@@ -19,6 +19,8 @@ function renderTextNode(node, ctx) {
19
19
  underline: convertUnderline(run.underline ?? node.underline),
20
20
  strike: convertStrike(run.strike ?? node.strike),
21
21
  highlight: run.highlight ?? node.highlight,
22
+ glow: convertGlow(node.glow),
23
+ outline: convertOutline(node.outline),
22
24
  charSpacing: letterSpacingPx !== void 0 ? pxToPt(letterSpacingPx) : void 0,
23
25
  ...run.href ? { hyperlink: { url: run.href } } : {}
24
26
  }
@@ -29,6 +31,7 @@ function renderTextNode(node, ctx) {
29
31
  y: textOptions.y,
30
32
  w: textOptions.w,
31
33
  h: textOptions.h,
34
+ rotate: textOptions.rotate,
32
35
  align: textOptions.align,
33
36
  valign: textOptions.valign,
34
37
  margin: textOptions.margin,
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n createTextOptions,\n convertUnderline,\n convertStrike,\n} from \"../textOptions.ts\";\nimport { pxToPt } from \"../units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n const textOptions = createTextOptions(node);\n\n if (node.runs && node.runs.length > 0) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const textItems = node.runs.map((run) => {\n const letterSpacingPx = run.letterSpacing ?? node.letterSpacing;\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(fontSizePx),\n fontFace: run.fontFamily ?? fontFamily,\n color: run.color ?? node.color,\n bold: run.bold ?? node.bold,\n italic: run.italic ?? node.italic,\n underline: convertUnderline(run.underline ?? node.underline),\n strike: convertStrike(run.strike ?? node.strike),\n highlight: run.highlight ?? node.highlight,\n charSpacing:\n letterSpacingPx !== undefined ? pxToPt(letterSpacingPx) : undefined,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n };\n });\n ctx.slide.addText(textItems, {\n x: textOptions.x,\n y: textOptions.y,\n w: textOptions.w,\n h: textOptions.h,\n align: textOptions.align,\n valign: textOptions.valign,\n margin: textOptions.margin,\n lineSpacingMultiple: textOptions.lineSpacingMultiple,\n });\n } else {\n ctx.slide.addText(node.text ?? \"\", textOptions);\n }\n}\n"],"mappings":";;;AAWA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAE1C,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;EACrC,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EACtC,MAAM,YAAY,KAAK,KAAK,KAAK,QAAQ;GACvC,MAAM,kBAAkB,IAAI,iBAAiB,KAAK;GAClD,OAAO;IACL,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,UAAU;KAC3B,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,aACE,oBAAoB,KAAA,IAAY,OAAO,eAAe,IAAI,KAAA;KAC5D,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF;EACF,CAAC;EACD,IAAI,MAAM,QAAQ,WAAW;GAC3B,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,OAAO,YAAY;GACnB,QAAQ,YAAY;GACpB,QAAQ,YAAY;GACpB,qBAAqB,YAAY;EACnC,CAAC;CACH,OACE,IAAI,MAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAElD"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n createTextOptions,\n convertUnderline,\n convertStrike,\n convertGlow,\n convertOutline,\n} from \"../textOptions.ts\";\nimport { pxToPt } from \"../units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n const textOptions = createTextOptions(node);\n\n if (node.runs && node.runs.length > 0) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const textItems = node.runs.map((run) => {\n const letterSpacingPx = run.letterSpacing ?? node.letterSpacing;\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(fontSizePx),\n fontFace: run.fontFamily ?? fontFamily,\n color: run.color ?? node.color,\n bold: run.bold ?? node.bold,\n italic: run.italic ?? node.italic,\n underline: convertUnderline(run.underline ?? node.underline),\n strike: convertStrike(run.strike ?? node.strike),\n highlight: run.highlight ?? node.highlight,\n // glow / outline はノード単位指定のみ (run 単位はスコープ外)\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\n charSpacing:\n letterSpacingPx !== undefined ? pxToPt(letterSpacingPx) : undefined,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n };\n });\n ctx.slide.addText(textItems, {\n x: textOptions.x,\n y: textOptions.y,\n w: textOptions.w,\n h: textOptions.h,\n rotate: textOptions.rotate,\n align: textOptions.align,\n valign: textOptions.valign,\n margin: textOptions.margin,\n lineSpacingMultiple: textOptions.lineSpacingMultiple,\n });\n } else {\n ctx.slide.addText(node.text ?? \"\", textOptions);\n }\n}\n"],"mappings":";;;AAaA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAE1C,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;EACrC,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EACtC,MAAM,YAAY,KAAK,KAAK,KAAK,QAAQ;GACvC,MAAM,kBAAkB,IAAI,iBAAiB,KAAK;GAClD,OAAO;IACL,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,UAAU;KAC3B,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KAEjC,MAAM,YAAY,KAAK,IAAI;KAC3B,SAAS,eAAe,KAAK,OAAO;KACpC,aACE,oBAAoB,KAAA,IAAY,OAAO,eAAe,IAAI,KAAA;KAC5D,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF;EACF,CAAC;EACD,IAAI,MAAM,QAAQ,WAAW;GAC3B,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,QAAQ,YAAY;GACpB,QAAQ,YAAY;GACpB,qBAAqB,YAAY;EACnC,CAAC;CACH,OACE,IAAI,MAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAElD"}
@@ -1,7 +1,8 @@
1
1
  import { pxToIn, pxToPt } from "../units.js";
2
- import { getContentArea } from "../utils/contentArea.js";
2
+ import { withContentBounds } from "../utils/contentArea.js";
3
+ import { stripHash } from "../utils/visualStyle.js";
3
4
  import { measureTimeline } from "../../calcYogaLayout/measureCompositeNodes.js";
4
- import { calcScaleFactor } from "../utils/scaleToFit.js";
5
+ import { resolveScaledContentArea } from "../utils/scaleToFit.js";
5
6
  //#region src/renderPptx/nodes/timeline.ts
6
7
  function renderTimelineNode(node, ctx) {
7
8
  const direction = node.direction ?? "horizontal";
@@ -10,22 +11,19 @@ function renderTimelineNode(node, ctx) {
10
11
  const defaultColor = "1D4ED8";
11
12
  const baseNodeRadius = 12;
12
13
  const baseLineWidth = 4;
13
- const content = getContentArea(node);
14
- const intrinsic = measureTimeline(node);
15
- const scaleFactor = calcScaleFactor(content.w, content.h, intrinsic.width, intrinsic.height, "timeline", ctx.buildContext.diagnostics);
14
+ const textColors = {
15
+ date: stripHash(node.dateColor) ?? "64748B",
16
+ title: stripHash(node.titleColor) ?? "1E293B",
17
+ description: stripHash(node.descriptionColor) ?? "64748B"
18
+ };
19
+ const { content, scaleFactor } = resolveScaledContentArea(node, measureTimeline(node), ctx);
16
20
  const nodeRadius = baseNodeRadius * scaleFactor;
17
21
  const lineWidth = baseLineWidth * scaleFactor;
18
- const contentNode = {
19
- ...node,
20
- x: content.x,
21
- y: content.y,
22
- w: content.w,
23
- h: content.h
24
- };
25
- if (direction === "horizontal") renderHorizontalTimeline(contentNode, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor);
26
- else renderVerticalTimeline(contentNode, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor);
22
+ const contentNode = withContentBounds(node, content);
23
+ if (direction === "horizontal") renderHorizontalTimeline(contentNode, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor, textColors);
24
+ else renderVerticalTimeline(contentNode, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor, textColors);
27
25
  }
28
- function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor) {
26
+ function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor, textColors) {
29
27
  const itemCount = items.length;
30
28
  const lineY = node.y + node.h / 2;
31
29
  const labelW = 120 * scaleFactor;
@@ -67,7 +65,7 @@ function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, li
67
65
  h: pxToIn(dateLabelH),
68
66
  fontSize: pxToPt(12 * scaleFactor),
69
67
  fontFace: "Noto Sans JP",
70
- color: "64748B",
68
+ color: textColors.date,
71
69
  align: "center",
72
70
  valign: "bottom"
73
71
  });
@@ -78,7 +76,7 @@ function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, li
78
76
  h: pxToIn(titleLabelH),
79
77
  fontSize: pxToPt(14 * scaleFactor),
80
78
  fontFace: "Noto Sans JP",
81
- color: "1E293B",
79
+ color: textColors.title,
82
80
  bold: true,
83
81
  align: "center",
84
82
  valign: "top"
@@ -90,13 +88,13 @@ function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, li
90
88
  h: pxToIn(descLabelH),
91
89
  fontSize: pxToPt(11 * scaleFactor),
92
90
  fontFace: "Noto Sans JP",
93
- color: "64748B",
91
+ color: textColors.description,
94
92
  align: "center",
95
93
  valign: "top"
96
94
  });
97
95
  });
98
96
  }
99
- function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor) {
97
+ function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth, scaleFactor, textColors) {
100
98
  const itemCount = items.length;
101
99
  const lineX = node.x + 40 * scaleFactor;
102
100
  const startY = node.y + nodeRadius;
@@ -138,7 +136,7 @@ function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, line
138
136
  h: pxToIn(dateLabelH),
139
137
  fontSize: pxToPt(12 * scaleFactor),
140
138
  fontFace: "Noto Sans JP",
141
- color: "64748B",
139
+ color: textColors.date,
142
140
  align: "left",
143
141
  valign: "bottom"
144
142
  });
@@ -149,7 +147,7 @@ function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, line
149
147
  h: pxToIn(titleLabelH),
150
148
  fontSize: pxToPt(14 * scaleFactor),
151
149
  fontFace: "Noto Sans JP",
152
- color: "1E293B",
150
+ color: textColors.title,
153
151
  bold: true,
154
152
  align: "left",
155
153
  valign: "top"
@@ -161,7 +159,7 @@ function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, line
161
159
  h: pxToIn(descLabelH),
162
160
  fontSize: pxToPt(11 * scaleFactor),
163
161
  fontFace: "Noto Sans JP",
164
- color: "64748B",
162
+ color: textColors.description,
165
163
  align: "left",
166
164
  valign: "top"
167
165
  });
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const intrinsic = measureTimeline(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n intrinsic.width,\n intrinsic.height,\n \"timeline\",\n ctx.buildContext.diagnostics,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = {\n ...node,\n x: content.x,\n y: content.y,\n w: content.w,\n h: content.h,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(\n contentNode,\n ctx,\n items,\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n );\n } else {\n renderVerticalTimeline(\n contentNode,\n ctx,\n items,\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n );\n }\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n defaultColor: string,\n nodeRadius: number,\n lineWidth: number,\n scaleFactor: number,\n): void {\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(startX),\n y: pxToIn(lineY),\n w: pxToIn(lineLength),\n h: 0,\n line: { color: \"E2E8F0\", width: pxToPt(lineWidth) },\n });\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy - nodeRadius - dateOffset),\n w: pxToIn(labelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"bottom\",\n });\n\n // タイトルを下に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + titleGap),\n w: pxToIn(labelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"1E293B\",\n bold: true,\n align: \"center\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + descOffset),\n w: pxToIn(labelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"top\",\n });\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n defaultColor: string,\n nodeRadius: number,\n lineWidth: number,\n scaleFactor: number,\n): void {\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(lineX),\n y: pxToIn(startY),\n w: 0,\n h: pxToIn(lineLength),\n line: { color: \"E2E8F0\", width: pxToPt(lineWidth) },\n });\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を左上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - nodeRadius - 4 * scaleFactor),\n w: pxToIn(dateLabelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"left\",\n valign: \"bottom\",\n });\n\n // タイトルを右に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - 4 * scaleFactor),\n w: pxToIn(titleLabelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"1E293B\",\n bold: true,\n align: \"left\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy + 20 * scaleFactor),\n w: pxToIn(descLabelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"left\",\n valign: \"top\",\n });\n }\n });\n}\n"],"mappings":";;;;;AASA,SAAgB,mBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,QAAQ,KAAK;CAGnB,IAFkB,MAAM,WAEN,GAAG;CAErB,MAAM,eAAe;CACrB,MAAM,iBAAiB;CACvB,MAAM,gBAAgB;CAGtB,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,YAAY,gBAAgB,IAAI;CACtC,MAAM,cAAc,gBAClB,QAAQ,GACR,QAAQ,GACR,UAAU,OACV,UAAU,QACV,YACA,IAAI,aAAa,WACnB;CAEA,MAAM,aAAa,iBAAiB;CACpC,MAAM,YAAY,gBAAgB;CAGlC,MAAM,cAAc;EAClB,GAAG;EACH,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;CACb;CAEA,IAAI,cAAc,cAChB,yBACE,aACA,KACA,OACA,cACA,YACA,WACA,WACF;MAEA,uBACE,aACA,KACA,OACA,cACA,YACA,WACA,WACF;AAEJ;AAEA,SAAS,yBACP,MACA,KACA,OACA,cACA,YACA,WACA,aACM;CACN,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI;CAChC,MAAM,SAAS,MAAM;CAErB,MAAM,QAAQ,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,CAAC;CAC7C,MAAM,SAAS,KAAK,IAAI;CAExB,MAAM,aADO,KAAK,IAAI,KAAK,IAAI,QACL;CAG1B,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;EAC1C,GAAG,OAAO,MAAM;EAChB,GAAG,OAAO,KAAK;EACf,GAAG,OAAO,UAAU;EACpB,GAAG;EACH,MAAM;GAAE,OAAO;GAAU,OAAO,OAAO,SAAS;EAAE;CACpD,CAAC;CACD,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,aAAa,KAAK;CACxB,MAAM,WAAW,IAAI;CACrB,MAAM,aAAa,KAAK;CAGxB,MAAM,SAAS,MAAM,UAAU;EAE7B,MAAM,KAAK,SAAS,cADH,cAAc,IAAI,KAAM,SAAS,YAAY;EAE9D,MAAM,KAAK;EACX,MAAM,QAAQ,KAAK,SAAS;EAG5B,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,SAAS;GAC7C,GAAG,OAAO,KAAK,UAAU;GACzB,GAAG,OAAO,KAAK,UAAU;GACzB,GAAG,OAAO,aAAa,CAAC;GACxB,GAAG,OAAO,aAAa,CAAC;GACxB,MAAM,EAAE,MAAM;GACd,MAAM,EAAE,MAAM,OAAgB;EAChC,CAAC;EAGD,IAAI,MAAM,QAAQ,KAAK,MAAM;GAC3B,GAAG,OAAO,KAAK,SAAS,CAAC;GACzB,GAAG,OAAO,KAAK,aAAa,UAAU;GACtC,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,UAAU;GACpB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CAAC;EAGD,IAAI,MAAM,QAAQ,KAAK,OAAO;GAC5B,GAAG,OAAO,KAAK,SAAS,CAAC;GACzB,GAAG,OAAO,KAAK,aAAa,QAAQ;GACpC,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,WAAW;GACrB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,MAAM;GACN,OAAO;GACP,QAAQ;EACV,CAAC;EAGD,IAAI,KAAK,aACP,IAAI,MAAM,QAAQ,KAAK,aAAa;GAClC,GAAG,OAAO,KAAK,SAAS,CAAC;GACzB,GAAG,OAAO,KAAK,aAAa,UAAU;GACtC,GAAG,OAAO,MAAM;GAChB,GAAG,OAAO,UAAU;GACpB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CAAC;CAEL,CAAC;AACH;AAEA,SAAS,uBACP,MACA,KACA,OACA,cACA,YACA,WACA,aACM;CACN,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,KAAK,IAAI,KAAK;CAC5B,MAAM,SAAS,KAAK,IAAI;CAExB,MAAM,aADO,KAAK,IAAI,KAAK,IAAI,aACL;CAG1B,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;EAC1C,GAAG,OAAO,KAAK;EACf,GAAG,OAAO,MAAM;EAChB,GAAG;EACH,GAAG,OAAO,UAAU;EACpB,MAAM;GAAE,OAAO;GAAU,OAAO,OAAO,SAAS;EAAE;CACpD,CAAC;CAED,MAAM,WAAW,KAAK;CACtB,MAAM,aAAa,MAAM;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK,IAAI,KAAK;CAClC,MAAM,aAAa,KAAK,IAAI,KAAK;CAGjC,MAAM,SAAS,MAAM,UAAU;EAC7B,MAAM,WAAW,cAAc,IAAI,KAAM,SAAS,YAAY;EAC9D,MAAM,KAAK;EACX,MAAM,KAAK,SAAS,aAAa;EACjC,MAAM,QAAQ,KAAK,SAAS;EAG5B,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,SAAS;GAC7C,GAAG,OAAO,KAAK,UAAU;GACzB,GAAG,OAAO,KAAK,UAAU;GACzB,GAAG,OAAO,aAAa,CAAC;GACxB,GAAG,OAAO,aAAa,CAAC;GACxB,MAAM,EAAE,MAAM;GACd,MAAM,EAAE,MAAM,OAAgB;EAChC,CAAC;EAGD,IAAI,MAAM,QAAQ,KAAK,MAAM;GAC3B,GAAG,OAAO,KAAK,aAAa,QAAQ;GACpC,GAAG,OAAO,KAAK,aAAa,IAAI,WAAW;GAC3C,GAAG,OAAO,UAAU;GACpB,GAAG,OAAO,UAAU;GACpB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CAAC;EAGD,IAAI,MAAM,QAAQ,KAAK,OAAO;GAC5B,GAAG,OAAO,KAAK,aAAa,QAAQ;GACpC,GAAG,OAAO,KAAK,IAAI,WAAW;GAC9B,GAAG,OAAO,WAAW;GACrB,GAAG,OAAO,WAAW;GACrB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,MAAM;GACN,OAAO;GACP,QAAQ;EACV,CAAC;EAGD,IAAI,KAAK,aACP,IAAI,MAAM,QAAQ,KAAK,aAAa;GAClC,GAAG,OAAO,KAAK,aAAa,QAAQ;GACpC,GAAG,OAAO,KAAK,KAAK,WAAW;GAC/B,GAAG,OAAO,UAAU;GACpB,GAAG,OAAO,UAAU;GACpB,UAAU,OAAO,KAAK,WAAW;GACjC,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CAAC;CAEL,CAAC;AACH"}
1
+ {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\ntype TimelineTextColors = {\n date: string;\n title: string;\n description: string;\n};\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n const textColors: TimelineTextColors = {\n date: stripHash(node.dateColor) ?? \"64748B\",\n title: stripHash(node.titleColor) ?? \"1E293B\",\n description: stripHash(node.descriptionColor) ?? \"64748B\",\n };\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureTimeline(node),\n ctx,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(\n contentNode,\n ctx,\n items,\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n );\n } else {\n renderVerticalTimeline(\n contentNode,\n ctx,\n items,\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n );\n }\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n defaultColor: string,\n nodeRadius: number,\n lineWidth: number,\n scaleFactor: number,\n textColors: TimelineTextColors,\n): void {\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(startX),\n y: pxToIn(lineY),\n w: pxToIn(lineLength),\n h: 0,\n line: { color: \"E2E8F0\", width: pxToPt(lineWidth) },\n });\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy - nodeRadius - dateOffset),\n w: pxToIn(labelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.date,\n align: \"center\",\n valign: \"bottom\",\n });\n\n // タイトルを下に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + titleGap),\n w: pxToIn(labelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + descOffset),\n w: pxToIn(labelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\n });\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n defaultColor: string,\n nodeRadius: number,\n lineWidth: number,\n scaleFactor: number,\n textColors: TimelineTextColors,\n): void {\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(lineX),\n y: pxToIn(startY),\n w: 0,\n h: pxToIn(lineLength),\n line: { color: \"E2E8F0\", width: pxToPt(lineWidth) },\n });\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n\n // ノード(円)を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を左上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - nodeRadius - 4 * scaleFactor),\n w: pxToIn(dateLabelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.date,\n align: \"left\",\n valign: \"bottom\",\n });\n\n // タイトルを右に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - 4 * scaleFactor),\n w: pxToIn(titleLabelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy + 20 * scaleFactor),\n w: pxToIn(descLabelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\n });\n }\n });\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { pxToIn, pxToPt } from "../units.js";
2
- import { getContentArea } from "../utils/contentArea.js";
3
- import { calcScaleFactor } from "../utils/scaleToFit.js";
2
+ import { stripHash } from "../utils/visualStyle.js";
3
+ import { resolveScaledContentArea } from "../utils/scaleToFit.js";
4
4
  //#region src/renderPptx/nodes/tree.ts
5
5
  function renderTreeNode(node, ctx) {
6
6
  const layout = node.layout ?? "vertical";
@@ -11,6 +11,7 @@ function renderTreeNode(node, ctx) {
11
11
  const siblingGap = node.siblingGap ?? 20;
12
12
  const connectorStyle = node.connectorStyle ?? {};
13
13
  const defaultColor = "1D4ED8";
14
+ const defaultTextColor = stripHash(node.textColor) ?? "FFFFFF";
14
15
  function calculateSubtreeSize(item) {
15
16
  if (!item.children || item.children.length === 0) return {
16
17
  width: nodeWidth,
@@ -188,7 +189,7 @@ function renderTreeNode(node, ctx) {
188
189
  h: pxToIn(drawH),
189
190
  fontSize: pxToPt(12 * sf),
190
191
  fontFace: "Noto Sans JP",
191
- color: "FFFFFF",
192
+ color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,
192
193
  align: "center",
193
194
  valign: "middle"
194
195
  });
@@ -204,8 +205,7 @@ function renderTreeNode(node, ctx) {
204
205
  for (const child of layoutNode.children) drawAllNodes(child, sf, ox, oy);
205
206
  }
206
207
  const treeSize = calculateSubtreeSize(node.data);
207
- const content = getContentArea(node);
208
- const scaleFactor = calcScaleFactor(content.w, content.h, treeSize.width, treeSize.height, "tree", ctx.buildContext.diagnostics);
208
+ const { content, scaleFactor } = resolveScaledContentArea(node, treeSize, ctx);
209
209
  const scaledW = treeSize.width * scaleFactor;
210
210
  const scaledH = treeSize.height * scaleFactor;
211
211
  const offsetX = content.x + (content.w - scaledW) / 2;