@hirokisakabe/pom 10.0.0 → 10.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 +1 -1
- package/dist/autoFit/autoFit.js +55 -15
- package/dist/autoFit/autoFit.js.map +1 -1
- package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
- package/dist/buildContext.js +3 -1
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts +3 -2
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +1 -1
- package/dist/buildPptx.js.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
- package/dist/calcYogaLayout/fontLoader.d.ts +13 -0
- package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -0
- package/dist/calcYogaLayout/fontLoader.js +70 -3
- package/dist/calcYogaLayout/fontLoader.js.map +1 -1
- package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
- package/dist/calcYogaLayout/measureText.js +6 -6
- package/dist/calcYogaLayout/measureText.js.map +1 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/extractSlideMastersAsPptx.d.ts.map +1 -1
- package/dist/extractThemeTokensFromPptx.d.ts +0 -1
- package/dist/extractThemeTokensFromPptx.d.ts.map +1 -1
- package/dist/icons/renderIcon.js +12 -10
- package/dist/icons/renderIcon.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/parseXml/coercionRules.js.map +1 -1
- package/dist/parseXml/parseXml.d.ts +0 -1
- package/dist/parseXml/parseXml.d.ts.map +1 -1
- package/dist/parseXml/parseXml.js.map +1 -1
- package/dist/parseXml/serializeXml.d.ts +0 -1
- package/dist/parseXml/serializeXml.d.ts.map +1 -1
- package/dist/parseXml/serializeXml.js.map +1 -1
- package/dist/registry/definitions/list.js +1 -1
- package/dist/registry/definitions/list.js.map +1 -1
- package/dist/registry/definitions/shape.js +1 -1
- package/dist/registry/definitions/shape.js.map +1 -1
- package/dist/registry/definitions/text.js +1 -1
- package/dist/registry/definitions/text.js.map +1 -1
- package/dist/renderPptx/writablePptx.d.ts +1 -1
- package/dist/renderPptx/writablePptx.d.ts.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
- **Leaf Rotation** — `Text`, `Shape`, `Image`, and `Icon` support `rotate` in clockwise degrees at render time without affecting flex layout.
|
|
49
49
|
- **Pixel Units** — Intuitive pixel-based sizing (internally converted to inches at 96 DPI).
|
|
50
50
|
- **Master Slide** — Define headers, footers, and page numbers once — applied to all slides automatically.
|
|
51
|
-
- **Accurate Text Measurement** — Text width measured with opentype.js and bundled
|
|
51
|
+
- **Accurate Text Measurement** — Text width measured with opentype.js and bundled or caller-supplied font bytes for consistent layout in Node.js and browsers.
|
|
52
52
|
|
|
53
53
|
## Installation
|
|
54
54
|
|
package/dist/autoFit/autoFit.js
CHANGED
|
@@ -18,35 +18,72 @@ const strategies = [
|
|
|
18
18
|
*/
|
|
19
19
|
async function measureOverflow(node, slideSize, ctx) {
|
|
20
20
|
const map = await calcYogaLayout(node, slideSize, ctx);
|
|
21
|
-
const contentHeight = calcContentHeight(map, node);
|
|
21
|
+
const { contentHeight, furthestNode } = calcContentHeight(map, node);
|
|
22
22
|
const isOverflowing = contentHeight > slideSize.h * OVERFLOW_TOLERANCE;
|
|
23
23
|
return {
|
|
24
24
|
contentHeight,
|
|
25
25
|
isOverflowing,
|
|
26
26
|
targetRatio: isOverflowing ? slideSize.h / contentHeight : 1,
|
|
27
|
-
map
|
|
27
|
+
map,
|
|
28
|
+
furthestNode
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
|
-
* Yoga
|
|
32
|
+
* Yoga レイアウトと Layer の絶対座標からコンテンツの占有高さを算出する。
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
34
|
+
* 描画時と同じ規則で各ノードの (y + h) を再帰的に計算し、ルートの
|
|
35
|
+
* padding.bottom を加算する。Layer では absolute child の y、Line では
|
|
36
|
+
* y1/y2 を使うため、Yoga の通常フローによる過大計測を避けられる。
|
|
35
37
|
* h="max" や flexGrow の影響を受けず、正確なコンテンツ高さを返す。
|
|
36
38
|
*/
|
|
37
39
|
function calcContentHeight(map, node) {
|
|
38
40
|
const rootYoga = map.get(node);
|
|
39
41
|
if (!rootYoga) throw new Error("YogaNode not found in map for root node");
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
42
|
-
let maxBottom = 0;
|
|
43
|
-
for (let i = 0; i < childCount; i++) {
|
|
44
|
-
const childLayout = rootYoga.getChild(i).getComputedLayout();
|
|
45
|
-
const bottom = childLayout.top + childLayout.height;
|
|
46
|
-
if (bottom > maxBottom) maxBottom = bottom;
|
|
47
|
-
}
|
|
42
|
+
const furthestNode = findFurthestChild(map, node, rootYoga.getComputedLayout().top);
|
|
43
|
+
if (!furthestNode) return { contentHeight: rootYoga.getComputedHeight() };
|
|
48
44
|
const paddingBottom = rootYoga.getComputedPadding(2);
|
|
49
|
-
return
|
|
45
|
+
return {
|
|
46
|
+
contentHeight: furthestNode.bottom + paddingBottom,
|
|
47
|
+
furthestNode
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function findFurthestChild(map, parent, parentY) {
|
|
51
|
+
if (!("children" in parent) || !Array.isArray(parent.children)) return;
|
|
52
|
+
let furthest;
|
|
53
|
+
for (const child of parent.children) {
|
|
54
|
+
const candidate = measureNodeBounds(map, child, parent, parentY);
|
|
55
|
+
if (candidate && (!furthest || candidate.bottom > furthest.bottom)) furthest = candidate;
|
|
56
|
+
}
|
|
57
|
+
return furthest;
|
|
58
|
+
}
|
|
59
|
+
function measureNodeBounds(map, node, parent, parentY) {
|
|
60
|
+
if (node.type === "line") {
|
|
61
|
+
const top = (parent.type === "layer" ? parentY + getLayerChildY(node) : 0) + Math.min(node.y1, node.y2);
|
|
62
|
+
const height = Math.abs(node.y2 - node.y1);
|
|
63
|
+
return {
|
|
64
|
+
type: node.type,
|
|
65
|
+
top,
|
|
66
|
+
height,
|
|
67
|
+
bottom: top + height
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const yogaNode = map.get(node);
|
|
71
|
+
if (!yogaNode) throw new Error(`YogaNode not found in map for ${node.type} node`);
|
|
72
|
+
const layout = yogaNode.getComputedLayout();
|
|
73
|
+
const top = parent.type === "layer" ? parentY + getLayerChildY(node) : parentY + layout.top;
|
|
74
|
+
let furthest;
|
|
75
|
+
if (node.type !== "layer" || node.h !== void 0) furthest = {
|
|
76
|
+
type: node.type,
|
|
77
|
+
top,
|
|
78
|
+
height: layout.height,
|
|
79
|
+
bottom: top + layout.height
|
|
80
|
+
};
|
|
81
|
+
const descendant = findFurthestChild(map, node, top);
|
|
82
|
+
if (descendant && (!furthest || descendant.bottom > furthest.bottom)) furthest = descendant;
|
|
83
|
+
return furthest;
|
|
84
|
+
}
|
|
85
|
+
function getLayerChildY(node) {
|
|
86
|
+
return node.y ?? 0;
|
|
50
87
|
}
|
|
51
88
|
/**
|
|
52
89
|
* スライドのオーバーフローを検出し、段階的に調整してスライド内に収める。
|
|
@@ -71,7 +108,10 @@ async function autoFitSlide(node, slideSize, ctx) {
|
|
|
71
108
|
*/
|
|
72
109
|
async function finalizeLayout(node, slideSize, ctx) {
|
|
73
110
|
const result = await measureOverflow(node, slideSize, ctx);
|
|
74
|
-
if (result.isOverflowing)
|
|
111
|
+
if (result.isOverflowing) {
|
|
112
|
+
const detail = result.furthestNode ? ` Furthest node: ${result.furthestNode.type} at y=${Math.round(result.furthestNode.top)}px with height=${Math.round(result.furthestNode.height)}px (bottom=${Math.round(result.furthestNode.bottom)}px).` : "";
|
|
113
|
+
ctx.diagnostics.add("AUTOFIT_OVERFLOW", `autoFit: content height (${Math.round(result.contentHeight)}px) exceeds slide height (${slideSize.h}px) after all adjustments.${detail}`);
|
|
114
|
+
}
|
|
75
115
|
freeYogaTree(result.map);
|
|
76
116
|
return calcYogaLayout(node, slideSize, ctx);
|
|
77
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoFit.js","names":[],"sources":["../../src/autoFit/autoFit.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { YogaNodeMap } from \"../calcYogaLayout/types.ts\";\nimport { calcYogaLayout } from \"../calcYogaLayout/calcYogaLayout.ts\";\nimport { freeYogaTree } from \"../shared/freeYogaTree.ts\";\nimport { reduceTableRowHeight } from \"./strategies/reduceTableRowHeight.ts\";\nimport { reduceFontSize } from \"./strategies/reduceFontSize.ts\";\nimport { reduceGapAndPadding } from \"./strategies/reduceGapAndPadding.ts\";\nimport { uniformScale } from \"./strategies/uniformScale.ts\";\nimport type { AutoFitStrategyResult } from \"./strategyResult.ts\";\n\n/** オーバーフロー判定の許容マージン(0.5%) */\nconst OVERFLOW_TOLERANCE = 1.005;\n\ntype Strategy = (node: POMNode, targetRatio: number) => AutoFitStrategyResult;\n\nconst strategies: Strategy[] = [\n reduceTableRowHeight,\n reduceFontSize,\n reduceGapAndPadding,\n uniformScale,\n];\n\n/** オーバーフロー測定結果 */\ninterface OverflowResult {\n contentHeight: number;\n isOverflowing: boolean;\n /** スライド高さ / コンテンツ高さ(オーバーフロー時 < 1) */\n targetRatio: number;\n map: YogaNodeMap;\n}\n\n/**\n * レイアウト計算を実行し、コンテンツのオーバーフロー状態を測定する。\n */\nasync function measureOverflow(\n node: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<OverflowResult> {\n const map = await calcYogaLayout(node, slideSize, ctx);\n const contentHeight = calcContentHeight(map, node);\n const isOverflowing = contentHeight > slideSize.h * OVERFLOW_TOLERANCE;\n const targetRatio = isOverflowing ? slideSize.h / contentHeight : 1;\n return { contentHeight, isOverflowing, targetRatio, map };\n}\n\n/**\n * Yoga
|
|
1
|
+
{"version":3,"file":"autoFit.js","names":[],"sources":["../../src/autoFit/autoFit.ts"],"sourcesContent":["import type { POMNode } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { YogaNodeMap } from \"../calcYogaLayout/types.ts\";\nimport { calcYogaLayout } from \"../calcYogaLayout/calcYogaLayout.ts\";\nimport { freeYogaTree } from \"../shared/freeYogaTree.ts\";\nimport { reduceTableRowHeight } from \"./strategies/reduceTableRowHeight.ts\";\nimport { reduceFontSize } from \"./strategies/reduceFontSize.ts\";\nimport { reduceGapAndPadding } from \"./strategies/reduceGapAndPadding.ts\";\nimport { uniformScale } from \"./strategies/uniformScale.ts\";\nimport type { AutoFitStrategyResult } from \"./strategyResult.ts\";\n\n/** オーバーフロー判定の許容マージン(0.5%) */\nconst OVERFLOW_TOLERANCE = 1.005;\n\ntype Strategy = (node: POMNode, targetRatio: number) => AutoFitStrategyResult;\n\nconst strategies: Strategy[] = [\n reduceTableRowHeight,\n reduceFontSize,\n reduceGapAndPadding,\n uniformScale,\n];\n\n/** オーバーフロー測定結果 */\ninterface OverflowResult {\n contentHeight: number;\n isOverflowing: boolean;\n /** スライド高さ / コンテンツ高さ(オーバーフロー時 < 1) */\n targetRatio: number;\n map: YogaNodeMap;\n furthestNode?: ContentHeightResult[\"furthestNode\"];\n}\n\ninterface ContentHeightResult {\n contentHeight: number;\n furthestNode?: {\n type: POMNode[\"type\"];\n top: number;\n height: number;\n bottom: number;\n };\n}\n\n/**\n * レイアウト計算を実行し、コンテンツのオーバーフロー状態を測定する。\n */\nasync function measureOverflow(\n node: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<OverflowResult> {\n const map = await calcYogaLayout(node, slideSize, ctx);\n const { contentHeight, furthestNode } = calcContentHeight(map, node);\n const isOverflowing = contentHeight > slideSize.h * OVERFLOW_TOLERANCE;\n const targetRatio = isOverflowing ? slideSize.h / contentHeight : 1;\n return { contentHeight, isOverflowing, targetRatio, map, furthestNode };\n}\n\n/**\n * Yoga レイアウトと Layer の絶対座標からコンテンツの占有高さを算出する。\n *\n * 描画時と同じ規則で各ノードの (y + h) を再帰的に計算し、ルートの\n * padding.bottom を加算する。Layer では absolute child の y、Line では\n * y1/y2 を使うため、Yoga の通常フローによる過大計測を避けられる。\n * h=\"max\" や flexGrow の影響を受けず、正確なコンテンツ高さを返す。\n */\nfunction calcContentHeight(\n map: YogaNodeMap,\n node: POMNode,\n): ContentHeightResult {\n const rootYoga = map.get(node);\n if (!rootYoga) {\n throw new Error(\"YogaNode not found in map for root node\");\n }\n\n const rootLayout = rootYoga.getComputedLayout();\n const furthestNode = findFurthestChild(map, node, rootLayout.top);\n if (!furthestNode) {\n return { contentHeight: rootYoga.getComputedHeight() };\n }\n\n const paddingBottom = rootYoga.getComputedPadding(2); // EDGE_BOTTOM = 2\n return {\n contentHeight: furthestNode.bottom + paddingBottom,\n furthestNode,\n };\n}\n\nfunction findFurthestChild(\n map: YogaNodeMap,\n parent: POMNode,\n parentY: number,\n): ContentHeightResult[\"furthestNode\"] {\n if (!(\"children\" in parent) || !Array.isArray(parent.children)) {\n return undefined;\n }\n\n let furthest: ContentHeightResult[\"furthestNode\"];\n for (const child of parent.children as POMNode[]) {\n const candidate = measureNodeBounds(map, child, parent, parentY);\n if (candidate && (!furthest || candidate.bottom > furthest.bottom)) {\n furthest = candidate;\n }\n }\n return furthest;\n}\n\nfunction measureNodeBounds(\n map: YogaNodeMap,\n node: POMNode,\n parent: POMNode,\n parentY: number,\n): ContentHeightResult[\"furthestNode\"] {\n if (node.type === \"line\") {\n // Line の端点は通常はスライド絶対座標。Layer 直下だけ Layer 相対になる。\n const offsetY =\n parent.type === \"layer\" ? parentY + getLayerChildY(node) : 0;\n const top = offsetY + Math.min(node.y1, node.y2);\n const height = Math.abs(node.y2 - node.y1);\n return { type: node.type, top, height, bottom: top + height };\n }\n\n const yogaNode = map.get(node);\n if (!yogaNode) {\n throw new Error(`YogaNode not found in map for ${node.type} node`);\n }\n const layout = yogaNode.getComputedLayout();\n const top =\n parent.type === \"layer\"\n ? parentY + getLayerChildY(node)\n : parentY + layout.top;\n\n let furthest: ContentHeightResult[\"furthestNode\"];\n // Layer の Yoga height は absolute children の通常フロー合計なので、\n // 明示サイズがある場合だけコンテナ自身の描画境界として採用する。\n if (node.type !== \"layer\" || node.h !== undefined) {\n furthest = {\n type: node.type,\n top,\n height: layout.height,\n bottom: top + layout.height,\n };\n }\n\n const descendant = findFurthestChild(map, node, top);\n if (descendant && (!furthest || descendant.bottom > furthest.bottom)) {\n furthest = descendant;\n }\n return furthest;\n}\n\nfunction getLayerChildY(node: POMNode): number {\n return (node as POMNode & { y?: number }).y ?? 0;\n}\n\n/**\n * スライドのオーバーフローを検出し、段階的に調整してスライド内に収める。\n *\n * 調整の優先順:\n * 1. テーブル行高さ縮小\n * 2. フォントサイズ縮小\n * 3. gap/padding 縮小\n * 4. 全体スケーリング(フォールバック)\n */\nexport async function autoFitSlide(\n node: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<YogaNodeMap> {\n // Phase 1: 戦略を順次適用してオーバーフローを解消\n for (const strategy of strategies) {\n const result = await measureOverflow(node, slideSize, ctx);\n freeYogaTree(result.map);\n\n if (!result.isOverflowing) {\n break;\n }\n\n const strategyResult = strategy(node, result.targetRatio);\n if (!strategyResult.changed) {\n continue;\n }\n }\n\n // Phase 2: 最終レイアウト計算とオーバーフロー検証\n return finalizeLayout(node, slideSize, ctx);\n}\n\n/**\n * 最終レイアウトを計算し、オーバーフローが残っていれば警告を出力する。\n */\nasync function finalizeLayout(\n node: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<YogaNodeMap> {\n const result = await measureOverflow(node, slideSize, ctx);\n if (result.isOverflowing) {\n const detail = result.furthestNode\n ? ` Furthest node: ${result.furthestNode.type} at y=${Math.round(result.furthestNode.top)}px with height=${Math.round(result.furthestNode.height)}px (bottom=${Math.round(result.furthestNode.bottom)}px).`\n : \"\";\n ctx.diagnostics.add(\n \"AUTOFIT_OVERFLOW\",\n `autoFit: content height (${Math.round(result.contentHeight)}px) exceeds slide height (${slideSize.h}px) after all adjustments.${detail}`,\n );\n }\n freeYogaTree(result.map);\n\n return calcYogaLayout(node, slideSize, ctx);\n}\n"],"mappings":";;;;;;;;AAYA,MAAM,qBAAqB;AAI3B,MAAM,aAAyB;CAC7B;CACA;CACA;CACA;AACF;;;;AAyBA,eAAe,gBACb,MACA,WACA,KACyB;CACzB,MAAM,MAAM,MAAM,eAAe,MAAM,WAAW,GAAG;CACrD,MAAM,EAAE,eAAe,iBAAiB,kBAAkB,KAAK,IAAI;CACnE,MAAM,gBAAgB,gBAAgB,UAAU,IAAI;CAEpD,OAAO;EAAE;EAAe;EAAe,aADnB,gBAAgB,UAAU,IAAI,gBAAgB;EACd;EAAK;CAAa;AACxE;;;;;;;;;AAUA,SAAS,kBACP,KACA,MACqB;CACrB,MAAM,WAAW,IAAI,IAAI,IAAI;CAC7B,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,yCAAyC;CAI3D,MAAM,eAAe,kBAAkB,KAAK,MADzB,SAAS,kBAC+B,CAAC,CAAC,GAAG;CAChE,IAAI,CAAC,cACH,OAAO,EAAE,eAAe,SAAS,kBAAkB,EAAE;CAGvD,MAAM,gBAAgB,SAAS,mBAAmB,CAAC;CACnD,OAAO;EACL,eAAe,aAAa,SAAS;EACrC;CACF;AACF;AAEA,SAAS,kBACP,KACA,QACA,SACqC;CACrC,IAAI,EAAE,cAAc,WAAW,CAAC,MAAM,QAAQ,OAAO,QAAQ,GAC3D;CAGF,IAAI;CACJ,KAAK,MAAM,SAAS,OAAO,UAAuB;EAChD,MAAM,YAAY,kBAAkB,KAAK,OAAO,QAAQ,OAAO;EAC/D,IAAI,cAAc,CAAC,YAAY,UAAU,SAAS,SAAS,SACzD,WAAW;CAEf;CACA,OAAO;AACT;AAEA,SAAS,kBACP,KACA,MACA,QACA,SACqC;CACrC,IAAI,KAAK,SAAS,QAAQ;EAIxB,MAAM,OADJ,OAAO,SAAS,UAAU,UAAU,eAAe,IAAI,IAAI,KACvC,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;EAC/C,MAAM,SAAS,KAAK,IAAI,KAAK,KAAK,KAAK,EAAE;EACzC,OAAO;GAAE,MAAM,KAAK;GAAM;GAAK;GAAQ,QAAQ,MAAM;EAAO;CAC9D;CAEA,MAAM,WAAW,IAAI,IAAI,IAAI;CAC7B,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,iCAAiC,KAAK,KAAK,MAAM;CAEnE,MAAM,SAAS,SAAS,kBAAkB;CAC1C,MAAM,MACJ,OAAO,SAAS,UACZ,UAAU,eAAe,IAAI,IAC7B,UAAU,OAAO;CAEvB,IAAI;CAGJ,IAAI,KAAK,SAAS,WAAW,KAAK,MAAM,KAAA,GACtC,WAAW;EACT,MAAM,KAAK;EACX;EACA,QAAQ,OAAO;EACf,QAAQ,MAAM,OAAO;CACvB;CAGF,MAAM,aAAa,kBAAkB,KAAK,MAAM,GAAG;CACnD,IAAI,eAAe,CAAC,YAAY,WAAW,SAAS,SAAS,SAC3D,WAAW;CAEb,OAAO;AACT;AAEA,SAAS,eAAe,MAAuB;CAC7C,OAAQ,KAAkC,KAAK;AACjD;;;;;;;;;;AAWA,eAAsB,aACpB,MACA,WACA,KACsB;CAEtB,KAAK,MAAM,YAAY,YAAY;EACjC,MAAM,SAAS,MAAM,gBAAgB,MAAM,WAAW,GAAG;EACzD,aAAa,OAAO,GAAG;EAEvB,IAAI,CAAC,OAAO,eACV;EAIF,IAAI,CADmB,SAAS,MAAM,OAAO,WAC3B,CAAC,CAAC,SAClB;CAEJ;CAGA,OAAO,eAAe,MAAM,WAAW,GAAG;AAC5C;;;;AAKA,eAAe,eACb,MACA,WACA,KACsB;CACtB,MAAM,SAAS,MAAM,gBAAgB,MAAM,WAAW,GAAG;CACzD,IAAI,OAAO,eAAe;EACxB,MAAM,SAAS,OAAO,eAClB,mBAAmB,OAAO,aAAa,KAAK,QAAQ,KAAK,MAAM,OAAO,aAAa,GAAG,EAAE,iBAAiB,KAAK,MAAM,OAAO,aAAa,MAAM,EAAE,aAAa,KAAK,MAAM,OAAO,aAAa,MAAM,EAAE,QACpM;EACJ,IAAI,YAAY,IACd,oBACA,4BAA4B,KAAK,MAAM,OAAO,aAAa,EAAE,4BAA4B,UAAU,EAAE,4BAA4B,QACnI;CACF;CACA,aAAa,OAAO,GAAG;CAEvB,OAAO,eAAe,MAAM,WAAW,GAAG;AAC5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceFontSize.js","names":[],"sources":["../../../src/autoFit/strategies/reduceFontSize.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport { walkPOMTree } from \"../../shared/walkTree.ts\";\nimport type { AutoFitStrategyResult } from \"../strategyResult.ts\";\nimport { toStrategyResult } from \"../strategyResult.ts\";\n\nconst MIN_FONT_SIZE = 10;\nconst MIN_SCALE = 0.6;\n\n/**\n * テキスト系ノードの fontSize を縮小する。\n * 対象: text, ul, ol, shape\n */\nexport function reduceFontSize(\n node: POMNode,\n targetRatio: number,\n): AutoFitStrategyResult {\n const ratio = Math.max(targetRatio, MIN_SCALE);\n let changed = false;\n let sawTarget = false;\n\n const scale = (current: number) =>\n Math.max(MIN_FONT_SIZE, Math.round(current * ratio));\n\n const scaleRuns = (runs: { fontSize?: number }[] | undefined) => {\n if (!runs) return;\n for (const run of runs) {\n if (run.fontSize === undefined) continue;\n sawTarget = true;\n const newSize = scale(run.fontSize);\n if (newSize !== run.fontSize) {\n run.fontSize = newSize;\n changed = true;\n }\n }\n };\n\n walkPOMTree(node, (n) => {\n if (\n n.type === \"text\" ||\n n.type === \"shape\" ||\n n.type === \"ul\" ||\n n.type === \"ol\"\n ) {\n if (n.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(n.fontSize);\n if (newSize !== n.fontSize) {\n n.fontSize = newSize;\n changed = true;\n }\n }\n }\n\n // text の <Span fontSize> による run 単位上書きも一緒に縮小する\n // (shape は runs フィールドを持たない)\n if (n.type === \"text\") {\n scaleRuns(n.runs);\n }\n\n // ul/ol の li 要素の fontSize と <Span fontSize> による run 単位上書きも縮小\n if (n.type === \"ul\" || n.type === \"ol\") {\n for (const item of n.items) {\n if (item.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(item.fontSize);\n if (newSize !== item.fontSize) {\n item.fontSize = newSize;\n changed = true;\n }\n }\n scaleRuns(item.runs);\n }\n }\n\n // table セルの fontSize と <Span fontSize> による run 単位上書きも縮小\n if (n.type === \"table\") {\n for (const row of n.rows) {\n for (const cell of row.cells) {\n if (cell.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(cell.fontSize);\n if (newSize !== cell.fontSize) {\n cell.fontSize = newSize;\n changed = true;\n }\n }\n scaleRuns(cell.runs);\n }\n }\n }\n });\n\n return toStrategyResult({ changed, sawTarget });\n}\n"],"mappings":";;;AAKA,MAAM,gBAAgB;AACtB,MAAM,YAAY;;;;;AAMlB,SAAgB,eACd,MACA,aACuB;CACvB,MAAM,QAAQ,KAAK,IAAI,aAAa,SAAS;CAC7C,IAAI,UAAU;CACd,IAAI,YAAY;CAEhB,MAAM,SAAS,YACb,KAAK,IAAI,eAAe,KAAK,MAAM,UAAU,KAAK,CAAC;CAErD,MAAM,aAAa,SAA8C;EAC/D,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,IAAI,aAAa,KAAA,GAAW;GAChC,YAAY;GACZ,MAAM,UAAU,MAAM,IAAI,QAAQ;GAClC,IAAI,YAAY,IAAI,UAAU;IAC5B,IAAI,WAAW;IACf,UAAU;GACZ;EACF;CACF;CAEA,YAAY,OAAO,MAAM;EACvB,IACE,EAAE,SAAS,UACX,EAAE,SAAS,WACX,EAAE,SAAS,QACX,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"reduceFontSize.js","names":[],"sources":["../../../src/autoFit/strategies/reduceFontSize.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport { walkPOMTree } from \"../../shared/walkTree.ts\";\nimport type { AutoFitStrategyResult } from \"../strategyResult.ts\";\nimport { toStrategyResult } from \"../strategyResult.ts\";\n\nconst MIN_FONT_SIZE = 10;\nconst MIN_SCALE = 0.6;\n\n/**\n * テキスト系ノードの fontSize を縮小する。\n * 対象: text, ul, ol, shape\n */\nexport function reduceFontSize(\n node: POMNode,\n targetRatio: number,\n): AutoFitStrategyResult {\n const ratio = Math.max(targetRatio, MIN_SCALE);\n let changed = false;\n let sawTarget = false;\n\n const scale = (current: number) =>\n Math.max(MIN_FONT_SIZE, Math.round(current * ratio));\n\n const scaleRuns = (runs: { fontSize?: number }[] | undefined) => {\n if (!runs) return;\n for (const run of runs) {\n if (run.fontSize === undefined) continue;\n sawTarget = true;\n const newSize = scale(run.fontSize);\n if (newSize !== run.fontSize) {\n run.fontSize = newSize;\n changed = true;\n }\n }\n };\n\n walkPOMTree(node, (n) => {\n if (\n n.type === \"text\" ||\n n.type === \"shape\" ||\n n.type === \"ul\" ||\n n.type === \"ol\"\n ) {\n if (n.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(n.fontSize);\n if (newSize !== n.fontSize) {\n n.fontSize = newSize;\n changed = true;\n }\n }\n }\n\n // text の <Span fontSize> による run 単位上書きも一緒に縮小する\n // (shape は runs フィールドを持たない)\n if (n.type === \"text\") {\n scaleRuns(n.runs);\n }\n\n // ul/ol の li 要素の fontSize と <Span fontSize> による run 単位上書きも縮小\n if (n.type === \"ul\" || n.type === \"ol\") {\n for (const item of n.items) {\n if (item.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(item.fontSize);\n if (newSize !== item.fontSize) {\n item.fontSize = newSize;\n changed = true;\n }\n }\n scaleRuns(item.runs);\n }\n }\n\n // table セルの fontSize と <Span fontSize> による run 単位上書きも縮小\n if (n.type === \"table\") {\n for (const row of n.rows) {\n for (const cell of row.cells) {\n if (cell.fontSize !== undefined) {\n sawTarget = true;\n const newSize = scale(cell.fontSize);\n if (newSize !== cell.fontSize) {\n cell.fontSize = newSize;\n changed = true;\n }\n }\n scaleRuns(cell.runs);\n }\n }\n }\n });\n\n return toStrategyResult({ changed, sawTarget });\n}\n"],"mappings":";;;AAKA,MAAM,gBAAgB;AACtB,MAAM,YAAY;;;;;AAMlB,SAAgB,eACd,MACA,aACuB;CACvB,MAAM,QAAQ,KAAK,IAAI,aAAa,SAAS;CAC7C,IAAI,UAAU;CACd,IAAI,YAAY;CAEhB,MAAM,SAAS,YACb,KAAK,IAAI,eAAe,KAAK,MAAM,UAAU,KAAK,CAAC;CAErD,MAAM,aAAa,SAA8C;EAC/D,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,IAAI,aAAa,KAAA,GAAW;GAChC,YAAY;GACZ,MAAM,UAAU,MAAM,IAAI,QAAQ;GAClC,IAAI,YAAY,IAAI,UAAU;IAC5B,IAAI,WAAW;IACf,UAAU;GACZ;EACF;CACF;CAEA,YAAY,OAAO,MAAM;EACvB,IACE,EAAE,SAAS,UACX,EAAE,SAAS,WACX,EAAE,SAAS,QACX,EAAE,SAAS,MAEP;OAAA,EAAE,aAAa,KAAA,GAAW;IAC5B,YAAY;IACZ,MAAM,UAAU,MAAM,EAAE,QAAQ;IAChC,IAAI,YAAY,EAAE,UAAU;KAC1B,EAAE,WAAW;KACb,UAAU;IACZ;GACF;;EAKF,IAAI,EAAE,SAAS,QACb,UAAU,EAAE,IAAI;EAIlB,IAAI,EAAE,SAAS,QAAQ,EAAE,SAAS,MAChC,KAAK,MAAM,QAAQ,EAAE,OAAO;GAC1B,IAAI,KAAK,aAAa,KAAA,GAAW;IAC/B,YAAY;IACZ,MAAM,UAAU,MAAM,KAAK,QAAQ;IACnC,IAAI,YAAY,KAAK,UAAU;KAC7B,KAAK,WAAW;KAChB,UAAU;IACZ;GACF;GACA,UAAU,KAAK,IAAI;EACrB;EAIF,IAAI,EAAE,SAAS,SACb,KAAK,MAAM,OAAO,EAAE,MAClB,KAAK,MAAM,QAAQ,IAAI,OAAO;GAC5B,IAAI,KAAK,aAAa,KAAA,GAAW;IAC/B,YAAY;IACZ,MAAM,UAAU,MAAM,KAAK,QAAQ;IACnC,IAAI,YAAY,KAAK,UAAU;KAC7B,KAAK,WAAW;KAChB,UAAU;IACZ;GACF;GACA,UAAU,KAAK,IAAI;EACrB;CAGN,CAAC;CAED,OAAO,iBAAiB;EAAE;EAAS;CAAU,CAAC;AAChD"}
|
package/dist/buildContext.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { FontRegistry } from "./calcYogaLayout/fontLoader.js";
|
|
1
2
|
import { PptxAuthoringRegistry } from "./renderPptx/pptxAuthoring.js";
|
|
2
3
|
import { DiagnosticCollector } from "./diagnostics.js";
|
|
3
4
|
//#region src/buildContext.ts
|
|
4
|
-
function createBuildContext(textMeasurementMode = "auto") {
|
|
5
|
+
function createBuildContext(textMeasurementMode = "auto", fonts = []) {
|
|
5
6
|
return {
|
|
6
7
|
textMeasurementMode,
|
|
8
|
+
fontRegistry: new FontRegistry(fonts),
|
|
7
9
|
imageSizeCache: /* @__PURE__ */ new Map(),
|
|
8
10
|
imageDataCache: /* @__PURE__ */ new Map(),
|
|
9
11
|
iconRasterCache: /* @__PURE__ */ new Map(),
|
package/dist/buildContext.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildContext.js","names":[],"sources":["../src/buildContext.ts"],"sourcesContent":["import type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport { DiagnosticCollector } from \"./diagnostics.ts\";\nimport { PptxAuthoringRegistry } from \"./renderPptx/pptxAuthoring.ts\";\n\nexport interface BuildContext {\n textMeasurementMode: TextMeasurementMode;\n imageSizeCache: Map<string, { widthPx: number; heightPx: number }>;\n imageDataCache: Map<string, string>;\n iconRasterCache: Map<string, string>;\n diagnostics: DiagnosticCollector;\n pptxAuthoring: PptxAuthoringRegistry;\n}\n\nexport function createBuildContext(\n textMeasurementMode: TextMeasurementMode = \"auto\",\n): BuildContext {\n return {\n textMeasurementMode,\n imageSizeCache: new Map(),\n imageDataCache: new Map(),\n iconRasterCache: new Map(),\n diagnostics: new DiagnosticCollector(),\n pptxAuthoring: new PptxAuthoringRegistry(),\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"buildContext.js","names":[],"sources":["../src/buildContext.ts"],"sourcesContent":["import type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport { FontRegistry, type FontInput } from \"./calcYogaLayout/fontLoader.ts\";\nimport { DiagnosticCollector } from \"./diagnostics.ts\";\nimport { PptxAuthoringRegistry } from \"./renderPptx/pptxAuthoring.ts\";\n\nexport interface BuildContext {\n textMeasurementMode: TextMeasurementMode;\n fontRegistry: FontRegistry;\n imageSizeCache: Map<string, { widthPx: number; heightPx: number }>;\n imageDataCache: Map<string, string>;\n iconRasterCache: Map<string, string>;\n diagnostics: DiagnosticCollector;\n pptxAuthoring: PptxAuthoringRegistry;\n}\n\nexport function createBuildContext(\n textMeasurementMode: TextMeasurementMode = \"auto\",\n fonts: readonly FontInput[] = [],\n): BuildContext {\n return {\n textMeasurementMode,\n fontRegistry: new FontRegistry(fonts),\n imageSizeCache: new Map(),\n imageDataCache: new Map(),\n iconRasterCache: new Map(),\n diagnostics: new DiagnosticCollector(),\n pptxAuthoring: new PptxAuthoringRegistry(),\n };\n}\n"],"mappings":";;;;AAeA,SAAgB,mBACd,sBAA2C,QAC3C,QAA8B,CAAC,GACjB;CACd,OAAO;EACL;EACA,cAAc,IAAI,aAAa,KAAK;EACpC,gCAAgB,IAAI,IAAI;EACxB,gCAAgB,IAAI,IAAI;EACxB,iCAAiB,IAAI,IAAI;EACzB,aAAa,IAAI,oBAAoB;EACrC,eAAe,IAAI,sBAAsB;CAC3C;AACF"}
|
package/dist/buildPptx.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SlideMasterOptions } from "./types.js";
|
|
2
|
+
import { FontInput } from "./calcYogaLayout/fontLoader.js";
|
|
2
3
|
import { TextMeasurementMode } from "./calcYogaLayout/measureText.js";
|
|
3
4
|
import { Diagnostic } from "./diagnostics.js";
|
|
4
5
|
import { WritablePptx } from "./renderPptx/writablePptx.js";
|
|
5
|
-
|
|
6
6
|
//#region src/buildPptx.d.ts
|
|
7
7
|
interface BuildPptxResult {
|
|
8
8
|
pptx: WritablePptx;
|
|
@@ -15,9 +15,10 @@ declare function buildPptx(xml: string, slideSize: {
|
|
|
15
15
|
master?: SlideMasterOptions;
|
|
16
16
|
masterPptx?: ArrayBuffer | Uint8Array;
|
|
17
17
|
textMeasurement?: TextMeasurementMode;
|
|
18
|
+
fonts?: FontInput[];
|
|
18
19
|
autoFit?: boolean;
|
|
19
20
|
strict?: boolean;
|
|
20
21
|
}): Promise<BuildPptxResult>;
|
|
21
22
|
//#endregion
|
|
22
|
-
export { BuildPptxResult, type TextMeasurementMode, buildPptx };
|
|
23
|
+
export { BuildPptxResult, type FontInput, type TextMeasurementMode, buildPptx };
|
|
23
24
|
//# sourceMappingURL=buildPptx.d.ts.map
|
package/dist/buildPptx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;;UAqBiB;EACf,MAAM;EACN,aAAa;;iBAGO,UACpB,aACA;EAAa;EAAW;GACxB;EACE,SAAS;EACT,aAAa,cAAc;EAC3B,kBAAkB;EAClB,QAAQ;EACR;EACA;IAED,QAAQ"}
|
package/dist/buildPptx.js
CHANGED
|
@@ -12,7 +12,7 @@ import { toPositioned } from "./toPositioned/toPositioned.js";
|
|
|
12
12
|
import { validatePositioned } from "./validatePositioned/validatePositioned.js";
|
|
13
13
|
//#region src/buildPptx.ts
|
|
14
14
|
async function buildPptx(xml, slideSize, options) {
|
|
15
|
-
const ctx = createBuildContext(options?.textMeasurement ?? "auto");
|
|
15
|
+
const ctx = createBuildContext(options?.textMeasurement ?? "auto", options?.fonts);
|
|
16
16
|
const nodes = parseXml(xml);
|
|
17
17
|
const positionedPages = [];
|
|
18
18
|
for (const [slideIndex, node] of nodes.entries()) {
|
package/dist/buildPptx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPptx.js","names":[],"sources":["../src/buildPptx.ts"],"sourcesContent":["import { autoFitSlide } from \"./autoFit/autoFit.ts\";\nimport { createBuildContext } from \"./buildContext.ts\";\nimport { calcYogaLayout } from \"./calcYogaLayout/calcYogaLayout.ts\";\nimport type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport type { YogaNodeMap } from \"./calcYogaLayout/types.ts\";\nimport { extractLayoutResults } from \"./calcYogaLayout/types.ts\";\nimport type { Diagnostic } from \"./diagnostics.ts\";\nimport { DiagnosticsError } from \"./diagnostics.ts\";\nimport { parseMasterPptx } from \"./parseMasterPptx.ts\";\nimport { parseXml } from \"./parseXml/parseXml.ts\";\nimport { renderPptx } from \"./renderPptx/renderPptx.ts\";\nimport type { WritablePptx } from \"./renderPptx/writablePptx.ts\";\nimport { freeYogaTree } from \"./shared/freeYogaTree.ts\";\nimport { prefetchImageSize } from \"./shared/measureImage.ts\";\nimport { toPositioned } from \"./toPositioned/toPositioned.ts\";\nimport { PositionedNode, SlideMasterOptions } from \"./types.ts\";\nimport { validatePositioned } from \"./validatePositioned/validatePositioned.ts\";\n\nexport type { TextMeasurementMode };\n\nexport interface BuildPptxResult {\n pptx: WritablePptx;\n diagnostics: Diagnostic[];\n}\n\nexport async function buildPptx(\n xml: string,\n slideSize: { w: number; h: number },\n options?: {\n master?: SlideMasterOptions;\n masterPptx?: ArrayBuffer | Uint8Array;\n textMeasurement?: TextMeasurementMode;\n autoFit?: boolean;\n strict?: boolean;\n },\n): Promise<BuildPptxResult> {\n const ctx = createBuildContext(options?.textMeasurement ?? \"auto\");\n\n const nodes = parseXml(xml);\n const positionedPages: PositionedNode[] = [];\n\n for (const [slideIndex, node] of nodes.entries()) {\n let map: YogaNodeMap | undefined;\n try {\n if (options?.autoFit !== false) {\n map = await autoFitSlide(node, slideSize, ctx);\n } else {\n map = await calcYogaLayout(node, slideSize, ctx);\n }\n const layoutMap = extractLayoutResults(map);\n const positioned = await toPositioned(node, ctx, layoutMap);\n validatePositioned(positioned, slideSize, ctx, slideIndex);\n positionedPages.push(positioned);\n } finally {\n if (map) freeYogaTree(map);\n }\n }\n\n // masterPptx から背景を抽出し、master オプションにマージ\n let master = options?.master;\n if (options?.masterPptx) {\n try {\n const bg = await parseMasterPptx(options.masterPptx);\n if (bg) {\n if (master) {\n // 明示的に background が指定されていない場合のみ、masterPptx の背景を使用\n if (!master.background) {\n master = { ...master, background: bg };\n }\n } else {\n master = { background: bg };\n }\n }\n } catch (e) {\n const message =\n e instanceof Error ? e.message : \"Unknown error parsing masterPptx\";\n ctx.diagnostics.add(\"MASTER_PPTX_PARSE_FAILED\", message);\n }\n }\n\n const masterImageSources = [\n ...new Set(\n [\n master?.background && \"image\" in master.background\n ? master.background.image\n : undefined,\n ...(master?.objects\n ?.filter((object) => object.type === \"image\")\n .map((object) => object.src) ?? []),\n ].filter((source): source is string =>\n Boolean(\n source?.startsWith(\"https://\") || source?.startsWith(\"http://\"),\n ),\n ),\n ),\n ];\n await Promise.all(\n masterImageSources.map((source) =>\n prefetchImageSize(\n source,\n ctx.imageSizeCache,\n ctx.imageDataCache,\n ctx.diagnostics,\n ),\n ),\n );\n\n const pptx = renderPptx(positionedPages, slideSize, ctx, master);\n\n const diagnostics = ctx.diagnostics.items;\n\n if (options?.strict && diagnostics.length > 0) {\n throw new DiagnosticsError(diagnostics);\n }\n\n return { pptx, diagnostics };\n}\n"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"buildPptx.js","names":[],"sources":["../src/buildPptx.ts"],"sourcesContent":["import { autoFitSlide } from \"./autoFit/autoFit.ts\";\nimport { createBuildContext } from \"./buildContext.ts\";\nimport { calcYogaLayout } from \"./calcYogaLayout/calcYogaLayout.ts\";\nimport type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport type { FontInput } from \"./calcYogaLayout/fontLoader.ts\";\nimport type { YogaNodeMap } from \"./calcYogaLayout/types.ts\";\nimport { extractLayoutResults } from \"./calcYogaLayout/types.ts\";\nimport type { Diagnostic } from \"./diagnostics.ts\";\nimport { DiagnosticsError } from \"./diagnostics.ts\";\nimport { parseMasterPptx } from \"./parseMasterPptx.ts\";\nimport { parseXml } from \"./parseXml/parseXml.ts\";\nimport { renderPptx } from \"./renderPptx/renderPptx.ts\";\nimport type { WritablePptx } from \"./renderPptx/writablePptx.ts\";\nimport { freeYogaTree } from \"./shared/freeYogaTree.ts\";\nimport { prefetchImageSize } from \"./shared/measureImage.ts\";\nimport { toPositioned } from \"./toPositioned/toPositioned.ts\";\nimport { PositionedNode, SlideMasterOptions } from \"./types.ts\";\nimport { validatePositioned } from \"./validatePositioned/validatePositioned.ts\";\n\nexport type { FontInput, TextMeasurementMode };\n\nexport interface BuildPptxResult {\n pptx: WritablePptx;\n diagnostics: Diagnostic[];\n}\n\nexport async function buildPptx(\n xml: string,\n slideSize: { w: number; h: number },\n options?: {\n master?: SlideMasterOptions;\n masterPptx?: ArrayBuffer | Uint8Array;\n textMeasurement?: TextMeasurementMode;\n fonts?: FontInput[];\n autoFit?: boolean;\n strict?: boolean;\n },\n): Promise<BuildPptxResult> {\n const ctx = createBuildContext(\n options?.textMeasurement ?? \"auto\",\n options?.fonts,\n );\n\n const nodes = parseXml(xml);\n const positionedPages: PositionedNode[] = [];\n\n for (const [slideIndex, node] of nodes.entries()) {\n let map: YogaNodeMap | undefined;\n try {\n if (options?.autoFit !== false) {\n map = await autoFitSlide(node, slideSize, ctx);\n } else {\n map = await calcYogaLayout(node, slideSize, ctx);\n }\n const layoutMap = extractLayoutResults(map);\n const positioned = await toPositioned(node, ctx, layoutMap);\n validatePositioned(positioned, slideSize, ctx, slideIndex);\n positionedPages.push(positioned);\n } finally {\n if (map) freeYogaTree(map);\n }\n }\n\n // masterPptx から背景を抽出し、master オプションにマージ\n let master = options?.master;\n if (options?.masterPptx) {\n try {\n const bg = await parseMasterPptx(options.masterPptx);\n if (bg) {\n if (master) {\n // 明示的に background が指定されていない場合のみ、masterPptx の背景を使用\n if (!master.background) {\n master = { ...master, background: bg };\n }\n } else {\n master = { background: bg };\n }\n }\n } catch (e) {\n const message =\n e instanceof Error ? e.message : \"Unknown error parsing masterPptx\";\n ctx.diagnostics.add(\"MASTER_PPTX_PARSE_FAILED\", message);\n }\n }\n\n const masterImageSources = [\n ...new Set(\n [\n master?.background && \"image\" in master.background\n ? master.background.image\n : undefined,\n ...(master?.objects\n ?.filter((object) => object.type === \"image\")\n .map((object) => object.src) ?? []),\n ].filter((source): source is string =>\n Boolean(\n source?.startsWith(\"https://\") || source?.startsWith(\"http://\"),\n ),\n ),\n ),\n ];\n await Promise.all(\n masterImageSources.map((source) =>\n prefetchImageSize(\n source,\n ctx.imageSizeCache,\n ctx.imageDataCache,\n ctx.diagnostics,\n ),\n ),\n );\n\n const pptx = renderPptx(positionedPages, slideSize, ctx, master);\n\n const diagnostics = ctx.diagnostics.items;\n\n if (options?.strict && diagnostics.length > 0) {\n throw new DiagnosticsError(diagnostics);\n }\n\n return { pptx, diagnostics };\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,eAAsB,UACpB,KACA,WACA,SAQ0B;CAC1B,MAAM,MAAM,mBACV,SAAS,mBAAmB,QAC5B,SAAS,KACX;CAEA,MAAM,QAAQ,SAAS,GAAG;CAC1B,MAAM,kBAAoC,CAAC;CAE3C,KAAK,MAAM,CAAC,YAAY,SAAS,MAAM,QAAQ,GAAG;EAChD,IAAI;EACJ,IAAI;GACF,IAAI,SAAS,YAAY,OACvB,MAAM,MAAM,aAAa,MAAM,WAAW,GAAG;QAE7C,MAAM,MAAM,eAAe,MAAM,WAAW,GAAG;GAGjD,MAAM,aAAa,MAAM,aAAa,MAAM,KAD1B,qBAAqB,GACkB,CAAC;GAC1D,mBAAmB,YAAY,WAAW,KAAK,UAAU;GACzD,gBAAgB,KAAK,UAAU;EACjC,UAAU;GACR,IAAI,KAAK,aAAa,GAAG;EAC3B;CACF;CAGA,IAAI,SAAS,SAAS;CACtB,IAAI,SAAS,YACX,IAAI;EACF,MAAM,KAAK,MAAM,gBAAgB,QAAQ,UAAU;EACnD,IAAI,IACF,IAAI,QAEE;OAAA,CAAC,OAAO,YACV,SAAS;IAAE,GAAG;IAAQ,YAAY;GAAG;EAAA,OAGvC,SAAS,EAAE,YAAY,GAAG;CAGhC,SAAS,GAAG;EACV,MAAM,UACJ,aAAa,QAAQ,EAAE,UAAU;EACnC,IAAI,YAAY,IAAI,4BAA4B,OAAO;CACzD;CAGF,MAAM,qBAAqB,CACzB,GAAG,IAAI,IACL,CACE,QAAQ,cAAc,WAAW,OAAO,aACpC,OAAO,WAAW,QAClB,KAAA,GACJ,GAAI,QAAQ,SACR,QAAQ,WAAW,OAAO,SAAS,OAAO,CAAC,CAC5C,KAAK,WAAW,OAAO,GAAG,KAAK,CAAC,CACrC,CAAC,CAAC,QAAQ,WACR,QACE,QAAQ,WAAW,UAAU,KAAK,QAAQ,WAAW,SAAS,CAChE,CACF,CACF,CACF;CACA,MAAM,QAAQ,IACZ,mBAAmB,KAAK,WACtB,kBACE,QACA,IAAI,gBACJ,IAAI,gBACJ,IAAI,WACN,CACF,CACF;CAEA,MAAM,OAAO,WAAW,iBAAiB,WAAW,KAAK,MAAM;CAE/D,MAAM,cAAc,IAAI,YAAY;CAEpC,IAAI,SAAS,UAAU,YAAY,SAAS,GAC1C,MAAM,IAAI,iBAAiB,WAAW;CAGxC,OAAO;EAAE;EAAM;CAAY;AAC7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calcYogaLayout.js","names":[],"sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"sourcesContent":["import type { POMNode, AlignItems } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { YogaNodeMap } from \"./types.ts\";\nimport { Node as YogaNode } from \"yoga-layout\";\nimport { loadYoga } from \"yoga-layout/load\";\nimport { prefetchImageSize } from \"../shared/measureImage.ts\";\nimport { freeYogaTree } from \"../shared/freeYogaTree.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\n/**\n * POMNode ツリーを Yoga でレイアウト計算する\n * POMNode と YogaNode の対応を YogaNodeMap として返す\n *\n * @param root 入力 POMNode ツリーのルート\n * @param slideSize スライド全体のサイズ(px)\n * @param ctx BuildContext\n * @returns YogaNodeMap(POMNode → YogaNode のマッピング)\n */\nexport async function calcYogaLayout(\n root: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<YogaNodeMap> {\n const Yoga = await getYoga();\n\n // 事前に全画像のサイズを取得(HTTPS対応のため)\n await prefetchAllImageSizes(root, ctx);\n\n const map: YogaNodeMap = new Map();\n\n try {\n const rootYoga = Yoga.Node.create();\n map.set(root, rootYoga);\n\n await buildPomWithYogaTree(root, rootYoga, ctx, map);\n\n // スライド全体サイズを指定\n rootYoga.setWidth(slideSize.w);\n rootYoga.setHeight(slideSize.h);\n\n rootYoga.calculateLayout(slideSize.w, slideSize.h, Yoga.DIRECTION_LTR);\n } catch (e) {\n // 途中で失敗した場合、作成済みの YogaNode を解放してから再 throw\n freeYogaTree(map);\n throw e;\n }\n\n return map;\n}\n\n/**\n * POMNode ツリー内のすべての画像のサイズを事前取得する\n */\nasync function prefetchAllImageSizes(\n node: POMNode,\n ctx: BuildContext,\n): Promise<void> {\n const imageSources = [...new Set(collectImageSources(node))];\n await Promise.all(\n imageSources.map((src) =>\n prefetchImageSize(\n src,\n ctx.imageSizeCache,\n ctx.imageDataCache,\n ctx.diagnostics,\n ),\n ),\n );\n}\n\n/**\n * POMNode ツリー内のすべての画像のsrcを収集する\n */\nfunction collectImageSources(node: POMNode): string[] {\n const sources: string[] = [];\n\n function traverse(n: POMNode) {\n // backgroundImage の src を収集(全ノード共通)\n if (n.backgroundImage) {\n sources.push(n.backgroundImage.src);\n }\n\n const def = getNodeDef(n.type);\n\n // ノード固有の画像ソース収集\n if (def.collectImageSources) {\n sources.push(...def.collectImageSources(n));\n }\n\n // 子要素の再帰\n if (def.category === \"multi-child\" || def.category === \"absolute-child\") {\n const containerNode = n as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n for (const child of containerNode.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return sources;\n}\n\n/**\n * Yogaシングルトン\n */\nlet yogaP: Promise<Yoga> | null = null;\ntype Yoga = Awaited<ReturnType<typeof loadYoga>>;\nasync function getYoga(): Promise<Yoga> {\n if (yogaP === null) yogaP = loadYoga();\n return yogaP;\n}\n\n/**\n * ノードが交差軸方向(幅)で確定サイズを持つかを判定する\n * - 明示的な w がある場合は確定\n * - alignSelf で stretch 以外が指定されている場合は不確定\n * - 親の alignItems で stretch(デフォルト)以外が指定されている場合は不確定\n */\nfunction nodeHasDefiniteWidth(node: POMNode, parentNode?: POMNode): boolean {\n // 明示的な幅がある\n if (node.w !== undefined) return true;\n\n // alignSelf で stretch 以外が明示されている場合は不確定\n if (\n node.alignSelf !== undefined &&\n node.alignSelf !== \"stretch\" &&\n node.alignSelf !== \"auto\"\n ) {\n return false;\n }\n\n // 親がいない場合(ルートノード)は確定\n if (!parentNode) return true;\n\n // 親の alignItems を取得(VStack/HStack のみ持つ)\n let parentAlignItems: AlignItems | undefined;\n if (parentNode.type === \"vstack\") {\n parentAlignItems = parentNode.alignItems;\n } else if (parentNode.type === \"hstack\") {\n parentAlignItems = parentNode.alignItems;\n }\n\n // VStack(column 方向)の子の場合、交差軸は水平方向\n // alignItems が stretch(デフォルト)なら子は親幅に伸長される\n if (parentNode.type === \"vstack\" || parentNode.type === \"layer\") {\n return parentAlignItems === undefined || parentAlignItems === \"stretch\";\n }\n\n // HStack の子の場合、幅は主軸方向で flex により決まるため確定とみなす\n if (parentNode.type === \"hstack\") {\n return true;\n }\n\n return true;\n}\n\n/**\n * POMNode ツリーを再帰的に走査し、YogaNode ツリーを構築する\n */\nasync function buildPomWithYogaTree(\n node: POMNode,\n parentYoga: YogaNode,\n ctx: BuildContext,\n map: YogaNodeMap,\n parentNode?: POMNode,\n grandparentNode?: POMNode,\n) {\n const yoga = await getYoga();\n\n const yn = yoga.Node.create();\n map.set(node, yn); // 対応する YogaNode をマップに登録\n\n await applyStyleToYogaNode(node, yn, ctx);\n\n // HStack/VStack の子要素に flexShrink=1 をデフォルト設定(CSS Flexbox と同じ挙動)\n // 主軸方向で %サイズ + gap がある場合の overflow を防ぐ\n // アイコンは固定サイズのコンテンツなので shrink させない\n if (parentNode?.type === \"hstack\" || parentNode?.type === \"vstack\") {\n yn.setFlexShrink(node.type === \"icon\" ? 0 : 1);\n }\n\n // HStack の子要素で幅が指定されていない場合、grow の比率で余白を配分\n // (grow 未指定は 1 として扱い均等分割)\n // テーブルは setMeasureFunc でカラム幅合計を返すため除外\n // アイコンは固定サイズのコンテンツなので除外\n if (\n parentNode?.type === \"hstack\" &&\n node.w === undefined &&\n node.type !== \"table\" &&\n node.type !== \"icon\"\n ) {\n yn.setFlexGrow(node.grow ?? 1);\n // HStack が確定幅を持つ場合のみ flexBasis=0 で均等分割\n // HStack が auto-sized(親の alignItems が center/start/end 等)の場合、\n // flexBasis=0 だと子要素の自然な幅が失われてレイアウトが崩れるため、\n // flexBasis=auto(デフォルト)のまま維持する\n if (nodeHasDefiniteWidth(parentNode, grandparentNode)) {\n yn.setFlexBasis(0);\n }\n }\n\n parentYoga.insertChild(yn, parentYoga.getChildCount());\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n for (const child of containerNode.children) {\n await buildPomWithYogaTree(child, yn, ctx, map, node, parentNode);\n }\n break;\n }\n case \"leaf\":\n // 子要素なし\n break;\n }\n}\n\n/**\n * node のスタイルを YogaNode に適用する\n */\nasync function applyStyleToYogaNode(\n node: POMNode,\n yn: YogaNode,\n ctx: BuildContext,\n) {\n const yoga = await getYoga();\n\n // デフォルト: 縦並び\n yn.setFlexDirection(yoga.FLEX_DIRECTION_COLUMN);\n\n // width\n if (node.w !== undefined) {\n if (typeof node.w === \"number\") {\n yn.setWidth(node.w);\n } else if (node.w === \"max\") {\n yn.setFlexGrow(1);\n } else if (node.w.endsWith(\"%\")) {\n const percent = parseFloat(node.w);\n yn.setWidthPercent(percent);\n }\n }\n\n // height\n if (node.h !== undefined) {\n if (typeof node.h === \"number\") {\n yn.setHeight(node.h);\n } else if (node.h === \"max\") {\n yn.setFlexGrow(1);\n } else if (node.h.endsWith(\"%\")) {\n const percent = parseFloat(node.h);\n yn.setHeightPercent(percent);\n }\n }\n\n // flex-grow(w=\"max\" / h=\"max\" の setFlexGrow(1) より優先)\n if (node.grow !== undefined) {\n yn.setFlexGrow(node.grow);\n }\n\n // min/max constraints\n if (node.minW !== undefined) {\n yn.setMinWidth(node.minW);\n }\n if (node.maxW !== undefined) {\n yn.setMaxWidth(node.maxW);\n }\n if (node.minH !== undefined) {\n yn.setMinHeight(node.minH);\n }\n if (node.maxH !== undefined) {\n yn.setMaxHeight(node.maxH);\n }\n\n // padding(yoga の未設定 edge は 0 扱いのため、解決済みの 0 を明示設定しても等価)\n if (node.padding !== undefined) {\n const padding = resolveBoxSpacing(node.padding);\n yn.setPadding(yoga.EDGE_TOP, padding.top);\n yn.setPadding(yoga.EDGE_RIGHT, padding.right);\n yn.setPadding(yoga.EDGE_BOTTOM, padding.bottom);\n yn.setPadding(yoga.EDGE_LEFT, padding.left);\n }\n\n // margin\n if (node.margin !== undefined) {\n const margin = resolveBoxSpacing(node.margin);\n yn.setMargin(yoga.EDGE_TOP, margin.top);\n yn.setMargin(yoga.EDGE_RIGHT, margin.right);\n yn.setMargin(yoga.EDGE_BOTTOM, margin.bottom);\n yn.setMargin(yoga.EDGE_LEFT, margin.left);\n }\n\n // position\n if (node.position === \"absolute\") {\n yn.setPositionType(yoga.POSITION_TYPE_ABSOLUTE);\n }\n if (node.top !== undefined) {\n yn.setPosition(yoga.EDGE_TOP, node.top);\n }\n if (node.right !== undefined) {\n yn.setPosition(yoga.EDGE_RIGHT, node.right);\n }\n if (node.bottom !== undefined) {\n yn.setPosition(yoga.EDGE_BOTTOM, node.bottom);\n }\n if (node.left !== undefined) {\n yn.setPosition(yoga.EDGE_LEFT, node.left);\n }\n\n // alignSelf\n if (node.alignSelf !== undefined) {\n switch (node.alignSelf) {\n case \"auto\":\n yn.setAlignSelf(yoga.ALIGN_AUTO);\n break;\n case \"start\":\n yn.setAlignSelf(yoga.ALIGN_FLEX_START);\n break;\n case \"center\":\n yn.setAlignSelf(yoga.ALIGN_CENTER);\n break;\n case \"end\":\n yn.setAlignSelf(yoga.ALIGN_FLEX_END);\n break;\n case \"stretch\":\n yn.setAlignSelf(yoga.ALIGN_STRETCH);\n break;\n }\n }\n\n // ノード固有のスタイル適用(measureFunc 等)\n const def = getNodeDef(node.type);\n if (def.applyYogaStyle) {\n await def.applyYogaStyle(node, yn, yoga, ctx);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"calcYogaLayout.js","names":[],"sources":["../../src/calcYogaLayout/calcYogaLayout.ts"],"sourcesContent":["import type { POMNode, AlignItems } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { YogaNodeMap } from \"./types.ts\";\nimport { Node as YogaNode } from \"yoga-layout\";\nimport { loadYoga } from \"yoga-layout/load\";\nimport { prefetchImageSize } from \"../shared/measureImage.ts\";\nimport { freeYogaTree } from \"../shared/freeYogaTree.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\n/**\n * POMNode ツリーを Yoga でレイアウト計算する\n * POMNode と YogaNode の対応を YogaNodeMap として返す\n *\n * @param root 入力 POMNode ツリーのルート\n * @param slideSize スライド全体のサイズ(px)\n * @param ctx BuildContext\n * @returns YogaNodeMap(POMNode → YogaNode のマッピング)\n */\nexport async function calcYogaLayout(\n root: POMNode,\n slideSize: { w: number; h: number },\n ctx: BuildContext,\n): Promise<YogaNodeMap> {\n const Yoga = await getYoga();\n\n // 事前に全画像のサイズを取得(HTTPS対応のため)\n await prefetchAllImageSizes(root, ctx);\n\n const map: YogaNodeMap = new Map();\n\n try {\n const rootYoga = Yoga.Node.create();\n map.set(root, rootYoga);\n\n await buildPomWithYogaTree(root, rootYoga, ctx, map);\n\n // スライド全体サイズを指定\n rootYoga.setWidth(slideSize.w);\n rootYoga.setHeight(slideSize.h);\n\n rootYoga.calculateLayout(slideSize.w, slideSize.h, Yoga.DIRECTION_LTR);\n } catch (e) {\n // 途中で失敗した場合、作成済みの YogaNode を解放してから再 throw\n freeYogaTree(map);\n throw e;\n }\n\n return map;\n}\n\n/**\n * POMNode ツリー内のすべての画像のサイズを事前取得する\n */\nasync function prefetchAllImageSizes(\n node: POMNode,\n ctx: BuildContext,\n): Promise<void> {\n const imageSources = [...new Set(collectImageSources(node))];\n await Promise.all(\n imageSources.map((src) =>\n prefetchImageSize(\n src,\n ctx.imageSizeCache,\n ctx.imageDataCache,\n ctx.diagnostics,\n ),\n ),\n );\n}\n\n/**\n * POMNode ツリー内のすべての画像のsrcを収集する\n */\nfunction collectImageSources(node: POMNode): string[] {\n const sources: string[] = [];\n\n function traverse(n: POMNode) {\n // backgroundImage の src を収集(全ノード共通)\n if (n.backgroundImage) {\n sources.push(n.backgroundImage.src);\n }\n\n const def = getNodeDef(n.type);\n\n // ノード固有の画像ソース収集\n if (def.collectImageSources) {\n sources.push(...def.collectImageSources(n));\n }\n\n // 子要素の再帰\n if (def.category === \"multi-child\" || def.category === \"absolute-child\") {\n const containerNode = n as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n for (const child of containerNode.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return sources;\n}\n\n/**\n * Yogaシングルトン\n */\nlet yogaP: Promise<Yoga> | null = null;\ntype Yoga = Awaited<ReturnType<typeof loadYoga>>;\nasync function getYoga(): Promise<Yoga> {\n if (yogaP === null) yogaP = loadYoga();\n return yogaP;\n}\n\n/**\n * ノードが交差軸方向(幅)で確定サイズを持つかを判定する\n * - 明示的な w がある場合は確定\n * - alignSelf で stretch 以外が指定されている場合は不確定\n * - 親の alignItems で stretch(デフォルト)以外が指定されている場合は不確定\n */\nfunction nodeHasDefiniteWidth(node: POMNode, parentNode?: POMNode): boolean {\n // 明示的な幅がある\n if (node.w !== undefined) return true;\n\n // alignSelf で stretch 以外が明示されている場合は不確定\n if (\n node.alignSelf !== undefined &&\n node.alignSelf !== \"stretch\" &&\n node.alignSelf !== \"auto\"\n ) {\n return false;\n }\n\n // 親がいない場合(ルートノード)は確定\n if (!parentNode) return true;\n\n // 親の alignItems を取得(VStack/HStack のみ持つ)\n let parentAlignItems: AlignItems | undefined;\n if (parentNode.type === \"vstack\") {\n parentAlignItems = parentNode.alignItems;\n } else if (parentNode.type === \"hstack\") {\n parentAlignItems = parentNode.alignItems;\n }\n\n // VStack(column 方向)の子の場合、交差軸は水平方向\n // alignItems が stretch(デフォルト)なら子は親幅に伸長される\n if (parentNode.type === \"vstack\" || parentNode.type === \"layer\") {\n return parentAlignItems === undefined || parentAlignItems === \"stretch\";\n }\n\n // HStack の子の場合、幅は主軸方向で flex により決まるため確定とみなす\n if (parentNode.type === \"hstack\") {\n return true;\n }\n\n return true;\n}\n\n/**\n * POMNode ツリーを再帰的に走査し、YogaNode ツリーを構築する\n */\nasync function buildPomWithYogaTree(\n node: POMNode,\n parentYoga: YogaNode,\n ctx: BuildContext,\n map: YogaNodeMap,\n parentNode?: POMNode,\n grandparentNode?: POMNode,\n) {\n const yoga = await getYoga();\n\n const yn = yoga.Node.create();\n map.set(node, yn); // 対応する YogaNode をマップに登録\n\n await applyStyleToYogaNode(node, yn, ctx);\n\n // HStack/VStack の子要素に flexShrink=1 をデフォルト設定(CSS Flexbox と同じ挙動)\n // 主軸方向で %サイズ + gap がある場合の overflow を防ぐ\n // アイコンは固定サイズのコンテンツなので shrink させない\n if (parentNode?.type === \"hstack\" || parentNode?.type === \"vstack\") {\n yn.setFlexShrink(node.type === \"icon\" ? 0 : 1);\n }\n\n // HStack の子要素で幅が指定されていない場合、grow の比率で余白を配分\n // (grow 未指定は 1 として扱い均等分割)\n // テーブルは setMeasureFunc でカラム幅合計を返すため除外\n // アイコンは固定サイズのコンテンツなので除外\n if (\n parentNode?.type === \"hstack\" &&\n node.w === undefined &&\n node.type !== \"table\" &&\n node.type !== \"icon\"\n ) {\n yn.setFlexGrow(node.grow ?? 1);\n // HStack が確定幅を持つ場合のみ flexBasis=0 で均等分割\n // HStack が auto-sized(親の alignItems が center/start/end 等)の場合、\n // flexBasis=0 だと子要素の自然な幅が失われてレイアウトが崩れるため、\n // flexBasis=auto(デフォルト)のまま維持する\n if (nodeHasDefiniteWidth(parentNode, grandparentNode)) {\n yn.setFlexBasis(0);\n }\n }\n\n parentYoga.insertChild(yn, parentYoga.getChildCount());\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n POMNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n for (const child of containerNode.children) {\n await buildPomWithYogaTree(child, yn, ctx, map, node, parentNode);\n }\n break;\n }\n case \"leaf\":\n // 子要素なし\n break;\n }\n}\n\n/**\n * node のスタイルを YogaNode に適用する\n */\nasync function applyStyleToYogaNode(\n node: POMNode,\n yn: YogaNode,\n ctx: BuildContext,\n) {\n const yoga = await getYoga();\n\n // デフォルト: 縦並び\n yn.setFlexDirection(yoga.FLEX_DIRECTION_COLUMN);\n\n // width\n if (node.w !== undefined) {\n if (typeof node.w === \"number\") {\n yn.setWidth(node.w);\n } else if (node.w === \"max\") {\n yn.setFlexGrow(1);\n } else if (node.w.endsWith(\"%\")) {\n const percent = parseFloat(node.w);\n yn.setWidthPercent(percent);\n }\n }\n\n // height\n if (node.h !== undefined) {\n if (typeof node.h === \"number\") {\n yn.setHeight(node.h);\n } else if (node.h === \"max\") {\n yn.setFlexGrow(1);\n } else if (node.h.endsWith(\"%\")) {\n const percent = parseFloat(node.h);\n yn.setHeightPercent(percent);\n }\n }\n\n // flex-grow(w=\"max\" / h=\"max\" の setFlexGrow(1) より優先)\n if (node.grow !== undefined) {\n yn.setFlexGrow(node.grow);\n }\n\n // min/max constraints\n if (node.minW !== undefined) {\n yn.setMinWidth(node.minW);\n }\n if (node.maxW !== undefined) {\n yn.setMaxWidth(node.maxW);\n }\n if (node.minH !== undefined) {\n yn.setMinHeight(node.minH);\n }\n if (node.maxH !== undefined) {\n yn.setMaxHeight(node.maxH);\n }\n\n // padding(yoga の未設定 edge は 0 扱いのため、解決済みの 0 を明示設定しても等価)\n if (node.padding !== undefined) {\n const padding = resolveBoxSpacing(node.padding);\n yn.setPadding(yoga.EDGE_TOP, padding.top);\n yn.setPadding(yoga.EDGE_RIGHT, padding.right);\n yn.setPadding(yoga.EDGE_BOTTOM, padding.bottom);\n yn.setPadding(yoga.EDGE_LEFT, padding.left);\n }\n\n // margin\n if (node.margin !== undefined) {\n const margin = resolveBoxSpacing(node.margin);\n yn.setMargin(yoga.EDGE_TOP, margin.top);\n yn.setMargin(yoga.EDGE_RIGHT, margin.right);\n yn.setMargin(yoga.EDGE_BOTTOM, margin.bottom);\n yn.setMargin(yoga.EDGE_LEFT, margin.left);\n }\n\n // position\n if (node.position === \"absolute\") {\n yn.setPositionType(yoga.POSITION_TYPE_ABSOLUTE);\n }\n if (node.top !== undefined) {\n yn.setPosition(yoga.EDGE_TOP, node.top);\n }\n if (node.right !== undefined) {\n yn.setPosition(yoga.EDGE_RIGHT, node.right);\n }\n if (node.bottom !== undefined) {\n yn.setPosition(yoga.EDGE_BOTTOM, node.bottom);\n }\n if (node.left !== undefined) {\n yn.setPosition(yoga.EDGE_LEFT, node.left);\n }\n\n // alignSelf\n if (node.alignSelf !== undefined) {\n switch (node.alignSelf) {\n case \"auto\":\n yn.setAlignSelf(yoga.ALIGN_AUTO);\n break;\n case \"start\":\n yn.setAlignSelf(yoga.ALIGN_FLEX_START);\n break;\n case \"center\":\n yn.setAlignSelf(yoga.ALIGN_CENTER);\n break;\n case \"end\":\n yn.setAlignSelf(yoga.ALIGN_FLEX_END);\n break;\n case \"stretch\":\n yn.setAlignSelf(yoga.ALIGN_STRETCH);\n break;\n }\n }\n\n // ノード固有のスタイル適用(measureFunc 等)\n const def = getNodeDef(node.type);\n if (def.applyYogaStyle) {\n await def.applyYogaStyle(node, yn, yoga, ctx);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/calcYogaLayout/fontLoader.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* opentype.js を使用したフォント読み込みモジュール
|
|
4
|
+
* Node.js とブラウザ両方で動作する
|
|
5
|
+
*/
|
|
6
|
+
interface FontInput {
|
|
7
|
+
name?: string;
|
|
8
|
+
data: ArrayBuffer | Uint8Array;
|
|
9
|
+
weight?: "normal" | "bold" | number;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { FontInput };
|
|
13
|
+
//# sourceMappingURL=fontLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontLoader.d.ts","names":[],"sources":["../../src/calcYogaLayout/fontLoader.ts"],"mappings":";;;;;UAUiB;EACf;EACA,MAAM,cAAc;EACpB"}
|
|
@@ -4,6 +4,73 @@ import * as opentypeModule from "opentype.js";
|
|
|
4
4
|
//#region src/calcYogaLayout/fontLoader.ts
|
|
5
5
|
const opentype = opentypeModule.default ?? opentypeModule;
|
|
6
6
|
const fontCache = /* @__PURE__ */ new Map();
|
|
7
|
+
function normalizeFamilyName(fontFamily) {
|
|
8
|
+
return fontFamily.trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function normalizeWeight(weight, font) {
|
|
11
|
+
if (weight === "bold" || typeof weight === "number" && weight >= 600) return "bold";
|
|
12
|
+
if (weight === "normal" || typeof weight === "number") return "normal";
|
|
13
|
+
const metadataWeight = (font?.tables?.os2)?.usWeightClass;
|
|
14
|
+
if (typeof metadataWeight === "number") return metadataWeight >= 600 ? "bold" : "normal";
|
|
15
|
+
return getLocalizedNames(font, "fontSubfamily").some((name) => /\bbold\b/i.test(name)) ? "bold" : "normal";
|
|
16
|
+
}
|
|
17
|
+
function getLocalizedNames(font, key) {
|
|
18
|
+
const results = /* @__PURE__ */ new Set();
|
|
19
|
+
const collectStrings = (value) => {
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
if (value.length > 0) results.add(value);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!value || typeof value !== "object") return;
|
|
25
|
+
for (const child of Object.values(value)) collectStrings(child);
|
|
26
|
+
};
|
|
27
|
+
const visit = (value) => {
|
|
28
|
+
if (!value || typeof value !== "object") return;
|
|
29
|
+
const record = value;
|
|
30
|
+
if (key in record) collectStrings(record[key]);
|
|
31
|
+
for (const child of Object.values(record)) visit(child);
|
|
32
|
+
};
|
|
33
|
+
visit(font?.names);
|
|
34
|
+
return [...results];
|
|
35
|
+
}
|
|
36
|
+
function toArrayBuffer(data) {
|
|
37
|
+
if (data instanceof ArrayBuffer) return data;
|
|
38
|
+
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
39
|
+
}
|
|
40
|
+
/** build ごとに独立した custom font face の registry。 */
|
|
41
|
+
var FontRegistry = class {
|
|
42
|
+
#families = /* @__PURE__ */ new Map();
|
|
43
|
+
constructor(inputs = []) {
|
|
44
|
+
for (const input of inputs) {
|
|
45
|
+
const font = opentype.parse(toArrayBuffer(input.data));
|
|
46
|
+
const weight = normalizeWeight(input.weight, font);
|
|
47
|
+
const familyNames = /* @__PURE__ */ new Set([
|
|
48
|
+
...input.name ? [input.name] : [],
|
|
49
|
+
...getLocalizedNames(font, "fontFamily"),
|
|
50
|
+
...getLocalizedNames(font, "preferredFamily")
|
|
51
|
+
]);
|
|
52
|
+
for (const familyName of familyNames) {
|
|
53
|
+
const key = normalizeFamilyName(familyName);
|
|
54
|
+
if (!key) continue;
|
|
55
|
+
const faces = this.#families.get(key) ?? {};
|
|
56
|
+
faces[weight] = font;
|
|
57
|
+
this.#families.set(key, faces);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
hasFont(fontFamily, weight) {
|
|
62
|
+
return this.resolve(fontFamily, weight) !== void 0;
|
|
63
|
+
}
|
|
64
|
+
measureTextWidth(text, fontFamily, fontSizePx, weight) {
|
|
65
|
+
return this.resolve(fontFamily, weight)?.getAdvanceWidth(text, fontSizePx, { kerning: true });
|
|
66
|
+
}
|
|
67
|
+
resolve(fontFamily, weight) {
|
|
68
|
+
const faces = this.#families.get(normalizeFamilyName(fontFamily));
|
|
69
|
+
if (!faces) return void 0;
|
|
70
|
+
if (weight === "bold") return faces.bold ?? faces.normal;
|
|
71
|
+
return faces.normal;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
7
74
|
/**
|
|
8
75
|
* Base64 文字列を ArrayBuffer に変換する
|
|
9
76
|
* Node.js とブラウザ両方で動作する
|
|
@@ -33,12 +100,12 @@ function getFont(weight) {
|
|
|
33
100
|
return font;
|
|
34
101
|
}
|
|
35
102
|
/** バンドル済みフォント名の一覧 */
|
|
36
|
-
const BUNDLED_FONT_NAMES = /* @__PURE__ */ new Set(["Noto Sans JP"]);
|
|
103
|
+
const BUNDLED_FONT_NAMES = /* @__PURE__ */ new Set([normalizeFamilyName("Noto Sans JP")]);
|
|
37
104
|
/**
|
|
38
105
|
* 指定されたフォントがバンドル済みかどうかを判定する
|
|
39
106
|
*/
|
|
40
107
|
function isBundledFont(fontFamily) {
|
|
41
|
-
return BUNDLED_FONT_NAMES.has(fontFamily);
|
|
108
|
+
return BUNDLED_FONT_NAMES.has(normalizeFamilyName(fontFamily));
|
|
42
109
|
}
|
|
43
110
|
/**
|
|
44
111
|
* 指定したテキストの幅を計測する
|
|
@@ -103,6 +170,6 @@ function measureFontLineHeightRatio(weight) {
|
|
|
103
170
|
return (os2.usWinAscent + os2.usWinDescent) / upm;
|
|
104
171
|
}
|
|
105
172
|
//#endregion
|
|
106
|
-
export { isBundledFont, measureFontLineHeightRatio, measureFontVerticalMetricsRatio, measureTextWidth };
|
|
173
|
+
export { FontRegistry, isBundledFont, measureFontLineHeightRatio, measureFontVerticalMetricsRatio, measureTextWidth };
|
|
107
174
|
|
|
108
175
|
//# sourceMappingURL=fontLoader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fontLoader.js","names":[],"sources":["../../src/calcYogaLayout/fontLoader.ts"],"sourcesContent":["/**\n * opentype.js を使用したフォント読み込みモジュール\n * Node.js とブラウザ両方で動作する\n */\n\nimport type { Font } from \"opentype.js\";\nimport * as opentypeModule from \"opentype.js\";\nimport { NOTO_SANS_JP_REGULAR_BASE64 } from \"./fonts/notoSansJPRegular.ts\";\nimport { NOTO_SANS_JP_BOLD_BASE64 } from \"./fonts/notoSansJPBold.ts\";\n\n// opentype.js 2.0 は ESM ビルドで named export のみを提供する一方、\n// CJS UMD ビルドでは module.exports = factory() の動的構造のため\n// Node ESM から取り込むと named exports が静的解析できない。\n// どちらの形でも動くよう default プロパティを優先して unwrap する。\nconst opentype =\n (opentypeModule as unknown as { default?: typeof opentypeModule }).default ??\n opentypeModule;\n\n// フォントキャッシュ\nconst fontCache = new Map<string, Font>();\n\n/**\n * Base64 文字列を ArrayBuffer に変換する\n * Node.js とブラウザ両方で動作する\n */\nfunction base64ToArrayBuffer(base64: string): ArrayBuffer {\n // Node.js 環境\n if (typeof Buffer !== \"undefined\") {\n const buffer = Buffer.from(base64, \"base64\");\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n }\n // ブラウザ環境\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * フォントを取得する(キャッシュ付き)\n * @param weight フォントウェイト (\"normal\" or \"bold\")\n * @returns opentype.js の Font オブジェクト\n */\nfunction getFont(weight: \"normal\" | \"bold\"): Font {\n const cacheKey = weight;\n\n // キャッシュがあればそれを返す\n const cached = fontCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n // Base64 データを選択\n const base64 =\n weight === \"bold\" ? NOTO_SANS_JP_BOLD_BASE64 : NOTO_SANS_JP_REGULAR_BASE64;\n\n // ArrayBuffer に変換してパース\n const buffer = base64ToArrayBuffer(base64);\n const font = opentype.parse(buffer);\n\n // キャッシュに保存\n fontCache.set(cacheKey, font);\n\n return font;\n}\n\n/** バンドル済みフォント名の一覧 */\nconst BUNDLED_FONT_NAMES = new Set([\"Noto Sans JP\"]);\n\n/**\n * 指定されたフォントがバンドル済みかどうかを判定する\n */\nexport function isBundledFont(fontFamily: string): boolean {\n return BUNDLED_FONT_NAMES.has(fontFamily);\n}\n\n/**\n * 指定したテキストの幅を計測する\n * @param text 計測するテキスト\n * @param fontSizePx フォントサイズ(ピクセル)\n * @param weight フォントウェイト\n * @returns テキスト幅(ピクセル)\n */\nexport function measureTextWidth(\n text: string,\n fontSizePx: number,\n weight: \"normal\" | \"bold\",\n): number {\n const font = getFont(weight);\n return font.getAdvanceWidth(text, fontSizePx, { kerning: true });\n}\n\n/**\n * フォントの縦方向メトリクスを fontSizePx に対する比率で取得する\n *\n * - typoAscender / typoDescender: グリフ ink のおおよその上端・下端\n * (descender は正の値に符号反転して返す)\n * - winDescent: レンダラが固定行送り (spcPts) のときに行下端から\n * baseline までの距離として確保する descent\n *\n * バンドル外フォント使用時もバンドルフォント (Noto Sans JP) の値を\n * 近似値として使う想定 (テキスト幅計測と同じ方針)。\n *\n * @param weight フォントウェイト\n * @returns 各メトリクスの fontSizePx に対する比率\n */\nexport function measureFontVerticalMetricsRatio(weight: \"normal\" | \"bold\"): {\n typoAscender: number;\n typoDescender: number;\n winDescent: number;\n} {\n const font = getFont(weight);\n const upm = font.unitsPerEm;\n const os2 = font.tables?.os2;\n\n if (!os2) {\n // メトリクスが取れない場合は Noto Sans JP 相当の値で近似する\n return { typoAscender: 0.88, typoDescender: 0.12, winDescent: 0.288 };\n }\n\n return {\n typoAscender: os2.sTypoAscender / upm,\n typoDescender: -os2.sTypoDescender / upm,\n winDescent: os2.usWinDescent / upm,\n };\n}\n\n/**\n * フォントの自然な行高さ比率を取得する\n *\n * PowerPoint の lineHeight はフォントサイズではなく、\n * フォントメトリクス(ascent + descent)に対する倍率として適用される。\n * この関数は fontSizePx に対する自然な行高さの比率を返す。\n *\n * - USE_TYPO_METRICS (fsSelection bit 7) が設定されている場合:\n * sTypoAscender, sTypoDescender, sTypoLineGap を使用\n * - 設定されていない場合:\n * usWinAscent, usWinDescent を使用\n *\n * @param weight フォントウェイト\n * @returns fontSizePx に対する行高さの比率(例: 1.448)\n */\nexport function measureFontLineHeightRatio(weight: \"normal\" | \"bold\"): number {\n const font = getFont(weight);\n const upm = font.unitsPerEm;\n const os2 = font.tables?.os2;\n\n if (!os2) {\n return 1.0;\n }\n\n const useTypoMetrics = Boolean(os2.fsSelection & (1 << 7));\n\n if (useTypoMetrics) {\n return (os2.sTypoAscender - os2.sTypoDescender + os2.sTypoLineGap) / upm;\n }\n\n return (os2.usWinAscent + os2.usWinDescent) / upm;\n}\n"],"mappings":";;;;AAcA,MAAM,WACH,eAAkE,WACnE;AAGF,MAAM,4BAAY,IAAI,IAAkB;;;;;AAMxC,SAAS,oBAAoB,QAA6B;CAExD,IAAI,OAAO,WAAW,aAAa;EACjC,MAAM,SAAS,OAAO,KAAK,QAAQ,QAAQ;EAC3C,OAAO,OAAO,OAAO,MACnB,OAAO,YACP,OAAO,aAAa,OAAO,UAC7B;CACF;CAEA,MAAM,eAAe,KAAK,MAAM;CAChC,MAAM,QAAQ,IAAI,WAAW,aAAa,MAAM;CAChD,KAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KACvC,MAAM,KAAK,aAAa,WAAW,CAAC;CAEtC,OAAO,MAAM;AACf;;;;;;AAOA,SAAS,QAAQ,QAAiC;CAChD,MAAM,WAAW;CAGjB,MAAM,SAAS,UAAU,IAAI,QAAQ;CACrC,IAAI,QACF,OAAO;CAQT,MAAM,SAAS,oBAHb,WAAW,SAAS,2BAA2B,2BAGR;CACzC,MAAM,OAAO,SAAS,MAAM,MAAM;CAGlC,UAAU,IAAI,UAAU,IAAI;CAE5B,OAAO;AACT;;AAGA,MAAM,qCAAqB,IAAI,IAAI,CAAC,cAAc,CAAC;;;;AAKnD,SAAgB,cAAc,YAA6B;CACzD,OAAO,mBAAmB,IAAI,UAAU;AAC1C;;;;;;;;AASA,SAAgB,iBACd,MACA,YACA,QACQ;CAER,OADa,QAAQ,MACX,CAAC,CAAC,gBAAgB,MAAM,YAAY,EAAE,SAAS,KAAK,CAAC;AACjE;;;;;;;;;;;;;;;AAgBA,SAAgB,gCAAgC,QAI9C;CACA,MAAM,OAAO,QAAQ,MAAM;CAC3B,MAAM,MAAM,KAAK;CACjB,MAAM,MAAM,KAAK,QAAQ;CAEzB,IAAI,CAAC,KAEH,OAAO;EAAE,cAAc;EAAM,eAAe;EAAM,YAAY;CAAM;CAGtE,OAAO;EACL,cAAc,IAAI,gBAAgB;EAClC,eAAe,CAAC,IAAI,iBAAiB;EACrC,YAAY,IAAI,eAAe;CACjC;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,2BAA2B,QAAmC;CAC5E,MAAM,OAAO,QAAQ,MAAM;CAC3B,MAAM,MAAM,KAAK;CACjB,MAAM,MAAM,KAAK,QAAQ;CAEzB,IAAI,CAAC,KACH,OAAO;CAKT,IAFuB,QAAQ,IAAI,cAAe,GAEjC,GACf,QAAQ,IAAI,gBAAgB,IAAI,iBAAiB,IAAI,gBAAgB;CAGvE,QAAQ,IAAI,cAAc,IAAI,gBAAgB;AAChD"}
|
|
1
|
+
{"version":3,"file":"fontLoader.js","names":["#families"],"sources":["../../src/calcYogaLayout/fontLoader.ts"],"sourcesContent":["/**\n * opentype.js を使用したフォント読み込みモジュール\n * Node.js とブラウザ両方で動作する\n */\n\nimport type { Font } from \"opentype.js\";\nimport * as opentypeModule from \"opentype.js\";\nimport { NOTO_SANS_JP_REGULAR_BASE64 } from \"./fonts/notoSansJPRegular.ts\";\nimport { NOTO_SANS_JP_BOLD_BASE64 } from \"./fonts/notoSansJPBold.ts\";\n\nexport interface FontInput {\n name?: string;\n data: ArrayBuffer | Uint8Array;\n weight?: \"normal\" | \"bold\" | number;\n}\n\ntype FontWeight = \"normal\" | \"bold\";\ntype FontFaces = Partial<Record<FontWeight, Font>>;\n\n// opentype.js 2.0 は ESM ビルドで named export のみを提供する一方、\n// CJS UMD ビルドでは module.exports = factory() の動的構造のため\n// Node ESM から取り込むと named exports が静的解析できない。\n// どちらの形でも動くよう default プロパティを優先して unwrap する。\nconst opentype =\n (opentypeModule as unknown as { default?: typeof opentypeModule }).default ??\n opentypeModule;\n\n// フォントキャッシュ\nconst fontCache = new Map<string, Font>();\n\nfunction normalizeFamilyName(fontFamily: string): string {\n return fontFamily.trim().toLowerCase();\n}\n\nfunction normalizeWeight(weight: FontInput[\"weight\"], font?: Font): FontWeight {\n if (weight === \"bold\" || (typeof weight === \"number\" && weight >= 600)) {\n return \"bold\";\n }\n if (weight === \"normal\" || typeof weight === \"number\") {\n return \"normal\";\n }\n\n const metadataWeight = (\n font?.tables?.os2 as { usWeightClass?: unknown } | undefined\n )?.usWeightClass;\n if (typeof metadataWeight === \"number\") {\n return metadataWeight >= 600 ? \"bold\" : \"normal\";\n }\n\n const subfamilyNames = getLocalizedNames(font, \"fontSubfamily\");\n return subfamilyNames.some((name) => /\\bbold\\b/i.test(name))\n ? \"bold\"\n : \"normal\";\n}\n\nfunction getLocalizedNames(font: Font | undefined, key: string): string[] {\n const results = new Set<string>();\n const collectStrings = (value: unknown): void => {\n if (typeof value === \"string\") {\n if (value.length > 0) results.add(value);\n return;\n }\n if (!value || typeof value !== \"object\") return;\n for (const child of Object.values(value)) collectStrings(child);\n };\n const visit = (value: unknown): void => {\n if (!value || typeof value !== \"object\") return;\n const record = value as Record<string, unknown>;\n if (key in record) collectStrings(record[key]);\n for (const child of Object.values(record)) visit(child);\n };\n\n visit(font?.names);\n return [...results];\n}\n\nfunction toArrayBuffer(data: ArrayBuffer | Uint8Array): ArrayBuffer {\n if (data instanceof ArrayBuffer) return data;\n return data.buffer.slice(\n data.byteOffset,\n data.byteOffset + data.byteLength,\n ) as ArrayBuffer;\n}\n\n/** build ごとに独立した custom font face の registry。 */\nexport class FontRegistry {\n readonly #families = new Map<string, FontFaces>();\n\n constructor(inputs: readonly FontInput[] = []) {\n for (const input of inputs) {\n const font = opentype.parse(toArrayBuffer(input.data));\n const weight = normalizeWeight(input.weight, font);\n const familyNames = new Set([\n ...(input.name ? [input.name] : []),\n ...getLocalizedNames(font, \"fontFamily\"),\n ...getLocalizedNames(font, \"preferredFamily\"),\n ]);\n\n for (const familyName of familyNames) {\n const key = normalizeFamilyName(familyName);\n if (!key) continue;\n const faces = this.#families.get(key) ?? {};\n faces[weight] = font;\n this.#families.set(key, faces);\n }\n }\n }\n\n hasFont(fontFamily: string, weight: FontWeight): boolean {\n return this.resolve(fontFamily, weight) !== undefined;\n }\n\n measureTextWidth(\n text: string,\n fontFamily: string,\n fontSizePx: number,\n weight: FontWeight,\n ): number | undefined {\n return this.resolve(fontFamily, weight)?.getAdvanceWidth(text, fontSizePx, {\n kerning: true,\n });\n }\n\n private resolve(fontFamily: string, weight: FontWeight): Font | undefined {\n const faces = this.#families.get(normalizeFamilyName(fontFamily));\n if (!faces) return undefined;\n if (weight === \"bold\") return faces.bold ?? faces.normal;\n return faces.normal;\n }\n}\n\n/**\n * Base64 文字列を ArrayBuffer に変換する\n * Node.js とブラウザ両方で動作する\n */\nfunction base64ToArrayBuffer(base64: string): ArrayBuffer {\n // Node.js 環境\n if (typeof Buffer !== \"undefined\") {\n const buffer = Buffer.from(base64, \"base64\");\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n );\n }\n // ブラウザ環境\n const binaryString = atob(base64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * フォントを取得する(キャッシュ付き)\n * @param weight フォントウェイト (\"normal\" or \"bold\")\n * @returns opentype.js の Font オブジェクト\n */\nfunction getFont(weight: \"normal\" | \"bold\"): Font {\n const cacheKey = weight;\n\n // キャッシュがあればそれを返す\n const cached = fontCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n // Base64 データを選択\n const base64 =\n weight === \"bold\" ? NOTO_SANS_JP_BOLD_BASE64 : NOTO_SANS_JP_REGULAR_BASE64;\n\n // ArrayBuffer に変換してパース\n const buffer = base64ToArrayBuffer(base64);\n const font = opentype.parse(buffer);\n\n // キャッシュに保存\n fontCache.set(cacheKey, font);\n\n return font;\n}\n\n/** バンドル済みフォント名の一覧 */\nconst BUNDLED_FONT_NAMES = new Set([normalizeFamilyName(\"Noto Sans JP\")]);\n\n/**\n * 指定されたフォントがバンドル済みかどうかを判定する\n */\nexport function isBundledFont(fontFamily: string): boolean {\n return BUNDLED_FONT_NAMES.has(normalizeFamilyName(fontFamily));\n}\n\n/**\n * 指定したテキストの幅を計測する\n * @param text 計測するテキスト\n * @param fontSizePx フォントサイズ(ピクセル)\n * @param weight フォントウェイト\n * @returns テキスト幅(ピクセル)\n */\nexport function measureTextWidth(\n text: string,\n fontSizePx: number,\n weight: \"normal\" | \"bold\",\n): number {\n const font = getFont(weight);\n return font.getAdvanceWidth(text, fontSizePx, { kerning: true });\n}\n\n/**\n * フォントの縦方向メトリクスを fontSizePx に対する比率で取得する\n *\n * - typoAscender / typoDescender: グリフ ink のおおよその上端・下端\n * (descender は正の値に符号反転して返す)\n * - winDescent: レンダラが固定行送り (spcPts) のときに行下端から\n * baseline までの距離として確保する descent\n *\n * バンドル外フォント使用時もバンドルフォント (Noto Sans JP) の値を\n * 近似値として使う想定 (テキスト幅計測と同じ方針)。\n *\n * @param weight フォントウェイト\n * @returns 各メトリクスの fontSizePx に対する比率\n */\nexport function measureFontVerticalMetricsRatio(weight: \"normal\" | \"bold\"): {\n typoAscender: number;\n typoDescender: number;\n winDescent: number;\n} {\n const font = getFont(weight);\n const upm = font.unitsPerEm;\n const os2 = font.tables?.os2;\n\n if (!os2) {\n // メトリクスが取れない場合は Noto Sans JP 相当の値で近似する\n return { typoAscender: 0.88, typoDescender: 0.12, winDescent: 0.288 };\n }\n\n return {\n typoAscender: os2.sTypoAscender / upm,\n typoDescender: -os2.sTypoDescender / upm,\n winDescent: os2.usWinDescent / upm,\n };\n}\n\n/**\n * フォントの自然な行高さ比率を取得する\n *\n * PowerPoint の lineHeight はフォントサイズではなく、\n * フォントメトリクス(ascent + descent)に対する倍率として適用される。\n * この関数は fontSizePx に対する自然な行高さの比率を返す。\n *\n * - USE_TYPO_METRICS (fsSelection bit 7) が設定されている場合:\n * sTypoAscender, sTypoDescender, sTypoLineGap を使用\n * - 設定されていない場合:\n * usWinAscent, usWinDescent を使用\n *\n * @param weight フォントウェイト\n * @returns fontSizePx に対する行高さの比率(例: 1.448)\n */\nexport function measureFontLineHeightRatio(weight: \"normal\" | \"bold\"): number {\n const font = getFont(weight);\n const upm = font.unitsPerEm;\n const os2 = font.tables?.os2;\n\n if (!os2) {\n return 1.0;\n }\n\n const useTypoMetrics = Boolean(os2.fsSelection & (1 << 7));\n\n if (useTypoMetrics) {\n return (os2.sTypoAscender - os2.sTypoDescender + os2.sTypoLineGap) / upm;\n }\n\n return (os2.usWinAscent + os2.usWinDescent) / upm;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measureText.d.ts","names":[],"sources":["../../src/calcYogaLayout/measureText.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"measureText.d.ts","names":[],"sources":["../../src/calcYogaLayout/measureText.ts"],"mappings":";KAcY"}
|
|
@@ -86,28 +86,28 @@ function calculateResult(lines, opts) {
|
|
|
86
86
|
* fontWeight を "normal" | "bold" に正規化する
|
|
87
87
|
*/
|
|
88
88
|
function normalizeFontWeight(weight) {
|
|
89
|
-
if (weight === "bold" || weight ===
|
|
89
|
+
if (weight === "bold" || typeof weight === "number" && weight >= 600) return "bold";
|
|
90
90
|
return "normal";
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
93
|
* テキストを折り返し付きでレイアウトし、そのサイズを測定する
|
|
94
94
|
*/
|
|
95
|
-
function measureText(text, maxWidthPx, opts, mode = "auto") {
|
|
95
|
+
function measureText(text, maxWidthPx, opts, mode = "auto", fontRegistry) {
|
|
96
96
|
if ((() => {
|
|
97
97
|
switch (mode) {
|
|
98
98
|
case "opentype": return false;
|
|
99
99
|
case "fallback": return true;
|
|
100
|
-
case "auto": return !isBundledFont(opts.fontFamily);
|
|
100
|
+
case "auto": return !isBundledFont(opts.fontFamily) && !fontRegistry?.hasFont(opts.fontFamily, normalizeFontWeight(opts.fontWeight));
|
|
101
101
|
}
|
|
102
102
|
})()) return measureTextFallback(text, maxWidthPx, opts);
|
|
103
|
-
return measureTextWithOpentype(text, maxWidthPx, opts);
|
|
103
|
+
return measureTextWithOpentype(text, maxWidthPx, opts, fontRegistry);
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* opentype.js を使ったテキスト計測
|
|
107
107
|
*/
|
|
108
|
-
function measureTextWithOpentype(text, maxWidthPx, opts) {
|
|
108
|
+
function measureTextWithOpentype(text, maxWidthPx, opts, fontRegistry) {
|
|
109
109
|
const fontWeight = normalizeFontWeight(opts.fontWeight);
|
|
110
|
-
return calculateResult(wrapText(text, maxWidthPx, withLetterSpacing((t) => measureTextWidth(t, opts.fontSizePx, fontWeight), opts.letterSpacingPx)), opts);
|
|
110
|
+
return calculateResult(wrapText(text, maxWidthPx, withLetterSpacing((t) => fontRegistry?.measureTextWidth(t, opts.fontFamily, opts.fontSizePx, fontWeight) ?? measureTextWidth(t, opts.fontSizePx, fontWeight), opts.letterSpacingPx)), opts);
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
113
|
* フォールバック計算を使ったテキスト計測
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measureText.js","names":["measureTextWidthOpentype"],"sources":["../../src/calcYogaLayout/measureText.ts"],"sourcesContent":["import {\n measureTextWidth as measureTextWidthOpentype,\n isBundledFont,\n} from \"./fontLoader.ts\";\n\ntype MeasureOptions = {\n fontFamily: string;\n fontSizePx: number;\n fontWeight?: \"normal\" | \"bold\" | number;\n lineHeight?: number;\n letterSpacingPx?: number;\n};\n\nexport type TextMeasurementMode = \"opentype\" | \"fallback\" | \"auto\";\n\n/**\n * 文字がCJK(日本語・中国語・韓国語)文字かどうかを判定する\n */\nfunction isCJKChar(char: string): boolean {\n const code = char.codePointAt(0);\n if (code === undefined) return false;\n\n // CJK統合漢字\n if (code >= 0x4e00 && code <= 0x9fff) return true;\n // CJK統合漢字拡張A\n if (code >= 0x3400 && code <= 0x4dbf) return true;\n // CJK統合漢字拡張B-F\n if (code >= 0x20000 && code <= 0x2ebef) return true;\n // ひらがな\n if (code >= 0x3040 && code <= 0x309f) return true;\n // カタカナ\n if (code >= 0x30a0 && code <= 0x30ff) return true;\n // 全角英数字・記号\n if (code >= 0xff00 && code <= 0xffef) return true;\n // CJK記号\n if (code >= 0x3000 && code <= 0x303f) return true;\n\n return false;\n}\n\n/**\n * フォールバック計算で文字の幅を推定する\n * - CJK文字: 1em(= fontSizePx)\n * - 英数字・半角記号: 0.5em\n */\nfunction estimateCharWidth(char: string, fontSizePx: number): number {\n if (isCJKChar(char)) {\n return fontSizePx; // 1em\n }\n return fontSizePx * 0.5; // 0.5em\n}\n\n/**\n * フォールバック計算でテキスト幅を推定する\n */\nfunction estimateTextWidth(text: string, fontSizePx: number): number {\n let width = 0;\n for (const char of text) {\n width += estimateCharWidth(char, fontSizePx);\n }\n return width;\n}\n\n/**\n * テキスト幅計測関数の型\n */\ntype MeasureTextWidthFn = (text: string) => number;\n\n/**\n * 計測関数に letterSpacing(文字数 × 字間 px)の加算を合成する\n */\nfunction withLetterSpacing(\n measureWidth: MeasureTextWidthFn,\n letterSpacingPx: number | undefined,\n): MeasureTextWidthFn {\n if (!letterSpacingPx) return measureWidth;\n return (text) =>\n measureWidth(text) + Array.from(text).length * letterSpacingPx;\n}\n\n/**\n * テキストを折り返して行ごとの幅を計算する\n */\nfunction wrapText(\n text: string,\n maxWidthPx: number,\n measureWidth: MeasureTextWidthFn,\n): { widthPx: number }[] {\n const paragraphs = text.split(\"\\n\");\n const lines: { widthPx: number }[] = [];\n\n for (const paragraph of paragraphs) {\n if (paragraph === \"\") {\n lines.push({ widthPx: 0 });\n continue;\n }\n\n const words = splitForWrap(paragraph);\n let current = \"\";\n let currentWidth = 0;\n\n for (const word of words) {\n const candidate = current ? current + word : word;\n const w = measureWidth(candidate);\n\n if (w <= maxWidthPx || !current) {\n current = candidate;\n currentWidth = w;\n } else {\n lines.push({ widthPx: currentWidth });\n current = word;\n currentWidth = measureWidth(word);\n }\n }\n\n if (current) {\n lines.push({ widthPx: currentWidth });\n }\n }\n\n return lines;\n}\n\n/**\n * 行情報から最終的なサイズを計算する\n */\nfunction calculateResult(\n lines: { widthPx: number }[],\n opts: MeasureOptions,\n): { widthPx: number; heightPx: number } {\n const lineHeightRatio = opts.lineHeight ?? 1.3;\n const lineHeightPx = opts.fontSizePx * lineHeightRatio;\n const widthPx = lines.length ? Math.max(...lines.map((l) => l.widthPx)) : 0;\n const heightPx = lines.length * lineHeightPx;\n // 端数切り上げ+余裕分 10px を足す\n return { widthPx: widthPx + 10, heightPx };\n}\n\n/**\n * fontWeight を \"normal\" | \"bold\" に正規化する\n */\nfunction normalizeFontWeight(\n weight: \"normal\" | \"bold\" | number | undefined,\n): \"normal\" | \"bold\" {\n if (weight === \"bold\" || weight ===
|
|
1
|
+
{"version":3,"file":"measureText.js","names":["measureTextWidthOpentype"],"sources":["../../src/calcYogaLayout/measureText.ts"],"sourcesContent":["import {\n measureTextWidth as measureTextWidthOpentype,\n isBundledFont,\n type FontRegistry,\n} from \"./fontLoader.ts\";\n\ntype MeasureOptions = {\n fontFamily: string;\n fontSizePx: number;\n fontWeight?: \"normal\" | \"bold\" | number;\n lineHeight?: number;\n letterSpacingPx?: number;\n};\n\nexport type TextMeasurementMode = \"opentype\" | \"fallback\" | \"auto\";\n\n/**\n * 文字がCJK(日本語・中国語・韓国語)文字かどうかを判定する\n */\nfunction isCJKChar(char: string): boolean {\n const code = char.codePointAt(0);\n if (code === undefined) return false;\n\n // CJK統合漢字\n if (code >= 0x4e00 && code <= 0x9fff) return true;\n // CJK統合漢字拡張A\n if (code >= 0x3400 && code <= 0x4dbf) return true;\n // CJK統合漢字拡張B-F\n if (code >= 0x20000 && code <= 0x2ebef) return true;\n // ひらがな\n if (code >= 0x3040 && code <= 0x309f) return true;\n // カタカナ\n if (code >= 0x30a0 && code <= 0x30ff) return true;\n // 全角英数字・記号\n if (code >= 0xff00 && code <= 0xffef) return true;\n // CJK記号\n if (code >= 0x3000 && code <= 0x303f) return true;\n\n return false;\n}\n\n/**\n * フォールバック計算で文字の幅を推定する\n * - CJK文字: 1em(= fontSizePx)\n * - 英数字・半角記号: 0.5em\n */\nfunction estimateCharWidth(char: string, fontSizePx: number): number {\n if (isCJKChar(char)) {\n return fontSizePx; // 1em\n }\n return fontSizePx * 0.5; // 0.5em\n}\n\n/**\n * フォールバック計算でテキスト幅を推定する\n */\nfunction estimateTextWidth(text: string, fontSizePx: number): number {\n let width = 0;\n for (const char of text) {\n width += estimateCharWidth(char, fontSizePx);\n }\n return width;\n}\n\n/**\n * テキスト幅計測関数の型\n */\ntype MeasureTextWidthFn = (text: string) => number;\n\n/**\n * 計測関数に letterSpacing(文字数 × 字間 px)の加算を合成する\n */\nfunction withLetterSpacing(\n measureWidth: MeasureTextWidthFn,\n letterSpacingPx: number | undefined,\n): MeasureTextWidthFn {\n if (!letterSpacingPx) return measureWidth;\n return (text) =>\n measureWidth(text) + Array.from(text).length * letterSpacingPx;\n}\n\n/**\n * テキストを折り返して行ごとの幅を計算する\n */\nfunction wrapText(\n text: string,\n maxWidthPx: number,\n measureWidth: MeasureTextWidthFn,\n): { widthPx: number }[] {\n const paragraphs = text.split(\"\\n\");\n const lines: { widthPx: number }[] = [];\n\n for (const paragraph of paragraphs) {\n if (paragraph === \"\") {\n lines.push({ widthPx: 0 });\n continue;\n }\n\n const words = splitForWrap(paragraph);\n let current = \"\";\n let currentWidth = 0;\n\n for (const word of words) {\n const candidate = current ? current + word : word;\n const w = measureWidth(candidate);\n\n if (w <= maxWidthPx || !current) {\n current = candidate;\n currentWidth = w;\n } else {\n lines.push({ widthPx: currentWidth });\n current = word;\n currentWidth = measureWidth(word);\n }\n }\n\n if (current) {\n lines.push({ widthPx: currentWidth });\n }\n }\n\n return lines;\n}\n\n/**\n * 行情報から最終的なサイズを計算する\n */\nfunction calculateResult(\n lines: { widthPx: number }[],\n opts: MeasureOptions,\n): { widthPx: number; heightPx: number } {\n const lineHeightRatio = opts.lineHeight ?? 1.3;\n const lineHeightPx = opts.fontSizePx * lineHeightRatio;\n const widthPx = lines.length ? Math.max(...lines.map((l) => l.widthPx)) : 0;\n const heightPx = lines.length * lineHeightPx;\n // 端数切り上げ+余裕分 10px を足す\n return { widthPx: widthPx + 10, heightPx };\n}\n\n/**\n * fontWeight を \"normal\" | \"bold\" に正規化する\n */\nfunction normalizeFontWeight(\n weight: \"normal\" | \"bold\" | number | undefined,\n): \"normal\" | \"bold\" {\n if (weight === \"bold\" || (typeof weight === \"number\" && weight >= 600)) {\n return \"bold\";\n }\n return \"normal\";\n}\n\n/**\n * テキストを折り返し付きでレイアウトし、そのサイズを測定する\n */\nexport function measureText(\n text: string,\n maxWidthPx: number,\n opts: MeasureOptions,\n mode: TextMeasurementMode = \"auto\",\n fontRegistry?: FontRegistry,\n): {\n widthPx: number;\n heightPx: number;\n} {\n // 計測方法を決定\n // \"opentype\" / \"fallback\" が明示指定された場合はそれを優先\n // \"auto\" の場合はバンドル外フォントならフォールバック計測を使用\n const shouldUseFallback = (() => {\n switch (mode) {\n case \"opentype\":\n return false;\n case \"fallback\":\n return true;\n case \"auto\":\n return (\n !isBundledFont(opts.fontFamily) &&\n !fontRegistry?.hasFont(\n opts.fontFamily,\n normalizeFontWeight(opts.fontWeight),\n )\n );\n }\n })();\n\n if (shouldUseFallback) {\n return measureTextFallback(text, maxWidthPx, opts);\n }\n\n return measureTextWithOpentype(text, maxWidthPx, opts, fontRegistry);\n}\n\n/**\n * opentype.js を使ったテキスト計測\n */\nfunction measureTextWithOpentype(\n text: string,\n maxWidthPx: number,\n opts: MeasureOptions,\n fontRegistry?: FontRegistry,\n): { widthPx: number; heightPx: number } {\n const fontWeight = normalizeFontWeight(opts.fontWeight);\n const lines = wrapText(\n text,\n maxWidthPx,\n withLetterSpacing(\n (t) =>\n fontRegistry?.measureTextWidth(\n t,\n opts.fontFamily,\n opts.fontSizePx,\n fontWeight,\n ) ?? measureTextWidthOpentype(t, opts.fontSizePx, fontWeight),\n opts.letterSpacingPx,\n ),\n );\n return calculateResult(lines, opts);\n}\n\n/**\n * フォールバック計算を使ったテキスト計測\n */\nfunction measureTextFallback(\n text: string,\n maxWidthPx: number,\n opts: MeasureOptions,\n): { widthPx: number; heightPx: number } {\n const { fontSizePx } = opts;\n const lines = wrapText(\n text,\n maxWidthPx,\n withLetterSpacing(\n (t) => estimateTextWidth(t, fontSizePx),\n opts.letterSpacingPx,\n ),\n );\n return calculateResult(lines, opts);\n}\n\n// ラップ用の分割ロジック\n// - 英文: 空白で分割しつつ、空白も行末に残す\n// - 日本語: とりあえず 1 文字ずつ(必要なら賢くする)\nfunction splitForWrap(text: string): string[] {\n // 超雑実装:全角ひらがな・カタカナ・漢字が多そうなら 1 文字ずつ、それ以外は空白区切り\n const hasCJK = /[\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Han}]/u.test(\n text,\n );\n\n if (hasCJK) {\n return Array.from(text); // 1 glyph ≒ 1 文字として扱う\n }\n\n // 英文用:単語 + 後続スペースをトークンにする\n const tokens: string[] = [];\n const re = /(\\S+\\s*|\\s+)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text))) {\n tokens.push(m[0]);\n }\n return tokens;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","names":[],"sources":["../src/diagnostics.ts"],"mappings":";KAAY
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","names":[],"sources":["../src/diagnostics.ts"],"mappings":";KAAY;UAYK;EACf,MAAM;EACN;;cAWW,yBAAyB;WACR,aAAa;EAAb,YAAA,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractSlideMastersAsPptx.d.ts","names":[],"sources":["../src/extractSlideMastersAsPptx.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"extractSlideMastersAsPptx.d.ts","names":[],"sources":["../src/extractSlideMastersAsPptx.ts"],"mappings":";;;;;;;;;iBAyMsB,0BACpB,YAAY,cAAc,aACzB,QAAQ"}
|