@hirokisakabe/pom 9.1.1 → 9.1.2

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 (48) hide show
  1. package/dist/buildContext.js +1 -5
  2. package/dist/buildContext.js.map +1 -1
  3. package/dist/buildPptx.d.ts.map +1 -1
  4. package/dist/buildPptx.js +1 -7
  5. package/dist/buildPptx.js.map +1 -1
  6. package/dist/renderPptx/glimpseTextBoxes.js +325 -26
  7. package/dist/renderPptx/glimpseTextBoxes.js.map +1 -1
  8. package/dist/renderPptx/nodes/flow.js +95 -90
  9. package/dist/renderPptx/nodes/flow.js.map +1 -1
  10. package/dist/renderPptx/nodes/icon.js +33 -24
  11. package/dist/renderPptx/nodes/icon.js.map +1 -1
  12. package/dist/renderPptx/nodes/image.js +5 -22
  13. package/dist/renderPptx/nodes/image.js.map +1 -1
  14. package/dist/renderPptx/nodes/list.js +96 -111
  15. package/dist/renderPptx/nodes/list.js.map +1 -1
  16. package/dist/renderPptx/nodes/matrix.js +92 -70
  17. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  18. package/dist/renderPptx/nodes/processArrow.js +68 -35
  19. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  20. package/dist/renderPptx/nodes/pyramid.js +34 -16
  21. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  22. package/dist/renderPptx/nodes/shape.js +63 -37
  23. package/dist/renderPptx/nodes/shape.js.map +1 -1
  24. package/dist/renderPptx/nodes/svg.js +2 -5
  25. package/dist/renderPptx/nodes/svg.js.map +1 -1
  26. package/dist/renderPptx/nodes/timeline.js +108 -74
  27. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  28. package/dist/renderPptx/nodes/tree.js +78 -79
  29. package/dist/renderPptx/nodes/tree.js.map +1 -1
  30. package/dist/renderPptx/renderPptx.js +4 -5
  31. package/dist/renderPptx/renderPptx.js.map +1 -1
  32. package/dist/renderPptx/utils/backgroundBorder.js +85 -49
  33. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  34. package/dist/renderPptx/utils/glimpsePicture.js +130 -0
  35. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
  36. package/dist/renderPptx/utils/glimpseShape.js +90 -0
  37. package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
  38. package/dist/renderPptx/utils/glimpseTextBox.js +126 -0
  39. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
  40. package/dist/renderPptx/utils/straightLine.js +29 -14
  41. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  42. package/dist/renderPptx/utils/visualStyle.js +1 -24
  43. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  44. package/package.json +2 -2
  45. package/dist/renderPptx/glowEffects.js +0 -157
  46. package/dist/renderPptx/glowEffects.js.map +0 -1
  47. package/dist/renderPptx/gradientFills.js +0 -154
  48. package/dist/renderPptx/gradientFills.js.map +0 -1
@@ -1,7 +1,5 @@
1
- import { GradientFillRegistry } from "./renderPptx/gradientFills.js";
2
- import { DiagnosticCollector } from "./diagnostics.js";
3
- import { GlowEffectRegistry } from "./renderPptx/glowEffects.js";
4
1
  import { GlimpseTextBoxRegistry } from "./renderPptx/glimpseTextBoxes.js";
2
+ import { DiagnosticCollector } from "./diagnostics.js";
5
3
  //#region src/buildContext.ts
