@hirokisakabe/pom 4.0.0 → 4.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">pom</h1>
2
2
  <p align="center">
3
- Declarative PowerPoint generation from XML built for AI
3
+ AI-friendly PowerPoint generation with a Flexbox layout engine.
4
4
  </p>
5
5
 
6
6
  <p align="center">
@@ -10,8 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- <b>pom (PowerPoint Object Model)</b> is a TypeScript library that converts XML into PowerPoint files (.pptx).<br>
14
- Flexbox-style layout powered by <a href="https://www.yogalayout.dev/">yoga-layout</a>, rendered with <a href="https://github.com/nicktomlin/pptxgenjs">pptxgenjs</a>.
13
+ <b>pom (PowerPoint Object Model)</b> is a TypeScript library that converts XML into editable PowerPoint files (.pptx).
15
14
  </p>
16
15
 
17
16
  <p align="center">
@@ -1 +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,EACR,WAAW,EAEZ,MAAM,aAAa,CAAC;AAErB;;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;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAcA"}
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,EACR,WAAW,EAEZ,MAAM,aAAa,CAAC;AAOrB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAuBA;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;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAcA"}
@@ -1,3 +1,4 @@
1
+ import { ARROW_DEPTH_RATIO, DEFAULT_PROCESS_ARROW_ITEM_WIDTH, DEFAULT_PROCESS_ARROW_ITEM_HEIGHT, } from "../shared/processArrowConstants.js";
1
2
  /**
2
3
  * ProcessArrow ノードの intrinsic size を計算する
3
4
  */
