@hirokisakabe/pom 5.4.0 → 5.5.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 (81) hide show
  1. package/README.md +6 -5
  2. package/dist/autoFit/autoFit.d.ts +2 -1
  3. package/dist/autoFit/autoFit.d.ts.map +1 -1
  4. package/dist/autoFit/autoFit.js +33 -17
  5. package/dist/buildContext.d.ts +2 -0
  6. package/dist/buildContext.d.ts.map +1 -1
  7. package/dist/buildContext.js +2 -0
  8. package/dist/buildPptx.d.ts +7 -1
  9. package/dist/buildPptx.d.ts.map +1 -1
  10. package/dist/buildPptx.js +14 -5
  11. package/dist/calcYogaLayout/calcYogaLayout.d.ts +4 -2
  12. package/dist/calcYogaLayout/calcYogaLayout.d.ts.map +1 -1
  13. package/dist/calcYogaLayout/calcYogaLayout.js +29 -15
  14. package/dist/calcYogaLayout/types.d.ts +16 -0
  15. package/dist/calcYogaLayout/types.d.ts.map +1 -0
  16. package/dist/calcYogaLayout/types.js +14 -0
  17. package/dist/diagnostics.d.ts +14 -0
  18. package/dist/diagnostics.d.ts.map +1 -0
  19. package/dist/diagnostics.js +17 -0
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -0
  23. package/dist/parseXml/parseXml.d.ts +1 -1
  24. package/dist/parseXml/parseXml.d.ts.map +1 -1
  25. package/dist/parseXml/parseXml.js +16 -16
  26. package/dist/registry/definitions/icon.d.ts.map +1 -1
  27. package/dist/registry/definitions/icon.js +26 -14
  28. package/dist/registry/definitions/image.d.ts.map +1 -1
  29. package/dist/registry/definitions/image.js +2 -3
  30. package/dist/registry/definitions/layer.d.ts.map +1 -1
  31. package/dist/registry/definitions/layer.js +9 -6
  32. package/dist/registry/definitions/line.d.ts.map +1 -1
  33. package/dist/registry/definitions/line.js +1 -2
  34. package/dist/registry/types.d.ts +2 -1
  35. package/dist/registry/types.d.ts.map +1 -1
  36. package/dist/renderPptx/nodes/chart.d.ts.map +1 -1
  37. package/dist/renderPptx/nodes/chart.js +6 -4
  38. package/dist/renderPptx/nodes/flow.d.ts.map +1 -1
  39. package/dist/renderPptx/nodes/flow.js +14 -4
  40. package/dist/renderPptx/nodes/image.d.ts.map +1 -1
  41. package/dist/renderPptx/nodes/image.js +8 -6
  42. package/dist/renderPptx/nodes/list.d.ts.map +1 -1
  43. package/dist/renderPptx/nodes/list.js +19 -16
  44. package/dist/renderPptx/nodes/matrix.d.ts.map +1 -1
  45. package/dist/renderPptx/nodes/matrix.js +11 -9
  46. package/dist/renderPptx/nodes/processArrow.d.ts.map +1 -1
  47. package/dist/renderPptx/nodes/processArrow.js +14 -4
  48. package/dist/renderPptx/nodes/pyramid.d.ts.map +1 -1
  49. package/dist/renderPptx/nodes/pyramid.js +6 -4
  50. package/dist/renderPptx/nodes/shape.d.ts.map +1 -1
  51. package/dist/renderPptx/nodes/shape.js +6 -4
  52. package/dist/renderPptx/nodes/table.d.ts.map +1 -1
  53. package/dist/renderPptx/nodes/table.js +7 -5
  54. package/dist/renderPptx/nodes/timeline.d.ts.map +1 -1
  55. package/dist/renderPptx/nodes/timeline.js +14 -4
  56. package/dist/renderPptx/nodes/tree.d.ts.map +1 -1
  57. package/dist/renderPptx/nodes/tree.js +6 -4
  58. package/dist/renderPptx/textOptions.d.ts.map +1 -1
  59. package/dist/renderPptx/textOptions.js +6 -4
  60. package/dist/renderPptx/utils/contentArea.d.ts +26 -0
  61. package/dist/renderPptx/utils/contentArea.d.ts.map +1 -0
  62. package/dist/renderPptx/utils/contentArea.js +26 -0
  63. package/dist/renderPptx/utils/scaleToFit.d.ts +3 -2
  64. package/dist/renderPptx/utils/scaleToFit.d.ts.map +1 -1
  65. package/dist/renderPptx/utils/scaleToFit.js +3 -3
  66. package/dist/shared/freeYogaTree.d.ts +3 -3
  67. package/dist/shared/freeYogaTree.d.ts.map +1 -1
  68. package/dist/shared/freeYogaTree.js +16 -21
  69. package/dist/shared/measureImage.d.ts +3 -2
  70. package/dist/shared/measureImage.d.ts.map +1 -1
  71. package/dist/shared/measureImage.js +5 -5
  72. package/dist/toPositioned/toPositioned.d.ts +3 -6
  73. package/dist/toPositioned/toPositioned.d.ts.map +1 -1
  74. package/dist/toPositioned/toPositioned.js +11 -19
  75. package/dist/types.d.ts +12 -445
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/types.js +43 -50
  78. package/package.json +5 -2
  79. package/dist/parseXml/inputSchema.d.ts +0 -1622
  80. package/dist/parseXml/inputSchema.d.ts.map +0 -1
  81. package/dist/parseXml/inputSchema.js +0 -257
