@hirokisakabe/pom 8.2.0 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +24 -24
  2. package/dist/autoFit/autoFit.js.map +1 -1
  3. package/dist/buildContext.js +3 -1
  4. package/dist/buildContext.js.map +1 -1
  5. package/dist/buildPptx.d.ts.map +1 -1
  6. package/dist/buildPptx.js +4 -0
  7. package/dist/buildPptx.js.map +1 -1
  8. package/dist/calcYogaLayout/calcYogaLayout.js +2 -1
  9. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  10. package/dist/calcYogaLayout/fontLoader.js.map +1 -1
  11. package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
  12. package/dist/calcYogaLayout/measureText.js +9 -2
  13. package/dist/calcYogaLayout/measureText.js.map +1 -1
  14. package/dist/diagnostics.js.map +1 -1
  15. package/dist/icons/renderIcon.js.map +1 -1
  16. package/dist/parseMasterPptx.js.map +1 -1
  17. package/dist/parseXml/coercionRules.js +6 -2
  18. package/dist/parseXml/coercionRules.js.map +1 -1
  19. package/dist/parseXml/parseXml.d.ts.map +1 -1
  20. package/dist/parseXml/parseXml.js +11 -8
  21. package/dist/parseXml/parseXml.js.map +1 -1
  22. package/dist/parseXml/serializeXml.d.ts.map +1 -1
  23. package/dist/parseXml/serializeXml.js +1 -0
  24. package/dist/parseXml/serializeXml.js.map +1 -1
  25. package/dist/registry/definitions/list.js.map +1 -1
  26. package/dist/registry/definitions/shape.js.map +1 -1
  27. package/dist/registry/definitions/text.js +3 -1
  28. package/dist/registry/definitions/text.js.map +1 -1
  29. package/dist/renderPptx/gradientFills.js +139 -0
  30. package/dist/renderPptx/gradientFills.js.map +1 -0
  31. package/dist/renderPptx/nodes/icon.js.map +1 -1
  32. package/dist/renderPptx/nodes/list.js.map +1 -1
  33. package/dist/renderPptx/nodes/matrix.js +8 -5
  34. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  35. package/dist/renderPptx/nodes/table.js.map +1 -1
  36. package/dist/renderPptx/nodes/text.js +18 -14
  37. package/dist/renderPptx/nodes/text.js.map +1 -1
  38. package/dist/renderPptx/nodes/tree.js.map +1 -1
  39. package/dist/renderPptx/renderPptx.js +6 -2
  40. package/dist/renderPptx/renderPptx.js.map +1 -1
  41. package/dist/renderPptx/textOptions.js +2 -1
  42. package/dist/renderPptx/textOptions.js.map +1 -1
  43. package/dist/renderPptx/utils/backgroundBorder.js +6 -4
  44. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  45. package/dist/shared/gradient.js +103 -0
  46. package/dist/shared/gradient.js.map +1 -0
  47. package/dist/shared/measureImage.js.map +1 -1
  48. package/dist/shared/tableUtils.js.map +1 -1
  49. package/dist/types.d.ts +41 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/types.js +8 -2
  52. package/dist/types.js.map +1 -1
  53. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/registry/definitions/text.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport type { NodeDefinition, Yoga } from \"../types.ts\";\nimport type { Node as YogaNode } from \"yoga-layout\";\nimport { measureText } from \"../../calcYogaLayout/measureText.ts\";\nimport type { BuildContext } from \"../../buildContext.ts\";\nimport { renderTextNode } from \"../../renderPptx/nodes/text.ts\";\n\nexport const textNodeDef: NodeDefinition = {\n type: \"text\",\n category: \"leaf\",\n applyYogaStyle(node: POMNode, yn: YogaNode, yoga: Yoga, ctx: BuildContext) {\n const n = node as Extract<POMNode, { type: \"text\" }>;\n const text = n.text;\n const fontSizePx = n.fontSize ?? 24;\n const fontFamily = n.fontFamily ?? \"Noto Sans JP\";\n const fontWeight = n.bold ? \"bold\" : \"normal\";\n const lineHeight = 1.3;\n\n yn.setMeasureFunc((width, widthMode) => {\n const maxWidthPx = (() => {\n switch (widthMode) {\n case yoga.MEASURE_MODE_UNDEFINED:\n return Number.POSITIVE_INFINITY;\n case yoga.MEASURE_MODE_EXACTLY:\n case yoga.MEASURE_MODE_AT_MOST:\n return width;\n default:\n return Number.POSITIVE_INFINITY;\n }\n })();\n\n const { widthPx, heightPx } = measureText(\n text,\n maxWidthPx,\n {\n fontFamily,\n fontSizePx,\n lineHeight,\n fontWeight,\n },\n ctx.textMeasurementMode,\n );\n\n return { width: widthPx, height: heightPx };\n });\n },\n render(node, ctx) {\n renderTextNode(node as Extract<typeof node, { type: \"text\" }>, ctx);\n },\n};\n"],"mappings":";;;AAOA,MAAa,cAA8B;CACzC,MAAM;CACN,UAAU;CACV,eAAe,MAAe,IAAc,MAAY,KAAmB;EACzE,MAAM,IAAI;EACV,MAAM,OAAO,EAAE;EACf,MAAM,aAAa,EAAE,YAAY;EACjC,MAAM,aAAa,EAAE,cAAc;EACnC,MAAM,aAAa,EAAE,OAAO,SAAS;EACrC,MAAM,aAAa;EAEnB,GAAG,gBAAgB,OAAO,cAAc;GAatC,MAAM,EAAE,SAAS,aAAa,YAC5B,aAbwB;IACxB,QAAQ,WAAR;KACE,KAAK,KAAK,wBACR,OAAO,OAAO;KAChB,KAAK,KAAK;KACV,KAAK,KAAK,sBACR,OAAO;KACT,SACE,OAAO,OAAO;IAClB;GACF,GAIW,GACT;IACE;IACA;IACA;IACA;GACF,GACA,IAAI,mBACN;GAEA,OAAO;IAAE,OAAO;IAAS,QAAQ;GAAS;EAC5C,CAAC;CACH;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/registry/definitions/text.ts"],"sourcesContent":["import type { POMNode } from \"../../types.ts\";\nimport type { NodeDefinition, Yoga } from \"../types.ts\";\nimport type { Node as YogaNode } from \"yoga-layout\";\nimport { measureText } from \"../../calcYogaLayout/measureText.ts\";\nimport type { BuildContext } from \"../../buildContext.ts\";\nimport { renderTextNode } from \"../../renderPptx/nodes/text.ts\";\n\nexport const textNodeDef: NodeDefinition = {\n type: \"text\",\n category: \"leaf\",\n applyYogaStyle(node: POMNode, yn: YogaNode, yoga: Yoga, ctx: BuildContext) {\n const n = node as Extract<POMNode, { type: \"text\" }>;\n const text = n.text;\n const fontSizePx = n.fontSize ?? 24;\n const fontFamily = n.fontFamily ?? \"Noto Sans JP\";\n const fontWeight = n.bold ? \"bold\" : \"normal\";\n const lineHeight = 1.3;\n const letterSpacingPx = n.letterSpacing;\n\n yn.setMeasureFunc((width, widthMode) => {\n const maxWidthPx = (() => {\n switch (widthMode) {\n case yoga.MEASURE_MODE_UNDEFINED:\n return Number.POSITIVE_INFINITY;\n case yoga.MEASURE_MODE_EXACTLY:\n case yoga.MEASURE_MODE_AT_MOST:\n return width;\n default:\n return Number.POSITIVE_INFINITY;\n }\n })();\n\n const { widthPx, heightPx } = measureText(\n text,\n maxWidthPx,\n {\n fontFamily,\n fontSizePx,\n lineHeight,\n fontWeight,\n letterSpacingPx,\n },\n ctx.textMeasurementMode,\n );\n\n return { width: widthPx, height: heightPx };\n });\n },\n render(node, ctx) {\n renderTextNode(node as Extract<typeof node, { type: \"text\" }>, ctx);\n },\n};\n"],"mappings":";;;AAOA,MAAa,cAA8B;CACzC,MAAM;CACN,UAAU;CACV,eAAe,MAAe,IAAc,MAAY,KAAmB;EACzE,MAAM,IAAI;EACV,MAAM,OAAO,EAAE;EACf,MAAM,aAAa,EAAE,YAAY;EACjC,MAAM,aAAa,EAAE,cAAc;EACnC,MAAM,aAAa,EAAE,OAAO,SAAS;EACrC,MAAM,aAAa;EACnB,MAAM,kBAAkB,EAAE;EAE1B,GAAG,gBAAgB,OAAO,cAAc;GAatC,MAAM,EAAE,SAAS,aAAa,YAC5B,aAbwB;IACxB,QAAQ,WAAR;KACE,KAAK,KAAK,wBACR,OAAO,OAAO;KAChB,KAAK,KAAK;KACV,KAAK,KAAK,sBACR,OAAO;KACT,SACE,OAAO,OAAO;IAClB;GACF,EAAA,CAIW,GACT;IACE;IACA;IACA;IACA;IACA;GACF,GACA,IAAI,mBACN;GAEA,OAAO;IAAE,OAAO;IAAS,QAAQ;GAAS;EAC5C,CAAC;CACH;CACA,OAAO,MAAM,KAAK;EAChB,eAAe,MAAgD,GAAG;CACpE;AACF"}
