@hirokisakabe/pom 9.1.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/buildContext.js +2 -6
  2. package/dist/buildContext.js.map +1 -1
  3. package/dist/buildPptx.d.ts +2 -1
  4. package/dist/buildPptx.d.ts.map +1 -1
  5. package/dist/buildPptx.js +4 -9
  6. package/dist/buildPptx.js.map +1 -1
  7. package/dist/calcYogaLayout/calcYogaLayout.js +1 -1
  8. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/registry/definitions/icon.js +1 -1
  11. package/dist/registry/definitions/list.js.map +1 -1
  12. package/dist/renderPptx/nodes/chart.js +172 -47
  13. package/dist/renderPptx/nodes/chart.js.map +1 -1
  14. package/dist/renderPptx/nodes/flow.js +98 -90
  15. package/dist/renderPptx/nodes/flow.js.map +1 -1
  16. package/dist/renderPptx/nodes/icon.js +36 -24
  17. package/dist/renderPptx/nodes/icon.js.map +1 -1
  18. package/dist/renderPptx/nodes/image.js +5 -22
  19. package/dist/renderPptx/nodes/image.js.map +1 -1
  20. package/dist/renderPptx/nodes/list.js +94 -111
  21. package/dist/renderPptx/nodes/list.js.map +1 -1
  22. package/dist/renderPptx/nodes/matrix.js +95 -70
  23. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  24. package/dist/renderPptx/nodes/processArrow.js +74 -35
  25. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  26. package/dist/renderPptx/nodes/pyramid.js +37 -16
  27. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  28. package/dist/renderPptx/nodes/shape.js +66 -37
  29. package/dist/renderPptx/nodes/shape.js.map +1 -1
  30. package/dist/renderPptx/nodes/svg.js +2 -5
  31. package/dist/renderPptx/nodes/svg.js.map +1 -1
  32. package/dist/renderPptx/nodes/table.js +89 -59
  33. package/dist/renderPptx/nodes/table.js.map +1 -1
  34. package/dist/renderPptx/nodes/text.js +1 -18
  35. package/dist/renderPptx/nodes/text.js.map +1 -1
  36. package/dist/renderPptx/nodes/timeline.js +114 -74
  37. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  38. package/dist/renderPptx/nodes/tree.js +81 -79
  39. package/dist/renderPptx/nodes/tree.js.map +1 -1
  40. package/dist/renderPptx/pptxAuthoring.js +381 -0
  41. package/dist/renderPptx/pptxAuthoring.js.map +1 -0
  42. package/dist/renderPptx/renderPptx.js +158 -128
  43. package/dist/renderPptx/renderPptx.js.map +1 -1
  44. package/dist/renderPptx/textOptions.js +7 -8
  45. package/dist/renderPptx/textOptions.js.map +1 -1
  46. package/dist/renderPptx/units.js +3 -3
  47. package/dist/renderPptx/units.js.map +1 -1
  48. package/dist/renderPptx/utils/backgroundBorder.js +70 -48
  49. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  50. package/dist/renderPptx/utils/contentArea.js +1 -1
  51. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  52. package/dist/renderPptx/utils/glimpsePicture.js +111 -0
  53. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
  54. package/dist/renderPptx/utils/glimpseShape.js +71 -0
  55. package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
  56. package/dist/renderPptx/utils/glimpseTextBox.js +107 -0
  57. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
  58. package/dist/renderPptx/utils/straightLine.js +33 -15
  59. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  60. package/dist/renderPptx/utils/visualStyle.js +3 -56
  61. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  62. package/dist/renderPptx/writablePptx.d.ts +23 -0
  63. package/dist/renderPptx/writablePptx.d.ts.map +1 -0
  64. package/dist/renderPptx/writablePptx.js +74 -0
  65. package/dist/renderPptx/writablePptx.js.map +1 -0
  66. package/dist/shared/measureImage.js.map +1 -1
  67. package/package.json +4 -5
  68. package/dist/renderPptx/glimpseTextBoxes.js +0 -347
  69. package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
  70. package/dist/renderPptx/glowEffects.js +0 -157
  71. package/dist/renderPptx/glowEffects.js.map +0 -1
  72. package/dist/renderPptx/gradientFills.js +0 -154
  73. package/dist/renderPptx/gradientFills.js.map +0 -1
