@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.
- package/dist/buildContext.js +1 -5
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +1 -7
- package/dist/buildPptx.js.map +1 -1
- package/dist/renderPptx/glimpseTextBoxes.js +325 -26
- package/dist/renderPptx/glimpseTextBoxes.js.map +1 -1
- package/dist/renderPptx/nodes/flow.js +95 -90
- package/dist/renderPptx/nodes/flow.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +33 -24
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/image.js +5 -22
- package/dist/renderPptx/nodes/image.js.map +1 -1
- package/dist/renderPptx/nodes/list.js +96 -111
- package/dist/renderPptx/nodes/list.js.map +1 -1
- package/dist/renderPptx/nodes/matrix.js +92 -70
- package/dist/renderPptx/nodes/matrix.js.map +1 -1
- package/dist/renderPptx/nodes/processArrow.js +68 -35
- package/dist/renderPptx/nodes/processArrow.js.map +1 -1
- package/dist/renderPptx/nodes/pyramid.js +34 -16
- package/dist/renderPptx/nodes/pyramid.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +63 -37
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/svg.js +2 -5
- package/dist/renderPptx/nodes/svg.js.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +108 -74
- package/dist/renderPptx/nodes/timeline.js.map +1 -1
- package/dist/renderPptx/nodes/tree.js +78 -79
- package/dist/renderPptx/nodes/tree.js.map +1 -1
- package/dist/renderPptx/renderPptx.js +4 -5
- package/dist/renderPptx/renderPptx.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +85 -49
- package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
- package/dist/renderPptx/utils/glimpsePicture.js +130 -0
- package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
- package/dist/renderPptx/utils/glimpseShape.js +90 -0
- package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
- package/dist/renderPptx/utils/glimpseTextBox.js +126 -0
- package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
- package/dist/renderPptx/utils/straightLine.js +29 -14
- package/dist/renderPptx/utils/straightLine.js.map +1 -1
- package/dist/renderPptx/utils/visualStyle.js +1 -24
- package/dist/renderPptx/utils/visualStyle.js.map +1 -1
- package/package.json +2 -2
- package/dist/renderPptx/glowEffects.js +0 -157
- package/dist/renderPptx/glowEffects.js.map +0 -1
- package/dist/renderPptx/gradientFills.js +0 -154
- package/dist/renderPptx/gradientFills.js.map +0 -1
package/dist/buildContext.js
CHANGED
|
@@ -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
|
}
|
package/dist/buildContext.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildContext.js","names":[],"sources":["../src/buildContext.ts"],"sourcesContent":["import type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport { DiagnosticCollector } from \"./diagnostics.ts\";\nimport {
|
|
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"}
|
package/dist/buildPptx.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"buildPptx.d.ts","names":[],"sources":["../src/buildPptx.ts"],"mappings":";;;;;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 {
|
|
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 {
|
package/dist/buildPptx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPptx.js","names":[],"sources":["../src/buildPptx.ts"],"sourcesContent":["import { autoFitSlide } from \"./autoFit/autoFit.ts\";\nimport { createBuildContext } from \"./buildContext.ts\";\nimport { calcYogaLayout } from \"./calcYogaLayout/calcYogaLayout.ts\";\nimport type { TextMeasurementMode } from \"./calcYogaLayout/measureText.ts\";\nimport type { YogaNodeMap } from \"./calcYogaLayout/types.ts\";\nimport { extractLayoutResults } from \"./calcYogaLayout/types.ts\";\nimport type { Diagnostic } from \"./diagnostics.ts\";\nimport { DiagnosticsError } from \"./diagnostics.ts\";\nimport { parseMasterPptx } from \"./parseMasterPptx.ts\";\nimport { parseXml } from \"./parseXml/parseXml.ts\";\nimport {
|
|
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:
|
|
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.
|
|
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("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -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
|
|
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
|