@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.
- package/README.md +31 -584
- package/dist/calcYogaLayout/calcYogaLayout.d.ts.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js +61 -84
- package/dist/calcYogaLayout/fontLoader.d.ts +20 -0
- package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -0
- package/dist/calcYogaLayout/fontLoader.js +59 -0
- package/dist/calcYogaLayout/fonts/notoSansJPBold.d.ts +7 -0
- package/dist/calcYogaLayout/fonts/notoSansJPBold.d.ts.map +1 -0
- package/dist/calcYogaLayout/fonts/notoSansJPBold.js +6 -0
- package/dist/calcYogaLayout/fonts/notoSansJPRegular.d.ts +7 -0
- package/dist/calcYogaLayout/fonts/notoSansJPRegular.d.ts.map +1 -0
- package/dist/calcYogaLayout/fonts/notoSansJPRegular.js +6 -0
- package/dist/calcYogaLayout/measureText.d.ts +1 -1
- package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
- package/dist/calcYogaLayout/measureText.js +65 -114
- package/dist/inputSchema.d.ts +268 -2
- package/dist/inputSchema.d.ts.map +1 -1
- package/dist/inputSchema.js +53 -1
- package/dist/renderPptx/nodes/box.d.ts +1 -0
- package/dist/renderPptx/nodes/box.d.ts.map +1 -0
- package/dist/renderPptx/nodes/box.js +2 -0
- package/dist/renderPptx/nodes/chart.d.ts +8 -0
- package/dist/renderPptx/nodes/chart.d.ts.map +1 -0
- package/dist/renderPptx/nodes/chart.js +23 -0
- package/dist/renderPptx/nodes/flow.d.ts +8 -0
- package/dist/renderPptx/nodes/flow.d.ts.map +1 -0
- package/dist/renderPptx/nodes/flow.js +208 -0
- package/dist/renderPptx/nodes/image.d.ts +8 -0
- package/dist/renderPptx/nodes/image.d.ts.map +1 -0
- package/dist/renderPptx/nodes/image.js +17 -0
- package/dist/renderPptx/nodes/index.d.ts +11 -0
- package/dist/renderPptx/nodes/index.d.ts.map +1 -0
- package/dist/renderPptx/nodes/index.js +10 -0
- package/dist/renderPptx/nodes/matrix.d.ts +8 -0
- package/dist/renderPptx/nodes/matrix.d.ts.map +1 -0
- package/dist/renderPptx/nodes/matrix.js +150 -0
- package/dist/renderPptx/nodes/processArrow.d.ts +8 -0
- package/dist/renderPptx/nodes/processArrow.d.ts.map +1 -0
- package/dist/renderPptx/nodes/processArrow.js +75 -0
- package/dist/renderPptx/nodes/shape.d.ts +8 -0
- package/dist/renderPptx/nodes/shape.d.ts.map +1 -0
- package/dist/renderPptx/nodes/shape.js +49 -0
- package/dist/renderPptx/nodes/table.d.ts +8 -0
- package/dist/renderPptx/nodes/table.d.ts.map +1 -0
- package/dist/renderPptx/nodes/table.js +29 -0
- package/dist/renderPptx/nodes/text.d.ts +8 -0
- package/dist/renderPptx/nodes/text.d.ts.map +1 -0
- package/dist/renderPptx/nodes/text.js +5 -0
- package/dist/renderPptx/nodes/timeline.d.ts +8 -0
- package/dist/renderPptx/nodes/timeline.d.ts.map +1 -0
- package/dist/renderPptx/nodes/timeline.js +157 -0
- package/dist/renderPptx/nodes/tree.d.ts +8 -0
- package/dist/renderPptx/nodes/tree.d.ts.map +1 -0
- package/dist/renderPptx/nodes/tree.js +223 -0
- package/dist/renderPptx/renderPptx.d.ts.map +1 -1
- package/dist/renderPptx/renderPptx.js +32 -166
- package/dist/renderPptx/types.d.ts +10 -0
- package/dist/renderPptx/types.d.ts.map +1 -0
- package/dist/renderPptx/utils/backgroundBorder.d.ts +8 -0
- package/dist/renderPptx/utils/backgroundBorder.d.ts.map +1 -0
- package/dist/renderPptx/utils/backgroundBorder.js +44 -0
- package/dist/renderPptx/utils/index.d.ts +6 -0
- package/dist/renderPptx/utils/index.d.ts.map +1 -0
- package/dist/renderPptx/utils/index.js +3 -0
- package/dist/renderPptx/utils/shapeDrawing.d.ts +27 -0
- package/dist/renderPptx/utils/shapeDrawing.d.ts.map +1 -0
- package/dist/renderPptx/utils/shapeDrawing.js +36 -0
- package/dist/renderPptx/utils/textDrawing.d.ts +20 -0
- package/dist/renderPptx/utils/textDrawing.d.ts.map +1 -0
- package/dist/renderPptx/utils/textDrawing.js +20 -0
- package/dist/toPositioned/toPositioned.d.ts.map +1 -1
- package/dist/toPositioned/toPositioned.js +45 -0
- package/dist/types.d.ts +399 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +134 -0
- package/package.json +13 -3
- package/dist/parsePptx/convertChart.d.ts +0 -8
- package/dist/parsePptx/convertChart.d.ts.map +0 -1
- package/dist/parsePptx/convertChart.js +0 -78
- package/dist/parsePptx/convertImage.d.ts +0 -8
- package/dist/parsePptx/convertImage.d.ts.map +0 -1
- package/dist/parsePptx/convertImage.js +0 -13
- package/dist/parsePptx/convertShape.d.ts +0 -7
- package/dist/parsePptx/convertShape.d.ts.map +0 -1
- package/dist/parsePptx/convertShape.js +0 -137
- package/dist/parsePptx/convertTable.d.ts +0 -7
- package/dist/parsePptx/convertTable.d.ts.map +0 -1
- package/dist/parsePptx/convertTable.js +0 -46
- package/dist/parsePptx/convertText.d.ts +0 -7
- package/dist/parsePptx/convertText.d.ts.map +0 -1
- package/dist/parsePptx/convertText.js +0 -32
- package/dist/parsePptx/index.d.ts +0 -23
- package/dist/parsePptx/index.d.ts.map +0 -1
- package/dist/parsePptx/index.js +0 -114
- package/dist/parsePptx/parseHtml.d.ts +0 -22
- package/dist/parsePptx/parseHtml.d.ts.map +0 -1
- package/dist/parsePptx/parseHtml.js +0 -53
- package/dist/parsePptx/types.d.ts +0 -15
- package/dist/parsePptx/types.d.ts.map +0 -1
- package/dist/parsePptx/units.d.ts +0 -13
- package/dist/parsePptx/units.d.ts.map +0 -1
- package/dist/parsePptx/units.js +0 -19
- /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,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;
|
|
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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
slide.addText(node.text ?? "", textOptions);
|
|
29
|
+
case "text":
|
|
30
|
+
renderTextNode(node, ctx);
|
|
70
31
|
break;
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|