package/README.md CHANGED
@@ -35,10 +35,10 @@
35
35
 
36
36
  ## Features
37
37
 
38
- - **AI Friendly** — Simple XML structure designed for LLM code generation. Pair with [LLM Integration guide](./docs/llm-integration.md) for prompt-ready references.
38
+ - **AI Friendly** — Simple XML structure designed for LLM code generation. Include [llm.txt](./website/public/llm.txt) in your system prompt for XML reference. Also available at `https://pom.pptx.app/llm.txt`.
39
39
  - **Declarative** — Describe slides as XML. No imperative API calls needed.
40
40
  - **Flexible Layout** — Flexbox-style layout with VStack / HStack / Box, powered by yoga-layout.
41
- - **Rich Nodes** — 15 built-in node types: charts, flowcharts, tables, timelines, org trees, and more.
41
+ - **Rich Nodes** — 19 built-in node types: charts, flowcharts, tables, timelines, org trees, and more.
42
42
  - **Schema-validated** — XML input is validated with Zod schemas at runtime with clear error messages.
43
43
  - **PowerPoint Native** — Full access to native PowerPoint shape features (roundRect, ellipse, arrows, etc.).
44
44
  - **Pixel Units** — Intuitive pixel-based sizing (internally converted to inches at 96 DPI).
@@ -63,7 +63,7 @@ const xml = `
63
63
  </VStack>
64
64
  `;
65
65
 
66
- const pptx = await buildPptx(xml, { w: 1280, h: 720 });
66
+ const { pptx } = await buildPptx(xml, { w: 1280, h: 720 });
67
67
  await pptx.writeFile({ fileName: "presentation.pptx" });
68
68
  ```
69
69
 
@@ -89,6 +89,7 @@ await pptx.writeFile({ fileName: "presentation.pptx" });
89
89
  | Box | Container for single child with padding |
90
90
  | VStack | Vertical stack layout |
91
91
  | HStack | Horizontal stack layout |
92
+ | Icon | Lucide icons |
92
93
 
93
94
  For detailed node documentation, see [Nodes](./docs/nodes.md).
94
95
 
@@ -209,7 +210,7 @@ Adjustments are applied in the following priority order:
209
210
  To disable:
210
211
 
211
212
  ```typescript
