@hirokisakabe/pom 0.3.0 → 1.1.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.
@@ -1,11 +1,11 @@
1
1
  import { TextMeasurementMode } from "./calcYogaLayout/measureText";
2
- import { POMNode, MasterSlideOptions } from "./types";
2
+ import { POMNode, SlideMasterOptions } from "./types";
3
3
  export type { TextMeasurementMode };
4
4
  export declare function buildPptx(nodes: POMNode[], slideSize: {
5
5
  w: number;
6
6
  h: number;
7
7
  }, options?: {
8
- master?: MasterSlideOptions;
8
+ master?: SlideMasterOptions;
9
9
  textMeasurement?: TextMeasurementMode;
10
10
  }): Promise<import("pptxgenjs").default>;
11
11
  //# sourceMappingURL=buildPptx.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,EAAkB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEtE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AA8FpC,wBAAsB,SAAS,CAC7B,KAAK,EAAE,OAAO,EAAE,EAChB,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,wCAsBF"}
1
+ {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,EAAkB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEtE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,KAAK,EAAE,OAAO,EAAE,EAChB,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,wCAoBF"}
package/dist/buildPptx.js CHANGED
@@ -2,65 +2,6 @@ import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout";
2
2
  import { setTextMeasurementMode, } from "./calcYogaLayout/measureText";
3
3
  import { renderPptx } from "./renderPptx/renderPptx";
4
4
  import { toPositioned } from "./toPositioned/toPositioned";
5
- function replacePlaceholders(node, pageNumber, totalPages, date) {
6
- if (node.type === "text") {
7
- return {
8
- ...node,
9
- text: node.text
10
- .replace(/\{\{page\}\}/g, String(pageNumber))
11
- .replace(/\{\{totalPages\}\}/g, String(totalPages))
12
- .replace(/\{\{date\}\}/g, date),
13
- };
14
- }
15
- if (node.type === "box") {
16
- return {
17
- ...node,
18
- children: replacePlaceholders(node.children, pageNumber, totalPages, date),
19
- };
20
- }
21
- if (node.type === "vstack" || node.type === "hstack") {
22
- return {
23
- ...node,
24
- children: node.children.map((child) => replacePlaceholders(child, pageNumber, totalPages, date)),
25
- };
26
- }
27
- return node;
28
- }
29
- function composePage(content, master, pageNumber, totalPages) {
30
- if (!master) {
31
- return content;
32
- }
33
- const date = master.date?.value ?? "";
34
- const children = [];
35
- // ヘッダーを追加
36
- if (master.header) {
37
- children.push(replacePlaceholders(master.header, pageNumber, totalPages, date));
38
- }
39
- // コンテンツを追加
40
- children.push(content);
41
- // フッターを追加
42
- if (master.footer) {
43
- children.push(replacePlaceholders(master.footer, pageNumber, totalPages, date));
44
- }
45
- // ページ番号を追加
46
- if (master.pageNumber) {
47
- const pageNumberNode = {
48
- type: "text",
49
- text: String(pageNumber),
50
- fontPx: 12,
51
- alignText: master.pageNumber.position,
52
- };
53
- children.push(pageNumberNode);
54
- }
55
- return {
56
- type: "vstack",
57
- w: "100%",
58
- h: "100%",
59
- alignItems: "stretch",
60
- justifyContent: "start",
61
- children,
62
- };
63
- }
64
5
  export async function buildPptx(nodes, slideSize, options) {
65
6
  // テキスト計測モードを設定(デフォルトは auto)
66
7
  if (options?.textMeasurement) {
@@ -70,14 +11,11 @@ export async function buildPptx(nodes, slideSize, options) {
70
11
  setTextMeasurementMode("auto");
71
12
  }
72
13
  const positionedPages = [];
73
- const totalPages = nodes.length;
74
- for (let i = 0; i < nodes.length; i++) {
75
- const node = nodes[i];
76
- const composedNode = composePage(node, options?.master, i + 1, totalPages);
77
- await calcYogaLayout(composedNode, slideSize);
78
- const positioned = toPositioned(composedNode);
14
+ for (const node of nodes) {
15
+ await calcYogaLayout(node, slideSize);
16
+ const positioned = toPositioned(node);
79
17
  positionedPages.push(positioned);
80
18
  }
81
- const pptx = renderPptx(positionedPages, slideSize);
19
+ const pptx = renderPptx(positionedPages, slideSize, options?.master);
82
20
  return pptx;
83
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,UAAU,CAAC;AAOhE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,iBAiBpC"}
1
+ {"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,UAAU,CAAC;AAchE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,iBAiBpC"}
@@ -2,6 +2,7 @@ import { loadYoga } from "yoga-layout/load";
2
2
  import { measureText } from "./measureText";
3
3
  import { measureImage, prefetchImageSize } from "./measureImage";
4
4
  import { calcTableIntrinsicSize } from "../table/utils";
5
+ import { measureProcessArrow, measureTimeline, measureMatrix, measureTree, measureFlow, } from "./measureCompositeNodes";
5
6
  /**
6
7
  * POMNode ツリーを Yoga でレイアウト計算する
7
8
  * POMNode ツリーの各ノードに yogaNode プロパティがセットされる
@@ -45,6 +46,11 @@ function collectImageSources(node) {
45
46
  traverse(child);
46
47
  }
47
48
  }
49
+ else if (n.type === "layer") {
50
+ for (const child of n.children) {
51
+ traverse(child);
52
+ }
53
+ }
48
54
  }
49
55
  traverse(node);
50
56
  return sources;
@@ -84,6 +90,14 @@ async function buildPomWithYogaTree(node, parentYoga, parentNode) {
84
90
  }
85
91
  break;
86
92
  }
93
+ case "layer": {
94
+ // layer の子要素は絶対配置なので、各子要素のサイズ計算のみ行う
95
+ // x, y は toPositioned で適用される
96
+ for (const child of node.children) {
97
+ await buildPomWithYogaTree(child, yn, node);
98
+ }
99
+ break;
100
+ }
87
101
  case "text":
88
102
  case "image":
89
103
  case "table":
@@ -93,6 +107,7 @@ async function buildPomWithYogaTree(node, parentYoga, parentNode) {
93
107
  case "tree":
94
108
  case "flow":
95
109
  case "processArrow":
110
+ case "line":
96
111
  // 子要素なし
97
112
  break;
98
113
  }
@@ -268,12 +283,54 @@ async function applyStyleToYogaNode(node, yn) {
268
283
  // テキストがない場合は、明示的にサイズが指定されていることを期待
269
284
  }
270
285
  break;
286
+ case "processArrow":
287
+ {
288
+ yn.setMeasureFunc(() => {
289
+ const { width, height } = measureProcessArrow(node);
290
+ return { width, height };
291
+ });
292
+ }
293
+ break;
271
294
  case "timeline":
295
+ {
296
+ yn.setMeasureFunc(() => {
297
+ const { width, height } = measureTimeline(node);
298
+ return { width, height };
299
+ });
300
+ }
301
+ break;
272
302
  case "matrix":
303
+ {
304
+ yn.setMeasureFunc(() => {
305
+ const { width, height } = measureMatrix(node);
306
+ return { width, height };
307
+ });
308
+ }
309
+ break;
273
310
  case "tree":
311
+ {
312
+ yn.setMeasureFunc(() => {
313
+ const { width, height } = measureTree(node);
314
+ return { width, height };
315
+ });
316
+ }
317
+ break;
274
318
  case "flow":
275
- case "processArrow":
276
- // 明示的にサイズが指定されていることを期待
319
+ {
320
+ yn.setMeasureFunc(() => {
321
+ const { width, height } = measureFlow(node);
322
+ return { width, height };
323
+ });
324
+ }
325
+ break;
326
+ case "line":
327
+ // line ノードは絶対座標を使用するため、Yoga レイアウトではサイズ 0 として扱う
328
+ yn.setWidth(0);
329
+ yn.setHeight(0);
330
+ break;
331
+ case "layer":
332
+ // layer は子を絶対配置するコンテナ
333
+ // サイズは明示的に指定されることを期待
277
334
  break;
278
335
  }
279
336
  }
@@ -0,0 +1,49 @@
1
+ import type { ProcessArrowNode, TimelineNode, MatrixNode, TreeNode, FlowNode } from "../types";
2
+ /**
3
+ * ProcessArrow ノードの intrinsic size を計算する
4
+ */
5
+ export declare function measureProcessArrow(node: ProcessArrowNode): {
6
+ width: number;
7
+ height: number;
8
+ };
9
+ /**
10
+ * Timeline ノードの intrinsic size を計算する
11
+ *
12
+ * Timeline の描画には以下の固定値が使用される:
13
+ * - nodeRadius: 12px (円のサイズ)
14
+ * - 日付ラベル領域: 上部 40px
15
+ * - タイトルラベル領域: 下部 24px
16
+ * - 説明ラベル領域: 下部 32px
17
+ */
18
+ export declare function measureTimeline(node: TimelineNode): {
19
+ width: number;
20
+ height: number;
21
+ };
22
+ /**
23
+ * Matrix ノードの intrinsic size を計算する
24
+ *
25
+ * Matrix の描画には以下の固定値が使用される:
26
+ * - padding: 60px (軸ラベル用の余白)
27
+ * - itemSize: 24px (アイテムの円のサイズ)
28
+ */
29
+ export declare function measureMatrix(_: MatrixNode): {
30
+ width: number;
31
+ height: number;
32
+ };
33
+ /**
34
+ * Tree ノードの intrinsic size を計算する
35
+ *
36
+ * ツリー構造を再帰的に走査し、必要なサイズを計算する
37
+ */
38
+ export declare function measureTree(node: TreeNode): {
39
+ width: number;
40
+ height: number;
41
+ };
42
+ /**
43
+ * Flow ノードの intrinsic size を計算する
44
+ */
45
+ export declare function measureFlow(node: FlowNode): {
46
+ width: number;
47
+ height: number;
48
+ };
49
+ //# sourceMappingURL=measureCompositeNodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measureCompositeNodes.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureCompositeNodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,QAAQ,EAET,MAAM,UAAU,CAAC;AAElB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAsBA;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAoCA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAQA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAuCA;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAsBA"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * ProcessArrow ノードの intrinsic size を計算する
3
+ */
4
+ export function measureProcessArrow(node) {
5
+ const stepCount = node.steps.length;
6
+ if (stepCount === 0) {
7
+ return { width: 0, height: 0 };
8
+ }
9
+ const itemWidth = node.itemWidth ?? 150;
10
+ const itemHeight = node.itemHeight ?? 60;
11
+ const gap = node.gap ?? -15;
12
+ const direction = node.direction ?? "horizontal";
13
+ if (direction === "horizontal") {
14
+ return {
15
+ width: stepCount * itemWidth + (stepCount - 1) * gap,
16
+ height: itemHeight,
17
+ };
18
+ }
19
+ else {
20
+ return {
21
+ width: itemWidth,
22
+ height: stepCount * itemHeight + (stepCount - 1) * gap,
23
+ };
24
+ }
25
+ }
26
+ /**
27
+ * Timeline ノードの intrinsic size を計算する
28
+ *
29
+ * Timeline の描画には以下の固定値が使用される:
30
+ * - nodeRadius: 12px (円のサイズ)
31
+ * - 日付ラベル領域: 上部 40px
32
+ * - タイトルラベル領域: 下部 24px
33
+ * - 説明ラベル領域: 下部 32px
34
+ */
35
+ export function measureTimeline(node) {
36
+ const itemCount = node.items.length;
37
+ if (itemCount === 0) {
38
+ return { width: 0, height: 0 };
39
+ }
40
+ const nodeRadius = 12;
41
+ const direction = node.direction ?? "horizontal";
42
+ if (direction === "horizontal") {
43
+ // 各アイテムの幅: 120px (ラベル幅)
44
+ // 最小幅: nodeRadius * 2 + (itemCount - 1) * 間隔
45
+ const minItemSpacing = 120; // 各アイテムの最小間隔
46
+ const minWidth = nodeRadius * 2 + (itemCount - 1) * minItemSpacing;
47
+ // 高さ: 上部ラベル(40) + nodeRadius*2 + 下部ラベル(24+32) + マージン
48
+ const height = 40 + nodeRadius * 2 + 8 + 24 + 32;
49
+ return {
50
+ width: minWidth,
51
+ height: height,
52
+ };
53
+ }
54
+ else {
55
+ // 垂直方向
56
+ // 各アイテムの高さ: 約 60px
57
+ const minItemSpacing = 60;
58
+ const minHeight = nodeRadius * 2 + (itemCount - 1) * minItemSpacing;
59
+ // 幅: lineX(40) + nodeRadius + ラベル幅
60
+ const width = 40 + nodeRadius * 2 + 16 + 100;
61
+ return {
62
+ width: width,
63
+ height: minHeight,
64
+ };
65
+ }
66
+ }
67
+ /**
68
+ * Matrix ノードの intrinsic size を計算する
69
+ *
70
+ * Matrix の描画には以下の固定値が使用される:
71
+ * - padding: 60px (軸ラベル用の余白)
72
+ * - itemSize: 24px (アイテムの円のサイズ)
73
+ */
74
+ export function measureMatrix(_) {
75
+ const padding = 60;
76
+ const minAreaSize = 100; // 最小の内部描画領域サイズ
77
+ return {
78
+ width: padding * 2 + minAreaSize,
79
+ height: padding * 2 + minAreaSize,
80
+ };
81
+ }
82
+ /**
83
+ * Tree ノードの intrinsic size を計算する
84
+ *
85
+ * ツリー構造を再帰的に走査し、必要なサイズを計算する
86
+ */
87
+ export function measureTree(node) {
88
+ const layout = node.layout ?? "vertical";
89
+ const nodeWidth = node.nodeWidth ?? 120;
90
+ const nodeHeight = node.nodeHeight ?? 40;
91
+ const levelGap = node.levelGap ?? 60;
92
+ const siblingGap = node.siblingGap ?? 20;
93
+ function calculateSubtreeSize(item) {
94
+ if (!item.children || item.children.length === 0) {
95
+ return { width: nodeWidth, height: nodeHeight };
96
+ }
97
+ const childSizes = item.children.map(calculateSubtreeSize);
98
+ if (layout === "vertical") {
99
+ const childrenWidth = childSizes.reduce((sum, s) => sum + s.width, 0) +
100
+ siblingGap * (childSizes.length - 1);
101
+ const childrenHeight = Math.max(...childSizes.map((s) => s.height));
102
+ return {
103
+ width: Math.max(nodeWidth, childrenWidth),
104
+ height: nodeHeight + levelGap + childrenHeight,
105
+ };
106
+ }
107
+ else {
108
+ const childrenHeight = childSizes.reduce((sum, s) => sum + s.height, 0) +
109
+ siblingGap * (childSizes.length - 1);
110
+ const childrenWidth = Math.max(...childSizes.map((s) => s.width));
111
+ return {
112
+ width: nodeWidth + levelGap + childrenWidth,
113
+ height: Math.max(nodeHeight, childrenHeight),
114
+ };
115
+ }
116
+ }
117
+ return calculateSubtreeSize(node.data);
118
+ }
119
+ /**
120
+ * Flow ノードの intrinsic size を計算する
121
+ */
122
+ export function measureFlow(node) {
123
+ const nodeCount = node.nodes.length;
124
+ if (nodeCount === 0) {
125
+ return { width: 0, height: 0 };
126
+ }
127
+ const nodeWidth = node.nodeWidth ?? 120;
128
+ const nodeHeight = node.nodeHeight ?? 60;
129
+ const nodeGap = node.nodeGap ?? 80;
130
+ const direction = node.direction ?? "horizontal";
131
+ if (direction === "horizontal") {
132
+ return {
133
+ width: nodeCount * nodeWidth + (nodeCount - 1) * nodeGap,
134
+ height: nodeHeight,
135
+ };
136
+ }
137
+ else {
138
+ return {
139
+ width: nodeWidth,
140
+ height: nodeCount * nodeHeight + (nodeCount - 1) * nodeGap,
141
+ };
142
+ }
143
+ }