@hirokisakabe/pom 0.1.12 → 0.3.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 (103) hide show
  1. package/README.md +31 -584
  2. package/dist/calcYogaLayout/calcYogaLayout.d.ts.map +1 -1
  3. package/dist/calcYogaLayout/calcYogaLayout.js +61 -84
  4. package/dist/calcYogaLayout/fontLoader.d.ts +20 -0
  5. package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -0
  6. package/dist/calcYogaLayout/fontLoader.js +59 -0
  7. package/dist/calcYogaLayout/fonts/notoSansJPBold.d.ts +7 -0
  8. package/dist/calcYogaLayout/fonts/notoSansJPBold.d.ts.map +1 -0
  9. package/dist/calcYogaLayout/fonts/notoSansJPBold.js +6 -0
  10. package/dist/calcYogaLayout/fonts/notoSansJPRegular.d.ts +7 -0
  11. package/dist/calcYogaLayout/fonts/notoSansJPRegular.d.ts.map +1 -0
  12. package/dist/calcYogaLayout/fonts/notoSansJPRegular.js +6 -0
  13. package/dist/calcYogaLayout/measureText.d.ts +1 -1
  14. package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
  15. package/dist/calcYogaLayout/measureText.js +65 -114
  16. package/dist/inputSchema.d.ts +268 -2
  17. package/dist/inputSchema.d.ts.map +1 -1
  18. package/dist/inputSchema.js +53 -1
  19. package/dist/renderPptx/nodes/box.d.ts +1 -0
  20. package/dist/renderPptx/nodes/box.d.ts.map +1 -0
  21. package/dist/renderPptx/nodes/box.js +2 -0
  22. package/dist/renderPptx/nodes/chart.d.ts +8 -0
  23. package/dist/renderPptx/nodes/chart.d.ts.map +1 -0
  24. package/dist/renderPptx/nodes/chart.js +23 -0
  25. package/dist/renderPptx/nodes/flow.d.ts +8 -0
  26. package/dist/renderPptx/nodes/flow.d.ts.map +1 -0
  27. package/dist/renderPptx/nodes/flow.js +208 -0
  28. package/dist/renderPptx/nodes/image.d.ts +8 -0
  29. package/dist/renderPptx/nodes/image.d.ts.map +1 -0
  30. package/dist/renderPptx/nodes/image.js +17 -0
  31. package/dist/renderPptx/nodes/index.d.ts +11 -0
  32. package/dist/renderPptx/nodes/index.d.ts.map +1 -0
  33. package/dist/renderPptx/nodes/index.js +10 -0
  34. package/dist/renderPptx/nodes/matrix.d.ts +8 -0
  35. package/dist/renderPptx/nodes/matrix.d.ts.map +1 -0
  36. package/dist/renderPptx/nodes/matrix.js +150 -0
  37. package/dist/renderPptx/nodes/processArrow.d.ts +8 -0
  38. package/dist/renderPptx/nodes/processArrow.d.ts.map +1 -0
  39. package/dist/renderPptx/nodes/processArrow.js +75 -0
  40. package/dist/renderPptx/nodes/shape.d.ts +8 -0
  41. package/dist/renderPptx/nodes/shape.d.ts.map +1 -0
  42. package/dist/renderPptx/nodes/shape.js +49 -0
  43. package/dist/renderPptx/nodes/table.d.ts +8 -0
  44. package/dist/renderPptx/nodes/table.d.ts.map +1 -0
  45. package/dist/renderPptx/nodes/table.js +29 -0
  46. package/dist/renderPptx/nodes/text.d.ts +8 -0
  47. package/dist/renderPptx/nodes/text.d.ts.map +1 -0
  48. package/dist/renderPptx/nodes/text.js +5 -0
  49. package/dist/renderPptx/nodes/timeline.d.ts +8 -0
  50. package/dist/renderPptx/nodes/timeline.d.ts.map +1 -0
  51. package/dist/renderPptx/nodes/timeline.js +157 -0
  52. package/dist/renderPptx/nodes/tree.d.ts +8 -0
  53. package/dist/renderPptx/nodes/tree.d.ts.map +1 -0
  54. package/dist/renderPptx/nodes/tree.js +223 -0
  55. package/dist/renderPptx/renderPptx.d.ts.map +1 -1
  56. package/dist/renderPptx/renderPptx.js +32 -166
  57. package/dist/renderPptx/types.d.ts +10 -0
  58. package/dist/renderPptx/types.d.ts.map +1 -0
  59. package/dist/renderPptx/utils/backgroundBorder.d.ts +8 -0
  60. package/dist/renderPptx/utils/backgroundBorder.d.ts.map +1 -0
  61. package/dist/renderPptx/utils/backgroundBorder.js +44 -0
  62. package/dist/renderPptx/utils/index.d.ts +6 -0
  63. package/dist/renderPptx/utils/index.d.ts.map +1 -0
  64. package/dist/renderPptx/utils/index.js +3 -0
  65. package/dist/renderPptx/utils/shapeDrawing.d.ts +27 -0
  66. package/dist/renderPptx/utils/shapeDrawing.d.ts.map +1 -0
  67. package/dist/renderPptx/utils/shapeDrawing.js +36 -0
  68. package/dist/renderPptx/utils/textDrawing.d.ts +20 -0
  69. package/dist/renderPptx/utils/textDrawing.d.ts.map +1 -0
  70. package/dist/renderPptx/utils/textDrawing.js +20 -0
  71. package/dist/toPositioned/toPositioned.d.ts.map +1 -1
  72. package/dist/toPositioned/toPositioned.js +45 -0
  73. package/dist/types.d.ts +399 -2
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js +134 -0
  76. package/package.json +13 -3
  77. package/dist/parsePptx/convertChart.d.ts +0 -8
  78. package/dist/parsePptx/convertChart.d.ts.map +0 -1
  79. package/dist/parsePptx/convertChart.js +0 -78
  80. package/dist/parsePptx/convertImage.d.ts +0 -8
  81. package/dist/parsePptx/convertImage.d.ts.map +0 -1
  82. package/dist/parsePptx/convertImage.js +0 -13
  83. package/dist/parsePptx/convertShape.d.ts +0 -7
  84. package/dist/parsePptx/convertShape.d.ts.map +0 -1
  85. package/dist/parsePptx/convertShape.js +0 -137
  86. package/dist/parsePptx/convertTable.d.ts +0 -7
  87. package/dist/parsePptx/convertTable.d.ts.map +0 -1
  88. package/dist/parsePptx/convertTable.js +0 -46
  89. package/dist/parsePptx/convertText.d.ts +0 -7
  90. package/dist/parsePptx/convertText.d.ts.map +0 -1
  91. package/dist/parsePptx/convertText.js +0 -32
  92. package/dist/parsePptx/index.d.ts +0 -23
  93. package/dist/parsePptx/index.d.ts.map +0 -1
  94. package/dist/parsePptx/index.js +0 -114
  95. package/dist/parsePptx/parseHtml.d.ts +0 -22
  96. package/dist/parsePptx/parseHtml.d.ts.map +0 -1
  97. package/dist/parsePptx/parseHtml.js +0 -53
  98. package/dist/parsePptx/types.d.ts +0 -15
  99. package/dist/parsePptx/types.d.ts.map +0 -1
  100. package/dist/parsePptx/units.d.ts +0 -13
  101. package/dist/parsePptx/units.d.ts.map +0 -1
  102. package/dist/parsePptx/units.js +0 -19
  103. /package/dist/{parsePptx → renderPptx}/types.js +0 -0