@@ -6,9 +7,10 @@ export function measureProcessArrow(node) {
6
7
  if (stepCount === 0) {
7
8
  return { width: 0, height: 0 };
8
9
  }
9
- const itemWidth = node.itemWidth ?? 150;
10
- const itemHeight = node.itemHeight ?? 60;
11
- const gap = node.gap ?? -15;
10
+ const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;
11
+ const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;
12
+ const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;
13
+ const gap = node.gap ?? -arrowDepth;
12
14
  const direction = node.direction ?? "horizontal";
13
15
  if (direction === "horizontal") {
14
16
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"processArrow.d.ts","sourceRoot":"","sources":["../../../src/renderPptx/nodes/processArrow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,KAAK,0BAA0B,GAAG,OAAO,CACvC,cAAc,EACd;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CACzB,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,0BAA0B,EAChC,GAAG,EAAE,aAAa,GACjB,IAAI,CAsDN"}
1
+ {"version":3,"file":"processArrow.d.ts","sourceRoot":"","sources":["../../../src/renderPptx/nodes/processArrow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAWjD,KAAK,0BAA0B,GAAG,OAAO,CACvC,cAAc,EACd;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CACzB,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,0BAA0B,EAChC,GAAG,EAAE,aAAa,GACjB,IAAI,CA0DN"}
@@ -2,6 +2,7 @@ import { pxToIn, pxToPt } from "../units.js";
2
2
  import { convertUnderline, convertStrike } from "../textOptions.js";
3
3
  import { measureProcessArrow } from "../../calcYogaLayout/measureCompositeNodes.js";
4
4
  import { calcScaleFactor } from "../utils/scaleToFit.js";
5
+ import { ARROW_DEPTH_RATIO, DEFAULT_PROCESS_ARROW_ITEM_WIDTH, DEFAULT_PROCESS_ARROW_ITEM_HEIGHT, } from "../../shared/processArrowConstants.js";
5
6
  export function renderProcessArrowNode(node, ctx) {
6
7
  const direction = node.direction ?? "horizontal";
7
8
  const steps = node.steps;
@@ -10,23 +11,25 @@ export function renderProcessArrowNode(node, ctx) {
10
11
  return;
11
12
  const defaultColor = "4472C4"; // PowerPoint標準の青
12
13
  const defaultTextColor = "FFFFFF";
13
- const itemWidth = node.itemWidth ?? 150;
14
- const itemHeight = node.itemHeight ?? 60;
15
- const gap = node.gap ?? -15; // 負の値でシェブロンを重ねる
14
+ const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;
15
+ const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;
16
+ const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;
17
+ const gap = node.gap ?? -arrowDepth;
16
18
  // スケール係数を計算
17
19
  const intrinsic = measureProcessArrow(node);
18
20
  const scaleFactor = calcScaleFactor(node.w, node.h, intrinsic.width, intrinsic.height, "processArrow");
19
21
  const scaledItemWidth = itemWidth * scaleFactor;
20
22
  const scaledItemHeight = itemHeight * scaleFactor;
21
23
  const scaledGap = gap * scaleFactor;
24
+ const scaledArrowDepth = arrowDepth * scaleFactor;
22
25
  if (direction === "horizontal") {
23
- renderHorizontalProcessArrow(node, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, defaultColor, defaultTextColor, scaleFactor);
26
+ renderHorizontalProcessArrow(node, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, scaledArrowDepth, defaultColor, defaultTextColor, scaleFactor);
24
27
  }
25
28
  else {
26
- renderVerticalProcessArrow(node, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, defaultColor, defaultTextColor, scaleFactor);
29
+ renderVerticalProcessArrow(node, ctx, steps, stepCount, scaledItemWidth, scaledItemHeight, scaledGap, scaledArrowDepth, defaultColor, defaultTextColor, scaleFactor);
27
30
  }
28
31
  }
29
- function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, itemHeight, gap, defaultColor, defaultTextColor, scaleFactor) {
32
+ function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, itemHeight, gap, arrowDepth, defaultColor, defaultTextColor, scaleFactor) {
30
33
  const totalWidth = stepCount * itemWidth + (stepCount - 1) * gap;
31
34
  const startX = node.x + (node.w - totalWidth) / 2;
32
35
  const centerY = node.y + node.h / 2;
@@ -35,16 +38,45 @@ function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, it
35
38
  const stepY = centerY - itemHeight / 2;
36
39
  const fillColor = step.color?.replace("#", "") ?? defaultColor;
37
40
  const textColor = step.textColor?.replace("#", "") ?? defaultTextColor;
38
- // 最初のステップは homePlate、以降は chevron
39
- const shapeType = index === 0 ? ctx.pptx.ShapeType.homePlate : ctx.pptx.ShapeType.chevron;
40
- ctx.slide.addText(step.label, {
41
+ // custGeom でシェブロン形状を描画
42
+ const isFirst = index === 0;
43
+ const points = isFirst
44
+ ? [
45
+ // homePlate 風: 左辺フラット、右辺が矢印
46
+ { x: 0, y: 0 },
47
+ { x: pxToIn(itemWidth - arrowDepth), y: 0 },
48
+ { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },
49
+ { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },
50
+ { x: 0, y: pxToIn(itemHeight) },
51
+ { close: true },
52
+ ]
53
+ : [
54
+ // chevron 風: 左辺に切り欠き、右辺が矢印
55
+ { x: 0, y: 0 },
56
+ { x: pxToIn(itemWidth - arrowDepth), y: 0 },
57
+ { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },
58
+ { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },
59
+ { x: 0, y: pxToIn(itemHeight) },
60
+ { x: pxToIn(arrowDepth), y: pxToIn(itemHeight / 2) },
61
+ { close: true },
62
+ ];
63
+ ctx.slide.addShape("custGeom", {
41
64
  x: pxToIn(stepX),
42
65
  y: pxToIn(stepY),
43
66
  w: pxToIn(itemWidth),
44
67
  h: pxToIn(itemHeight),
45
- shape: shapeType,
68
+ points,
46
69
  fill: { color: fillColor },
47
70
  line: { type: "none" },
71
+ });
72
+ // テキストを図形の中央(矢印部分を除いた領域)に配置
73
+ const textOffsetLeft = isFirst ? 0 : arrowDepth;
74
+ const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);
75
+ ctx.slide.addText(step.label, {
76
+ x: pxToIn(stepX + textOffsetLeft),
77
+ y: pxToIn(stepY),
78
+ w: pxToIn(textWidth),
79
+ h: pxToIn(itemHeight),
48
80
  fontSize: pxToPt((node.fontPx ?? 14) * scaleFactor),
49
81
  fontFace: "Noto Sans JP",
50
82
  color: textColor,
@@ -58,7 +90,7 @@ function renderHorizontalProcessArrow(node, ctx, steps, stepCount, itemWidth, it
58
90
  });
59
91
  });
60
92
  }
61
- function renderVerticalProcessArrow(node, ctx, steps, stepCount, itemWidth, itemHeight, gap, defaultColor, defaultTextColor, scaleFactor) {
93
+ function renderVerticalProcessArrow(node, ctx, steps, stepCount, itemWidth, itemHeight, gap, arrowDepth, defaultColor, defaultTextColor, scaleFactor) {
62
94
  const totalHeight = stepCount * itemHeight + (stepCount - 1) * gap;
63
95
  const startY = node.y + (node.h - totalHeight) / 2;
64
96
  const centerX = node.x + node.w / 2;
@@ -67,16 +99,44 @@ function renderVerticalProcessArrow(node, ctx, steps, stepCount, itemWidth, item
67
99
  const stepY = startY + index * (itemHeight + gap);
68
100
  const fillColor = step.color?.replace("#", "") ?? defaultColor;
69
101
  const textColor = step.textColor?.replace("#", "") ?? defaultTextColor;
70
- // 垂直方向では pentagon を使用(下向き矢印風)
71
- const shapeType = index === 0 ? ctx.pptx.ShapeType.rect : ctx.pptx.ShapeType.pentagon;
72
- ctx.slide.addText(step.label, {
102
+ const isFirst = index === 0;
103
+ const points = isFirst
104
+ ? [
105
+ // rect 風: 上辺フラット、下辺が矢印
106
+ { x: 0, y: 0 },
107
+ { x: pxToIn(itemWidth), y: 0 },
108
+ { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },
109
+ { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },
110
+ { x: 0, y: pxToIn(itemHeight - arrowDepth) },
111
+ { close: true },
112
+ ]
113
+ : [
114
+ // pentagon 風: 上辺に切り欠き、下辺が矢印
115
+ { x: pxToIn(itemWidth / 2), y: 0 },
116
+ { x: pxToIn(itemWidth), y: pxToIn(arrowDepth) },
117
+ { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },
118
+ { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },
119
+ { x: 0, y: pxToIn(itemHeight - arrowDepth) },
120
+ { x: 0, y: pxToIn(arrowDepth) },
121
+ { close: true },
122
+ ];
123
+ ctx.slide.addShape("custGeom", {
73
124
  x: pxToIn(stepX),
74
125
  y: pxToIn(stepY),
75
126
  w: pxToIn(itemWidth),
76
127
  h: pxToIn(itemHeight),
77
- shape: shapeType,
128
+ points,
78
129
  fill: { color: fillColor },
79
130
  line: { type: "none" },
131
+ });
132
+ // テキストを図形の中央(矢印部分を除いた領域)に配置
133
+ const textOffsetTop = isFirst ? 0 : arrowDepth;
134
+ const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);
135
+ ctx.slide.addText(step.label, {
136
+ x: pxToIn(stepX),
137
+ y: pxToIn(stepY + textOffsetTop),
138
+ w: pxToIn(itemWidth),
139
+ h: pxToIn(textHeight),
80
140
  fontSize: pxToPt((node.fontPx ?? 14) * scaleFactor),
81
141
  fontFace: "Noto Sans JP",
82
142
  color: textColor,
@@ -0,0 +1,7 @@
1
+ /** 矢印の先端/切り欠きの深さ(itemHeight に対する比率) */
2
+ export declare const ARROW_DEPTH_RATIO = 0.35;
3
+ /** ProcessArrow のデフォルト itemHeight(px) */
4
+ export declare const DEFAULT_PROCESS_ARROW_ITEM_HEIGHT = 80;
5
+ /** ProcessArrow のデフォルト itemWidth(px) */
6
+ export declare const DEFAULT_PROCESS_ARROW_ITEM_WIDTH = 150;
7
+ //# sourceMappingURL=processArrowConstants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processArrowConstants.d.ts","sourceRoot":"","sources":["../../src/shared/processArrowConstants.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,yCAAyC;AACzC,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD,wCAAwC;AACxC,eAAO,MAAM,gCAAgC,MAAM,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** 矢印の先端/切り欠きの深さ(itemHeight に対する比率) */
2
+ export const ARROW_DEPTH_RATIO = 0.35;
3
+ /** ProcessArrow のデフォルト itemHeight(px) */
4
+ export const DEFAULT_PROCESS_ARROW_ITEM_HEIGHT = 80;
5
+ /** ProcessArrow のデフォルト itemWidth(px) */
6
+ export const DEFAULT_PROCESS_ARROW_ITEM_WIDTH = 150;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hirokisakabe/pom",
3
- "version": "4.0.0",
4
- "description": "PowerPoint Object Model - A declarative TypeScript library for creating PowerPoint presentations",
3
+ "version": "4.1.0",
4
+ "description": "AI-friendly PowerPoint generation with a Flexbox layout engine.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  "fmt:check": "prettier --check .",
48
48
  "knip": "knip",
49
49
  "lint": "eslint",
50
- "typecheck": "tsc --noEmit",
50
+ "typecheck": "tsc --noEmit -p tsconfig.check.json",
51
51
  "test": "vitest",
52
52
  "test:ui": "vitest --ui",
53
53
  "test:run": "vitest run",