@hirokisakabe/pom 5.5.0 → 5.5.1

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 +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;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
+ {"version":3,"file":"calcYogaLayout.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AACvD,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"}
@@ -83,10 +83,50 @@ async function getYoga() {
83
83
  yogaP = loadYoga();
84
84
  return yogaP;
85
85
  }
86
+ /**
87
+ * ノードが交差軸方向(幅)で確定サイズを持つかを判定する
88
+ * - 明示的な w がある場合は確定
89
+ * - alignSelf で stretch 以外が指定されている場合は不確定
90
+ * - 親の alignItems で stretch(デフォルト)以外が指定されている場合は不確定
91
+ */
92
+ function nodeHasDefiniteWidth(node, parentNode) {
93
+ // 明示的な幅がある
94
+ if (node.w !== undefined)
95
+ return true;
96
+ // alignSelf で stretch 以外が明示されている場合は不確定
97
+ if (node.alignSelf !== undefined &&
98
+ node.alignSelf !== "stretch" &&
99
+ node.alignSelf !== "auto") {
100
+ return false;
101
+ }
102
+ // 親がいない場合(ルートノード)は確定
103
+ if (!parentNode)
104
+ return true;
105
+ // 親の alignItems を取得(VStack/HStack のみ持つ、Box 等は undefined でデフォルト stretch)
106
+ let parentAlignItems;
107
+ if (parentNode.type === "vstack") {
108
+ parentAlignItems = parentNode.alignItems;
109
+ }
110
+ else if (parentNode.type === "hstack") {
111
+ parentAlignItems = parentNode.alignItems;
112
+ }
113
+ // VStack/Box(column 方向)の子の場合、交差軸は水平方向
114
+ // alignItems が stretch(デフォルト)なら子は親幅に伸長される
115
+ if (parentNode.type === "vstack" ||
116
+ parentNode.type === "box" ||
117
+ parentNode.type === "layer") {
118
+ return parentAlignItems === undefined || parentAlignItems === "stretch";
119
+ }
120
+ // HStack の子の場合、幅は主軸方向で flex により決まるため確定とみなす
121
+ if (parentNode.type === "hstack") {
122
+ return true;
123
+ }
124
+ return true;
125
+ }
86
126
  /**
87
127
  * POMNode ツリーを再帰的に走査し、YogaNode ツリーを構築する
88
128
  */