@@ -0,0 +1,8 @@
1
+ import type { PositionedNode } from "../../types";
2
+ import type { RenderContext } from "../types";
3
+ type TextPositionedNode = Extract<PositionedNode, {
4
+ type: "text";
5
+ }>;
6
+ export declare function renderTextNode(node: TextPositionedNode, ctx: RenderContext): void;
7
+ export {};
8
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/renderPptx/nodes/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,KAAK,kBAAkB,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,kBAAkB,EACxB,GAAG,EAAE,aAAa,GACjB,IAAI,CAGN"}
@@ -0,0 +1,5 @@
1
+ import { createTextOptions } from "../textOptions";
2
+ export function renderTextNode(node, ctx) {
3
+ const textOptions = createTextOptions(node);
4
+ ctx.slide.addText(node.text ?? "", textOptions);
5
+ }
@@ -0,0 +1,8 @@
1
+ import type { PositionedNode } from "../../types";
2
+ import type { RenderContext } from "../types";
3
+ type TimelinePositionedNode = Extract<PositionedNode, {
4
+ type: "timeline";
5
+ }>;
6
+ export declare function renderTimelineNode(node: TimelinePositionedNode, ctx: RenderContext): void;
7
+ export {};
8
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/renderPptx/nodes/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,KAAK,sBAAsB,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAE5E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,sBAAsB,EAC5B,GAAG,EAAE,aAAa,GACjB,IAAI,CA8BN"}
@@ -0,0 +1,157 @@
1
+ import { pxToIn, pxToPt } from "../units";
2
+ export function renderTimelineNode(node, ctx) {
3
+ const direction = node.direction ?? "horizontal";
4
+ const items = node.items;
5
+ const itemCount = items.length;
6
+ if (itemCount === 0)
7
+ return;
8
+ const defaultColor = "1D4ED8"; // blue
9
+ const nodeRadius = 12; // px
10
+ const lineWidth = 4; // px
11
+ if (direction === "horizontal") {
12
+ renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth);
13
+ }
14
+ else {
15
+ renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth);
16
+ }
17
+ }
18
+ function renderHorizontalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth) {
19
+ const itemCount = items.length;
20
+ const lineY = node.y + node.h / 2;
21
+ const startX = node.x + nodeRadius;
22
+ const endX = node.x + node.w - nodeRadius;
23
+ const lineLength = endX - startX;
24
+ // メインの線を描画
25
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
26
+ x: pxToIn(startX),
27
+ y: pxToIn(lineY),
28
+ w: pxToIn(lineLength),
29
+ h: 0,
30
+ line: { color: "E2E8F0", width: pxToPt(lineWidth) },
31
+ });
32
+ // 各アイテムを描画
33
+ items.forEach((item, index) => {
34
+ const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);
35
+ const cx = startX + lineLength * progress;
36
+ const cy = lineY;
37
+ const color = item.color ?? defaultColor;
38
+ // ノード(円)を描画
39
+ ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
40
+ x: pxToIn(cx - nodeRadius),
41
+ y: pxToIn(cy - nodeRadius),
42
+ w: pxToIn(nodeRadius * 2),
43
+ h: pxToIn(nodeRadius * 2),
44
+ fill: { color },
45
+ line: { type: "none" },
46
+ });
47
+ // 日付を上に表示
48
+ ctx.slide.addText(item.date, {
49
+ x: pxToIn(cx - 60),
50
+ y: pxToIn(cy - nodeRadius - 40),
51
+ w: pxToIn(120),
52
+ h: pxToIn(24),
53
+ fontSize: pxToPt(12),
54
+ fontFace: "Noto Sans JP",
55
+ color: "64748B",
56
+ align: "center",
57
+ valign: "bottom",
58
+ });
59
+ // タイトルを下に表示
60
+ ctx.slide.addText(item.title, {
61
+ x: pxToIn(cx - 60),
62
+ y: pxToIn(cy + nodeRadius + 8),
63
+ w: pxToIn(120),
64
+ h: pxToIn(24),
65
+ fontSize: pxToPt(14),
66
+ fontFace: "Noto Sans JP",
67
+ color: "1E293B",
68
+ bold: true,
69
+ align: "center",
70
+ valign: "top",
71
+ });
72
+ // 説明を表示
73
+ if (item.description) {
74
+ ctx.slide.addText(item.description, {
75
+ x: pxToIn(cx - 60),
76
+ y: pxToIn(cy + nodeRadius + 32),
77
+ w: pxToIn(120),
78
+ h: pxToIn(32),
79
+ fontSize: pxToPt(11),
80
+ fontFace: "Noto Sans JP",
81
+ color: "64748B",
82
+ align: "center",
83
+ valign: "top",
84
+ });
85
+ }
86
+ });
87
+ }
88
+ function renderVerticalTimeline(node, ctx, items, defaultColor, nodeRadius, lineWidth) {
89
+ const itemCount = items.length;
90
+ const lineX = node.x + 40;
91
+ const startY = node.y + nodeRadius;
92
+ const endY = node.y + node.h - nodeRadius;
93
+ const lineLength = endY - startY;
94
+ // メインの線を描画
95
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
96
+ x: pxToIn(lineX),
97
+ y: pxToIn(startY),
98
+ w: 0,
99
+ h: pxToIn(lineLength),
100
+ line: { color: "E2E8F0", width: pxToPt(lineWidth) },
101
+ });
102
+ // 各アイテムを描画
103
+ items.forEach((item, index) => {
104
+ const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);
105
+ const cx = lineX;
106
+ const cy = startY + lineLength * progress;
107
+ const color = item.color ?? defaultColor;
108
+ // ノード(円)を描画
109
+ ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
110
+ x: pxToIn(cx - nodeRadius),
111
+ y: pxToIn(cy - nodeRadius),
112
+ w: pxToIn(nodeRadius * 2),
113
+ h: pxToIn(nodeRadius * 2),
114
+ fill: { color },
115
+ line: { type: "none" },
116
+ });
117
+ // 日付を左上に表示
118
+ ctx.slide.addText(item.date, {
119
+ x: pxToIn(cx + nodeRadius + 16),
120
+ y: pxToIn(cy - nodeRadius - 4),
121
+ w: pxToIn(100),
122
+ h: pxToIn(20),
123
+ fontSize: pxToPt(12),
124
+ fontFace: "Noto Sans JP",
125
+ color: "64748B",
126
+ align: "left",
127
+ valign: "bottom",
128
+ });
129
+ // タイトルを右に表示
130
+ ctx.slide.addText(item.title, {
131
+ x: pxToIn(cx + nodeRadius + 16),
132
+ y: pxToIn(cy - 4),
133
+ w: pxToIn(node.w - 80),
134
+ h: pxToIn(24),
135
+ fontSize: pxToPt(14),
136
+ fontFace: "Noto Sans JP",
137
+ color: "1E293B",
138
+ bold: true,
139
+ align: "left",
140
+ valign: "top",
141
+ });
142
+ // 説明を表示
143
+ if (item.description) {
144
+ ctx.slide.addText(item.description, {
145
+ x: pxToIn(cx + nodeRadius + 16),
146
+ y: pxToIn(cy + 20),
147
+ w: pxToIn(node.w - 80),
148
+ h: pxToIn(32),
149
+ fontSize: pxToPt(11),
150
+ fontFace: "Noto Sans JP",
151
+ color: "64748B",
152
+ align: "left",
153
+ valign: "top",
154
+ });
155
+ }
156
+ });
157
+ }
@@ -0,0 +1,8 @@
1
+ import type { PositionedNode } from "../../types";
2
+ import type { RenderContext } from "../types";
3
+ type TreePositionedNode = Extract<PositionedNode, {
4
+ type: "tree";
5
+ }>;
6
+ export declare function renderTreeNode(node: TreePositionedNode, ctx: RenderContext): void;
7
+ export {};
8
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../../src/renderPptx/nodes/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,KAAK,kBAAkB,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAWpE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,kBAAkB,EACxB,GAAG,EAAE,aAAa,GACjB,IAAI,CAwQN"}
@@ -0,0 +1,223 @@
1
+ import { pxToIn, pxToPt } from "../units";
2
+ export function renderTreeNode(node, ctx) {
3
+ const layout = node.layout ?? "vertical";
4
+ const nodeShape = node.nodeShape ?? "rect";
5
+ const nodeWidth = node.nodeWidth ?? 120;
6
+ const nodeHeight = node.nodeHeight ?? 40;
7
+ const levelGap = node.levelGap ?? 60;
8
+ const siblingGap = node.siblingGap ?? 20;
9
+ const connectorStyle = node.connectorStyle ?? {};
10
+ const defaultColor = "1D4ED8";
11
+ // サブツリーの幅/高さを計算
12
+ function calculateSubtreeSize(item) {
13
+ if (!item.children || item.children.length === 0) {
14
+ return { width: nodeWidth, height: nodeHeight };
15
+ }
16
+ const childSizes = item.children.map(calculateSubtreeSize);
17
+ if (layout === "vertical") {
18
+ const childrenWidth = childSizes.reduce((sum, s) => sum + s.width, 0) +
19
+ siblingGap * (childSizes.length - 1);
20
+ const childrenHeight = Math.max(...childSizes.map((s) => s.height));
21
+ return {
22
+ width: Math.max(nodeWidth, childrenWidth),
23
+ height: nodeHeight + levelGap + childrenHeight,
24
+ };
25
+ }
26
+ else {
27
+ const childrenHeight = childSizes.reduce((sum, s) => sum + s.height, 0) +
28
+ siblingGap * (childSizes.length - 1);
29
+ const childrenWidth = Math.max(...childSizes.map((s) => s.width));
30
+ return {
31
+ width: nodeWidth + levelGap + childrenWidth,
32
+ height: Math.max(nodeHeight, childrenHeight),
33
+ };
34
+ }
35
+ }
36
+ // ツリーレイアウトを計算
37
+ function calculateTreeLayout(item, x, y) {
38
+ const subtreeSize = calculateSubtreeSize(item);
39
+ const layoutNode = {
40
+ item,
41
+ x: 0,
42
+ y: 0,
43
+ width: nodeWidth,
44
+ height: nodeHeight,
45
+ children: [],
46
+ };
47
+ if (layout === "vertical") {
48
+ // ノードを中央上部に配置
49
+ layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;
50
+ layoutNode.y = y;
51
+ // 子ノードを配置
52
+ if (item.children && item.children.length > 0) {
53
+ const childSizes = item.children.map(calculateSubtreeSize);
54
+ const totalChildWidth = childSizes.reduce((sum, s) => sum + s.width, 0) +
55
+ siblingGap * (childSizes.length - 1);
56
+ let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;
57
+ const childY = y + nodeHeight + levelGap;
58
+ for (let i = 0; i < item.children.length; i++) {
59
+ const child = item.children[i];
60
+ const childLayout = calculateTreeLayout(child, childX, childY);
61
+ layoutNode.children.push(childLayout);
62
+ childX += childSizes[i].width + siblingGap;
63
+ }
64
+ }
65
+ }
66
+ else {
67
+ // horizontal: ノードを左中央に配置
68
+ layoutNode.x = x;
69
+ layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;
70
+ // 子ノードを配置
71
+ if (item.children && item.children.length > 0) {
72
+ const childSizes = item.children.map(calculateSubtreeSize);
73
+ const totalChildHeight = childSizes.reduce((sum, s) => sum + s.height, 0) +
74
+ siblingGap * (childSizes.length - 1);
75
+ const childX = x + nodeWidth + levelGap;
76
+ let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;
77
+ for (let i = 0; i < item.children.length; i++) {
78
+ const child = item.children[i];
79
+ const childLayout = calculateTreeLayout(child, childX, childY);
80
+ layoutNode.children.push(childLayout);
81
+ childY += childSizes[i].height + siblingGap;
82
+ }
83
+ }
84
+ }
85
+ return layoutNode;
86
+ }
87
+ // 接続線を描画
88
+ function drawConnector(parent, child, style) {
89
+ const lineColor = style.color ?? "333333";
90
+ const lineWidth = style.width ?? 2;
91
+ if (layout === "vertical") {
92
+ // 親の下端中央から子の上端中央へ
93
+ const parentCenterX = parent.x + parent.width / 2;
94
+ const parentBottomY = parent.y + parent.height;
95
+ const childCenterX = child.x + child.width / 2;
96
+ const childTopY = child.y;
97
+ const midY = (parentBottomY + childTopY) / 2;
98
+ // 垂直線(親から中間点まで)
99
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
100
+ x: pxToIn(parentCenterX),
101
+ y: pxToIn(parentBottomY),
102
+ w: 0,
103
+ h: pxToIn(midY - parentBottomY),
104
+ line: { color: lineColor, width: pxToPt(lineWidth) },
105
+ });
106
+ // 水平線(中間点で)
107
+ const minX = Math.min(parentCenterX, childCenterX);
108
+ const maxX = Math.max(parentCenterX, childCenterX);
109
+ if (maxX > minX) {
110
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
111
+ x: pxToIn(minX),
112
+ y: pxToIn(midY),
113
+ w: pxToIn(maxX - minX),
114
+ h: 0,
115
+ line: { color: lineColor, width: pxToPt(lineWidth) },
116
+ });
117
+ }
118
+ // 垂直線(中間点から子まで)
119
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
120
+ x: pxToIn(childCenterX),
121
+ y: pxToIn(midY),
122
+ w: 0,
123
+ h: pxToIn(childTopY - midY),
124
+ line: { color: lineColor, width: pxToPt(lineWidth) },
125
+ });
126
+ }
127
+ else {
128
+ // 親の右端中央から子の左端中央へ
129
+ const parentRightX = parent.x + parent.width;
130
+ const parentCenterY = parent.y + parent.height / 2;
131
+ const childLeftX = child.x;
132
+ const childCenterY = child.y + child.height / 2;
133
+ const midX = (parentRightX + childLeftX) / 2;
134
+ // 水平線(親から中間点まで)
135
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
136
+ x: pxToIn(parentRightX),
137
+ y: pxToIn(parentCenterY),
138
+ w: pxToIn(midX - parentRightX),
139
+ h: 0,
140
+ line: { color: lineColor, width: pxToPt(lineWidth) },
141
+ });
142
+ // 垂直線(中間点で)
143
+ const minY = Math.min(parentCenterY, childCenterY);
144
+ const maxY = Math.max(parentCenterY, childCenterY);
145
+ if (maxY > minY) {
146
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
147
+ x: pxToIn(midX),
148
+ y: pxToIn(minY),
149
+ w: 0,
150
+ h: pxToIn(maxY - minY),
151
+ line: { color: lineColor, width: pxToPt(lineWidth) },
152
+ });
153
+ }
154
+ // 水平線(中間点から子まで)
155
+ ctx.slide.addShape(ctx.pptx.ShapeType.line, {
156
+ x: pxToIn(midX),
157
+ y: pxToIn(childCenterY),
158
+ w: pxToIn(childLeftX - midX),
159
+ h: 0,
160
+ line: { color: lineColor, width: pxToPt(lineWidth) },
161
+ });
162
+ }
163
+ }
164
+ // ノードを描画
165
+ function drawTreeNode(layoutNode, shape, defaultNodeColor) {
166
+ const color = layoutNode.item.color ?? defaultNodeColor;
167
+ const shapeType = (() => {
168
+ switch (shape) {
169
+ case "rect":
170
+ return ctx.pptx.ShapeType.rect;
171
+ case "roundRect":
172
+ return ctx.pptx.ShapeType.roundRect;
173
+ case "ellipse":
174
+ return ctx.pptx.ShapeType.ellipse;
175
+ }
176
+ })();
177
+ // ノードの背景
178
+ ctx.slide.addShape(shapeType, {
179
+ x: pxToIn(layoutNode.x),
180
+ y: pxToIn(layoutNode.y),
181
+ w: pxToIn(layoutNode.width),
182
+ h: pxToIn(layoutNode.height),
183
+ fill: { color },
184
+ line: { color: "333333", width: pxToPt(1) },
185
+ });
186
+ // ノードのラベル
187
+ ctx.slide.addText(layoutNode.item.label, {
188
+ x: pxToIn(layoutNode.x),
189
+ y: pxToIn(layoutNode.y),
190
+ w: pxToIn(layoutNode.width),
191
+ h: pxToIn(layoutNode.height),
192
+ fontSize: pxToPt(12),
193
+ fontFace: "Noto Sans JP",
194
+ color: "FFFFFF",
195
+ align: "center",
196
+ valign: "middle",
197
+ });
198
+ }
199
+ // すべての接続線を再帰的に描画
200
+ function drawAllConnectors(layoutNode) {
201
+ for (const child of layoutNode.children) {
202
+ drawConnector(layoutNode, child, connectorStyle);
203
+ drawAllConnectors(child);
204
+ }
205
+ }
206
+ // すべてのノードを再帰的に描画
207
+ function drawAllNodes(layoutNode) {
208
+ drawTreeNode(layoutNode, nodeShape, defaultColor);
209
+ for (const child of layoutNode.children) {
210
+ drawAllNodes(child);
211
+ }
212
+ }
213
+ // ツリーのサイズを計算
214
+ const treeSize = calculateSubtreeSize(node.data);
215
+ // 描画領域内の中央に配置
216
+ const offsetX = node.x + (node.w - treeSize.width) / 2;
217
+ const offsetY = node.y + (node.h - treeSize.height) / 2;
218
+ // レイアウト計算
219
+ const rootLayout = calculateTreeLayout(node.data, offsetX, offsetY);
220
+ // 描画(接続線を先に、ノードを後に描画)
221
+ drawAllConnectors(rootLayout);
222
+ drawAllNodes(rootLayout);
223
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"renderPptx.d.ts","sourceRoot":"","sources":["../../src/renderPptx/renderPptx.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,KAAK,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,OAAO,iBAqOnE"}
1
+ {"version":3,"file":"renderPptx.d.ts","sourceRoot":"","sources":["../../src/renderPptx/renderPptx.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAiB/C,KAAK,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,OAAO,iBA8EnE"}
@@ -1,9 +1,9 @@
1
1
  import { createRequire } from "module";
2
2
  const require = createRequire(import.meta.url);
3
3
  const PptxGenJS = require("pptxgenjs");
4
- import { resolveColumnWidths, resolveRowHeights } from "../table/utils";
5
- import { createTextOptions } from "./textOptions";
6
- import { pxToIn, pxToPt } from "./units";
4
+ import { pxToIn } from "./units";
5
+ import { renderBackgroundAndBorder } from "./utils/backgroundBorder";
6
+ import { renderTextNode, renderImageNode, renderTableNode, renderShapeNode, renderChartNode, renderTimelineNode, renderMatrixNode, renderTreeNode, renderFlowNode, renderProcessArrowNode, } from "./nodes";
7
7
  export { createTextOptions } from "./textOptions";
8
8
  export { PX_PER_IN, pxToIn, pxToPt } from "./units";
9
9
  /**
@@ -19,188 +19,54 @@ export function renderPptx(pages, slidePx) {
19
19
  pptx.layout = "custom";
20
20
  for (const data of pages) {
21
21
  const slide = pptx.addSlide();
22
- function renderBackgroundAndBorder(node) {
23
- const { backgroundColor, border, borderRadius } = node;
24
- const hasBackground = Boolean(backgroundColor);
25
- const hasBorder = Boolean(border &&
26
- (border.color !== undefined ||
27
- border.width !== undefined ||
28
- border.dashType !== undefined));
29
- if (!hasBackground && !hasBorder) {
30
- return;
31
- }
32
- const fill = hasBackground
33
- ? { color: backgroundColor }
34
- : { type: "none" };
35
- const line = hasBorder
36
- ? {
37
- color: border?.color ?? "000000",
38
- width: border?.width !== undefined ? pxToPt(border.width) : undefined,
39
- dashType: border?.dashType,
40
- }
41
- : { type: "none" };
42
- // borderRadius がある場合は roundRect を使用し、rectRadius を計算
43
- const shapeType = borderRadius
44
- ? pptx.ShapeType.roundRect
45
- : pptx.ShapeType.rect;
46
- // px を 0-1 の正規化値に変換
47
- const rectRadius = borderRadius
48
- ? Math.min((borderRadius / Math.min(node.w, node.h)) * 2, 1)
49
- : undefined;
50
- const shapeOptions = {
51
- x: pxToIn(node.x),
52
- y: pxToIn(node.y),
53
- w: pxToIn(node.w),
54
- h: pxToIn(node.h),
55
- fill,
56
- line,
57
- rectRadius,
58
- };
59
- slide.addShape(shapeType, shapeOptions);
60
- }
22
+ const ctx = { slide, pptx };
61
23
  /**
62
24
  * node をスライドにレンダリングする
63
25
  */
64
26
  function renderNode(node) {
65
- renderBackgroundAndBorder(node);
27
+ renderBackgroundAndBorder(node, ctx);
66
28
  switch (node.type) {
67
- case "text": {
68
- const textOptions = createTextOptions(node);
69
- slide.addText(node.text ?? "", textOptions);
29
+ case "text":
30
+ renderTextNode(node, ctx);
70
31
  break;
71
- }
72
- case "image": {
73
- const imageOptions = {
74
- x: pxToIn(node.x),
75
- y: pxToIn(node.y),
76
- w: pxToIn(node.w),
77
- h: pxToIn(node.h),
78
- };
79
- if (node.imageData) {
80
- // Base64 データがある場合は data プロパティを使用(リモート画像)
81
- slide.addImage({ ...imageOptions, data: node.imageData });
82
- }
83
- else {
84
- // ローカルパスの場合は path プロパティを使用
85
- slide.addImage({ ...imageOptions, path: node.src });
86
- }
32
+ case "image":
33
+ renderImageNode(node, ctx);
87
34
  break;
88
- }
89
- case "box": {
35
+ case "box":
90
36
  // 子要素を再帰的に処理
91
37
  renderNode(node.children);
92
38
  break;
93
- }
94
39
  case "vstack":
95
- case "hstack": {
40
+ case "hstack":
96
41
  // 子要素を再帰的に処理
97
42
  for (const child of node.children) {
98
43
  renderNode(child);
99
44
  }
100
45
  break;
101
- }
102
- case "table": {
103
- const tableRows = node.rows.map((row) => row.cells.map((cell) => {
104
- const cellOptions = {
105
- fontSize: pxToPt(cell.fontPx ?? 18),
106
- color: cell.color,
107
- bold: cell.bold,
108
- align: cell.alignText ?? "left",
109
- fill: cell.backgroundColor
110
- ? { color: cell.backgroundColor }
111
- : undefined,
112
- };
113
- return {
114
- text: cell.text,
115
- options: cellOptions,
116
- };
117
- }));
118
- const tableOptions = {
119
- x: pxToIn(node.x),
120
- y: pxToIn(node.y),
121
- w: pxToIn(node.w),
122
- h: pxToIn(node.h),
123
- colW: resolveColumnWidths(node, node.w).map((width) => pxToIn(width)),
124
- rowH: resolveRowHeights(node).map((height) => pxToIn(height)),
125
- margin: 0,
126
- };
127
- slide.addTable(tableRows, tableOptions);
46
+ case "table":
47
+ renderTableNode(node, ctx);
128
48
  break;
129
- }
130
- case "shape": {
131
- const shapeOptions = {
132
- x: pxToIn(node.x),
133
- y: pxToIn(node.y),
134
- w: pxToIn(node.w),
135
- h: pxToIn(node.h),
136
- fill: node.fill
137
- ? {
138
- color: node.fill.color,
139
- transparency: node.fill.transparency,
140
- }
141
- : undefined,
142
- line: node.line
143
- ? {
144
- color: node.line.color,
145
- width: node.line.width !== undefined
146
- ? pxToPt(node.line.width)
147
- : undefined,
148
- dashType: node.line.dashType,
149
- }
150
- : undefined,
151
- shadow: node.shadow
152
- ? {
153
- type: node.shadow.type,
154
- opacity: node.shadow.opacity,
155
- blur: node.shadow.blur,
156
- angle: node.shadow.angle,
157
- offset: node.shadow.offset,
158
- color: node.shadow.color,
159
- }
160
- : undefined,
161
- };
162
- if (node.text) {
163
- // テキストがある場合:addTextでshapeを指定
164
- slide.addText(node.text, {
165
- ...shapeOptions,
166
- shape: node.shapeType,
167
- fontSize: pxToPt(node.fontPx ?? 24),
168
- fontFace: "Noto Sans JP",
169
- color: node.color,
170
- align: node.alignText ?? "center",
171
- valign: "middle",
172
- lineSpacingMultiple: 1.3,
173
- });
174
- }
175
- else {
176
- // テキストがない場合:addShapeを使用
177
- slide.addShape(node.shapeType, shapeOptions);
178
- }
49
+ case "shape":
50
+ renderShapeNode(node, ctx);
179
51
  break;
180
- }
181
- case "chart": {
182
- const chartData = node.data.map((d) => ({
183
- name: d.name,
184
- labels: d.labels,
185
- values: d.values,
186
- }));
187
- const chartOptions = {
188
- x: pxToIn(node.x),
189
- y: pxToIn(node.y),
190
- w: pxToIn(node.w),
191
- h: pxToIn(node.h),
192
- showLegend: node.showLegend ?? false,
193
- showTitle: node.showTitle ?? false,
194
- title: node.title,
195
- chartColors: node.chartColors,
196
- };
197
- // radar専用オプション
198
- if (node.chartType === "radar" && node.radarStyle) {
199
- chartOptions.radarStyle = node.radarStyle;
200
- }
201
- slide.addChart(node.chartType, chartData, chartOptions);
52
+ case "chart":
53
+ renderChartNode(node, ctx);
54
+ break;
55
+ case "timeline":
56
+ renderTimelineNode(node, ctx);
57
+ break;
58
+ case "matrix":
59
+ renderMatrixNode(node, ctx);
60
+ break;
61
+ case "tree":
62
+ renderTreeNode(node, ctx);
63
+ break;
64
+ case "flow":
65
+ renderFlowNode(node, ctx);
66
+ break;
67
+ case "processArrow":
68
+ renderProcessArrowNode(node, ctx);
202
69
  break;
203
- }
204
70
  }
205
71
  }
206
72
  renderNode(data);
@@ -0,0 +1,10 @@
1
+ import type PptxGenJSType from "pptxgenjs";
2
+ import type { PositionedNode } from "../types";
3
+ export type SlideInstance = ReturnType<InstanceType<typeof PptxGenJSType>["addSlide"]>;
4
+ export type PptxInstance = InstanceType<typeof PptxGenJSType>;
5
+ export type RenderContext = {
6
+ slide: SlideInstance;
7
+ pptx: PptxInstance;
8
+ };
9
+ export type NodeRenderer<T extends PositionedNode> = (node: T, ctx: RenderContext) => void;
10
+ //# sourceMappingURL=types.d.ts.map