212
- const pptx = await buildPptx(xml, { w: 1280, h: 720 }, { autoFit: false });
213
+ const { pptx } = await buildPptx(xml, { w: 1280, h: 720 }, { autoFit: false });
213
214
  ```
214
215
 
215
216
  ## Documentation
@@ -220,7 +221,7 @@ const pptx = await buildPptx(xml, { w: 1280, h: 720 }, { autoFit: false });
220
221
  | [Nodes](./docs/nodes.md) | Complete reference for all node types |
221
222
  | [Master Slide](./docs/master-slide.md) | Headers, footers, and page numbers |
222
223
  | [Text Measurement](./docs/text-measurement.md) | Text measurement options and settings |
223
- | [LLM Integration](./docs/llm-integration.md) | Compact XML reference for LLM prompts |
224
+ | [llm.txt](./website/public/llm.txt) | Compact XML reference for LLM prompts |
224
225
  | [Playground](https://pom.pptx.app/playground) | Try pom XML in the browser |
225
226
 
226
227
  ## License
@@ -1,5 +1,6 @@
1
1
  import type { POMNode } from "../types.ts";
2
2
  import type { BuildContext } from "../buildContext.ts";
3
+ import type { YogaNodeMap } from "../calcYogaLayout/types.ts";
3
4
  /**
4
5
  * スライドのオーバーフローを検出し、段階的に調整してスライド内に収める。
5
6
  *
@@ -12,5 +13,5 @@ import type { BuildContext } from "../buildContext.ts";
12
13
  export declare function autoFitSlide(node: POMNode, slideSize: {
13
14
  w: number;
14
15
  h: number;
15
- }, ctx: BuildContext): Promise<void>;
16
+ }, ctx: BuildContext): Promise<YogaNodeMap>;
16
17
  //# sourceMappingURL=autoFit.d.ts.map
@@ -1 +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;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAuDvD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
1
+ {"version":3,"file":"autoFit.d.ts","sourceRoot":"","sources":["../../src/autoFit/autoFit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AA6E9D;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,WAAW,CAAC,CAkBtB"}
@@ -13,15 +13,27 @@ const strategies = [
13
13
  uniformScale,
14
14
  ];
15
15
  /**
16
- * 通常の slideSize でレイアウト計算し、コンテンツの占有高さを取得する。
16
+ * レイアウト計算を実行し、コンテンツのオーバーフロー状態を測定する。
17
+ */
18
+ async function measureOverflow(node, slideSize, ctx) {
19
+ const map = await calcYogaLayout(node, slideSize, ctx);
20
+ const contentHeight = calcContentHeight(map, node);
21
+ const isOverflowing = contentHeight > slideSize.h * OVERFLOW_TOLERANCE;
22
+ const targetRatio = isOverflowing ? slideSize.h / contentHeight : 1;
23
+ return { contentHeight, isOverflowing, targetRatio, map };
24
+ }
25
+ /**
26
+ * Yoga レイアウト結果からコンテンツの占有高さを算出する。
17
27
  *
18
28
  * ルートの yogaNode の子要素の (top + height) の最大値を計算し、
19
29
  * ルートの padding.bottom を加算してコンテンツの占有高さとする。
20
30
  * h="max" や flexGrow の影響を受けず、正確なコンテンツ高さを返す。
21
31
  */
22
- async function measureContentHeight(node, slideSize, ctx) {
23
- await calcYogaLayout(node, slideSize, ctx);
24
- const rootYoga = node.yogaNode;
32
+ function calcContentHeight(map, node) {
33
+ const rootYoga = map.get(node);
34
+ if (!rootYoga) {
35
+ throw new Error("YogaNode not found in map for root node");
36
+ }
25
37
  const childCount = rootYoga.getChildCount();
26
38
  if (childCount === 0) {
27
39
  return rootYoga.getComputedHeight();
@@ -49,25 +61,29 @@ async function measureContentHeight(node, slideSize, ctx) {
49
61
  * 4. 全体スケーリング(フォールバック)
50
62
  */
51
63
  export async function autoFitSlide(node, slideSize, ctx) {
64
+ // Phase 1: 戦略を順次適用してオーバーフローを解消
52
65
  for (const strategy of strategies) {
53
- freeYogaTree(node);
54
- const contentHeight = await measureContentHeight(node, slideSize, ctx);
55
- if (contentHeight <= slideSize.h * OVERFLOW_TOLERANCE) {
66
+ const result = await measureOverflow(node, slideSize, ctx);
67
+ freeYogaTree(result.map);
68
+ if (!result.isOverflowing) {
56
69
  break;
57
70
  }
58
- const ratio = slideSize.h / contentHeight;
59
- const changed = strategy(node, ratio);
71
+ const changed = strategy(node, result.targetRatio);
60
72
  if (!changed) {
61
73
  continue;
62
74
  }
63
75
  }
64
- // 最終的にオーバーフローが解消されたか確認
65
- freeYogaTree(node);
66
- const finalHeight = await measureContentHeight(node, slideSize, ctx);
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.`);
76
+ // Phase 2: 最終レイアウト計算とオーバーフロー検証
77
+ return finalizeLayout(node, slideSize, ctx);
78
+ }
79
+ /**
80
+ * 最終レイアウトを計算し、オーバーフローが残っていれば警告を出力する。
81
+ */
82
+ async function finalizeLayout(node, slideSize, ctx) {
83
+ const result = await measureOverflow(node, slideSize, ctx);
84
+ if (result.isOverflowing) {
85
+ ctx.diagnostics.add("AUTOFIT_OVERFLOW", `autoFit: content height (${Math.round(result.contentHeight)}px) exceeds slide height (${slideSize.h}px) after all adjustments.`);
69
86
  }
70
- // 最終レイアウト(正しい slideSize で)
71
- freeYogaTree(node);
72
- await calcYogaLayout(node, slideSize, ctx);
87
+ freeYogaTree(result.map);
88
+ return calcYogaLayout(node, slideSize, ctx);
73
89
  }
@@ -1,4 +1,5 @@
1
1
  import type { TextMeasurementMode } from "./calcYogaLayout/measureText.ts";
