@hirokisakabe/pom 5.0.1 → 5.2.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 (37) hide show
  1. package/README.md +17 -0
  2. package/dist/autoFit/autoFit.d.ts +15 -0
  3. package/dist/autoFit/autoFit.d.ts.map +1 -0
  4. package/dist/autoFit/autoFit.js +73 -0
  5. package/dist/autoFit/freeYogaTree.d.ts +7 -0
  6. package/dist/autoFit/freeYogaTree.d.ts.map +1 -0
  7. package/dist/autoFit/freeYogaTree.js +29 -0
  8. package/dist/autoFit/strategies/reduceFontSize.d.ts +8 -0
  9. package/dist/autoFit/strategies/reduceFontSize.d.ts.map +1 -0
  10. package/dist/autoFit/strategies/reduceFontSize.js +56 -0
  11. package/dist/autoFit/strategies/reduceGapAndPadding.d.ts +7 -0
  12. package/dist/autoFit/strategies/reduceGapAndPadding.d.ts.map +1 -0
  13. package/dist/autoFit/strategies/reduceGapAndPadding.js +46 -0
  14. package/dist/autoFit/strategies/reduceTableRowHeight.d.ts +7 -0
  15. package/dist/autoFit/strategies/reduceTableRowHeight.d.ts.map +1 -0
  16. package/dist/autoFit/strategies/reduceTableRowHeight.js +32 -0
  17. package/dist/autoFit/strategies/uniformScale.d.ts +7 -0
  18. package/dist/autoFit/strategies/uniformScale.d.ts.map +1 -0
  19. package/dist/autoFit/strategies/uniformScale.js +108 -0
  20. package/dist/autoFit/walkTree.d.ts +6 -0
  21. package/dist/autoFit/walkTree.d.ts.map +1 -0
  22. package/dist/autoFit/walkTree.js +18 -0
  23. package/dist/buildPptx.d.ts +1 -0
  24. package/dist/buildPptx.d.ts.map +1 -1
  25. package/dist/buildPptx.js +7 -1
  26. package/dist/calcYogaLayout/calcYogaLayout.js +77 -0
  27. package/dist/parseXml/inputSchema.d.ts +352 -0
  28. package/dist/parseXml/inputSchema.d.ts.map +1 -1
  29. package/dist/parseXml/inputSchema.js +13 -1
  30. package/dist/parseXml/parseXml.d.ts.map +1 -1
  31. package/dist/parseXml/parseXml.js +38 -1
  32. package/dist/renderPptx/renderPptx.d.ts.map +1 -1
  33. package/dist/renderPptx/renderPptx.js +14 -4
  34. package/dist/types.d.ts +373 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/types.js +21 -0
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -196,6 +196,23 @@ For detailed node documentation, see [Nodes Reference](./docs/nodes.md).
196
196
 
197
197
  <img src="./docs/images/pyramid.png" alt="Pyramid example" width="600">
198
198
 
199
+ ## Auto-Fit
200
+
201
+ When content exceeds the slide height, pom automatically adjusts it to fit within the slide. This is enabled by default.
202
+
203
+ Adjustments are applied in the following priority order:
204
+
205
+ 1. Reduce table row heights
206
+ 2. Reduce text font sizes
207
+ 3. Reduce gap / padding
208
+ 4. Uniform scaling (fallback)
209
+
210
+ To disable:
211
+
212
+ ```typescript
213
+ const pptx = await buildPptx(xml, { w: 1280, h: 720 }, { autoFit: false });
214
+ ```
215
+
199
216
  ## Documentation
200
217
 
201
218
  | Document | Description |