89
- async function buildPomWithYogaTree(node, parentYoga, ctx, map, parentNode) {
129
+ async function buildPomWithYogaTree(node, parentYoga, ctx, map, parentNode, grandparentNode) {
90
130
  const yoga = await getYoga();
91
131
  const yn = yoga.Node.create();
92
132
  map.set(node, yn); // 対応する YogaNode をマップに登録
@@ -105,21 +145,27 @@ async function buildPomWithYogaTree(node, parentYoga, ctx, map, parentNode) {
105
145
  node.type !== "table" &&
106
146
  node.type !== "icon") {
107
147
  yn.setFlexGrow(1);
108
- yn.setFlexBasis(0);
148
+ // HStack が確定幅を持つ場合のみ flexBasis=0 で均等分割
149
+ // HStack が auto-sized(親の alignItems が center/start/end 等)の場合、
150
+ // flexBasis=0 だと子要素の自然な幅が失われてレイアウトが崩れるため、
151
+ // flexBasis=auto(デフォルト)のまま維持する
152
+ if (nodeHasDefiniteWidth(parentNode, grandparentNode)) {
153
+ yn.setFlexBasis(0);
154
+ }
109
155
  }
110
156
  parentYoga.insertChild(yn, parentYoga.getChildCount());
111
157
  const def = getNodeDef(node.type);
112
158
  switch (def.category) {
113
159
  case "single-child": {
114
160
  const boxNode = node;
115
- await buildPomWithYogaTree(boxNode.children, yn, ctx, map, node);
161
+ await buildPomWithYogaTree(boxNode.children, yn, ctx, map, node, parentNode);
116
162
  break;
117
163
  }
118
164
  case "multi-child":
119
165
  case "absolute-child": {
120
166
  const containerNode = node;
121
167
  for (const child of containerNode.children) {
122
- await buildPomWithYogaTree(child, yn, ctx, map, node);
168
+ await buildPomWithYogaTree(child, yn, ctx, map, node, parentNode);
123
169
  }
124
170
  break;
125
171
  }
@@ -2,6 +2,10 @@
2
2
  * opentype.js を使用したフォント読み込みモジュール
3
3
  * Node.js とブラウザ両方で動作する
4
4
  */
5
+ /**
6
+ * 指定されたフォントがバンドル済みかどうかを判定する
7
+ */
8
+ export declare function isBundledFont(fontFamily: string): boolean;
5
9
  /**
6
10
  * 指定したテキストの幅を計測する
7
11
  * @param text 計測するテキスト
@@ -1 +1 @@
1
- {"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2DH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,MAAM,CAGR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAgB5E"}
1
+ {"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA8DH;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,MAAM,CAGR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAgB5E"}
@@ -46,6 +46,14 @@ function getFont(weight) {
46
46
  fontCache.set(cacheKey, font);
47
47
  return font;
48
48
  }
49
+ /** バンドル済みフォント名の一覧 */
50
+ const BUNDLED_FONT_NAMES = new Set(["Noto Sans JP"]);
51
+ /**
52
+ * 指定されたフォントがバンドル済みかどうかを判定する
53
+ */
54
+ export function isBundledFont(fontFamily) {
55
+ return BUNDLED_FONT_NAMES.has(fontFamily);
56
+ }
49
57
  /**
50
58
  * 指定したテキストの幅を計測する
51
59
  * @param text 計測するテキスト
@@ -1 +1 @@
1
- {"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AA6HnE;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,mBAA4B,GACjC;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA"}
1
+ {"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAKA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AA6HnE;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,mBAA4B,GACjC;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAoBA"}
@@ -1,4 +1,4 @@
1
- import { measureTextWidth as measureTextWidthOpentype } from "./fontLoader.js";
1
+ import { measureTextWidth as measureTextWidthOpentype, isBundledFont, } from "./fontLoader.js";
2
2
  /**
3
3
  * 文字がCJK(日本語・中国語・韓国語)文字かどうかを判定する
4
4
  */
@@ -108,6 +108,8 @@ function normalizeFontWeight(weight) {
108
108
  */
109
109
  export function measureText(text, maxWidthPx, opts, mode = "auto") {
110
110
  // 計測方法を決定
111
+ // "opentype" / "fallback" が明示指定された場合はそれを優先
112
+ // "auto" の場合はバンドル外フォントならフォールバック計測を使用
111
113
  const shouldUseFallback = (() => {
112
114
  switch (mode) {
113
115
  case "opentype":
@@ -115,8 +117,7 @@ export function measureText(text, maxWidthPx, opts, mode = "auto") {
115
117
  case "fallback":
116
118
  return true;
117
119
  case "auto":
118
- // opentype.js はバンドルされたフォントを使うため常に利用可能
119
- return false;
120
+ return !isBundledFont(opts.fontFamily);
120
121
  }
121
122
  })();
122
123
  if (shouldUseFallback) {
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AA0DxD,eAAO,MAAM,SAAS,EAAE,cAOvB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,cAOvB,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AA4DxD,eAAO,MAAM,SAAS,EAAE,cAOvB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,cAOvB,CAAC"}
@@ -5,7 +5,7 @@ function applyListYogaStyle(node, yn, yoga, ctx) {
5
5
  const n = node;
6
6
  const combinedText = n.items.map((item) => item.text).join("\n");
7
7
  const fontSizePx = n.fontSize ?? 24;
8
- const fontFamily = "Noto Sans JP";
8
+ const fontFamily = n.fontFamily ?? "Noto Sans JP";
9
9
  const fontWeight = n.bold ? "bold" : "normal";
10
10
  const spacingMultiple = n.lineHeight ?? 1.3;
11
11
  const fontMetricsRatio = measureFontLineHeightRatio(fontWeight);
@@ -20,6 +20,8 @@ function applyListYogaStyle(node, yn, yoga, ctx) {
20
20
  case yoga.MEASURE_MODE_EXACTLY:
21
21
  case yoga.MEASURE_MODE_AT_MOST:
22
22
  return width;
23
+ default:
24
+ return Number.POSITIVE_INFINITY;
23
25
  }
24
26
  })();
25
27
  const textMaxWidthPx = Math.max(0, maxWidthPx - bulletIndentPx);
@@ -1 +1 @@
1
- {"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/shape.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,YAAY,EAAE,cA0C1B,CAAC"}
1
+ {"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/shape.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,YAAY,EAAE,cA4C1B,CAAC"}
@@ -19,6 +19,8 @@ export const shapeNodeDef = {
19
19
  case yoga.MEASURE_MODE_EXACTLY:
20
20
  case yoga.MEASURE_MODE_AT_MOST:
21
21
  return width;
22
+ default:
23
+ return Number.POSITIVE_INFINITY;
22
24
  }
23
25
  })();
24
26
  const { widthPx, heightPx } = measureText(text, maxWidthPx, {
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/text.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,WAAW,EAAE,cAwCzB,CAAC"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/text.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,aAAa,CAAC;AAMxD,eAAO,MAAM,WAAW,EAAE,cA0CzB,CAAC"}
@@ -7,7 +7,7 @@ export const textNodeDef = {
7
7
  const n = node;
8
8
  const text = n.text;
9
9
  const fontSizePx = n.fontSize ?? 24;
10
- const fontFamily = "Noto Sans JP";
10
+ const fontFamily = n.fontFamily ?? "Noto Sans JP";
11
11
  const fontWeight = n.bold ? "bold" : "normal";
12
12
  const lineHeight = 1.3;
13
13
  yn.setMeasureFunc((width, widthMode) => {
@@ -18,6 +18,8 @@ export const textNodeDef = {
18
18
  case yoga.MEASURE_MODE_EXACTLY:
19
19
  case yoga.MEASURE_MODE_AT_MOST:
20
20
  return width;
21
+ default:
22
+ return Number.POSITIVE_INFINITY;
21
23
  }
22
24
  })();
23
25
  const { widthPx, heightPx } = measureText(text, maxWidthPx, {
@@ -9,7 +9,7 @@ export function renderImageNode(node, ctx) {
9
9
  h: pxToIn(content.h),
10
10
  shadow: node.shadow
11
11
  ? {
12
- type: node.shadow.type,
12
+ type: node.shadow.type ?? "outer",
13
13
  opacity: node.shadow.opacity,
14
14
  blur: node.shadow.blur,
15
15
  angle: node.shadow.angle,
@@ -23,7 +23,7 @@ export function renderShapeNode(node, ctx) {
23
23
  : undefined,
24
24
  shadow: node.shadow
25
25
  ? {
26
- type: node.shadow.type,
26
+ type: node.shadow.type ?? "outer",
27
27
  opacity: node.shadow.opacity,
28
28
  blur: node.shadow.blur,
29
29
  angle: node.shadow.angle,
@@ -24,15 +24,15 @@ export declare function createTextOptions(node: TextNode): {
24
24
  valign: "top";
25
25
  margin: number;
26
26
  lineSpacingMultiple: number;
27
- color: string;
28
- bold: boolean;
29
- italic: boolean;
27
+ color: string | undefined;
28
+ bold: boolean | undefined;
29
+ italic: boolean | undefined;
30
30
  underline: {
31
31
  style?: UnderlineStyle;
32
32
  color?: string;
33
- };
34
- strike: "sngStrike";
35
- highlight: string;
33
+ } | undefined;
34
+ strike: "sngStrike" | undefined;
35
+ highlight: string | undefined;
36
36
  };
37
37
  export {};
38
38
  //# sourceMappingURL=textOptions.d.ts.map
@@ -2,7 +2,7 @@ import { getImageData } from "../../shared/measureImage.js";
2
2
  import { pxToIn, pxToPt } from "../units.js";
3
3
  function convertShadow(shadow) {
4
4
  return {
5
- type: shadow.type,
5
+ type: shadow.type ?? "outer",
6
6
  opacity: shadow.opacity,
7
7
  blur: shadow.blur,
8
8
  angle: shadow.angle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hirokisakabe/pom",
3
- "version": "5.5.0",
3
+ "version": "5.5.1",
4
4
  "description": "AI-friendly PowerPoint generation with a Flexbox layout engine.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -88,7 +88,7 @@
88
88
  "prettier": "3.6.2",
89
89
  "size-limit": "^12.0.1",
90
90
  "tsx": "4.21.0",
91
- "typescript": "5.9.3",
91
+ "typescript": "6.0.2",
92
92
  "typescript-eslint": "^8.47.0",
93
93
  "vitest": "^4.1.0"
94
94
  },