2
+ import { DiagnosticCollector } from "./diagnostics.ts";
2
3
  export interface BuildContext {
3
4
  textMeasurementMode: TextMeasurementMode;
4
5
  imageSizeCache: Map<string, {
@@ -7,6 +8,7 @@ export interface BuildContext {
7
8
  }>;
8
9
  imageDataCache: Map<string, string>;
9
10
  iconRasterCache: Map<string, string>;
11
+ diagnostics: DiagnosticCollector;
10
12
  }
11
13
  export declare function createBuildContext(textMeasurementMode?: TextMeasurementMode): BuildContext;
12
14
  //# sourceMappingURL=buildContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildContext.d.ts","sourceRoot":"","sources":["../src/buildContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,wBAAgB,kBAAkB,CAChC,mBAAmB,GAAE,mBAA4B,GAChD,YAAY,CAOd"}
1
+ {"version":3,"file":"buildContext.d.ts","sourceRoot":"","sources":["../src/buildContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,wBAAgB,kBAAkB,CAChC,mBAAmB,GAAE,mBAA4B,GAChD,YAAY,CAQd"}
@@ -1,8 +1,10 @@
1
+ import { DiagnosticCollector } from "./diagnostics.js";
1
2
  export function createBuildContext(textMeasurementMode = "auto") {
2
3
  return {
3
4
  textMeasurementMode,
4
5
  imageSizeCache: new Map(),
5
6
  imageDataCache: new Map(),
6
7
  iconRasterCache: new Map(),
8
+ diagnostics: new DiagnosticCollector(),
7
9
  };
8
10
  }
@@ -1,6 +1,11 @@
1
1
  import type { TextMeasurementMode } from "./calcYogaLayout/measureText.ts";
2
+ import type { Diagnostic } from "./diagnostics.ts";
2
3
  import { SlideMasterOptions } from "./types.ts";
3
4
  export type { TextMeasurementMode };
5
+ export interface BuildPptxResult {
6
+ pptx: import("pptxgenjs").default;
7
+ diagnostics: Diagnostic[];
8
+ }
4
9
  export declare function buildPptx(xml: string, slideSize: {
5
10
  w: number;
6
11
  h: number;
@@ -8,5 +13,6 @@ export declare function buildPptx(xml: string, slideSize: {
8
13
  master?: SlideMasterOptions;
9
14
  textMeasurement?: TextMeasurementMode;
10
15
  autoFit?: boolean;
11
- }): Promise<import("pptxgenjs").default>;
16
+ strict?: boolean;
17
+ }): Promise<BuildPptxResult>;
12
18
  //# sourceMappingURL=buildPptx.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAK3E,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,wCAwBF"}
1
+ {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAG3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,WAAW,EAAE,OAAO,CAAC;IAClC,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,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;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,OAAO,CAAC,eAAe,CAAC,CA8B1B"}
package/dist/buildPptx.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { autoFitSlide } from "./autoFit/autoFit.js";
2
2
  import { createBuildContext } from "./buildContext.js";
3
3
  import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout.js";
4
+ import { extractLayoutResults } from "./calcYogaLayout/types.js";
5
+ import { DiagnosticsError } from "./diagnostics.js";
4
6
  import { parseXml } from "./parseXml/parseXml.js";
5
7
  import { renderPptx } from "./renderPptx/renderPptx.js";
6
8
  import { freeYogaTree } from "./shared/freeYogaTree.js";
@@ -10,20 +12,27 @@ export async function buildPptx(xml, slideSize, options) {
10
12
  const nodes = parseXml(xml);
11
13
  const positionedPages = [];
12
14
  for (const node of nodes) {
15
+ let map;
13
16
  try {
14
17
  if (options?.autoFit !== false) {
15
- await autoFitSlide(node, slideSize, ctx);
18
+ map = await autoFitSlide(node, slideSize, ctx);
16
19
  }
17
20
  else {
18
- await calcYogaLayout(node, slideSize, ctx);
21
+ map = await calcYogaLayout(node, slideSize, ctx);
19
22
  }
20
- const positioned = toPositioned(node, ctx);
23
+ const layoutMap = extractLayoutResults(map);
24
+ const positioned = toPositioned(node, ctx, layoutMap);
21
25
  positionedPages.push(positioned);
22
26
  }
23
27
  finally {
24
- freeYogaTree(node);
28
+ if (map)
29
+ freeYogaTree(map);
25
30
  }
26
31
  }
27
32
  const pptx = renderPptx(positionedPages, slideSize, ctx, options?.master);
28
- return pptx;
33
+ const diagnostics = ctx.diagnostics.items;
34
+ if (options?.strict && diagnostics.length > 0) {
35
+ throw new DiagnosticsError(diagnostics);
36
+ }
37
+ return { pptx, diagnostics };
29
38
  }
@@ -1,15 +1,17 @@
1
1
  import type { POMNode } from "../types.ts";
2
2
  import type { BuildContext } from "../buildContext.ts";
3
+ import type { YogaNodeMap } from "./types.ts";
3
4
  /**
4
5
  * POMNode ツリーを Yoga でレイアウト計算する
5
- * POMNode ツリーの各ノードに yogaNode プロパティがセットされる
6
+ * POMNode YogaNode の対応を YogaNodeMap として返す
6
7
  *
7
8
  * @param root 入力 POMNode ツリーのルート
8
9
  * @param slideSize スライド全体のサイズ(px)
9
10
  * @param ctx BuildContext
11
+ * @returns YogaNodeMap(POMNode → YogaNode のマッピング)
10
12
  */
11
13
  export declare function calcYogaLayout(root: POMNode, slideSize: {
12
14
  w: number;
13
15
  h: number;
14
- }, ctx: BuildContext): Promise<void>;
16
+ }, ctx: BuildContext): Promise<YogaNodeMap>;
15
17
  //# sourceMappingURL=calcYogaLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMvD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,GAAG,EAAE,YAAY,iBAiBlB"}
1
+ {"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO9C;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,WAAW,CAAC,CA0BtB"}
@@ -1,32 +1,43 @@
1
1
  import { loadYoga } from "yoga-layout/load";
2
2
  import { prefetchImageSize } from "../shared/measureImage.js";
3
+ import { freeYogaTree } from "../shared/freeYogaTree.js";
3
4
  import { getNodeDef } from "../registry/index.js";
4
5
  /**
5
6
  * POMNode ツリーを Yoga でレイアウト計算する
6
- * POMNode ツリーの各ノードに yogaNode プロパティがセットされる
7
+ * POMNode YogaNode の対応を YogaNodeMap として返す
7
8
  *
8
9
  * @param root 入力 POMNode ツリーのルート
9
10
  * @param slideSize スライド全体のサイズ(px)
10
11
  * @param ctx BuildContext
12
+ * @returns YogaNodeMap(POMNode → YogaNode のマッピング)
11
13
  */
12
14
  export async function calcYogaLayout(root, slideSize, ctx) {
13
15
  const Yoga = await getYoga();
14
16
  // 事前に全画像のサイズを取得(HTTPS対応のため)
15
17
  await prefetchAllImageSizes(root, ctx);
16
- const rootYoga = Yoga.Node.create();
17
- root.yogaNode = rootYoga;
18
- await buildPomWithYogaTree(root, rootYoga, ctx);
19
- // スライド全体サイズを指定
20
- rootYoga.setWidth(slideSize.w);
21
- rootYoga.setHeight(slideSize.h);
22
- rootYoga.calculateLayout(slideSize.w, slideSize.h, Yoga.DIRECTION_LTR);
18
+ const map = new Map();
19
+ try {
20
+ const rootYoga = Yoga.Node.create();
21
+ map.set(root, rootYoga);
22
+ await buildPomWithYogaTree(root, rootYoga, ctx, map);
23
+ // スライド全体サイズを指定
24
+ rootYoga.setWidth(slideSize.w);
25
+ rootYoga.setHeight(slideSize.h);
26
+ rootYoga.calculateLayout(slideSize.w, slideSize.h, Yoga.DIRECTION_LTR);
27
+ }
28
+ catch (e) {
29
+ // 途中で失敗した場合、作成済みの YogaNode を解放してから再 throw
30
+ freeYogaTree(map);
31
+ throw e;
32
+ }
33
+ return map;
23
34
  }
24
35
  /**
25
36
  * POMNode ツリー内のすべての画像のサイズを事前取得する
26
37
  */
27
38
  async function prefetchAllImageSizes(node, ctx) {
28
39
  const imageSources = collectImageSources(node);
29
- await Promise.all(imageSources.map((src) => prefetchImageSize(src, ctx.imageSizeCache, ctx.imageDataCache)));
40
+ await Promise.all(imageSources.map((src) => prefetchImageSize(src, ctx.imageSizeCache, ctx.imageDataCache, ctx.diagnostics)));
30
41
  }
31
42
  /**
32
43
  * POMNode ツリー内のすべての画像のsrcを収集する
@@ -75,21 +86,24 @@ async function getYoga() {
75
86
  /**
76
87
  * POMNode ツリーを再帰的に走査し、YogaNode ツリーを構築する
77
88
  */
78
- async function buildPomWithYogaTree(node, parentYoga, ctx, parentNode) {
89
+ async function buildPomWithYogaTree(node, parentYoga, ctx, map, parentNode) {
79
90
  const yoga = await getYoga();
80
91
  const yn = yoga.Node.create();
81
- node.yogaNode = yn; // 対応する YogaNode をセット
92
+ map.set(node, yn); // 対応する YogaNode をマップに登録
82
93
  await applyStyleToYogaNode(node, yn, ctx);
83
94
  // HStack/VStack の子要素に flexShrink=1 をデフォルト設定(CSS Flexbox と同じ挙動)
84
95
  // 主軸方向で %サイズ + gap がある場合の overflow を防ぐ
96
+ // アイコンは固定サイズのコンテンツなので shrink させない
85
97
  if (parentNode?.type === "hstack" || parentNode?.type === "vstack") {
86
- yn.setFlexShrink(1);
98
+ yn.setFlexShrink(node.type === "icon" ? 0 : 1);
87
99
  }
88
100
  // HStack の子要素で幅が指定されていない場合、デフォルトで均等分割
89
101
  // テーブルは setMeasureFunc でカラム幅合計を返すため除外
102
+ // アイコンは固定サイズのコンテンツなので除外
90
103
  if (parentNode?.type === "hstack" &&
91
104
  node.w === undefined &&
92
- node.type !== "table") {
105
+ node.type !== "table" &&
106
+ node.type !== "icon") {
93
107
  yn.setFlexGrow(1);
94
108
  yn.setFlexBasis(0);
95
109
  }
@@ -98,14 +112,14 @@ async function buildPomWithYogaTree(node, parentYoga, ctx, parentNode) {
98
112
  switch (def.category) {
99
113
  case "single-child": {
100
114
  const boxNode = node;
101
- await buildPomWithYogaTree(boxNode.children, yn, ctx, node);
115
+ await buildPomWithYogaTree(boxNode.children, yn, ctx, map, node);
102
116
  break;
103
117
  }
104
118
  case "multi-child":
105
119
  case "absolute-child": {
106
120
  const containerNode = node;
107
121
  for (const child of containerNode.children) {
108
- await buildPomWithYogaTree(child, yn, ctx, node);
122
+ await buildPomWithYogaTree(child, yn, ctx, map, node);
109
123
  }
110
124
  break;
111
125
  }
@@ -0,0 +1,16 @@
1
+ import type { Node as YogaNode } from "yoga-layout";
2
+ import type { POMNode } from "../types.ts";
3
+ /** POMNode と対応する YogaNode のマッピング。レイアウト計算フェーズでのみ存在する */
4
+ export type YogaNodeMap = Map<POMNode, YogaNode>;
5
+ /** Yoga 計算結果の軽量表現。Yoga ランタイムへの依存なし */
6
+ export interface LayoutResult {
7
+ left: number;
8
+ top: number;
9
+ width: number;
10
+ height: number;
11
+ }
12
+ /** POMNode と計算済みレイアウト結果のマッピング */
13
+ export type LayoutResultMap = Map<POMNode, LayoutResult>;
14
+ /** YogaNodeMap から計算済みレイアウト結果を抽出する */
15
+ export declare function extractLayoutResults(yogaMap: YogaNodeMap): LayoutResultMap;
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,uDAAuD;AACvD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAEjD,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iCAAiC;AACjC,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEzD,qCAAqC;AACrC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,eAAe,CAY1E"}
@@ -0,0 +1,14 @@
1
+ /** YogaNodeMap から計算済みレイアウト結果を抽出する */
2
+ export function extractLayoutResults(yogaMap) {
3
+ const layoutMap = new Map();
4
+ for (const [pomNode, yogaNode] of yogaMap) {
5
+ const computed = yogaNode.getComputedLayout();
6
+ layoutMap.set(pomNode, {
7
+ left: computed.left,
8
+ top: computed.top,
9
+ width: computed.width,
10
+ height: computed.height,
11
+ });
12
+ }
13
+ return layoutMap;
14
+ }
@@ -0,0 +1,14 @@
1
+ export type DiagnosticCode = "IMAGE_MEASURE_FAILED" | "IMAGE_NOT_PREFETCHED" | "AUTOFIT_OVERFLOW" | "SCALE_BELOW_THRESHOLD";
2
+ export interface Diagnostic {
3
+ code: DiagnosticCode;
4
+ message: string;
5
+ }
6
+ export declare class DiagnosticCollector {
7
+ readonly items: Diagnostic[];
8
+ add(code: DiagnosticCode, message: string): void;
9
+ }
10
+ export declare class DiagnosticsError extends Error {
11
+ readonly diagnostics: Diagnostic[];
12
+ constructor(diagnostics: Diagnostic[]);
13
+ }
14
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,sBAAsB,GACtB,sBAAsB,GACtB,kBAAkB,GAClB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,CAAM;IAElC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;CAGjD;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aACb,WAAW,EAAE,UAAU,EAAE;gBAAzB,WAAW,EAAE,UAAU,EAAE;CAOtD"}
@@ -0,0 +1,17 @@
1
+ export class DiagnosticCollector {
2
+ items = [];
3
+ add(code, message) {
4
+ this.items.push({ code, message });
5
+ }
6
+ }
7
+ export class DiagnosticsError extends Error {
8
+ diagnostics;
9
+ constructor(diagnostics) {
10
+ const summary = diagnostics
11
+ .map((d) => `[${d.code}] ${d.message}`)
12
+ .join("\n");
13
+ super(`Build completed with diagnostics:\n${summary}`);
14
+ this.diagnostics = diagnostics;
15
+ this.name = "DiagnosticsError";
16
+ }
17
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { buildPptx } from "./buildPptx.ts";
2
- export type { TextMeasurementMode } from "./buildPptx.ts";
2
+ export type { BuildPptxResult, TextMeasurementMode } from "./buildPptx.ts";
3
+ export { DiagnosticsError } from "./diagnostics.ts";
4
+ export type { Diagnostic, DiagnosticCode } from "./diagnostics.ts";
3
5
  export { ParseXmlError } from "./parseXml/parseXml.ts";
4
6
  export type { SlideMasterOptions, SlideMasterBackground, SlideMasterMargin, MasterObject, MasterTextObject, MasterImageObject, MasterRectObject, MasterLineObject, SlideNumberOptions, } from "./types.ts";
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { buildPptx } from "./buildPptx.js";
2
+ export { DiagnosticsError } from "./diagnostics.js";
2
3
  export { ParseXmlError } from "./parseXml/parseXml.js";
@@ -1,4 +1,4 @@
1
- import type { POMNode } from "../types.ts";
1
+ import { type POMNode } from "../types.ts";
2
2
  export declare class ParseXmlError extends Error {
3
3
  readonly errors: string[];
4
4
  constructor(errors: string[]);
@@ -1 +1 @@
1
- {"version":3,"file":"parseXml.d.ts","sourceRoot":"","sources":["../../src/parseXml/parseXml.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA6B3C,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,MAAM,EAAE,MAAM,EAAE,CAAC;gBACrB,MAAM,EAAE,MAAM,EAAE;CAM7B;AAm6BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAiCrD"}
1
+ {"version":3,"file":"parseXml.d.ts","sourceRoot":"","sources":["../../src/parseXml/parseXml.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EAgBb,MAAM,aAAa,CAAC;AAYrB,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,MAAM,EAAE,MAAM,EAAE,CAAC;gBACrB,MAAM,EAAE,MAAM,EAAE;CAM7B;AAm6BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAiCrD"}
@@ -1,5 +1,5 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
- import { inputTextNodeSchema, inputUlNodeSchema, inputOlNodeSchema, inputImageNodeSchema, inputTableNodeSchema, inputShapeNodeSchema, inputChartNodeSchema, inputTimelineNodeSchema, inputMatrixNodeSchema, inputTreeNodeSchema, inputFlowNodeSchema, inputProcessArrowNodeSchema, inputPyramidNodeSchema, inputLineNodeSchema, inputIconNodeSchema, } from "./inputSchema.js";
2
+ import { textNodeSchema, ulNodeSchema, olNodeSchema, imageNodeSchema, tableNodeSchema, shapeNodeSchema, chartNodeSchema, timelineNodeSchema, matrixNodeSchema, treeNodeSchema, flowNodeSchema, processArrowNodeSchema, pyramidNodeSchema, lineNodeSchema, iconNodeSchema, } from "../types.js";
3
3
  import { NODE_COERCION_MAP, CHILD_ELEMENT_COERCION_MAP, coerceWithRule, coerceFallback, getObjectShapeFromRule, isBooleanObjectUnionRule, } from "./coercionRules.js";
4
4
  // ===== ParseXmlError =====
5
5
  export class ParseXmlError extends Error {
@@ -85,21 +85,21 @@ function getKnownChildAttributes(tagName) {
85
85
  }
86
86
  // ===== Leaf node Zod validation schemas =====
87
87
  const leafNodeValidationSchemas = {
88
- text: inputTextNodeSchema,
89
- image: inputImageNodeSchema,
90
- table: inputTableNodeSchema,
91
- shape: inputShapeNodeSchema,
92
- chart: inputChartNodeSchema,
93
- timeline: inputTimelineNodeSchema,
94
- matrix: inputMatrixNodeSchema,
95
- tree: inputTreeNodeSchema,
96
- flow: inputFlowNodeSchema,
97
- processArrow: inputProcessArrowNodeSchema,
98
- pyramid: inputPyramidNodeSchema,
99
- line: inputLineNodeSchema,
100
- ul: inputUlNodeSchema,
101
- ol: inputOlNodeSchema,
102
- icon: inputIconNodeSchema,
88
+ text: textNodeSchema,
89
+ image: imageNodeSchema,
90
+ table: tableNodeSchema,
91
+ shape: shapeNodeSchema,
92
+ chart: chartNodeSchema,
93
+ timeline: timelineNodeSchema,
94
+ matrix: matrixNodeSchema,
95
+ tree: treeNodeSchema,
96
+ flow: flowNodeSchema,
97
+ processArrow: processArrowNodeSchema,
98
+ pyramid: pyramidNodeSchema,
99
+ line: lineNodeSchema,
100
+ ul: ulNodeSchema,
101
+ ol: olNodeSchema,
102
+ icon: iconNodeSchema,
103
103
  };
104
104
  function formatZodIssue(issue, tagName) {
105
105
  const path = issue.path;
@@ -1 +1 @@
1
- {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/icon.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,eAAO,MAAM,WAAW,EAAE,cAwDzB,CAAC"}
1
+ {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/icon.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD,eAAO,MAAM,WAAW,EAAE,cAqEzB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { rasterizeIcon } from "../../icons/index.js";
2
- import { omitYogaNode } from "../../toPositioned/toPositioned.js";
3
2
  import { renderIconNode } from "../../renderPptx/nodes/icon.js";
3
+ import { getContentArea } from "../../renderPptx/utils/contentArea.js";
4
4
  export const iconNodeDef = {
5
5
  type: "icon",
6
6
  category: "leaf",
@@ -14,11 +14,19 @@ export const iconNodeDef = {
14
14
  toPositioned(pom, absoluteX, absoluteY, layout, ctx) {
15
15
  const n = pom;
16
16
  const iconSize = n.size ?? 24;
17
- const rasterSize = Math.max(Math.ceil(n.variant ? iconSize : layout.width), Math.ceil(n.variant ? iconSize : layout.height), iconSize);
17
+ // padding を考慮したコンテンツ領域で bg/icon の座標を計算
18
+ const content = getContentArea({
19
+ x: absoluteX,
20
+ y: absoluteY,
21
+ w: layout.width,
22
+ h: layout.height,
23
+ padding: n.padding,
24
+ });
25
+ // 実描画サイズに合わせてラスタライズ(不要に大きい PNG を防ぐ)
26
+ const rasterSize = Math.max(Math.ceil(n.variant ? iconSize : Math.min(content.w, content.h)), iconSize);
18
27
  const iconImageData = rasterizeIcon(n.name, rasterSize, n.color ?? "#000000", ctx.iconRasterCache);
19
- // variant 指定時はアイコンを中央に配置
20
28
  const positioned = {
21
- ...omitYogaNode(n),
29
+ ...n,
22
30
  x: absoluteX,
23
31
  y: absoluteY,
24
32
  w: layout.width,
@@ -27,21 +35,25 @@ export const iconNodeDef = {
27
35
  };
28
36
  if (n.variant) {
29
37
  const totalSize = Math.ceil(iconSize * 1.75);
30
- // 背景図形は totalSize の正方形として、layout 領域の中央に配置
31
- const bgOffsetX = (layout.width - totalSize) / 2;
32
- const bgOffsetY = (layout.height - totalSize) / 2;
33
- positioned.bgX = absoluteX + bgOffsetX;
34
- positioned.bgY = absoluteY + bgOffsetY;
38
+ // 背景図形は totalSize の正方形として、コンテンツ領域の中央に配置
39
+ positioned.bgX = content.x + (content.w - totalSize) / 2;
40
+ positioned.bgY = content.y + (content.h - totalSize) / 2;
35
41
  positioned.bgW = totalSize;
36
42
  positioned.bgH = totalSize;
37
- // アイコンは背景図形の中央に配置
38
- const iconOffsetX = (layout.width - iconSize) / 2;
39
- const iconOffsetY = (layout.height - iconSize) / 2;
40
- positioned.iconX = absoluteX + iconOffsetX;
41
- positioned.iconY = absoluteY + iconOffsetY;
43
+ // アイコンはコンテンツ領域の中央に配置
44
+ positioned.iconX = content.x + (content.w - iconSize) / 2;
45
+ positioned.iconY = content.y + (content.h - iconSize) / 2;
42
46
  positioned.iconW = iconSize;
43
47
  positioned.iconH = iconSize;
44
48
  }
49
+ else {
50
+ // variant なしの場合もアスペクト比を維持し、コンテンツ領域の中央に配置
51
+ const iconSide = Math.min(content.w, content.h);
52
+ positioned.iconX = content.x + (content.w - iconSide) / 2;
53
+ positioned.iconY = content.y + (content.h - iconSide) / 2;
54
+ positioned.iconW = iconSide;
55
+ positioned.iconH = iconSide;
56
+ }
45
57
  return positioned;
46
58
  },
47
59
  render(node, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAOxD,eAAO,MAAM,YAAY,EAAE,cA+B1B,CAAC"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,YAAY,EAAE,cAmC1B,CAAC"}
@@ -1,6 +1,5 @@
1
1
  import { measureImage, getImageData } from "../../shared/measureImage.js";
2
2
  import { renderImageNode } from "../../renderPptx/nodes/image.js";
3
- import { omitYogaNode } from "../../toPositioned/toPositioned.js";
4
3
  export const imageNodeDef = {
5
4
  type: "image",
6
5
  category: "leaf",
@@ -8,7 +7,7 @@ export const imageNodeDef = {
8
7
  const n = node;
9
8
  const src = n.src;
10
9
  yn.setMeasureFunc(() => {
11
- const { widthPx, heightPx } = measureImage(src, ctx.imageSizeCache);
10
+ const { widthPx, heightPx } = measureImage(src, ctx.imageSizeCache, ctx.diagnostics);
12
11
  return { width: widthPx, height: heightPx };
13
12
  });
14
13
  },
@@ -16,7 +15,7 @@ export const imageNodeDef = {
16
15
  const n = pom;
17
16
  const imageData = getImageData(n.src, ctx.imageDataCache);
18
17
  return {
19
- ...omitYogaNode(n),
18
+ ...n,
20
19
  x: absoluteX,
21
20
  y: absoluteY,
22
21
  w: layout.width,
@@ -1 +1 @@
1
- {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/layer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,EAAE,cAwD1B,CAAC"}
1
+ {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/layer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,EAAE,cA4D1B,CAAC"}