@@ -0,0 +1,15 @@
1
+ import type { POMNode } from "../types.ts";
2
+ /**
3
+ * スライドのオーバーフローを検出し、段階的に調整してスライド内に収める。
4
+ *
5
+ * 調整の優先順:
6
+ * 1. テーブル行高さ縮小
7
+ * 2. フォントサイズ縮小
8
+ * 3. gap/padding 縮小
9
+ * 4. 全体スケーリング(フォールバック)
10
+ */
11
+ export declare function autoFitSlide(node: POMNode, slideSize: {
12
+ w: number;
13
+ h: number;
14
+ }): Promise<void>;
15
+ //# sourceMappingURL=autoFit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoFit.d.ts","sourceRoot":"","sources":["../../src/autoFit/autoFit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAsD3C;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -0,0 +1,73 @@
1
+ import { calcYogaLayout } from "../calcYogaLayout/calcYogaLayout.js";
2
+ import { freeYogaTree } from "./freeYogaTree.js";
3
+ import { reduceTableRowHeight } from "./strategies/reduceTableRowHeight.js";
4
+ import { reduceFontSize } from "./strategies/reduceFontSize.js";
5
+ import { reduceGapAndPadding } from "./strategies/reduceGapAndPadding.js";
6
+ import { uniformScale } from "./strategies/uniformScale.js";
7
+ /** オーバーフロー判定の許容マージン(0.5%) */
8
+ const OVERFLOW_TOLERANCE = 1.005;
9
+ const strategies = [
10
+ reduceTableRowHeight,
11
+ reduceFontSize,
12
+ reduceGapAndPadding,
13
+ uniformScale,
14
+ ];
15
+ /**
16
+ * 通常の slideSize でレイアウト計算し、コンテンツの占有高さを取得する。
17
+ *
18
+ * ルートの yogaNode の子要素の (top + height) の最大値を計算し、
19
+ * ルートの padding.bottom を加算してコンテンツの占有高さとする。
20
+ * h="max" や flexGrow の影響を受けず、正確なコンテンツ高さを返す。
21
+ */
22
+ async function measureContentHeight(node, slideSize) {
23
+ await calcYogaLayout(node, slideSize);
24
+ const rootYoga = node.yogaNode;
25
+ const childCount = rootYoga.getChildCount();
26
+ if (childCount === 0) {
27
+ return rootYoga.getComputedHeight();
28
+ }
29
+ let maxBottom = 0;
30
+ for (let i = 0; i < childCount; i++) {
31
+ const child = rootYoga.getChild(i);
32
+ const childLayout = child.getComputedLayout();
33
+ const bottom = childLayout.top + childLayout.height;
34
+ if (bottom > maxBottom) {
35
+ maxBottom = bottom;
36
+ }
37
+ }
38
+ // ルートの paddingBottom を加算
39
+ const paddingBottom = rootYoga.getComputedPadding(2); // EDGE_BOTTOM = 2
40
+ return maxBottom + paddingBottom;
41
+ }
42
+ /**
43
+ * スライドのオーバーフローを検出し、段階的に調整してスライド内に収める。
44
+ *
45
+ * 調整の優先順:
46
+ * 1. テーブル行高さ縮小
47
+ * 2. フォントサイズ縮小
48
+ * 3. gap/padding 縮小
49
+ * 4. 全体スケーリング(フォールバック)
50
+ */
51
+ export async function autoFitSlide(node, slideSize) {
52
+ for (const strategy of strategies) {
53
+ freeYogaTree(node);
54
+ const contentHeight = await measureContentHeight(node, slideSize);
55
+ if (contentHeight <= slideSize.h * OVERFLOW_TOLERANCE) {
56
+ break;
57
+ }
58
+ const ratio = slideSize.h / contentHeight;
59
+ const changed = strategy(node, ratio);
60
+ if (!changed) {
61
+ continue;
62
+ }
63
+ }
64
+ // 最終的にオーバーフローが解消されたか確認
65
+ freeYogaTree(node);
66
+ const finalHeight = await measureContentHeight(node, slideSize);
67
+ if (finalHeight > slideSize.h * OVERFLOW_TOLERANCE) {
68
+ console.warn(`[pom] autoFit: content height (${Math.round(finalHeight)}px) exceeds slide height (${slideSize.h}px) after all adjustments.`);
69
+ }
70
+ // 最終レイアウト(正しい slideSize で)
71
+ freeYogaTree(node);
72
+ await calcYogaLayout(node, slideSize);
73
+ }
@@ -0,0 +1,7 @@
1
+ import type { POMNode } from "../types.ts";
2
+ /**
3
+ * POMNode ツリー内の全 yogaNode を解放し、参照をクリアする。
4
+ * calcYogaLayout を再実行する前に呼び出すこと。
5
+ */
6
+ export declare function freeYogaTree(node: POMNode): void;
7
+ //# sourceMappingURL=freeYogaTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freeYogaTree.d.ts","sourceRoot":"","sources":["../../src/autoFit/freeYogaTree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAwBhD"}
@@ -0,0 +1,29 @@
1
+ import { walkPOMTree } from "./walkTree.js";
2
+ /**
3
+ * POMNode ツリー内の全 yogaNode を解放し、参照をクリアする。
4
+ * calcYogaLayout を再実行する前に呼び出すこと。
5
+ */
6
+ export function freeYogaTree(node) {
7
+ // 子から先に解放する(yoga-layout は親が子を参照しているため)
8
+ const nodes = [];
9
+ walkPOMTree(node, (n) => nodes.push(n));
10
+ // 逆順(リーフから)で解放
11
+ for (let i = nodes.length - 1; i >= 0; i--) {
12
+ const n = nodes[i];
13
+ if (n.yogaNode) {
14
+ // 親から切り離してから解放
15
+ const owner = n.yogaNode.getParent();
16
+ if (owner) {
17
+ const childCount = owner.getChildCount();
18
+ for (let j = 0; j < childCount; j++) {
19
+ if (owner.getChild(j) === n.yogaNode) {
20
+ owner.removeChild(n.yogaNode);
21
+ break;
22
+ }
23
+ }
24
+ }
25
+ n.yogaNode.free();
26
+ n.yogaNode = undefined;
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,8 @@
1
+ import type { POMNode } from "../../types.ts";
2
+ /**
3
+ * テキスト系ノードの fontSize を縮小する。
4
+ * 対象: text, ul, ol, shape
5
+ * @returns 変更があった場合 true
6
+ */
7
+ export declare function reduceFontSize(node: POMNode, targetRatio: number): boolean;
8
+ //# sourceMappingURL=reduceFontSize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduceFontSize.d.ts","sourceRoot":"","sources":["../../../src/autoFit/strategies/reduceFontSize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAM9C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CA4D1E"}
@@ -0,0 +1,56 @@
1
+ import { walkPOMTree } from "../walkTree.js";
2
+ const MIN_FONT_SIZE = 10;
3
+ const MIN_SCALE = 0.6;
4
+ /**
5
+ * テキスト系ノードの fontSize を縮小する。
6
+ * 対象: text, ul, ol, shape
7
+ * @returns 変更があった場合 true
8
+ */
9
+ export function reduceFontSize(node, targetRatio) {
10
+ const ratio = Math.max(targetRatio, MIN_SCALE);
11
+ let changed = false;
12
+ walkPOMTree(node, (n) => {
13
+ switch (n.type) {
14
+ case "text":
15
+ case "shape":
16
+ case "ul":
17
+ case "ol": {
18
+ if (n.fontSize !== undefined) {
19
+ const newSize = Math.max(MIN_FONT_SIZE, Math.round(n.fontSize * ratio));
20
+ if (newSize !== n.fontSize) {
21
+ n.fontSize = newSize;
22
+ changed = true;
23
+ }
24
+ }
25
+ break;
26
+ }
27
+ }
28
+ // ul/ol の li 要素の fontSize も縮小
29
+ if (n.type === "ul" || n.type === "ol") {
30
+ for (const item of n.items) {
31
+ if (item.fontSize !== undefined) {
32
+ const newSize = Math.max(MIN_FONT_SIZE, Math.round(item.fontSize * ratio));
33
+ if (newSize !== item.fontSize) {
34
+ item.fontSize = newSize;
35
+ changed = true;
36
+ }
37
+ }
38
+ }
39
+ }
40
+ // table セルの fontSize も縮小
41
+ if (n.type === "table") {
42
+ for (const row of n.rows) {
43
+ for (const cell of row.cells) {
44
+ if (cell.fontSize !== undefined) {
45
+ const newSize = Math.max(MIN_FONT_SIZE, Math.round(cell.fontSize * ratio));
46
+ if (newSize !== cell.fontSize) {
47
+ cell.fontSize = newSize;
48
+ changed = true;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ });
55
+ return changed;
56
+ }
@@ -0,0 +1,7 @@
1
+ import type { POMNode } from "../../types.ts";
2
+ /**
3
+ * gap と padding を縮小する。
4
+ * @returns 変更があった場合 true
5
+ */
6
+ export declare function reduceGapAndPadding(node: POMNode, targetRatio: number): boolean;
7
+ //# sourceMappingURL=reduceGapAndPadding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduceGapAndPadding.d.ts","sourceRoot":"","sources":["../../../src/autoFit/strategies/reduceGapAndPadding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAO9C;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAuCT"}
@@ -0,0 +1,46 @@
1
+ import { walkPOMTree } from "../walkTree.js";
2
+ const MIN_GAP = 2;
3
+ const MIN_PADDING = 2;
4
+ const MIN_SCALE = 0.25;
5
+ /**
6
+ * gap と padding を縮小する。
7
+ * @returns 変更があった場合 true
8
+ */
9
+ export function reduceGapAndPadding(node, targetRatio) {
10
+ const ratio = Math.max(targetRatio, MIN_SCALE);
11
+ let changed = false;
12
+ walkPOMTree(node, (n) => {
13
+ // gap の縮小(vstack, hstack)
14
+ if ((n.type === "vstack" || n.type === "hstack") && n.gap !== undefined) {
15
+ const newGap = Math.max(MIN_GAP, Math.round(n.gap * ratio));
16
+ if (newGap !== n.gap) {
17
+ n.gap = newGap;
18
+ changed = true;
19
+ }
20
+ }
21
+ // padding の縮小
22
+ if (n.padding !== undefined) {
23
+ if (typeof n.padding === "number") {
24
+ const newPadding = Math.max(MIN_PADDING, Math.round(n.padding * ratio));
25
+ if (newPadding !== n.padding) {
26
+ n.padding = newPadding;
27
+ changed = true;
28
+ }
29
+ }
30
+ else {
31
+ const dirs = ["top", "right", "bottom", "left"];
32
+ for (const dir of dirs) {
33
+ const val = n.padding[dir];
34
+ if (val !== undefined) {
35
+ const newVal = Math.max(MIN_PADDING, Math.round(val * ratio));
36
+ if (newVal !== val) {
37
+ n.padding[dir] = newVal;
38
+ changed = true;
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ });
45
+ return changed;
46
+ }
@@ -0,0 +1,7 @@
1
+ import type { POMNode } from "../../types.ts";
2
+ /**
3
+ * テーブルの defaultRowHeight と各行の height を縮小する。
4
+ * @returns 変更があった場合 true
5
+ */
6
+ export declare function reduceTableRowHeight(node: POMNode, targetRatio: number): boolean;
7
+ //# sourceMappingURL=reduceTableRowHeight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduceTableRowHeight.d.ts","sourceRoot":"","sources":["../../../src/autoFit/strategies/reduceTableRowHeight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAM9C;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAiCT"}
@@ -0,0 +1,32 @@
1
+ import { walkPOMTree } from "../walkTree.js";
2
+ const MIN_ROW_HEIGHT = 20;
3
+ const MIN_SCALE = 0.5;
4
+ /**
5
+ * テーブルの defaultRowHeight と各行の height を縮小する。
6
+ * @returns 変更があった場合 true
7
+ */
8
+ export function reduceTableRowHeight(node, targetRatio) {
9
+ const ratio = Math.max(targetRatio, MIN_SCALE);
10
+ let changed = false;
11
+ walkPOMTree(node, (n) => {
12
+ if (n.type !== "table")
13
+ return;
14
+ if (n.defaultRowHeight !== undefined) {
15
+ const newHeight = Math.max(MIN_ROW_HEIGHT, Math.round(n.defaultRowHeight * ratio));
16
+ if (newHeight !== n.defaultRowHeight) {
17
+ n.defaultRowHeight = newHeight;
18
+ changed = true;
19
+ }
20
+ }
21
+ for (const row of n.rows) {
22
+ if (row.height !== undefined) {
23
+ const newHeight = Math.max(MIN_ROW_HEIGHT, Math.round(row.height * ratio));
24
+ if (newHeight !== row.height) {
25
+ row.height = newHeight;
26
+ changed = true;
27
+ }
28
+ }
29
+ }
30
+ });
31
+ return changed;
32
+ }
@@ -0,0 +1,7 @@
1
+ import type { POMNode } from "../../types.ts";
2
+ /**
3
+ * 全サイズ関連プロパティを一律スケーリングする(フォールバック)。
4
+ * @returns 変更があった場合 true
5
+ */
6
+ export declare function uniformScale(node: POMNode, targetRatio: number): boolean;
7
+ //# sourceMappingURL=uniformScale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uniformScale.d.ts","sourceRoot":"","sources":["../../../src/autoFit/strategies/uniformScale.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAS9C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAyGxE"}
@@ -0,0 +1,108 @@
1
+ import { walkPOMTree } from "../walkTree.js";
2
+ const MIN_SCALE = 0.5;
3
+ function scaleNumber(value, ratio, min) {
4
+ return Math.max(min, Math.round(value * ratio));
5
+ }
6
+ /**
7
+ * 全サイズ関連プロパティを一律スケーリングする(フォールバック)。
8
+ * @returns 変更があった場合 true
9
+ */
10
+ export function uniformScale(node, targetRatio) {
11
+ const ratio = Math.max(targetRatio, MIN_SCALE);
12
+ let changed = false;
13
+ walkPOMTree(node, (n) => {
14
+ // fontSize
15
+ if ("fontSize" in n && typeof n.fontSize === "number") {
16
+ const newVal = scaleNumber(n.fontSize, ratio, 8);
17
+ if (newVal !== n.fontSize) {
18
+ n.fontSize = newVal;
19
+ changed = true;
20
+ }
21
+ }
22
+ // gap (vstack/hstack)
23
+ if ((n.type === "vstack" || n.type === "hstack") && n.gap !== undefined) {
24
+ const newVal = scaleNumber(n.gap, ratio, 1);
25
+ if (newVal !== n.gap) {
26
+ n.gap = newVal;
27
+ changed = true;
28
+ }
29
+ }
30
+ // padding
31
+ if (n.padding !== undefined) {
32
+ if (typeof n.padding === "number") {
33
+ const newVal = scaleNumber(n.padding, ratio, 1);
34
+ if (newVal !== n.padding) {
35
+ n.padding = newVal;
36
+ changed = true;
37
+ }
38
+ }
39
+ else {
40
+ const dirs = ["top", "right", "bottom", "left"];
41
+ for (const dir of dirs) {
42
+ const val = n.padding[dir];
43
+ if (val !== undefined) {
44
+ const newVal = scaleNumber(val, ratio, 1);
45
+ if (newVal !== val) {
46
+ n.padding[dir] = newVal;
47
+ changed = true;
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ // table: defaultRowHeight, row.height
54
+ if (n.type === "table") {
55
+ if (n.defaultRowHeight !== undefined) {
56
+ const newVal = scaleNumber(n.defaultRowHeight, ratio, 16);
57
+ if (newVal !== n.defaultRowHeight) {
58
+ n.defaultRowHeight = newVal;
59
+ changed = true;
60
+ }
61
+ }
62
+ for (const row of n.rows) {
63
+ if (row.height !== undefined) {
64
+ const newVal = scaleNumber(row.height, ratio, 16);
65
+ if (newVal !== row.height) {
66
+ row.height = newVal;
67
+ changed = true;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ // ul/ol items fontSize
73
+ if (n.type === "ul" || n.type === "ol") {
74
+ for (const item of n.items) {
75
+ if (item.fontSize !== undefined) {
76
+ const newVal = scaleNumber(item.fontSize, ratio, 8);
77
+ if (newVal !== item.fontSize) {
78
+ item.fontSize = newVal;
79
+ changed = true;
80
+ }
81
+ }
82
+ }
83
+ }
84
+ // icon size
85
+ if (n.type === "icon" && n.size !== undefined) {
86
+ const newVal = scaleNumber(n.size, ratio, 8);
87
+ if (newVal !== n.size) {
88
+ n.size = newVal;
89
+ changed = true;
90
+ }
91
+ }
92
+ // table cells fontSize
93
+ if (n.type === "table") {
94
+ for (const row of n.rows) {
95
+ for (const cell of row.cells) {
96
+ if (cell.fontSize !== undefined) {
97
+ const newVal = scaleNumber(cell.fontSize, ratio, 8);
98
+ if (newVal !== cell.fontSize) {
99
+ cell.fontSize = newVal;
100
+ changed = true;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ });
107
+ return changed;
108
+ }
@@ -0,0 +1,6 @@
1
+ import type { POMNode } from "../types.ts";
2
+ /**
3
+ * POMNode ツリーを再帰的に走査し、各ノードに visitor を適用する
4
+ */
5
+ export declare function walkPOMTree(node: POMNode, visitor: (node: POMNode) => void): void;
6
+ //# sourceMappingURL=walkTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walkTree.d.ts","sourceRoot":"","sources":["../../src/autoFit/walkTree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAC/B,IAAI,CAeN"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * POMNode ツリーを再帰的に走査し、各ノードに visitor を適用する
3
+ */
4
+ export function walkPOMTree(node, visitor) {
5
+ visitor(node);
6
+ switch (node.type) {
7
+ case "box":
8
+ walkPOMTree(node.children, visitor);
9
+ break;
10
+ case "vstack":
11
+ case "hstack":
12
+ case "layer":
13
+ for (const child of node.children) {
14
+ walkPOMTree(child, visitor);
15
+ }
16
+ break;
17
+ }
18
+ }
@@ -7,5 +7,6 @@ export declare function buildPptx(xml: string, slideSize: {
7
7
  }, options?: {
8
8
  master?: SlideMasterOptions;
9
9
  textMeasurement?: TextMeasurementMode;
10
+ autoFit?: boolean;
10
11
  }): Promise<import("pptxgenjs").default>;
11
12
  //# 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,iCAAiC,CAAC;AAIzC,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,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,wCAqBF"}
1
+ {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;AAIzC,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,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;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,wCAyBF"}
package/dist/buildPptx.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { autoFitSlide } from "./autoFit/autoFit.js";
1
2
  import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout.js";
2
3
  import { setTextMeasurementMode, } from "./calcYogaLayout/measureText.js";
3
4
  import { parseXml } from "./parseXml/parseXml.js";
@@ -14,7 +15,12 @@ export async function buildPptx(xml, slideSize, options) {
14
15
  const nodes = parseXml(xml);
15
16
  const positionedPages = [];
16
17
  for (const node of nodes) {
17
- await calcYogaLayout(node, slideSize);
18
+ if (options?.autoFit !== false) {
19
+ await autoFitSlide(node, slideSize);
20
+ }
21
+ else {
22
+ await calcYogaLayout(node, slideSize);
23
+ }
18
24
  const positioned = toPositioned(node);
19
25
  positionedPages.push(positioned);
20
26
  }
@@ -87,6 +87,11 @@ async function buildPomWithYogaTree(node, parentYoga, parentNode) {
87
87
  const yn = yoga.Node.create();
88
88
  node.yogaNode = yn; // 対応する YogaNode をセット
89
89
  await applyStyleToYogaNode(node, yn);
90
+ // HStack/VStack の子要素に flexShrink=1 をデフォルト設定(CSS Flexbox と同じ挙動)
91
+ // 主軸方向で %サイズ + gap がある場合の overflow を防ぐ
92
+ if (parentNode?.type === "hstack" || parentNode?.type === "vstack") {
93
+ yn.setFlexShrink(1);
94
+ }
90
95
  // HStack の子要素で幅が指定されていない場合、デフォルトで均等分割
91
96
  // テーブルは setMeasureFunc でカラム幅合計を返すため除外
92
97
  if (parentNode?.type === "hstack" &&
@@ -201,6 +206,65 @@ async function applyStyleToYogaNode(node, yn) {
201
206
  }
202
207
  }
203
208
  }
209
+ // margin
210
+ if (node.margin !== undefined) {
211
+ if (typeof node.margin === "number") {
212
+ yn.setMargin(yoga.EDGE_TOP, node.margin);
213
+ yn.setMargin(yoga.EDGE_RIGHT, node.margin);
214
+ yn.setMargin(yoga.EDGE_BOTTOM, node.margin);
215
+ yn.setMargin(yoga.EDGE_LEFT, node.margin);
216
+ }
217
+ else {
218
+ if (node.margin.top !== undefined) {
219
+ yn.setMargin(yoga.EDGE_TOP, node.margin.top);
220
+ }
221
+ if (node.margin.right !== undefined) {
222
+ yn.setMargin(yoga.EDGE_RIGHT, node.margin.right);
223
+ }
224
+ if (node.margin.bottom !== undefined) {
225
+ yn.setMargin(yoga.EDGE_BOTTOM, node.margin.bottom);
226
+ }
227
+ if (node.margin.left !== undefined) {
228
+ yn.setMargin(yoga.EDGE_LEFT, node.margin.left);
229
+ }
230
+ }
231
+ }
232
+ // position
233
+ if (node.position === "absolute") {
234
+ yn.setPositionType(yoga.POSITION_TYPE_ABSOLUTE);
235
+ }
236
+ if (node.top !== undefined) {
237
+ yn.setPosition(yoga.EDGE_TOP, node.top);
238
+ }
239
+ if (node.right !== undefined) {
240
+ yn.setPosition(yoga.EDGE_RIGHT, node.right);
241
+ }
242
+ if (node.bottom !== undefined) {
243
+ yn.setPosition(yoga.EDGE_BOTTOM, node.bottom);
244
+ }
245
+ if (node.left !== undefined) {
246
+ yn.setPosition(yoga.EDGE_LEFT, node.left);
247
+ }
248
+ // alignSelf
249
+ if (node.alignSelf !== undefined) {
250
+ switch (node.alignSelf) {
251
+ case "auto":
252
+ yn.setAlignSelf(yoga.ALIGN_AUTO);
253
+ break;
254
+ case "start":
255
+ yn.setAlignSelf(yoga.ALIGN_FLEX_START);
256
+ break;
257
+ case "center":
258
+ yn.setAlignSelf(yoga.ALIGN_CENTER);
259
+ break;
260
+ case "end":
261
+ yn.setAlignSelf(yoga.ALIGN_FLEX_END);
262
+ break;
263
+ case "stretch":
264
+ yn.setAlignSelf(yoga.ALIGN_STRETCH);
265
+ break;
266
+ }
267
+ }
204
268
  switch (node.type) {
205
269
  case "box":
206
270
  // 特になし
@@ -498,4 +562,17 @@ function applyFlexProperties(node, yn, yoga) {
498
562
  break;
499
563
  }
500
564
  }
565
+ if (node.flexWrap !== undefined) {
566
+ switch (node.flexWrap) {
567
+ case "nowrap":
568
+ yn.setFlexWrap(yoga.WRAP_NO_WRAP);
569
+ break;
570
+ case "wrap":
571
+ yn.setFlexWrap(yoga.WRAP_WRAP);
572
+ break;
573
+ case "wrapReverse":
574
+ yn.setFlexWrap(yoga.WRAP_WRAP_REVERSE);
575
+ break;
576
+ }
577
+ }
501
578
  }