6
4
  function createBuildContext(textMeasurementMode = "auto") {
7
5
  return {
@@ -10,8 +8,6 @@ function createBuildContext(textMeasurementMode = "auto") {
10
8
  imageDataCache: /* @__PURE__ */ new Map(),
11
9
  iconRasterCache: /* @__PURE__ */ new Map(),
12
10
  diagnostics: new DiagnosticCollector(),
13
- gradientFills: new GradientFillRegistry(),
14
- glowEffects: new GlowEffectRegistry(),
15
11
  glimpseTextBoxes: new GlimpseTextBoxRegistry()
16
12
  };
17
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"buildContext.js","names":[],"sources":["../src/buildContext.ts"],"sourcesContent":["import type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport { DiagnosticCollector } from \"./diagnostics.ts\";\nimport { GlowEffectRegistry } from \"./renderPptx/glowEffects.ts\";\nimport { GradientFillRegistry } from \"./renderPptx/gradientFills.ts\";\nimport { GlimpseTextBoxRegistry } from \"./renderPptx/glimpseTextBoxes.ts\";\n\nexport interface BuildContext {\n textMeasurementMode: TextMeasurementMode;\n imageSizeCache: Map<string, { widthPx: number; heightPx: number }>;\n imageDataCache: Map<string, string>;\n iconRasterCache: Map<string, string>;\n diagnostics: DiagnosticCollector;\n gradientFills: GradientFillRegistry;\n glowEffects: GlowEffectRegistry;\n glimpseTextBoxes: GlimpseTextBoxRegistry;\n}\n\nexport function createBuildContext(\n textMeasurementMode: TextMeasurementMode = \"auto\",\n): BuildContext {\n return {\n textMeasurementMode,\n imageSizeCache: new Map(),\n imageDataCache: new Map(),\n iconRasterCache: new Map(),\n diagnostics: new DiagnosticCollector(),\n gradientFills: new GradientFillRegistry(),\n glowEffects: new GlowEffectRegistry(),\n glimpseTextBoxes: new GlimpseTextBoxRegistry(),\n };\n}\n"],"mappings":";;;;;AAiBA,SAAgB,mBACd,sBAA2C,QAC7B;CACd,OAAO;EACL;EACA,gCAAgB,IAAI,IAAI;EACxB,gCAAgB,IAAI,IAAI;EACxB,iCAAiB,IAAI,IAAI;EACzB,aAAa,IAAI,oBAAoB;EACrC,eAAe,IAAI,qBAAqB;EACxC,aAAa,IAAI,mBAAmB;EACpC,kBAAkB,IAAI,uBAAuB;CAC/C;AACF"}
1
+ {"version":3,"file":"buildContext.js","names":[],"sources":["../src/buildContext.ts"],"sourcesContent":["import type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport { DiagnosticCollector } from \"./diagnostics.ts\";\nimport { GlimpseTextBoxRegistry } from \"./renderPptx/glimpseTextBoxes.ts\";\n\nexport interface BuildContext {\n textMeasurementMode: TextMeasurementMode;\n imageSizeCache: Map<string, { widthPx: number; heightPx: number }>;\n imageDataCache: Map<string, string>;\n iconRasterCache: Map<string, string>;\n diagnostics: DiagnosticCollector;\n glimpseTextBoxes: GlimpseTextBoxRegistry;\n}\n\nexport function createBuildContext(\n textMeasurementMode: TextMeasurementMode = \"auto\",\n): BuildContext {\n return {\n textMeasurementMode,\n imageSizeCache: new Map(),\n imageDataCache: new Map(),\n iconRasterCache: new Map(),\n diagnostics: new DiagnosticCollector(),\n glimpseTextBoxes: new GlimpseTextBoxRegistry(),\n };\n}\n"],"mappings":";;;AAaA,SAAgB,mBACd,sBAA2C,QAC7B;CACd,OAAO;EACL;EACA,gCAAgB,IAAI,IAAI;EACxB,gCAAgB,IAAI,IAAI;EACxB,iCAAiB,IAAI,IAAI;EACzB,aAAa,IAAI,oBAAoB;EACrC,kBAAkB,IAAI,uBAAuB;CAC/C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;UAqBiB,eAAA;EACf,IAAA,sBAA0B,OAAA;EAC1B,WAAA,EAAa,UAAU;AAAA;AAAA,iBAGH,SAAA,CACpB,GAAA,UACA,SAAA;EAAa,CAAA;EAAW,CAAA;AAAA,GACxB,OAAA;EACE,MAAA,GAAS,kBAAA;EACT,UAAA,GAAa,WAAA,GAAc,UAAA;EAC3B,eAAA,GAAkB,mBAAA;EAClB,OAAA;EACA,MAAA;AAAA,IAED,OAAA,CAAQ,eAAA"}
1
+ {"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;UAmBiB,eAAA;EACf,IAAA,sBAA0B,OAAA;EAC1B,WAAA,EAAa,UAAU;AAAA;AAAA,iBAGH,SAAA,CACpB,GAAA,UACA,SAAA;EAAa,CAAA;EAAW,CAAA;AAAA,GACxB,OAAA;EACE,MAAA,GAAS,kBAAA;EACT,UAAA,GAAa,WAAA,GAAc,UAAA;EAC3B,eAAA,GAAkB,mBAAA;EAClB,OAAA;EACA,MAAA;AAAA,IAED,OAAA,CAAQ,eAAA"}
package/dist/buildPptx.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import { freeYogaTree } from "./shared/freeYogaTree.js";
2
- import { patchPptxWriteForGradientFills } from "./renderPptx/gradientFills.js";
2
+ import { patchPptxWriteForGlimpseTextBoxes } from "./renderPptx/glimpseTextBoxes.js";
3
3
  import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout.js";
4
4
  import { autoFitSlide } from "./autoFit/autoFit.js";
5
5
  import { DiagnosticsError } from "./diagnostics.js";
6
- import { patchPptxWriteForGlowEffects } from "./renderPptx/glowEffects.js";
7
- import { patchPptxWriteForGlimpseTextBoxes } from "./renderPptx/glimpseTextBoxes.js";
8
6
  import { createBuildContext } from "./buildContext.js";
9
7
  import { extractLayoutResults } from "./calcYogaLayout/types.js";
10
8
  import { parseMasterPptx } from "./parseMasterPptx.js";
@@ -15,8 +13,6 @@ import { validatePositioned } from "./validatePositioned/validatePositioned.js";
15
13
  //#region src/buildPptx.ts
16
14
  async function buildPptx(xml, slideSize, options) {
17
15
  const ctx = createBuildContext(options?.textMeasurement ?? "auto");
18
- ctx.gradientFills.reserveColors(xml);
19
- if (options?.master) ctx.gradientFills.reserveColors(JSON.stringify(options.master));
20
16
  const nodes = parseXml(xml);
21
17
  const positionedPages = [];
22
18
  for (const [slideIndex, node] of nodes.entries()) {
@@ -45,9 +41,7 @@ async function buildPptx(xml, slideSize, options) {
45
41
  ctx.diagnostics.add("MASTER_PPTX_PARSE_FAILED", message);
46
42
  }
47
43
  const pptx = await renderPptx(positionedPages, slideSize, ctx, master);
48
- patchPptxWriteForGradientFills(pptx, ctx.gradientFills);
49
44
  patchPptxWriteForGlimpseTextBoxes(pptx, ctx.glimpseTextBoxes);
50
- patchPptxWriteForGlowEffects(pptx, ctx.glowEffects);
51
45
  const diagnostics = ctx.diagnostics.items;
52
46
  if (options?.strict && diagnostics.length > 0) throw new DiagnosticsError(diagnostics);
53
47
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"buildPptx.js","names":[],"sources":["../src/buildPptx.ts"],"sourcesContent":["import { autoFitSlide } from \"./autoFit/autoFit.ts\";\nimport { createBuildContext } from \"./buildContext.ts\";\nimport { calcYogaLayout } from \"./calcYogaLayout/calcYogaLayout.ts\";\nimport type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport type { YogaNodeMap } from \"./calcYogaLayout/types.ts\";\nimport { extractLayoutResults } from \"./calcYogaLayout/types.ts\";\nimport type { Diagnostic } from \"./diagnostics.ts\";\nimport { DiagnosticsError } from \"./diagnostics.ts\";\nimport { parseMasterPptx } from \"./parseMasterPptx.ts\";\nimport { parseXml } from \"./parseXml/parseXml.ts\";\nimport { patchPptxWriteForGlowEffects } from \"./renderPptx/glowEffects.ts\";\nimport { patchPptxWriteForGlimpseTextBoxes } from \"./renderPptx/glimpseTextBoxes.ts\";\nimport { patchPptxWriteForGradientFills } from \"./renderPptx/gradientFills.ts\";\nimport { renderPptx } from \"./renderPptx/renderPptx.ts\";\nimport { freeYogaTree } from \"./shared/freeYogaTree.ts\";\nimport { toPositioned } from \"./toPositioned/toPositioned.ts\";\nimport { PositionedNode, SlideMasterOptions } from \"./types.ts\";\nimport { validatePositioned } from \"./validatePositioned/validatePositioned.ts\";\n\nexport type { TextMeasurementMode };\n\nexport interface BuildPptxResult {\n pptx: import(\"pptxgenjs\").default;\n diagnostics: Diagnostic[];\n}\n\nexport async function buildPptx(\n xml: string,\n slideSize: { w: number; h: number },\n options?: {\n master?: SlideMasterOptions;\n masterPptx?: ArrayBuffer | Uint8Array;\n textMeasurement?: TextMeasurementMode;\n autoFit?: boolean;\n strict?: boolean;\n },\n): Promise<BuildPptxResult> {\n const ctx = createBuildContext(options?.textMeasurement ?? \"auto\");\n\n // グラデーション後処理のマーカー色がユーザー指定色と衝突しないよう、\n // 入力 XML / master オプション中に現れる色を予約しておく\n ctx.gradientFills.reserveColors(xml);\n if (options?.master) {\n ctx.gradientFills.reserveColors(JSON.stringify(options.master));\n }\n\n const nodes = parseXml(xml);\n const positionedPages: PositionedNode[] = [];\n\n for (const [slideIndex, node] of nodes.entries()) {\n let map: YogaNodeMap | undefined;\n try {\n if (options?.autoFit !== false) {\n map = await autoFitSlide(node, slideSize, ctx);\n } else {\n map = await calcYogaLayout(node, slideSize, ctx);\n }\n const layoutMap = extractLayoutResults(map);\n const positioned = await toPositioned(node, ctx, layoutMap);\n validatePositioned(positioned, slideSize, ctx, slideIndex);\n positionedPages.push(positioned);\n } finally {\n if (map) freeYogaTree(map);\n }\n }\n\n // masterPptx から背景を抽出し、master オプションにマージ\n let master = options?.master;\n if (options?.masterPptx) {\n try {\n const bg = await parseMasterPptx(options.masterPptx);\n if (bg) {\n if (master) {\n // 明示的に background が指定されていない場合のみ、masterPptx の背景を使用\n if (!master.background) {\n master = { ...master, background: bg };\n }\n } else {\n master = { background: bg };\n }\n }\n } catch (e) {\n const message =\n e instanceof Error ? e.message : \"Unknown error parsing masterPptx\";\n ctx.diagnostics.add(\"MASTER_PPTX_PARSE_FAILED\", message);\n }\n }\n\n const pptx = await renderPptx(positionedPages, slideSize, ctx, master);\n\n // backgroundGradient 使用時は write/writeFile に gradFill 置換の後処理を仕込む\n patchPptxWriteForGradientFills(pptx, ctx.gradientFills);\n\n // Text primitive glimpse writer の text box XML で marker shape を置換する\n patchPptxWriteForGlimpseTextBoxes(pptx, ctx.glimpseTextBoxes);\n\n // Shape / Icon の glow 指定がある場合は write/writeFile に effectLst 挿入の\n // 後処理を仕込む (gradientFills の patch 後にチェーンする)\n patchPptxWriteForGlowEffects(pptx, ctx.glowEffects);\n\n const diagnostics = ctx.diagnostics.items;\n\n if (options?.strict && diagnostics.length > 0) {\n throw new DiagnosticsError(diagnostics);\n }\n\n return { pptx, diagnostics };\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0BA,eAAsB,UACpB,KACA,WACA,SAO0B;CAC1B,MAAM,MAAM,mBAAmB,SAAS,mBAAmB,MAAM;CAIjE,IAAI,cAAc,cAAc,GAAG;CACnC,IAAI,SAAS,QACX,IAAI,cAAc,cAAc,KAAK,UAAU,QAAQ,MAAM,CAAC;CAGhE,MAAM,QAAQ,SAAS,GAAG;CAC1B,MAAM,kBAAoC,CAAC;CAE3C,KAAK,MAAM,CAAC,YAAY,SAAS,MAAM,QAAQ,GAAG;EAChD,IAAI;EACJ,IAAI;GACF,IAAI,SAAS,YAAY,OACvB,MAAM,MAAM,aAAa,MAAM,WAAW,GAAG;QAE7C,MAAM,MAAM,eAAe,MAAM,WAAW,GAAG;GAGjD,MAAM,aAAa,MAAM,aAAa,MAAM,KAD1B,qBAAqB,GACkB,CAAC;GAC1D,mBAAmB,YAAY,WAAW,KAAK,UAAU;GACzD,gBAAgB,KAAK,UAAU;EACjC,UAAU;GACR,IAAI,KAAK,aAAa,GAAG;EAC3B;CACF;CAGA,IAAI,SAAS,SAAS;CACtB,IAAI,SAAS,YACX,IAAI;EACF,MAAM,KAAK,MAAM,gBAAgB,QAAQ,UAAU;EACnD,IAAI,IACF,IAAI;OAEE,CAAC,OAAO,YACV,SAAS;IAAE,GAAG;IAAQ,YAAY;GAAG;EAAA,OAGvC,SAAS,EAAE,YAAY,GAAG;CAGhC,SAAS,GAAG;EACV,MAAM,UACJ,aAAa,QAAQ,EAAE,UAAU;EACnC,IAAI,YAAY,IAAI,4BAA4B,OAAO;CACzD;CAGF,MAAM,OAAO,MAAM,WAAW,iBAAiB,WAAW,KAAK,MAAM;CAGrE,+BAA+B,MAAM,IAAI,aAAa;CAGtD,kCAAkC,MAAM,IAAI,gBAAgB;CAI5D,6BAA6B,MAAM,IAAI,WAAW;CAElD,MAAM,cAAc,IAAI,YAAY;CAEpC,IAAI,SAAS,UAAU,YAAY,SAAS,GAC1C,MAAM,IAAI,iBAAiB,WAAW;CAGxC,OAAO;EAAE;EAAM;CAAY;AAC7B"}
1
+ {"version":3,"file":"buildPptx.js","names":[],"sources":["../src/buildPptx.ts"],"sourcesContent":["import { autoFitSlide } from \"./autoFit/autoFit.ts\";\nimport { createBuildContext } from \"./buildContext.ts\";\nimport { calcYogaLayout } from \"./calcYogaLayout/calcYogaLayout.ts\";\nimport type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport type { YogaNodeMap } from \"./calcYogaLayout/types.ts\";\nimport { extractLayoutResults } from \"./calcYogaLayout/types.ts\";\nimport type { Diagnostic } from \"./diagnostics.ts\";\nimport { DiagnosticsError } from \"./diagnostics.ts\";\nimport { parseMasterPptx } from \"./parseMasterPptx.ts\";\nimport { parseXml } from \"./parseXml/parseXml.ts\";\nimport { patchPptxWriteForGlimpseTextBoxes } from \"./renderPptx/glimpseTextBoxes.ts\";\nimport { renderPptx } from \"./renderPptx/renderPptx.ts\";\nimport { freeYogaTree } from \"./shared/freeYogaTree.ts\";\nimport { toPositioned } from \"./toPositioned/toPositioned.ts\";\nimport { PositionedNode, SlideMasterOptions } from \"./types.ts\";\nimport { validatePositioned } from \"./validatePositioned/validatePositioned.ts\";\n\nexport type { TextMeasurementMode };\n\nexport interface BuildPptxResult {\n pptx: import(\"pptxgenjs\").default;\n diagnostics: Diagnostic[];\n}\n\nexport async function buildPptx(\n xml: string,\n slideSize: { w: number; h: number },\n options?: {\n master?: SlideMasterOptions;\n masterPptx?: ArrayBuffer | Uint8Array;\n textMeasurement?: TextMeasurementMode;\n autoFit?: boolean;\n strict?: boolean;\n },\n): Promise<BuildPptxResult> {\n const ctx = createBuildContext(options?.textMeasurement ?? \"auto\");\n\n const nodes = parseXml(xml);\n const positionedPages: PositionedNode[] = [];\n\n for (const [slideIndex, node] of nodes.entries()) {\n let map: YogaNodeMap | undefined;\n try {\n if (options?.autoFit !== false) {\n map = await autoFitSlide(node, slideSize, ctx);\n } else {\n map = await calcYogaLayout(node, slideSize, ctx);\n }\n const layoutMap = extractLayoutResults(map);\n const positioned = await toPositioned(node, ctx, layoutMap);\n validatePositioned(positioned, slideSize, ctx, slideIndex);\n positionedPages.push(positioned);\n } finally {\n if (map) freeYogaTree(map);\n }\n }\n\n // masterPptx から背景を抽出し、master オプションにマージ\n let master = options?.master;\n if (options?.masterPptx) {\n try {\n const bg = await parseMasterPptx(options.masterPptx);\n if (bg) {\n if (master) {\n // 明示的に background が指定されていない場合のみ、masterPptx の背景を使用\n if (!master.background) {\n master = { ...master, background: bg };\n }\n } else {\n master = { background: bg };\n }\n }\n } catch (e) {\n const message =\n e instanceof Error ? e.message : \"Unknown error parsing masterPptx\";\n ctx.diagnostics.add(\"MASTER_PPTX_PARSE_FAILED\", message);\n }\n }\n\n const pptx = await renderPptx(positionedPages, slideSize, ctx, master);\n\n // glimpse writer で生成した Text / Shape / Picture XML で marker shape を置換する\n patchPptxWriteForGlimpseTextBoxes(pptx, ctx.glimpseTextBoxes);\n\n const diagnostics = ctx.diagnostics.items;\n\n if (options?.strict && diagnostics.length > 0) {\n throw new DiagnosticsError(diagnostics);\n }\n\n return { pptx, diagnostics };\n}\n"],"mappings":";;;;;;;;;;;;;AAwBA,eAAsB,UACpB,KACA,WACA,SAO0B;CAC1B,MAAM,MAAM,mBAAmB,SAAS,mBAAmB,MAAM;CAEjE,MAAM,QAAQ,SAAS,GAAG;CAC1B,MAAM,kBAAoC,CAAC;CAE3C,KAAK,MAAM,CAAC,YAAY,SAAS,MAAM,QAAQ,GAAG;EAChD,IAAI;EACJ,IAAI;GACF,IAAI,SAAS,YAAY,OACvB,MAAM,MAAM,aAAa,MAAM,WAAW,GAAG;QAE7C,MAAM,MAAM,eAAe,MAAM,WAAW,GAAG;GAGjD,MAAM,aAAa,MAAM,aAAa,MAAM,KAD1B,qBAAqB,GACkB,CAAC;GAC1D,mBAAmB,YAAY,WAAW,KAAK,UAAU;GACzD,gBAAgB,KAAK,UAAU;EACjC,UAAU;GACR,IAAI,KAAK,aAAa,GAAG;EAC3B;CACF;CAGA,IAAI,SAAS,SAAS;CACtB,IAAI,SAAS,YACX,IAAI;EACF,MAAM,KAAK,MAAM,gBAAgB,QAAQ,UAAU;EACnD,IAAI,IACF,IAAI;OAEE,CAAC,OAAO,YACV,SAAS;IAAE,GAAG;IAAQ,YAAY;GAAG;EAAA,OAGvC,SAAS,EAAE,YAAY,GAAG;CAGhC,SAAS,GAAG;EACV,MAAM,UACJ,aAAa,QAAQ,EAAE,UAAU;EACnC,IAAI,YAAY,IAAI,4BAA4B,OAAO;CACzD;CAGF,MAAM,OAAO,MAAM,WAAW,iBAAiB,WAAW,KAAK,MAAM;CAGrE,kCAAkC,MAAM,IAAI,gBAAgB;CAE5D,MAAM,cAAc,IAAI,YAAY;CAEpC,IAAI,SAAS,UAAU,YAAY,SAAS,GAC1C,MAAM,IAAI,iBAAiB,WAAW;CAGxC,OAAO;EAAE;EAAM;CAAY;AAC7B"}
@@ -1,7 +1,7 @@
1
1
  import { EMU_PER_IN, pxToEmu, pxToPt } from "./units.js";
2
2
  import { createTextOptions, resolveSubSup } from "./textOptions.js";
3
- import { parseLinearGradient } from "../shared/gradient.js";
4
- import { addTextBox, asEmu, asHundredthPt, asOoxmlAngle, asOoxmlPercent, asPt, createPptx } from "@pptx-glimpse/document";
3
+ import { parseGradient, parseLinearGradient } from "../shared/gradient.js";
4
+ import { addPicture, addShape, addTextBox, asEmu, asHundredthPt, asOoxmlAngle, asOoxmlPercent, asPt, createPptx, readPptx } from "@pptx-glimpse/document";
5
5
  //#region src/renderPptx/glimpseTextBoxes.ts
6
6
  /**
7
7
  * Text primitive の @pptx-glimpse/document writer への段階的 swap。
@@ -21,7 +21,11 @@ import { addTextBox, asEmu, asHundredthPt, asOoxmlAngle, asOoxmlPercent, asPt, c
21
21
  * を経由しない。
22
22
  */
23
23
  const MARKER_PREFIX = "pom-text:";
24
+ const SHAPE_MARKER_PREFIX = "pom-shape:";
25
+ const PICTURE_MARKER_PREFIX = "pom-picture:";
26
+ const SLIDE_BACKGROUND_MARKER_BASE = 1014333;
24
27
  const HYPERLINK_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
28
+ const IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
25
29
  async function loadJSZip() {
26
30
  const mod = await import("jszip");
27
31
  return mod.default ?? mod;
@@ -151,6 +155,89 @@ function withGlowAlpha(xml, node) {
151
155
  const replacement = `<a:glow rad="${Math.round(pxToEmu(glow.size ?? 8))}"><a:srgbClr val="${color}"><a:alpha val="${alpha}"/></a:srgbClr></a:glow>`;
152
156
  return xml.replaceAll(target, replacement);
153
157
  }
158
+ function withShapeGlowAlpha(xml, glow) {
159
+ if (!glow) return xml;
160
+ const alpha = Math.round((glow.opacity ?? .75) * 1e5);
161
+ const color = cleanHex(glow.color ?? "FFFFFF");
162
+ const target = `<a:glow rad="${Math.round(pxToEmu(glow.size ?? 8))}"><a:srgbClr val="${color}"/></a:glow>`;
163
+ const replacement = `<a:glow rad="${Math.round(pxToEmu(glow.size ?? 8))}"><a:srgbClr val="${color}"><a:alpha val="${alpha}"/></a:srgbClr></a:glow>`;
164
+ return xml.replaceAll(target, replacement);
165
+ }
166
+ function colorWithOptionalAlphaXml(color, opacity) {
167
+ const clean = cleanHex(color) ?? color;
168
+ if (opacity === void 0) return `<a:srgbClr val="${clean}"/>`;
169
+ return `<a:srgbClr val="${clean}"><a:alpha val="${Math.round(opacity * 1e5)}"/></a:srgbClr>`;
170
+ }
171
+ function buildGradFillXml(value, opacity) {
172
+ const gradient = parseGradient(value);
173
+ if (!gradient) return void 0;
174
+ const gsXml = gradient.value.stops.map((stop) => `<a:gs pos="${Math.round(stop.position * 1e3)}">${colorWithOptionalAlphaXml(stop.color, opacity)}</a:gs>`).join("");
175
+ if (gradient.kind === "linear") {
176
+ const dmlAngle = ((gradient.value.angle - 90) % 360 + 360) % 360;
177
+ return `<a:gradFill flip="none" rotWithShape="1"><a:gsLst>${gsXml}</a:gsLst><a:lin ang="${Math.round(dmlAngle * 6e4)}" scaled="0"/></a:gradFill>`;
178
+ }
179
+ const { centerX, centerY } = gradient.value;
180
+ return `<a:gradFill flip="none" rotWithShape="1"><a:gsLst>${gsXml}</a:gsLst><a:path path="circle"><a:fillToRect l="${Math.round(centerX * 1e3)}" t="${Math.round(centerY * 1e3)}" r="${Math.round((100 - centerX) * 1e3)}" b="${Math.round((100 - centerY) * 1e3)}"/></a:path></a:gradFill>`;
181
+ }
182
+ function withSolidFillAlpha(xml, color, opacity) {
183
+ if (color === void 0 || opacity === void 0) return xml;
184
+ const clean = cleanHex(color);
185
+ return xml.replaceAll(`<a:solidFill><a:srgbClr val="${clean}"/></a:solidFill>`, `<a:solidFill>${colorWithOptionalAlphaXml(clean ?? color, opacity)}</a:solidFill>`);
186
+ }
187
+ function withGradientFill(xml, backgroundGradient, opacity, preset) {
188
+ if (!backgroundGradient) return xml;
189
+ const gradFill = buildGradFillXml(backgroundGradient, opacity);
190
+ if (!gradFill) return xml;
191
+ if (preset === "line") return xml.replace(/<a:ln\b([^>]*)>([\s\S]*?)<\/a:ln>/, (match, attrs, body) => {
192
+ const nextBody = body.replace(/<a:(?:solidFill|gradFill)\b[\s\S]*?<\/a:(?:solidFill|gradFill)>|<a:noFill\/>/, gradFill);
193
+ return nextBody === body ? match : `<a:ln${attrs}>${nextBody}</a:ln>`;
194
+ });
195
+ return xml.replace(/<a:(?:solidFill|gradFill)\b[\s\S]*?<\/a:(?:solidFill|gradFill)>|<a:noFill\/>/, gradFill);
196
+ }
197
+ function withRoundRectAdjust(xml, input, rectRadius) {
198
+ if (input.preset !== "roundRect" || rectRadius === void 0) return xml;
199
+ const adj = Math.round(rectRadius * EMU_PER_IN * 1e5 / Math.min(input.width, input.height));
200
+ return xml.replace(/<a:prstGeom prst="roundRect"><a:avLst\/><\/a:prstGeom>/, `<a:prstGeom prst="roundRect"><a:avLst><a:gd name="adj" fmla="val ${adj}"/></a:avLst></a:prstGeom>`);
201
+ }
202
+ function shadowXml(shadow) {
203
+ if (!shadow) return void 0;
204
+ const blur = Math.round((shadow.blur ?? 3) * 12700);
205
+ const dist = Math.round((shadow.offset ?? 23e3 / 12700) * 12700);
206
+ const dir = Math.round((shadow.angle ?? 90) * 6e4);
207
+ const color = cleanHex(shadow.color ?? "000000") ?? "000000";
208
+ const alpha = Math.round((shadow.opacity ?? .35) * 1e5);
209
+ const type = shadow.type ?? "outer";
210
+ return `<a:${type}Shdw sx="100000" sy="100000" kx="0" ky="0" algn="bl" blurRad="${blur}" rotWithShape="1" dist="${dist}" dir="${dir}"><a:srgbClr val="${color}"><a:alpha val="${alpha}"/></a:srgbClr></a:${type}Shdw>`;
211
+ }
212
+ function withShadow(xml, shadow) {
213
+ const effect = shadowXml(shadow);
214
+ if (!effect) return xml;
215
+ if (xml.includes("<a:effectLst>")) return xml.replace("</a:effectLst>", `${effect}</a:effectLst>`);
216
+ return xml.replace("</p:spPr>", `<a:effectLst>${effect}</a:effectLst></p:spPr>`);
217
+ }
218
+ function withLineFlip(xml, flipH, flipV) {
219
+ if (!flipH && !flipV) return xml;
220
+ return xml.replace("<a:xfrm", `<a:xfrm${flipH ? " flipH=\"1\"" : ""}${flipV ? " flipV=\"1\"" : ""}`);
221
+ }
222
+ function withLineZeroExtent(xml, preset, zeroWidth, zeroHeight) {
223
+ if (preset !== "line" || !zeroWidth && !zeroHeight) return xml;
224
+ return xml.replace(/<a:ext cx="(\d+)" cy="(\d+)"\/>/, (_match, cx, cy) => `<a:ext cx="${zeroWidth ? "0" : cx}" cy="${zeroHeight ? "0" : cy}"/>`);
225
+ }
226
+ function withPrstDash(xml, dashType) {
227
+ return xml.replace(/<a:ln\b([^>]*)>([\s\S]*?)<\/a:ln>/g, (match, attrs, body) => {
228
+ const bodyText = body;
229
+ if (bodyText.includes("<a:prstDash")) return match;
230
+ return `<a:ln${attrs}>${bodyText.replace(/(<a:(?:solidFill|noFill\/)>[\s\S]*?<\/a:solidFill>|<a:noFill\/>)/, `$1<a:prstDash val="${dashType}"/>`)}</a:ln>`;
231
+ });
232
+ }
233
+ function withUnsupportedDashStyle(xml, dashType) {
234
+ if (dashType !== "lgDashDotDot") return xml;
235
+ return withPrstDash(xml, dashType);
236
+ }
237
+ function withPptxGenLineArrowDefaults(xml, preset) {
238
+ if (preset !== "line") return xml;
239
+ return withPrstDash(xml.replace(/<a:(headEnd|tailEnd) type="([^"]+)" w="med" len="med"\/>/g, "<a:$1 type=\"$2\"/>"), "solid");
240
+ }
154
241
  function withPptxGenParagraphDefaults(xml) {
155
242
  let result = xml.replaceAll("baseline=\"-25000\"", "baseline=\"-40000\"");
156
243
  result = result.replace(/<a:bodyPr\b([^>]*)\/>/g, (_match, attrs) => {
@@ -168,40 +255,101 @@ function withPptxGenParagraphDefaults(xml) {
168
255
  return result;
169
256
  }
170
257
  function createTextBoxXml(node, name) {
171
- const source = createPptx();
172
- const slideHandle = source.slides[0]?.handle;
173
- if (!slideHandle) throw new Error("createPptx did not create an editable slide");
174
258
  const textOptions = createTextOptions(node);
175
259
  const { paragraphs, hyperlinks } = buildParagraphs(node);
176
- const edit = addTextBox(source, slideHandle, {
177
- offsetX: asEmu(Math.round(textOptions.x * EMU_PER_IN)),
178
- offsetY: asEmu(Math.round(textOptions.y * EMU_PER_IN)),
179
- width: asEmu(Math.round(textOptions.w * EMU_PER_IN)),
180
- height: asEmu(Math.round(textOptions.h * EMU_PER_IN)),
181
- rotation: node.rotate !== void 0 ? asOoxmlAngle(Math.round(node.rotate * 6e4)) : void 0,
182
- name,
183
- body: {
184
- marginLeft: asEmu(0),
185
- marginRight: asEmu(0),
186
- marginTop: asEmu(0),
187
- marginBottom: asEmu(0)
188
- },
189
- paragraphs
190
- }).edits?.at(-1);
191
- if (edit?.kind !== "addTextBox") throw new Error("addTextBox did not produce an addTextBox edit");
192
260
  return {
193
- xml: withPptxGenParagraphDefaults(withGlowAlpha(edit.xml, node)),
261
+ xml: createTextBoxXmlFromInput({
262
+ offsetX: asEmu(Math.round(textOptions.x * EMU_PER_IN)),
263
+ offsetY: asEmu(Math.round(textOptions.y * EMU_PER_IN)),
264
+ width: asEmu(Math.round(textOptions.w * EMU_PER_IN)),
265
+ height: asEmu(Math.round(textOptions.h * EMU_PER_IN)),
266
+ rotation: node.rotate !== void 0 ? asOoxmlAngle(Math.round(node.rotate * 6e4)) : void 0,
267
+ name,
268
+ body: {
269
+ marginLeft: asEmu(0),
270
+ marginRight: asEmu(0),
271
+ marginTop: asEmu(0),
272
+ marginBottom: asEmu(0)
273
+ },
274
+ paragraphs
275
+ }, { xmlTransform: (xml) => withGlowAlpha(xml, node) }),
194
276
  hyperlinks
195
277
  };
196
278
  }
279
+ function createTextBoxXmlFromInput(input, options) {
280
+ const source = createPptx();
281
+ const slideHandle = source.slides[0]?.handle;
282
+ if (!slideHandle) throw new Error("createPptx did not create an editable slide");
283
+ const edit = addTextBox(source, slideHandle, input).edits?.at(-1);
284
+ if (edit?.kind !== "addTextBox") throw new Error("addTextBox did not produce an addTextBox edit");
285
+ const xml = withPptxGenParagraphDefaults(edit.xml);
286
+ return options?.xmlTransform ? options.xmlTransform(xml) : xml;
287
+ }
288
+ function geometryPointXml(point) {
289
+ return `<a:pt x="${Math.round(point.x * EMU_PER_IN)}" y="${Math.round(point.y * EMU_PER_IN)}"/>`;
290
+ }
291
+ function buildCustomGeometryXml(geometry) {
292
+ if (!geometry.points.find((point) => "x" in point)) return void 0;
293
+ let hasMoveTo = false;
294
+ const commands = geometry.points.map((point) => {
295
+ if ("close" in point) return hasMoveTo ? "<a:close/>" : "";
296
+ if (!hasMoveTo) {
297
+ hasMoveTo = true;
298
+ return `<a:moveTo>${geometryPointXml(point)}</a:moveTo>`;
299
+ }
300
+ return `<a:lnTo>${geometryPointXml(point)}</a:lnTo>`;
301
+ }).join("");
302
+ return `<a:custGeom><a:avLst/><a:gdLst/><a:ahLst/><a:cxnLst/><a:rect l="l" t="t" r="r" b="b"/><a:pathLst><a:path w="${Math.round(geometry.width * EMU_PER_IN)}" h="${Math.round(geometry.height * EMU_PER_IN)}">${commands}</a:path></a:pathLst></a:custGeom>`;
303
+ }
304
+ function withCustomGeometry(xml, geometry) {
305
+ if (!geometry) return xml;
306
+ const customGeometry = buildCustomGeometryXml(geometry);
307
+ if (!customGeometry) return xml;
308
+ return xml.replace(/<a:prstGeom\b[\s\S]*?<\/a:prstGeom>/, customGeometry);
309
+ }
310
+ function withOutlineGradientFill(xml, outlineGradient, opacity) {
311
+ if (!outlineGradient) return xml;
312
+ const gradFill = buildGradFillXml(outlineGradient, opacity);
313
+ if (!gradFill) return xml;
314
+ const lineFillPattern = /<a:(?:solidFill|gradFill)\b[\s\S]*?<\/a:(?:solidFill|gradFill)>|<a:noFill\/>/;
315
+ if (/<a:ln\b[^>]*\/>/.test(xml)) return xml.replace(/<a:ln\b([^>]*)\/>/, (_match, attrs) => `<a:ln${attrs}>${gradFill}</a:ln>`);
316
+ return xml.replace(/(<a:ln\b[^>]*>)([\s\S]*?)(<\/a:ln>)/, (_match, open, body, close) => {
317
+ return `${open}${lineFillPattern.test(body) ? body.replace(lineFillPattern, gradFill) : `${gradFill}${body}`}${close}`;
318
+ });
319
+ }
320
+ function createShapeXml(input, options) {
321
+ const source = createPptx();
322
+ const slideHandle = source.slides[0]?.handle;
323
+ if (!slideHandle) throw new Error("createPptx did not create an editable slide");
324
+ const edit = addShape(source, slideHandle, input).edits?.at(-1);
325
+ if (edit?.kind !== "addShape") throw new Error("addShape did not produce an addShape edit");
326
+ let xml = edit.xml;
327
+ xml = withShapeGlowAlpha(xml, options?.glow);
328
+ xml = withSolidFillAlpha(xml, options?.fillColor, options?.fillOpacity);
329
+ xml = withGradientFill(xml, options?.backgroundGradient, options?.fillOpacity, input.preset);
330
+ xml = withOutlineGradientFill(xml, options?.outlineGradient, options?.outlineOpacity ?? options?.fillOpacity);
331
+ xml = withRoundRectAdjust(xml, input, options?.rectRadius);
332
+ xml = withShadow(xml, options?.shadow);
333
+ xml = withUnsupportedDashStyle(xml, options?.dashType);
334
+ xml = withLineFlip(xml, options?.flipH, options?.flipV);
335
+ xml = withLineZeroExtent(xml, input.preset, options?.zeroWidth, options?.zeroHeight);
336
+ xml = withPptxGenLineArrowDefaults(xml, input.preset);
337
+ xml = withCustomGeometry(xml, options?.customGeometry);
338
+ return xml;
339
+ }
197
340
  var GlimpseTextBoxRegistry = class {
198
341
  registered = [];
342
+ textCount = 0;
343
+ shapeCount = 0;
344
+ pictureCount = 0;
345
+ slideBackgroundCount = 0;
199
346
  register(node) {
200
- const index = this.registered.length;
347
+ const index = this.textCount++;
201
348
  const marker = `${MARKER_PREFIX}${index}`;
202
349
  const name = `Text ${index + 1}`;
203
350
  const { xml, hyperlinks } = createTextBoxXml(node, name);
204
351
  this.registered.push({
352
+ kind: "shape",
205
353
  marker,
206
354
  name,
207
355
  xml,
@@ -209,6 +357,68 @@ var GlimpseTextBoxRegistry = class {
209
357
  });
210
358
  return marker;
211
359
  }
360
+ registerTextBox(input, options) {
361
+ const index = this.textCount++;
362
+ const marker = `${MARKER_PREFIX}${index}`;
363
+ const name = options?.name ?? `Text ${index + 1}`;
364
+ const xml = createTextBoxXmlFromInput({
365
+ ...input,
366
+ name
367
+ }, options);
368
+ this.registered.push({
369
+ kind: "shape",
370
+ marker,
371
+ name,
372
+ xml,
373
+ hyperlinks: options?.hyperlinks ?? []
374
+ });
375
+ return marker;
376
+ }
377
+ registerShape(input, options) {
378
+ const index = this.shapeCount++;
379
+ const marker = `${SHAPE_MARKER_PREFIX}${index}`;
380
+ const name = options?.name ?? `Shape ${index + 1}`;
381
+ const xml = createShapeXml({
382
+ ...input,
383
+ name
384
+ }, options);
385
+ this.registered.push({
386
+ kind: "shape",
387
+ marker,
388
+ name,
389
+ xml,
390
+ hyperlinks: []
391
+ });
392
+ return marker;
393
+ }
394
+ registerPicture(input, options) {
395
+ const index = this.pictureCount++;
396
+ const marker = `${PICTURE_MARKER_PREFIX}${index}`;
397
+ const name = options?.name ?? `Picture ${index + 1}`;
398
+ this.registered.push({
399
+ kind: "picture",
400
+ marker,
401
+ name,
402
+ input: {
403
+ ...input,
404
+ name
405
+ },
406
+ shadow: options?.shadow
407
+ });
408
+ return marker;
409
+ }
410
+ registerSlideBackgroundGradient(backgroundGradient, slideNumber, opacity) {
411
+ const xml = buildGradFillXml(backgroundGradient, opacity);
412
+ if (!xml) return void 0;
413
+ const marker = (SLIDE_BACKGROUND_MARKER_BASE + this.slideBackgroundCount++).toString(16).toUpperCase().padStart(6, "0");
414
+ this.registered.push({
415
+ kind: "slideBackground",
416
+ marker,
417
+ slideNumber,
418
+ xml
419
+ });
420
+ return marker;
421
+ }
212
422
  get isEmpty() {
213
423
  return this.registered.length === 0;
214
424
  }
@@ -220,7 +430,7 @@ function escapeRegExp(value) {
220
430
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
221
431
  }
222
432
  function replaceShapeId(xml, id, name) {
223
- return xml.replace(/<p:cNvPr id="[^"]+" name="[^"]*"/, `<p:cNvPr id="${id}" name="${name}"`);
433
+ return xml.replace(/<p:cNvPr id="[^"]+" name="[^"]*"/, `<p:cNvPr id="${xmlAttr(id)}" name="${xmlAttr(name)}"`);
224
434
  }
225
435
  function xmlAttr(value) {
226
436
  return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
@@ -244,6 +454,14 @@ var SlideRelationshipEditor = class {
244
454
  this.changed = true;
245
455
  return id;
246
456
  }
457
+ addImageRelationship(id, target) {
458
+ if (new RegExp(`\\bId="${escapeRegExp(id)}"`).test(this.xml)) return;
459
+ const rel = `<Relationship Id="${xmlAttr(id)}" Type="${IMAGE_REL_TYPE}" Target="${xmlAttr(target)}"/>`;
460
+ this.xml = this.xml.replace("</Relationships>", `${rel}</Relationships>`);
461
+ const numeric = id.match(/^rId(\d+)$/)?.[1];
462
+ if (numeric) this.nextId = Math.max(this.nextId, Number(numeric) + 1);
463
+ this.changed = true;
464
+ }
247
465
  get result() {
248
466
  return {
249
467
  xml: this.xml,
@@ -263,9 +481,23 @@ function withHyperlinkRelationships(xml, hyperlinks, addRelationship) {
263
481
  return `<a:rPr${attrs}>${body}<a:hlinkClick r:id="${addRelationship(href)}"/></a:rPr>`;
264
482
  });
265
483
  }
484
+ function findSlideHandle(source, slidePath) {
485
+ return source.slides.find((slide) => slide.partPath === slidePath)?.handle;
486
+ }
487
+ function findMediaPart(source, partPath) {
488
+ const media = source.packageGraph.media.find((part) => part.partPath === partPath);
489
+ if (!media) throw new Error(`addPicture media part was not found: ${partPath}`);
490
+ return media;
491
+ }
492
+ function findSlideImageTarget(source, slidePath, relationshipId) {
493
+ const relationship = source.packageGraph.relationships.find((group) => group.sourcePartPath === slidePath)?.relationships.find((candidate) => candidate.id === relationshipId);
494
+ if (!relationship) throw new Error(`addPicture relationship was not found: ${relationshipId}`);
495
+ return relationship.target;
496
+ }
266
497
  function applyGlimpseTextBoxesToXml(xml, registry, addRelationship) {
267
498
  let result = xml;
268
499
  for (const entry of registry.entries) {
500
+ if (entry.kind !== "shape") continue;
269
501
  const re = new RegExp(`<p:sp><p:nvSpPr><p:cNvPr id="([^"]+)" name="${escapeRegExp(entry.marker)}"[\\s\\S]*?</p:sp>`, "g");
270
502
  result = result.replace(re, (_match, id) => {
271
503
  return withHyperlinkRelationships(replaceShapeId(entry.xml, id, entry.name), entry.hyperlinks, addRelationship);
@@ -273,8 +505,66 @@ function applyGlimpseTextBoxesToXml(xml, registry, addRelationship) {
273
505
  }
274
506
  return result;
275
507
  }
508
+ function applySlideBackgroundGradientsToXml(xml, registry, slidePath) {
509
+ let result = xml;
510
+ const slideNumber = Number(slidePath.match(/slide(\d+)\.xml$/)?.[1]);
511
+ for (const entry of registry.entries) {
512
+ if (entry.kind !== "slideBackground") continue;
513
+ if (entry.slideNumber !== slideNumber) continue;
514
+ const target = `<p:bgPr><a:solidFill><a:srgbClr val="${entry.marker}"/></a:solidFill></p:bgPr>`;
515
+ result = result.replace(target, `<p:bgPr>${entry.xml}</p:bgPr>`);
516
+ }
517
+ return result;
518
+ }
519
+ function withPptxGenPictureSizingXml(xml, hasSizing) {
520
+ if (!hasSizing) return xml;
521
+ return xml.replace("<a:stretch><a:fillRect/></a:stretch>", "<a:stretch/>");
522
+ }
523
+ function applyGlimpsePicturesToXml(xml, registry, source, slidePath, relationshipEditor, addMedia) {
524
+ let result = xml;
525
+ let editedSource = source;
526
+ for (const entry of registry.entries) {
527
+ if (entry.kind !== "picture") continue;
528
+ const re = new RegExp(`<p:sp><p:nvSpPr><p:cNvPr id="([^"]+)" name="${escapeRegExp(entry.marker)}"[\\s\\S]*?</p:sp>`, "g");
529
+ result = result.replace(re, (_match, id) => {
530
+ const slideHandle = findSlideHandle(editedSource, slidePath);
531
+ if (!slideHandle) throw new Error(`slide handle was not found: ${slidePath}`);
532
+ editedSource = addPicture(editedSource, slideHandle, entry.input);
533
+ const edit = editedSource.edits?.at(-1);
534
+ if (edit?.kind !== "addPicture") throw new Error("addPicture did not produce an addPicture edit");
535
+ addMedia(findMediaPart(editedSource, edit.mediaPartPath));
536
+ relationshipEditor.addImageRelationship(edit.relationshipId, findSlideImageTarget(editedSource, slidePath, edit.relationshipId));
537
+ return withShadow(withPptxGenPictureSizingXml(replaceShapeId(edit.xml, id, entry.name), entry.input.crop !== void 0), entry.shadow);
538
+ });
539
+ }
540
+ return {
541
+ xml: result,
542
+ source: editedSource
543
+ };
544
+ }
545
+ function ensureContentTypeDefault(xml, extension, contentType) {
546
+ if (new RegExp(`<Default\\s+[^>]*Extension="${escapeRegExp(extension)}"[^>]*/>`).test(xml)) return xml;
547
+ const entry = `<Default Extension="${xmlAttr(extension)}" ContentType="${xmlAttr(contentType)}"/>`;
548
+ return xml.replace("</Types>", `${entry}</Types>`);
549
+ }
550
+ async function applyGlimpseMediaParts(zip, mediaParts) {
551
+ const mediaList = Array.from(mediaParts);
552
+ if (mediaList.length === 0) return;
553
+ const contentTypesFile = zip.file("[Content_Types].xml");
554
+ if (!contentTypesFile) throw new Error("[Content_Types].xml was not found in pptx output");
555
+ let contentTypesXml = await contentTypesFile.async("text");
556
+ for (const media of mediaList) {
557
+ zip.file(media.partPath, media.bytes);
558
+ const extension = media.partPath.split(".").at(-1);
559
+ if (!extension) continue;
560
+ contentTypesXml = ensureContentTypeDefault(contentTypesXml, extension, media.contentType);
561
+ }
562
+ zip.file("[Content_Types].xml", contentTypesXml);
563
+ }
276
564
  async function applyGlimpseTextBoxes(data, registry) {
277
565
  const zip = await (await loadJSZip()).loadAsync(data);
566
+ let source = registry.entries.some((entry) => entry.kind === "picture") ? readPptx(data instanceof Uint8Array ? data : new Uint8Array(data)) : void 0;
567
+ const addedMedia = /* @__PURE__ */ new Map();
278
568
  const slidePaths = Object.keys(zip.files).filter((path) => /^ppt\/slides\/slide\d+\.xml$/.test(path));
279
569
  for (const path of slidePaths) {
280
570
  const file = zip.file(path);
@@ -283,11 +573,20 @@ async function applyGlimpseTextBoxes(data, registry) {
283
573
  const relsPath = slideRelsPath(path);
284
574
  const relsFile = zip.file(relsPath);
285
575
  const relationshipEditor = createRelationshipEditor(relsFile ? await relsFile.async("text") : void 0);
286
- const replaced = applyGlimpseTextBoxesToXml(original, registry, (href) => relationshipEditor.addHyperlink(href));
576
+ const withSlideBackground = applySlideBackgroundGradientsToXml(original, registry, path);
577
+ const pictureResult = source ? applyGlimpsePicturesToXml(withSlideBackground, registry, source, path, relationshipEditor, (media) => {
578
+ addedMedia.set(media.partPath, media);
579
+ }) : {
580
+ xml: withSlideBackground,
581
+ source
582
+ };
583
+ source = pictureResult.source;
584
+ const replaced = applyGlimpseTextBoxesToXml(pictureResult.xml, registry, (href) => relationshipEditor.addHyperlink(href));
287
585
  if (replaced !== original) zip.file(path, replaced);
288
586
  const relationships = relationshipEditor.result;
289
587
  if (relationships.changed) zip.file(relsPath, relationships.xml);
290
588
  }
589
+ await applyGlimpseMediaParts(zip, addedMedia.values());
291
590
  return zip;
292
591
  }
293
592
  function patchPptxWriteForGlimpseTextBoxes(pptx, registry) {
@@ -342,6 +641,6 @@ function patchPptxWriteForGlimpseTextBoxes(pptx, registry) {
342
641
  pptx.writeFile = patchedWriteFile;
343
642
  }
344
643
  //#endregion
345
- export { GlimpseTextBoxRegistry, patchPptxWriteForGlimpseTextBoxes };
644
+ export { GlimpseTextBoxRegistry, cleanHex, patchPptxWriteForGlimpseTextBoxes, toColorInput };
346
645
 
347
646
  //# sourceMappingURL=glimpseTextBoxes.js.map