@@ -1,9 +1,10 @@
1
- import { pxToIn, pxToPt } from "../units.js";
1
+ import { pxToIn } from "../units.js";
2
2
  import { withContentBounds } from "../utils/contentArea.js";
3
- import { convertStrike, convertUnderline } from "../textOptions.js";
4
- import { stripHash } from "../utils/visualStyle.js";
3
+ import { addGlimpseTextBox } from "../utils/glimpseTextBox.js";
4
+ import { addGlimpseShape, createShapeBoundsInput, noShapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
5
5
  import { ARROW_DEPTH_RATIO } from "../../shared/processArrowConstants.js";
6
6
  import { measureProcessArrow } from "../../calcYogaLayout/measureCompositeNodes.js";
7
+ import { stripHash } from "../utils/visualStyle.js";
7
8
  import { resolveScaledContentArea } from "../utils/scaleToFit.js";
8
9
  //#region src/renderPptx/nodes/processArrow.ts
9
10
  function renderProcessArrowNode(node, ctx) {
@@ -85,29 +86,48 @@ function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, it
85
86
  },
86
87
  { close: true }
87
88
  ];
88
- ctx.slide.addShape("custGeom", {
89
- x: pxToIn(stepX),
90
- y: pxToIn(stepY),
91
- w: pxToIn(itemWidth),
92
- h: pxToIn(itemHeight),
93
- points,
94
- fill: { color: fillColor },
95
- line: { type: "none" }
89
+ addGlimpseShape(ctx, {
90
+ geometry: {
91
+ kind: "preset",
92
+ preset: "rect"
93
+ },
94
+ ...createShapeBoundsInput({
95
+ x: stepX,
96
+ y: stepY,
97
+ w: itemWidth,
98
+ h: itemHeight
99
+ }),
100
+ fill: solidShapeFill(fillColor),
101
+ outline: noShapeOutline()
102
+ }, {
103
+ x: stepX,
104
+ y: stepY,
105
+ w: itemWidth,
106
+ h: itemHeight
107
+ }, {
108
+ fillColor,
109
+ customGeometry: {
110
+ width: pxToIn(itemWidth),
111
+ height: pxToIn(itemHeight),
112
+ points
113
+ }
96
114
  });
97
115
  const textOffsetLeft = isFirst ? 0 : arrowDepth;
98
116
  const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);
99
- ctx.slide.addText(step.label, {
100
- x: pxToIn(stepX + textOffsetLeft),
101
- y: pxToIn(stepY),
102
- w: pxToIn(textWidth),
103
- h: pxToIn(itemHeight),
104
- fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),
117
+ addGlimpseTextBox(ctx, {
118
+ x: stepX + textOffsetLeft,
119
+ y: stepY,
120
+ w: textWidth,
121
+ h: itemHeight
122
+ }, {
123
+ text: step.label,
124
+ fontSize: (node.fontSize ?? 14) * scaleFactor,
105
125
  fontFace: node.fontFamily ?? "Noto Sans JP",
106
126
  color: textColor,
107
127
  bold: node.bold ?? false,
108
128
  italic: node.italic,
109
- underline: convertUnderline(node.underline),
110
- strike: convertStrike(node.strike),
129
+ underline: node.underline,
130
+ strike: node.strike,
111
131
  highlight: node.highlight,
112
132
  align: "center",
113
133
  valign: "middle"
@@ -173,29 +193,48 @@ function renderVerticalProcessArrow(node, ctx, steps, stepCount, itemWidth, item
173
193
  },
174
194
  { close: true }
175
195
  ];
176
- ctx.slide.addShape("custGeom", {
177
- x: pxToIn(stepX),
178
- y: pxToIn(stepY),
179
- w: pxToIn(itemWidth),
180
- h: pxToIn(itemHeight),
181
- points,
182
- fill: { color: fillColor },
183
- line: { type: "none" }
196
+ addGlimpseShape(ctx, {
197
+ geometry: {
198
+ kind: "preset",
199
+ preset: "rect"
200
+ },
201
+ ...createShapeBoundsInput({
202
+ x: stepX,
203
+ y: stepY,
204
+ w: itemWidth,
205
+ h: itemHeight
206
+ }),
207
+ fill: solidShapeFill(fillColor),
208
+ outline: noShapeOutline()
209
+ }, {
210
+ x: stepX,
211
+ y: stepY,
212
+ w: itemWidth,
213
+ h: itemHeight
214
+ }, {
215
+ fillColor,
216
+ customGeometry: {
217
+ width: pxToIn(itemWidth),
218
+ height: pxToIn(itemHeight),
219
+ points
220
+ }
184
221
  });
185
222
  const textOffsetTop = isFirst ? 0 : arrowDepth;
186
223
  const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);
187
- ctx.slide.addText(step.label, {
188
- x: pxToIn(stepX),
189
- y: pxToIn(stepY + textOffsetTop),
190
- w: pxToIn(itemWidth),
191
- h: pxToIn(textHeight),
192
- fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),
224
+ addGlimpseTextBox(ctx, {
225
+ x: stepX,
226
+ y: stepY + textOffsetTop,
227
+ w: itemWidth,
228
+ h: textHeight
229
+ }, {
230
+ text: step.label,
231
+ fontSize: (node.fontSize ?? 14) * scaleFactor,
193
232
  fontFace: node.fontFamily ?? "Noto Sans JP",
194
233
  color: textColor,
195
234
  bold: node.bold ?? false,
196
235
  italic: node.italic,
197
- underline: convertUnderline(node.underline),
198
- strike: convertStrike(node.strike),
236
+ underline: node.underline,
237
+ strike: node.strike,
199
238
  highlight: node.highlight,
200
239
  align: "center",
201
240
  valign: "middle"
@@ -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 { 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
+ {"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 } from \"../units.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\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.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 addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetLeft = isFirst ? 0 : arrowDepth;\n const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX + textOffsetLeft,\n y: stepY,\n w: textWidth,\n h: itemHeight,\n },\n {\n text: step.label,\n fontSize: (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: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\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 addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetTop = isFirst ? 0 : arrowDepth;\n const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX,\n y: stepY + textOffsetTop,\n w: itemWidth,\n h: textHeight,\n },\n {\n text: step.label,\n fontSize: (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: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\n );\n });\n}\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
- import { pxToIn, pxToPt } from "../units.js";
2
- import { stripHash } from "../utils/visualStyle.js";
1
+ import { pxToIn } from "../units.js";
2
+ import { addGlimpseTextBox } from "../utils/glimpseTextBox.js";
3
+ import { addGlimpseShape, createShapeBoundsInput, noShapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
3
4
  import { measurePyramid } from "../../calcYogaLayout/measureCompositeNodes.js";
5
+ import { stripHash } from "../utils/visualStyle.js";
4
6
  import { resolveScaledContentArea } from "../utils/scaleToFit.js";
5
7
  //#region src/renderPptx/nodes/pyramid.ts
6
8
  function renderPyramidNode(node, ctx) {
@@ -58,21 +60,40 @@ function renderPyramidNode(node, ctx) {
58
60
  },
59
61
  { close: true }
60
62
  ];
61
- ctx.slide.addShape("custGeom", {
62
- x: pxToIn(bboxX),
63
- y: pxToIn(layerY),
64
- w: pxToIn(bboxW),
65
- h: pxToIn(layerHeight),
66
- points,
67
- fill: { color: fillColor },
68
- line: { type: "none" }
63
+ addGlimpseShape(ctx, {
64
+ geometry: {
65
+ kind: "preset",
66
+ preset: "rect"
67
+ },
68
+ ...createShapeBoundsInput({
69
+ x: bboxX,
70
+ y: layerY,
71
+ w: bboxW,
72
+ h: layerHeight
73
+ }),
74
+ fill: solidShapeFill(fillColor),
75
+ outline: noShapeOutline()
76
+ }, {
77
+ x: bboxX,
78
+ y: layerY,
79
+ w: bboxW,
80
+ h: layerHeight
81
+ }, {
82
+ fillColor,
83
+ customGeometry: {
84
+ width: pxToIn(bboxW),
85
+ height: pxToIn(layerHeight),
86
+ points
87
+ }
69
88
  });
70
- ctx.slide.addText(level.label, {
71
- x: pxToIn(bboxX),
72
- y: pxToIn(layerY),
73
- w: pxToIn(bboxW),
74
- h: pxToIn(layerHeight),
75
- fontSize: pxToPt((node.fontSize ?? 14) * scaleFactor),
89
+ addGlimpseTextBox(ctx, {
90
+ x: bboxX,
91
+ y: layerY,
92
+ w: bboxW,
93
+ h: layerHeight
94
+ }, {
95
+ text: level.label,
96
+ fontSize: (node.fontSize ?? 14) * scaleFactor,
76
97
  fontFace: node.fontFamily ?? "Noto Sans JP",
77
98
  color: textColor,
78
99
  bold: node.bold ?? false,
@@ -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 { 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
+ {"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 } from \"../units.ts\";\nimport { measurePyramid } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.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 addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: bboxX, y: layerY, w: bboxW, h: layerHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(bboxW),\n height: pxToIn(layerHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央に重ねて描画\n addGlimpseTextBox(\n ctx,\n {\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n },\n {\n text: level.label,\n fontSize: (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}\n"],"mappings":";;;;;;;AAgBA,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;EAEA,gBACE,KACA;GACE,UAAU;IAAE,MAAM;IAAU,QAAQ;GAAO;GAC3C,GAAG,uBAAuB;IACxB,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;GACL,CAAC;GACD,MAAM,eAAe,SAAS;GAC9B,SAAS,eAAe;EAC1B,GACA;GAAE,GAAG;GAAO,GAAG;GAAQ,GAAG;GAAO,GAAG;EAAY,GAChD;GACE;GACA,gBAAgB;IACd,OAAO,OAAO,KAAK;IACnB,QAAQ,OAAO,WAAW;IAC1B;GACF;EACF,CACF;EAGA,kBACE,KACA;GACE,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,MAAM;GACZ,WAAW,KAAK,YAAY,MAAM;GAClC,UAAU,KAAK,cAAc;GAC7B,OAAO;GACP,MAAM,KAAK,QAAQ;GACnB,OAAO;GACP,QAAQ;GACR,SAAS;EACX,CACF;CACF;AACF"}
@@ -1,7 +1,8 @@
1
- import { pxToPt } from "../units.js";
2
- import { getContentAreaIn } from "../utils/contentArea.js";
3
- import { convertStrike, convertUnderline } from "../textOptions.js";
4
- import { convertBorderLine, convertShadow } from "../utils/visualStyle.js";
1
+ import { pxToEmu, pxToPt } from "../units.js";
2
+ import { getContentArea } from "../utils/contentArea.js";
3
+ import { toColorInput } from "../pptxAuthoring.js";
4
+ import { addGlimpseShape, createShapeBoundsInput, createShapeRotationInput, noneShapeFill, shapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
5
+ import { asEmu, asHundredthPt, asPt } from "@pptx-glimpse/document";
5
6
  //#region src/renderPptx/nodes/shape.ts
6
7
  /**
7
8
  * outline (Text と同じ書式の `outline.size` / `outline.color`) と
@@ -22,41 +23,69 @@ function resolveShapeLine(line, outline) {
22
23
  dashType: line?.dashType
23
24
  };
24
25
  }
25
- function renderShapeNode(node, ctx) {
26
- const lineSpec = resolveShapeLine(node.line, node.outline);
27
- const glowMarker = node.glow ? ctx.buildContext.glowEffects.register(node.glow) : void 0;
28
- const shapeOptions = {
29
- ...getContentAreaIn(node),
30
- fill: node.fill ? {
31
- color: node.fill.color,
32
- transparency: node.fill.transparency
33
- } : void 0,
34
- line: lineSpec ? convertBorderLine(lineSpec) : void 0,
35
- shadow: convertShadow(node.shadow),
36
- rotate: node.rotate,
37
- objectName: glowMarker
26
+ function toUnderlineInput(underline) {
27
+ if (underline === void 0 || underline === false) return void 0;
28
+ if (underline === true) return true;
29
+ return {
30
+ style: underline.style,
31
+ color: toColorInput(underline.color)
32
+ };
33
+ }
34
+ function buildShapeTextProperties(node) {
35
+ return {
36
+ fontFace: node.fontFamily ?? "Noto Sans JP",
37
+ fontSize: asPt(pxToPt(node.fontSize ?? 24)),
38
+ color: toColorInput(node.color),
39
+ bold: node.bold,
40
+ italic: node.italic,
41
+ underline: toUnderlineInput(node.underline),
42
+ strike: node.strike,
43
+ baseline: node.subscript ? "subscript" : node.superscript ? "superscript" : void 0,
44
+ highlight: toColorInput(node.highlight)
38
45
  };
39
- if (node.text) {
40
- const fontSizePx = node.fontSize ?? 24;
41
- const lineHeight = node.lineHeight ?? 1.3;
42
- ctx.slide.addText(node.text, {
43
- ...shapeOptions,
44
- shape: node.shapeType,
45
- fontSize: pxToPt(fontSizePx),
46
- fontFace: node.fontFamily ?? "Noto Sans JP",
47
- color: node.color,
48
- bold: node.bold,
49
- italic: node.italic,
50
- underline: convertUnderline(node.underline),
51
- strike: convertStrike(node.strike),
52
- subscript: node.subscript,
53
- superscript: node.superscript,
54
- highlight: node.highlight,
46
+ }
47
+ function buildShapeParagraphs(node) {
48
+ if (!node.text) return void 0;
49
+ const fontSizePx = node.fontSize ?? 24;
50
+ const lineHeight = node.lineHeight ?? 1.3;
51
+ const properties = buildShapeTextProperties(node);
52
+ return node.text.replace(/\r*\n/g, "\n").split("\n").map((text) => ({
53
+ properties: {
55
54
  align: node.textAlign ?? "center",
56
- valign: "middle",
57
- lineSpacing: pxToPt(fontSizePx * lineHeight)
58
- });
59
- } else ctx.slide.addShape(node.shapeType, shapeOptions);
55
+ lineSpacing: asHundredthPt(Math.round(pxToPt(fontSizePx * lineHeight) * 100))
56
+ },
57
+ runs: [{
58
+ text,
59
+ properties
60
+ }]
61
+ }));
62
+ }
63
+ function renderShapeNode(node, ctx) {
64
+ const lineSpec = resolveShapeLine(node.line, node.outline);
65
+ const boundsPx = getContentArea(node);
66
+ const fillOpacity = node.fill?.transparency !== void 0 ? 1 - node.fill.transparency / 100 : void 0;
67
+ addGlimpseShape(ctx, {
68
+ geometry: {
69
+ kind: "preset",
70
+ preset: node.shapeType
71
+ },
72
+ ...createShapeBoundsInput(boundsPx),
73
+ rotation: createShapeRotationInput(node.rotate),
74
+ fill: node.fill?.color ? solidShapeFill(node.fill.color) : noneShapeFill(),
75
+ outline: shapeOutline(lineSpec),
76
+ effects: node.glow ? { glow: {
77
+ radius: asEmu(Math.round(pxToEmu(node.glow.size ?? 8))),
78
+ color: toColorInput(node.glow.color ?? "FFFFFF")
79
+ } } : void 0,
80
+ body: node.text ? { anchor: "middle" } : void 0,
81
+ paragraphs: buildShapeParagraphs(node)
82
+ }, boundsPx, {
83
+ fillColor: node.fill?.color,
84
+ fillOpacity,
85
+ glow: node.glow,
86
+ shadow: node.shadow,
87
+ dashType: lineSpec?.dashType
88
+ });
60
89
  }
61
90
  //#endregion
62
91
  export { renderShapeNode };
@@ -1 +1 @@
1
- {"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import type { BorderStyle, 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\n/**\n * outline (Text と同じ書式の `outline.size` / `outline.color`) と\n * 既存 `line` 属性 (`line.color` / `line.width` / `line.dashType`) を\n * 1 つの BorderStyle にマージする。\n *\n * フィールド単位のマージで、`outline` の指定があるフィールドは `line` を\n * 上書きするが、`outline` 側で省略されたフィールドは `line` の値を引き継ぎ、\n * `line` にも値が無い場合は Text outline と同じ既定値 (`width: 1pt 相当` /\n * `color: FFFFFF`) を採用する。`dashType` は `outline` に対応フィールドが\n * 無いため `line.dashType` をそのまま使う。\n */\nfunction resolveShapeLine(\n line: BorderStyle | undefined,\n outline: { size?: number; color?: string } | undefined,\n): BorderStyle | undefined {\n if (!outline) return line;\n return {\n color: outline.color ?? line?.color ?? \"FFFFFF\",\n width: outline.size ?? line?.width ?? 1,\n dashType: line?.dashType,\n };\n}\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const lineSpec = resolveShapeLine(node.line, node.outline);\n const glowMarker = node.glow\n ? ctx.buildContext.glowEffects.register(node.glow)\n : undefined;\n\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: lineSpec ? convertBorderLine(lineSpec) : undefined,\n shadow: convertShadow(node.shadow),\n rotate: node.rotate,\n objectName: glowMarker,\n };\n\n if (node.text) {\n const fontSizePx = node.fontSize ?? 24;\n const lineHeight = node.lineHeight ?? 1.3;\n // テキストがある場合:addTextでshapeを指定\n ctx.slide.addText(node.text, {\n ...shapeOptions,\n shape: node.shapeType,\n fontSize: pxToPt(fontSizePx),\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 subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n align: node.textAlign ?? \"center\",\n valign: \"middle\" as const,\n // Text と同じく行送りを固定値 (spcPts) で指定し、計測高さ\n // (行数 × fontSize × lineHeight) と実描画の行高さを一致させる (#846)。\n // valign middle のためテキストブロックは枠内中央に配置され、\n // Text のような描画 y 補正は不要\n lineSpacing: pxToPt(fontSizePx * lineHeight),\n });\n } else {\n // テキストがない場合:addShapeを使用\n ctx.slide.addShape(node.shapeType, shapeOptions);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,SAAS,iBACP,MACA,SACyB;CACzB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,OAAO,QAAQ,SAAS,MAAM,SAAS;EACvC,OAAO,QAAQ,QAAQ,MAAM,SAAS;EACtC,UAAU,MAAM;CAClB;AACF;AAEA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,WAAW,iBAAiB,KAAK,MAAM,KAAK,OAAO;CACzD,MAAM,aAAa,KAAK,OACpB,IAAI,aAAa,YAAY,SAAS,KAAK,IAAI,IAC/C,KAAA;CAEJ,MAAM,eAAe;EACnB,GAAG,iBAAiB,IAAI;EACxB,MAAM,KAAK,OACP;GACE,OAAO,KAAK,KAAK;GACjB,cAAc,KAAK,KAAK;EAC1B,IACA,KAAA;EACJ,MAAM,WAAW,kBAAkB,QAAQ,IAAI,KAAA;EAC/C,QAAQ,cAAc,KAAK,MAAM;EACjC,QAAQ,KAAK;EACb,YAAY;CACd;CAEA,IAAI,KAAK,MAAM;EACb,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EAEtC,IAAI,MAAM,QAAQ,KAAK,MAAM;GAC3B,GAAG;GACH,OAAO,KAAK;GACZ,UAAU,OAAO,UAAU;GAC3B,UAAU,KAAK,cAAc;GAC7B,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,aAAa,KAAK;GAClB,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,QAAQ;GAKR,aAAa,OAAO,aAAa,UAAU;EAC7C,CAAC;CACH,OAEE,IAAI,MAAM,SAAS,KAAK,WAAW,YAAY;AAEnD"}
1
+ {"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import {\n asEmu,\n asHundredthPt,\n asPt,\n type AddShapeParagraphInput,\n type AddShapeRunPropertiesInput,\n} from \"@pptx-glimpse/document\";\nimport type { BorderStyle, PositionedNode, Underline } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\nimport { pxToEmu } from \"../units.ts\";\nimport { toColorInput } from \"../pptxAuthoring.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n createShapeRotationInput,\n noneShapeFill,\n shapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\n/**\n * outline (Text と同じ書式の `outline.size` / `outline.color`) と\n * 既存 `line` 属性 (`line.color` / `line.width` / `line.dashType`) を\n * 1 つの BorderStyle にマージする。\n *\n * フィールド単位のマージで、`outline` の指定があるフィールドは `line` を\n * 上書きするが、`outline` 側で省略されたフィールドは `line` の値を引き継ぎ、\n * `line` にも値が無い場合は Text outline と同じ既定値 (`width: 1pt 相当` /\n * `color: FFFFFF`) を採用する。`dashType` は `outline` に対応フィールドが\n * 無いため `line.dashType` をそのまま使う。\n */\nfunction resolveShapeLine(\n line: BorderStyle | undefined,\n outline: { size?: number; color?: string } | undefined,\n): BorderStyle | undefined {\n if (!outline) return line;\n return {\n color: outline.color ?? line?.color ?? \"FFFFFF\",\n width: outline.size ?? line?.width ?? 1,\n dashType: line?.dashType,\n };\n}\n\nfunction toUnderlineInput(underline: Underline | undefined) {\n if (underline === undefined || underline === false) return undefined;\n if (underline === true) return true;\n return {\n style: underline.style,\n color: toColorInput(underline.color),\n };\n}\n\nfunction buildShapeTextProperties(\n node: ShapePositionedNode,\n): AddShapeRunPropertiesInput {\n return {\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n fontSize: asPt(pxToPt(node.fontSize ?? 24)),\n color: toColorInput(node.color),\n bold: node.bold,\n italic: node.italic,\n underline: toUnderlineInput(node.underline),\n strike: node.strike,\n baseline: node.subscript\n ? \"subscript\"\n : node.superscript\n ? \"superscript\"\n : undefined,\n highlight: toColorInput(node.highlight),\n };\n}\n\nfunction buildShapeParagraphs(\n node: ShapePositionedNode,\n): AddShapeParagraphInput[] | undefined {\n if (!node.text) return undefined;\n const fontSizePx = node.fontSize ?? 24;\n const lineHeight = node.lineHeight ?? 1.3;\n const properties = buildShapeTextProperties(node);\n return node.text\n .replace(/\\r*\\n/g, \"\\n\")\n .split(\"\\n\")\n .map((text) => ({\n properties: {\n align: node.textAlign ?? \"center\",\n lineSpacing: asHundredthPt(\n Math.round(pxToPt(fontSizePx * lineHeight) * 100),\n ),\n },\n runs: [{ text, properties }],\n }));\n}\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const lineSpec = resolveShapeLine(node.line, node.outline);\n const boundsPx = getContentArea(node);\n const fillOpacity =\n node.fill?.transparency !== undefined\n ? 1 - node.fill.transparency / 100\n : undefined;\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: node.shapeType },\n ...createShapeBoundsInput(boundsPx),\n rotation: createShapeRotationInput(node.rotate),\n fill: node.fill?.color\n ? solidShapeFill(node.fill.color)\n : noneShapeFill(),\n outline: shapeOutline(lineSpec),\n effects: node.glow\n ? {\n glow: {\n radius: asEmu(Math.round(pxToEmu(node.glow.size ?? 8))),\n color: toColorInput(node.glow.color ?? \"FFFFFF\")!,\n },\n }\n : undefined,\n body: node.text\n ? {\n anchor: \"middle\",\n }\n : undefined,\n paragraphs: buildShapeParagraphs(node),\n },\n boundsPx,\n {\n fillColor: node.fill?.color,\n fillOpacity,\n glow: node.glow,\n shadow: node.shadow,\n dashType: lineSpec?.dashType,\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmCA,SAAS,iBACP,MACA,SACyB;CACzB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,OAAO,QAAQ,SAAS,MAAM,SAAS;EACvC,OAAO,QAAQ,QAAQ,MAAM,SAAS;EACtC,UAAU,MAAM;CAClB;AACF;AAEA,SAAS,iBAAiB,WAAkC;CAC1D,IAAI,cAAc,KAAA,KAAa,cAAc,OAAO,OAAO,KAAA;CAC3D,IAAI,cAAc,MAAM,OAAO;CAC/B,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,aAAa,UAAU,KAAK;CACrC;AACF;AAEA,SAAS,yBACP,MAC4B;CAC5B,OAAO;EACL,UAAU,KAAK,cAAc;EAC7B,UAAU,KAAK,OAAO,KAAK,YAAY,EAAE,CAAC;EAC1C,OAAO,aAAa,KAAK,KAAK;EAC9B,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,KAAK;EACb,UAAU,KAAK,YACX,cACA,KAAK,cACH,gBACA,KAAA;EACN,WAAW,aAAa,KAAK,SAAS;CACxC;AACF;AAEA,SAAS,qBACP,MACsC;CACtC,IAAI,CAAC,KAAK,MAAM,OAAO,KAAA;CACvB,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,yBAAyB,IAAI;CAChD,OAAO,KAAK,KACT,QAAQ,UAAU,IAAI,CAAC,CACvB,MAAM,IAAI,CAAC,CACX,KAAK,UAAU;EACd,YAAY;GACV,OAAO,KAAK,aAAa;GACzB,aAAa,cACX,KAAK,MAAM,OAAO,aAAa,UAAU,IAAI,GAAG,CAClD;EACF;EACA,MAAM,CAAC;GAAE;GAAM;EAAW,CAAC;CAC7B,EAAE;AACN;AAEA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,WAAW,iBAAiB,KAAK,MAAM,KAAK,OAAO;CACzD,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,cACJ,KAAK,MAAM,iBAAiB,KAAA,IACxB,IAAI,KAAK,KAAK,eAAe,MAC7B,KAAA;CAEN,gBACE,KACA;EACE,UAAU;GAAE,MAAM;GAAU,QAAQ,KAAK;EAAU;EACnD,GAAG,uBAAuB,QAAQ;EAClC,UAAU,yBAAyB,KAAK,MAAM;EAC9C,MAAM,KAAK,MAAM,QACb,eAAe,KAAK,KAAK,KAAK,IAC9B,cAAc;EAClB,SAAS,aAAa,QAAQ;EAC9B,SAAS,KAAK,OACV,EACE,MAAM;GACJ,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC;GACtD,OAAO,aAAa,KAAK,KAAK,SAAS,QAAQ;EACjD,EACF,IACA,KAAA;EACJ,MAAM,KAAK,OACP,EACE,QAAQ,SACV,IACA,KAAA;EACJ,YAAY,qBAAqB,IAAI;CACvC,GACA,UACA;EACE,WAAW,KAAK,MAAM;EACtB;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,UAAU,UAAU;CACtB,CACF;AACF"}
@@ -1,10 +1,7 @@
1
- import { rectPxToIn } from "../units.js";
1
+ import { addGlimpsePicture, imageBytesFromSource } from "../utils/glimpsePicture.js";
2
2
  //#region src/renderPptx/nodes/svg.ts
3
3
  function renderSvgNode(node, ctx) {
4
- ctx.slide.addImage({
5
- data: node.iconImageData,
6
- ...rectPxToIn(node)
7
- });
4
+ addGlimpsePicture(ctx, node, imageBytesFromSource("", node.iconImageData));
8
5
  }
9
6
  //#endregion
10
7
  export { renderSvgNode };
@@ -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 { 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
+ {"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 {\n addGlimpsePicture,\n imageBytesFromSource,\n} from \"../utils/glimpsePicture.ts\";\n\ntype SvgPositionedNode = Extract<PositionedNode, { type: \"svg\" }>;\n\nexport function renderSvgNode(\n node: SvgPositionedNode,\n ctx: RenderContext,\n): void {\n addGlimpsePicture(ctx, node, imageBytesFromSource(\"\", node.iconImageData));\n}\n"],"mappings":";;AASA,SAAgB,cACd,MACA,KACM;CACN,kBAAkB,KAAK,MAAM,qBAAqB,IAAI,KAAK,aAAa,CAAC;AAC3E"}