@@ -0,0 +1,139 @@
1
+ import { parseLinearGradient } from "../shared/gradient.js";
2
+ //#region src/renderPptx/gradientFills.ts
3
+ async function loadJSZip() {
4
+ const mod = await import("jszip");
5
+ return mod.default ?? mod;
6
+ }
7
+ /** マーカー色の探索開始値。reserveColors で予約済みの色はスキップされる */
8
+ const MARKER_BASE = 1014333;
9
+ var GradientFillRegistry = class {
10
+ reserved = /* @__PURE__ */ new Set();
11
+ markerBySpec = /* @__PURE__ */ new Map();
12
+ registered = [];
13
+ nextCandidate = MARKER_BASE;
14
+ /**
15
+ * テキスト中に現れる 6桁 HEX をマーカー候補から除外する。
16
+ * 入力 XML 由来のユーザー指定色とマーカーの衝突を防ぐため、
17
+ * register より前に入力 XML 文字列を渡しておく。
18
+ */
19
+ reserveColors(text) {
20
+ for (const match of text.matchAll(/[0-9a-fA-F]{6}/g)) this.reserved.add(match[0].toUpperCase());
21
+ }
22
+ /** グラデーションを登録し、対応するマーカー色を返す */
23
+ register(gradient, opacity) {
24
+ const specKey = JSON.stringify({
25
+ gradient,
26
+ opacity
27
+ });
28
+ const existing = this.markerBySpec.get(specKey);
29
+ if (existing) return existing;
30
+ let marker;
31
+ do {
32
+ marker = this.nextCandidate.toString(16).toUpperCase().padStart(6, "0");
33
+ this.nextCandidate = (this.nextCandidate + 1) % 16777216;
34
+ } while (this.reserved.has(marker));
35
+ this.reserved.add(marker);
36
+ this.markerBySpec.set(specKey, marker);
37
+ this.registered.push({
38
+ marker,
39
+ gradient,
40
+ opacity
41
+ });
42
+ return marker;
43
+ }
44
+ get isEmpty() {
45
+ return this.registered.length === 0;
46
+ }
47
+ get entries() {
48
+ return this.registered;
49
+ }
50
+ };
51
+ /**
52
+ * backgroundGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。
53
+ * パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。
54
+ */
55
+ function registerBackgroundGradient(value, opacity, registry) {
56
+ const gradient = parseLinearGradient(value);
57
+ if (!gradient) return void 0;
58
+ return registry.register(gradient, opacity);
59
+ }
60
+ /**
61
+ * LinearGradient を DrawingML の `<a:gradFill>` 要素に変換する
62
+ *
63
+ * - カラーストップ位置: % → 1/1000 % (0-100000)
64
+ * - 角度: CSS 基準 (0deg = 上向き) → DrawingML 基準 (0 = 右向き、1/60000 度)
65
+ */
66
+ function buildGradFillXml(gradient, opacity) {
67
+ const alphaXml = opacity !== void 0 ? `<a:alpha val="${Math.round(opacity * 1e5)}"/>` : "";
68
+ const gsXml = gradient.stops.map((stop) => {
69
+ return `<a:gs pos="${Math.round(stop.position * 1e3)}">${alphaXml ? `<a:srgbClr val="${stop.color}">${alphaXml}</a:srgbClr>` : `<a:srgbClr val="${stop.color}"/>`}</a:gs>`;
70
+ }).join("");
71
+ const dmlAngle = ((gradient.angle - 90) % 360 + 360) % 360;
72
+ return `<a:gradFill flip="none" rotWithShape="1"><a:gsLst>${gsXml}</a:gsLst><a:lin ang="${Math.round(dmlAngle * 6e4)}" scaled="0"/></a:gradFill>`;
73
+ }
74
+ /**
75
+ * 出力 zip 内のスライド XML のマーカー solidFill を gradFill に置換する
76
+ */
77
+ async function applyGradientFills(data, registry) {
78
+ const zip = await (await loadJSZip()).loadAsync(data);
79
+ const slidePaths = Object.keys(zip.files).filter((path) => /^ppt\/slides\/slide\d+\.xml$/.test(path));
80
+ for (const path of slidePaths) {
81
+ const file = zip.file(path);
82
+ if (!file) continue;
83
+ let xml = await file.async("text");
84
+ let replaced = false;
85
+ for (const { marker, gradient, opacity } of registry.entries) {
86
+ const target = `<a:solidFill><a:srgbClr val="${marker}"/></a:solidFill>`;
87
+ if (!xml.includes(target)) continue;
88
+ xml = xml.replaceAll(target, buildGradFillXml(gradient, opacity));
89
+ replaced = true;
90
+ }
91
+ if (replaced) zip.file(path, xml);
92
+ }
93
+ return zip;
94
+ }
95
+ /**
96
+ * pptx インスタンスの write / writeFile をラップし、
97
+ * 出力時にグラデーション後処理を適用する。
98
+ *
99
+ * pptxgenjs の write と同じ outputType / compression の挙動を再現する。
100
+ * writeFile は Node 環境のみ後処理対象 (ブラウザでは pptxgenjs がダウンロード
101
+ * 処理を行うため、元の実装にフォールバックする)。
102
+ */
103
+ function patchPptxWriteForGradientFills(pptx, registry) {
104
+ if (registry.isEmpty) return;
105
+ const originalWrite = pptx.write.bind(pptx);
106
+ const originalWriteFile = pptx.writeFile.bind(pptx);
107
+ const patchedWrite = async (rawProps) => {
108
+ const props = typeof rawProps === "string" ? { outputType: rawProps } : rawProps;
109
+ const zip = await applyGradientFills(await originalWrite({ outputType: "uint8array" }), registry);
110
+ const outputType = props?.outputType;
111
+ if (outputType === "STREAM") return zip.generateAsync({
112
+ type: "nodebuffer",
113
+ compression: props?.compression ? "DEFLATE" : "STORE"
114
+ });
115
+ if (outputType) return zip.generateAsync({ type: outputType });
116
+ return zip.generateAsync({
117
+ type: "blob",
118
+ compression: props?.compression ? "DEFLATE" : "STORE"
119
+ });
120
+ };
121
+ pptx.write = patchedWrite;
122
+ const patchedWriteFile = async (rawProps) => {
123
+ const props = typeof rawProps === "string" ? { fileName: rawProps } : rawProps;
124
+ if (!(typeof process !== "undefined" && Boolean(process.versions?.node))) return originalWriteFile(props);
125
+ const rawName = props?.fileName ?? "Presentation.pptx";
126
+ const fileName = rawName.toLowerCase().endsWith(".pptx") ? rawName : `${rawName}.pptx`;
127
+ const buffer = await patchedWrite({
128
+ outputType: "nodebuffer",
129
+ compression: props?.compression
130
+ });
131
+ await (await import("fs")).promises.writeFile(fileName, buffer);
132
+ return fileName;
133
+ };
134
+ pptx.writeFile = patchedWriteFile;
135
+ }
136
+ //#endregion
137
+ export { GradientFillRegistry, patchPptxWriteForGradientFills, registerBackgroundGradient };
138
+
139
+ //# sourceMappingURL=gradientFills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradientFills.js","names":[],"sources":["../../src/renderPptx/gradientFills.ts"],"sourcesContent":["/**\n * グラデーション塗りの実現\n *\n * pptxgenjs はグラデーション塗りを未サポート\n * (https://github.com/gitbrent/PptxGenJS/issues/102) のため、以下の方式で実現する:\n *\n * 1. レンダリング時: グラデーション指定のある shape をビルド内で一意なマーカー色の\n * 単色塗り (solidFill) として描画し、レジストリに登録する\n * 2. 出力時: pptx.write() / writeFile() をラップし、出力 zip 内のスライド XML の\n * `<a:solidFill><a:srgbClr val=\"マーカー色\"/></a:solidFill>` を\n * DrawingML ネイティブの `<a:gradFill>` に置換する\n *\n * 置換は pptxgenjs が生成する固定パターンに対する完全一致の文字列置換で行う。\n * スライド XML 全体をパーサーで往復させると無関係な要素の表現が変わり得るため、\n * 意図的に文字列置換を採用している。\n */\nimport type { LinearGradient } from \"../shared/gradient.ts\";\nimport { parseLinearGradient } from \"../shared/gradient.ts\";\n\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\ntype WriteProps = NonNullable<Parameters<PptxGenJSInstance[\"write\"]>[0]>;\ntype WriteFileProps = NonNullable<\n Parameters<PptxGenJSInstance[\"writeFile\"]>[0]\n>;\n\n// JSZip は CJS パッケージのため動的 import で読み込む\nasync function loadJSZip(): Promise<typeof import(\"jszip\")> {\n const mod = await import(\"jszip\");\n /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n return (mod as any).default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\n\ninterface RegisteredGradientFill {\n /** マーカー色 (6桁大文字 HEX、# なし) */\n marker: string;\n gradient: LinearGradient;\n /** 0-1。指定時は各カラーストップに alpha として反映する */\n opacity?: number;\n}\n\n/** マーカー色の探索開始値。reserveColors で予約済みの色はスキップされる */\nconst MARKER_BASE = 0x0f7a3d;\n\nexport class GradientFillRegistry {\n private readonly reserved = new Set<string>();\n private readonly markerBySpec = new Map<string, string>();\n private readonly registered: RegisteredGradientFill[] = [];\n private nextCandidate = MARKER_BASE;\n\n /**\n * テキスト中に現れる 6桁 HEX をマーカー候補から除外する。\n * 入力 XML 由来のユーザー指定色とマーカーの衝突を防ぐため、\n * register より前に入力 XML 文字列を渡しておく。\n */\n reserveColors(text: string): void {\n for (const match of text.matchAll(/[0-9a-fA-F]{6}/g)) {\n this.reserved.add(match[0].toUpperCase());\n }\n }\n\n /** グラデーションを登録し、対応するマーカー色を返す */\n register(gradient: LinearGradient, opacity?: number): string {\n const specKey = JSON.stringify({ gradient, opacity });\n const existing = this.markerBySpec.get(specKey);\n if (existing) return existing;\n\n let marker: string;\n do {\n marker = this.nextCandidate.toString(16).toUpperCase().padStart(6, \"0\");\n this.nextCandidate = (this.nextCandidate + 1) % 0x1000000;\n } while (this.reserved.has(marker));\n this.reserved.add(marker);\n\n this.markerBySpec.set(specKey, marker);\n this.registered.push({ marker, gradient, opacity });\n return marker;\n }\n\n get isEmpty(): boolean {\n return this.registered.length === 0;\n }\n\n get entries(): readonly RegisteredGradientFill[] {\n return this.registered;\n }\n}\n\n/**\n * backgroundGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。\n * パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。\n */\nexport function registerBackgroundGradient(\n value: string,\n opacity: number | undefined,\n registry: GradientFillRegistry,\n): string | undefined {\n const gradient = parseLinearGradient(value);\n if (!gradient) return undefined;\n return registry.register(gradient, opacity);\n}\n\n/**\n * LinearGradient を DrawingML の `<a:gradFill>` 要素に変換する\n *\n * - カラーストップ位置: % → 1/1000 % (0-100000)\n * - 角度: CSS 基準 (0deg = 上向き) → DrawingML 基準 (0 = 右向き、1/60000 度)\n */\nfunction buildGradFillXml(gradient: LinearGradient, opacity?: number): string {\n const alphaXml =\n opacity !== undefined\n ? `<a:alpha val=\"${Math.round(opacity * 100000)}\"/>`\n : \"\";\n const gsXml = gradient.stops\n .map((stop) => {\n const pos = Math.round(stop.position * 1000);\n const srgbClr = alphaXml\n ? `<a:srgbClr val=\"${stop.color}\">${alphaXml}</a:srgbClr>`\n : `<a:srgbClr val=\"${stop.color}\"/>`;\n return `<a:gs pos=\"${pos}\">${srgbClr}</a:gs>`;\n })\n .join(\"\");\n const dmlAngle = (((gradient.angle - 90) % 360) + 360) % 360;\n const ang = Math.round(dmlAngle * 60000);\n return `<a:gradFill flip=\"none\" rotWithShape=\"1\"><a:gsLst>${gsXml}</a:gsLst><a:lin ang=\"${ang}\" scaled=\"0\"/></a:gradFill>`;\n}\n\n/**\n * 出力 zip 内のスライド XML のマーカー solidFill を gradFill に置換する\n */\nexport async function applyGradientFills(\n data: Uint8Array | ArrayBuffer,\n registry: GradientFillRegistry,\n): Promise<import(\"jszip\")> {\n const JSZip = await loadJSZip();\n const zip = await JSZip.loadAsync(data);\n\n const slidePaths = Object.keys(zip.files).filter((path) =>\n /^ppt\\/slides\\/slide\\d+\\.xml$/.test(path),\n );\n for (const path of slidePaths) {\n const file = zip.file(path);\n if (!file) continue;\n let xml = await file.async(\"text\");\n let replaced = false;\n for (const { marker, gradient, opacity } of registry.entries) {\n const target = `<a:solidFill><a:srgbClr val=\"${marker}\"/></a:solidFill>`;\n if (!xml.includes(target)) continue;\n xml = xml.replaceAll(target, buildGradFillXml(gradient, opacity));\n replaced = true;\n }\n if (replaced) {\n zip.file(path, xml);\n }\n }\n return zip;\n}\n\n/**\n * pptx インスタンスの write / writeFile をラップし、\n * 出力時にグラデーション後処理を適用する。\n *\n * pptxgenjs の write と同じ outputType / compression の挙動を再現する。\n * writeFile は Node 環境のみ後処理対象 (ブラウザでは pptxgenjs がダウンロード\n * 処理を行うため、元の実装にフォールバックする)。\n */\nexport function patchPptxWriteForGradientFills(\n pptx: PptxGenJSInstance,\n registry: GradientFillRegistry,\n): void {\n if (registry.isEmpty) return;\n\n const originalWrite = pptx.write.bind(pptx);\n const originalWriteFile = pptx.writeFile.bind(pptx);\n\n const patchedWrite = async (rawProps?: WriteProps | string) => {\n // DEPRECATED: pptxgenjs は write(outputType) の文字列 overload を\n // ランタイムでは今も受け付けるため、同様に正規化する\n const props: WriteProps | undefined =\n typeof rawProps === \"string\"\n ? ({ outputType: rawProps } as WriteProps)\n : rawProps;\n const data = (await originalWrite({\n outputType: \"uint8array\",\n })) as Uint8Array;\n const zip = await applyGradientFills(data, registry);\n\n const outputType = props?.outputType;\n if (outputType === \"STREAM\") {\n return zip.generateAsync({\n type: \"nodebuffer\",\n compression: props?.compression ? \"DEFLATE\" : \"STORE\",\n });\n }\n if (outputType) {\n return zip.generateAsync({ type: outputType });\n }\n return zip.generateAsync({\n type: \"blob\",\n compression: props?.compression ? \"DEFLATE\" : \"STORE\",\n });\n };\n pptx.write = patchedWrite;\n\n const patchedWriteFile = async (rawProps?: WriteFileProps | string) => {\n // DEPRECATED: pptxgenjs は writeFile(fileName) の文字列 overload を\n // ランタイムでは今も受け付けるため、同様に正規化する\n const props: WriteFileProps | undefined =\n typeof rawProps === \"string\" ? { fileName: rawProps } : rawProps;\n const isNode =\n typeof process !== \"undefined\" && Boolean(process.versions?.node);\n if (!isNode) {\n return originalWriteFile(props);\n }\n const rawName = props?.fileName ?? \"Presentation.pptx\";\n const fileName = rawName.toLowerCase().endsWith(\".pptx\")\n ? rawName\n : `${rawName}.pptx`;\n const buffer = (await patchedWrite({\n outputType: \"nodebuffer\",\n compression: props?.compression,\n })) as Buffer;\n const fs = await import(\"fs\");\n await fs.promises.writeFile(fileName, buffer);\n return fileName;\n };\n pptx.writeFile = patchedWriteFile;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"icon.js","names":[],"sources":["../../../src/renderPptx/nodes/icon.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn } from \"../units.ts\";\n\ntype IconPositionedNode = Extract<PositionedNode, { type: \"icon\" }>;\n\nexport function renderIconNode(\n node: IconPositionedNode,\n ctx: RenderContext,\n): void {\n // variant 指定時は背景図形を描画\n if (node.variant) {\n const isCircle = node.variant.startsWith(\"circle\");\n const isFilled = node.variant.endsWith(\"-filled\");\n const bgColor = node.bgColor ?? \"#E0E0E0\";\n const colorValue = bgColor.replace(/^#/, \"\");\n\n const shapeType = isCircle ? \"ellipse\" : \"roundRect\";\n const shapeOptions: Record<string, unknown> = {\n x: pxToIn(node.bgX ?? node.x),\n y: pxToIn(node.bgY ?? node.y),\n w: pxToIn(node.bgW ?? node.w),\n h: pxToIn(node.bgH ?? node.h),\n fill: isFilled ? { color: colorValue } : { type: \"none\" as const },\n line: isFilled ? undefined : { color: colorValue, width: 1.5 },\n rectRadius: isCircle ? undefined : 0.1,\n };\n\n ctx.slide.addShape(shapeType, shapeOptions);\n }\n\n ctx.slide.addImage({\n data: node.iconImageData,\n x: pxToIn(node.iconX ?? node.x),\n y: pxToIn(node.iconY ?? node.y),\n w: pxToIn(node.iconW ?? node.w),\n h: pxToIn(node.iconH ?? node.h),\n });\n}\n"],"mappings":";;AAMA,SAAgB,eACd,MACA,KACM;CAEN,IAAI,KAAK,SAAS;EAChB,MAAM,WAAW,KAAK,QAAQ,WAAW,QAAQ;EACjD,MAAM,WAAW,KAAK,QAAQ,SAAS,SAAS;EAEhD,MAAM,cADU,KAAK,WAAW,WACL,QAAQ,MAAM,EAAE;EAE3C,MAAM,YAAY,WAAW,YAAY;EACzC,MAAM,eAAwC;GAC5C,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,MAAM,WAAW,EAAE,OAAO,WAAW,IAAI,EAAE,MAAM,OAAgB;GACjE,MAAM,WAAW,KAAA,IAAY;IAAE,OAAO;IAAY,OAAO;GAAI;GAC7D,YAAY,WAAW,KAAA,IAAY;EACrC;EAEA,IAAI,MAAM,SAAS,WAAW,YAAY;CAC5C;CAEA,IAAI,MAAM,SAAS;EACjB,MAAM,KAAK;EACX,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;CAChC,CAAC;AACH"}
1
+ {"version":3,"file":"icon.js","names":[],"sources":["../../../src/renderPptx/nodes/icon.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn } from \"../units.ts\";\n\ntype IconPositionedNode = Extract<PositionedNode, { type: \"icon\" }>;\n\nexport function renderIconNode(\n node: IconPositionedNode,\n ctx: RenderContext,\n): void {\n // variant 指定時は背景図形を描画\n if (node.variant) {\n const isCircle = node.variant.startsWith(\"circle\");\n const isFilled = node.variant.endsWith(\"-filled\");\n const bgColor = node.bgColor ?? \"#E0E0E0\";\n const colorValue = bgColor.replace(/^#/, \"\");\n\n const shapeType = isCircle ? \"ellipse\" : \"roundRect\";\n const shapeOptions: Record<string, unknown> = {\n x: pxToIn(node.bgX ?? node.x),\n y: pxToIn(node.bgY ?? node.y),\n w: pxToIn(node.bgW ?? node.w),\n h: pxToIn(node.bgH ?? node.h),\n fill: isFilled ? { color: colorValue } : { type: \"none\" as const },\n line: isFilled ? undefined : { color: colorValue, width: 1.5 },\n rectRadius: isCircle ? undefined : 0.1,\n };\n\n ctx.slide.addShape(shapeType, shapeOptions);\n }\n\n ctx.slide.addImage({\n data: node.iconImageData,\n x: pxToIn(node.iconX ?? node.x),\n y: pxToIn(node.iconY ?? node.y),\n w: pxToIn(node.iconW ?? node.w),\n h: pxToIn(node.iconH ?? node.h),\n });\n}\n"],"mappings":";;AAMA,SAAgB,eACd,MACA,KACM;CAEN,IAAI,KAAK,SAAS;EAChB,MAAM,WAAW,KAAK,QAAQ,WAAW,QAAQ;EACjD,MAAM,WAAW,KAAK,QAAQ,SAAS,SAAS;EAEhD,MAAM,cADU,KAAK,WAAW,UAAA,CACL,QAAQ,MAAM,EAAE;EAE3C,MAAM,YAAY,WAAW,YAAY;EACzC,MAAM,eAAwC;GAC5C,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC;GAC5B,MAAM,WAAW,EAAE,OAAO,WAAW,IAAI,EAAE,MAAM,OAAgB;GACjE,MAAM,WAAW,KAAA,IAAY;IAAE,OAAO;IAAY,OAAO;GAAI;GAC7D,YAAY,WAAW,KAAA,IAAY;EACrC;EAEA,IAAI,MAAM,SAAS,WAAW,YAAY;CAC5C;CAEA,IAAI,MAAM,SAAS;EACjB,MAAM,KAAK;EACX,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAC9B,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;CAChC,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","names":[],"sources":["../../../src/renderPptx/nodes/list.ts"],"sourcesContent":["import type { PositionedNode, LiNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype UlPositionedNode = Extract<PositionedNode, { type: \"ul\" }>;\ntype OlPositionedNode = Extract<PositionedNode, { type: \"ol\" }>;\n\nfunction resolveStyle(li: LiNode, parent: UlPositionedNode | OlPositionedNode) {\n return {\n fontSize: li.fontSize ?? parent.fontSize ?? 24,\n color: li.color ?? parent.color,\n bold: li.bold ?? parent.bold,\n italic: li.italic ?? parent.italic,\n underline: li.underline ?? parent.underline,\n strike: li.strike ?? parent.strike,\n highlight: li.highlight ?? parent.highlight,\n fontFamily: li.fontFamily ?? parent.fontFamily ?? \"Noto Sans JP\",\n };\n}\n\nfunction buildListTextItems(\n items: LiNode[],\n parent: UlPositionedNode | OlPositionedNode,\n bullet: boolean | Record<string, unknown>,\n) {\n const textItems: { text: string; options: Record<string, unknown> }[] = [];\n for (let i = 0; i < items.length; i++) {\n const li = items[i];\n const style = resolveStyle(li, parent);\n const isLast = i === items.length - 1;\n const baseOptions = {\n fontSize: pxToPt(style.fontSize),\n fontFace: style.fontFamily,\n color: style.color,\n underline: convertUnderline(style.underline),\n strike: convertStrike(style.strike),\n highlight: style.highlight,\n };\n\n if (li.runs && li.runs.length > 0) {\n for (let j = 0; j < li.runs.length; j++) {\n const run = li.runs[j];\n const isLastRun = j === li.runs.length - 1;\n let text = run.text;\n if (isLastRun && !isLast) text += \"\\n\";\n textItems.push({\n text,\n options: {\n ...baseOptions,\n fontFace: run.fontFamily ?? style.fontFamily,\n color: run.color ?? style.color,\n bold: run.bold ?? style.bold,\n italic: run.italic ?? style.italic,\n underline: convertUnderline(run.underline ?? style.underline),\n strike: convertStrike(run.strike ?? style.strike),\n highlight: run.highlight ?? style.highlight,\n bullet: j === 0 ? bullet : false,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n });\n }\n } else {\n textItems.push({\n text: isLast ? li.text : li.text + \"\\n\",\n options: {\n ...baseOptions,\n bold: style.bold,\n italic: style.italic,\n bullet,\n },\n });\n }\n }\n return textItems;\n}\n\nfunction hasItemStyleOverride(items: LiNode[]): boolean {\n return items.some(\n (li) =>\n li.fontSize !== undefined ||\n li.color !== undefined ||\n li.bold !== undefined ||\n li.italic !== undefined ||\n li.underline !== undefined ||\n li.strike !== undefined ||\n li.highlight !== undefined ||\n li.fontFamily !== undefined ||\n li.runs !== undefined,\n );\n}\n\nexport function renderUlNode(node: UlPositionedNode, ctx: RenderContext): void {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n const content = getContentArea(node);\n\n if (hasItemStyleOverride(node.items)) {\n // Li に個別スタイルがある場合は配列形式を使用\n const textItems = buildListTextItems(node.items, node, true);\n\n ctx.slide.addText(textItems, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n });\n } else {\n // Li にスタイルオーバーライドがない場合は単一文字列形式を使用\n const text = node.items.map((li) => li.text).join(\"\\n\");\n\n ctx.slide.addText(text, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n bullet: true,\n });\n }\n}\n\nexport function renderOlNode(node: OlPositionedNode, ctx: RenderContext): void {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n const content = getContentArea(node);\n\n const bulletOptions: Record<string, unknown> = { type: \"number\" };\n if (node.numberType !== undefined) {\n bulletOptions.numberType = node.numberType;\n }\n if (node.numberStartAt !== undefined) {\n bulletOptions.numberStartAt = node.numberStartAt;\n }\n\n if (hasItemStyleOverride(node.items)) {\n const textItems = buildListTextItems(node.items, node, bulletOptions);\n\n ctx.slide.addText(textItems, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n });\n } else {\n const text = node.items.map((li) => li.text).join(\"\\n\");\n\n ctx.slide.addText(text, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n bullet: bulletOptions,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"list.js","names":[],"sources":["../../../src/renderPptx/nodes/list.ts"],"sourcesContent":["import type { PositionedNode, LiNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype UlPositionedNode = Extract<PositionedNode, { type: \"ul\" }>;\ntype OlPositionedNode = Extract<PositionedNode, { type: \"ol\" }>;\n\nfunction resolveStyle(li: LiNode, parent: UlPositionedNode | OlPositionedNode) {\n return {\n fontSize: li.fontSize ?? parent.fontSize ?? 24,\n color: li.color ?? parent.color,\n bold: li.bold ?? parent.bold,\n italic: li.italic ?? parent.italic,\n underline: li.underline ?? parent.underline,\n strike: li.strike ?? parent.strike,\n highlight: li.highlight ?? parent.highlight,\n fontFamily: li.fontFamily ?? parent.fontFamily ?? \"Noto Sans JP\",\n };\n}\n\nfunction buildListTextItems(\n items: LiNode[],\n parent: UlPositionedNode | OlPositionedNode,\n bullet: boolean | Record<string, unknown>,\n) {\n const textItems: { text: string; options: Record<string, unknown> }[] = [];\n for (let i = 0; i < items.length; i++) {\n const li = items[i];\n const style = resolveStyle(li, parent);\n const isLast = i === items.length - 1;\n const baseOptions = {\n fontSize: pxToPt(style.fontSize),\n fontFace: style.fontFamily,\n color: style.color,\n underline: convertUnderline(style.underline),\n strike: convertStrike(style.strike),\n highlight: style.highlight,\n };\n\n if (li.runs && li.runs.length > 0) {\n for (let j = 0; j < li.runs.length; j++) {\n const run = li.runs[j];\n const isLastRun = j === li.runs.length - 1;\n let text = run.text;\n if (isLastRun && !isLast) text += \"\\n\";\n textItems.push({\n text,\n options: {\n ...baseOptions,\n fontFace: run.fontFamily ?? style.fontFamily,\n color: run.color ?? style.color,\n bold: run.bold ?? style.bold,\n italic: run.italic ?? style.italic,\n underline: convertUnderline(run.underline ?? style.underline),\n strike: convertStrike(run.strike ?? style.strike),\n highlight: run.highlight ?? style.highlight,\n bullet: j === 0 ? bullet : false,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n });\n }\n } else {\n textItems.push({\n text: isLast ? li.text : li.text + \"\\n\",\n options: {\n ...baseOptions,\n bold: style.bold,\n italic: style.italic,\n bullet,\n },\n });\n }\n }\n return textItems;\n}\n\nfunction hasItemStyleOverride(items: LiNode[]): boolean {\n return items.some(\n (li) =>\n li.fontSize !== undefined ||\n li.color !== undefined ||\n li.bold !== undefined ||\n li.italic !== undefined ||\n li.underline !== undefined ||\n li.strike !== undefined ||\n li.highlight !== undefined ||\n li.fontFamily !== undefined ||\n li.runs !== undefined,\n );\n}\n\nexport function renderUlNode(node: UlPositionedNode, ctx: RenderContext): void {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n const content = getContentArea(node);\n\n if (hasItemStyleOverride(node.items)) {\n // Li に個別スタイルがある場合は配列形式を使用\n const textItems = buildListTextItems(node.items, node, true);\n\n ctx.slide.addText(textItems, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n });\n } else {\n // Li にスタイルオーバーライドがない場合は単一文字列形式を使用\n const text = node.items.map((li) => li.text).join(\"\\n\");\n\n ctx.slide.addText(text, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n bullet: true,\n });\n }\n}\n\nexport function renderOlNode(node: OlPositionedNode, ctx: RenderContext): void {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n const content = getContentArea(node);\n\n const bulletOptions: Record<string, unknown> = { type: \"number\" };\n if (node.numberType !== undefined) {\n bulletOptions.numberType = node.numberType;\n }\n if (node.numberStartAt !== undefined) {\n bulletOptions.numberStartAt = node.numberStartAt;\n }\n\n if (hasItemStyleOverride(node.items)) {\n const textItems = buildListTextItems(node.items, node, bulletOptions);\n\n ctx.slide.addText(textItems, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n });\n } else {\n const text = node.items.map((li) => li.text).join(\"\\n\");\n\n ctx.slide.addText(text, {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n lineSpacingMultiple: lineHeight,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n highlight: node.highlight,\n bullet: bulletOptions,\n });\n }\n}\n"],"mappings":"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"}
@@ -56,16 +56,19 @@ function renderMatrixNode(node, ctx) {
56
56
  align: "center",
57
57
  valign: "top"
58
58
  });
59
+ const yLabelW = 100 * scaleFactor;
60
+ const yLabelH = 20 * scaleFactor;
59
61
  ctx.slide.addText(axes.y, {
60
- x: pxToIn(content.x + 4 * scaleFactor),
61
- y: pxToIn(centerY - 12 * scaleFactor),
62
- w: pxToIn(48 * scaleFactor),
63
- h: pxToIn(axisLabelH),
62
+ x: pxToIn(content.x + axisMargin / 2 - yLabelW / 2),
63
+ y: pxToIn(centerY - yLabelH / 2),
64
+ w: pxToIn(yLabelW),
65
+ h: pxToIn(yLabelH),
64
66
  fontSize: pxToPt(12 * scaleFactor),
65
67
  fontFace: "Noto Sans JP",
66
68
  color: "64748B",
67
69
  align: "center",
68
- valign: "middle"
70
+ valign: "middle",
71
+ rotate: 270
69
72
  });
70
73
  if (quadrants) renderQuadrantLabels(ctx, quadrants, areaX, areaY, areaW, areaH, centerX, centerY, scaleFactor);
71
74
  const itemLabelW = 100 * scaleFactor;
@@ -1 +1 @@
1
- {"version":3,"file":"matrix.js","names":[],"sources":["../../../src/renderPptx/nodes/matrix.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureMatrix } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype MatrixPositionedNode = Extract<PositionedNode, { type: \"matrix\" }>;\n\nexport function renderMatrixNode(\n node: MatrixPositionedNode,\n ctx: RenderContext,\n): void {\n const items = node.items;\n const axes = node.axes;\n const quadrants = node.quadrants;\n\n const defaultItemColor = \"1D4ED8\"; // blue\n const baseItemSize = 24; // px\n const baseLineWidth = 2; // px\n const axisColor = \"E2E8F0\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const intrinsic = measureMatrix(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n intrinsic.width,\n intrinsic.height,\n \"matrix\",\n ctx.buildContext.diagnostics,\n );\n\n const itemSize = baseItemSize * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // マトリクスの描画領域(軸ラベル用の余白を考慮)\n const axisMargin = 60 * scaleFactor; // 軸ラベル用の余白\n const areaX = content.x + axisMargin;\n const areaY = content.y + axisMargin;\n const areaW = content.w - axisMargin * 2;\n const areaH = content.h - axisMargin * 2;\n\n // 中心座標\n const centerX = areaX + areaW / 2;\n const centerY = areaY + areaH / 2;\n\n // === 1. 十字線(軸線)を描画 ===\n // 横線(X軸)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(areaX),\n y: pxToIn(centerY),\n w: pxToIn(areaW),\n h: 0,\n line: { color: axisColor, width: pxToPt(lineWidth) },\n });\n\n // 縦線(Y軸)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(centerX),\n y: pxToIn(areaY),\n w: 0,\n h: pxToIn(areaH),\n line: { color: axisColor, width: pxToPt(lineWidth) },\n });\n\n // === 2. 軸ラベルを描画 ===\n const axisLabelW = 120 * scaleFactor;\n const axisLabelH = 24 * scaleFactor;\n\n // X軸ラベル(下部中央)\n ctx.slide.addText(axes.x, {\n x: pxToIn(centerX - axisLabelW / 2),\n y: pxToIn(areaY + areaH + 8 * scaleFactor),\n w: pxToIn(axisLabelW),\n h: pxToIn(axisLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"top\",\n });\n\n // Y軸ラベル(左部中央)\n ctx.slide.addText(axes.y, {\n x: pxToIn(content.x + 4 * scaleFactor),\n y: pxToIn(centerY - 12 * scaleFactor),\n w: pxToIn(48 * scaleFactor),\n h: pxToIn(axisLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"middle\",\n });\n\n // === 3. 象限ラベルを描画 ===\n if (quadrants) {\n renderQuadrantLabels(\n ctx,\n quadrants,\n areaX,\n areaY,\n areaW,\n areaH,\n centerX,\n centerY,\n scaleFactor,\n );\n }\n\n // === 4. アイテムをプロット ===\n const itemLabelW = 100 * scaleFactor;\n const itemLabelH = 18 * scaleFactor;\n\n for (const item of items) {\n // 座標変換: (0,0)=左下, (1,1)=右上\n // x: 0 -> areaX, 1 -> areaX + areaW\n // y: 0 -> areaY + areaH, 1 -> areaY (反転)\n const itemX = areaX + item.x * areaW;\n const itemY = areaY + (1 - item.y) * areaH; // Y軸反転\n const itemColor = item.color ?? defaultItemColor;\n\n // 円を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(itemX - itemSize / 2),\n y: pxToIn(itemY - itemSize / 2),\n w: pxToIn(itemSize),\n h: pxToIn(itemSize),\n fill: { color: itemColor },\n line: { type: \"none\" as const },\n });\n\n // ラベルを描画(円の上)\n ctx.slide.addText(item.label, {\n x: pxToIn(itemX - itemLabelW / 2),\n y: pxToIn(itemY - itemSize / 2 - 20 * scaleFactor),\n w: pxToIn(itemLabelW),\n h: pxToIn(itemLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"1E293B\",\n bold: true,\n align: \"center\",\n valign: \"bottom\",\n });\n }\n}\n\nfunction renderQuadrantLabels(\n ctx: RenderContext,\n quadrants: NonNullable<MatrixPositionedNode[\"quadrants\"]>,\n areaX: number,\n areaY: number,\n areaW: number,\n areaH: number,\n centerX: number,\n centerY: number,\n scaleFactor: number,\n): void {\n const quadrantFontSize = 11 * scaleFactor;\n const quadrantColor = \"94A3B8\"; // slate-400\n const quadrantInset = 10 * scaleFactor;\n const quadrantW = areaW / 2 - 20 * scaleFactor;\n const quadrantH = 48 * scaleFactor;\n\n // 左上\n ctx.slide.addText(quadrants.topLeft, {\n x: pxToIn(areaX + quadrantInset),\n y: pxToIn(areaY + quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"left\",\n valign: \"top\",\n });\n\n // 右上\n ctx.slide.addText(quadrants.topRight, {\n x: pxToIn(centerX + quadrantInset),\n y: pxToIn(areaY + quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"right\",\n valign: \"top\",\n });\n\n // 左下\n ctx.slide.addText(quadrants.bottomLeft, {\n x: pxToIn(areaX + quadrantInset),\n y: pxToIn(centerY + areaH / 2 - quadrantH - quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"left\",\n valign: \"bottom\",\n });\n\n // 右下\n ctx.slide.addText(quadrants.bottomRight, {\n x: pxToIn(centerX + quadrantInset),\n y: pxToIn(centerY + areaH / 2 - quadrantH - quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"right\",\n valign: \"bottom\",\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"matrix.js","names":[],"sources":["../../../src/renderPptx/nodes/matrix.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureMatrix } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype MatrixPositionedNode = Extract<PositionedNode, { type: \"matrix\" }>;\n\nexport function renderMatrixNode(\n node: MatrixPositionedNode,\n ctx: RenderContext,\n): void {\n const items = node.items;\n const axes = node.axes;\n const quadrants = node.quadrants;\n\n const defaultItemColor = \"1D4ED8\"; // blue\n const baseItemSize = 24; // px\n const baseLineWidth = 2; // px\n const axisColor = \"E2E8F0\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const intrinsic = measureMatrix(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n intrinsic.width,\n intrinsic.height,\n \"matrix\",\n ctx.buildContext.diagnostics,\n );\n\n const itemSize = baseItemSize * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // マトリクスの描画領域(軸ラベル用の余白を考慮)\n const axisMargin = 60 * scaleFactor; // 軸ラベル用の余白\n const areaX = content.x + axisMargin;\n const areaY = content.y + axisMargin;\n const areaW = content.w - axisMargin * 2;\n const areaH = content.h - axisMargin * 2;\n\n // 中心座標\n const centerX = areaX + areaW / 2;\n const centerY = areaY + areaH / 2;\n\n // === 1. 十字線(軸線)を描画 ===\n // 横線(X軸)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(areaX),\n y: pxToIn(centerY),\n w: pxToIn(areaW),\n h: 0,\n line: { color: axisColor, width: pxToPt(lineWidth) },\n });\n\n // 縦線(Y軸)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(centerX),\n y: pxToIn(areaY),\n w: 0,\n h: pxToIn(areaH),\n line: { color: axisColor, width: pxToPt(lineWidth) },\n });\n\n // === 2. 軸ラベルを描画 ===\n const axisLabelW = 120 * scaleFactor;\n const axisLabelH = 24 * scaleFactor;\n\n // X軸ラベル(下部中央)\n ctx.slide.addText(axes.x, {\n x: pxToIn(centerX - axisLabelW / 2),\n y: pxToIn(areaY + areaH + 8 * scaleFactor),\n w: pxToIn(axisLabelW),\n h: pxToIn(axisLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"top\",\n });\n\n // Y軸ラベル(左部中央)270° 回転で下から上読み。w が視覚的な高さになるため CJK 5 文字以上も収まる幅を確保\n const yLabelW = 100 * scaleFactor;\n const yLabelH = 20 * scaleFactor;\n ctx.slide.addText(axes.y, {\n x: pxToIn(content.x + axisMargin / 2 - yLabelW / 2),\n y: pxToIn(centerY - yLabelH / 2),\n w: pxToIn(yLabelW),\n h: pxToIn(yLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"64748B\",\n align: \"center\",\n valign: \"middle\",\n rotate: 270,\n });\n\n // === 3. 象限ラベルを描画 ===\n if (quadrants) {\n renderQuadrantLabels(\n ctx,\n quadrants,\n areaX,\n areaY,\n areaW,\n areaH,\n centerX,\n centerY,\n scaleFactor,\n );\n }\n\n // === 4. アイテムをプロット ===\n const itemLabelW = 100 * scaleFactor;\n const itemLabelH = 18 * scaleFactor;\n\n for (const item of items) {\n // 座標変換: (0,0)=左下, (1,1)=右上\n // x: 0 -> areaX, 1 -> areaX + areaW\n // y: 0 -> areaY + areaH, 1 -> areaY (反転)\n const itemX = areaX + item.x * areaW;\n const itemY = areaY + (1 - item.y) * areaH; // Y軸反転\n const itemColor = item.color ?? defaultItemColor;\n\n // 円を描画\n ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(itemX - itemSize / 2),\n y: pxToIn(itemY - itemSize / 2),\n w: pxToIn(itemSize),\n h: pxToIn(itemSize),\n fill: { color: itemColor },\n line: { type: \"none\" as const },\n });\n\n // ラベルを描画(円の上)\n ctx.slide.addText(item.label, {\n x: pxToIn(itemX - itemLabelW / 2),\n y: pxToIn(itemY - itemSize / 2 - 20 * scaleFactor),\n w: pxToIn(itemLabelW),\n h: pxToIn(itemLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace: \"Noto Sans JP\",\n color: \"1E293B\",\n bold: true,\n align: \"center\",\n valign: \"bottom\",\n });\n }\n}\n\nfunction renderQuadrantLabels(\n ctx: RenderContext,\n quadrants: NonNullable<MatrixPositionedNode[\"quadrants\"]>,\n areaX: number,\n areaY: number,\n areaW: number,\n areaH: number,\n centerX: number,\n centerY: number,\n scaleFactor: number,\n): void {\n const quadrantFontSize = 11 * scaleFactor;\n const quadrantColor = \"94A3B8\"; // slate-400\n const quadrantInset = 10 * scaleFactor;\n const quadrantW = areaW / 2 - 20 * scaleFactor;\n const quadrantH = 48 * scaleFactor;\n\n // 左上\n ctx.slide.addText(quadrants.topLeft, {\n x: pxToIn(areaX + quadrantInset),\n y: pxToIn(areaY + quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"left\",\n valign: \"top\",\n });\n\n // 右上\n ctx.slide.addText(quadrants.topRight, {\n x: pxToIn(centerX + quadrantInset),\n y: pxToIn(areaY + quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"right\",\n valign: \"top\",\n });\n\n // 左下\n ctx.slide.addText(quadrants.bottomLeft, {\n x: pxToIn(areaX + quadrantInset),\n y: pxToIn(centerY + areaH / 2 - quadrantH - quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"left\",\n valign: \"bottom\",\n });\n\n // 右下\n ctx.slide.addText(quadrants.bottomRight, {\n x: pxToIn(centerX + quadrantInset),\n y: pxToIn(centerY + areaH / 2 - quadrantH - quadrantInset),\n w: pxToIn(quadrantW),\n h: pxToIn(quadrantH),\n fontSize: pxToPt(quadrantFontSize),\n fontFace: \"Noto Sans JP\",\n color: quadrantColor,\n align: \"right\",\n valign: \"bottom\",\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const tableRows = node.rows.map((row) =>\n row.cells.map((cell) => {\n const cellFontFace = cell.fontFamily;\n const cellOptions: Record<string, unknown> = {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: cellFontFace,\n color: cell.color,\n bold: cell.bold,\n italic: cell.italic,\n underline: convertUnderline(cell.underline),\n strike: convertStrike(cell.strike),\n highlight: cell.highlight,\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n\n if (cell.runs && cell.runs.length > 0) {\n const textItems = cell.runs.map((run) => ({\n text: run.text,\n options: {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: run.fontFamily ?? cellFontFace,\n color: run.color ?? cell.color,\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline: convertUnderline(run.underline ?? cell.underline),\n strike: convertStrike(run.strike ?? cell.strike),\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n }));\n return {\n text: textItems,\n options: {\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n },\n };\n }\n\n return {\n text: cell.text,\n options: cellOptions,\n };\n }),\n );\n\n const content = getContentArea(node);\n const tableOptions: Record<string, unknown> = {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),\n rowH: resolveRowHeights(node).map((height) => pxToIn(height)),\n margin: 0,\n };\n\n if (node.cellBorder) {\n tableOptions.border = {\n color: node.cellBorder.color ?? \"000000\",\n pt:\n node.cellBorder.width !== undefined ? pxToPt(node.cellBorder.width) : 1,\n type: node.cellBorder.dashType ?? \"solid\",\n };\n }\n\n ctx.slide.addTable(tableRows, tableOptions);\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,QAC/B,IAAI,MAAM,KAAK,SAAS;EACtB,MAAM,eAAe,KAAK;EAC1B,MAAM,cAAuC;GAC3C,UAAU,OAAO,KAAK,YAAY,EAAE;GACpC,UAAU;GACV,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;GACJ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EAEA,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAelC,OAAO;GACL,MAfgB,KAAK,KAAK,KAAK,SAAS;IACxC,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,KAAK,YAAY,EAAE;KACpC,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF,EAEgB;GACd,SAAS;IACP,OAAO,KAAK,aAAa;IACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;IACJ,SAAS,KAAK;IACd,SAAS,KAAK;GAChB;EACF;EAGF,OAAO;GACL,MAAM,KAAK;GACX,SAAS;EACX;CACF,CAAC,CACH;CAEA,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAwC;EAC5C,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,MAAM,oBAAoB,MAAM,QAAQ,CAAC,EAAE,KAAK,UAAU,OAAO,KAAK,CAAC;EACvE,MAAM,kBAAkB,IAAI,EAAE,KAAK,WAAW,OAAO,MAAM,CAAC;EAC5D,QAAQ;CACV;CAEA,IAAI,KAAK,YACP,aAAa,SAAS;EACpB,OAAO,KAAK,WAAW,SAAS;EAChC,IACE,KAAK,WAAW,UAAU,KAAA,IAAY,OAAO,KAAK,WAAW,KAAK,IAAI;EACxE,MAAM,KAAK,WAAW,YAAY;CACpC;CAGF,IAAI,MAAM,SAAS,WAAW,YAAY;AAC5C"}
1
+ {"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const tableRows = node.rows.map((row) =>\n row.cells.map((cell) => {\n const cellFontFace = cell.fontFamily;\n const cellOptions: Record<string, unknown> = {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: cellFontFace,\n color: cell.color,\n bold: cell.bold,\n italic: cell.italic,\n underline: convertUnderline(cell.underline),\n strike: convertStrike(cell.strike),\n highlight: cell.highlight,\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n\n if (cell.runs && cell.runs.length > 0) {\n const textItems = cell.runs.map((run) => ({\n text: run.text,\n options: {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: run.fontFamily ?? cellFontFace,\n color: run.color ?? cell.color,\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline: convertUnderline(run.underline ?? cell.underline),\n strike: convertStrike(run.strike ?? cell.strike),\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n }));\n return {\n text: textItems,\n options: {\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n },\n };\n }\n\n return {\n text: cell.text,\n options: cellOptions,\n };\n }),\n );\n\n const content = getContentArea(node);\n const tableOptions: Record<string, unknown> = {\n x: pxToIn(content.x),\n y: pxToIn(content.y),\n w: pxToIn(content.w),\n h: pxToIn(content.h),\n colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),\n rowH: resolveRowHeights(node).map((height) => pxToIn(height)),\n margin: 0,\n };\n\n if (node.cellBorder) {\n tableOptions.border = {\n color: node.cellBorder.color ?? \"000000\",\n pt:\n node.cellBorder.width !== undefined ? pxToPt(node.cellBorder.width) : 1,\n type: node.cellBorder.dashType ?? \"solid\",\n };\n }\n\n ctx.slide.addTable(tableRows, tableOptions);\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,QAC/B,IAAI,MAAM,KAAK,SAAS;EACtB,MAAM,eAAe,KAAK;EAC1B,MAAM,cAAuC;GAC3C,UAAU,OAAO,KAAK,YAAY,EAAE;GACpC,UAAU;GACV,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;GACJ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EAEA,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAelC,OAAO;GACL,MAfgB,KAAK,KAAK,KAAK,SAAS;IACxC,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,KAAK,YAAY,EAAE;KACpC,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF,EAEgB;GACd,SAAS;IACP,OAAO,KAAK,aAAa;IACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;IACJ,SAAS,KAAK;IACd,SAAS,KAAK;GAChB;EACF;EAGF,OAAO;GACL,MAAM,KAAK;GACX,SAAS;EACX;CACF,CAAC,CACH;CAEA,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAwC;EAC5C,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,GAAG,OAAO,QAAQ,CAAC;EACnB,MAAM,oBAAoB,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,OAAO,KAAK,CAAC;EACvE,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,WAAW,OAAO,MAAM,CAAC;EAC5D,QAAQ;CACV;CAEA,IAAI,KAAK,YACP,aAAa,SAAS;EACpB,OAAO,KAAK,WAAW,SAAS;EAChC,IACE,KAAK,WAAW,UAAU,KAAA,IAAY,OAAO,KAAK,WAAW,KAAK,IAAI;EACxE,MAAM,KAAK,WAAW,YAAY;CACpC;CAGF,IAAI,MAAM,SAAS,WAAW,YAAY;AAC5C"}
@@ -6,20 +6,24 @@ function renderTextNode(node, ctx) {
6
6
  if (node.runs && node.runs.length > 0) {
7
7
  const fontSizePx = node.fontSize ?? 24;
8
8
  const fontFamily = node.fontFamily ?? "Noto Sans JP";
9
- const textItems = node.runs.map((run) => ({
10
- text: run.text,
11
- options: {
12
- fontSize: pxToPt(fontSizePx),
13
- fontFace: run.fontFamily ?? fontFamily,
14
- color: run.color ?? node.color,
15
- bold: run.bold ?? node.bold,
16
- italic: run.italic ?? node.italic,
17
- underline: convertUnderline(run.underline ?? node.underline),
18
- strike: convertStrike(run.strike ?? node.strike),
19
- highlight: run.highlight ?? node.highlight,
20
- ...run.href ? { hyperlink: { url: run.href } } : {}
21
- }
22
- }));
9
+ const textItems = node.runs.map((run) => {
10
+ const letterSpacingPx = run.letterSpacing ?? node.letterSpacing;
11
+ return {
12
+ text: run.text,
13
+ options: {
14
+ fontSize: pxToPt(fontSizePx),
15
+ fontFace: run.fontFamily ?? fontFamily,
16
+ color: run.color ?? node.color,
17
+ bold: run.bold ?? node.bold,
18
+ italic: run.italic ?? node.italic,
19
+ underline: convertUnderline(run.underline ?? node.underline),
20
+ strike: convertStrike(run.strike ?? node.strike),
21
+ highlight: run.highlight ?? node.highlight,
22
+ charSpacing: letterSpacingPx !== void 0 ? pxToPt(letterSpacingPx) : void 0,
23
+ ...run.href ? { hyperlink: { url: run.href } } : {}
24
+ }
25
+ };
26
+ });
23
27
  ctx.slide.addText(textItems, {
24
28
  x: textOptions.x,
25
29
  y: textOptions.y,
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n createTextOptions,\n convertUnderline,\n convertStrike,\n} from \"../textOptions.ts\";\nimport { pxToPt } from \"../units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n const textOptions = createTextOptions(node);\n\n if (node.runs && node.runs.length > 0) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const textItems = node.runs.map((run) => ({\n text: run.text,\n options: {\n fontSize: pxToPt(fontSizePx),\n fontFace: run.fontFamily ?? fontFamily,\n color: run.color ?? node.color,\n bold: run.bold ?? node.bold,\n italic: run.italic ?? node.italic,\n underline: convertUnderline(run.underline ?? node.underline),\n strike: convertStrike(run.strike ?? node.strike),\n highlight: run.highlight ?? node.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n }));\n ctx.slide.addText(textItems, {\n x: textOptions.x,\n y: textOptions.y,\n w: textOptions.w,\n h: textOptions.h,\n align: textOptions.align,\n valign: textOptions.valign,\n margin: textOptions.margin,\n lineSpacingMultiple: textOptions.lineSpacingMultiple,\n });\n } else {\n ctx.slide.addText(node.text ?? \"\", textOptions);\n }\n}\n"],"mappings":";;;AAWA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAE1C,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;EACrC,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EACtC,MAAM,YAAY,KAAK,KAAK,KAAK,SAAS;GACxC,MAAM,IAAI;GACV,SAAS;IACP,UAAU,OAAO,UAAU;IAC3B,UAAU,IAAI,cAAc;IAC5B,OAAO,IAAI,SAAS,KAAK;IACzB,MAAM,IAAI,QAAQ,KAAK;IACvB,QAAQ,IAAI,UAAU,KAAK;IAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;IAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;IAC/C,WAAW,IAAI,aAAa,KAAK;IACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;GACrD;EACF,EAAE;EACF,IAAI,MAAM,QAAQ,WAAW;GAC3B,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,OAAO,YAAY;GACnB,QAAQ,YAAY;GACpB,QAAQ,YAAY;GACpB,qBAAqB,YAAY;EACnC,CAAC;CACH,OACE,IAAI,MAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAElD"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n createTextOptions,\n convertUnderline,\n convertStrike,\n} from \"../textOptions.ts\";\nimport { pxToPt } from \"../units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n const textOptions = createTextOptions(node);\n\n if (node.runs && node.runs.length > 0) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const textItems = node.runs.map((run) => {\n const letterSpacingPx = run.letterSpacing ?? node.letterSpacing;\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(fontSizePx),\n fontFace: run.fontFamily ?? fontFamily,\n color: run.color ?? node.color,\n bold: run.bold ?? node.bold,\n italic: run.italic ?? node.italic,\n underline: convertUnderline(run.underline ?? node.underline),\n strike: convertStrike(run.strike ?? node.strike),\n highlight: run.highlight ?? node.highlight,\n charSpacing:\n letterSpacingPx !== undefined ? pxToPt(letterSpacingPx) : undefined,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n };\n });\n ctx.slide.addText(textItems, {\n x: textOptions.x,\n y: textOptions.y,\n w: textOptions.w,\n h: textOptions.h,\n align: textOptions.align,\n valign: textOptions.valign,\n margin: textOptions.margin,\n lineSpacingMultiple: textOptions.lineSpacingMultiple,\n });\n } else {\n ctx.slide.addText(node.text ?? \"\", textOptions);\n }\n}\n"],"mappings":";;;AAWA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAE1C,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAAG;EACrC,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EACtC,MAAM,YAAY,KAAK,KAAK,KAAK,QAAQ;GACvC,MAAM,kBAAkB,IAAI,iBAAiB,KAAK;GAClD,OAAO;IACL,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,UAAU;KAC3B,UAAU,IAAI,cAAc;KAC5B,OAAO,IAAI,SAAS,KAAK;KACzB,MAAM,IAAI,QAAQ,KAAK;KACvB,QAAQ,IAAI,UAAU,KAAK;KAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;KAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;KAC/C,WAAW,IAAI,aAAa,KAAK;KACjC,aACE,oBAAoB,KAAA,IAAY,OAAO,eAAe,IAAI,KAAA;KAC5D,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;IACrD;GACF;EACF,CAAC;EACD,IAAI,MAAM,QAAQ,WAAW;GAC3B,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,GAAG,YAAY;GACf,OAAO,YAAY;GACnB,QAAQ,YAAY;GACpB,QAAQ,YAAY;GACpB,qBAAqB,YAAY;EACnC,CAAC;CACH,OACE,IAAI,MAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAElD"}
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentCenterX),\n y: pxToIn(parentBottomY),\n w: 0,\n h: pxToIn(midY - parentBottomY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(minX),\n y: pxToIn(midY),\n w: pxToIn(maxX - minX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 垂直線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(childCenterX),\n y: pxToIn(midY),\n w: 0,\n h: pxToIn(childTopY - midY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentRightX),\n y: pxToIn(parentCenterY),\n w: pxToIn(midX - parentRightX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(minY),\n w: 0,\n h: pxToIn(maxY - minY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 水平線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(childCenterY),\n w: pxToIn(childLeftX - midX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return ctx.pptx.ShapeType.rect;\n case \"roundRect\":\n return ctx.pptx.ShapeType.roundRect;\n case \"ellipse\":\n return ctx.pptx.ShapeType.ellipse;\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n // ノードの背景\n ctx.slide.addShape(shapeType, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fill: { color },\n line: { color: \"333333\", width: pxToPt(1 * sf) },\n });\n\n // ノードのラベル\n ctx.slide.addText(layoutNode.item.label, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fontSize: pxToPt(12 * sf),\n fontFace: \"Noto Sans JP\",\n color: \"FFFFFF\",\n align: \"center\",\n valign: \"middle\",\n });\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n treeSize.width,\n treeSize.height,\n \"tree\",\n ctx.buildContext.diagnostics,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"../units.ts\";\nimport { calcScaleFactor } from \"../utils/scaleToFit.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentCenterX),\n y: pxToIn(parentBottomY),\n w: 0,\n h: pxToIn(midY - parentBottomY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(minX),\n y: pxToIn(midY),\n w: pxToIn(maxX - minX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 垂直線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(childCenterX),\n y: pxToIn(midY),\n w: 0,\n h: pxToIn(childTopY - midY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(parentRightX),\n y: pxToIn(parentCenterY),\n w: pxToIn(midX - parentRightX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(minY),\n w: 0,\n h: pxToIn(maxY - minY),\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n\n // 水平線(中間点から子まで)\n ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(midX),\n y: pxToIn(childCenterY),\n w: pxToIn(childLeftX - midX),\n h: 0,\n line: { color: lineColor, width: pxToPt(lineWidth * sf) },\n });\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return ctx.pptx.ShapeType.rect;\n case \"roundRect\":\n return ctx.pptx.ShapeType.roundRect;\n case \"ellipse\":\n return ctx.pptx.ShapeType.ellipse;\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n // ノードの背景\n ctx.slide.addShape(shapeType, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fill: { color },\n line: { color: \"333333\", width: pxToPt(1 * sf) },\n });\n\n // ノードのラベル\n ctx.slide.addText(layoutNode.item.label, {\n x: pxToIn(drawX),\n y: pxToIn(drawY),\n w: pxToIn(drawW),\n h: pxToIn(drawH),\n fontSize: pxToPt(12 * sf),\n fontFace: \"Noto Sans JP\",\n color: \"FFFFFF\",\n align: \"center\",\n valign: \"middle\",\n });\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const content = getContentArea(node);\n const scaleFactor = calcScaleFactor(\n content.w,\n content.h,\n treeSize.width,\n treeSize.height,\n \"tree\",\n ctx.buildContext.diagnostics,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { getNodeDef } from "../registry/nodeRegistry.js";
3
3
  import { pxToIn, pxToPt } from "./units.js";
4
4
  import { convertStrike, convertUnderline } from "./textOptions.js";
5
5
  import "../registry/index.js";
6
+ import { registerBackgroundGradient } from "./gradientFills.js";
6
7
  import { renderBackgroundAndBorder } from "./utils/backgroundBorder.js";
7
8
  //#region src/renderPptx/renderPptx.ts
8
9
  async function loadPptxGenJS() {
@@ -160,8 +161,11 @@ async function renderPptx(pages, slidePx, buildContext, master) {
160
161
  };
161
162
  const isLinelike = data.type === "line" || data.type === "arrow";
162
163
  const rootBackgroundColor = !isLinelike ? data.backgroundColor : void 0;
164
+ const rootBackgroundGradient = !isLinelike ? data.backgroundGradient : void 0;
163
165
  const rootHasOpacity = !isLinelike && "opacity" in data && data.opacity !== void 0;
164
- if (rootBackgroundColor && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
166
+ const rootGradientMarker = rootBackgroundGradient && !rootHasOpacity ? registerBackgroundGradient(rootBackgroundGradient, void 0, buildContext.gradientFills) : void 0;
167
+ if (rootGradientMarker) slide.background = { color: rootGradientMarker };
168
+ else if (rootBackgroundColor && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
165
169
  const rootBackgroundImage = !isLinelike ? data.backgroundImage : void 0;
166
170
  if (rootBackgroundImage) {
167
171
  const cachedData = getImageData(rootBackgroundImage.src, buildContext.imageDataCache);
@@ -173,7 +177,7 @@ async function renderPptx(pages, slidePx, buildContext, master) {
173
177
  * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)
174
178
  */
175
179
  function renderNode(node, isRoot = false) {
176
- if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || rootBackgroundColor && !rootHasOpacity)) {
180
+ if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootBackgroundGradient) && !rootHasOpacity)) {
177
181
  const { border, borderRadius } = node;
178
182
  if (Boolean(border && (border.color !== void 0 || border.width !== void 0 || border.dashType !== void 0))) {
179
183
  const line = {