@hirokisakabe/pom 8.6.0 → 8.8.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 +33 -23
- package/dist/autoFit/strategies/reduceFontSize.js +23 -7
- package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
- package/dist/autoFit/strategies/uniformScale.js +19 -4
- package/dist/autoFit/strategies/uniformScale.js.map +1 -1
- package/dist/buildContext.js +4 -2
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +3 -1
- package/dist/buildPptx.js.map +1 -1
- package/dist/parseXml/coercionRules.js +8 -1
- package/dist/parseXml/coercionRules.js.map +1 -1
- package/dist/parseXml/parseXml.d.ts.map +1 -1
- package/dist/parseXml/parseXml.js +4 -3
- package/dist/parseXml/parseXml.js.map +1 -1
- package/dist/parseXml/serializeXml.d.ts.map +1 -1
- package/dist/parseXml/serializeXml.js +1 -0
- package/dist/parseXml/serializeXml.js.map +1 -1
- package/dist/registry/definitions/list.js +2 -1
- package/dist/registry/definitions/list.js.map +1 -1
- package/dist/registry/definitions/text.js +2 -1
- package/dist/registry/definitions/text.js.map +1 -1
- package/dist/registry/xmlChildRules.js +1 -1
- package/dist/registry/xmlChildRules.js.map +1 -1
- package/dist/renderPptx/glowEffects.js +147 -0
- package/dist/renderPptx/glowEffects.js.map +1 -0
- package/dist/renderPptx/gradientFills.js +14 -1
- package/dist/renderPptx/gradientFills.js.map +1 -1
- package/dist/renderPptx/nodes/chart.js +43 -3
- package/dist/renderPptx/nodes/chart.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +13 -6
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/list.js +1 -0
- package/dist/renderPptx/nodes/list.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +24 -2
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/table.js +1 -1
- package/dist/renderPptx/nodes/table.js.map +1 -1
- package/dist/renderPptx/nodes/text.js +9 -3
- package/dist/renderPptx/nodes/text.js.map +1 -1
- package/dist/renderPptx/renderPptx.js +1 -1
- package/dist/renderPptx/units.js +8 -1
- package/dist/renderPptx/units.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -2
- package/dist/types.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glowEffects.js","names":[],"sources":["../../src/renderPptx/glowEffects.ts"],"sourcesContent":["/**\n * Shape / Icon の glow (光彩) 効果の実現\n *\n * pptxgenjs の Shape options には glow 相当のネイティブサポートがないため\n * (TextPropsOptions の glow は文字グリフ用)、以下の方式で実現する:\n *\n * 1. レンダリング時: glow 指定のある shape に一意なマーカー名を `objectName`\n * として埋め込み、レジストリに登録する。マーカー名は `<p:cNvPr name=\"...\"/>`\n * として PPTX 出力 XML に書き込まれる。\n * 2. 出力時: pptx.write() / writeFile() をラップし、出力 zip 内のスライド XML\n * の該当 `<p:sp>` の `<p:spPr>` 末尾に DrawingML ネイティブの\n * `<a:effectLst><a:glow>...</a:glow></a:effectLst>` を挿入する。\n *\n * 文字列置換採用理由 / 注意点は gradientFills.ts と同様。\n */\nimport type { TextGlow } from \"../types.ts\";\nimport { pxToEmu } from \"./units.ts\";\n\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\ntype WriteProps = NonNullable<Parameters<PptxGenJSInstance[\"write\"]>[0]>;\ntype WriteFileProps = NonNullable<\n Parameters<PptxGenJSInstance[\"writeFile\"]>[0]\n>;\n\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 RegisteredGlow {\n /** objectName に埋め込むマーカー文字列 */\n marker: string;\n /** size (px) — XML 出力時に EMU に変換する */\n sizePx: number;\n /** opacity 0-1 */\n opacity: number;\n /** 6桁 HEX (# なし、大文字) */\n color: string;\n}\n\nconst MARKER_PREFIX = \"pom-glow:\";\n\n/**\n * glow 仕様を一意なマーカー名にマップし、後処理で\n * `<a:effectLst><a:glow>` 要素として挿入できるようにするレジストリ\n */\nexport class GlowEffectRegistry {\n private readonly markerBySpec = new Map<string, string>();\n private readonly registered: RegisteredGlow[] = [];\n\n /**\n * glow を登録し、`objectName` に渡すマーカー文字列を返す。\n * 同じ仕様の glow は同じマーカーを返す。\n */\n register(glow: TextGlow): string {\n const sizePx = glow.size ?? 8;\n const opacity = glow.opacity ?? 0.75;\n const color = (glow.color ?? \"FFFFFF\").replace(/^#/, \"\").toUpperCase();\n const specKey = JSON.stringify({ sizePx, opacity, color });\n const existing = this.markerBySpec.get(specKey);\n if (existing) return existing;\n\n const index = this.registered.length;\n const marker = `${MARKER_PREFIX}${index}`;\n this.markerBySpec.set(specKey, marker);\n this.registered.push({ marker, sizePx, opacity, color });\n return marker;\n }\n\n get isEmpty(): boolean {\n return this.registered.length === 0;\n }\n\n get entries(): readonly RegisteredGlow[] {\n return this.registered;\n }\n}\n\n/**\n * DrawingML の `<a:glow>` 要素を構築する (effectLst で囲まない、内側だけ)\n *\n * - rad (光彩半径): px → EMU\n * - alpha (透明度): 0-1 → 1/1000 % (0-100000)\n */\nfunction buildGlowXml(entry: RegisteredGlow): string {\n const rad = Math.round(pxToEmu(entry.sizePx));\n const alpha = Math.round(entry.opacity * 100000);\n return `<a:glow rad=\"${rad}\"><a:srgbClr val=\"${entry.color}\"><a:alpha val=\"${alpha}\"/></a:srgbClr></a:glow>`;\n}\n\n/**\n * 1 つの slide XML を受け取り、登録された glow を該当 shape に適用した XML を返す\n *\n * `<p:cNvPr ... name=\"pom-glow:N\">` を含む `<p:sp>` ブロックを正規表現で検出し、\n * 以下のいずれかで `<a:glow>` を挿入する:\n *\n * 1. 既に `<a:effectLst>...</a:effectLst>` が存在する場合\n * (= pptxgenjs が shape の `shadow` 等で同要素を生成済み):\n * その `effectLst` 内側の末尾 (`</a:effectLst>` の直前) に `<a:glow>` を追加する。\n * OOXML スキーマ上 `<p:spPr>` 内の `<a:effectLst>` は最大 1 個までで、二重に\n * 並べると不正になるため。\n * 2. 既存 `effectLst` が無い場合:\n * `</p:spPr>` の直前に新規 `<a:effectLst><a:glow/></a:effectLst>` を挿入する。\n *\n * pptxgenjs は cNvPr を `<p:cNvPr.../>` (self-closing) と\n * `<p:cNvPr...></p:cNvPr>` (open+close) のどちらの形式でも出力し得るため、\n * cNvPr の閉じ形式に依存せず name 属性のあとから lazy に進める。\n */\nfunction applyGlowToXml(xml: string, registry: GlowEffectRegistry): string {\n let result = xml;\n for (const entry of registry.entries) {\n const glowXml = buildGlowXml(entry);\n const re = new RegExp(\n `(<p:cNvPr[^>]*name=\"${entry.marker}\"[\\\\s\\\\S]*?)(</p:spPr>)`,\n \"g\",\n );\n result = result.replace(re, (_match, prefix, suffix) => {\n const block = prefix as string;\n // shape ブロック内に既存の effectLst があるかを `</a:effectLst>` の有無で判定する。\n // 別 shape の effectLst を誤って書き換えないよう、検索範囲は prefix\n // (= 該当 cNvPr 〜 直後の </p:spPr> の手前まで) に限定している。\n const closingIdx = block.lastIndexOf(\"</a:effectLst>\");\n if (closingIdx >= 0) {\n const before = block.substring(0, closingIdx);\n const after = block.substring(closingIdx);\n return `${before}${glowXml}${after}${suffix as string}`;\n }\n return `${block}<a:effectLst>${glowXml}</a:effectLst>${suffix as string}`;\n });\n }\n return result;\n}\n\n/**\n * 出力 zip 内のスライド XML に glow 効果を挿入する\n */\nexport async function applyGlowEffects(\n data: Uint8Array | ArrayBuffer,\n registry: GlowEffectRegistry,\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 const original = await file.async(\"text\");\n const replaced = applyGlowToXml(original, registry);\n if (replaced !== original) {\n zip.file(path, replaced);\n }\n }\n return zip;\n}\n\n/**\n * pptx インスタンスの write / writeFile をラップし、出力時に glow 後処理を適用する。\n *\n * 既に他の後処理 (gradientFills 等) によりラップされている場合に備え、\n * 元の関数を保存してチェーン可能にしている。\n */\nexport function patchPptxWriteForGlowEffects(\n pptx: PptxGenJSInstance,\n registry: GlowEffectRegistry,\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 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 applyGlowEffects(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 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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"}
|
|
@@ -58,6 +58,19 @@ function registerBackgroundGradient(value, opacity, registry) {
|
|
|
58
58
|
return registry.register(gradient, opacity);
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
+
* textGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。
|
|
62
|
+
* 戻り値のマーカー色を text run の color に渡すと、pptxgenjs が出力する
|
|
63
|
+
* `<a:rPr><a:solidFill><a:srgbClr val="マーカー色"/></a:solidFill></a:rPr>` が
|
|
64
|
+
* 後処理で gradFill に置換され、PowerPoint 上ネイティブの文字グラデーションとして
|
|
65
|
+
* 表示・編集可能になる。
|
|
66
|
+
* パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。
|
|
67
|
+
*/
|
|
68
|
+
function registerTextGradient(value, registry) {
|
|
69
|
+
const gradient = parseLinearGradient(value);
|
|
70
|
+
if (!gradient) return void 0;
|
|
71
|
+
return registry.register(gradient);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
61
74
|
* LinearGradient を DrawingML の `<a:gradFill>` 要素に変換する
|
|
62
75
|
*
|
|
63
76
|
* - カラーストップ位置: % → 1/1000 % (0-100000)
|
|
@@ -134,6 +147,6 @@ function patchPptxWriteForGradientFills(pptx, registry) {
|
|
|
134
147
|
pptx.writeFile = patchedWriteFile;
|
|
135
148
|
}
|
|
136
149
|
//#endregion
|
|
137
|
-
export { GradientFillRegistry, patchPptxWriteForGradientFills, registerBackgroundGradient };
|
|
150
|
+
export { GradientFillRegistry, patchPptxWriteForGradientFills, registerBackgroundGradient, registerTextGradient };
|
|
138
151
|
|
|
139
152
|
//# sourceMappingURL=gradientFills.js.map
|
|
@@ -1 +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
|
+
{"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 * textGradient 属性値をパースしてレジストリに登録し、マーカー色を返す。\n * 戻り値のマーカー色を text run の color に渡すと、pptxgenjs が出力する\n * `<a:rPr><a:solidFill><a:srgbClr val=\"マーカー色\"/></a:solidFill></a:rPr>` が\n * 後処理で gradFill に置換され、PowerPoint 上ネイティブの文字グラデーションとして\n * 表示・編集可能になる。\n * パースできない場合 (スキーマ検証済みのため通常発生しない) は undefined を返す。\n */\nexport function registerTextGradient(\n value: string,\n registry: GradientFillRegistry,\n): string | undefined {\n const gradient = parseLinearGradient(value);\n if (!gradient) return undefined;\n return registry.register(gradient);\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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"}
|
|
@@ -6,14 +6,54 @@ function renderChartNode(node, ctx) {
|
|
|
6
6
|
labels: d.labels,
|
|
7
7
|
values: d.values
|
|
8
8
|
}));
|
|
9
|
+
const isSparkline = node.sparkline === true && (node.chartType === "bar" || node.chartType === "line" || node.chartType === "area");
|
|
9
10
|
const chartOptions = {
|
|
10
11
|
...getContentAreaIn(node),
|
|
11
|
-
showLegend: node.showLegend ?? false,
|
|
12
|
-
showTitle: node.showTitle ?? false,
|
|
13
|
-
title: node.title,
|
|
12
|
+
showLegend: isSparkline ? false : node.showLegend ?? false,
|
|
13
|
+
showTitle: isSparkline ? false : node.showTitle ?? false,
|
|
14
|
+
title: isSparkline ? void 0 : node.title,
|
|
14
15
|
chartColors: node.chartColors
|
|
15
16
|
};
|
|
16
17
|
if (node.chartType === "radar" && node.radarStyle) chartOptions.radarStyle = node.radarStyle;
|
|
18
|
+
if (isSparkline) {
|
|
19
|
+
chartOptions.catAxisHidden = true;
|
|
20
|
+
chartOptions.valAxisHidden = true;
|
|
21
|
+
chartOptions.catAxisLineShow = false;
|
|
22
|
+
chartOptions.valAxisLineShow = false;
|
|
23
|
+
chartOptions.showCatAxisTitle = false;
|
|
24
|
+
chartOptions.showValAxisTitle = false;
|
|
25
|
+
chartOptions.catGridLine = { style: "none" };
|
|
26
|
+
chartOptions.valGridLine = { style: "none" };
|
|
27
|
+
chartOptions.chartArea = {
|
|
28
|
+
fill: {
|
|
29
|
+
type: "solid",
|
|
30
|
+
color: "FFFFFF",
|
|
31
|
+
transparency: 100
|
|
32
|
+
},
|
|
33
|
+
border: {
|
|
34
|
+
color: "FFFFFF",
|
|
35
|
+
pt: 0
|
|
36
|
+
},
|
|
37
|
+
roundedCorners: false
|
|
38
|
+
};
|
|
39
|
+
chartOptions.plotArea = {
|
|
40
|
+
fill: {
|
|
41
|
+
type: "solid",
|
|
42
|
+
color: "FFFFFF",
|
|
43
|
+
transparency: 100
|
|
44
|
+
},
|
|
45
|
+
border: {
|
|
46
|
+
color: "FFFFFF",
|
|
47
|
+
pt: 0
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
chartOptions.layout = {
|
|
51
|
+
x: 0,
|
|
52
|
+
y: 0,
|
|
53
|
+
w: 1,
|
|
54
|
+
h: 1
|
|
55
|
+
};
|
|
56
|
+
}
|
|
17
57
|
ctx.slide.addChart(node.chartType, chartData, chartOptions);
|
|
18
58
|
}
|
|
19
59
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart.js","names":[],"sources":["../../../src/renderPptx/nodes/chart.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { getContentAreaIn } from \"../utils/contentArea.ts\";\n\ntype ChartPositionedNode = Extract<PositionedNode, { type: \"chart\" }>;\n\nexport function renderChartNode(\n node: ChartPositionedNode,\n ctx: RenderContext,\n): void {\n const chartData = node.data.map((d) => ({\n name: d.name,\n labels: d.labels,\n values: d.values,\n }));\n\n const chartOptions: Record<string, unknown> = {\n ...getContentAreaIn(node),\n showLegend: node.showLegend ?? false,\n showTitle: node.showTitle ?? false,\n title: node.title,\n chartColors: node.chartColors,\n };\n\n // radar専用オプション\n if (node.chartType === \"radar\" && node.radarStyle) {\n chartOptions.radarStyle = node.radarStyle;\n }\n\n ctx.slide.addChart(node.chartType, chartData, chartOptions);\n}\n"],"mappings":";;AAMA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,OAAO;EACtC,MAAM,EAAE;EACR,QAAQ,EAAE;EACV,QAAQ,EAAE;CACZ,EAAE;
|
|
1
|
+
{"version":3,"file":"chart.js","names":[],"sources":["../../../src/renderPptx/nodes/chart.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { getContentAreaIn } from \"../utils/contentArea.ts\";\n\ntype ChartPositionedNode = Extract<PositionedNode, { type: \"chart\" }>;\n\nexport function renderChartNode(\n node: ChartPositionedNode,\n ctx: RenderContext,\n): void {\n const chartData = node.data.map((d) => ({\n name: d.name,\n labels: d.labels,\n values: d.values,\n }));\n\n // sparkline モードは bar / line / area のみ対応。pie / doughnut / radar は\n // 元々凡例 / 軸の概念が異なるため sparkline=true でも通常描画にフォールバックする。\n const isSparkline =\n node.sparkline === true &&\n (node.chartType === \"bar\" ||\n node.chartType === \"line\" ||\n node.chartType === \"area\");\n\n const chartOptions: Record<string, unknown> = {\n ...getContentAreaIn(node),\n showLegend: isSparkline ? false : (node.showLegend ?? false),\n showTitle: isSparkline ? false : (node.showTitle ?? false),\n title: isSparkline ? undefined : node.title,\n chartColors: node.chartColors,\n };\n\n // radar専用オプション\n if (node.chartType === \"radar\" && node.radarStyle) {\n chartOptions.radarStyle = node.radarStyle;\n }\n\n // sparkline モード: 凡例 / 軸 / グリッド線 / マージンをすべて非表示にし、\n // プロット領域をチャート領域いっぱいに広げて小寸法でも視認できるようにする\n if (isSparkline) {\n chartOptions.catAxisHidden = true;\n chartOptions.valAxisHidden = true;\n chartOptions.catAxisLineShow = false;\n chartOptions.valAxisLineShow = false;\n chartOptions.showCatAxisTitle = false;\n chartOptions.showValAxisTitle = false;\n chartOptions.catGridLine = { style: \"none\" };\n chartOptions.valGridLine = { style: \"none\" };\n chartOptions.chartArea = {\n fill: { type: \"solid\", color: \"FFFFFF\", transparency: 100 },\n border: { color: \"FFFFFF\", pt: 0 },\n roundedCorners: false,\n };\n chartOptions.plotArea = {\n fill: { type: \"solid\", color: \"FFFFFF\", transparency: 100 },\n border: { color: \"FFFFFF\", pt: 0 },\n };\n chartOptions.layout = { x: 0, y: 0, w: 1, h: 1 };\n }\n\n ctx.slide.addChart(node.chartType, chartData, chartOptions);\n}\n"],"mappings":";;AAMA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,OAAO;EACtC,MAAM,EAAE;EACR,QAAQ,EAAE;EACV,QAAQ,EAAE;CACZ,EAAE;CAIF,MAAM,cACJ,KAAK,cAAc,SAClB,KAAK,cAAc,SAClB,KAAK,cAAc,UACnB,KAAK,cAAc;CAEvB,MAAM,eAAwC;EAC5C,GAAG,iBAAiB,IAAI;EACxB,YAAY,cAAc,QAAS,KAAK,cAAc;EACtD,WAAW,cAAc,QAAS,KAAK,aAAa;EACpD,OAAO,cAAc,KAAA,IAAY,KAAK;EACtC,aAAa,KAAK;CACpB;CAGA,IAAI,KAAK,cAAc,WAAW,KAAK,YACrC,aAAa,aAAa,KAAK;CAKjC,IAAI,aAAa;EACf,aAAa,gBAAgB;EAC7B,aAAa,gBAAgB;EAC7B,aAAa,kBAAkB;EAC/B,aAAa,kBAAkB;EAC/B,aAAa,mBAAmB;EAChC,aAAa,mBAAmB;EAChC,aAAa,cAAc,EAAE,OAAO,OAAO;EAC3C,aAAa,cAAc,EAAE,OAAO,OAAO;EAC3C,aAAa,YAAY;GACvB,MAAM;IAAE,MAAM;IAAS,OAAO;IAAU,cAAc;GAAI;GAC1D,QAAQ;IAAE,OAAO;IAAU,IAAI;GAAE;GACjC,gBAAgB;EAClB;EACA,aAAa,WAAW;GACtB,MAAM;IAAE,MAAM;IAAS,OAAO;IAAU,cAAc;GAAI;GAC1D,QAAQ;IAAE,OAAO;IAAU,IAAI;GAAE;EACnC;EACA,aAAa,SAAS;GAAE,GAAG;GAAG,GAAG;GAAG,GAAG;GAAG,GAAG;EAAE;CACjD;CAEA,IAAI,MAAM,SAAS,KAAK,WAAW,WAAW,YAAY;AAC5D"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { pxToIn } from "../units.js";
|
|
1
|
+
import { pxToIn, pxToPt } from "../units.js";
|
|
2
2
|
//#region src/renderPptx/nodes/icon.ts
|
|
3
3
|
function renderIconNode(node, ctx) {
|
|
4
4
|
if (node.variant) {
|
|
5
5
|
const isCircle = node.variant.startsWith("circle");
|
|
6
6
|
const isFilled = node.variant.endsWith("-filled");
|
|
7
7
|
const colorValue = (node.bgColor ?? "#E0E0E0").replace(/^#/, "");
|
|
8
|
+
const variantDefaultLine = isFilled ? void 0 : {
|
|
9
|
+
color: colorValue,
|
|
10
|
+
width: 1.5
|
|
11
|
+
};
|
|
12
|
+
const outlineLine = node.outline ? {
|
|
13
|
+
color: node.outline.color ?? variantDefaultLine?.color ?? "FFFFFF",
|
|
14
|
+
width: node.outline.size !== void 0 ? pxToPt(node.outline.size) : variantDefaultLine?.width ?? 1
|
|
15
|
+
} : variantDefaultLine;
|
|
16
|
+
const glowMarker = node.glow ? ctx.buildContext.glowEffects.register(node.glow) : void 0;
|
|
8
17
|
const shapeType = isCircle ? "ellipse" : "roundRect";
|
|
9
18
|
const shapeOptions = {
|
|
10
19
|
x: pxToIn(node.bgX ?? node.x),
|
|
@@ -12,12 +21,10 @@ function renderIconNode(node, ctx) {
|
|
|
12
21
|
w: pxToIn(node.bgW ?? node.w),
|
|
13
22
|
h: pxToIn(node.bgH ?? node.h),
|
|
14
23
|
fill: isFilled ? { color: colorValue } : { type: "none" },
|
|
15
|
-
line:
|
|
16
|
-
color: colorValue,
|
|
17
|
-
width: 1.5
|
|
18
|
-
},
|
|
24
|
+
line: outlineLine,
|
|
19
25
|
rectRadius: isCircle ? void 0 : .1,
|
|
20
|
-
rotate: node.rotate
|
|
26
|
+
rotate: node.rotate,
|
|
27
|
+
objectName: glowMarker
|
|
21
28
|
};
|
|
22
29
|
ctx.slide.addShape(shapeType, shapeOptions);
|
|
23
30
|
}
|
|
@@ -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:
|
|
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, pxToPt } 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 // 背景図形の line のデフォルト: outlined variant は colorValue / 1.5pt、\n // filled variant は undefined (枠線なし)。\n const variantDefaultLine = isFilled\n ? undefined\n : { color: colorValue, width: 1.5 };\n // outline 指定時は variant のデフォルト line とフィールド単位でマージする。\n // outline 側で省略された属性は variant default の値を引き継ぐので、例えば\n // outlined variant に `outline.color` だけ指定すると、太さは 1.5pt のまま\n // 色だけ outline で上書きされる。\n const outlineLine = node.outline\n ? {\n color: node.outline.color ?? variantDefaultLine?.color ?? \"FFFFFF\",\n width:\n node.outline.size !== undefined\n ? pxToPt(node.outline.size)\n : (variantDefaultLine?.width ?? 1),\n }\n : variantDefaultLine;\n\n const glowMarker = node.glow\n ? ctx.buildContext.glowEffects.register(node.glow)\n : undefined;\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: outlineLine,\n rectRadius: isCircle ? undefined : 0.1,\n rotate: node.rotate,\n objectName: glowMarker,\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 rotate: node.rotate,\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;EAI3C,MAAM,qBAAqB,WACvB,KAAA,IACA;GAAE,OAAO;GAAY,OAAO;EAAI;EAKpC,MAAM,cAAc,KAAK,UACrB;GACE,OAAO,KAAK,QAAQ,SAAS,oBAAoB,SAAS;GAC1D,OACE,KAAK,QAAQ,SAAS,KAAA,IAClB,OAAO,KAAK,QAAQ,IAAI,IACvB,oBAAoB,SAAS;EACtC,IACA;EAEJ,MAAM,aAAa,KAAK,OACpB,IAAI,aAAa,YAAY,SAAS,KAAK,IAAI,IAC/C,KAAA;EAEJ,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;GACN,YAAY,WAAW,KAAA,IAAY;GACnC,QAAQ,KAAK;GACb,YAAY;EACd;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;EAC9B,QAAQ,KAAK;CACf,CAAC;AACH"}
|
|
@@ -43,6 +43,7 @@ function buildListTextItems(items, parent, bullet) {
|
|
|
43
43
|
text,
|
|
44
44
|
options: {
|
|
45
45
|
...baseOptions,
|
|
46
|
+
fontSize: pxToPt(run.fontSize ?? style.fontSize),
|
|
46
47
|
fontFace: run.fontFamily ?? style.fontFamily,
|
|
47
48
|
color: run.color ?? style.color,
|
|
48
49
|
bold: run.bold ?? style.bold,
|
|
@@ -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 { pxToPt } from \"../units.ts\";\nimport {\n convertUnderline,\n convertStrike,\n resolveSubSup,\n} from \"../textOptions.ts\";\nimport { getContentAreaIn } 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 const subSup = resolveSubSup(li, parent);\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 subscript: subSup.subscript,\n superscript: subSup.superscript,\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 subscript: style.subscript,\n superscript: style.superscript,\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 const runSubSup = resolveSubSup(run, style);\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 subscript: runSubSup.subscript,\n superscript: runSubSup.superscript,\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.subscript !== undefined ||\n li.superscript !== 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 contentIn = getContentAreaIn(node);\n\n if (hasItemStyleOverride(node.items)) {\n // Li に個別スタイルがある場合は配列形式を使用\n const textItems = buildListTextItems(node.items, node, true);\n\n ctx.slide.addText(textItems, {\n ...contentIn,\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 ...contentIn,\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 subscript: node.subscript,\n superscript: node.superscript,\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 contentIn = getContentAreaIn(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 ...contentIn,\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 ...contentIn,\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 subscript: node.subscript,\n superscript: node.superscript,\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 { pxToPt } from \"../units.ts\";\nimport {\n convertUnderline,\n convertStrike,\n resolveSubSup,\n} from \"../textOptions.ts\";\nimport { getContentAreaIn } 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 const subSup = resolveSubSup(li, parent);\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 subscript: subSup.subscript,\n superscript: subSup.superscript,\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 subscript: style.subscript,\n superscript: style.superscript,\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 const runSubSup = resolveSubSup(run, style);\n textItems.push({\n text,\n options: {\n ...baseOptions,\n fontSize: pxToPt(run.fontSize ?? style.fontSize),\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 subscript: runSubSup.subscript,\n superscript: runSubSup.superscript,\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.subscript !== undefined ||\n li.superscript !== 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 contentIn = getContentAreaIn(node);\n\n if (hasItemStyleOverride(node.items)) {\n // Li に個別スタイルがある場合は配列形式を使用\n const textItems = buildListTextItems(node.items, node, true);\n\n ctx.slide.addText(textItems, {\n ...contentIn,\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 ...contentIn,\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 subscript: node.subscript,\n superscript: node.superscript,\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 contentIn = getContentAreaIn(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 ...contentIn,\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 ...contentIn,\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 subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n bullet: bulletOptions,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,16 +3,38 @@ import { getContentAreaIn } from "../utils/contentArea.js";
|
|
|
3
3
|
import { convertStrike, convertUnderline } from "../textOptions.js";
|
|
4
4
|
import { convertBorderLine, convertShadow } from "../utils/visualStyle.js";
|
|
5
5
|
//#region src/renderPptx/nodes/shape.ts
|
|
6
|
+
/**
|
|
7
|
+
* outline (Text と同じ書式の `outline.size` / `outline.color`) と
|
|
8
|
+
* 既存 `line` 属性 (`line.color` / `line.width` / `line.dashType`) を
|
|
9
|
+
* 1 つの BorderStyle にマージする。
|
|
10
|
+
*
|
|
11
|
+
* フィールド単位のマージで、`outline` の指定があるフィールドは `line` を
|
|
12
|
+
* 上書きするが、`outline` 側で省略されたフィールドは `line` の値を引き継ぎ、
|
|
13
|
+
* `line` にも値が無い場合は Text outline と同じ既定値 (`width: 1pt 相当` /
|
|
14
|
+
* `color: FFFFFF`) を採用する。`dashType` は `outline` に対応フィールドが
|
|
15
|
+
* 無いため `line.dashType` をそのまま使う。
|
|
16
|
+
*/
|
|
17
|
+
function resolveShapeLine(line, outline) {
|
|
18
|
+
if (!outline) return line;
|
|
19
|
+
return {
|
|
20
|
+
color: outline.color ?? line?.color ?? "FFFFFF",
|
|
21
|
+
width: outline.size ?? line?.width ?? 1,
|
|
22
|
+
dashType: line?.dashType
|
|
23
|
+
};
|
|
24
|
+
}
|
|
6
25
|
function renderShapeNode(node, ctx) {
|
|
26
|
+
const lineSpec = resolveShapeLine(node.line, node.outline);
|
|
27
|
+
const glowMarker = node.glow ? ctx.buildContext.glowEffects.register(node.glow) : void 0;
|
|
7
28
|
const shapeOptions = {
|
|
8
29
|
...getContentAreaIn(node),
|
|
9
30
|
fill: node.fill ? {
|
|
10
31
|
color: node.fill.color,
|
|
11
32
|
transparency: node.fill.transparency
|
|
12
33
|
} : void 0,
|
|
13
|
-
line:
|
|
34
|
+
line: lineSpec ? convertBorderLine(lineSpec) : void 0,
|
|
14
35
|
shadow: convertShadow(node.shadow),
|
|
15
|
-
rotate: node.rotate
|
|
36
|
+
rotate: node.rotate,
|
|
37
|
+
objectName: glowMarker
|
|
16
38
|
};
|
|
17
39
|
if (node.text) {
|
|
18
40
|
const fontSizePx = node.fontSize ?? 24;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentAreaIn } from \"../utils/contentArea.ts\";\nimport { convertBorderLine, convertShadow } from \"../utils/visualStyle.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const shapeOptions = {\n ...getContentAreaIn(node),\n fill: node.fill\n ? {\n color: node.fill.color,\n transparency: node.fill.transparency,\n }\n : undefined,\n line:
|
|
1
|
+
{"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import type { BorderStyle, PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { convertUnderline, convertStrike } from \"../textOptions.ts\";\nimport { getContentAreaIn } from \"../utils/contentArea.ts\";\nimport { convertBorderLine, convertShadow } from \"../utils/visualStyle.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\n/**\n * outline (Text と同じ書式の `outline.size` / `outline.color`) と\n * 既存 `line` 属性 (`line.color` / `line.width` / `line.dashType`) を\n * 1 つの BorderStyle にマージする。\n *\n * フィールド単位のマージで、`outline` の指定があるフィールドは `line` を\n * 上書きするが、`outline` 側で省略されたフィールドは `line` の値を引き継ぎ、\n * `line` にも値が無い場合は Text outline と同じ既定値 (`width: 1pt 相当` /\n * `color: FFFFFF`) を採用する。`dashType` は `outline` に対応フィールドが\n * 無いため `line.dashType` をそのまま使う。\n */\nfunction resolveShapeLine(\n line: BorderStyle | undefined,\n outline: { size?: number; color?: string } | undefined,\n): BorderStyle | undefined {\n if (!outline) return line;\n return {\n color: outline.color ?? line?.color ?? \"FFFFFF\",\n width: outline.size ?? line?.width ?? 1,\n dashType: line?.dashType,\n };\n}\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const lineSpec = resolveShapeLine(node.line, node.outline);\n const glowMarker = node.glow\n ? ctx.buildContext.glowEffects.register(node.glow)\n : undefined;\n\n const shapeOptions = {\n ...getContentAreaIn(node),\n fill: node.fill\n ? {\n color: node.fill.color,\n transparency: node.fill.transparency,\n }\n : undefined,\n line: lineSpec ? convertBorderLine(lineSpec) : undefined,\n shadow: convertShadow(node.shadow),\n rotate: node.rotate,\n objectName: glowMarker,\n };\n\n if (node.text) {\n const fontSizePx = node.fontSize ?? 24;\n const lineHeight = node.lineHeight ?? 1.3;\n // テキストがある場合:addTextでshapeを指定\n ctx.slide.addText(node.text, {\n ...shapeOptions,\n shape: node.shapeType,\n fontSize: pxToPt(fontSizePx),\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n align: node.textAlign ?? \"center\",\n valign: \"middle\" as const,\n // Text と同じく行送りを固定値 (spcPts) で指定し、計測高さ\n // (行数 × fontSize × lineHeight) と実描画の行高さを一致させる (#846)。\n // valign middle のためテキストブロックは枠内中央に配置され、\n // Text のような描画 y 補正は不要\n lineSpacing: pxToPt(fontSizePx * lineHeight),\n });\n } else {\n // テキストがない場合:addShapeを使用\n ctx.slide.addShape(node.shapeType, shapeOptions);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,SAAS,iBACP,MACA,SACyB;CACzB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,OAAO,QAAQ,SAAS,MAAM,SAAS;EACvC,OAAO,QAAQ,QAAQ,MAAM,SAAS;EACtC,UAAU,MAAM;CAClB;AACF;AAEA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,WAAW,iBAAiB,KAAK,MAAM,KAAK,OAAO;CACzD,MAAM,aAAa,KAAK,OACpB,IAAI,aAAa,YAAY,SAAS,KAAK,IAAI,IAC/C,KAAA;CAEJ,MAAM,eAAe;EACnB,GAAG,iBAAiB,IAAI;EACxB,MAAM,KAAK,OACP;GACE,OAAO,KAAK,KAAK;GACjB,cAAc,KAAK,KAAK;EAC1B,IACA,KAAA;EACJ,MAAM,WAAW,kBAAkB,QAAQ,IAAI,KAAA;EAC/C,QAAQ,cAAc,KAAK,MAAM;EACjC,QAAQ,KAAK;EACb,YAAY;CACd;CAEA,IAAI,KAAK,MAAM;EACb,MAAM,aAAa,KAAK,YAAY;EACpC,MAAM,aAAa,KAAK,cAAc;EAEtC,IAAI,MAAM,QAAQ,KAAK,MAAM;GAC3B,GAAG;GACH,OAAO,KAAK;GACZ,UAAU,OAAO,UAAU;GAC3B,UAAU,KAAK,cAAc;GAC7B,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,aAAa,KAAK;GAClB,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,QAAQ;GAKR,aAAa,OAAO,aAAa,UAAU;EAC7C,CAAC;CACH,OAEE,IAAI,MAAM,SAAS,KAAK,WAAW,YAAY;AAEnD"}
|
|
@@ -28,7 +28,7 @@ function renderTableNode(node, ctx) {
|
|
|
28
28
|
return {
|
|
29
29
|
text: run.text,
|
|
30
30
|
options: {
|
|
31
|
-
fontSize: pxToPt(cell.fontSize ?? 18),
|
|
31
|
+
fontSize: pxToPt(run.fontSize ?? cell.fontSize ?? 18),
|
|
32
32
|
fontFace: run.fontFamily ?? cellFontFace,
|
|
33
33
|
color: run.color ?? cell.color,
|
|
34
34
|
bold: run.bold ?? cell.bold,
|
|
@@ -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, rectPxToIn } from \"../units.ts\";\nimport {\n convertUnderline,\n convertStrike,\n resolveSubSup,\n} 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 subscript: cell.subscript,\n superscript: cell.superscript,\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 const runSubSup = resolveSubSup(run, cell);\n return {\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 subscript: runSubSup.subscript,\n superscript: runSubSup.superscript,\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\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 ...rectPxToIn(content),\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":";;;;;AAgBA,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,aAAa,KAAK;GAClB,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,GAoBlC,OAAO;GACL,MApBgB,KAAK,KAAK,KAAK,QAAQ;IACvC,MAAM,YAAY,cAAc,KAAK,IAAI;IACzC,OAAO;KACL,MAAM,IAAI;KACV,SAAS;MACP,UAAU,OAAO,KAAK,YAAY,EAAE;
|
|
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, rectPxToIn } from \"../units.ts\";\nimport {\n convertUnderline,\n convertStrike,\n resolveSubSup,\n} 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 subscript: cell.subscript,\n superscript: cell.superscript,\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 const runSubSup = resolveSubSup(run, cell);\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(run.fontSize ?? 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 subscript: runSubSup.subscript,\n superscript: runSubSup.superscript,\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\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 ...rectPxToIn(content),\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":";;;;;AAgBA,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,aAAa,KAAK;GAClB,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,GAoBlC,OAAO;GACL,MApBgB,KAAK,KAAK,KAAK,QAAQ;IACvC,MAAM,YAAY,cAAc,KAAK,IAAI;IACzC,OAAO;KACL,MAAM,IAAI;KACV,SAAS;MACP,UAAU,OAAO,IAAI,YAAY,KAAK,YAAY,EAAE;MACpD,UAAU,IAAI,cAAc;MAC5B,OAAO,IAAI,SAAS,KAAK;MACzB,MAAM,IAAI,QAAQ,KAAK;MACvB,QAAQ,IAAI,UAAU,KAAK;MAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;MAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;MAC/C,WAAW,UAAU;MACrB,aAAa,UAAU;MACvB,WAAW,IAAI,aAAa,KAAK;MACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;KACrD;IACF;GACF,CAEgB;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,WAAW,OAAO;EACrB,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"}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { pxToPt } from "../units.js";
|
|
2
2
|
import { convertGlow, convertOutline, convertStrike, convertUnderline, createTextOptions, resolveSubSup } from "../textOptions.js";
|
|
3
|
+
import { registerTextGradient } from "../gradientFills.js";
|
|
3
4
|
//#region src/renderPptx/nodes/text.ts
|
|
4
5
|
function renderTextNode(node, ctx) {
|
|
5
6
|
const textOptions = createTextOptions(node);
|
|
7
|
+
const textGradientMarker = node.textGradient ? registerTextGradient(node.textGradient, ctx.buildContext.gradientFills) : void 0;
|
|
6
8
|
if (node.runs && node.runs.length > 0) {
|
|
7
9
|
const fontSizePx = node.fontSize ?? 24;
|
|
8
10
|
const fontFamily = node.fontFamily ?? "Noto Sans JP";
|
|
9
11
|
const textItems = node.runs.map((run) => {
|
|
10
12
|
const letterSpacingPx = run.letterSpacing ?? node.letterSpacing;
|
|
13
|
+
const runFontSizePx = run.fontSize ?? fontSizePx;
|
|
11
14
|
const subSup = resolveSubSup(run, node);
|
|
12
15
|
return {
|
|
13
16
|
text: run.text,
|
|
14
17
|
options: {
|
|
15
|
-
fontSize: pxToPt(
|
|
18
|
+
fontSize: pxToPt(runFontSizePx),
|
|
16
19
|
fontFace: run.fontFamily ?? fontFamily,
|
|
17
|
-
color: run.color ?? node.color,
|
|
20
|
+
color: textGradientMarker ?? run.color ?? node.color,
|
|
18
21
|
bold: run.bold ?? node.bold,
|
|
19
22
|
italic: run.italic ?? node.italic,
|
|
20
23
|
underline: convertUnderline(run.underline ?? node.underline),
|
|
@@ -40,7 +43,10 @@ function renderTextNode(node, ctx) {
|
|
|
40
43
|
margin: textOptions.margin,
|
|
41
44
|
lineSpacing: textOptions.lineSpacing
|
|
42
45
|
});
|
|
43
|
-
} else ctx.slide.addText(node.text ?? "",
|
|
46
|
+
} else ctx.slide.addText(node.text ?? "", {
|
|
47
|
+
...textOptions,
|
|
48
|
+
color: textGradientMarker ?? textOptions.color
|
|
49
|
+
});
|
|
44
50
|
}
|
|
45
51
|
//#endregion
|
|
46
52
|
export { renderTextNode };
|
|
@@ -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 convertGlow,\n convertOutline,\n resolveSubSup,\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 const subSup = resolveSubSup(run, node);\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(
|
|
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 convertGlow,\n convertOutline,\n resolveSubSup,\n} from \"../textOptions.ts\";\nimport { registerTextGradient } from \"../gradientFills.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 // textGradient はマーカー色で text run color に適用し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)。\n // node 単位の指定として全 run の color を上書きする (run 単位指定はスコープ外)。\n const textGradientMarker = node.textGradient\n ? registerTextGradient(node.textGradient, ctx.buildContext.gradientFills)\n : undefined;\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 const runFontSizePx = run.fontSize ?? fontSizePx;\n const subSup = resolveSubSup(run, node);\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(runFontSizePx),\n fontFace: run.fontFamily ?? fontFamily,\n color: textGradientMarker ?? 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 subscript: subSup.subscript,\n superscript: subSup.superscript,\n highlight: run.highlight ?? node.highlight,\n // glow / outline はノード単位指定のみ (run 単位はスコープ外)\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\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 rotate: textOptions.rotate,\n align: textOptions.align,\n valign: textOptions.valign,\n margin: textOptions.margin,\n lineSpacing: textOptions.lineSpacing,\n });\n } else {\n ctx.slide.addText(node.text ?? \"\", {\n ...textOptions,\n color: textGradientMarker ?? textOptions.color,\n });\n }\n}\n"],"mappings":";;;;AAeA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAK1C,MAAM,qBAAqB,KAAK,eAC5B,qBAAqB,KAAK,cAAc,IAAI,aAAa,aAAa,IACtE,KAAA;CAEJ,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,MAAM,gBAAgB,IAAI,YAAY;GACtC,MAAM,SAAS,cAAc,KAAK,IAAI;GACtC,OAAO;IACL,MAAM,IAAI;IACV,SAAS;KACP,UAAU,OAAO,aAAa;KAC9B,UAAU,IAAI,cAAc;KAC5B,OAAO,sBAAsB,IAAI,SAAS,KAAK;KAC/C,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,OAAO;KAClB,aAAa,OAAO;KACpB,WAAW,IAAI,aAAa,KAAK;KAEjC,MAAM,YAAY,KAAK,IAAI;KAC3B,SAAS,eAAe,KAAK,OAAO;KACpC,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,QAAQ,YAAY;GACpB,OAAO,YAAY;GACnB,QAAQ,YAAY;GACpB,QAAQ,YAAY;GACpB,aAAa,YAAY;EAC3B,CAAC;CACH,OACE,IAAI,MAAM,QAAQ,KAAK,QAAQ,IAAI;EACjC,GAAG;EACH,OAAO,sBAAsB,YAAY;CAC3C,CAAC;AAEL"}
|
|
@@ -3,8 +3,8 @@ import { resolveBoxSpacing } from "../shared/boxSpacing.js";
|
|
|
3
3
|
import { getNodeDef } from "../registry/nodeRegistry.js";
|
|
4
4
|
import { pxToIn, pxToPt } from "./units.js";
|
|
5
5
|
import { convertStrike, convertUnderline } from "./textOptions.js";
|
|
6
|
-
import "../registry/index.js";
|
|
7
6
|
import { registerBackgroundGradient } from "./gradientFills.js";
|
|
7
|
+
import "../registry/index.js";
|
|
8
8
|
import { renderBackgroundAndBorder, renderBorderOnly } from "./utils/backgroundBorder.js";
|
|
9
9
|
//#region src/renderPptx/renderPptx.ts
|
|
10
10
|
async function loadPptxGenJS() {
|
package/dist/renderPptx/units.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
const EMU_PER_IN = 914400;
|
|
1
2
|
const pxToIn = (px) => px / 96;
|
|
2
3
|
const pxToPt = (px) => px * 72 / 96;
|
|
3
4
|
/**
|
|
5
|
+
* px を DrawingML の EMU (English Metric Unit) に変換する。
|
|
6
|
+
* 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。
|
|
7
|
+
* `<a:glow rad="...">` など EMU を直接埋め込む XML 後処理で使う。
|
|
8
|
+
*/
|
|
9
|
+
const pxToEmu = (px) => px * EMU_PER_IN / 96;
|
|
10
|
+
/**
|
|
4
11
|
* px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に
|
|
5
12
|
* まとめて変換する。addShape / addText 等のオプションへ spread して使う。
|
|
6
13
|
*/
|
|
@@ -11,6 +18,6 @@ const rectPxToIn = (rect) => ({
|
|
|
11
18
|
h: pxToIn(rect.h)
|
|
12
19
|
});
|
|
13
20
|
//#endregion
|
|
14
|
-
export { pxToIn, pxToPt, rectPxToIn };
|
|
21
|
+
export { pxToEmu, pxToIn, pxToPt, rectPxToIn };
|
|
15
22
|
|
|
16
23
|
//# sourceMappingURL=units.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。addShape / addText 等のオプションへ spread して使う。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\nexport const EMU_PER_IN = 914400;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px を DrawingML の EMU (English Metric Unit) に変換する。\n * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。\n * `<a:glow rad=\"...\">` など EMU を直接埋め込む XML 後処理で使う。\n */\nexport const pxToEmu = (px: number) => (px * EMU_PER_IN) / PX_PER_IN;\n\n/**\n * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。addShape / addText 等のオプションへ spread して使う。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"AACA,MAAa,aAAa;AAE1B,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA;;;;;;AAO5C,MAAa,WAAW,OAAgB,KAAK,aAAA;;;;;AAM7C,MAAa,cAAc,UAKpB;CACL,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;AAClB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getImageData } from "../../shared/measureImage.js";
|
|
2
2
|
import { pxToIn, rectPxToIn } from "../units.js";
|
|
3
|
-
import { BORDER_SIDES, convertBorderLine, convertShadow, hasVisibleBorder, resolveBackgroundFill, resolvePerSideBorders, resolveRectRadius } from "./visualStyle.js";
|
|
4
3
|
import { registerBackgroundGradient } from "../gradientFills.js";
|
|
4
|
+
import { BORDER_SIDES, convertBorderLine, convertShadow, hasVisibleBorder, resolveBackgroundFill, resolvePerSideBorders, resolveRectRadius } from "./visualStyle.js";
|
|
5
5
|
//#region src/renderPptx/utils/backgroundBorder.ts
|
|
6
6
|
/**
|
|
7
7
|
* ノードの背景色・背景画像・ボーダー・影を描画する
|
package/dist/types.d.ts
CHANGED
|
@@ -297,11 +297,13 @@ declare const textNodeSchema: z.ZodObject<{
|
|
|
297
297
|
color: z.ZodOptional<z.ZodString>;
|
|
298
298
|
href: z.ZodOptional<z.ZodString>;
|
|
299
299
|
fontFamily: z.ZodOptional<z.ZodString>;
|
|
300
|
+
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
300
301
|
letterSpacing: z.ZodOptional<z.ZodNumber>;
|
|
301
302
|
}, z.core.$strip>>>;
|
|
302
303
|
rotate: z.ZodOptional<z.ZodNumber>;
|
|
303
304
|
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
304
305
|
color: z.ZodOptional<z.ZodString>;
|
|
306
|
+
textGradient: z.ZodOptional<z.ZodString>;
|
|
305
307
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
306
308
|
right: "right";
|
|
307
309
|
left: "left";
|
|
@@ -490,6 +492,7 @@ declare const ulNodeSchema: z.ZodObject<{
|
|
|
490
492
|
color: z.ZodOptional<z.ZodString>;
|
|
491
493
|
href: z.ZodOptional<z.ZodString>;
|
|
492
494
|
fontFamily: z.ZodOptional<z.ZodString>;
|
|
495
|
+
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
493
496
|
letterSpacing: z.ZodOptional<z.ZodNumber>;
|
|
494
497
|
}, z.core.$strip>>>;
|
|
495
498
|
bold: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -702,6 +705,7 @@ declare const olNodeSchema: z.ZodObject<{
|
|
|
702
705
|
color: z.ZodOptional<z.ZodString>;
|
|
703
706
|
href: z.ZodOptional<z.ZodString>;
|
|
704
707
|
fontFamily: z.ZodOptional<z.ZodString>;
|
|
708
|
+
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
705
709
|
letterSpacing: z.ZodOptional<z.ZodNumber>;
|
|
706
710
|
}, z.core.$strip>>>;
|
|
707
711
|
bold: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1075,6 +1079,15 @@ declare const iconNodeSchema: z.ZodObject<{
|
|
|
1075
1079
|
"square-outlined": "square-outlined";
|
|
1076
1080
|
}>>;
|
|
1077
1081
|
bgColor: z.ZodOptional<z.ZodString>;
|
|
1082
|
+
glow: z.ZodOptional<z.ZodObject<{
|
|
1083
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
1084
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
1085
|
+
color: z.ZodOptional<z.ZodString>;
|
|
1086
|
+
}, z.core.$strip>>;
|
|
1087
|
+
outline: z.ZodOptional<z.ZodObject<{
|
|
1088
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
1089
|
+
color: z.ZodOptional<z.ZodString>;
|
|
1090
|
+
}, z.core.$strip>>;
|
|
1078
1091
|
rotate: z.ZodOptional<z.ZodNumber>;
|
|
1079
1092
|
}, z.core.$strip>;
|
|
1080
1093
|
type IconNode = z.infer<typeof iconNodeSchema>;
|
|
@@ -1360,6 +1373,7 @@ declare const tableNodeSchema: z.ZodObject<{
|
|
|
1360
1373
|
color: z.ZodOptional<z.ZodString>;
|
|
1361
1374
|
href: z.ZodOptional<z.ZodString>;
|
|
1362
1375
|
fontFamily: z.ZodOptional<z.ZodString>;
|
|
1376
|
+
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
1363
1377
|
letterSpacing: z.ZodOptional<z.ZodNumber>;
|
|
1364
1378
|
}, z.core.$strip>>>;
|
|
1365
1379
|
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1748,6 +1762,15 @@ declare const shapeNodeSchema: z.ZodObject<{
|
|
|
1748
1762
|
sysDot: "sysDot";
|
|
1749
1763
|
}>>;
|
|
1750
1764
|
}, z.core.$strip>>;
|
|
1765
|
+
glow: z.ZodOptional<z.ZodObject<{
|
|
1766
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
1767
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
1768
|
+
color: z.ZodOptional<z.ZodString>;
|
|
1769
|
+
}, z.core.$strip>>;
|
|
1770
|
+
outline: z.ZodOptional<z.ZodObject<{
|
|
1771
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
1772
|
+
color: z.ZodOptional<z.ZodString>;
|
|
1773
|
+
}, z.core.$strip>>;
|
|
1751
1774
|
fontSize: z.ZodOptional<z.ZodNumber>;
|
|
1752
1775
|
color: z.ZodOptional<z.ZodString>;
|
|
1753
1776
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
@@ -1936,6 +1959,7 @@ declare const chartNodeSchema: z.ZodObject<{
|
|
|
1936
1959
|
marker: "marker";
|
|
1937
1960
|
filled: "filled";
|
|
1938
1961
|
}>>;
|
|
1962
|
+
sparkline: z.ZodOptional<z.ZodBoolean>;
|
|
1939
1963
|
}, z.core.$strip>;
|
|
1940
1964
|
type TextNode = z.infer<typeof textNodeSchema>;
|
|
1941
1965
|
type UlNode = z.infer<typeof ulNodeSchema>;
|