pptx-glimpse 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/index.cjs +4531 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +4493 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4493 @@
|
|
|
1
|
+
// src/parser/pptx-reader.ts
|
|
2
|
+
import JSZip from "jszip";
|
|
3
|
+
async function readPptx(input) {
|
|
4
|
+
const zip = await JSZip.loadAsync(input);
|
|
5
|
+
const files = /* @__PURE__ */ new Map();
|
|
6
|
+
const media = /* @__PURE__ */ new Map();
|
|
7
|
+
const promises = [];
|
|
8
|
+
zip.forEach((relativePath, zipEntry) => {
|
|
9
|
+
if (zipEntry.dir) return;
|
|
10
|
+
if (relativePath.startsWith("ppt/media/")) {
|
|
11
|
+
promises.push(
|
|
12
|
+
zipEntry.async("nodebuffer").then((buf) => {
|
|
13
|
+
media.set(relativePath, buf);
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
} else if (relativePath.endsWith(".xml") || relativePath.endsWith(".rels") || relativePath === "[Content_Types].xml") {
|
|
17
|
+
promises.push(
|
|
18
|
+
zipEntry.async("string").then((str) => {
|
|
19
|
+
files.set(relativePath, str);
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
await Promise.all(promises);
|
|
25
|
+
return { files, media };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/parser/xml-parser.ts
|
|
29
|
+
import { XMLParser } from "fast-xml-parser";
|
|
30
|
+
var ARRAY_TAGS = /* @__PURE__ */ new Set([
|
|
31
|
+
"sp",
|
|
32
|
+
"pic",
|
|
33
|
+
"cxnSp",
|
|
34
|
+
"grpSp",
|
|
35
|
+
"graphicFrame",
|
|
36
|
+
"p",
|
|
37
|
+
"r",
|
|
38
|
+
"br",
|
|
39
|
+
"Relationship",
|
|
40
|
+
"sldId",
|
|
41
|
+
"gs",
|
|
42
|
+
"gridCol",
|
|
43
|
+
"tr",
|
|
44
|
+
"tc",
|
|
45
|
+
"ser",
|
|
46
|
+
"pt"
|
|
47
|
+
]);
|
|
48
|
+
function createXmlParser() {
|
|
49
|
+
return new XMLParser({
|
|
50
|
+
ignoreAttributes: false,
|
|
51
|
+
attributeNamePrefix: "@_",
|
|
52
|
+
removeNSPrefix: true,
|
|
53
|
+
isArray: (_name, jpath) => {
|
|
54
|
+
const tag = jpath.split(".").pop() ?? "";
|
|
55
|
+
return ARRAY_TAGS.has(tag);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function parseXml(xml) {
|
|
60
|
+
const parser = createXmlParser();
|
|
61
|
+
return parser.parse(xml);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/parser/presentation-parser.ts
|
|
65
|
+
var WARN_PREFIX = "[pptx-glimpse]";
|
|
66
|
+
var DEFAULT_SLIDE_WIDTH = 9144e3;
|
|
67
|
+
var DEFAULT_SLIDE_HEIGHT = 5143500;
|
|
68
|
+
function parsePresentation(xml) {
|
|
69
|
+
const parsed = parseXml(xml);
|
|
70
|
+
const pres = parsed.presentation;
|
|
71
|
+
if (!pres) {
|
|
72
|
+
console.warn(`${WARN_PREFIX} Presentation: missing root element "presentation" in XML`);
|
|
73
|
+
return {
|
|
74
|
+
slideSize: { width: DEFAULT_SLIDE_WIDTH, height: DEFAULT_SLIDE_HEIGHT },
|
|
75
|
+
slideRIds: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const sldSz = pres.sldSz;
|
|
79
|
+
let slideSize;
|
|
80
|
+
if (!sldSz || sldSz["@_cx"] === void 0 || sldSz["@_cy"] === void 0) {
|
|
81
|
+
console.warn(
|
|
82
|
+
`${WARN_PREFIX} Presentation: sldSz missing, using default ${DEFAULT_SLIDE_WIDTH}x${DEFAULT_SLIDE_HEIGHT} EMU`
|
|
83
|
+
);
|
|
84
|
+
slideSize = { width: DEFAULT_SLIDE_WIDTH, height: DEFAULT_SLIDE_HEIGHT };
|
|
85
|
+
} else {
|
|
86
|
+
slideSize = {
|
|
87
|
+
width: Number(sldSz["@_cx"]),
|
|
88
|
+
height: Number(sldSz["@_cy"])
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const sldIdLst = pres.sldIdLst?.sldId ?? [];
|
|
92
|
+
const slideRIds = sldIdLst.map((s) => s["@_r:id"] ?? s["@_id"]).filter((id) => {
|
|
93
|
+
if (id === void 0) {
|
|
94
|
+
console.warn(
|
|
95
|
+
`${WARN_PREFIX} Presentation: undefined slide relationship ID found, skipping`
|
|
96
|
+
);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
return { slideSize, slideRIds };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/parser/theme-parser.ts
|
|
105
|
+
var WARN_PREFIX2 = "[pptx-glimpse]";
|
|
106
|
+
function parseTheme(xml) {
|
|
107
|
+
const parsed = parseXml(xml);
|
|
108
|
+
if (!parsed.theme) {
|
|
109
|
+
console.warn(`${WARN_PREFIX2} Theme: missing root element "theme" in XML`);
|
|
110
|
+
return {
|
|
111
|
+
colorScheme: defaultColorScheme(),
|
|
112
|
+
fontScheme: {
|
|
113
|
+
majorFont: "Calibri",
|
|
114
|
+
minorFont: "Calibri",
|
|
115
|
+
majorFontEa: null,
|
|
116
|
+
minorFontEa: null
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const themeElements = parsed.theme.themeElements;
|
|
121
|
+
if (!themeElements) {
|
|
122
|
+
console.warn(`${WARN_PREFIX2} Theme: themeElements not found, using defaults`);
|
|
123
|
+
return {
|
|
124
|
+
colorScheme: defaultColorScheme(),
|
|
125
|
+
fontScheme: {
|
|
126
|
+
majorFont: "Calibri",
|
|
127
|
+
minorFont: "Calibri",
|
|
128
|
+
majorFontEa: null,
|
|
129
|
+
minorFontEa: null
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (!themeElements.clrScheme) {
|
|
134
|
+
console.warn(`${WARN_PREFIX2} Theme: colorScheme not found, using defaults`);
|
|
135
|
+
}
|
|
136
|
+
if (!themeElements.fontScheme) {
|
|
137
|
+
console.warn(`${WARN_PREFIX2} Theme: fontScheme not found, using defaults`);
|
|
138
|
+
}
|
|
139
|
+
const colorScheme = parseColorScheme(themeElements.clrScheme);
|
|
140
|
+
const fontScheme = parseFontScheme(themeElements.fontScheme);
|
|
141
|
+
return { colorScheme, fontScheme };
|
|
142
|
+
}
|
|
143
|
+
function parseColorScheme(clrScheme) {
|
|
144
|
+
if (!clrScheme) return defaultColorScheme();
|
|
145
|
+
return {
|
|
146
|
+
dk1: extractColor(clrScheme.dk1),
|
|
147
|
+
lt1: extractColor(clrScheme.lt1),
|
|
148
|
+
dk2: extractColor(clrScheme.dk2),
|
|
149
|
+
lt2: extractColor(clrScheme.lt2),
|
|
150
|
+
accent1: extractColor(clrScheme.accent1),
|
|
151
|
+
accent2: extractColor(clrScheme.accent2),
|
|
152
|
+
accent3: extractColor(clrScheme.accent3),
|
|
153
|
+
accent4: extractColor(clrScheme.accent4),
|
|
154
|
+
accent5: extractColor(clrScheme.accent5),
|
|
155
|
+
accent6: extractColor(clrScheme.accent6),
|
|
156
|
+
hlink: extractColor(clrScheme.hlink),
|
|
157
|
+
folHlink: extractColor(clrScheme.folHlink)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function extractColor(colorNode) {
|
|
161
|
+
if (!colorNode) return "#000000";
|
|
162
|
+
if (colorNode.srgbClr) {
|
|
163
|
+
return `#${colorNode.srgbClr["@_val"]}`;
|
|
164
|
+
}
|
|
165
|
+
if (colorNode.sysClr) {
|
|
166
|
+
return `#${colorNode.sysClr["@_lastClr"] ?? "000000"}`;
|
|
167
|
+
}
|
|
168
|
+
return "#000000";
|
|
169
|
+
}
|
|
170
|
+
function parseFontScheme(fontScheme) {
|
|
171
|
+
if (!fontScheme)
|
|
172
|
+
return { majorFont: "Calibri", minorFont: "Calibri", majorFontEa: null, minorFontEa: null };
|
|
173
|
+
const majorFont = fontScheme.majorFont?.latin?.["@_typeface"] ?? "Calibri";
|
|
174
|
+
const minorFont = fontScheme.minorFont?.latin?.["@_typeface"] ?? "Calibri";
|
|
175
|
+
const majorFontEa = fontScheme.majorFont?.ea?.["@_typeface"] ?? null;
|
|
176
|
+
const minorFontEa = fontScheme.minorFont?.ea?.["@_typeface"] ?? null;
|
|
177
|
+
return { majorFont, minorFont, majorFontEa, minorFontEa };
|
|
178
|
+
}
|
|
179
|
+
function defaultColorScheme() {
|
|
180
|
+
return {
|
|
181
|
+
dk1: "#000000",
|
|
182
|
+
lt1: "#FFFFFF",
|
|
183
|
+
dk2: "#44546A",
|
|
184
|
+
lt2: "#E7E6E6",
|
|
185
|
+
accent1: "#4472C4",
|
|
186
|
+
accent2: "#ED7D31",
|
|
187
|
+
accent3: "#A5A5A5",
|
|
188
|
+
accent4: "#FFC000",
|
|
189
|
+
accent5: "#5B9BD5",
|
|
190
|
+
accent6: "#70AD47",
|
|
191
|
+
hlink: "#0563C1",
|
|
192
|
+
folHlink: "#954F72"
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/parser/relationship-parser.ts
|
|
197
|
+
var WARN_PREFIX3 = "[pptx-glimpse]";
|
|
198
|
+
function parseRelationships(xml) {
|
|
199
|
+
const parsed = parseXml(xml);
|
|
200
|
+
const rels = /* @__PURE__ */ new Map();
|
|
201
|
+
const root = parsed;
|
|
202
|
+
if (!root?.Relationships) {
|
|
203
|
+
console.warn(`${WARN_PREFIX3} Relationship: missing root element "Relationships" in XML`);
|
|
204
|
+
return rels;
|
|
205
|
+
}
|
|
206
|
+
const relationships = root.Relationships.Relationship;
|
|
207
|
+
if (!relationships) return rels;
|
|
208
|
+
for (const rel of relationships) {
|
|
209
|
+
const id = rel["@_Id"];
|
|
210
|
+
const type = rel["@_Type"];
|
|
211
|
+
const target = rel["@_Target"];
|
|
212
|
+
if (!id || !type || !target) {
|
|
213
|
+
console.warn(`${WARN_PREFIX3} Relationship: entry missing required attribute, skipping`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
rels.set(id, { id, type, target });
|
|
217
|
+
}
|
|
218
|
+
return rels;
|
|
219
|
+
}
|
|
220
|
+
function buildRelsPath(xmlPath) {
|
|
221
|
+
const lastSlash = xmlPath.lastIndexOf("/");
|
|
222
|
+
const dir = xmlPath.substring(0, lastSlash);
|
|
223
|
+
const filename = xmlPath.substring(lastSlash + 1);
|
|
224
|
+
return `${dir}/_rels/${filename}.rels`;
|
|
225
|
+
}
|
|
226
|
+
function resolveRelationshipTarget(basePath, relTarget) {
|
|
227
|
+
if (relTarget.startsWith("/")) {
|
|
228
|
+
return relTarget.slice(1);
|
|
229
|
+
}
|
|
230
|
+
const baseDir = basePath.substring(0, basePath.lastIndexOf("/"));
|
|
231
|
+
const parts = `${baseDir}/${relTarget}`.split("/");
|
|
232
|
+
const resolved = [];
|
|
233
|
+
for (const part of parts) {
|
|
234
|
+
if (part === "..") {
|
|
235
|
+
resolved.pop();
|
|
236
|
+
} else if (part !== ".") {
|
|
237
|
+
resolved.push(part);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return resolved.join("/");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/parser/fill-parser.ts
|
|
244
|
+
var WARN_PREFIX4 = "[pptx-glimpse]";
|
|
245
|
+
function parseFillFromNode(node, colorResolver, context) {
|
|
246
|
+
if (!node) return null;
|
|
247
|
+
if (node.noFill !== void 0) {
|
|
248
|
+
return { type: "none" };
|
|
249
|
+
}
|
|
250
|
+
if (node.solidFill) {
|
|
251
|
+
const color = colorResolver.resolve(node.solidFill);
|
|
252
|
+
if (color) {
|
|
253
|
+
return { type: "solid", color };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (node.gradFill) {
|
|
257
|
+
return parseGradientFill(node.gradFill, colorResolver);
|
|
258
|
+
}
|
|
259
|
+
if (node.blipFill && context) {
|
|
260
|
+
return parseBlipFill(node.blipFill, context);
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
function parseBlipFill(blipFillNode, context) {
|
|
265
|
+
const rId = blipFillNode?.blip?.["@_r:embed"] ?? blipFillNode?.blip?.["@_embed"];
|
|
266
|
+
if (!rId) return null;
|
|
267
|
+
const rel = context.rels.get(rId);
|
|
268
|
+
if (!rel) return null;
|
|
269
|
+
const mediaPath = resolveRelationshipTarget(context.basePath, rel.target);
|
|
270
|
+
const mediaData = context.archive.media.get(mediaPath);
|
|
271
|
+
if (!mediaData) return null;
|
|
272
|
+
const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
|
|
273
|
+
const mimeMap = {
|
|
274
|
+
png: "image/png",
|
|
275
|
+
jpg: "image/jpeg",
|
|
276
|
+
jpeg: "image/jpeg",
|
|
277
|
+
gif: "image/gif",
|
|
278
|
+
svg: "image/svg+xml",
|
|
279
|
+
emf: "image/emf",
|
|
280
|
+
wmf: "image/wmf"
|
|
281
|
+
};
|
|
282
|
+
const mimeType = mimeMap[ext] ?? "image/png";
|
|
283
|
+
const imageData = mediaData.toString("base64");
|
|
284
|
+
return { type: "image", imageData, mimeType };
|
|
285
|
+
}
|
|
286
|
+
function parseGradientFill(gradNode, colorResolver) {
|
|
287
|
+
const gsLst = gradNode.gsLst?.gs;
|
|
288
|
+
if (!gsLst) {
|
|
289
|
+
console.warn(`${WARN_PREFIX4} GradientFill: gsLst not found, skipping gradient`);
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
const stops = [];
|
|
293
|
+
for (const gs of gsLst) {
|
|
294
|
+
const position = Number(gs["@_pos"] ?? 0) / 1e5;
|
|
295
|
+
const color = colorResolver.resolve(gs);
|
|
296
|
+
if (color) {
|
|
297
|
+
stops.push({ position, color });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
let angle = 0;
|
|
301
|
+
if (gradNode.lin) {
|
|
302
|
+
angle = Number(gradNode.lin["@_ang"] ?? 0) / 6e4;
|
|
303
|
+
}
|
|
304
|
+
return { type: "gradient", stops, angle };
|
|
305
|
+
}
|
|
306
|
+
function parseOutline(lnNode, colorResolver) {
|
|
307
|
+
if (!lnNode) return null;
|
|
308
|
+
const width = Number(lnNode["@_w"] ?? 12700);
|
|
309
|
+
let fill = null;
|
|
310
|
+
if (lnNode.solidFill) {
|
|
311
|
+
const color = colorResolver.resolve(lnNode.solidFill);
|
|
312
|
+
if (color) {
|
|
313
|
+
fill = { type: "solid", color };
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (lnNode.noFill !== void 0) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
const dashStyle = lnNode.prstDash?.["@_val"] ?? "solid";
|
|
320
|
+
return { width, fill, dashStyle };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/parser/effect-parser.ts
|
|
324
|
+
function parseEffectList(effectLstNode, colorResolver) {
|
|
325
|
+
if (!effectLstNode) return null;
|
|
326
|
+
const outerShadow = parseOuterShadow(effectLstNode.outerShdw, colorResolver);
|
|
327
|
+
const innerShadow = parseInnerShadow(effectLstNode.innerShdw, colorResolver);
|
|
328
|
+
const glow = parseGlow(effectLstNode.glow, colorResolver);
|
|
329
|
+
const softEdge = parseSoftEdge(effectLstNode.softEdge);
|
|
330
|
+
if (!outerShadow && !innerShadow && !glow && !softEdge) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return { outerShadow, innerShadow, glow, softEdge };
|
|
334
|
+
}
|
|
335
|
+
function parseOuterShadow(node, colorResolver) {
|
|
336
|
+
if (!node) return null;
|
|
337
|
+
const color = colorResolver.resolve(node);
|
|
338
|
+
if (!color) return null;
|
|
339
|
+
return {
|
|
340
|
+
blurRadius: Number(node["@_blurRad"] ?? 0),
|
|
341
|
+
distance: Number(node["@_dist"] ?? 0),
|
|
342
|
+
direction: Number(node["@_dir"] ?? 0) / 6e4,
|
|
343
|
+
color,
|
|
344
|
+
alignment: node["@_algn"] ?? "b",
|
|
345
|
+
rotateWithShape: node["@_rotWithShape"] !== "0"
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function parseInnerShadow(node, colorResolver) {
|
|
349
|
+
if (!node) return null;
|
|
350
|
+
const color = colorResolver.resolve(node);
|
|
351
|
+
if (!color) return null;
|
|
352
|
+
return {
|
|
353
|
+
blurRadius: Number(node["@_blurRad"] ?? 0),
|
|
354
|
+
distance: Number(node["@_dist"] ?? 0),
|
|
355
|
+
direction: Number(node["@_dir"] ?? 0) / 6e4,
|
|
356
|
+
color
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function parseGlow(node, colorResolver) {
|
|
360
|
+
if (!node) return null;
|
|
361
|
+
const color = colorResolver.resolve(node);
|
|
362
|
+
if (!color) return null;
|
|
363
|
+
return {
|
|
364
|
+
radius: Number(node["@_rad"] ?? 0),
|
|
365
|
+
color
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function parseSoftEdge(node) {
|
|
369
|
+
if (!node) return null;
|
|
370
|
+
return {
|
|
371
|
+
radius: Number(node["@_rad"] ?? 0)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/parser/chart-parser.ts
|
|
376
|
+
var ACCENT_KEYS = ["accent1", "accent2", "accent3", "accent4", "accent5", "accent6"];
|
|
377
|
+
var CHART_TYPE_MAP = [
|
|
378
|
+
["barChart", "bar"],
|
|
379
|
+
["bar3DChart", "bar"],
|
|
380
|
+
["lineChart", "line"],
|
|
381
|
+
["line3DChart", "line"],
|
|
382
|
+
["pieChart", "pie"],
|
|
383
|
+
["pie3DChart", "pie"],
|
|
384
|
+
["doughnutChart", "pie"],
|
|
385
|
+
["scatterChart", "scatter"]
|
|
386
|
+
];
|
|
387
|
+
function parseChart(chartXml, colorResolver) {
|
|
388
|
+
const parsed = parseXml(chartXml);
|
|
389
|
+
const chartSpace = parsed.chartSpace;
|
|
390
|
+
if (!chartSpace) return null;
|
|
391
|
+
const chart = chartSpace.chart;
|
|
392
|
+
if (!chart) return null;
|
|
393
|
+
const plotArea = chart.plotArea;
|
|
394
|
+
if (!plotArea) return null;
|
|
395
|
+
const title = parseChartTitle(chart.title);
|
|
396
|
+
const { chartType, series, categories, barDirection } = parseChartTypeAndData(
|
|
397
|
+
plotArea,
|
|
398
|
+
colorResolver
|
|
399
|
+
);
|
|
400
|
+
if (!chartType) return null;
|
|
401
|
+
const legend = parseLegend(chart.legend);
|
|
402
|
+
return {
|
|
403
|
+
chartType,
|
|
404
|
+
title,
|
|
405
|
+
series,
|
|
406
|
+
categories,
|
|
407
|
+
...barDirection !== void 0 && { barDirection },
|
|
408
|
+
legend
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function parseChartTypeAndData(plotArea, colorResolver) {
|
|
412
|
+
for (const [xmlTag, chartType] of CHART_TYPE_MAP) {
|
|
413
|
+
const chartNode = plotArea[xmlTag];
|
|
414
|
+
if (!chartNode) continue;
|
|
415
|
+
const serList = chartNode.ser ?? [];
|
|
416
|
+
const series = serList.map(
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
418
|
+
(ser, index) => parseSeries(ser, chartType, index, colorResolver)
|
|
419
|
+
);
|
|
420
|
+
const categories = extractCategories(serList);
|
|
421
|
+
const barDirection = chartType === "bar" ? chartNode.barDir?.["@_val"] ?? "col" : void 0;
|
|
422
|
+
return { chartType, series, categories, barDirection };
|
|
423
|
+
}
|
|
424
|
+
return { chartType: null, series: [], categories: [] };
|
|
425
|
+
}
|
|
426
|
+
function parseSeries(ser, chartType, seriesIndex, colorResolver) {
|
|
427
|
+
const name = parseSeriesName(ser.tx);
|
|
428
|
+
const values = parseNumericData(chartType === "scatter" ? ser.yVal : ser.val);
|
|
429
|
+
const xValues = chartType === "scatter" ? parseNumericData(ser.xVal) : void 0;
|
|
430
|
+
const color = resolveSeriesColor(ser.spPr, seriesIndex, colorResolver);
|
|
431
|
+
return {
|
|
432
|
+
name,
|
|
433
|
+
values,
|
|
434
|
+
...xValues !== void 0 && { xValues },
|
|
435
|
+
color
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function parseSeriesName(tx) {
|
|
439
|
+
if (!tx) return null;
|
|
440
|
+
const strCache = tx.strRef?.strCache;
|
|
441
|
+
if (strCache?.pt) {
|
|
442
|
+
const pts = strCache.pt;
|
|
443
|
+
return pts[0]?.v ?? null;
|
|
444
|
+
}
|
|
445
|
+
if (typeof tx.v === "string") return tx.v;
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
function parseNumericData(valNode) {
|
|
449
|
+
if (!valNode) return [];
|
|
450
|
+
const numCache = valNode.numRef?.numCache;
|
|
451
|
+
if (!numCache?.pt) return [];
|
|
452
|
+
const pts = numCache.pt;
|
|
453
|
+
return pts.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => Number(pt.v ?? 0));
|
|
454
|
+
}
|
|
455
|
+
function extractCategories(serList) {
|
|
456
|
+
for (const ser of serList) {
|
|
457
|
+
const cat = ser.cat;
|
|
458
|
+
if (!cat) continue;
|
|
459
|
+
const strCache = cat.strRef?.strCache ?? cat.numRef?.numCache;
|
|
460
|
+
if (strCache?.pt) {
|
|
461
|
+
return strCache.pt.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => String(pt.v ?? ""));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return [];
|
|
465
|
+
}
|
|
466
|
+
function resolveSeriesColor(spPr, seriesIndex, colorResolver) {
|
|
467
|
+
if (spPr) {
|
|
468
|
+
const resolved2 = colorResolver.resolve(spPr.solidFill ?? spPr);
|
|
469
|
+
if (resolved2) return resolved2;
|
|
470
|
+
}
|
|
471
|
+
const accentKey = ACCENT_KEYS[seriesIndex % ACCENT_KEYS.length];
|
|
472
|
+
const resolved = colorResolver.resolve({ schemeClr: { "@_val": accentKey } });
|
|
473
|
+
return resolved ?? { hex: "#4472C4", alpha: 1 };
|
|
474
|
+
}
|
|
475
|
+
function parseChartTitle(titleNode) {
|
|
476
|
+
if (!titleNode) return null;
|
|
477
|
+
const rich = titleNode.tx?.rich;
|
|
478
|
+
if (!rich?.p) return null;
|
|
479
|
+
const pList = Array.isArray(rich.p) ? rich.p : [rich.p];
|
|
480
|
+
const texts = [];
|
|
481
|
+
for (const p of pList) {
|
|
482
|
+
const rList = p.r ?? [];
|
|
483
|
+
for (const r of rList) {
|
|
484
|
+
const t = r.t;
|
|
485
|
+
if (typeof t === "string") texts.push(t);
|
|
486
|
+
else if (t?.["#text"]) texts.push(String(t["#text"]));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return texts.length > 0 ? texts.join("") : null;
|
|
490
|
+
}
|
|
491
|
+
function parseLegend(legendNode) {
|
|
492
|
+
if (!legendNode) return null;
|
|
493
|
+
const pos = legendNode.legendPos?.["@_val"] ?? "b";
|
|
494
|
+
return { position: pos };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/parser/table-parser.ts
|
|
498
|
+
function parseTable(tblNode, colorResolver) {
|
|
499
|
+
if (!tblNode) return null;
|
|
500
|
+
const columns = parseColumns(tblNode.tblGrid);
|
|
501
|
+
if (columns.length === 0) return null;
|
|
502
|
+
const rows = parseRows(tblNode.tr, colorResolver);
|
|
503
|
+
return { rows, columns };
|
|
504
|
+
}
|
|
505
|
+
function parseColumns(tblGrid) {
|
|
506
|
+
if (!tblGrid) return [];
|
|
507
|
+
const gridCols = tblGrid.gridCol ?? [];
|
|
508
|
+
return gridCols.map((col) => ({
|
|
509
|
+
width: Number(col["@_w"] ?? 0)
|
|
510
|
+
}));
|
|
511
|
+
}
|
|
512
|
+
function parseRows(trList, colorResolver) {
|
|
513
|
+
if (!trList) return [];
|
|
514
|
+
const rows = [];
|
|
515
|
+
for (const tr of trList) {
|
|
516
|
+
const height = Number(tr["@_h"] ?? 0);
|
|
517
|
+
const cells = parseCells(tr.tc, colorResolver);
|
|
518
|
+
rows.push({ height, cells });
|
|
519
|
+
}
|
|
520
|
+
return rows;
|
|
521
|
+
}
|
|
522
|
+
function parseCells(tcList, colorResolver) {
|
|
523
|
+
if (!tcList) return [];
|
|
524
|
+
const cells = [];
|
|
525
|
+
for (const tc of tcList) {
|
|
526
|
+
const textBody = parseTextBody(tc.txBody, colorResolver);
|
|
527
|
+
const tcPr = tc.tcPr;
|
|
528
|
+
const fill = tcPr ? parseFillFromNode(tcPr, colorResolver) : null;
|
|
529
|
+
const borders = tcPr ? parseCellBorders(tcPr, colorResolver) : null;
|
|
530
|
+
const gridSpan = Number(tc["@_gridSpan"] ?? 1);
|
|
531
|
+
const rowSpan = Number(tc["@_rowSpan"] ?? 1);
|
|
532
|
+
const hMerge = tcPr?.["@_hMerge"] === "1" || tcPr?.["@_hMerge"] === "true";
|
|
533
|
+
const vMerge = tcPr?.["@_vMerge"] === "1" || tcPr?.["@_vMerge"] === "true";
|
|
534
|
+
cells.push({ textBody, fill, borders, gridSpan, rowSpan, hMerge, vMerge });
|
|
535
|
+
}
|
|
536
|
+
return cells;
|
|
537
|
+
}
|
|
538
|
+
function parseCellBorders(tcPr, colorResolver) {
|
|
539
|
+
const top = parseOutline(tcPr.lnT, colorResolver);
|
|
540
|
+
const bottom = parseOutline(tcPr.lnB, colorResolver);
|
|
541
|
+
const left = parseOutline(tcPr.lnL, colorResolver);
|
|
542
|
+
const right = parseOutline(tcPr.lnR, colorResolver);
|
|
543
|
+
if (!top && !bottom && !left && !right) return null;
|
|
544
|
+
return { top, bottom, left, right };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/utils/constants.ts
|
|
548
|
+
var EMU_PER_INCH = 914400;
|
|
549
|
+
var DEFAULT_DPI = 96;
|
|
550
|
+
var DEFAULT_OUTPUT_WIDTH = 960;
|
|
551
|
+
|
|
552
|
+
// src/utils/emu.ts
|
|
553
|
+
function emuToPixels(emu, dpi = DEFAULT_DPI) {
|
|
554
|
+
return emu / EMU_PER_INCH * dpi;
|
|
555
|
+
}
|
|
556
|
+
function hundredthPointToPoint(value) {
|
|
557
|
+
return value / 100;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/parser/slide-parser.ts
|
|
561
|
+
var WARN_PREFIX5 = "[pptx-glimpse]";
|
|
562
|
+
function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver) {
|
|
563
|
+
const parsed = parseXml(slideXml);
|
|
564
|
+
const sld = parsed.sld;
|
|
565
|
+
if (!sld) {
|
|
566
|
+
console.warn(`${WARN_PREFIX5} Slide ${slideNumber}: missing root element "sld" in XML`);
|
|
567
|
+
}
|
|
568
|
+
const relsPath = slidePath.replace("ppt/slides/", "ppt/slides/_rels/") + ".rels";
|
|
569
|
+
const relsXml = archive.files.get(relsPath);
|
|
570
|
+
const rels = relsXml ? parseRelationships(relsXml) : /* @__PURE__ */ new Map();
|
|
571
|
+
const fillContext = { rels, archive, basePath: slidePath };
|
|
572
|
+
const background = parseBackground(sld?.cSld?.bg, colorResolver, fillContext);
|
|
573
|
+
const elements = parseShapeTree(
|
|
574
|
+
sld?.cSld?.spTree,
|
|
575
|
+
rels,
|
|
576
|
+
slidePath,
|
|
577
|
+
archive,
|
|
578
|
+
colorResolver,
|
|
579
|
+
`Slide ${slideNumber}`
|
|
580
|
+
);
|
|
581
|
+
return { slideNumber, background, elements };
|
|
582
|
+
}
|
|
583
|
+
function parseBackground(bgNode, colorResolver, context) {
|
|
584
|
+
if (!bgNode) return null;
|
|
585
|
+
const bgPr = bgNode.bgPr;
|
|
586
|
+
if (!bgPr) return null;
|
|
587
|
+
const fill = parseFillFromNode(bgPr, colorResolver, context);
|
|
588
|
+
return { fill };
|
|
589
|
+
}
|
|
590
|
+
function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context) {
|
|
591
|
+
if (!spTree) return [];
|
|
592
|
+
const ctx = context ?? slidePath;
|
|
593
|
+
const elements = [];
|
|
594
|
+
const shapes = spTree.sp ?? [];
|
|
595
|
+
for (const sp of shapes) {
|
|
596
|
+
const shape = parseShape(sp, colorResolver);
|
|
597
|
+
if (shape) {
|
|
598
|
+
elements.push(shape);
|
|
599
|
+
} else {
|
|
600
|
+
console.warn(`${WARN_PREFIX5} ${ctx}: shape skipped (parse returned null)`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const pics = spTree.pic ?? [];
|
|
604
|
+
for (const pic of pics) {
|
|
605
|
+
const img = parseImage(pic, rels, slidePath, archive, colorResolver);
|
|
606
|
+
if (img) {
|
|
607
|
+
elements.push(img);
|
|
608
|
+
} else {
|
|
609
|
+
console.warn(`${WARN_PREFIX5} ${ctx}: image skipped (parse returned null)`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const cxnSps = spTree.cxnSp ?? [];
|
|
613
|
+
for (const cxn of cxnSps) {
|
|
614
|
+
const connector = parseConnector(cxn, colorResolver);
|
|
615
|
+
if (connector) {
|
|
616
|
+
elements.push(connector);
|
|
617
|
+
} else {
|
|
618
|
+
console.warn(`${WARN_PREFIX5} ${ctx}: connector skipped (parse returned null)`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const grpSps = spTree.grpSp ?? [];
|
|
622
|
+
for (const grp of grpSps) {
|
|
623
|
+
const group = parseGroup(grp, rels, slidePath, archive, colorResolver);
|
|
624
|
+
if (group) {
|
|
625
|
+
elements.push(group);
|
|
626
|
+
} else {
|
|
627
|
+
console.warn(`${WARN_PREFIX5} ${ctx}: group skipped (parse returned null)`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const graphicFrames = spTree.graphicFrame ?? [];
|
|
631
|
+
for (const gf of graphicFrames) {
|
|
632
|
+
const chart = parseGraphicFrame(gf, rels, slidePath, archive, colorResolver);
|
|
633
|
+
if (chart) {
|
|
634
|
+
elements.push(chart);
|
|
635
|
+
} else {
|
|
636
|
+
console.warn(`${WARN_PREFIX5} ${ctx}: graphicFrame skipped (parse returned null)`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return elements;
|
|
640
|
+
}
|
|
641
|
+
function parseShape(sp, colorResolver) {
|
|
642
|
+
const spPr = sp.spPr;
|
|
643
|
+
if (!spPr) return null;
|
|
644
|
+
const transform = parseTransform(spPr.xfrm);
|
|
645
|
+
if (!transform) return null;
|
|
646
|
+
const geometry = parseGeometry(spPr);
|
|
647
|
+
const fill = parseFillFromNode(spPr, colorResolver);
|
|
648
|
+
const outline = parseOutline(spPr.ln, colorResolver);
|
|
649
|
+
const textBody = parseTextBody(sp.txBody, colorResolver);
|
|
650
|
+
const effects = parseEffectList(spPr.effectLst, colorResolver);
|
|
651
|
+
const ph = sp.nvSpPr?.nvPr?.ph;
|
|
652
|
+
const placeholderType = ph ? ph["@_type"] ?? "body" : void 0;
|
|
653
|
+
const placeholderIdx = ph?.["@_idx"] !== void 0 ? Number(ph["@_idx"]) : void 0;
|
|
654
|
+
return {
|
|
655
|
+
type: "shape",
|
|
656
|
+
transform,
|
|
657
|
+
geometry,
|
|
658
|
+
fill,
|
|
659
|
+
outline,
|
|
660
|
+
textBody,
|
|
661
|
+
effects,
|
|
662
|
+
...placeholderType !== void 0 && { placeholderType },
|
|
663
|
+
...placeholderIdx !== void 0 && { placeholderIdx }
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function parseImage(pic, rels, slidePath, archive, colorResolver) {
|
|
667
|
+
const spPr = pic.spPr;
|
|
668
|
+
if (!spPr) return null;
|
|
669
|
+
const transform = parseTransform(spPr.xfrm);
|
|
670
|
+
if (!transform) return null;
|
|
671
|
+
const blipFill = pic.blipFill;
|
|
672
|
+
const rId = blipFill?.blip?.["@_r:embed"] ?? blipFill?.blip?.["@_embed"];
|
|
673
|
+
if (!rId) return null;
|
|
674
|
+
const rel = rels.get(rId);
|
|
675
|
+
if (!rel) return null;
|
|
676
|
+
const mediaPath = resolveRelationshipTarget(slidePath, rel.target);
|
|
677
|
+
const mediaData = archive.media.get(mediaPath);
|
|
678
|
+
if (!mediaData) return null;
|
|
679
|
+
const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
|
|
680
|
+
const mimeMap = {
|
|
681
|
+
png: "image/png",
|
|
682
|
+
jpg: "image/jpeg",
|
|
683
|
+
jpeg: "image/jpeg",
|
|
684
|
+
gif: "image/gif",
|
|
685
|
+
svg: "image/svg+xml",
|
|
686
|
+
emf: "image/emf",
|
|
687
|
+
wmf: "image/wmf"
|
|
688
|
+
};
|
|
689
|
+
const mimeType = mimeMap[ext] ?? "image/png";
|
|
690
|
+
const imageData = mediaData.toString("base64");
|
|
691
|
+
const effects = parseEffectList(spPr.effectLst, colorResolver);
|
|
692
|
+
return {
|
|
693
|
+
type: "image",
|
|
694
|
+
transform,
|
|
695
|
+
imageData,
|
|
696
|
+
mimeType,
|
|
697
|
+
effects
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function parseConnector(cxn, colorResolver) {
|
|
701
|
+
const spPr = cxn.spPr;
|
|
702
|
+
if (!spPr) return null;
|
|
703
|
+
const transform = parseTransform(spPr.xfrm);
|
|
704
|
+
if (!transform) return null;
|
|
705
|
+
const outline = parseOutline(spPr.ln, colorResolver);
|
|
706
|
+
const effects = parseEffectList(spPr.effectLst, colorResolver);
|
|
707
|
+
return { type: "connector", transform, outline, effects };
|
|
708
|
+
}
|
|
709
|
+
function parseGroup(grp, rels, slidePath, archive, colorResolver) {
|
|
710
|
+
const grpSpPr = grp.grpSpPr;
|
|
711
|
+
if (!grpSpPr) return null;
|
|
712
|
+
const transform = parseTransform(grpSpPr.xfrm);
|
|
713
|
+
if (!transform) return null;
|
|
714
|
+
const childOff = grpSpPr.xfrm?.chOff;
|
|
715
|
+
const childExt = grpSpPr.xfrm?.chExt;
|
|
716
|
+
const childTransform = {
|
|
717
|
+
offsetX: Number(childOff?.["@_x"] ?? 0),
|
|
718
|
+
offsetY: Number(childOff?.["@_y"] ?? 0),
|
|
719
|
+
extentWidth: Number(childExt?.["@_cx"] ?? transform.extentWidth),
|
|
720
|
+
extentHeight: Number(childExt?.["@_cy"] ?? transform.extentHeight),
|
|
721
|
+
rotation: 0,
|
|
722
|
+
flipH: false,
|
|
723
|
+
flipV: false
|
|
724
|
+
};
|
|
725
|
+
const children = parseShapeTree(grp, rels, slidePath, archive, colorResolver);
|
|
726
|
+
const effects = parseEffectList(grpSpPr.effectLst, colorResolver);
|
|
727
|
+
return { type: "group", transform, childTransform, children, effects };
|
|
728
|
+
}
|
|
729
|
+
function parseGraphicFrame(gf, rels, slidePath, archive, colorResolver) {
|
|
730
|
+
const xfrm = gf.xfrm;
|
|
731
|
+
const transform = parseTransform(xfrm);
|
|
732
|
+
if (!transform) return null;
|
|
733
|
+
const graphicData = gf.graphic?.graphicData;
|
|
734
|
+
if (!graphicData) return null;
|
|
735
|
+
const chartRef = graphicData.chart;
|
|
736
|
+
if (chartRef) {
|
|
737
|
+
const rId = chartRef["@_r:id"] ?? chartRef["@_id"];
|
|
738
|
+
if (!rId) return null;
|
|
739
|
+
const rel = rels.get(rId);
|
|
740
|
+
if (!rel) return null;
|
|
741
|
+
const chartPath = resolveRelationshipTarget(slidePath, rel.target);
|
|
742
|
+
const chartXml = archive.files.get(chartPath);
|
|
743
|
+
if (!chartXml) return null;
|
|
744
|
+
const chartData = parseChart(chartXml, colorResolver);
|
|
745
|
+
if (!chartData) return null;
|
|
746
|
+
return { type: "chart", transform, chart: chartData };
|
|
747
|
+
}
|
|
748
|
+
const tblNode = graphicData.tbl;
|
|
749
|
+
if (tblNode) {
|
|
750
|
+
const tableData = parseTable(tblNode, colorResolver);
|
|
751
|
+
if (!tableData) return null;
|
|
752
|
+
return { type: "table", transform, table: tableData };
|
|
753
|
+
}
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
function parseTransform(xfrm) {
|
|
757
|
+
if (!xfrm) return null;
|
|
758
|
+
const off = xfrm.off;
|
|
759
|
+
const ext = xfrm.ext;
|
|
760
|
+
if (!off || !ext) return null;
|
|
761
|
+
let offsetX = Number(off["@_x"] ?? 0);
|
|
762
|
+
let offsetY = Number(off["@_y"] ?? 0);
|
|
763
|
+
let extentWidth = Number(ext["@_cx"] ?? 0);
|
|
764
|
+
let extentHeight = Number(ext["@_cy"] ?? 0);
|
|
765
|
+
let rotation = Number(xfrm["@_rot"] ?? 0);
|
|
766
|
+
if (Number.isNaN(offsetX)) {
|
|
767
|
+
console.warn(`${WARN_PREFIX5} NaN detected in transform offsetX, defaulting to 0`);
|
|
768
|
+
offsetX = 0;
|
|
769
|
+
}
|
|
770
|
+
if (Number.isNaN(offsetY)) {
|
|
771
|
+
console.warn(`${WARN_PREFIX5} NaN detected in transform offsetY, defaulting to 0`);
|
|
772
|
+
offsetY = 0;
|
|
773
|
+
}
|
|
774
|
+
if (Number.isNaN(extentWidth)) {
|
|
775
|
+
console.warn(`${WARN_PREFIX5} NaN detected in transform extentWidth, defaulting to 0`);
|
|
776
|
+
extentWidth = 0;
|
|
777
|
+
}
|
|
778
|
+
if (Number.isNaN(extentHeight)) {
|
|
779
|
+
console.warn(`${WARN_PREFIX5} NaN detected in transform extentHeight, defaulting to 0`);
|
|
780
|
+
extentHeight = 0;
|
|
781
|
+
}
|
|
782
|
+
if (Number.isNaN(rotation)) {
|
|
783
|
+
console.warn(`${WARN_PREFIX5} NaN detected in transform rotation, defaulting to 0`);
|
|
784
|
+
rotation = 0;
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
offsetX,
|
|
788
|
+
offsetY,
|
|
789
|
+
extentWidth,
|
|
790
|
+
extentHeight,
|
|
791
|
+
rotation: rotation / 6e4,
|
|
792
|
+
flipH: xfrm["@_flipH"] === "1" || xfrm["@_flipH"] === "true",
|
|
793
|
+
flipV: xfrm["@_flipV"] === "1" || xfrm["@_flipV"] === "true"
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function parseGeometry(spPr) {
|
|
797
|
+
if (spPr.prstGeom) {
|
|
798
|
+
const preset = spPr.prstGeom["@_prst"] ?? "rect";
|
|
799
|
+
const avLst = spPr.prstGeom.avLst;
|
|
800
|
+
const adjustValues = {};
|
|
801
|
+
if (avLst?.gd) {
|
|
802
|
+
const guides = Array.isArray(avLst.gd) ? avLst.gd : [avLst.gd];
|
|
803
|
+
for (const gd of guides) {
|
|
804
|
+
const name = gd["@_name"];
|
|
805
|
+
const fmla = gd["@_fmla"];
|
|
806
|
+
const match = fmla?.match(/val\s+(\d+)/);
|
|
807
|
+
if (name && match) {
|
|
808
|
+
adjustValues[name] = Number(match[1]);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return { type: "preset", preset, adjustValues };
|
|
813
|
+
}
|
|
814
|
+
if (spPr.custGeom) {
|
|
815
|
+
const pathData = parseCustomGeometryPaths(spPr.custGeom);
|
|
816
|
+
if (pathData) {
|
|
817
|
+
return { type: "custom", pathData };
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return { type: "preset", preset: "rect", adjustValues: {} };
|
|
821
|
+
}
|
|
822
|
+
function parseCustomGeometryPaths(custGeom) {
|
|
823
|
+
const pathLst = custGeom.pathLst;
|
|
824
|
+
if (!pathLst?.path) return null;
|
|
825
|
+
const paths = Array.isArray(pathLst.path) ? pathLst.path : [pathLst.path];
|
|
826
|
+
const svgParts = [];
|
|
827
|
+
for (const path of paths) {
|
|
828
|
+
const w = Number(path["@_w"] ?? 0);
|
|
829
|
+
const h = Number(path["@_h"] ?? 0);
|
|
830
|
+
if (w === 0 && h === 0) continue;
|
|
831
|
+
const processCommands = (commands, prefix) => {
|
|
832
|
+
if (!commands) return;
|
|
833
|
+
const list = Array.isArray(commands) ? commands : [commands];
|
|
834
|
+
for (const cmd of list) {
|
|
835
|
+
if (prefix === "M" || prefix === "L") {
|
|
836
|
+
const pt = cmd.pt;
|
|
837
|
+
if (pt) {
|
|
838
|
+
const pts = Array.isArray(pt) ? pt : [pt];
|
|
839
|
+
svgParts.push(
|
|
840
|
+
`${prefix} ${pts.map((p) => `${p["@_x"]} ${p["@_y"]}`).join(" ")}`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
if (path.moveTo) processCommands(Array.isArray(path.moveTo) ? path.moveTo : [path.moveTo], "M");
|
|
847
|
+
if (path.lnTo) processCommands(Array.isArray(path.lnTo) ? path.lnTo : [path.lnTo], "L");
|
|
848
|
+
if (path.cubicBezTo) {
|
|
849
|
+
const bezList = Array.isArray(path.cubicBezTo) ? path.cubicBezTo : [path.cubicBezTo];
|
|
850
|
+
for (const bez of bezList) {
|
|
851
|
+
const pts = Array.isArray(bez.pt) ? bez.pt : [bez.pt];
|
|
852
|
+
if (pts.length >= 3) {
|
|
853
|
+
svgParts.push(
|
|
854
|
+
`C ${pts.map((p) => `${p["@_x"]} ${p["@_y"]}`).join(", ")}`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (path.close !== void 0) {
|
|
860
|
+
svgParts.push("Z");
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return svgParts.length > 0 ? svgParts.join(" ") : null;
|
|
864
|
+
}
|
|
865
|
+
function parseTextBody(txBody, colorResolver) {
|
|
866
|
+
if (!txBody) return null;
|
|
867
|
+
const bodyPr = txBody.bodyPr;
|
|
868
|
+
let autoFit = "noAutofit";
|
|
869
|
+
let fontScale = 1;
|
|
870
|
+
let lnSpcReduction = 0;
|
|
871
|
+
if (bodyPr?.normAutofit !== void 0) {
|
|
872
|
+
autoFit = "normAutofit";
|
|
873
|
+
const normAutofit = bodyPr.normAutofit;
|
|
874
|
+
if (typeof normAutofit === "object" && normAutofit !== null) {
|
|
875
|
+
fontScale = normAutofit["@_fontScale"] ? Number(normAutofit["@_fontScale"]) / 1e5 : 1;
|
|
876
|
+
lnSpcReduction = normAutofit["@_lnSpcReduction"] ? Number(normAutofit["@_lnSpcReduction"]) / 1e5 : 0;
|
|
877
|
+
}
|
|
878
|
+
} else if (bodyPr?.spAutoFit !== void 0) {
|
|
879
|
+
autoFit = "spAutofit";
|
|
880
|
+
}
|
|
881
|
+
const bodyProperties = {
|
|
882
|
+
anchor: bodyPr?.["@_anchor"] ?? "t",
|
|
883
|
+
marginLeft: Number(bodyPr?.["@_lIns"] ?? 91440),
|
|
884
|
+
marginRight: Number(bodyPr?.["@_rIns"] ?? 91440),
|
|
885
|
+
marginTop: Number(bodyPr?.["@_tIns"] ?? 45720),
|
|
886
|
+
marginBottom: Number(bodyPr?.["@_bIns"] ?? 45720),
|
|
887
|
+
wrap: bodyPr?.["@_wrap"] ?? "square",
|
|
888
|
+
autoFit,
|
|
889
|
+
fontScale,
|
|
890
|
+
lnSpcReduction
|
|
891
|
+
};
|
|
892
|
+
const paragraphs = [];
|
|
893
|
+
const pList = txBody.p ?? [];
|
|
894
|
+
for (const p of pList) {
|
|
895
|
+
paragraphs.push(parseParagraph(p, colorResolver));
|
|
896
|
+
}
|
|
897
|
+
if (paragraphs.length === 0) return null;
|
|
898
|
+
return { paragraphs, bodyProperties };
|
|
899
|
+
}
|
|
900
|
+
var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
|
|
901
|
+
"arabicPeriod",
|
|
902
|
+
"arabicParenR",
|
|
903
|
+
"romanUcPeriod",
|
|
904
|
+
"romanLcPeriod",
|
|
905
|
+
"alphaUcPeriod",
|
|
906
|
+
"alphaLcPeriod",
|
|
907
|
+
"alphaLcParenR",
|
|
908
|
+
"alphaUcParenR",
|
|
909
|
+
"arabicPlain"
|
|
910
|
+
]);
|
|
911
|
+
function parseBullet(pPr, colorResolver) {
|
|
912
|
+
let bullet = null;
|
|
913
|
+
let bulletFont = null;
|
|
914
|
+
let bulletColor = colorResolver.resolve(pPr?.buClr);
|
|
915
|
+
if (!pPr?.buClr) bulletColor = null;
|
|
916
|
+
const bulletSizePct = pPr?.buSzPct ? Number(pPr.buSzPct["@_val"]) : null;
|
|
917
|
+
if (pPr?.buNone !== void 0) {
|
|
918
|
+
bullet = { type: "none" };
|
|
919
|
+
} else if (pPr?.buChar) {
|
|
920
|
+
bullet = { type: "char", char: pPr.buChar["@_char"] ?? "\u2022" };
|
|
921
|
+
} else if (pPr?.buAutoNum) {
|
|
922
|
+
const scheme = pPr.buAutoNum["@_type"] ?? "arabicPeriod";
|
|
923
|
+
bullet = {
|
|
924
|
+
type: "autoNum",
|
|
925
|
+
scheme: VALID_AUTO_NUM_SCHEMES.has(scheme) ? scheme : "arabicPeriod",
|
|
926
|
+
startAt: Number(pPr.buAutoNum["@_startAt"] ?? 1)
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
if (pPr?.buFont) {
|
|
930
|
+
bulletFont = pPr.buFont["@_typeface"] ?? null;
|
|
931
|
+
}
|
|
932
|
+
return { bullet, bulletFont, bulletColor, bulletSizePct };
|
|
933
|
+
}
|
|
934
|
+
function parseParagraph(p, colorResolver) {
|
|
935
|
+
const pPr = p.pPr;
|
|
936
|
+
const { bullet, bulletFont, bulletColor, bulletSizePct } = parseBullet(pPr, colorResolver);
|
|
937
|
+
const properties = {
|
|
938
|
+
alignment: pPr?.["@_algn"] ?? "l",
|
|
939
|
+
lineSpacing: pPr?.lnSpc?.spcPct ? Number(pPr.lnSpc.spcPct["@_val"]) : null,
|
|
940
|
+
spaceBefore: pPr?.spcBef?.spcPts ? Number(pPr.spcBef.spcPts["@_val"]) : 0,
|
|
941
|
+
spaceAfter: pPr?.spcAft?.spcPts ? Number(pPr.spcAft.spcPts["@_val"]) : 0,
|
|
942
|
+
level: Number(pPr?.["@_lvl"] ?? 0),
|
|
943
|
+
bullet,
|
|
944
|
+
bulletFont,
|
|
945
|
+
bulletColor,
|
|
946
|
+
bulletSizePct,
|
|
947
|
+
marginLeft: Number(pPr?.["@_marL"] ?? 0),
|
|
948
|
+
indent: Number(pPr?.["@_indent"] ?? 0)
|
|
949
|
+
};
|
|
950
|
+
const runs = [];
|
|
951
|
+
const rList = p.r ?? [];
|
|
952
|
+
for (const r of rList) {
|
|
953
|
+
const text = r.t ?? "";
|
|
954
|
+
const textContent = typeof text === "object" ? text["#text"] ?? "" : String(text);
|
|
955
|
+
const rPr = r.rPr;
|
|
956
|
+
const runProps = parseRunProperties(rPr, colorResolver);
|
|
957
|
+
runs.push({ text: textContent, properties: runProps });
|
|
958
|
+
}
|
|
959
|
+
return { runs, properties };
|
|
960
|
+
}
|
|
961
|
+
function parseRunProperties(rPr, colorResolver) {
|
|
962
|
+
if (!rPr) {
|
|
963
|
+
return {
|
|
964
|
+
fontSize: null,
|
|
965
|
+
fontFamily: null,
|
|
966
|
+
fontFamilyEa: null,
|
|
967
|
+
bold: false,
|
|
968
|
+
italic: false,
|
|
969
|
+
underline: false,
|
|
970
|
+
strikethrough: false,
|
|
971
|
+
color: null,
|
|
972
|
+
baseline: 0
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const fontSize = rPr["@_sz"] ? hundredthPointToPoint(Number(rPr["@_sz"])) : null;
|
|
976
|
+
const fontFamily = rPr.latin?.["@_typeface"] ?? null;
|
|
977
|
+
const fontFamilyEa = rPr.ea?.["@_typeface"] ?? null;
|
|
978
|
+
const bold = rPr["@_b"] === "1" || rPr["@_b"] === "true";
|
|
979
|
+
const italic = rPr["@_i"] === "1" || rPr["@_i"] === "true";
|
|
980
|
+
const underline = rPr["@_u"] !== void 0 && rPr["@_u"] !== "none";
|
|
981
|
+
const strikethrough = rPr["@_strike"] !== void 0 && rPr["@_strike"] !== "noStrike";
|
|
982
|
+
const baseline = rPr["@_baseline"] ? Number(rPr["@_baseline"]) / 1e3 : 0;
|
|
983
|
+
let color = colorResolver.resolve(rPr.solidFill ?? rPr);
|
|
984
|
+
if (!rPr.solidFill && !rPr.srgbClr && !rPr.schemeClr && !rPr.sysClr) {
|
|
985
|
+
color = null;
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
fontSize,
|
|
989
|
+
fontFamily,
|
|
990
|
+
fontFamilyEa,
|
|
991
|
+
bold,
|
|
992
|
+
italic,
|
|
993
|
+
underline,
|
|
994
|
+
strikethrough,
|
|
995
|
+
color,
|
|
996
|
+
baseline
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// src/parser/slide-master-parser.ts
|
|
1001
|
+
var WARN_PREFIX6 = "[pptx-glimpse]";
|
|
1002
|
+
var DEFAULT_COLOR_MAP = {
|
|
1003
|
+
bg1: "lt1",
|
|
1004
|
+
tx1: "dk1",
|
|
1005
|
+
bg2: "lt2",
|
|
1006
|
+
tx2: "dk2",
|
|
1007
|
+
accent1: "accent1",
|
|
1008
|
+
accent2: "accent2",
|
|
1009
|
+
accent3: "accent3",
|
|
1010
|
+
accent4: "accent4",
|
|
1011
|
+
accent5: "accent5",
|
|
1012
|
+
accent6: "accent6",
|
|
1013
|
+
hlink: "hlink",
|
|
1014
|
+
folHlink: "folHlink"
|
|
1015
|
+
};
|
|
1016
|
+
function parseSlideMasterColorMap(xml) {
|
|
1017
|
+
const parsed = parseXml(xml);
|
|
1018
|
+
if (!parsed.sldMaster) {
|
|
1019
|
+
console.warn(`${WARN_PREFIX6} SlideMaster: missing root element "sldMaster" in XML`);
|
|
1020
|
+
return { ...DEFAULT_COLOR_MAP };
|
|
1021
|
+
}
|
|
1022
|
+
const clrMap = parsed.sldMaster.clrMap;
|
|
1023
|
+
if (!clrMap) return { ...DEFAULT_COLOR_MAP };
|
|
1024
|
+
const result = {};
|
|
1025
|
+
for (const key of Object.keys(DEFAULT_COLOR_MAP)) {
|
|
1026
|
+
const val = clrMap[`@_${key}`];
|
|
1027
|
+
result[key] = val ?? DEFAULT_COLOR_MAP[key];
|
|
1028
|
+
}
|
|
1029
|
+
return result;
|
|
1030
|
+
}
|
|
1031
|
+
function parseSlideMasterBackground(xml, colorResolver, context) {
|
|
1032
|
+
const parsed = parseXml(xml);
|
|
1033
|
+
if (!parsed.sldMaster) {
|
|
1034
|
+
console.warn(`${WARN_PREFIX6} SlideMaster: missing root element "sldMaster" in XML`);
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
const bg = parsed.sldMaster.cSld?.bg;
|
|
1038
|
+
if (!bg) return null;
|
|
1039
|
+
const bgPr = bg.bgPr;
|
|
1040
|
+
if (!bgPr) return null;
|
|
1041
|
+
const fill = parseFillFromNode(bgPr, colorResolver, context);
|
|
1042
|
+
return { fill };
|
|
1043
|
+
}
|
|
1044
|
+
function parseSlideMasterElements(xml, masterPath, archive, colorResolver) {
|
|
1045
|
+
const parsed = parseXml(xml);
|
|
1046
|
+
if (!parsed.sldMaster) {
|
|
1047
|
+
console.warn(`${WARN_PREFIX6} SlideMaster: missing root element "sldMaster" in XML`);
|
|
1048
|
+
return [];
|
|
1049
|
+
}
|
|
1050
|
+
const spTree = parsed.sldMaster.cSld?.spTree;
|
|
1051
|
+
if (!spTree) return [];
|
|
1052
|
+
const relsPath = buildRelsPath(masterPath);
|
|
1053
|
+
const relsXml = archive.files.get(relsPath);
|
|
1054
|
+
const rels = relsXml ? parseRelationships(relsXml) : /* @__PURE__ */ new Map();
|
|
1055
|
+
return parseShapeTree(spTree, rels, masterPath, archive, colorResolver);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/parser/slide-layout-parser.ts
|
|
1059
|
+
var WARN_PREFIX7 = "[pptx-glimpse]";
|
|
1060
|
+
function parseSlideLayoutBackground(xml, colorResolver, context) {
|
|
1061
|
+
const parsed = parseXml(xml);
|
|
1062
|
+
if (!parsed.sldLayout) {
|
|
1063
|
+
console.warn(`${WARN_PREFIX7} SlideLayout: missing root element "sldLayout" in XML`);
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
const bg = parsed.sldLayout.cSld?.bg;
|
|
1067
|
+
if (!bg) return null;
|
|
1068
|
+
const bgPr = bg.bgPr;
|
|
1069
|
+
if (!bgPr) return null;
|
|
1070
|
+
const fill = parseFillFromNode(bgPr, colorResolver, context);
|
|
1071
|
+
return { fill };
|
|
1072
|
+
}
|
|
1073
|
+
function parseSlideLayoutElements(xml, layoutPath, archive, colorResolver) {
|
|
1074
|
+
const parsed = parseXml(xml);
|
|
1075
|
+
if (!parsed.sldLayout) {
|
|
1076
|
+
console.warn(`${WARN_PREFIX7} SlideLayout: missing root element "sldLayout" in XML`);
|
|
1077
|
+
return [];
|
|
1078
|
+
}
|
|
1079
|
+
const spTree = parsed.sldLayout.cSld?.spTree;
|
|
1080
|
+
if (!spTree) return [];
|
|
1081
|
+
const relsPath = buildRelsPath(layoutPath);
|
|
1082
|
+
const relsXml = archive.files.get(relsPath);
|
|
1083
|
+
const rels = relsXml ? parseRelationships(relsXml) : /* @__PURE__ */ new Map();
|
|
1084
|
+
return parseShapeTree(spTree, rels, layoutPath, archive, colorResolver);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/color/color-transforms.ts
|
|
1088
|
+
function applyColorTransforms(color, node) {
|
|
1089
|
+
let { hex, alpha } = color;
|
|
1090
|
+
if (node.lumMod || node.lumOff) {
|
|
1091
|
+
hex = applyLuminance(hex, node.lumMod?.["@_val"], node.lumOff?.["@_val"]);
|
|
1092
|
+
}
|
|
1093
|
+
if (node.tint) {
|
|
1094
|
+
hex = applyTint(hex, Number(node.tint["@_val"]) / 1e5);
|
|
1095
|
+
}
|
|
1096
|
+
if (node.shade) {
|
|
1097
|
+
hex = applyShade(hex, Number(node.shade["@_val"]) / 1e5);
|
|
1098
|
+
}
|
|
1099
|
+
if (node.alpha) {
|
|
1100
|
+
alpha = Number(node.alpha["@_val"]) / 1e5;
|
|
1101
|
+
}
|
|
1102
|
+
return { hex, alpha };
|
|
1103
|
+
}
|
|
1104
|
+
function applyLuminance(hex, lumModVal, lumOffVal) {
|
|
1105
|
+
const { h, s, l } = hexToHsl(hex);
|
|
1106
|
+
const lumMod = lumModVal ? Number(lumModVal) / 1e5 : 1;
|
|
1107
|
+
const lumOff = lumOffVal ? Number(lumOffVal) / 1e5 : 0;
|
|
1108
|
+
const newL = Math.min(1, Math.max(0, l * lumMod + lumOff));
|
|
1109
|
+
return hslToHex(h, s, newL);
|
|
1110
|
+
}
|
|
1111
|
+
function applyTint(hex, tintAmount) {
|
|
1112
|
+
const { r, g, b } = hexToRgb(hex);
|
|
1113
|
+
const newR = Math.round(r + (255 - r) * tintAmount);
|
|
1114
|
+
const newG = Math.round(g + (255 - g) * tintAmount);
|
|
1115
|
+
const newB = Math.round(b + (255 - b) * tintAmount);
|
|
1116
|
+
return rgbToHex(newR, newG, newB);
|
|
1117
|
+
}
|
|
1118
|
+
function applyShade(hex, shadeAmount) {
|
|
1119
|
+
const { r, g, b } = hexToRgb(hex);
|
|
1120
|
+
const newR = Math.round(r * shadeAmount);
|
|
1121
|
+
const newG = Math.round(g * shadeAmount);
|
|
1122
|
+
const newB = Math.round(b * shadeAmount);
|
|
1123
|
+
return rgbToHex(newR, newG, newB);
|
|
1124
|
+
}
|
|
1125
|
+
function hexToRgb(hex) {
|
|
1126
|
+
const h = hex.replace("#", "");
|
|
1127
|
+
return {
|
|
1128
|
+
r: parseInt(h.substring(0, 2), 16),
|
|
1129
|
+
g: parseInt(h.substring(2, 4), 16),
|
|
1130
|
+
b: parseInt(h.substring(4, 6), 16)
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
function rgbToHex(r, g, b) {
|
|
1134
|
+
const toHex = (n) => Math.min(255, Math.max(0, n)).toString(16).padStart(2, "0");
|
|
1135
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
1136
|
+
}
|
|
1137
|
+
function hexToHsl(hex) {
|
|
1138
|
+
const { r: r255, g: g255, b: b255 } = hexToRgb(hex);
|
|
1139
|
+
const r = r255 / 255;
|
|
1140
|
+
const g = g255 / 255;
|
|
1141
|
+
const b = b255 / 255;
|
|
1142
|
+
const max = Math.max(r, g, b);
|
|
1143
|
+
const min = Math.min(r, g, b);
|
|
1144
|
+
const l = (max + min) / 2;
|
|
1145
|
+
if (max === min) {
|
|
1146
|
+
return { h: 0, s: 0, l };
|
|
1147
|
+
}
|
|
1148
|
+
const d = max - min;
|
|
1149
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
1150
|
+
let h;
|
|
1151
|
+
if (max === r) {
|
|
1152
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
1153
|
+
} else if (max === g) {
|
|
1154
|
+
h = ((b - r) / d + 2) / 6;
|
|
1155
|
+
} else {
|
|
1156
|
+
h = ((r - g) / d + 4) / 6;
|
|
1157
|
+
}
|
|
1158
|
+
return { h, s, l };
|
|
1159
|
+
}
|
|
1160
|
+
function hslToHex(h, s, l) {
|
|
1161
|
+
if (s === 0) {
|
|
1162
|
+
const v = Math.round(l * 255);
|
|
1163
|
+
return rgbToHex(v, v, v);
|
|
1164
|
+
}
|
|
1165
|
+
const hue2rgb = (p2, q2, t) => {
|
|
1166
|
+
let tt = t;
|
|
1167
|
+
if (tt < 0) tt += 1;
|
|
1168
|
+
if (tt > 1) tt -= 1;
|
|
1169
|
+
if (tt < 1 / 6) return p2 + (q2 - p2) * 6 * tt;
|
|
1170
|
+
if (tt < 1 / 2) return q2;
|
|
1171
|
+
if (tt < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - tt) * 6;
|
|
1172
|
+
return p2;
|
|
1173
|
+
};
|
|
1174
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
1175
|
+
const p = 2 * l - q;
|
|
1176
|
+
const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
|
|
1177
|
+
const g = Math.round(hue2rgb(p, q, h) * 255);
|
|
1178
|
+
const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
|
|
1179
|
+
return rgbToHex(r, g, b);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/color/color-resolver.ts
|
|
1183
|
+
var WARN_PREFIX8 = "[pptx-glimpse]";
|
|
1184
|
+
var ColorResolver = class {
|
|
1185
|
+
constructor(colorScheme, colorMap) {
|
|
1186
|
+
this.colorScheme = colorScheme;
|
|
1187
|
+
this.colorMap = colorMap;
|
|
1188
|
+
}
|
|
1189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1190
|
+
resolve(colorNode) {
|
|
1191
|
+
if (!colorNode) return null;
|
|
1192
|
+
if (colorNode.srgbClr) {
|
|
1193
|
+
return this.resolveSrgbClr(colorNode.srgbClr);
|
|
1194
|
+
}
|
|
1195
|
+
if (colorNode.schemeClr) {
|
|
1196
|
+
return this.resolveSchemeClr(colorNode.schemeClr);
|
|
1197
|
+
}
|
|
1198
|
+
if (colorNode.sysClr) {
|
|
1199
|
+
return this.resolveSysClr(colorNode.sysClr);
|
|
1200
|
+
}
|
|
1201
|
+
const keys = Object.keys(colorNode).filter((k) => !k.startsWith("@_"));
|
|
1202
|
+
if (keys.length > 0) {
|
|
1203
|
+
console.warn(
|
|
1204
|
+
`${WARN_PREFIX8} ColorResolver: unknown color node structure [${keys.join(", ")}]`
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1210
|
+
resolveSrgbClr(node) {
|
|
1211
|
+
const hex = `#${node["@_val"]}`;
|
|
1212
|
+
const alpha = extractAlpha(node);
|
|
1213
|
+
return applyColorTransforms({ hex, alpha }, node);
|
|
1214
|
+
}
|
|
1215
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1216
|
+
resolveSchemeClr(node) {
|
|
1217
|
+
const schemeName = node["@_val"];
|
|
1218
|
+
const hex = this.resolveSchemeColorName(schemeName);
|
|
1219
|
+
const alpha = extractAlpha(node);
|
|
1220
|
+
return applyColorTransforms({ hex, alpha }, node);
|
|
1221
|
+
}
|
|
1222
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1223
|
+
resolveSysClr(node) {
|
|
1224
|
+
const hex = `#${node["@_lastClr"] ?? "000000"}`;
|
|
1225
|
+
const alpha = extractAlpha(node);
|
|
1226
|
+
return applyColorTransforms({ hex, alpha }, node);
|
|
1227
|
+
}
|
|
1228
|
+
resolveSchemeColorName(name) {
|
|
1229
|
+
const mapped = this.mapColorName(name);
|
|
1230
|
+
return this.colorScheme[mapped] ?? "#000000";
|
|
1231
|
+
}
|
|
1232
|
+
mapColorName(name) {
|
|
1233
|
+
if (name in this.colorMap) {
|
|
1234
|
+
return this.colorMap[name];
|
|
1235
|
+
}
|
|
1236
|
+
if (name in this.colorScheme) {
|
|
1237
|
+
return name;
|
|
1238
|
+
}
|
|
1239
|
+
return "dk1";
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
function extractAlpha(node) {
|
|
1243
|
+
const alphaNode = node.alpha;
|
|
1244
|
+
if (alphaNode) {
|
|
1245
|
+
return Number(alphaNode["@_val"]) / 1e5;
|
|
1246
|
+
}
|
|
1247
|
+
return 1;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// src/renderer/geometry/preset-geometries.ts
|
|
1251
|
+
function regularPolygon(w, h, sides) {
|
|
1252
|
+
const cx = w / 2;
|
|
1253
|
+
const cy = h / 2;
|
|
1254
|
+
const points = Array.from({ length: sides }, (_, i) => {
|
|
1255
|
+
const angle = Math.PI * 2 * i / sides - Math.PI / 2;
|
|
1256
|
+
return `${cx + cx * Math.cos(angle)},${cy + cy * Math.sin(angle)}`;
|
|
1257
|
+
}).join(" ");
|
|
1258
|
+
return `<polygon points="${points}"/>`;
|
|
1259
|
+
}
|
|
1260
|
+
function starPolygon(w, h, points, innerRatio) {
|
|
1261
|
+
const cx = w / 2;
|
|
1262
|
+
const cy = h / 2;
|
|
1263
|
+
const coords = [];
|
|
1264
|
+
for (let i = 0; i < points * 2; i++) {
|
|
1265
|
+
const angle = Math.PI * 2 * i / (points * 2) - Math.PI / 2;
|
|
1266
|
+
const r = i % 2 === 0 ? 1 : innerRatio;
|
|
1267
|
+
coords.push(`${cx + cx * r * Math.cos(angle)},${cy + cy * r * Math.sin(angle)}`);
|
|
1268
|
+
}
|
|
1269
|
+
return `<polygon points="${coords.join(" ")}"/>`;
|
|
1270
|
+
}
|
|
1271
|
+
function ooxmlAngleToRadians(angle60k) {
|
|
1272
|
+
return angle60k / 6e4 * (Math.PI / 180);
|
|
1273
|
+
}
|
|
1274
|
+
var presetGeometries = {
|
|
1275
|
+
// =====================
|
|
1276
|
+
// Basic shapes (existing)
|
|
1277
|
+
// =====================
|
|
1278
|
+
rect: (w, h) => `<rect width="${w}" height="${h}"/>`,
|
|
1279
|
+
ellipse: (w, h) => `<ellipse cx="${w / 2}" cy="${h / 2}" rx="${w / 2}" ry="${h / 2}"/>`,
|
|
1280
|
+
roundRect: (w, h, adj) => {
|
|
1281
|
+
const r = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1282
|
+
return `<rect width="${w}" height="${h}" rx="${r}" ry="${r}"/>`;
|
|
1283
|
+
},
|
|
1284
|
+
triangle: (w, h, adj) => {
|
|
1285
|
+
const topX = (adj["adj"] ?? 5e4) / 1e5 * w;
|
|
1286
|
+
return `<polygon points="${topX},0 ${w},${h} 0,${h}"/>`;
|
|
1287
|
+
},
|
|
1288
|
+
rtTriangle: (w, h) => `<polygon points="0,0 ${w},${h} 0,${h}"/>`,
|
|
1289
|
+
diamond: (w, h) => `<polygon points="${w / 2},0 ${w},${h / 2} ${w / 2},${h} 0,${h / 2}"/>`,
|
|
1290
|
+
parallelogram: (w, h, adj) => {
|
|
1291
|
+
const offset = (adj["adj"] ?? 25e3) / 1e5 * w;
|
|
1292
|
+
return `<polygon points="${offset},0 ${w},0 ${w - offset},${h} 0,${h}"/>`;
|
|
1293
|
+
},
|
|
1294
|
+
trapezoid: (w, h, adj) => {
|
|
1295
|
+
const offset = (adj["adj"] ?? 25e3) / 1e5 * w;
|
|
1296
|
+
return `<polygon points="${offset},0 ${w - offset},0 ${w},${h} 0,${h}"/>`;
|
|
1297
|
+
},
|
|
1298
|
+
pentagon: (w, h) => regularPolygon(w, h, 5),
|
|
1299
|
+
hexagon: (w, h, adj) => {
|
|
1300
|
+
const offset = (adj["adj"] ?? 25e3) / 1e5 * w;
|
|
1301
|
+
return `<polygon points="${offset},0 ${w - offset},0 ${w},${h / 2} ${w - offset},${h} ${offset},${h} 0,${h / 2}"/>`;
|
|
1302
|
+
},
|
|
1303
|
+
star4: (w, h) => {
|
|
1304
|
+
const cx = w / 2;
|
|
1305
|
+
const cy = h / 2;
|
|
1306
|
+
const ir = 0.38;
|
|
1307
|
+
return `<polygon points="${cx},0 ${cx + cx * ir},${cy - cy * ir} ${w},${cy} ${cx + cx * ir},${cy + cy * ir} ${cx},${h} ${cx - cx * ir},${cy + cy * ir} 0,${cy} ${cx - cx * ir},${cy - cy * ir}"/>`;
|
|
1308
|
+
},
|
|
1309
|
+
star5: (w, h) => starPolygon(w, h, 5, 0.38),
|
|
1310
|
+
rightArrow: (w, h, adj) => {
|
|
1311
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * h;
|
|
1312
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1313
|
+
const bodyTop = (h - headWidth) / 2;
|
|
1314
|
+
const bodyBottom = h - bodyTop;
|
|
1315
|
+
const shaftEnd = w - headLength;
|
|
1316
|
+
return `<polygon points="0,${bodyTop} ${shaftEnd},${bodyTop} ${shaftEnd},0 ${w},${h / 2} ${shaftEnd},${h} ${shaftEnd},${bodyBottom} 0,${bodyBottom}"/>`;
|
|
1317
|
+
},
|
|
1318
|
+
leftArrow: (w, h, adj) => {
|
|
1319
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * h;
|
|
1320
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1321
|
+
const bodyTop = (h - headWidth) / 2;
|
|
1322
|
+
const bodyBottom = h - bodyTop;
|
|
1323
|
+
return `<polygon points="${headLength},${bodyTop} ${headLength},0 0,${h / 2} ${headLength},${h} ${headLength},${bodyBottom} ${w},${bodyBottom} ${w},${bodyTop}"/>`;
|
|
1324
|
+
},
|
|
1325
|
+
upArrow: (w, h, adj) => {
|
|
1326
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * w;
|
|
1327
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * h;
|
|
1328
|
+
const bodyLeft = (w - headWidth) / 2;
|
|
1329
|
+
const bodyRight = w - bodyLeft;
|
|
1330
|
+
return `<polygon points="${bodyLeft},${headLength} 0,${headLength} ${w / 2},0 ${w},${headLength} ${bodyRight},${headLength} ${bodyRight},${h} ${bodyLeft},${h}"/>`;
|
|
1331
|
+
},
|
|
1332
|
+
downArrow: (w, h, adj) => {
|
|
1333
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * w;
|
|
1334
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * h;
|
|
1335
|
+
const bodyLeft = (w - headWidth) / 2;
|
|
1336
|
+
const bodyRight = w - bodyLeft;
|
|
1337
|
+
const shaftEnd = h - headLength;
|
|
1338
|
+
return `<polygon points="${bodyLeft},0 ${bodyRight},0 ${bodyRight},${shaftEnd} ${w},${shaftEnd} ${w / 2},${h} 0,${shaftEnd} ${bodyLeft},${shaftEnd}"/>`;
|
|
1339
|
+
},
|
|
1340
|
+
line: () => "",
|
|
1341
|
+
cloud: (w, h) => `<rect width="${w}" height="${h}" rx="${Math.min(w, h) * 0.15}"/>`,
|
|
1342
|
+
heart: (w, h) => {
|
|
1343
|
+
const cx = w / 2;
|
|
1344
|
+
return `<path d="M ${cx} ${h * 0.35} C ${cx} ${h * 0.1}, 0 0, 0 ${h * 0.35} C 0 ${h * 0.65}, ${cx} ${h * 0.85}, ${cx} ${h} C ${cx} ${h * 0.85}, ${w} ${h * 0.65}, ${w} ${h * 0.35} C ${w} 0, ${cx} ${h * 0.1}, ${cx} ${h * 0.35} Z"/>`;
|
|
1345
|
+
},
|
|
1346
|
+
// =====================
|
|
1347
|
+
// Additional polygons
|
|
1348
|
+
// =====================
|
|
1349
|
+
heptagon: (w, h) => regularPolygon(w, h, 7),
|
|
1350
|
+
octagon: (w, h) => regularPolygon(w, h, 8),
|
|
1351
|
+
decagon: (w, h) => regularPolygon(w, h, 10),
|
|
1352
|
+
dodecagon: (w, h) => regularPolygon(w, h, 12),
|
|
1353
|
+
// =====================
|
|
1354
|
+
// Stars
|
|
1355
|
+
// =====================
|
|
1356
|
+
star6: (w, h) => starPolygon(w, h, 6, 0.5),
|
|
1357
|
+
star8: (w, h) => starPolygon(w, h, 8, 0.38),
|
|
1358
|
+
star10: (w, h) => starPolygon(w, h, 10, 0.38),
|
|
1359
|
+
star12: (w, h) => starPolygon(w, h, 12, 0.38),
|
|
1360
|
+
star16: (w, h) => starPolygon(w, h, 16, 0.38),
|
|
1361
|
+
star24: (w, h) => starPolygon(w, h, 24, 0.38),
|
|
1362
|
+
star32: (w, h) => starPolygon(w, h, 32, 0.38),
|
|
1363
|
+
irregularSeal1: (w, h) => `<polygon points="${w * 0.15},${h * 0.35} ${w * 0.27},${h * 0.03} ${w * 0.38},${h * 0.28} ${w * 0.5},0 ${w * 0.6},${h * 0.23} ${w * 0.73},${h * 0.08} ${w * 0.72},${h * 0.35} ${w},${h * 0.35} ${w * 0.78},${h * 0.5} ${w * 0.95},${h * 0.7} ${w * 0.73},${h * 0.65} ${w * 0.65},${h} ${w * 0.5},${h * 0.72} ${w * 0.35},${h * 0.95} ${w * 0.32},${h * 0.65} ${w * 0.05},${h * 0.7} ${w * 0.18},${h * 0.5} 0,${h * 0.35}"/>`,
|
|
1364
|
+
irregularSeal2: (w, h) => `<polygon points="${w * 0.1},${h * 0.4} ${w * 0.18},${h * 0.08} ${w * 0.32},${h * 0.3} ${w * 0.45},0 ${w * 0.55},${h * 0.18} ${w * 0.72},${h * 0.05} ${w * 0.68},${h * 0.32} ${w},${h * 0.3} ${w * 0.82},${h * 0.5} ${w * 0.98},${h * 0.68} ${w * 0.75},${h * 0.65} ${w * 0.8},${h * 0.92} ${w * 0.55},${h * 0.75} ${w * 0.42},${h} ${w * 0.38},${h * 0.72} ${w * 0.12},${h * 0.88} ${w * 0.22},${h * 0.6} 0,${h * 0.55}"/>`,
|
|
1365
|
+
// =====================
|
|
1366
|
+
// Additional arrows
|
|
1367
|
+
// =====================
|
|
1368
|
+
leftRightArrow: (w, h, adj) => {
|
|
1369
|
+
const headW = (adj["adj1"] ?? 5e4) / 1e5 * h;
|
|
1370
|
+
const headL = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1371
|
+
const bodyTop = (h - headW) / 2;
|
|
1372
|
+
const bodyBot = h - bodyTop;
|
|
1373
|
+
return `<polygon points="${headL},${bodyTop} ${headL},0 0,${h / 2} ${headL},${h} ${headL},${bodyBot} ${w - headL},${bodyBot} ${w - headL},${h} ${w},${h / 2} ${w - headL},0 ${w - headL},${bodyTop}"/>`;
|
|
1374
|
+
},
|
|
1375
|
+
upDownArrow: (w, h, adj) => {
|
|
1376
|
+
const headW = (adj["adj1"] ?? 5e4) / 1e5 * w;
|
|
1377
|
+
const headL = (adj["adj2"] ?? 5e4) / 1e5 * h;
|
|
1378
|
+
const bodyL = (w - headW) / 2;
|
|
1379
|
+
const bodyR = w - bodyL;
|
|
1380
|
+
return `<polygon points="${bodyL},${headL} 0,${headL} ${w / 2},0 ${w},${headL} ${bodyR},${headL} ${bodyR},${h - headL} ${w},${h - headL} ${w / 2},${h} 0,${h - headL} ${bodyL},${h - headL}"/>`;
|
|
1381
|
+
},
|
|
1382
|
+
notchedRightArrow: (w, h, adj) => {
|
|
1383
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * h;
|
|
1384
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1385
|
+
const bodyTop = (h - headWidth) / 2;
|
|
1386
|
+
const bodyBottom = h - bodyTop;
|
|
1387
|
+
const shaftEnd = w - headLength;
|
|
1388
|
+
const notch = headLength * 0.5;
|
|
1389
|
+
return `<polygon points="0,${bodyTop} ${shaftEnd},${bodyTop} ${shaftEnd},0 ${w},${h / 2} ${shaftEnd},${h} ${shaftEnd},${bodyBottom} 0,${bodyBottom} ${notch},${h / 2}"/>`;
|
|
1390
|
+
},
|
|
1391
|
+
stripedRightArrow: (w, h, adj) => {
|
|
1392
|
+
const headWidth = (adj["adj1"] ?? 5e4) / 1e5 * h;
|
|
1393
|
+
const headLength = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1394
|
+
const bodyTop = (h - headWidth) / 2;
|
|
1395
|
+
const bodyBottom = h - bodyTop;
|
|
1396
|
+
const shaftEnd = w - headLength;
|
|
1397
|
+
const sw = w * 0.05;
|
|
1398
|
+
return `<path d="M 0 ${bodyTop} L ${sw} ${bodyTop} L ${sw} ${bodyBottom} L 0 ${bodyBottom} Z M ${sw * 1.5} ${bodyTop} L ${sw * 2.5} ${bodyTop} L ${sw * 2.5} ${bodyBottom} L ${sw * 1.5} ${bodyBottom} Z M ${sw * 3} ${bodyTop} L ${shaftEnd} ${bodyTop} L ${shaftEnd} 0 L ${w} ${h / 2} L ${shaftEnd} ${h} L ${shaftEnd} ${bodyBottom} L ${sw * 3} ${bodyBottom} Z"/>`;
|
|
1399
|
+
},
|
|
1400
|
+
chevron: (w, h, adj) => {
|
|
1401
|
+
const offset = (adj["adj"] ?? 5e4) / 1e5 * w;
|
|
1402
|
+
return `<polygon points="0,0 ${w - offset},0 ${w},${h / 2} ${w - offset},${h} 0,${h} ${offset},${h / 2}"/>`;
|
|
1403
|
+
},
|
|
1404
|
+
homePlate: (w, h, adj) => {
|
|
1405
|
+
const offset = (adj["adj"] ?? 5e4) / 1e5 * w;
|
|
1406
|
+
return `<polygon points="0,0 ${w - offset},0 ${w},${h / 2} ${w - offset},${h} 0,${h}"/>`;
|
|
1407
|
+
},
|
|
1408
|
+
leftRightUpArrow: (w, h, adj) => {
|
|
1409
|
+
const headW = (adj["adj1"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1410
|
+
const headL = (adj["adj2"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1411
|
+
const bodyW = (adj["adj3"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1412
|
+
const cx = w / 2;
|
|
1413
|
+
const bodyHalf = bodyW / 2;
|
|
1414
|
+
const bodyMid = h - headL - bodyW;
|
|
1415
|
+
return `<path d="M ${cx} 0 L ${cx + headW / 2} ${headL} L ${cx + bodyHalf} ${headL} L ${cx + bodyHalf} ${bodyMid} L ${w - headL} ${bodyMid} L ${w - headL} ${h / 2 + bodyMid / 2 - headW / 2} L ${w} ${h / 2 + bodyMid / 2} L ${w - headL} ${h / 2 + bodyMid / 2 + headW / 2} L ${w - headL} ${bodyMid + bodyW} L ${cx + bodyHalf} ${bodyMid + bodyW} L ${cx + bodyHalf} ${bodyMid + bodyW} L ${cx - bodyHalf} ${bodyMid + bodyW} L ${cx - bodyHalf} ${bodyMid + bodyW} L ${headL} ${bodyMid + bodyW} L ${headL} ${h / 2 + bodyMid / 2 + headW / 2} L 0 ${h / 2 + bodyMid / 2} L ${headL} ${h / 2 + bodyMid / 2 - headW / 2} L ${headL} ${bodyMid} L ${cx - bodyHalf} ${bodyMid} L ${cx - bodyHalf} ${headL} L ${cx - headW / 2} ${headL} Z"/>`;
|
|
1416
|
+
},
|
|
1417
|
+
quadArrow: (w, h, adj) => {
|
|
1418
|
+
const headW = (adj["adj1"] ?? 22500) / 1e5 * Math.min(w, h);
|
|
1419
|
+
const headL = (adj["adj2"] ?? 22500) / 1e5 * Math.min(w, h);
|
|
1420
|
+
const bodyW = (adj["adj3"] ?? 11250) / 1e5 * Math.min(w, h);
|
|
1421
|
+
const cx = w / 2;
|
|
1422
|
+
const cy = h / 2;
|
|
1423
|
+
const bh = bodyW / 2;
|
|
1424
|
+
return `<path d="M ${cx} 0 L ${cx + headW / 2} ${headL} L ${cx + bh} ${headL} L ${cx + bh} ${cy - bh} L ${w - headL} ${cy - bh} L ${w - headL} ${cy - headW / 2} L ${w} ${cy} L ${w - headL} ${cy + headW / 2} L ${w - headL} ${cy + bh} L ${cx + bh} ${cy + bh} L ${cx + bh} ${h - headL} L ${cx + headW / 2} ${h - headL} L ${cx} ${h} L ${cx - headW / 2} ${h - headL} L ${cx - bh} ${h - headL} L ${cx - bh} ${cy + bh} L ${headL} ${cy + bh} L ${headL} ${cy + headW / 2} L 0 ${cy} L ${headL} ${cy - headW / 2} L ${headL} ${cy - bh} L ${cx - bh} ${cy - bh} L ${cx - bh} ${headL} L ${cx - headW / 2} ${headL} Z"/>`;
|
|
1425
|
+
},
|
|
1426
|
+
bentArrow: (w, h, adj) => {
|
|
1427
|
+
const headW = (adj["adj1"] ?? 25e3) / 1e5 * h;
|
|
1428
|
+
const headL = (adj["adj2"] ?? 25e3) / 1e5 * w;
|
|
1429
|
+
const bodyW = (adj["adj3"] ?? 25e3) / 1e5 * h;
|
|
1430
|
+
const shaftEnd = w - headL;
|
|
1431
|
+
const arrowTop = 0;
|
|
1432
|
+
const bodyTop = headW / 2 - bodyW / 2;
|
|
1433
|
+
const bodyBot = headW / 2 + bodyW / 2;
|
|
1434
|
+
return `<polygon points="${shaftEnd},${bodyTop} ${shaftEnd},${arrowTop} ${w},${headW / 2} ${shaftEnd},${headW} ${shaftEnd},${bodyBot} ${bodyW},${bodyBot} ${bodyW},${h} 0,${h} 0,${h - bodyW} ${0},${h - bodyW}"/>`;
|
|
1435
|
+
},
|
|
1436
|
+
bendUpArrow: (w, h, adj) => {
|
|
1437
|
+
const headW = (adj["adj1"] ?? 25e3) / 1e5 * w;
|
|
1438
|
+
const headL = (adj["adj2"] ?? 25e3) / 1e5 * h;
|
|
1439
|
+
const bodyW = (adj["adj3"] ?? 25e3) / 1e5 * w;
|
|
1440
|
+
const cx = w - headW / 2;
|
|
1441
|
+
const bodyLeft = cx - bodyW / 2;
|
|
1442
|
+
const bodyRight = cx + bodyW / 2;
|
|
1443
|
+
return `<polygon points="${cx - headW / 2},${headL} ${cx},0 ${cx + headW / 2},${headL} ${bodyRight},${headL} ${bodyRight},${h - bodyW} ${bodyW},${h - bodyW} ${bodyW},${h} 0,${h} 0,${h - bodyW} ${bodyLeft},${h - bodyW} ${bodyLeft},${headL}"/>`;
|
|
1444
|
+
},
|
|
1445
|
+
leftUpArrow: (w, h, adj) => {
|
|
1446
|
+
const headW = (adj["adj1"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1447
|
+
const headL = (adj["adj2"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1448
|
+
const bodyW = (adj["adj3"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1449
|
+
const bh = bodyW / 2;
|
|
1450
|
+
const topCx = w - headW / 2;
|
|
1451
|
+
const leftCy = h - headW / 2;
|
|
1452
|
+
return `<path d="M ${topCx} 0 L ${topCx + headW / 2} ${headL} L ${topCx + bh} ${headL} L ${topCx + bh} ${leftCy - bh} L ${headL} ${leftCy - bh} L ${headL} ${leftCy - headW / 2} L 0 ${leftCy} L ${headL} ${leftCy + headW / 2} L ${headL} ${leftCy + bh} L ${topCx - bh} ${leftCy + bh} L ${topCx - bh} ${headL} L ${topCx - headW / 2} ${headL} Z"/>`;
|
|
1453
|
+
},
|
|
1454
|
+
uturnArrow: (w, h, adj) => {
|
|
1455
|
+
const headW = (adj["adj1"] ?? 25e3) / 1e5 * w;
|
|
1456
|
+
const headL = (adj["adj2"] ?? 25e3) / 1e5 * h;
|
|
1457
|
+
const bodyW = (adj["adj3"] ?? 25e3) / 1e5 * w;
|
|
1458
|
+
const arcR = w * 0.35;
|
|
1459
|
+
const cx = w / 2;
|
|
1460
|
+
const arrowBot = h;
|
|
1461
|
+
const arrowStart = h - headL;
|
|
1462
|
+
const bodyRight = w - (headW / 2 - bodyW / 2);
|
|
1463
|
+
const bodyLeft = w - (headW / 2 + bodyW / 2);
|
|
1464
|
+
return `<path d="M ${w - headW / 2 - headW / 2} ${arrowStart} L ${w - headW / 2} ${arrowBot} L ${w} ${arrowStart} L ${bodyRight} ${arrowStart} L ${bodyRight} ${arcR} A ${arcR} ${arcR} 0 0 0 ${bodyW} ${arcR} L ${bodyW} ${arrowStart} L 0 ${arrowStart} L 0 ${arcR} A ${cx} ${cx} 0 0 1 ${bodyLeft} ${arcR} L ${bodyLeft} ${arrowStart} Z"/>`;
|
|
1465
|
+
},
|
|
1466
|
+
// =====================
|
|
1467
|
+
// Flowchart shapes
|
|
1468
|
+
// =====================
|
|
1469
|
+
flowChartProcess: (w, h) => `<rect width="${w}" height="${h}"/>`,
|
|
1470
|
+
flowChartAlternateProcess: (w, h) => {
|
|
1471
|
+
const r = Math.min(w, h) / 6;
|
|
1472
|
+
return `<rect width="${w}" height="${h}" rx="${r}" ry="${r}"/>`;
|
|
1473
|
+
},
|
|
1474
|
+
flowChartDecision: (w, h) => `<polygon points="${w / 2},0 ${w},${h / 2} ${w / 2},${h} 0,${h / 2}"/>`,
|
|
1475
|
+
flowChartInputOutput: (w, h) => {
|
|
1476
|
+
const offset = w / 5;
|
|
1477
|
+
return `<polygon points="${offset},0 ${w},0 ${w - offset},${h} 0,${h}"/>`;
|
|
1478
|
+
},
|
|
1479
|
+
flowChartPredefinedProcess: (w, h) => {
|
|
1480
|
+
const d = w / 8;
|
|
1481
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${d} 0 L ${d} ${h} M ${w - d} 0 L ${w - d} ${h}"/>`;
|
|
1482
|
+
},
|
|
1483
|
+
flowChartInternalStorage: (w, h) => {
|
|
1484
|
+
const dx = w / 8;
|
|
1485
|
+
const dy = h / 8;
|
|
1486
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${dx} 0 L ${dx} ${h} M 0 ${dy} L ${w} ${dy}"/>`;
|
|
1487
|
+
},
|
|
1488
|
+
flowChartDocument: (w, h) => {
|
|
1489
|
+
const bh = h * 0.83;
|
|
1490
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${bh} C ${w * 0.75} ${h}, ${w * 0.25} ${h * 0.66}, 0 ${bh} Z"/>`;
|
|
1491
|
+
},
|
|
1492
|
+
flowChartMultidocument: (w, h) => {
|
|
1493
|
+
const dx = w * 0.1;
|
|
1494
|
+
const dy = h * 0.1;
|
|
1495
|
+
const bw = w - dx;
|
|
1496
|
+
const bh = (h - dy) * 0.83;
|
|
1497
|
+
return `<path d="M ${dx} ${dy} L ${w} ${dy} L ${w} ${dy + bh} C ${w - bw * 0.25} ${dy + (h - dy)}, ${dx + bw * 0.25} ${dy + (h - dy) * 0.66}, ${dx} ${dy + bh} Z M ${dx / 2} ${dy / 2} L ${dx} ${dy / 2} L ${dx} ${dy} M 0 0 L ${dx / 2} 0 L ${dx / 2} ${dy / 2}"/>`;
|
|
1498
|
+
},
|
|
1499
|
+
flowChartTerminator: (w, h) => {
|
|
1500
|
+
const r = h / 2;
|
|
1501
|
+
return `<rect width="${w}" height="${h}" rx="${r}" ry="${r}"/>`;
|
|
1502
|
+
},
|
|
1503
|
+
flowChartPreparation: (w, h) => {
|
|
1504
|
+
const offset = w / 5;
|
|
1505
|
+
return `<polygon points="${offset},0 ${w - offset},0 ${w},${h / 2} ${w - offset},${h} ${offset},${h} 0,${h / 2}"/>`;
|
|
1506
|
+
},
|
|
1507
|
+
flowChartManualInput: (w, h) => {
|
|
1508
|
+
const topY = h / 5;
|
|
1509
|
+
return `<polygon points="0,${topY} ${w},0 ${w},${h} 0,${h}"/>`;
|
|
1510
|
+
},
|
|
1511
|
+
flowChartManualOperation: (w, h) => {
|
|
1512
|
+
const offset = w / 5;
|
|
1513
|
+
return `<polygon points="0,0 ${w},0 ${w - offset},${h} ${offset},${h}"/>`;
|
|
1514
|
+
},
|
|
1515
|
+
flowChartConnector: (w, h) => `<ellipse cx="${w / 2}" cy="${h / 2}" rx="${w / 2}" ry="${h / 2}"/>`,
|
|
1516
|
+
flowChartOffpageConnector: (w, h) => {
|
|
1517
|
+
const arrowH = h * 0.2;
|
|
1518
|
+
return `<polygon points="0,0 ${w},0 ${w},${h - arrowH} ${w / 2},${h} 0,${h - arrowH}"/>`;
|
|
1519
|
+
},
|
|
1520
|
+
flowChartPunchedCard: (w, h) => {
|
|
1521
|
+
const cut = Math.min(w, h) * 0.2;
|
|
1522
|
+
return `<polygon points="${cut},0 ${w},0 ${w},${h} 0,${h} 0,${cut}"/>`;
|
|
1523
|
+
},
|
|
1524
|
+
flowChartPunchedTape: (w, h) => {
|
|
1525
|
+
const wave = h * 0.1;
|
|
1526
|
+
return `<path d="M 0 ${wave} C ${w * 0.25} ${-wave}, ${w * 0.75} ${wave * 3}, ${w} ${wave} L ${w} ${h - wave} C ${w * 0.75} ${h + wave}, ${w * 0.25} ${h - wave * 3}, 0 ${h - wave} Z"/>`;
|
|
1527
|
+
},
|
|
1528
|
+
flowChartCollate: (w, h) => `<polygon points="0,0 ${w},0 ${w / 2},${h / 2} ${w},${h} 0,${h} ${w / 2},${h / 2}"/>`,
|
|
1529
|
+
flowChartSort: (w, h) => `<path d="M ${w / 2} 0 L ${w} ${h / 2} L 0 ${h / 2} Z M 0 ${h / 2} L ${w} ${h / 2} M ${w / 2} ${h} L ${w} ${h / 2} L 0 ${h / 2} Z"/>`,
|
|
1530
|
+
flowChartExtract: (w, h) => `<polygon points="${w / 2},0 ${w},${h} 0,${h}"/>`,
|
|
1531
|
+
flowChartMerge: (w, h) => `<polygon points="0,0 ${w},0 ${w / 2},${h}"/>`,
|
|
1532
|
+
flowChartOnlineStorage: (w, h) => {
|
|
1533
|
+
const arcW = w * 0.15;
|
|
1534
|
+
return `<path d="M ${arcW} 0 L ${w} 0 L ${w} ${h} L ${arcW} ${h} A ${arcW} ${h / 2} 0 0 1 ${arcW} 0 Z"/>`;
|
|
1535
|
+
},
|
|
1536
|
+
flowChartDelay: (w, h) => {
|
|
1537
|
+
const arcW = w * 0.35;
|
|
1538
|
+
return `<path d="M 0 0 L ${w - arcW} 0 A ${arcW} ${h / 2} 0 0 1 ${w - arcW} ${h} L 0 ${h} Z"/>`;
|
|
1539
|
+
},
|
|
1540
|
+
flowChartDisplay: (w, h) => {
|
|
1541
|
+
const leftW = w * 0.15;
|
|
1542
|
+
const arcW = w * 0.35;
|
|
1543
|
+
return `<path d="M ${leftW} 0 L ${w - arcW} 0 A ${arcW} ${h / 2} 0 0 1 ${w - arcW} ${h} L ${leftW} ${h} L 0 ${h / 2} Z"/>`;
|
|
1544
|
+
},
|
|
1545
|
+
flowChartMagneticTape: (w, h) => {
|
|
1546
|
+
const r = Math.min(w, h) / 2;
|
|
1547
|
+
const cx = w / 2;
|
|
1548
|
+
const cy = h / 2;
|
|
1549
|
+
return `<path d="M ${cx + r} ${cy} A ${r} ${r} 0 1 1 ${cx + r - 0.01} ${cy + 0.01} L ${w} ${cy} L ${w} ${h} L ${w - r * 0.3} ${h} L ${cx + r * Math.cos(Math.PI / 6)} ${cy + r * Math.sin(Math.PI / 6)}"/>`;
|
|
1550
|
+
},
|
|
1551
|
+
flowChartMagneticDisk: (w, h) => {
|
|
1552
|
+
const ry = h * 0.15;
|
|
1553
|
+
return `<path d="M 0 ${ry} A ${w / 2} ${ry} 0 0 1 ${w} ${ry} L ${w} ${h - ry} A ${w / 2} ${ry} 0 0 1 0 ${h - ry} Z M 0 ${ry} A ${w / 2} ${ry} 0 0 0 ${w} ${ry}"/>`;
|
|
1554
|
+
},
|
|
1555
|
+
flowChartMagneticDrum: (w, h) => {
|
|
1556
|
+
const rx = w * 0.15;
|
|
1557
|
+
return `<path d="M ${rx} 0 A ${rx} ${h / 2} 0 0 0 ${rx} ${h} L ${w - rx} ${h} A ${rx} ${h / 2} 0 0 0 ${w - rx} 0 Z M ${w - rx} 0 A ${rx} ${h / 2} 0 0 1 ${w - rx} ${h}"/>`;
|
|
1558
|
+
},
|
|
1559
|
+
flowChartSummingJunction: (w, h) => {
|
|
1560
|
+
const cx = w / 2;
|
|
1561
|
+
const cy = h / 2;
|
|
1562
|
+
const rx = w / 2;
|
|
1563
|
+
const ry = h / 2;
|
|
1564
|
+
const d = 0.707;
|
|
1565
|
+
return `<path d="M ${cx + rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx + rx - 0.01} ${cy - 0.01} Z M ${cx - rx * d} ${cy - ry * d} L ${cx + rx * d} ${cy + ry * d} M ${cx + rx * d} ${cy - ry * d} L ${cx - rx * d} ${cy + ry * d}"/>`;
|
|
1566
|
+
},
|
|
1567
|
+
flowChartOr: (w, h) => {
|
|
1568
|
+
const cx = w / 2;
|
|
1569
|
+
const cy = h / 2;
|
|
1570
|
+
const rx = w / 2;
|
|
1571
|
+
const ry = h / 2;
|
|
1572
|
+
return `<path d="M ${cx + rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx + rx - 0.01} ${cy - 0.01} Z M ${cx} 0 L ${cx} ${h} M 0 ${cy} L ${w} ${cy}"/>`;
|
|
1573
|
+
},
|
|
1574
|
+
// =====================
|
|
1575
|
+
// Callout shapes
|
|
1576
|
+
// =====================
|
|
1577
|
+
wedgeRectCallout: (w, h, adj) => {
|
|
1578
|
+
const tipX = w / 2 + (adj["adj1"] ?? -20833) / 1e5 * w;
|
|
1579
|
+
const tipY = h / 2 + (adj["adj2"] ?? 62500) / 1e5 * h;
|
|
1580
|
+
const bx = w / 2;
|
|
1581
|
+
const wedgeW = w * 0.06;
|
|
1582
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L ${bx + wedgeW} ${h} L ${tipX} ${tipY} L ${bx - wedgeW} ${h} L 0 ${h} Z"/>`;
|
|
1583
|
+
},
|
|
1584
|
+
wedgeRoundRectCallout: (w, h, adj) => {
|
|
1585
|
+
const tipX = w / 2 + (adj["adj1"] ?? -20833) / 1e5 * w;
|
|
1586
|
+
const tipY = h / 2 + (adj["adj2"] ?? 62500) / 1e5 * h;
|
|
1587
|
+
const r = (adj["adj3"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1588
|
+
const bx = w / 2;
|
|
1589
|
+
const wedgeW = w * 0.06;
|
|
1590
|
+
return `<path d="M ${r} 0 L ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h - r} A ${r} ${r} 0 0 1 ${w - r} ${h} L ${bx + wedgeW} ${h} L ${tipX} ${tipY} L ${bx - wedgeW} ${h} L ${r} ${h} A ${r} ${r} 0 0 1 0 ${h - r} L 0 ${r} A ${r} ${r} 0 0 1 ${r} 0 Z"/>`;
|
|
1591
|
+
},
|
|
1592
|
+
wedgeEllipseCallout: (w, h, adj) => {
|
|
1593
|
+
const tipX = w / 2 + (adj["adj1"] ?? -20833) / 1e5 * w;
|
|
1594
|
+
const tipY = h / 2 + (adj["adj2"] ?? 62500) / 1e5 * h;
|
|
1595
|
+
const cx = w / 2;
|
|
1596
|
+
const cy = h / 2;
|
|
1597
|
+
const rx = w / 2;
|
|
1598
|
+
const ry = h / 2;
|
|
1599
|
+
const angle = Math.atan2(tipY - cy, tipX - cx);
|
|
1600
|
+
const wedgeAngle = 0.15;
|
|
1601
|
+
const x1 = cx + rx * Math.cos(angle - wedgeAngle);
|
|
1602
|
+
const y1 = cy + ry * Math.sin(angle - wedgeAngle);
|
|
1603
|
+
const x2 = cx + rx * Math.cos(angle + wedgeAngle);
|
|
1604
|
+
const y2 = cy + ry * Math.sin(angle + wedgeAngle);
|
|
1605
|
+
return `<path d="M ${x1} ${y1} L ${tipX} ${tipY} L ${x2} ${y2} A ${rx} ${ry} 0 1 1 ${x1} ${y1} Z"/>`;
|
|
1606
|
+
},
|
|
1607
|
+
cloudCallout: (w, h, adj) => {
|
|
1608
|
+
const tipX = w / 2 + (adj["adj1"] ?? -20833) / 1e5 * w;
|
|
1609
|
+
const tipY = h / 2 + (adj["adj2"] ?? 62500) / 1e5 * h;
|
|
1610
|
+
const r = Math.min(w, h) * 0.15;
|
|
1611
|
+
const bx = w / 2;
|
|
1612
|
+
const by = h / 2;
|
|
1613
|
+
const dx = tipX - bx;
|
|
1614
|
+
const dy = tipY - by;
|
|
1615
|
+
const d1x = bx + dx * 0.33;
|
|
1616
|
+
const d1y = by + dy * 0.33;
|
|
1617
|
+
const d2x = bx + dx * 0.66;
|
|
1618
|
+
const d2y = by + dy * 0.66;
|
|
1619
|
+
return `<path d="M ${r} ${h} A ${r} ${r} 0 0 1 0 ${h - r} A ${r} ${r} 0 0 1 ${r} ${h - 2 * r} L ${r} ${r} A ${r} ${r} 0 0 1 ${2 * r} 0 L ${w - 2 * r} 0 A ${r} ${r} 0 0 1 ${w - r} ${r} L ${w - r} ${h - 2 * r} A ${r} ${r} 0 0 1 ${w - 2 * r} ${h - r} A ${r} ${r} 0 0 1 ${w - 2 * r} ${h} Z M ${d1x} ${d1y} m ${r * 0.25} 0 a ${r * 0.25} ${r * 0.25} 0 1 1 -${r * 0.5} 0 a ${r * 0.25} ${r * 0.25} 0 1 1 ${r * 0.5} 0 Z M ${d2x} ${d2y} m ${r * 0.15} 0 a ${r * 0.15} ${r * 0.15} 0 1 1 -${r * 0.3} 0 a ${r * 0.15} ${r * 0.15} 0 1 1 ${r * 0.3} 0 Z"/>`;
|
|
1620
|
+
},
|
|
1621
|
+
borderCallout1: (w, h, adj) => {
|
|
1622
|
+
const y1 = (adj["adj1"] ?? 18750) / 1e5 * h;
|
|
1623
|
+
const x1 = (adj["adj2"] ?? -8333) / 1e5 * w;
|
|
1624
|
+
const y2 = (adj["adj3"] ?? 112500) / 1e5 * h;
|
|
1625
|
+
const x2 = (adj["adj4"] ?? -38333) / 1e5 * w;
|
|
1626
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${x1} ${y1} L ${x2} ${y2}"/>`;
|
|
1627
|
+
},
|
|
1628
|
+
borderCallout2: (w, h, adj) => {
|
|
1629
|
+
const y1 = (adj["adj1"] ?? 18750) / 1e5 * h;
|
|
1630
|
+
const x1 = (adj["adj2"] ?? -8333) / 1e5 * w;
|
|
1631
|
+
const y2 = (adj["adj3"] ?? 18750) / 1e5 * h;
|
|
1632
|
+
const x2 = (adj["adj4"] ?? -16667) / 1e5 * w;
|
|
1633
|
+
const y3 = (adj["adj5"] ?? 112500) / 1e5 * h;
|
|
1634
|
+
const x3 = (adj["adj6"] ?? -46667) / 1e5 * w;
|
|
1635
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${x1} ${y1} L ${x2} ${y2} L ${x3} ${y3}"/>`;
|
|
1636
|
+
},
|
|
1637
|
+
borderCallout3: (w, h, adj) => {
|
|
1638
|
+
const y1 = (adj["adj1"] ?? 18750) / 1e5 * h;
|
|
1639
|
+
const x1 = (adj["adj2"] ?? -8333) / 1e5 * w;
|
|
1640
|
+
const y2 = (adj["adj3"] ?? 18750) / 1e5 * h;
|
|
1641
|
+
const x2 = (adj["adj4"] ?? -16667) / 1e5 * w;
|
|
1642
|
+
const y3 = (adj["adj5"] ?? 1e5) / 1e5 * h;
|
|
1643
|
+
const x3 = (adj["adj6"] ?? -16667) / 1e5 * w;
|
|
1644
|
+
const y4 = (adj["adj7"] ?? 112963) / 1e5 * h;
|
|
1645
|
+
const x4 = (adj["adj8"] ?? -46667) / 1e5 * w;
|
|
1646
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${x1} ${y1} L ${x2} ${y2} L ${x3} ${y3} L ${x4} ${y4}"/>`;
|
|
1647
|
+
},
|
|
1648
|
+
// =====================
|
|
1649
|
+
// Arc shapes
|
|
1650
|
+
// =====================
|
|
1651
|
+
arc: (w, h, adj) => {
|
|
1652
|
+
const stAng = ooxmlAngleToRadians(adj["adj1"] ?? 162e5);
|
|
1653
|
+
const endAng = ooxmlAngleToRadians(adj["adj2"] ?? 0);
|
|
1654
|
+
const rx = w / 2;
|
|
1655
|
+
const ry = h / 2;
|
|
1656
|
+
const cx = rx;
|
|
1657
|
+
const cy = ry;
|
|
1658
|
+
const x1 = cx + rx * Math.cos(stAng);
|
|
1659
|
+
const y1 = cy - ry * Math.sin(stAng);
|
|
1660
|
+
const x2 = cx + rx * Math.cos(endAng);
|
|
1661
|
+
const y2 = cy - ry * Math.sin(endAng);
|
|
1662
|
+
let sweep = stAng - endAng;
|
|
1663
|
+
if (sweep < 0) sweep += 2 * Math.PI;
|
|
1664
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
1665
|
+
return `<path d="M ${x1} ${y1} A ${rx} ${ry} 0 ${largeArc} 0 ${x2} ${y2}"/>`;
|
|
1666
|
+
},
|
|
1667
|
+
chord: (w, h, adj) => {
|
|
1668
|
+
const stAng = ooxmlAngleToRadians(adj["adj1"] ?? 27e5);
|
|
1669
|
+
const endAng = ooxmlAngleToRadians(adj["adj2"] ?? 162e5);
|
|
1670
|
+
const rx = w / 2;
|
|
1671
|
+
const ry = h / 2;
|
|
1672
|
+
const cx = rx;
|
|
1673
|
+
const cy = ry;
|
|
1674
|
+
const x1 = cx + rx * Math.cos(stAng);
|
|
1675
|
+
const y1 = cy - ry * Math.sin(stAng);
|
|
1676
|
+
const x2 = cx + rx * Math.cos(endAng);
|
|
1677
|
+
const y2 = cy - ry * Math.sin(endAng);
|
|
1678
|
+
let sweep = stAng - endAng;
|
|
1679
|
+
if (sweep < 0) sweep += 2 * Math.PI;
|
|
1680
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
1681
|
+
return `<path d="M ${x1} ${y1} A ${rx} ${ry} 0 ${largeArc} 0 ${x2} ${y2} Z"/>`;
|
|
1682
|
+
},
|
|
1683
|
+
pie: (w, h, adj) => {
|
|
1684
|
+
const stAng = ooxmlAngleToRadians(adj["adj1"] ?? 0);
|
|
1685
|
+
const endAng = ooxmlAngleToRadians(adj["adj2"] ?? 162e5);
|
|
1686
|
+
const rx = w / 2;
|
|
1687
|
+
const ry = h / 2;
|
|
1688
|
+
const cx = rx;
|
|
1689
|
+
const cy = ry;
|
|
1690
|
+
const x1 = cx + rx * Math.cos(stAng);
|
|
1691
|
+
const y1 = cy - ry * Math.sin(stAng);
|
|
1692
|
+
const x2 = cx + rx * Math.cos(endAng);
|
|
1693
|
+
const y2 = cy - ry * Math.sin(endAng);
|
|
1694
|
+
let sweep = stAng - endAng;
|
|
1695
|
+
if (sweep < 0) sweep += 2 * Math.PI;
|
|
1696
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
1697
|
+
return `<path d="M ${cx} ${cy} L ${x1} ${y1} A ${rx} ${ry} 0 ${largeArc} 0 ${x2} ${y2} Z"/>`;
|
|
1698
|
+
},
|
|
1699
|
+
blockArc: (w, h, adj) => {
|
|
1700
|
+
const stAng = ooxmlAngleToRadians(adj["adj1"] ?? 108e5);
|
|
1701
|
+
const endAng = ooxmlAngleToRadians(adj["adj2"] ?? 0);
|
|
1702
|
+
const thickness = (adj["adj3"] ?? 25e3) / 1e5;
|
|
1703
|
+
const rx = w / 2;
|
|
1704
|
+
const ry = h / 2;
|
|
1705
|
+
const cx = rx;
|
|
1706
|
+
const cy = ry;
|
|
1707
|
+
const irx = rx * (1 - thickness);
|
|
1708
|
+
const iry = ry * (1 - thickness);
|
|
1709
|
+
const ox1 = cx + rx * Math.cos(stAng);
|
|
1710
|
+
const oy1 = cy - ry * Math.sin(stAng);
|
|
1711
|
+
const ox2 = cx + rx * Math.cos(endAng);
|
|
1712
|
+
const oy2 = cy - ry * Math.sin(endAng);
|
|
1713
|
+
const ix1 = cx + irx * Math.cos(stAng);
|
|
1714
|
+
const iy1 = cy - iry * Math.sin(stAng);
|
|
1715
|
+
const ix2 = cx + irx * Math.cos(endAng);
|
|
1716
|
+
const iy2 = cy - iry * Math.sin(endAng);
|
|
1717
|
+
let sweep = stAng - endAng;
|
|
1718
|
+
if (sweep < 0) sweep += 2 * Math.PI;
|
|
1719
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
1720
|
+
return `<path d="M ${ox1} ${oy1} A ${rx} ${ry} 0 ${largeArc} 0 ${ox2} ${oy2} L ${ix2} ${iy2} A ${irx} ${iry} 0 ${largeArc} 1 ${ix1} ${iy1} Z"/>`;
|
|
1721
|
+
},
|
|
1722
|
+
// =====================
|
|
1723
|
+
// Math shapes
|
|
1724
|
+
// =====================
|
|
1725
|
+
mathPlus: (w, h, adj) => {
|
|
1726
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1727
|
+
const tw = t * w;
|
|
1728
|
+
const th = t * h;
|
|
1729
|
+
const lx = (w - tw) / 2;
|
|
1730
|
+
const rx = lx + tw;
|
|
1731
|
+
const ty = (h - th) / 2;
|
|
1732
|
+
const by = ty + th;
|
|
1733
|
+
return `<polygon points="${lx},0 ${rx},0 ${rx},${ty} ${w},${ty} ${w},${by} ${rx},${by} ${rx},${h} ${lx},${h} ${lx},${by} 0,${by} 0,${ty} ${lx},${ty}"/>`;
|
|
1734
|
+
},
|
|
1735
|
+
mathMinus: (w, h, adj) => {
|
|
1736
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1737
|
+
const th = t * h;
|
|
1738
|
+
const ty = (h - th) / 2;
|
|
1739
|
+
const by = ty + th;
|
|
1740
|
+
return `<rect x="0" y="${ty}" width="${w}" height="${by - ty}"/>`;
|
|
1741
|
+
},
|
|
1742
|
+
mathMultiply: (w, h, adj) => {
|
|
1743
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1744
|
+
const d = t * Math.min(w, h) * 0.5;
|
|
1745
|
+
const cx = w / 2;
|
|
1746
|
+
const cy = h / 2;
|
|
1747
|
+
return `<path d="M ${cx} ${cy - d} L ${w - d} 0 L ${w} ${d} L ${cx + d} ${cy} L ${w} ${h - d} L ${w - d} ${h} L ${cx} ${cy + d} L ${d} ${h} L 0 ${h - d} L ${cx - d} ${cy} L 0 ${d} L ${d} 0 Z"/>`;
|
|
1748
|
+
},
|
|
1749
|
+
mathDivide: (w, h, adj) => {
|
|
1750
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1751
|
+
const th = t * h;
|
|
1752
|
+
const ty = (h - th) / 2;
|
|
1753
|
+
const by = ty + th;
|
|
1754
|
+
const dotR = Math.min(w, h) * t * 0.5;
|
|
1755
|
+
const cx = w / 2;
|
|
1756
|
+
const topDotY = ty / 2;
|
|
1757
|
+
const botDotY = h - ty / 2;
|
|
1758
|
+
return `<path d="M 0 ${ty} L ${w} ${ty} L ${w} ${by} L 0 ${by} Z M ${cx + dotR} ${topDotY} A ${dotR} ${dotR} 0 1 1 ${cx + dotR - 0.01} ${topDotY - 0.01} Z M ${cx + dotR} ${botDotY} A ${dotR} ${dotR} 0 1 1 ${cx + dotR - 0.01} ${botDotY - 0.01} Z"/>`;
|
|
1759
|
+
},
|
|
1760
|
+
mathEqual: (w, h, adj) => {
|
|
1761
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1762
|
+
const gap = t * h;
|
|
1763
|
+
const barH = t * h;
|
|
1764
|
+
const y1 = (h - gap) / 2 - barH;
|
|
1765
|
+
const y2 = (h + gap) / 2;
|
|
1766
|
+
return `<path d="M 0 ${y1} L ${w} ${y1} L ${w} ${y1 + barH} L 0 ${y1 + barH} Z M 0 ${y2} L ${w} ${y2} L ${w} ${y2 + barH} L 0 ${y2 + barH} Z"/>`;
|
|
1767
|
+
},
|
|
1768
|
+
mathNotEqual: (w, h, adj) => {
|
|
1769
|
+
const t = (adj["adj1"] ?? 23520) / 1e5;
|
|
1770
|
+
const gap = t * h;
|
|
1771
|
+
const barH = t * h;
|
|
1772
|
+
const y1 = (h - gap) / 2 - barH;
|
|
1773
|
+
const y2 = (h + gap) / 2;
|
|
1774
|
+
const slashW = w * 0.15;
|
|
1775
|
+
const sx = w / 2 - slashW / 2;
|
|
1776
|
+
return `<path d="M 0 ${y1} L ${w} ${y1} L ${w} ${y1 + barH} L 0 ${y1 + barH} Z M 0 ${y2} L ${w} ${y2} L ${w} ${y2 + barH} L 0 ${y2 + barH} Z M ${sx + slashW} ${y1 - barH} L ${sx + slashW + slashW} ${y1 - barH} L ${sx} ${y2 + barH + barH} L ${sx - slashW} ${y2 + barH + barH} Z"/>`;
|
|
1777
|
+
},
|
|
1778
|
+
// =====================
|
|
1779
|
+
// Other shapes
|
|
1780
|
+
// =====================
|
|
1781
|
+
plus: (w, h, adj) => {
|
|
1782
|
+
const t = (adj["adj"] ?? 25e3) / 1e5;
|
|
1783
|
+
const lx = t * w;
|
|
1784
|
+
const rx = w - lx;
|
|
1785
|
+
const ty = t * h;
|
|
1786
|
+
const by = h - ty;
|
|
1787
|
+
return `<polygon points="${lx},0 ${rx},0 ${rx},${ty} ${w},${ty} ${w},${by} ${rx},${by} ${rx},${h} ${lx},${h} ${lx},${by} 0,${by} 0,${ty} ${lx},${ty}"/>`;
|
|
1788
|
+
},
|
|
1789
|
+
corner: (w, h, adj) => {
|
|
1790
|
+
const adjX = (adj["adj1"] ?? 5e4) / 1e5;
|
|
1791
|
+
const adjY = (adj["adj2"] ?? 5e4) / 1e5;
|
|
1792
|
+
const cx = adjX * w;
|
|
1793
|
+
const cy = adjY * h;
|
|
1794
|
+
return `<polygon points="0,0 ${cx},0 ${cx},${cy} ${w},${cy} ${w},${h} 0,${h}"/>`;
|
|
1795
|
+
},
|
|
1796
|
+
diagStripe: (w, h, adj) => {
|
|
1797
|
+
const d = (adj["adj"] ?? 5e4) / 1e5 * Math.min(w, h);
|
|
1798
|
+
return `<polygon points="0,${d} ${d},0 ${w},0 0,${h}"/>`;
|
|
1799
|
+
},
|
|
1800
|
+
foldedCorner: (w, h, adj) => {
|
|
1801
|
+
const fold = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1802
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h - fold} L ${w - fold} ${h} L 0 ${h} Z M ${w - fold} ${h} L ${w - fold} ${h - fold} L ${w} ${h - fold}"/>`;
|
|
1803
|
+
},
|
|
1804
|
+
plaque: (w, h, adj) => {
|
|
1805
|
+
const r = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1806
|
+
return `<path d="M 0 ${r} A ${r} ${r} 0 0 1 ${r} 0 L ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h - r} A ${r} ${r} 0 0 1 ${w - r} ${h} L ${r} ${h} A ${r} ${r} 0 0 1 0 ${h - r} Z"/>`;
|
|
1807
|
+
},
|
|
1808
|
+
can: (w, h, adj) => {
|
|
1809
|
+
const ry = (adj["adj"] ?? 25e3) / 1e5 * h * 0.5;
|
|
1810
|
+
return `<path d="M 0 ${ry} A ${w / 2} ${ry} 0 0 1 ${w} ${ry} L ${w} ${h - ry} A ${w / 2} ${ry} 0 0 1 0 ${h - ry} Z M 0 ${ry} A ${w / 2} ${ry} 0 0 0 ${w} ${ry}"/>`;
|
|
1811
|
+
},
|
|
1812
|
+
cube: (w, h, adj) => {
|
|
1813
|
+
const d = (adj["adj"] ?? 25e3) / 1e5 * Math.min(w, h);
|
|
1814
|
+
return `<path d="M 0 ${d} L ${d} 0 L ${w} 0 L ${w} ${h - d} L ${w - d} ${h} L 0 ${h} Z M 0 ${d} L ${w - d} ${d} L ${w} 0 M ${w - d} ${d} L ${w - d} ${h}"/>`;
|
|
1815
|
+
},
|
|
1816
|
+
donut: (w, h, adj) => {
|
|
1817
|
+
const t = (adj["adj"] ?? 25e3) / 1e5;
|
|
1818
|
+
const rx = w / 2;
|
|
1819
|
+
const ry = h / 2;
|
|
1820
|
+
const irx = rx * (1 - t);
|
|
1821
|
+
const iry = ry * (1 - t);
|
|
1822
|
+
const cx = rx;
|
|
1823
|
+
const cy = ry;
|
|
1824
|
+
return `<path fill-rule="evenodd" d="M ${cx + rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx - rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx + rx} ${cy} Z M ${cx + irx} ${cy} A ${irx} ${iry} 0 1 0 ${cx - irx} ${cy} A ${irx} ${iry} 0 1 0 ${cx + irx} ${cy} Z"/>`;
|
|
1825
|
+
},
|
|
1826
|
+
noSmoking: (w, h, adj) => {
|
|
1827
|
+
const t = (adj["adj"] ?? 18750) / 1e5;
|
|
1828
|
+
const rx = w / 2;
|
|
1829
|
+
const ry = h / 2;
|
|
1830
|
+
const cx = rx;
|
|
1831
|
+
const cy = ry;
|
|
1832
|
+
const irx = rx * (1 - t);
|
|
1833
|
+
const iry = ry * (1 - t);
|
|
1834
|
+
const angle = Math.PI / 4;
|
|
1835
|
+
const lx1 = cx + irx * Math.cos(angle);
|
|
1836
|
+
const ly1 = cy - iry * Math.sin(angle);
|
|
1837
|
+
const lx2 = cx - irx * Math.cos(angle);
|
|
1838
|
+
const ly2 = cy + iry * Math.sin(angle);
|
|
1839
|
+
return `<path fill-rule="evenodd" d="M ${cx + rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx - rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx + rx} ${cy} Z M ${cx + irx} ${cy} A ${irx} ${iry} 0 1 0 ${cx - irx} ${cy} A ${irx} ${iry} 0 1 0 ${cx + irx} ${cy} Z M ${lx1} ${ly1} L ${lx2} ${ly2}"/>`;
|
|
1840
|
+
},
|
|
1841
|
+
smileyFace: (w, h, adj) => {
|
|
1842
|
+
const smile = (adj["adj"] ?? 4653) / 1e5;
|
|
1843
|
+
const rx = w / 2;
|
|
1844
|
+
const ry = h / 2;
|
|
1845
|
+
const cx = rx;
|
|
1846
|
+
const cy = ry;
|
|
1847
|
+
const eyeRx = w * 0.06;
|
|
1848
|
+
const eyeRy = h * 0.06;
|
|
1849
|
+
const eyeY = h * 0.35;
|
|
1850
|
+
const leftEyeX = w * 0.35;
|
|
1851
|
+
const rightEyeX = w * 0.65;
|
|
1852
|
+
const mouthY = h * 0.6;
|
|
1853
|
+
const mouthW = w * 0.3;
|
|
1854
|
+
const mouthCurve = smile * h;
|
|
1855
|
+
return `<path d="M ${cx + rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx - rx} ${cy} A ${rx} ${ry} 0 1 1 ${cx + rx} ${cy} Z M ${leftEyeX + eyeRx} ${eyeY} A ${eyeRx} ${eyeRy} 0 1 1 ${leftEyeX - eyeRx} ${eyeY} A ${eyeRx} ${eyeRy} 0 1 1 ${leftEyeX + eyeRx} ${eyeY} Z M ${rightEyeX + eyeRx} ${eyeY} A ${eyeRx} ${eyeRy} 0 1 1 ${rightEyeX - eyeRx} ${eyeY} A ${eyeRx} ${eyeRy} 0 1 1 ${rightEyeX + eyeRx} ${eyeY} Z M ${cx - mouthW} ${mouthY} C ${cx - mouthW * 0.5} ${mouthY + mouthCurve}, ${cx + mouthW * 0.5} ${mouthY + mouthCurve}, ${cx + mouthW} ${mouthY}"/>`;
|
|
1856
|
+
},
|
|
1857
|
+
frame: (w, h, adj) => {
|
|
1858
|
+
const t = (adj["adj1"] ?? 12500) / 1e5 * Math.min(w, h);
|
|
1859
|
+
return `<path fill-rule="evenodd" d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${t} ${t} L ${t} ${h - t} L ${w - t} ${h - t} L ${w - t} ${t} Z"/>`;
|
|
1860
|
+
},
|
|
1861
|
+
bevel: (w, h, adj) => {
|
|
1862
|
+
const t = (adj["adj"] ?? 12500) / 1e5 * Math.min(w, h);
|
|
1863
|
+
return `<path d="M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z M ${t} ${t} L ${w - t} ${t} L ${w - t} ${h - t} L ${t} ${h - t} Z M 0 0 L ${t} ${t} M ${w} 0 L ${w - t} ${t} M ${w} ${h} L ${w - t} ${h - t} M 0 ${h} L ${t} ${h - t}"/>`;
|
|
1864
|
+
},
|
|
1865
|
+
halfFrame: (w, h, adj) => {
|
|
1866
|
+
const adjX = (adj["adj1"] ?? 33333) / 1e5 * w;
|
|
1867
|
+
const adjY = (adj["adj2"] ?? 33333) / 1e5 * h;
|
|
1868
|
+
return `<polygon points="0,0 ${w},0 ${w},${adjY} ${adjX},${adjY} ${adjX},${h} 0,${h}"/>`;
|
|
1869
|
+
},
|
|
1870
|
+
// Snip/Round corner rectangles
|
|
1871
|
+
snip1Rect: (w, h, adj) => {
|
|
1872
|
+
const d = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1873
|
+
return `<polygon points="0,0 ${w - d},0 ${w},${d} ${w},${h} 0,${h}"/>`;
|
|
1874
|
+
},
|
|
1875
|
+
snip2SameRect: (w, h, adj) => {
|
|
1876
|
+
const d = (adj["adj1"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1877
|
+
const d2 = (adj["adj2"] ?? 0) / 1e5 * Math.min(w, h);
|
|
1878
|
+
return `<polygon points="${d},0 ${w - d},0 ${w},${d} ${w},${h - d2} ${w - d2},${h} ${d2},${h} 0,${h - d2} 0,${d}"/>`;
|
|
1879
|
+
},
|
|
1880
|
+
snip2DiagRect: (w, h, adj) => {
|
|
1881
|
+
const d1 = (adj["adj1"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1882
|
+
const d2 = (adj["adj2"] ?? 0) / 1e5 * Math.min(w, h);
|
|
1883
|
+
return `<polygon points="${d1},0 ${w},0 ${w},${h - d2} ${w - d2},${h} 0,${h} 0,${d1}"/>`;
|
|
1884
|
+
},
|
|
1885
|
+
snipRoundRect: (w, h, adj) => {
|
|
1886
|
+
const r = (adj["adj1"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1887
|
+
const d = (adj["adj2"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1888
|
+
return `<path d="M ${r} 0 L ${w - d} 0 L ${w} ${d} L ${w} ${h} L 0 ${h} L 0 ${r} A ${r} ${r} 0 0 1 ${r} 0 Z"/>`;
|
|
1889
|
+
},
|
|
1890
|
+
round1Rect: (w, h, adj) => {
|
|
1891
|
+
const r = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1892
|
+
return `<path d="M 0 0 L ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h} L 0 ${h} Z"/>`;
|
|
1893
|
+
},
|
|
1894
|
+
round2SameRect: (w, h, adj) => {
|
|
1895
|
+
const r1 = (adj["adj1"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1896
|
+
const r2 = (adj["adj2"] ?? 0) / 1e5 * Math.min(w, h);
|
|
1897
|
+
return `<path d="M ${r1} 0 L ${w - r1} 0 A ${r1} ${r1} 0 0 1 ${w} ${r1} L ${w} ${h - r2} A ${r2} ${r2} 0 0 1 ${w - r2} ${h} L ${r2} ${h} A ${r2} ${r2} 0 0 1 0 ${h - r2} L 0 ${r1} A ${r1} ${r1} 0 0 1 ${r1} 0 Z"/>`;
|
|
1898
|
+
},
|
|
1899
|
+
round2DiagRect: (w, h, adj) => {
|
|
1900
|
+
const r1 = (adj["adj1"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1901
|
+
const r2 = (adj["adj2"] ?? 0) / 1e5 * Math.min(w, h);
|
|
1902
|
+
return `<path d="M ${r1} 0 L ${w} 0 L ${w} ${h - r2} A ${r2} ${r2} 0 0 1 ${w - r2} ${h} L 0 ${h} L 0 ${r1} A ${r1} ${r1} 0 0 1 ${r1} 0 Z"/>`;
|
|
1903
|
+
},
|
|
1904
|
+
// Brackets and braces
|
|
1905
|
+
leftBracket: (w, h, adj) => {
|
|
1906
|
+
const r = (adj["adj"] ?? 8333) / 1e5 * h;
|
|
1907
|
+
return `<path d="M ${w} 0 L ${r} 0 A ${r} ${r} 0 0 0 0 ${r} L 0 ${h - r} A ${r} ${r} 0 0 0 ${r} ${h} L ${w} ${h}"/>`;
|
|
1908
|
+
},
|
|
1909
|
+
rightBracket: (w, h, adj) => {
|
|
1910
|
+
const r = (adj["adj"] ?? 8333) / 1e5 * h;
|
|
1911
|
+
return `<path d="M 0 0 L ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h - r} A ${r} ${r} 0 0 1 ${w - r} ${h} L 0 ${h}"/>`;
|
|
1912
|
+
},
|
|
1913
|
+
leftBrace: (w, h, adj) => {
|
|
1914
|
+
const r = (adj["adj1"] ?? 8333) / 1e5 * h;
|
|
1915
|
+
const mid = (adj["adj2"] ?? 5e4) / 1e5 * h;
|
|
1916
|
+
return `<path d="M ${w} 0 A ${w / 2} ${r} 0 0 0 ${w / 2} ${r} L ${w / 2} ${mid - r} A ${w / 2} ${r} 0 0 1 0 ${mid} A ${w / 2} ${r} 0 0 1 ${w / 2} ${mid + r} L ${w / 2} ${h - r} A ${w / 2} ${r} 0 0 0 ${w} ${h}"/>`;
|
|
1917
|
+
},
|
|
1918
|
+
rightBrace: (w, h, adj) => {
|
|
1919
|
+
const r = (adj["adj1"] ?? 8333) / 1e5 * h;
|
|
1920
|
+
const mid = (adj["adj2"] ?? 5e4) / 1e5 * h;
|
|
1921
|
+
return `<path d="M 0 0 A ${w / 2} ${r} 0 0 1 ${w / 2} ${r} L ${w / 2} ${mid - r} A ${w / 2} ${r} 0 0 0 ${w} ${mid} A ${w / 2} ${r} 0 0 0 ${w / 2} ${mid + r} L ${w / 2} ${h - r} A ${w / 2} ${r} 0 0 1 0 ${h}"/>`;
|
|
1922
|
+
},
|
|
1923
|
+
bracketPair: (w, h, adj) => {
|
|
1924
|
+
const r = (adj["adj"] ?? 16667) / 1e5 * Math.min(w, h);
|
|
1925
|
+
return `<path d="M ${r} 0 A ${r} ${r} 0 0 0 0 ${r} L 0 ${h - r} A ${r} ${r} 0 0 0 ${r} ${h} M ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h - r} A ${r} ${r} 0 0 1 ${w - r} ${h}"/>`;
|
|
1926
|
+
},
|
|
1927
|
+
bracePair: (w, h, adj) => {
|
|
1928
|
+
const r = (adj["adj"] ?? 8333) / 1e5 * Math.min(w, h);
|
|
1929
|
+
return `<path d="M ${r} 0 A ${r} ${r} 0 0 0 0 ${r} L 0 ${h / 2 - r} A ${r} ${r} 0 0 1 ${-r} ${h / 2} A ${r} ${r} 0 0 1 0 ${h / 2 + r} L 0 ${h - r} A ${r} ${r} 0 0 0 ${r} ${h} M ${w - r} 0 A ${r} ${r} 0 0 1 ${w} ${r} L ${w} ${h / 2 - r} A ${r} ${r} 0 0 0 ${w + r} ${h / 2} A ${r} ${r} 0 0 0 ${w} ${h / 2 + r} L ${w} ${h - r} A ${r} ${r} 0 0 1 ${w - r} ${h}"/>`;
|
|
1930
|
+
},
|
|
1931
|
+
// Other misc shapes
|
|
1932
|
+
lightningBolt: (w, h) => `<polygon points="${w * 0.55},0 ${w * 0.3},${h * 0.4} ${w * 0.52},${h * 0.4} ${w * 0.25},${h} ${w * 0.75},${h * 0.5} ${w * 0.52},${h * 0.5} ${w * 0.85},0"/>`,
|
|
1933
|
+
moon: (w, h, adj) => {
|
|
1934
|
+
const t = (adj["adj"] ?? 5e4) / 1e5 * w;
|
|
1935
|
+
const rx = w / 2;
|
|
1936
|
+
const ry = h / 2;
|
|
1937
|
+
const irx = t / 2;
|
|
1938
|
+
return `<path d="M ${w} 0 A ${rx} ${ry} 0 1 0 ${w} ${h} A ${irx} ${ry} 0 1 1 ${w} 0 Z"/>`;
|
|
1939
|
+
},
|
|
1940
|
+
teardrop: (w, h, adj) => {
|
|
1941
|
+
const d = (adj["adj"] ?? 1e5) / 1e5 * Math.min(w, h) * 0.5;
|
|
1942
|
+
const rx = w / 2;
|
|
1943
|
+
const ry = h / 2;
|
|
1944
|
+
const cx = rx;
|
|
1945
|
+
const cy = ry;
|
|
1946
|
+
return `<path d="M ${cx} 0 L ${cx + d} 0 L ${w} ${cy - d + ry} A ${rx} ${ry} 0 1 1 ${cx} 0 Z"/>`;
|
|
1947
|
+
},
|
|
1948
|
+
sun: (w, h) => {
|
|
1949
|
+
const cx = w / 2;
|
|
1950
|
+
const cy = h / 2;
|
|
1951
|
+
const points = [];
|
|
1952
|
+
for (let i = 0; i < 16; i++) {
|
|
1953
|
+
const angle = Math.PI * 2 * i / 16 - Math.PI / 2;
|
|
1954
|
+
const r = i % 2 === 0 ? 1 : 0.7;
|
|
1955
|
+
points.push(`${cx + cx * r * Math.cos(angle)},${cy + cy * r * Math.sin(angle)}`);
|
|
1956
|
+
}
|
|
1957
|
+
const circleR = 0.4;
|
|
1958
|
+
return `<path d="M ${points.map((p, i) => i === 0 ? `${p}` : ` L ${p}`).join("")} Z M ${cx + cx * circleR} ${cy} A ${cx * circleR} ${cy * circleR} 0 1 0 ${cx - cx * circleR} ${cy} A ${cx * circleR} ${cy * circleR} 0 1 0 ${cx + cx * circleR} ${cy} Z"/>`;
|
|
1959
|
+
},
|
|
1960
|
+
wave: (w, h, adj) => {
|
|
1961
|
+
const dy = (adj["adj1"] ?? 12500) / 1e5 * h;
|
|
1962
|
+
const dx = (adj["adj2"] ?? 0) / 1e5 * w;
|
|
1963
|
+
return `<path d="M ${dx} ${dy} C ${dx + w * 0.25} 0, ${dx + w * 0.5} 0, ${w} ${dy} L ${w - dx} ${h - dy} C ${w - dx - w * 0.25} ${h}, ${w - dx - w * 0.5} ${h}, 0 ${h - dy} Z"/>`;
|
|
1964
|
+
},
|
|
1965
|
+
doubleWave: (w, h, adj) => {
|
|
1966
|
+
const dy = (adj["adj1"] ?? 6250) / 1e5 * h;
|
|
1967
|
+
const dx = (adj["adj2"] ?? 0) / 1e5 * w;
|
|
1968
|
+
return `<path d="M ${dx} ${dy} C ${dx + w * 0.167} 0, ${dx + w * 0.333} ${dy * 2}, ${w / 2} ${dy} C ${w / 2 + w * 0.167} 0, ${w / 2 + w * 0.333} ${dy * 2}, ${w} ${dy} L ${w - dx} ${h - dy} C ${w - dx - w * 0.167} ${h}, ${w - dx - w * 0.333} ${h - dy * 2}, ${w / 2} ${h - dy} C ${w / 2 - w * 0.167} ${h}, ${w / 2 - w * 0.333} ${h - dy * 2}, 0 ${h - dy} Z"/>`;
|
|
1969
|
+
},
|
|
1970
|
+
ribbon: (w, h, adj) => {
|
|
1971
|
+
const tabH = (adj["adj1"] ?? 16667) / 1e5 * h;
|
|
1972
|
+
const tabW = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1973
|
+
const foldW = tabW * 0.3;
|
|
1974
|
+
return `<path d="M 0 ${tabH} L ${foldW} ${tabH * 1.5} L ${foldW} ${h} L ${tabW} ${h - tabH} L ${w - tabW} ${h - tabH} L ${w - foldW} ${h} L ${w - foldW} ${tabH * 1.5} L ${w} ${tabH} L ${w} 0 L ${w - tabW} 0 L ${w - tabW} ${tabH} L ${tabW} ${tabH} L ${tabW} 0 L 0 0 Z"/>`;
|
|
1975
|
+
},
|
|
1976
|
+
ribbon2: (w, h, adj) => {
|
|
1977
|
+
const tabH = (adj["adj1"] ?? 16667) / 1e5 * h;
|
|
1978
|
+
const tabW = (adj["adj2"] ?? 5e4) / 1e5 * w;
|
|
1979
|
+
const foldW = tabW * 0.3;
|
|
1980
|
+
return `<path d="M 0 ${h - tabH} L ${foldW} ${h - tabH * 1.5} L ${foldW} 0 L ${tabW} ${tabH} L ${w - tabW} ${tabH} L ${w - foldW} 0 L ${w - foldW} ${h - tabH * 1.5} L ${w} ${h - tabH} L ${w} ${h} L ${w - tabW} ${h} L ${w - tabW} ${h - tabH} L ${tabW} ${h - tabH} L ${tabW} ${h} L 0 ${h} Z"/>`;
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
function getPresetGeometrySvg(preset, width, height, adjustValues) {
|
|
1984
|
+
const generator = presetGeometries[preset];
|
|
1985
|
+
if (generator) {
|
|
1986
|
+
return generator(width, height, adjustValues);
|
|
1987
|
+
}
|
|
1988
|
+
return `<rect width="${width}" height="${height}"/>`;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// src/renderer/geometry/geometry-renderer.ts
|
|
1992
|
+
function renderGeometry(geometry, width, height) {
|
|
1993
|
+
if (geometry.type === "preset") {
|
|
1994
|
+
return getPresetGeometrySvg(geometry.preset, width, height, geometry.adjustValues);
|
|
1995
|
+
}
|
|
1996
|
+
if (geometry.type === "custom" && geometry.pathData) {
|
|
1997
|
+
return `<path d="${geometry.pathData}"/>`;
|
|
1998
|
+
}
|
|
1999
|
+
return `<rect width="${width}" height="${height}"/>`;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/renderer/fill-renderer.ts
|
|
2003
|
+
function renderFillAttrs(fill) {
|
|
2004
|
+
if (!fill || fill.type === "none") {
|
|
2005
|
+
return { attrs: `fill="none"`, defs: "" };
|
|
2006
|
+
}
|
|
2007
|
+
if (fill.type === "solid") {
|
|
2008
|
+
const alphaAttr = fill.color.alpha < 1 ? ` fill-opacity="${fill.color.alpha}"` : "";
|
|
2009
|
+
return { attrs: `fill="${fill.color.hex}"${alphaAttr}`, defs: "" };
|
|
2010
|
+
}
|
|
2011
|
+
if (fill.type === "gradient") {
|
|
2012
|
+
const id = `grad-${crypto.randomUUID()}`;
|
|
2013
|
+
const angle = fill.angle;
|
|
2014
|
+
const rad = angle * Math.PI / 180;
|
|
2015
|
+
const x1 = 50 - Math.cos(rad) * 50;
|
|
2016
|
+
const y1 = 50 - Math.sin(rad) * 50;
|
|
2017
|
+
const x2 = 50 + Math.cos(rad) * 50;
|
|
2018
|
+
const y2 = 50 + Math.sin(rad) * 50;
|
|
2019
|
+
const stops = fill.stops.map((s) => {
|
|
2020
|
+
const opacityAttr = s.color.alpha < 1 ? ` stop-opacity="${s.color.alpha}"` : "";
|
|
2021
|
+
return `<stop offset="${s.position * 100}%" stop-color="${s.color.hex}"${opacityAttr}/>`;
|
|
2022
|
+
}).join("");
|
|
2023
|
+
const defs = `<linearGradient id="${id}" x1="${x1}%" y1="${y1}%" x2="${x2}%" y2="${y2}%">${stops}</linearGradient>`;
|
|
2024
|
+
return { attrs: `fill="url(#${id})"`, defs };
|
|
2025
|
+
}
|
|
2026
|
+
return { attrs: `fill="none"`, defs: "" };
|
|
2027
|
+
}
|
|
2028
|
+
function renderOutlineAttrs(outline) {
|
|
2029
|
+
if (!outline) return `stroke="none"`;
|
|
2030
|
+
const widthPx = emuToPixels(outline.width);
|
|
2031
|
+
const parts = [`stroke-width="${widthPx}"`];
|
|
2032
|
+
if (outline.fill) {
|
|
2033
|
+
parts.push(`stroke="${outline.fill.color.hex}"`);
|
|
2034
|
+
if (outline.fill.color.alpha < 1) {
|
|
2035
|
+
parts.push(`stroke-opacity="${outline.fill.color.alpha}"`);
|
|
2036
|
+
}
|
|
2037
|
+
} else {
|
|
2038
|
+
parts.push(`stroke="none"`);
|
|
2039
|
+
}
|
|
2040
|
+
if (outline.dashStyle !== "solid") {
|
|
2041
|
+
const dashArray = getDashArray(outline.dashStyle, widthPx);
|
|
2042
|
+
if (dashArray) {
|
|
2043
|
+
parts.push(`stroke-dasharray="${dashArray}"`);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
return parts.join(" ");
|
|
2047
|
+
}
|
|
2048
|
+
function getDashArray(style, w) {
|
|
2049
|
+
const patterns = {
|
|
2050
|
+
dash: [4, 3],
|
|
2051
|
+
dot: [1, 3],
|
|
2052
|
+
dashDot: [4, 3, 1, 3],
|
|
2053
|
+
lgDash: [8, 3],
|
|
2054
|
+
lgDashDot: [8, 3, 1, 3],
|
|
2055
|
+
sysDash: [3, 1],
|
|
2056
|
+
sysDot: [1, 1]
|
|
2057
|
+
};
|
|
2058
|
+
const pattern = patterns[style];
|
|
2059
|
+
if (!pattern) return null;
|
|
2060
|
+
return pattern.map((v) => v * w).join(" ");
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
// src/data/font-metrics.ts
|
|
2064
|
+
var metricsData = {
|
|
2065
|
+
Carlito: {
|
|
2066
|
+
unitsPerEm: 2048,
|
|
2067
|
+
ascender: 1950,
|
|
2068
|
+
descender: -550,
|
|
2069
|
+
defaultWidth: 991,
|
|
2070
|
+
cjkWidth: 2048,
|
|
2071
|
+
widths: {
|
|
2072
|
+
" ": 463,
|
|
2073
|
+
"!": 667,
|
|
2074
|
+
'"': 821,
|
|
2075
|
+
"#": 1020,
|
|
2076
|
+
"$": 1038,
|
|
2077
|
+
"%": 1464,
|
|
2078
|
+
"&": 1397,
|
|
2079
|
+
"'": 452,
|
|
2080
|
+
"(": 621,
|
|
2081
|
+
")": 621,
|
|
2082
|
+
"*": 1020,
|
|
2083
|
+
"+": 1020,
|
|
2084
|
+
",": 511,
|
|
2085
|
+
"-": 627,
|
|
2086
|
+
".": 517,
|
|
2087
|
+
"/": 791,
|
|
2088
|
+
"0": 1038,
|
|
2089
|
+
"1": 1038,
|
|
2090
|
+
"2": 1038,
|
|
2091
|
+
"3": 1038,
|
|
2092
|
+
"4": 1038,
|
|
2093
|
+
"5": 1038,
|
|
2094
|
+
"6": 1038,
|
|
2095
|
+
"7": 1038,
|
|
2096
|
+
"8": 1038,
|
|
2097
|
+
"9": 1038,
|
|
2098
|
+
":": 548,
|
|
2099
|
+
";": 548,
|
|
2100
|
+
"<": 1020,
|
|
2101
|
+
"=": 1020,
|
|
2102
|
+
">": 1020,
|
|
2103
|
+
"?": 949,
|
|
2104
|
+
"@": 1831,
|
|
2105
|
+
"A": 1185,
|
|
2106
|
+
"B": 1114,
|
|
2107
|
+
"C": 1092,
|
|
2108
|
+
"D": 1260,
|
|
2109
|
+
"E": 1e3,
|
|
2110
|
+
"F": 941,
|
|
2111
|
+
"G": 1292,
|
|
2112
|
+
"H": 1276,
|
|
2113
|
+
"I": 516,
|
|
2114
|
+
"J": 653,
|
|
2115
|
+
"K": 1064,
|
|
2116
|
+
"L": 861,
|
|
2117
|
+
"M": 1751,
|
|
2118
|
+
"N": 1322,
|
|
2119
|
+
"O": 1356,
|
|
2120
|
+
"P": 1058,
|
|
2121
|
+
"Q": 1378,
|
|
2122
|
+
"R": 1112,
|
|
2123
|
+
"S": 941,
|
|
2124
|
+
"T": 998,
|
|
2125
|
+
"U": 1314,
|
|
2126
|
+
"V": 1162,
|
|
2127
|
+
"W": 1822,
|
|
2128
|
+
"X": 1063,
|
|
2129
|
+
"Y": 998,
|
|
2130
|
+
"Z": 959,
|
|
2131
|
+
"[": 628,
|
|
2132
|
+
"\\": 791,
|
|
2133
|
+
"]": 628,
|
|
2134
|
+
"^": 1020,
|
|
2135
|
+
"_": 1020,
|
|
2136
|
+
"`": 596,
|
|
2137
|
+
"a": 981,
|
|
2138
|
+
"b": 1076,
|
|
2139
|
+
"c": 866,
|
|
2140
|
+
"d": 1076,
|
|
2141
|
+
"e": 1019,
|
|
2142
|
+
"f": 625,
|
|
2143
|
+
"g": 964,
|
|
2144
|
+
"h": 1076,
|
|
2145
|
+
"i": 470,
|
|
2146
|
+
"j": 490,
|
|
2147
|
+
"k": 931,
|
|
2148
|
+
"l": 470,
|
|
2149
|
+
"m": 1636,
|
|
2150
|
+
"n": 1076,
|
|
2151
|
+
"o": 1080,
|
|
2152
|
+
"p": 1076,
|
|
2153
|
+
"q": 1076,
|
|
2154
|
+
"r": 714,
|
|
2155
|
+
"s": 801,
|
|
2156
|
+
"t": 686,
|
|
2157
|
+
"u": 1076,
|
|
2158
|
+
"v": 925,
|
|
2159
|
+
"w": 1464,
|
|
2160
|
+
"x": 887,
|
|
2161
|
+
"y": 927,
|
|
2162
|
+
"z": 809,
|
|
2163
|
+
"{": 644,
|
|
2164
|
+
"|": 943,
|
|
2165
|
+
"}": 644,
|
|
2166
|
+
"~": 1020,
|
|
2167
|
+
"\xA0": 463,
|
|
2168
|
+
"\xA1": 667,
|
|
2169
|
+
"\xA2": 1020,
|
|
2170
|
+
"\xA3": 1038,
|
|
2171
|
+
"\xA4": 1020,
|
|
2172
|
+
"\xA5": 1038,
|
|
2173
|
+
"\xA6": 1020,
|
|
2174
|
+
"\xA7": 1020,
|
|
2175
|
+
"\xA8": 804,
|
|
2176
|
+
"\xA9": 1709,
|
|
2177
|
+
"\xAA": 824,
|
|
2178
|
+
"\xAB": 1049,
|
|
2179
|
+
"\xAC": 1020,
|
|
2180
|
+
"\xAE": 1038,
|
|
2181
|
+
"\xAF": 807,
|
|
2182
|
+
"\xB0": 694,
|
|
2183
|
+
"\xB1": 1020,
|
|
2184
|
+
"\xB2": 688,
|
|
2185
|
+
"\xB3": 685,
|
|
2186
|
+
"\xB4": 598,
|
|
2187
|
+
"\xB5": 1126,
|
|
2188
|
+
"\xB6": 1200,
|
|
2189
|
+
"\xB7": 517,
|
|
2190
|
+
"\xB8": 629,
|
|
2191
|
+
"\xB9": 504,
|
|
2192
|
+
"\xBA": 865,
|
|
2193
|
+
"\xBB": 1049,
|
|
2194
|
+
"\xBC": 1303,
|
|
2195
|
+
"\xBD": 1375,
|
|
2196
|
+
"\xBE": 1383,
|
|
2197
|
+
"\xBF": 949,
|
|
2198
|
+
"\xC0": 1185,
|
|
2199
|
+
"\xC1": 1185,
|
|
2200
|
+
"\xC2": 1185,
|
|
2201
|
+
"\xC3": 1185,
|
|
2202
|
+
"\xC4": 1185,
|
|
2203
|
+
"\xC5": 1185,
|
|
2204
|
+
"\xC6": 1563,
|
|
2205
|
+
"\xC7": 1092,
|
|
2206
|
+
"\xC8": 1e3,
|
|
2207
|
+
"\xC9": 1e3,
|
|
2208
|
+
"\xCA": 1e3,
|
|
2209
|
+
"\xCB": 1e3,
|
|
2210
|
+
"\xCC": 516,
|
|
2211
|
+
"\xCD": 516,
|
|
2212
|
+
"\xCE": 516,
|
|
2213
|
+
"\xCF": 516,
|
|
2214
|
+
"\xD0": 1279,
|
|
2215
|
+
"\xD1": 1322,
|
|
2216
|
+
"\xD2": 1356,
|
|
2217
|
+
"\xD3": 1356,
|
|
2218
|
+
"\xD4": 1356,
|
|
2219
|
+
"\xD5": 1356,
|
|
2220
|
+
"\xD6": 1356,
|
|
2221
|
+
"\xD7": 1020,
|
|
2222
|
+
"\xD8": 1359,
|
|
2223
|
+
"\xD9": 1314,
|
|
2224
|
+
"\xDA": 1314,
|
|
2225
|
+
"\xDB": 1314,
|
|
2226
|
+
"\xDC": 1314,
|
|
2227
|
+
"\xDD": 998,
|
|
2228
|
+
"\xDE": 1058,
|
|
2229
|
+
"\xDF": 1080,
|
|
2230
|
+
"\xE0": 981,
|
|
2231
|
+
"\xE1": 981,
|
|
2232
|
+
"\xE2": 981,
|
|
2233
|
+
"\xE3": 981,
|
|
2234
|
+
"\xE4": 981,
|
|
2235
|
+
"\xE5": 981,
|
|
2236
|
+
"\xE6": 1583,
|
|
2237
|
+
"\xE7": 866,
|
|
2238
|
+
"\xE8": 1019,
|
|
2239
|
+
"\xE9": 1019,
|
|
2240
|
+
"\xEA": 1019,
|
|
2241
|
+
"\xEB": 1019,
|
|
2242
|
+
"\xEC": 470,
|
|
2243
|
+
"\xED": 470,
|
|
2244
|
+
"\xEE": 470,
|
|
2245
|
+
"\xEF": 470,
|
|
2246
|
+
"\xF0": 1076,
|
|
2247
|
+
"\xF1": 1076,
|
|
2248
|
+
"\xF2": 1080,
|
|
2249
|
+
"\xF3": 1080,
|
|
2250
|
+
"\xF4": 1080,
|
|
2251
|
+
"\xF5": 1080,
|
|
2252
|
+
"\xF6": 1080,
|
|
2253
|
+
"\xF7": 1020,
|
|
2254
|
+
"\xF8": 1084,
|
|
2255
|
+
"\xF9": 1076,
|
|
2256
|
+
"\xFA": 1076,
|
|
2257
|
+
"\xFB": 1076,
|
|
2258
|
+
"\xFC": 1076,
|
|
2259
|
+
"\xFD": 927,
|
|
2260
|
+
"\xFE": 1076,
|
|
2261
|
+
"\xFF": 927
|
|
2262
|
+
}
|
|
2263
|
+
},
|
|
2264
|
+
LiberationSans: {
|
|
2265
|
+
unitsPerEm: 2048,
|
|
2266
|
+
ascender: 1854,
|
|
2267
|
+
descender: -434,
|
|
2268
|
+
defaultWidth: 1114,
|
|
2269
|
+
cjkWidth: 2048,
|
|
2270
|
+
widths: {
|
|
2271
|
+
" ": 569,
|
|
2272
|
+
"!": 569,
|
|
2273
|
+
'"': 727,
|
|
2274
|
+
"#": 1139,
|
|
2275
|
+
"$": 1139,
|
|
2276
|
+
"%": 1821,
|
|
2277
|
+
"&": 1366,
|
|
2278
|
+
"'": 391,
|
|
2279
|
+
"(": 682,
|
|
2280
|
+
")": 682,
|
|
2281
|
+
"*": 797,
|
|
2282
|
+
"+": 1196,
|
|
2283
|
+
",": 569,
|
|
2284
|
+
"-": 682,
|
|
2285
|
+
".": 569,
|
|
2286
|
+
"/": 569,
|
|
2287
|
+
"0": 1139,
|
|
2288
|
+
"1": 1139,
|
|
2289
|
+
"2": 1139,
|
|
2290
|
+
"3": 1139,
|
|
2291
|
+
"4": 1139,
|
|
2292
|
+
"5": 1139,
|
|
2293
|
+
"6": 1139,
|
|
2294
|
+
"7": 1139,
|
|
2295
|
+
"8": 1139,
|
|
2296
|
+
"9": 1139,
|
|
2297
|
+
":": 569,
|
|
2298
|
+
";": 569,
|
|
2299
|
+
"<": 1196,
|
|
2300
|
+
"=": 1196,
|
|
2301
|
+
">": 1196,
|
|
2302
|
+
"?": 1139,
|
|
2303
|
+
"@": 2079,
|
|
2304
|
+
"A": 1366,
|
|
2305
|
+
"B": 1366,
|
|
2306
|
+
"C": 1479,
|
|
2307
|
+
"D": 1479,
|
|
2308
|
+
"E": 1366,
|
|
2309
|
+
"F": 1251,
|
|
2310
|
+
"G": 1593,
|
|
2311
|
+
"H": 1479,
|
|
2312
|
+
"I": 569,
|
|
2313
|
+
"J": 1024,
|
|
2314
|
+
"K": 1366,
|
|
2315
|
+
"L": 1139,
|
|
2316
|
+
"M": 1706,
|
|
2317
|
+
"N": 1479,
|
|
2318
|
+
"O": 1593,
|
|
2319
|
+
"P": 1366,
|
|
2320
|
+
"Q": 1593,
|
|
2321
|
+
"R": 1479,
|
|
2322
|
+
"S": 1366,
|
|
2323
|
+
"T": 1251,
|
|
2324
|
+
"U": 1479,
|
|
2325
|
+
"V": 1366,
|
|
2326
|
+
"W": 1933,
|
|
2327
|
+
"X": 1366,
|
|
2328
|
+
"Y": 1366,
|
|
2329
|
+
"Z": 1251,
|
|
2330
|
+
"[": 569,
|
|
2331
|
+
"\\": 569,
|
|
2332
|
+
"]": 569,
|
|
2333
|
+
"^": 961,
|
|
2334
|
+
"_": 1139,
|
|
2335
|
+
"`": 682,
|
|
2336
|
+
"a": 1139,
|
|
2337
|
+
"b": 1139,
|
|
2338
|
+
"c": 1024,
|
|
2339
|
+
"d": 1139,
|
|
2340
|
+
"e": 1139,
|
|
2341
|
+
"f": 569,
|
|
2342
|
+
"g": 1139,
|
|
2343
|
+
"h": 1139,
|
|
2344
|
+
"i": 455,
|
|
2345
|
+
"j": 455,
|
|
2346
|
+
"k": 1024,
|
|
2347
|
+
"l": 455,
|
|
2348
|
+
"m": 1706,
|
|
2349
|
+
"n": 1139,
|
|
2350
|
+
"o": 1139,
|
|
2351
|
+
"p": 1139,
|
|
2352
|
+
"q": 1139,
|
|
2353
|
+
"r": 682,
|
|
2354
|
+
"s": 1024,
|
|
2355
|
+
"t": 569,
|
|
2356
|
+
"u": 1139,
|
|
2357
|
+
"v": 1024,
|
|
2358
|
+
"w": 1479,
|
|
2359
|
+
"x": 1024,
|
|
2360
|
+
"y": 1024,
|
|
2361
|
+
"z": 1024,
|
|
2362
|
+
"{": 684,
|
|
2363
|
+
"|": 532,
|
|
2364
|
+
"}": 684,
|
|
2365
|
+
"~": 1196,
|
|
2366
|
+
"\xA0": 569,
|
|
2367
|
+
"\xA1": 682,
|
|
2368
|
+
"\xA2": 1139,
|
|
2369
|
+
"\xA3": 1139,
|
|
2370
|
+
"\xA4": 1139,
|
|
2371
|
+
"\xA5": 1139,
|
|
2372
|
+
"\xA6": 532,
|
|
2373
|
+
"\xA7": 1139,
|
|
2374
|
+
"\xA8": 682,
|
|
2375
|
+
"\xA9": 1509,
|
|
2376
|
+
"\xAA": 758,
|
|
2377
|
+
"\xAB": 1139,
|
|
2378
|
+
"\xAC": 1196,
|
|
2379
|
+
"\xAD": 682,
|
|
2380
|
+
"\xAE": 1509,
|
|
2381
|
+
"\xAF": 1131,
|
|
2382
|
+
"\xB0": 819,
|
|
2383
|
+
"\xB1": 1124,
|
|
2384
|
+
"\xB2": 682,
|
|
2385
|
+
"\xB3": 682,
|
|
2386
|
+
"\xB4": 682,
|
|
2387
|
+
"\xB5": 1180,
|
|
2388
|
+
"\xB6": 1100,
|
|
2389
|
+
"\xB7": 682,
|
|
2390
|
+
"\xB8": 682,
|
|
2391
|
+
"\xB9": 682,
|
|
2392
|
+
"\xBA": 748,
|
|
2393
|
+
"\xBB": 1139,
|
|
2394
|
+
"\xBC": 1708,
|
|
2395
|
+
"\xBD": 1708,
|
|
2396
|
+
"\xBE": 1708,
|
|
2397
|
+
"\xBF": 1251,
|
|
2398
|
+
"\xC0": 1366,
|
|
2399
|
+
"\xC1": 1366,
|
|
2400
|
+
"\xC2": 1366,
|
|
2401
|
+
"\xC3": 1366,
|
|
2402
|
+
"\xC4": 1366,
|
|
2403
|
+
"\xC5": 1366,
|
|
2404
|
+
"\xC6": 2048,
|
|
2405
|
+
"\xC7": 1479,
|
|
2406
|
+
"\xC8": 1366,
|
|
2407
|
+
"\xC9": 1366,
|
|
2408
|
+
"\xCA": 1366,
|
|
2409
|
+
"\xCB": 1366,
|
|
2410
|
+
"\xCC": 569,
|
|
2411
|
+
"\xCD": 569,
|
|
2412
|
+
"\xCE": 569,
|
|
2413
|
+
"\xCF": 569,
|
|
2414
|
+
"\xD0": 1479,
|
|
2415
|
+
"\xD1": 1479,
|
|
2416
|
+
"\xD2": 1593,
|
|
2417
|
+
"\xD3": 1593,
|
|
2418
|
+
"\xD4": 1593,
|
|
2419
|
+
"\xD5": 1593,
|
|
2420
|
+
"\xD6": 1593,
|
|
2421
|
+
"\xD7": 1196,
|
|
2422
|
+
"\xD8": 1593,
|
|
2423
|
+
"\xD9": 1479,
|
|
2424
|
+
"\xDA": 1479,
|
|
2425
|
+
"\xDB": 1479,
|
|
2426
|
+
"\xDC": 1479,
|
|
2427
|
+
"\xDD": 1366,
|
|
2428
|
+
"\xDE": 1366,
|
|
2429
|
+
"\xDF": 1251,
|
|
2430
|
+
"\xE0": 1139,
|
|
2431
|
+
"\xE1": 1139,
|
|
2432
|
+
"\xE2": 1139,
|
|
2433
|
+
"\xE3": 1139,
|
|
2434
|
+
"\xE4": 1139,
|
|
2435
|
+
"\xE5": 1139,
|
|
2436
|
+
"\xE6": 1821,
|
|
2437
|
+
"\xE7": 1024,
|
|
2438
|
+
"\xE8": 1139,
|
|
2439
|
+
"\xE9": 1139,
|
|
2440
|
+
"\xEA": 1139,
|
|
2441
|
+
"\xEB": 1139,
|
|
2442
|
+
"\xEC": 569,
|
|
2443
|
+
"\xED": 569,
|
|
2444
|
+
"\xEE": 569,
|
|
2445
|
+
"\xEF": 569,
|
|
2446
|
+
"\xF0": 1139,
|
|
2447
|
+
"\xF1": 1139,
|
|
2448
|
+
"\xF2": 1139,
|
|
2449
|
+
"\xF3": 1139,
|
|
2450
|
+
"\xF4": 1139,
|
|
2451
|
+
"\xF5": 1139,
|
|
2452
|
+
"\xF6": 1139,
|
|
2453
|
+
"\xF7": 1124,
|
|
2454
|
+
"\xF8": 1251,
|
|
2455
|
+
"\xF9": 1139,
|
|
2456
|
+
"\xFA": 1139,
|
|
2457
|
+
"\xFB": 1139,
|
|
2458
|
+
"\xFC": 1139,
|
|
2459
|
+
"\xFD": 1024,
|
|
2460
|
+
"\xFE": 1139,
|
|
2461
|
+
"\xFF": 1024
|
|
2462
|
+
}
|
|
2463
|
+
},
|
|
2464
|
+
LiberationSerif: {
|
|
2465
|
+
unitsPerEm: 2048,
|
|
2466
|
+
ascender: 1825,
|
|
2467
|
+
descender: -443,
|
|
2468
|
+
defaultWidth: 1056,
|
|
2469
|
+
cjkWidth: 2048,
|
|
2470
|
+
widths: {
|
|
2471
|
+
" ": 512,
|
|
2472
|
+
"!": 682,
|
|
2473
|
+
'"': 836,
|
|
2474
|
+
"#": 1024,
|
|
2475
|
+
"$": 1024,
|
|
2476
|
+
"%": 1706,
|
|
2477
|
+
"&": 1593,
|
|
2478
|
+
"'": 369,
|
|
2479
|
+
"(": 682,
|
|
2480
|
+
")": 682,
|
|
2481
|
+
"*": 1024,
|
|
2482
|
+
"+": 1155,
|
|
2483
|
+
",": 512,
|
|
2484
|
+
"-": 682,
|
|
2485
|
+
".": 512,
|
|
2486
|
+
"/": 569,
|
|
2487
|
+
"0": 1024,
|
|
2488
|
+
"1": 1024,
|
|
2489
|
+
"2": 1024,
|
|
2490
|
+
"3": 1024,
|
|
2491
|
+
"4": 1024,
|
|
2492
|
+
"5": 1024,
|
|
2493
|
+
"6": 1024,
|
|
2494
|
+
"7": 1024,
|
|
2495
|
+
"8": 1024,
|
|
2496
|
+
"9": 1024,
|
|
2497
|
+
":": 569,
|
|
2498
|
+
";": 569,
|
|
2499
|
+
"<": 1155,
|
|
2500
|
+
"=": 1155,
|
|
2501
|
+
">": 1155,
|
|
2502
|
+
"?": 909,
|
|
2503
|
+
"@": 1886,
|
|
2504
|
+
"A": 1479,
|
|
2505
|
+
"B": 1366,
|
|
2506
|
+
"C": 1366,
|
|
2507
|
+
"D": 1479,
|
|
2508
|
+
"E": 1251,
|
|
2509
|
+
"F": 1139,
|
|
2510
|
+
"G": 1479,
|
|
2511
|
+
"H": 1479,
|
|
2512
|
+
"I": 682,
|
|
2513
|
+
"J": 797,
|
|
2514
|
+
"K": 1479,
|
|
2515
|
+
"L": 1251,
|
|
2516
|
+
"M": 1821,
|
|
2517
|
+
"N": 1479,
|
|
2518
|
+
"O": 1479,
|
|
2519
|
+
"P": 1139,
|
|
2520
|
+
"Q": 1479,
|
|
2521
|
+
"R": 1366,
|
|
2522
|
+
"S": 1139,
|
|
2523
|
+
"T": 1251,
|
|
2524
|
+
"U": 1479,
|
|
2525
|
+
"V": 1479,
|
|
2526
|
+
"W": 1933,
|
|
2527
|
+
"X": 1479,
|
|
2528
|
+
"Y": 1479,
|
|
2529
|
+
"Z": 1251,
|
|
2530
|
+
"[": 682,
|
|
2531
|
+
"\\": 569,
|
|
2532
|
+
"]": 682,
|
|
2533
|
+
"^": 961,
|
|
2534
|
+
"_": 1024,
|
|
2535
|
+
"`": 682,
|
|
2536
|
+
"a": 909,
|
|
2537
|
+
"b": 1024,
|
|
2538
|
+
"c": 909,
|
|
2539
|
+
"d": 1024,
|
|
2540
|
+
"e": 909,
|
|
2541
|
+
"f": 682,
|
|
2542
|
+
"g": 1024,
|
|
2543
|
+
"h": 1024,
|
|
2544
|
+
"i": 569,
|
|
2545
|
+
"j": 569,
|
|
2546
|
+
"k": 1024,
|
|
2547
|
+
"l": 569,
|
|
2548
|
+
"m": 1593,
|
|
2549
|
+
"n": 1024,
|
|
2550
|
+
"o": 1024,
|
|
2551
|
+
"p": 1024,
|
|
2552
|
+
"q": 1024,
|
|
2553
|
+
"r": 682,
|
|
2554
|
+
"s": 797,
|
|
2555
|
+
"t": 569,
|
|
2556
|
+
"u": 1024,
|
|
2557
|
+
"v": 1024,
|
|
2558
|
+
"w": 1479,
|
|
2559
|
+
"x": 1024,
|
|
2560
|
+
"y": 1024,
|
|
2561
|
+
"z": 909,
|
|
2562
|
+
"{": 983,
|
|
2563
|
+
"|": 410,
|
|
2564
|
+
"}": 983,
|
|
2565
|
+
"~": 1108,
|
|
2566
|
+
"\xA0": 512,
|
|
2567
|
+
"\xA1": 682,
|
|
2568
|
+
"\xA2": 1024,
|
|
2569
|
+
"\xA3": 1024,
|
|
2570
|
+
"\xA4": 1024,
|
|
2571
|
+
"\xA5": 1024,
|
|
2572
|
+
"\xA6": 410,
|
|
2573
|
+
"\xA7": 1024,
|
|
2574
|
+
"\xA8": 682,
|
|
2575
|
+
"\xA9": 1556,
|
|
2576
|
+
"\xAA": 565,
|
|
2577
|
+
"\xAB": 1024,
|
|
2578
|
+
"\xAC": 1155,
|
|
2579
|
+
"\xAD": 682,
|
|
2580
|
+
"\xAE": 1556,
|
|
2581
|
+
"\xAF": 1024,
|
|
2582
|
+
"\xB0": 819,
|
|
2583
|
+
"\xB1": 1124,
|
|
2584
|
+
"\xB2": 614,
|
|
2585
|
+
"\xB3": 614,
|
|
2586
|
+
"\xB4": 682,
|
|
2587
|
+
"\xB5": 1180,
|
|
2588
|
+
"\xB6": 928,
|
|
2589
|
+
"\xB7": 682,
|
|
2590
|
+
"\xB8": 682,
|
|
2591
|
+
"\xB9": 614,
|
|
2592
|
+
"\xBA": 635,
|
|
2593
|
+
"\xBB": 1024,
|
|
2594
|
+
"\xBC": 1536,
|
|
2595
|
+
"\xBD": 1536,
|
|
2596
|
+
"\xBE": 1536,
|
|
2597
|
+
"\xBF": 909,
|
|
2598
|
+
"\xC0": 1479,
|
|
2599
|
+
"\xC1": 1479,
|
|
2600
|
+
"\xC2": 1479,
|
|
2601
|
+
"\xC3": 1479,
|
|
2602
|
+
"\xC4": 1479,
|
|
2603
|
+
"\xC5": 1479,
|
|
2604
|
+
"\xC6": 1821,
|
|
2605
|
+
"\xC7": 1366,
|
|
2606
|
+
"\xC8": 1251,
|
|
2607
|
+
"\xC9": 1251,
|
|
2608
|
+
"\xCA": 1251,
|
|
2609
|
+
"\xCB": 1251,
|
|
2610
|
+
"\xCC": 682,
|
|
2611
|
+
"\xCD": 682,
|
|
2612
|
+
"\xCE": 682,
|
|
2613
|
+
"\xCF": 682,
|
|
2614
|
+
"\xD0": 1479,
|
|
2615
|
+
"\xD1": 1479,
|
|
2616
|
+
"\xD2": 1479,
|
|
2617
|
+
"\xD3": 1479,
|
|
2618
|
+
"\xD4": 1479,
|
|
2619
|
+
"\xD5": 1479,
|
|
2620
|
+
"\xD6": 1479,
|
|
2621
|
+
"\xD7": 1155,
|
|
2622
|
+
"\xD8": 1479,
|
|
2623
|
+
"\xD9": 1479,
|
|
2624
|
+
"\xDA": 1479,
|
|
2625
|
+
"\xDB": 1479,
|
|
2626
|
+
"\xDC": 1479,
|
|
2627
|
+
"\xDD": 1479,
|
|
2628
|
+
"\xDE": 1139,
|
|
2629
|
+
"\xDF": 1024,
|
|
2630
|
+
"\xE0": 909,
|
|
2631
|
+
"\xE1": 909,
|
|
2632
|
+
"\xE2": 909,
|
|
2633
|
+
"\xE3": 909,
|
|
2634
|
+
"\xE4": 909,
|
|
2635
|
+
"\xE5": 909,
|
|
2636
|
+
"\xE6": 1366,
|
|
2637
|
+
"\xE7": 909,
|
|
2638
|
+
"\xE8": 909,
|
|
2639
|
+
"\xE9": 909,
|
|
2640
|
+
"\xEA": 909,
|
|
2641
|
+
"\xEB": 909,
|
|
2642
|
+
"\xEC": 569,
|
|
2643
|
+
"\xED": 569,
|
|
2644
|
+
"\xEE": 569,
|
|
2645
|
+
"\xEF": 569,
|
|
2646
|
+
"\xF0": 1024,
|
|
2647
|
+
"\xF1": 1024,
|
|
2648
|
+
"\xF2": 1024,
|
|
2649
|
+
"\xF3": 1024,
|
|
2650
|
+
"\xF4": 1024,
|
|
2651
|
+
"\xF5": 1024,
|
|
2652
|
+
"\xF6": 1024,
|
|
2653
|
+
"\xF7": 1124,
|
|
2654
|
+
"\xF8": 1024,
|
|
2655
|
+
"\xF9": 1024,
|
|
2656
|
+
"\xFA": 1024,
|
|
2657
|
+
"\xFB": 1024,
|
|
2658
|
+
"\xFC": 1024,
|
|
2659
|
+
"\xFD": 1024,
|
|
2660
|
+
"\xFE": 1024,
|
|
2661
|
+
"\xFF": 1024
|
|
2662
|
+
}
|
|
2663
|
+
},
|
|
2664
|
+
NotoSansJP: {
|
|
2665
|
+
unitsPerEm: 1e3,
|
|
2666
|
+
ascender: 1160,
|
|
2667
|
+
descender: -288,
|
|
2668
|
+
defaultWidth: 563,
|
|
2669
|
+
cjkWidth: 1e3,
|
|
2670
|
+
widths: {
|
|
2671
|
+
" ": 224,
|
|
2672
|
+
"!": 323,
|
|
2673
|
+
'"': 474,
|
|
2674
|
+
"#": 555,
|
|
2675
|
+
"$": 555,
|
|
2676
|
+
"%": 921,
|
|
2677
|
+
"&": 680,
|
|
2678
|
+
"'": 278,
|
|
2679
|
+
"(": 338,
|
|
2680
|
+
")": 338,
|
|
2681
|
+
"*": 467,
|
|
2682
|
+
"+": 555,
|
|
2683
|
+
",": 278,
|
|
2684
|
+
"-": 347,
|
|
2685
|
+
".": 278,
|
|
2686
|
+
"/": 392,
|
|
2687
|
+
"0": 555,
|
|
2688
|
+
"1": 555,
|
|
2689
|
+
"2": 555,
|
|
2690
|
+
"3": 555,
|
|
2691
|
+
"4": 555,
|
|
2692
|
+
"5": 555,
|
|
2693
|
+
"6": 555,
|
|
2694
|
+
"7": 555,
|
|
2695
|
+
"8": 555,
|
|
2696
|
+
"9": 555,
|
|
2697
|
+
":": 278,
|
|
2698
|
+
";": 278,
|
|
2699
|
+
"<": 555,
|
|
2700
|
+
"=": 555,
|
|
2701
|
+
">": 555,
|
|
2702
|
+
"?": 474,
|
|
2703
|
+
"@": 946,
|
|
2704
|
+
"A": 608,
|
|
2705
|
+
"B": 657,
|
|
2706
|
+
"C": 638,
|
|
2707
|
+
"D": 688,
|
|
2708
|
+
"E": 589,
|
|
2709
|
+
"F": 552,
|
|
2710
|
+
"G": 689,
|
|
2711
|
+
"H": 728,
|
|
2712
|
+
"I": 293,
|
|
2713
|
+
"J": 535,
|
|
2714
|
+
"K": 646,
|
|
2715
|
+
"L": 543,
|
|
2716
|
+
"M": 812,
|
|
2717
|
+
"N": 723,
|
|
2718
|
+
"O": 742,
|
|
2719
|
+
"P": 633,
|
|
2720
|
+
"Q": 742,
|
|
2721
|
+
"R": 635,
|
|
2722
|
+
"S": 596,
|
|
2723
|
+
"T": 599,
|
|
2724
|
+
"U": 721,
|
|
2725
|
+
"V": 575,
|
|
2726
|
+
"W": 878,
|
|
2727
|
+
"X": 573,
|
|
2728
|
+
"Y": 531,
|
|
2729
|
+
"Z": 603,
|
|
2730
|
+
"[": 338,
|
|
2731
|
+
"\\": 392,
|
|
2732
|
+
"]": 338,
|
|
2733
|
+
"^": 555,
|
|
2734
|
+
"_": 559,
|
|
2735
|
+
"`": 606,
|
|
2736
|
+
"a": 563,
|
|
2737
|
+
"b": 618,
|
|
2738
|
+
"c": 510,
|
|
2739
|
+
"d": 620,
|
|
2740
|
+
"e": 554,
|
|
2741
|
+
"f": 325,
|
|
2742
|
+
"g": 564,
|
|
2743
|
+
"h": 607,
|
|
2744
|
+
"i": 275,
|
|
2745
|
+
"j": 275,
|
|
2746
|
+
"k": 552,
|
|
2747
|
+
"l": 284,
|
|
2748
|
+
"m": 926,
|
|
2749
|
+
"n": 610,
|
|
2750
|
+
"o": 606,
|
|
2751
|
+
"p": 620,
|
|
2752
|
+
"q": 620,
|
|
2753
|
+
"r": 388,
|
|
2754
|
+
"s": 468,
|
|
2755
|
+
"t": 377,
|
|
2756
|
+
"u": 607,
|
|
2757
|
+
"v": 521,
|
|
2758
|
+
"w": 802,
|
|
2759
|
+
"x": 498,
|
|
2760
|
+
"y": 521,
|
|
2761
|
+
"z": 475,
|
|
2762
|
+
"{": 338,
|
|
2763
|
+
"|": 270,
|
|
2764
|
+
"}": 338,
|
|
2765
|
+
"~": 555,
|
|
2766
|
+
"\xA0": 224,
|
|
2767
|
+
"\xA1": 323,
|
|
2768
|
+
"\xA2": 555,
|
|
2769
|
+
"\xA3": 555,
|
|
2770
|
+
"\xA4": 555,
|
|
2771
|
+
"\xA5": 555,
|
|
2772
|
+
"\xA6": 270,
|
|
2773
|
+
"\xA7": 1e3,
|
|
2774
|
+
"\xA8": 606,
|
|
2775
|
+
"\xA9": 832,
|
|
2776
|
+
"\xAA": 386,
|
|
2777
|
+
"\xAB": 479,
|
|
2778
|
+
"\xAC": 555,
|
|
2779
|
+
"\xAD": 347,
|
|
2780
|
+
"\xAE": 473,
|
|
2781
|
+
"\xAF": 606,
|
|
2782
|
+
"\xB0": 370,
|
|
2783
|
+
"\xB1": 1e3,
|
|
2784
|
+
"\xB2": 411,
|
|
2785
|
+
"\xB3": 411,
|
|
2786
|
+
"\xB4": 606,
|
|
2787
|
+
"\xB5": 628,
|
|
2788
|
+
"\xB6": 1e3,
|
|
2789
|
+
"\xB7": 561,
|
|
2790
|
+
"\xB8": 606,
|
|
2791
|
+
"\xB9": 411,
|
|
2792
|
+
"\xBA": 407,
|
|
2793
|
+
"\xBB": 479,
|
|
2794
|
+
"\xBC": 873,
|
|
2795
|
+
"\xBD": 903,
|
|
2796
|
+
"\xBE": 889,
|
|
2797
|
+
"\xBF": 474,
|
|
2798
|
+
"\xC0": 608,
|
|
2799
|
+
"\xC1": 608,
|
|
2800
|
+
"\xC2": 608,
|
|
2801
|
+
"\xC3": 608,
|
|
2802
|
+
"\xC4": 608,
|
|
2803
|
+
"\xC5": 608,
|
|
2804
|
+
"\xC6": 918,
|
|
2805
|
+
"\xC7": 638,
|
|
2806
|
+
"\xC8": 589,
|
|
2807
|
+
"\xC9": 589,
|
|
2808
|
+
"\xCA": 589,
|
|
2809
|
+
"\xCB": 589,
|
|
2810
|
+
"\xCC": 293,
|
|
2811
|
+
"\xCD": 293,
|
|
2812
|
+
"\xCE": 293,
|
|
2813
|
+
"\xCF": 293,
|
|
2814
|
+
"\xD0": 712,
|
|
2815
|
+
"\xD1": 723,
|
|
2816
|
+
"\xD2": 742,
|
|
2817
|
+
"\xD3": 742,
|
|
2818
|
+
"\xD4": 742,
|
|
2819
|
+
"\xD5": 742,
|
|
2820
|
+
"\xD6": 742,
|
|
2821
|
+
"\xD7": 1e3,
|
|
2822
|
+
"\xD8": 742,
|
|
2823
|
+
"\xD9": 721,
|
|
2824
|
+
"\xDA": 721,
|
|
2825
|
+
"\xDB": 721,
|
|
2826
|
+
"\xDC": 721,
|
|
2827
|
+
"\xDD": 531,
|
|
2828
|
+
"\xDE": 652,
|
|
2829
|
+
"\xDF": 643,
|
|
2830
|
+
"\xE0": 563,
|
|
2831
|
+
"\xE1": 563,
|
|
2832
|
+
"\xE2": 563,
|
|
2833
|
+
"\xE3": 563,
|
|
2834
|
+
"\xE4": 563,
|
|
2835
|
+
"\xE5": 563,
|
|
2836
|
+
"\xE6": 877,
|
|
2837
|
+
"\xE7": 510,
|
|
2838
|
+
"\xE8": 554,
|
|
2839
|
+
"\xE9": 554,
|
|
2840
|
+
"\xEA": 554,
|
|
2841
|
+
"\xEB": 554,
|
|
2842
|
+
"\xEC": 275,
|
|
2843
|
+
"\xED": 275,
|
|
2844
|
+
"\xEE": 275,
|
|
2845
|
+
"\xEF": 275,
|
|
2846
|
+
"\xF0": 608,
|
|
2847
|
+
"\xF1": 610,
|
|
2848
|
+
"\xF2": 606,
|
|
2849
|
+
"\xF3": 606,
|
|
2850
|
+
"\xF4": 606,
|
|
2851
|
+
"\xF5": 606,
|
|
2852
|
+
"\xF6": 606,
|
|
2853
|
+
"\xF7": 1e3,
|
|
2854
|
+
"\xF8": 606,
|
|
2855
|
+
"\xF9": 607,
|
|
2856
|
+
"\xFA": 607,
|
|
2857
|
+
"\xFB": 607,
|
|
2858
|
+
"\xFC": 607,
|
|
2859
|
+
"\xFD": 521,
|
|
2860
|
+
"\xFE": 620,
|
|
2861
|
+
"\xFF": 521
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
};
|
|
2865
|
+
var fontNameMap = {
|
|
2866
|
+
// Calibri → Carlito
|
|
2867
|
+
Calibri: "Carlito",
|
|
2868
|
+
calibri: "Carlito",
|
|
2869
|
+
// Arial / Helvetica → Liberation Sans
|
|
2870
|
+
Arial: "LiberationSans",
|
|
2871
|
+
arial: "LiberationSans",
|
|
2872
|
+
Helvetica: "LiberationSans",
|
|
2873
|
+
helvetica: "LiberationSans",
|
|
2874
|
+
// Times New Roman → Liberation Serif
|
|
2875
|
+
"Times New Roman": "LiberationSerif",
|
|
2876
|
+
"times new roman": "LiberationSerif",
|
|
2877
|
+
// 日本語フォント → Noto Sans JP
|
|
2878
|
+
"MS PGothic": "NotoSansJP",
|
|
2879
|
+
"MS P\u30B4\u30B7\u30C3\u30AF": "NotoSansJP",
|
|
2880
|
+
"MS Gothic": "NotoSansJP",
|
|
2881
|
+
"MS \u30B4\u30B7\u30C3\u30AF": "NotoSansJP",
|
|
2882
|
+
Meiryo: "NotoSansJP",
|
|
2883
|
+
\u30E1\u30A4\u30EA\u30AA: "NotoSansJP",
|
|
2884
|
+
"Yu Gothic": "NotoSansJP",
|
|
2885
|
+
\u6E38\u30B4\u30B7\u30C3\u30AF: "NotoSansJP",
|
|
2886
|
+
"Yu Mincho": "NotoSansJP",
|
|
2887
|
+
\u6E38\u660E\u671D: "NotoSansJP",
|
|
2888
|
+
"Hiragino Sans": "NotoSansJP",
|
|
2889
|
+
"Hiragino Kaku Gothic ProN": "NotoSansJP",
|
|
2890
|
+
"Noto Sans JP": "NotoSansJP",
|
|
2891
|
+
"Noto Sans CJK JP": "NotoSansJP"
|
|
2892
|
+
};
|
|
2893
|
+
var metricsKeyToFontName = {
|
|
2894
|
+
Carlito: "Carlito",
|
|
2895
|
+
LiberationSans: "Liberation Sans",
|
|
2896
|
+
LiberationSerif: "Liberation Serif",
|
|
2897
|
+
NotoSansJP: "Noto Sans JP"
|
|
2898
|
+
};
|
|
2899
|
+
function getFontMetrics(fontFamily) {
|
|
2900
|
+
if (!fontFamily) return null;
|
|
2901
|
+
const key = fontNameMap[fontFamily] ?? fontNameMap[fontFamily.toLowerCase()];
|
|
2902
|
+
if (!key) return null;
|
|
2903
|
+
return metricsData[key] ?? null;
|
|
2904
|
+
}
|
|
2905
|
+
function getMetricsFallbackFont(fontFamily) {
|
|
2906
|
+
if (!fontFamily) return null;
|
|
2907
|
+
const key = fontNameMap[fontFamily] ?? fontNameMap[fontFamily.toLowerCase()];
|
|
2908
|
+
if (!key) return null;
|
|
2909
|
+
return metricsKeyToFontName[key] ?? null;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
// src/utils/text-measure.ts
|
|
2913
|
+
var WIDTH_RATIO = {
|
|
2914
|
+
narrow: 0.3,
|
|
2915
|
+
normal: 0.6,
|
|
2916
|
+
wide: 1
|
|
2917
|
+
};
|
|
2918
|
+
var BOLD_FACTOR = 1.05;
|
|
2919
|
+
var PX_PER_PT = 96 / 72;
|
|
2920
|
+
var DEFAULT_LINE_HEIGHT_RATIO = 1.2;
|
|
2921
|
+
function getLineHeightRatio(fontFamily, fontFamilyEa) {
|
|
2922
|
+
const metrics = getFontMetrics(fontFamily) ?? getFontMetrics(fontFamilyEa);
|
|
2923
|
+
if (!metrics) return DEFAULT_LINE_HEIGHT_RATIO;
|
|
2924
|
+
return (metrics.ascender + Math.abs(metrics.descender)) / metrics.unitsPerEm;
|
|
2925
|
+
}
|
|
2926
|
+
function isCjkCodePoint(codePoint) {
|
|
2927
|
+
return codePoint >= 12288 && codePoint <= 40959 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 131072 && codePoint <= 173791;
|
|
2928
|
+
}
|
|
2929
|
+
function categorizeChar(codePoint) {
|
|
2930
|
+
if (isCjkCodePoint(codePoint)) {
|
|
2931
|
+
return "wide";
|
|
2932
|
+
}
|
|
2933
|
+
if (codePoint === 32 || // space
|
|
2934
|
+
codePoint === 33 || // !
|
|
2935
|
+
codePoint === 44 || // ,
|
|
2936
|
+
codePoint === 46 || // .
|
|
2937
|
+
codePoint === 58 || // :
|
|
2938
|
+
codePoint === 59 || // ;
|
|
2939
|
+
codePoint === 105 || // i
|
|
2940
|
+
codePoint === 106 || // j
|
|
2941
|
+
codePoint === 108 || // l
|
|
2942
|
+
codePoint === 49 || // 1
|
|
2943
|
+
codePoint === 124 || // |
|
|
2944
|
+
codePoint === 39 || // '
|
|
2945
|
+
codePoint === 40 || // (
|
|
2946
|
+
codePoint === 41 || // )
|
|
2947
|
+
codePoint === 91 || // [
|
|
2948
|
+
codePoint === 93 || // ]
|
|
2949
|
+
codePoint === 123 || // {
|
|
2950
|
+
codePoint === 125) {
|
|
2951
|
+
return "narrow";
|
|
2952
|
+
}
|
|
2953
|
+
return "normal";
|
|
2954
|
+
}
|
|
2955
|
+
function measureCharHeuristic(codePoint, baseSizePx) {
|
|
2956
|
+
return baseSizePx * WIDTH_RATIO[categorizeChar(codePoint)];
|
|
2957
|
+
}
|
|
2958
|
+
function measureCharMetrics(char, codePoint, baseSizePx, metrics) {
|
|
2959
|
+
const charWidth = metrics.widths[char];
|
|
2960
|
+
if (charWidth !== void 0) {
|
|
2961
|
+
return charWidth / metrics.unitsPerEm * baseSizePx;
|
|
2962
|
+
}
|
|
2963
|
+
if (isCjkCodePoint(codePoint)) {
|
|
2964
|
+
return metrics.cjkWidth / metrics.unitsPerEm * baseSizePx;
|
|
2965
|
+
}
|
|
2966
|
+
return metrics.defaultWidth / metrics.unitsPerEm * baseSizePx;
|
|
2967
|
+
}
|
|
2968
|
+
function measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
|
|
2969
|
+
const baseSizePx = fontSizePt * PX_PER_PT;
|
|
2970
|
+
const latinMetrics = getFontMetrics(fontFamily);
|
|
2971
|
+
const eaMetrics = getFontMetrics(fontFamilyEa);
|
|
2972
|
+
let totalWidth = 0;
|
|
2973
|
+
for (const char of text) {
|
|
2974
|
+
const codePoint = char.codePointAt(0);
|
|
2975
|
+
const isEa = isCjkCodePoint(codePoint);
|
|
2976
|
+
const metrics = isEa && eaMetrics ? eaMetrics : latinMetrics;
|
|
2977
|
+
if (metrics) {
|
|
2978
|
+
totalWidth += measureCharMetrics(char, codePoint, baseSizePx, metrics);
|
|
2979
|
+
} else {
|
|
2980
|
+
totalWidth += measureCharHeuristic(codePoint, baseSizePx);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
if (bold) {
|
|
2984
|
+
totalWidth *= BOLD_FACTOR;
|
|
2985
|
+
}
|
|
2986
|
+
return totalWidth;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// src/utils/text-wrap.ts
|
|
2990
|
+
var DEFAULT_FONT_SIZE = 18;
|
|
2991
|
+
function isCjk(codePoint) {
|
|
2992
|
+
return codePoint >= 12288 && codePoint <= 40959 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 131072 && codePoint <= 173791;
|
|
2993
|
+
}
|
|
2994
|
+
function isWhitespace(codePoint) {
|
|
2995
|
+
return codePoint === 32 || codePoint === 9 || codePoint === 10 || codePoint === 13;
|
|
2996
|
+
}
|
|
2997
|
+
function splitTextIntoFragments(text) {
|
|
2998
|
+
const fragments = [];
|
|
2999
|
+
let current = "";
|
|
3000
|
+
let currentType = null;
|
|
3001
|
+
for (const char of text) {
|
|
3002
|
+
const cp = char.codePointAt(0);
|
|
3003
|
+
if (isWhitespace(cp)) {
|
|
3004
|
+
if (current && currentType !== "space") {
|
|
3005
|
+
fragments.push({ fragment: current, breakable: currentType === "cjk" });
|
|
3006
|
+
current = "";
|
|
3007
|
+
}
|
|
3008
|
+
currentType = "space";
|
|
3009
|
+
current += char;
|
|
3010
|
+
} else if (isCjk(cp)) {
|
|
3011
|
+
if (current) {
|
|
3012
|
+
fragments.push({
|
|
3013
|
+
fragment: current,
|
|
3014
|
+
breakable: currentType === "cjk" || currentType === "space"
|
|
3015
|
+
});
|
|
3016
|
+
current = "";
|
|
3017
|
+
}
|
|
3018
|
+
fragments.push({ fragment: char, breakable: true });
|
|
3019
|
+
currentType = "cjk";
|
|
3020
|
+
current = "";
|
|
3021
|
+
} else {
|
|
3022
|
+
if (current && currentType !== "latin") {
|
|
3023
|
+
fragments.push({ fragment: current, breakable: currentType === "space" });
|
|
3024
|
+
current = "";
|
|
3025
|
+
}
|
|
3026
|
+
currentType = "latin";
|
|
3027
|
+
current += char;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
if (current) {
|
|
3031
|
+
fragments.push({
|
|
3032
|
+
fragment: current,
|
|
3033
|
+
breakable: currentType === "space" || currentType === "cjk"
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
return fragments;
|
|
3037
|
+
}
|
|
3038
|
+
function tokenizeRuns(runs, defaultFontSize, fontScale) {
|
|
3039
|
+
const tokens = [];
|
|
3040
|
+
let isFirst = true;
|
|
3041
|
+
for (const run of runs) {
|
|
3042
|
+
if (run.text.length === 0) continue;
|
|
3043
|
+
const fontSize = run.properties.fontSize ? run.properties.fontSize * fontScale : defaultFontSize;
|
|
3044
|
+
const bold = run.properties.bold;
|
|
3045
|
+
const fontFamily = run.properties.fontFamily;
|
|
3046
|
+
const fontFamilyEa = run.properties.fontFamilyEa;
|
|
3047
|
+
const fragments = splitTextIntoFragments(run.text);
|
|
3048
|
+
for (const { fragment, breakable } of fragments) {
|
|
3049
|
+
const width = measureTextWidth(fragment, fontSize, bold, fontFamily, fontFamilyEa);
|
|
3050
|
+
tokens.push({
|
|
3051
|
+
text: fragment,
|
|
3052
|
+
properties: run.properties,
|
|
3053
|
+
width,
|
|
3054
|
+
breakable: isFirst ? false : breakable
|
|
3055
|
+
});
|
|
3056
|
+
isFirst = false;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
return tokens;
|
|
3060
|
+
}
|
|
3061
|
+
function isSpaceOnly(text) {
|
|
3062
|
+
for (const char of text) {
|
|
3063
|
+
if (!isWhitespace(char.codePointAt(0))) return false;
|
|
3064
|
+
}
|
|
3065
|
+
return true;
|
|
3066
|
+
}
|
|
3067
|
+
function splitTokenByChars(token, availableWidth, defaultFontSize, fontScale) {
|
|
3068
|
+
const lines = [];
|
|
3069
|
+
let currentLine = [];
|
|
3070
|
+
let currentWidth = 0;
|
|
3071
|
+
const fontSize = token.properties.fontSize ? token.properties.fontSize * fontScale : defaultFontSize;
|
|
3072
|
+
const bold = token.properties.bold;
|
|
3073
|
+
const fontFamily = token.properties.fontFamily;
|
|
3074
|
+
const fontFamilyEa = token.properties.fontFamilyEa;
|
|
3075
|
+
for (const char of token.text) {
|
|
3076
|
+
const charWidth = measureTextWidth(char, fontSize, bold, fontFamily, fontFamilyEa);
|
|
3077
|
+
if (currentWidth + charWidth > availableWidth && currentLine.length > 0) {
|
|
3078
|
+
lines.push(currentLine);
|
|
3079
|
+
currentLine = [];
|
|
3080
|
+
currentWidth = 0;
|
|
3081
|
+
}
|
|
3082
|
+
currentLine.push({
|
|
3083
|
+
text: char,
|
|
3084
|
+
properties: token.properties,
|
|
3085
|
+
width: charWidth,
|
|
3086
|
+
breakable: false
|
|
3087
|
+
});
|
|
3088
|
+
currentWidth += charWidth;
|
|
3089
|
+
}
|
|
3090
|
+
if (currentLine.length > 0) {
|
|
3091
|
+
lines.push(currentLine);
|
|
3092
|
+
}
|
|
3093
|
+
return lines;
|
|
3094
|
+
}
|
|
3095
|
+
function mergeSegments(tokens) {
|
|
3096
|
+
const segments = [];
|
|
3097
|
+
for (const token of tokens) {
|
|
3098
|
+
if (segments.length > 0) {
|
|
3099
|
+
const last = segments[segments.length - 1];
|
|
3100
|
+
if (last.properties === token.properties) {
|
|
3101
|
+
last.text += token.text;
|
|
3102
|
+
continue;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
segments.push({ text: token.text, properties: token.properties });
|
|
3106
|
+
}
|
|
3107
|
+
return segments;
|
|
3108
|
+
}
|
|
3109
|
+
function trimTrailingSpaces(segments) {
|
|
3110
|
+
if (segments.length === 0) return segments;
|
|
3111
|
+
const last = segments[segments.length - 1];
|
|
3112
|
+
const trimmed = last.text.replace(/\s+$/, "");
|
|
3113
|
+
if (trimmed.length === 0) {
|
|
3114
|
+
const result = segments.slice(0, -1);
|
|
3115
|
+
return trimTrailingSpaces(result);
|
|
3116
|
+
}
|
|
3117
|
+
if (trimmed !== last.text) {
|
|
3118
|
+
return [...segments.slice(0, -1), { text: trimmed, properties: last.properties }];
|
|
3119
|
+
}
|
|
3120
|
+
return segments;
|
|
3121
|
+
}
|
|
3122
|
+
function layoutTokensIntoLines(tokens, availableWidth, defaultFontSize, fontScale) {
|
|
3123
|
+
if (tokens.length === 0) return [{ segments: [] }];
|
|
3124
|
+
const lines = [];
|
|
3125
|
+
let currentLine = [];
|
|
3126
|
+
let currentWidth = 0;
|
|
3127
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
3128
|
+
const token = tokens[i];
|
|
3129
|
+
if (currentWidth + token.width <= availableWidth) {
|
|
3130
|
+
currentLine.push(token);
|
|
3131
|
+
currentWidth += token.width;
|
|
3132
|
+
} else if (currentLine.length === 0) {
|
|
3133
|
+
if (isSpaceOnly(token.text)) {
|
|
3134
|
+
continue;
|
|
3135
|
+
}
|
|
3136
|
+
const splitLines = splitTokenByChars(token, availableWidth, defaultFontSize, fontScale);
|
|
3137
|
+
for (let j = 0; j < splitLines.length; j++) {
|
|
3138
|
+
if (j < splitLines.length - 1) {
|
|
3139
|
+
const segments = trimTrailingSpaces(mergeSegments(splitLines[j]));
|
|
3140
|
+
if (segments.length > 0) lines.push({ segments });
|
|
3141
|
+
} else {
|
|
3142
|
+
currentLine = splitLines[j];
|
|
3143
|
+
currentWidth = splitLines[j].reduce((sum, t) => sum + t.width, 0);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
} else if (token.breakable) {
|
|
3147
|
+
const segments = trimTrailingSpaces(mergeSegments(currentLine));
|
|
3148
|
+
if (segments.length > 0) lines.push({ segments });
|
|
3149
|
+
if (isSpaceOnly(token.text)) {
|
|
3150
|
+
currentLine = [];
|
|
3151
|
+
currentWidth = 0;
|
|
3152
|
+
} else {
|
|
3153
|
+
currentLine = [token];
|
|
3154
|
+
currentWidth = token.width;
|
|
3155
|
+
}
|
|
3156
|
+
} else {
|
|
3157
|
+
const segments = trimTrailingSpaces(mergeSegments(currentLine));
|
|
3158
|
+
if (segments.length > 0) lines.push({ segments });
|
|
3159
|
+
currentLine = [token];
|
|
3160
|
+
currentWidth = token.width;
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
if (currentLine.length > 0) {
|
|
3164
|
+
const segments = trimTrailingSpaces(mergeSegments(currentLine));
|
|
3165
|
+
if (segments.length > 0) lines.push({ segments });
|
|
3166
|
+
}
|
|
3167
|
+
return lines.length > 0 ? lines : [{ segments: [] }];
|
|
3168
|
+
}
|
|
3169
|
+
function wrapParagraph(paragraph, availableWidth, defaultFontSize = DEFAULT_FONT_SIZE, fontScale = 1) {
|
|
3170
|
+
if (paragraph.runs.length === 0 || !paragraph.runs.some((r) => r.text.length > 0)) {
|
|
3171
|
+
return [{ segments: [] }];
|
|
3172
|
+
}
|
|
3173
|
+
const safeWidth = Math.max(availableWidth, 1);
|
|
3174
|
+
const tokens = tokenizeRuns(paragraph.runs, defaultFontSize, fontScale);
|
|
3175
|
+
if (tokens.length === 0) return [{ segments: [] }];
|
|
3176
|
+
return layoutTokensIntoLines(tokens, safeWidth, defaultFontSize, fontScale);
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
// src/renderer/text-renderer.ts
|
|
3180
|
+
var PX_PER_PT2 = 96 / 72;
|
|
3181
|
+
var DEFAULT_LINE_SPACING = 1;
|
|
3182
|
+
var DEFAULT_FONT_SIZE_PT = 18;
|
|
3183
|
+
function renderTextBody(textBody, transform) {
|
|
3184
|
+
const { bodyProperties, paragraphs } = textBody;
|
|
3185
|
+
const width = emuToPixels(transform.extentWidth);
|
|
3186
|
+
const height = emuToPixels(transform.extentHeight);
|
|
3187
|
+
const marginLeft = emuToPixels(bodyProperties.marginLeft);
|
|
3188
|
+
const marginRight = emuToPixels(bodyProperties.marginRight);
|
|
3189
|
+
const marginTop = emuToPixels(bodyProperties.marginTop);
|
|
3190
|
+
const marginBottom = emuToPixels(bodyProperties.marginBottom);
|
|
3191
|
+
const hasText = paragraphs.some((p) => p.runs.some((r) => r.text.length > 0));
|
|
3192
|
+
if (!hasText) return "";
|
|
3193
|
+
const textWidth = width - marginLeft - marginRight;
|
|
3194
|
+
const defaultFontSize = getDefaultFontSize(paragraphs);
|
|
3195
|
+
const shouldWrap = bodyProperties.wrap !== "none";
|
|
3196
|
+
const fontScale = bodyProperties.fontScale;
|
|
3197
|
+
const lnSpcReduction = bodyProperties.lnSpcReduction;
|
|
3198
|
+
const scaledDefaultFontSize = defaultFontSize * fontScale;
|
|
3199
|
+
const defaultLineHeightRatio = getDefaultLineHeightRatio(paragraphs);
|
|
3200
|
+
const defaultNaturalHeight = scaledDefaultFontSize * defaultLineHeightRatio;
|
|
3201
|
+
const tspans = [];
|
|
3202
|
+
let isFirstLine = true;
|
|
3203
|
+
const autoNumCounters = /* @__PURE__ */ new Map();
|
|
3204
|
+
for (const para of paragraphs) {
|
|
3205
|
+
const paraMarginLeft = emuToPixels(para.properties.marginLeft);
|
|
3206
|
+
const paraIndent = emuToPixels(para.properties.indent);
|
|
3207
|
+
const textStartX = marginLeft + paraMarginLeft;
|
|
3208
|
+
const bulletX = textStartX + paraIndent;
|
|
3209
|
+
const effectiveTextWidth = textWidth - paraMarginLeft;
|
|
3210
|
+
const bulletText = resolveBulletText(para.properties, autoNumCounters);
|
|
3211
|
+
const { xPos, anchorValue } = getAlignmentInfo(
|
|
3212
|
+
para.properties.alignment,
|
|
3213
|
+
textStartX,
|
|
3214
|
+
effectiveTextWidth,
|
|
3215
|
+
width,
|
|
3216
|
+
marginRight
|
|
3217
|
+
);
|
|
3218
|
+
if (para.runs.length === 0 || !para.runs.some((r) => r.text.length > 0)) {
|
|
3219
|
+
const dy = computeDy(isFirstLine, defaultNaturalHeight, DEFAULT_LINE_SPACING, para, true);
|
|
3220
|
+
tspans.push(`<tspan x="${xPos}" dy="${dy}" text-anchor="${anchorValue}"> </tspan>`);
|
|
3221
|
+
isFirstLine = false;
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
if (shouldWrap) {
|
|
3225
|
+
const wrappedLines = wrapParagraph(
|
|
3226
|
+
para,
|
|
3227
|
+
effectiveTextWidth,
|
|
3228
|
+
scaledDefaultFontSize,
|
|
3229
|
+
fontScale
|
|
3230
|
+
);
|
|
3231
|
+
for (let lineIdx = 0; lineIdx < wrappedLines.length; lineIdx++) {
|
|
3232
|
+
const line = wrappedLines[lineIdx];
|
|
3233
|
+
if (line.segments.length === 0) {
|
|
3234
|
+
const dy = computeDy(
|
|
3235
|
+
isFirstLine,
|
|
3236
|
+
defaultNaturalHeight,
|
|
3237
|
+
getLineSpacing(para, lnSpcReduction),
|
|
3238
|
+
para,
|
|
3239
|
+
lineIdx === 0
|
|
3240
|
+
);
|
|
3241
|
+
tspans.push(`<tspan x="${xPos}" dy="${dy}" text-anchor="${anchorValue}"> </tspan>`);
|
|
3242
|
+
isFirstLine = false;
|
|
3243
|
+
continue;
|
|
3244
|
+
}
|
|
3245
|
+
if (lineIdx === 0 && bulletText) {
|
|
3246
|
+
const lineFontSize = getLineFontSize(line.segments, defaultFontSize) * fontScale;
|
|
3247
|
+
const lineNaturalHeight = computeLineNaturalHeight(
|
|
3248
|
+
line.segments,
|
|
3249
|
+
defaultFontSize,
|
|
3250
|
+
fontScale
|
|
3251
|
+
);
|
|
3252
|
+
const dy = computeDy(
|
|
3253
|
+
isFirstLine,
|
|
3254
|
+
lineNaturalHeight,
|
|
3255
|
+
getLineSpacing(para, lnSpcReduction),
|
|
3256
|
+
para,
|
|
3257
|
+
true
|
|
3258
|
+
);
|
|
3259
|
+
const bulletStyles = buildBulletStyleAttrs(para.properties, lineFontSize, fontScale);
|
|
3260
|
+
tspans.push(
|
|
3261
|
+
`<tspan x="${bulletX}" dy="${dy}" text-anchor="start" ${bulletStyles}>${escapeXml(bulletText)}</tspan>`
|
|
3262
|
+
);
|
|
3263
|
+
for (let segIdx = 0; segIdx < line.segments.length; segIdx++) {
|
|
3264
|
+
const seg = line.segments[segIdx];
|
|
3265
|
+
const prefix = segIdx === 0 ? `x="${xPos}" text-anchor="${anchorValue}" ` : "";
|
|
3266
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix));
|
|
3267
|
+
}
|
|
3268
|
+
} else {
|
|
3269
|
+
for (let segIdx = 0; segIdx < line.segments.length; segIdx++) {
|
|
3270
|
+
const seg = line.segments[segIdx];
|
|
3271
|
+
if (segIdx === 0) {
|
|
3272
|
+
const lineNaturalHeight = computeLineNaturalHeight(
|
|
3273
|
+
line.segments,
|
|
3274
|
+
defaultFontSize,
|
|
3275
|
+
fontScale
|
|
3276
|
+
);
|
|
3277
|
+
const dy = computeDy(
|
|
3278
|
+
isFirstLine,
|
|
3279
|
+
lineNaturalHeight,
|
|
3280
|
+
getLineSpacing(para, lnSpcReduction),
|
|
3281
|
+
para,
|
|
3282
|
+
lineIdx === 0
|
|
3283
|
+
);
|
|
3284
|
+
const prefix = `x="${xPos}" dy="${dy}" text-anchor="${anchorValue}" `;
|
|
3285
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix));
|
|
3286
|
+
} else {
|
|
3287
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, ""));
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
isFirstLine = false;
|
|
3292
|
+
}
|
|
3293
|
+
} else {
|
|
3294
|
+
let firstRunRendered = false;
|
|
3295
|
+
if (bulletText) {
|
|
3296
|
+
const firstRun = para.runs.find((r) => r.text.length > 0);
|
|
3297
|
+
const fontSize = (firstRun?.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
3298
|
+
const naturalHeight = computeLineNaturalHeight(para.runs, defaultFontSize, fontScale);
|
|
3299
|
+
const dy = computeDy(
|
|
3300
|
+
isFirstLine,
|
|
3301
|
+
naturalHeight,
|
|
3302
|
+
getLineSpacing(para, lnSpcReduction),
|
|
3303
|
+
para,
|
|
3304
|
+
true
|
|
3305
|
+
);
|
|
3306
|
+
const bulletStyles = buildBulletStyleAttrs(para.properties, fontSize, fontScale);
|
|
3307
|
+
tspans.push(
|
|
3308
|
+
`<tspan x="${bulletX}" dy="${dy}" text-anchor="start" ${bulletStyles}>${escapeXml(bulletText)}</tspan>`
|
|
3309
|
+
);
|
|
3310
|
+
}
|
|
3311
|
+
for (let i = 0; i < para.runs.length; i++) {
|
|
3312
|
+
const run = para.runs[i];
|
|
3313
|
+
if (run.text.length === 0) continue;
|
|
3314
|
+
if (!firstRunRendered) {
|
|
3315
|
+
if (bulletText) {
|
|
3316
|
+
const prefix = `x="${xPos}" text-anchor="${anchorValue}" `;
|
|
3317
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix));
|
|
3318
|
+
} else {
|
|
3319
|
+
const naturalHeight = computeLineNaturalHeight(para.runs, defaultFontSize, fontScale);
|
|
3320
|
+
const dy = computeDy(
|
|
3321
|
+
isFirstLine,
|
|
3322
|
+
naturalHeight,
|
|
3323
|
+
getLineSpacing(para, lnSpcReduction),
|
|
3324
|
+
para,
|
|
3325
|
+
true
|
|
3326
|
+
);
|
|
3327
|
+
const prefix = `x="${xPos}" dy="${dy}" text-anchor="${anchorValue}" `;
|
|
3328
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix));
|
|
3329
|
+
}
|
|
3330
|
+
firstRunRendered = true;
|
|
3331
|
+
} else {
|
|
3332
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, ""));
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
isFirstLine = false;
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
let yStart = marginTop;
|
|
3339
|
+
const totalTextHeight = estimateTextHeight(
|
|
3340
|
+
paragraphs,
|
|
3341
|
+
scaledDefaultFontSize,
|
|
3342
|
+
shouldWrap,
|
|
3343
|
+
textWidth,
|
|
3344
|
+
lnSpcReduction,
|
|
3345
|
+
fontScale
|
|
3346
|
+
);
|
|
3347
|
+
if (bodyProperties.anchor === "ctr") {
|
|
3348
|
+
yStart = Math.max(marginTop, (height - totalTextHeight) / 2);
|
|
3349
|
+
} else if (bodyProperties.anchor === "b") {
|
|
3350
|
+
yStart = Math.max(marginTop, height - totalTextHeight - marginBottom);
|
|
3351
|
+
}
|
|
3352
|
+
yStart += defaultNaturalHeight;
|
|
3353
|
+
return `<text x="0" y="${yStart}">${tspans.join("")}</text>`;
|
|
3354
|
+
}
|
|
3355
|
+
function resolveBulletText(props, autoNumCounters) {
|
|
3356
|
+
if (!props.bullet) return null;
|
|
3357
|
+
if (props.bullet.type === "none") return null;
|
|
3358
|
+
if (props.bullet.type === "char") {
|
|
3359
|
+
return props.bullet.char;
|
|
3360
|
+
}
|
|
3361
|
+
if (props.bullet.type === "autoNum") {
|
|
3362
|
+
const { scheme, startAt } = props.bullet;
|
|
3363
|
+
const counterKey = `${scheme}-${props.level}`;
|
|
3364
|
+
const current = autoNumCounters.get(counterKey) ?? 0;
|
|
3365
|
+
const nextVal = current + 1;
|
|
3366
|
+
autoNumCounters.set(counterKey, nextVal);
|
|
3367
|
+
const index = startAt + nextVal - 1;
|
|
3368
|
+
return formatAutoNum(scheme, index);
|
|
3369
|
+
}
|
|
3370
|
+
return null;
|
|
3371
|
+
}
|
|
3372
|
+
function formatAutoNum(scheme, index) {
|
|
3373
|
+
switch (scheme) {
|
|
3374
|
+
case "arabicPeriod":
|
|
3375
|
+
return `${index}.`;
|
|
3376
|
+
case "arabicParenR":
|
|
3377
|
+
return `${index})`;
|
|
3378
|
+
case "arabicPlain":
|
|
3379
|
+
return `${index}`;
|
|
3380
|
+
case "romanUcPeriod":
|
|
3381
|
+
return `${toRoman(index)}.`;
|
|
3382
|
+
case "romanLcPeriod":
|
|
3383
|
+
return `${toRoman(index).toLowerCase()}.`;
|
|
3384
|
+
case "alphaUcPeriod":
|
|
3385
|
+
return `${toAlpha(index)}.`;
|
|
3386
|
+
case "alphaLcPeriod":
|
|
3387
|
+
return `${toAlpha(index).toLowerCase()}.`;
|
|
3388
|
+
case "alphaUcParenR":
|
|
3389
|
+
return `${toAlpha(index)})`;
|
|
3390
|
+
case "alphaLcParenR":
|
|
3391
|
+
return `${toAlpha(index).toLowerCase()})`;
|
|
3392
|
+
default:
|
|
3393
|
+
return `${index}.`;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
function toRoman(num) {
|
|
3397
|
+
const romanNumerals = [
|
|
3398
|
+
[1e3, "M"],
|
|
3399
|
+
[900, "CM"],
|
|
3400
|
+
[500, "D"],
|
|
3401
|
+
[400, "CD"],
|
|
3402
|
+
[100, "C"],
|
|
3403
|
+
[90, "XC"],
|
|
3404
|
+
[50, "L"],
|
|
3405
|
+
[40, "XL"],
|
|
3406
|
+
[10, "X"],
|
|
3407
|
+
[9, "IX"],
|
|
3408
|
+
[5, "V"],
|
|
3409
|
+
[4, "IV"],
|
|
3410
|
+
[1, "I"]
|
|
3411
|
+
];
|
|
3412
|
+
let result = "";
|
|
3413
|
+
let remaining = num;
|
|
3414
|
+
for (const [value, symbol] of romanNumerals) {
|
|
3415
|
+
while (remaining >= value) {
|
|
3416
|
+
result += symbol;
|
|
3417
|
+
remaining -= value;
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
return result;
|
|
3421
|
+
}
|
|
3422
|
+
function toAlpha(num) {
|
|
3423
|
+
let result = "";
|
|
3424
|
+
let remaining = num;
|
|
3425
|
+
while (remaining > 0) {
|
|
3426
|
+
remaining--;
|
|
3427
|
+
result = String.fromCharCode(65 + remaining % 26) + result;
|
|
3428
|
+
remaining = Math.floor(remaining / 26);
|
|
3429
|
+
}
|
|
3430
|
+
return result;
|
|
3431
|
+
}
|
|
3432
|
+
function buildBulletStyleAttrs(props, textFontSizePt, fontScale) {
|
|
3433
|
+
const styles = [];
|
|
3434
|
+
if (props.bulletSizePct !== null) {
|
|
3435
|
+
const size = textFontSizePt * (props.bulletSizePct / 1e5);
|
|
3436
|
+
styles.push(`font-size="${size}pt"`);
|
|
3437
|
+
}
|
|
3438
|
+
if (props.bulletFont) {
|
|
3439
|
+
styles.push(`font-family="${escapeXml(props.bulletFont)}"`);
|
|
3440
|
+
}
|
|
3441
|
+
if (props.bulletColor) {
|
|
3442
|
+
styles.push(`fill="${props.bulletColor.hex}"`);
|
|
3443
|
+
if (props.bulletColor.alpha < 1) {
|
|
3444
|
+
styles.push(`fill-opacity="${props.bulletColor.alpha}"`);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
void fontScale;
|
|
3448
|
+
return styles.join(" ");
|
|
3449
|
+
}
|
|
3450
|
+
function getAlignmentInfo(alignment, marginLeft, textWidth, width, marginRight) {
|
|
3451
|
+
switch (alignment) {
|
|
3452
|
+
case "ctr":
|
|
3453
|
+
return { xPos: marginLeft + textWidth / 2, anchorValue: "middle" };
|
|
3454
|
+
case "r":
|
|
3455
|
+
return { xPos: width - marginRight, anchorValue: "end" };
|
|
3456
|
+
default:
|
|
3457
|
+
return { xPos: marginLeft, anchorValue: "start" };
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
function getLineSpacing(para, lnSpcReduction = 0) {
|
|
3461
|
+
let spacing;
|
|
3462
|
+
if (para.properties.lineSpacing !== null) {
|
|
3463
|
+
const factor = para.properties.lineSpacing / 1e5;
|
|
3464
|
+
spacing = Math.max(0.5, factor);
|
|
3465
|
+
} else {
|
|
3466
|
+
spacing = DEFAULT_LINE_SPACING;
|
|
3467
|
+
}
|
|
3468
|
+
return spacing * (1 - lnSpcReduction);
|
|
3469
|
+
}
|
|
3470
|
+
function computeDy(isFirstLine, fontSizePt, lineSpacingFactor, para, isFirstLineOfParagraph) {
|
|
3471
|
+
if (isFirstLine) return "0";
|
|
3472
|
+
const lineHeight = fontSizePt * PX_PER_PT2 * lineSpacingFactor;
|
|
3473
|
+
let dy = lineHeight;
|
|
3474
|
+
if (isFirstLineOfParagraph && para.properties.spaceBefore > 0) {
|
|
3475
|
+
dy += para.properties.spaceBefore / 100 * PX_PER_PT2;
|
|
3476
|
+
}
|
|
3477
|
+
return dy.toFixed(2);
|
|
3478
|
+
}
|
|
3479
|
+
function getLineFontSize(segments, defaultFontSize) {
|
|
3480
|
+
for (const seg of segments) {
|
|
3481
|
+
if (seg.properties.fontSize) return seg.properties.fontSize;
|
|
3482
|
+
}
|
|
3483
|
+
return defaultFontSize;
|
|
3484
|
+
}
|
|
3485
|
+
function computeLineNaturalHeight(segments, defaultFontSize, fontScale) {
|
|
3486
|
+
let maxHeight = 0;
|
|
3487
|
+
for (const seg of segments) {
|
|
3488
|
+
const fontSize = (seg.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
3489
|
+
const ratio = getLineHeightRatio(seg.properties.fontFamily, seg.properties.fontFamilyEa);
|
|
3490
|
+
maxHeight = Math.max(maxHeight, fontSize * ratio);
|
|
3491
|
+
}
|
|
3492
|
+
return maxHeight > 0 ? maxHeight : defaultFontSize * fontScale * 1.2;
|
|
3493
|
+
}
|
|
3494
|
+
function isCjkCodePoint2(codePoint) {
|
|
3495
|
+
return codePoint >= 12288 && codePoint <= 40959 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 131072 && codePoint <= 173791;
|
|
3496
|
+
}
|
|
3497
|
+
function splitByScript(text) {
|
|
3498
|
+
const segments = [];
|
|
3499
|
+
let current = "";
|
|
3500
|
+
let currentIsEa = null;
|
|
3501
|
+
for (const char of text) {
|
|
3502
|
+
const cp = char.codePointAt(0);
|
|
3503
|
+
const isEa = isCjkCodePoint2(cp);
|
|
3504
|
+
if (currentIsEa !== null && isEa !== currentIsEa) {
|
|
3505
|
+
segments.push({ text: current, isEa: currentIsEa });
|
|
3506
|
+
current = "";
|
|
3507
|
+
}
|
|
3508
|
+
currentIsEa = isEa;
|
|
3509
|
+
current += char;
|
|
3510
|
+
}
|
|
3511
|
+
if (current && currentIsEa !== null) {
|
|
3512
|
+
segments.push({ text: current, isEa: currentIsEa });
|
|
3513
|
+
}
|
|
3514
|
+
return segments;
|
|
3515
|
+
}
|
|
3516
|
+
function needsScriptSplit(props) {
|
|
3517
|
+
return props.fontFamily !== null && props.fontFamilyEa !== null && props.fontFamily !== props.fontFamilyEa;
|
|
3518
|
+
}
|
|
3519
|
+
function getGenericFamily(fontFamily) {
|
|
3520
|
+
const lower = fontFamily.toLowerCase();
|
|
3521
|
+
if (lower.includes("mincho") || lower.includes("\u660E\u671D") || lower === "times new roman" || lower === "georgia" || lower === "cambria" || lower.includes("serif") && !lower.includes("sans")) {
|
|
3522
|
+
return "serif";
|
|
3523
|
+
}
|
|
3524
|
+
return "sans-serif";
|
|
3525
|
+
}
|
|
3526
|
+
function escapeFontName(name) {
|
|
3527
|
+
return name.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3528
|
+
}
|
|
3529
|
+
function buildFontFamilyValue(fonts) {
|
|
3530
|
+
const uniqueFonts = [];
|
|
3531
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3532
|
+
for (const font of fonts) {
|
|
3533
|
+
if (font && !seen.has(font)) {
|
|
3534
|
+
seen.add(font);
|
|
3535
|
+
uniqueFonts.push(font);
|
|
3536
|
+
const fallback = getMetricsFallbackFont(font);
|
|
3537
|
+
if (fallback && !seen.has(fallback)) {
|
|
3538
|
+
seen.add(fallback);
|
|
3539
|
+
uniqueFonts.push(fallback);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
if (uniqueFonts.length === 0) return null;
|
|
3544
|
+
const genericFamily = getGenericFamily(uniqueFonts[0]);
|
|
3545
|
+
const parts = uniqueFonts.map((f) => {
|
|
3546
|
+
const escaped = escapeFontName(f);
|
|
3547
|
+
return f.includes(" ") ? `'${escaped}'` : escaped;
|
|
3548
|
+
});
|
|
3549
|
+
parts.push(genericFamily);
|
|
3550
|
+
return parts.join(", ");
|
|
3551
|
+
}
|
|
3552
|
+
function buildStyleAttrs(props, fontScale = 1, fontFamilies) {
|
|
3553
|
+
const styles = [];
|
|
3554
|
+
if (props.fontSize) {
|
|
3555
|
+
const scaledSize = props.fontSize * fontScale;
|
|
3556
|
+
styles.push(`font-size="${scaledSize}pt"`);
|
|
3557
|
+
}
|
|
3558
|
+
const fonts = fontFamilies ?? [props.fontFamily, props.fontFamilyEa];
|
|
3559
|
+
const fontFamilyValue = buildFontFamilyValue(fonts);
|
|
3560
|
+
if (fontFamilyValue) {
|
|
3561
|
+
styles.push(`font-family="${fontFamilyValue}"`);
|
|
3562
|
+
}
|
|
3563
|
+
if (props.bold) {
|
|
3564
|
+
styles.push(`font-weight="bold"`);
|
|
3565
|
+
}
|
|
3566
|
+
if (props.italic) {
|
|
3567
|
+
styles.push(`font-style="italic"`);
|
|
3568
|
+
}
|
|
3569
|
+
if (props.color) {
|
|
3570
|
+
styles.push(`fill="${props.color.hex}"`);
|
|
3571
|
+
if (props.color.alpha < 1) {
|
|
3572
|
+
styles.push(`fill-opacity="${props.color.alpha}"`);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
const decorations = [];
|
|
3576
|
+
if (props.underline) decorations.push("underline");
|
|
3577
|
+
if (props.strikethrough) decorations.push("line-through");
|
|
3578
|
+
if (decorations.length > 0) {
|
|
3579
|
+
styles.push(`text-decoration="${decorations.join(" ")}"`);
|
|
3580
|
+
}
|
|
3581
|
+
if (props.baseline > 0) {
|
|
3582
|
+
styles.push(`baseline-shift="super"`);
|
|
3583
|
+
} else if (props.baseline < 0) {
|
|
3584
|
+
styles.push(`baseline-shift="sub"`);
|
|
3585
|
+
}
|
|
3586
|
+
return styles.join(" ");
|
|
3587
|
+
}
|
|
3588
|
+
function renderSegment(text, props, fontScale, prefix) {
|
|
3589
|
+
if (!needsScriptSplit(props)) {
|
|
3590
|
+
const styles = buildStyleAttrs(props, fontScale);
|
|
3591
|
+
return `<tspan ${prefix}${styles}>${escapeXml(text)}</tspan>`;
|
|
3592
|
+
}
|
|
3593
|
+
const parts = splitByScript(text);
|
|
3594
|
+
const result = [];
|
|
3595
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3596
|
+
const part = parts[i];
|
|
3597
|
+
const fonts = part.isEa ? [props.fontFamilyEa, props.fontFamily] : [props.fontFamily, props.fontFamilyEa];
|
|
3598
|
+
const styles = buildStyleAttrs(props, fontScale, fonts);
|
|
3599
|
+
if (i === 0) {
|
|
3600
|
+
result.push(`<tspan ${prefix}${styles}>${escapeXml(part.text)}</tspan>`);
|
|
3601
|
+
} else {
|
|
3602
|
+
result.push(`<tspan ${styles}>${escapeXml(part.text)}</tspan>`);
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
return result.join("");
|
|
3606
|
+
}
|
|
3607
|
+
function getDefaultFontSize(paragraphs) {
|
|
3608
|
+
for (const p of paragraphs) {
|
|
3609
|
+
for (const r of p.runs) {
|
|
3610
|
+
if (r.properties.fontSize) return r.properties.fontSize;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
return DEFAULT_FONT_SIZE_PT;
|
|
3614
|
+
}
|
|
3615
|
+
function getDefaultLineHeightRatio(paragraphs) {
|
|
3616
|
+
for (const p of paragraphs) {
|
|
3617
|
+
for (const r of p.runs) {
|
|
3618
|
+
if (r.properties.fontFamily || r.properties.fontFamilyEa) {
|
|
3619
|
+
return getLineHeightRatio(r.properties.fontFamily, r.properties.fontFamilyEa);
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
return 1.2;
|
|
3624
|
+
}
|
|
3625
|
+
function estimateTextHeight(paragraphs, defaultFontSize, shouldWrap, textWidth, lnSpcReduction = 0, fontScale = 1) {
|
|
3626
|
+
let totalHeight = 0;
|
|
3627
|
+
const defaultRatio = getDefaultLineHeightRatio(paragraphs);
|
|
3628
|
+
for (let pIdx = 0; pIdx < paragraphs.length; pIdx++) {
|
|
3629
|
+
const para = paragraphs[pIdx];
|
|
3630
|
+
const lineSpacing = getLineSpacing(para, lnSpcReduction);
|
|
3631
|
+
const naturalHeight = computeLineNaturalHeight(para.runs, defaultFontSize, fontScale);
|
|
3632
|
+
const lineHeight = (naturalHeight > 0 ? naturalHeight : defaultFontSize * fontScale * defaultRatio) * PX_PER_PT2 * lineSpacing;
|
|
3633
|
+
let lineCount;
|
|
3634
|
+
if (shouldWrap && para.runs.length > 0 && para.runs.some((r) => r.text.length > 0)) {
|
|
3635
|
+
const wrappedLines = wrapParagraph(para, textWidth, defaultFontSize, fontScale);
|
|
3636
|
+
lineCount = wrappedLines.length;
|
|
3637
|
+
} else {
|
|
3638
|
+
lineCount = para.runs.some((r) => r.text.length > 0) ? 1 : 1;
|
|
3639
|
+
}
|
|
3640
|
+
totalHeight += lineCount * lineHeight;
|
|
3641
|
+
if (pIdx > 0 && para.properties.spaceBefore > 0) {
|
|
3642
|
+
totalHeight += para.properties.spaceBefore / 100 * PX_PER_PT2;
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
return totalHeight;
|
|
3646
|
+
}
|
|
3647
|
+
function escapeXml(str) {
|
|
3648
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
// src/renderer/effect-renderer.ts
|
|
3652
|
+
function renderEffects(effects) {
|
|
3653
|
+
if (!effects) {
|
|
3654
|
+
return { filterAttr: "", filterDefs: "" };
|
|
3655
|
+
}
|
|
3656
|
+
const primitives = [];
|
|
3657
|
+
let lastResult = "SourceGraphic";
|
|
3658
|
+
if (effects.softEdge) {
|
|
3659
|
+
const r = emuToPixels(effects.softEdge.radius);
|
|
3660
|
+
primitives.push(
|
|
3661
|
+
`<feGaussianBlur in="SourceAlpha" stdDeviation="${r}" result="softEdgeMask"/>`,
|
|
3662
|
+
`<feComposite in="SourceGraphic" in2="softEdgeMask" operator="in" result="softEdgeResult"/>`
|
|
3663
|
+
);
|
|
3664
|
+
lastResult = "softEdgeResult";
|
|
3665
|
+
}
|
|
3666
|
+
if (effects.glow) {
|
|
3667
|
+
const r = emuToPixels(effects.glow.radius);
|
|
3668
|
+
const { hex, alpha } = effects.glow.color;
|
|
3669
|
+
if (lastResult !== "SourceGraphic") {
|
|
3670
|
+
primitives.push(
|
|
3671
|
+
`<feColorMatrix in="${lastResult}" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="glowAlpha"/>`
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
const blurIn = lastResult === "SourceGraphic" ? "SourceAlpha" : "glowAlpha";
|
|
3675
|
+
const mergeIn = lastResult;
|
|
3676
|
+
primitives.push(
|
|
3677
|
+
`<feGaussianBlur in="${blurIn}" stdDeviation="${r}" result="glowBlur"/>`,
|
|
3678
|
+
`<feFlood flood-color="${hex}" flood-opacity="${alpha}" result="glowColor"/>`,
|
|
3679
|
+
`<feComposite in="glowColor" in2="glowBlur" operator="in" result="glowFinal"/>`,
|
|
3680
|
+
`<feMerge result="glowMerge">`,
|
|
3681
|
+
`<feMergeNode in="glowFinal"/>`,
|
|
3682
|
+
`<feMergeNode in="${mergeIn}"/>`,
|
|
3683
|
+
`</feMerge>`
|
|
3684
|
+
);
|
|
3685
|
+
lastResult = "glowMerge";
|
|
3686
|
+
}
|
|
3687
|
+
if (effects.outerShadow) {
|
|
3688
|
+
const stdDev = emuToPixels(effects.outerShadow.blurRadius) / 2;
|
|
3689
|
+
const dist = emuToPixels(effects.outerShadow.distance);
|
|
3690
|
+
const dirRad = effects.outerShadow.direction * Math.PI / 180;
|
|
3691
|
+
const dx = round(dist * Math.cos(dirRad));
|
|
3692
|
+
const dy = round(dist * Math.sin(dirRad));
|
|
3693
|
+
const { hex, alpha } = effects.outerShadow.color;
|
|
3694
|
+
const mergeIn = lastResult;
|
|
3695
|
+
primitives.push(
|
|
3696
|
+
`<feGaussianBlur in="SourceAlpha" stdDeviation="${stdDev}" result="shadowBlur"/>`,
|
|
3697
|
+
`<feOffset in="shadowBlur" dx="${dx}" dy="${dy}" result="shadowOffset"/>`,
|
|
3698
|
+
`<feFlood flood-color="${hex}" flood-opacity="${alpha}" result="shadowColor"/>`,
|
|
3699
|
+
`<feComposite in="shadowColor" in2="shadowOffset" operator="in" result="shadowFinal"/>`,
|
|
3700
|
+
`<feMerge result="outerShadowMerge">`,
|
|
3701
|
+
`<feMergeNode in="shadowFinal"/>`,
|
|
3702
|
+
`<feMergeNode in="${mergeIn}"/>`,
|
|
3703
|
+
`</feMerge>`
|
|
3704
|
+
);
|
|
3705
|
+
lastResult = "outerShadowMerge";
|
|
3706
|
+
}
|
|
3707
|
+
if (effects.innerShadow) {
|
|
3708
|
+
const stdDev = emuToPixels(effects.innerShadow.blurRadius) / 2;
|
|
3709
|
+
const dist = emuToPixels(effects.innerShadow.distance);
|
|
3710
|
+
const dirRad = effects.innerShadow.direction * Math.PI / 180;
|
|
3711
|
+
const dx = round(dist * Math.cos(dirRad));
|
|
3712
|
+
const dy = round(dist * Math.sin(dirRad));
|
|
3713
|
+
const { hex, alpha } = effects.innerShadow.color;
|
|
3714
|
+
const sourceIn = lastResult;
|
|
3715
|
+
primitives.push(
|
|
3716
|
+
`<feComponentTransfer in="SourceAlpha" result="innerShdwInverse">`,
|
|
3717
|
+
`<feFuncA type="table" tableValues="1 0"/>`,
|
|
3718
|
+
`</feComponentTransfer>`,
|
|
3719
|
+
`<feGaussianBlur in="innerShdwInverse" stdDeviation="${stdDev}" result="innerShdwBlur"/>`,
|
|
3720
|
+
`<feOffset in="innerShdwBlur" dx="${dx}" dy="${dy}" result="innerShdwOffset"/>`,
|
|
3721
|
+
`<feFlood flood-color="${hex}" flood-opacity="${alpha}" result="innerShdwFill"/>`,
|
|
3722
|
+
`<feComposite in="innerShdwFill" in2="innerShdwOffset" operator="in" result="innerShdwColored"/>`,
|
|
3723
|
+
`<feComposite in="innerShdwColored" in2="SourceAlpha" operator="in" result="innerShdwClipped"/>`,
|
|
3724
|
+
`<feComposite in="innerShdwClipped" in2="${sourceIn}" operator="over"/>`
|
|
3725
|
+
);
|
|
3726
|
+
lastResult = "";
|
|
3727
|
+
}
|
|
3728
|
+
if (primitives.length === 0) {
|
|
3729
|
+
return { filterAttr: "", filterDefs: "" };
|
|
3730
|
+
}
|
|
3731
|
+
const id = `effect-${crypto.randomUUID()}`;
|
|
3732
|
+
const filterDefs = [
|
|
3733
|
+
`<filter id="${id}" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">`,
|
|
3734
|
+
...primitives,
|
|
3735
|
+
`</filter>`
|
|
3736
|
+
].join("");
|
|
3737
|
+
return {
|
|
3738
|
+
filterAttr: `filter="url(#${id})"`,
|
|
3739
|
+
filterDefs
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
function round(n) {
|
|
3743
|
+
return Math.round(n * 100) / 100;
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
// src/renderer/transform.ts
|
|
3747
|
+
function buildTransformAttr(t) {
|
|
3748
|
+
const x = emuToPixels(t.offsetX);
|
|
3749
|
+
const y = emuToPixels(t.offsetY);
|
|
3750
|
+
const w = emuToPixels(t.extentWidth);
|
|
3751
|
+
const h = emuToPixels(t.extentHeight);
|
|
3752
|
+
const parts = [];
|
|
3753
|
+
parts.push(`translate(${x}, ${y})`);
|
|
3754
|
+
if (t.rotation !== 0) {
|
|
3755
|
+
parts.push(`rotate(${t.rotation}, ${w / 2}, ${h / 2})`);
|
|
3756
|
+
}
|
|
3757
|
+
if (t.flipH || t.flipV) {
|
|
3758
|
+
const sx = t.flipH ? -1 : 1;
|
|
3759
|
+
const sy = t.flipV ? -1 : 1;
|
|
3760
|
+
parts.push(`translate(${t.flipH ? w : 0}, ${t.flipV ? h : 0})`);
|
|
3761
|
+
parts.push(`scale(${sx}, ${sy})`);
|
|
3762
|
+
}
|
|
3763
|
+
return parts.join(" ");
|
|
3764
|
+
}
|
|
3765
|
+
|
|
3766
|
+
// src/renderer/shape-renderer.ts
|
|
3767
|
+
function renderShape(shape) {
|
|
3768
|
+
const { transform, geometry, fill, outline, textBody, effects } = shape;
|
|
3769
|
+
const w = emuToPixels(transform.extentWidth);
|
|
3770
|
+
const h = emuToPixels(transform.extentHeight);
|
|
3771
|
+
const transformAttr = buildTransformAttr(transform);
|
|
3772
|
+
const fillResult = renderFillAttrs(fill);
|
|
3773
|
+
const outlineAttr = renderOutlineAttrs(outline);
|
|
3774
|
+
const effectResult = renderEffects(effects);
|
|
3775
|
+
const geometrySvg = renderGeometry(geometry, w, h);
|
|
3776
|
+
const parts = [];
|
|
3777
|
+
if (fillResult.defs) {
|
|
3778
|
+
parts.push(fillResult.defs);
|
|
3779
|
+
}
|
|
3780
|
+
if (effectResult.filterDefs) {
|
|
3781
|
+
parts.push(effectResult.filterDefs);
|
|
3782
|
+
}
|
|
3783
|
+
const filterAttr = effectResult.filterAttr ? ` ${effectResult.filterAttr}` : "";
|
|
3784
|
+
parts.push(`<g transform="${transformAttr}"${filterAttr}>`);
|
|
3785
|
+
if (geometrySvg) {
|
|
3786
|
+
const styledGeometry = geometrySvg.replace(/^<(\w+)/, `<$1 ${fillResult.attrs} ${outlineAttr}`);
|
|
3787
|
+
parts.push(styledGeometry);
|
|
3788
|
+
}
|
|
3789
|
+
if (textBody) {
|
|
3790
|
+
const textSvg = renderTextBody(textBody, transform);
|
|
3791
|
+
if (textSvg) {
|
|
3792
|
+
parts.push(textSvg);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
parts.push("</g>");
|
|
3796
|
+
return parts.join("");
|
|
3797
|
+
}
|
|
3798
|
+
function renderConnector(connector) {
|
|
3799
|
+
const { transform, outline, effects } = connector;
|
|
3800
|
+
const w = emuToPixels(transform.extentWidth);
|
|
3801
|
+
const h = emuToPixels(transform.extentHeight);
|
|
3802
|
+
const transformAttr = buildTransformAttr(transform);
|
|
3803
|
+
const outlineAttr = renderOutlineAttrs(outline);
|
|
3804
|
+
const effectResult = renderEffects(effects);
|
|
3805
|
+
const parts = [];
|
|
3806
|
+
if (effectResult.filterDefs) {
|
|
3807
|
+
parts.push(effectResult.filterDefs);
|
|
3808
|
+
}
|
|
3809
|
+
const filterAttr = effectResult.filterAttr ? ` ${effectResult.filterAttr}` : "";
|
|
3810
|
+
parts.push(
|
|
3811
|
+
`<g transform="${transformAttr}"${filterAttr}><line x1="0" y1="0" x2="${w}" y2="${h}" ${outlineAttr} fill="none"/></g>`
|
|
3812
|
+
);
|
|
3813
|
+
return parts.join("");
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
// src/renderer/image-renderer.ts
|
|
3817
|
+
function renderImage(image) {
|
|
3818
|
+
const w = emuToPixels(image.transform.extentWidth);
|
|
3819
|
+
const h = emuToPixels(image.transform.extentHeight);
|
|
3820
|
+
const transformAttr = buildTransformAttr(image.transform);
|
|
3821
|
+
const effectResult = renderEffects(image.effects);
|
|
3822
|
+
const parts = [];
|
|
3823
|
+
if (effectResult.filterDefs) {
|
|
3824
|
+
parts.push(effectResult.filterDefs);
|
|
3825
|
+
}
|
|
3826
|
+
const filterAttr = effectResult.filterAttr ? ` ${effectResult.filterAttr}` : "";
|
|
3827
|
+
parts.push(
|
|
3828
|
+
`<g transform="${transformAttr}"${filterAttr}><image href="data:${image.mimeType};base64,${image.imageData}" width="${w}" height="${h}" preserveAspectRatio="none"/></g>`
|
|
3829
|
+
);
|
|
3830
|
+
return parts.join("");
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
// src/renderer/chart-renderer.ts
|
|
3834
|
+
var DEFAULT_SERIES_COLORS = [
|
|
3835
|
+
{ hex: "#4472C4", alpha: 1 },
|
|
3836
|
+
{ hex: "#ED7D31", alpha: 1 },
|
|
3837
|
+
{ hex: "#A5A5A5", alpha: 1 },
|
|
3838
|
+
{ hex: "#FFC000", alpha: 1 },
|
|
3839
|
+
{ hex: "#5B9BD5", alpha: 1 },
|
|
3840
|
+
{ hex: "#70AD47", alpha: 1 }
|
|
3841
|
+
];
|
|
3842
|
+
function renderChart(element) {
|
|
3843
|
+
const { transform, chart } = element;
|
|
3844
|
+
const w = emuToPixels(transform.extentWidth);
|
|
3845
|
+
const h = emuToPixels(transform.extentHeight);
|
|
3846
|
+
const transformAttr = buildTransformAttr(transform);
|
|
3847
|
+
const parts = [];
|
|
3848
|
+
parts.push(`<g transform="${transformAttr}">`);
|
|
3849
|
+
parts.push(
|
|
3850
|
+
`<rect width="${w}" height="${h}" fill="#FFFFFF" stroke="#D9D9D9" stroke-width="0.5"/>`
|
|
3851
|
+
);
|
|
3852
|
+
const margin = { top: 20, right: 20, bottom: 30, left: 50 };
|
|
3853
|
+
if (chart.title) {
|
|
3854
|
+
parts.push(renderChartTitle(chart.title, w));
|
|
3855
|
+
margin.top = 40;
|
|
3856
|
+
}
|
|
3857
|
+
if (chart.legend) {
|
|
3858
|
+
if (chart.legend.position === "b") margin.bottom = 50;
|
|
3859
|
+
else if (chart.legend.position === "t") margin.top += 20;
|
|
3860
|
+
}
|
|
3861
|
+
const plotX = margin.left;
|
|
3862
|
+
const plotY = margin.top;
|
|
3863
|
+
const plotW = Math.max(w - margin.left - margin.right, 0);
|
|
3864
|
+
const plotH = Math.max(h - margin.top - margin.bottom, 0);
|
|
3865
|
+
if (plotW > 0 && plotH > 0) {
|
|
3866
|
+
switch (chart.chartType) {
|
|
3867
|
+
case "bar":
|
|
3868
|
+
parts.push(renderBarChart(chart, plotX, plotY, plotW, plotH));
|
|
3869
|
+
break;
|
|
3870
|
+
case "line":
|
|
3871
|
+
parts.push(renderLineChart(chart, plotX, plotY, plotW, plotH));
|
|
3872
|
+
break;
|
|
3873
|
+
case "pie":
|
|
3874
|
+
parts.push(renderPieChart(chart, plotX, plotY, plotW, plotH));
|
|
3875
|
+
break;
|
|
3876
|
+
case "scatter":
|
|
3877
|
+
parts.push(renderScatterChart(chart, plotX, plotY, plotW, plotH));
|
|
3878
|
+
break;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
if (chart.legend && chart.series.length > 0) {
|
|
3882
|
+
parts.push(renderLegend(chart, w, h, chart.legend.position));
|
|
3883
|
+
}
|
|
3884
|
+
parts.push("</g>");
|
|
3885
|
+
return parts.join("");
|
|
3886
|
+
}
|
|
3887
|
+
function renderChartTitle(title, chartWidth) {
|
|
3888
|
+
return `<text x="${round2(chartWidth / 2)}" y="20" text-anchor="middle" font-size="14" font-weight="bold" fill="#404040">${escapeXml2(title)}</text>`;
|
|
3889
|
+
}
|
|
3890
|
+
function renderBarChart(chart, x, y, w, h) {
|
|
3891
|
+
const parts = [];
|
|
3892
|
+
const { series, categories } = chart;
|
|
3893
|
+
if (series.length === 0) return "";
|
|
3894
|
+
const maxVal = getMaxValue(series);
|
|
3895
|
+
if (maxVal === 0) return "";
|
|
3896
|
+
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
3897
|
+
if (catCount === 0) return "";
|
|
3898
|
+
const isHorizontal = chart.barDirection === "bar";
|
|
3899
|
+
parts.push(
|
|
3900
|
+
`<line x1="${round2(x)}" y1="${round2(y + h)}" x2="${round2(x + w)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
3901
|
+
);
|
|
3902
|
+
parts.push(
|
|
3903
|
+
`<line x1="${round2(x)}" y1="${round2(y)}" x2="${round2(x)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
3904
|
+
);
|
|
3905
|
+
if (isHorizontal) {
|
|
3906
|
+
const groupHeight = h / catCount;
|
|
3907
|
+
const barHeight = groupHeight * 0.7 / series.length;
|
|
3908
|
+
const groupPadding = groupHeight * 0.15;
|
|
3909
|
+
for (let c = 0; c < catCount; c++) {
|
|
3910
|
+
const label = categories[c] ?? "";
|
|
3911
|
+
const labelY = y + c * groupHeight + groupHeight / 2;
|
|
3912
|
+
parts.push(
|
|
3913
|
+
`<text x="${round2(x - 5)}" y="${round2(labelY + 4)}" text-anchor="end" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
|
|
3914
|
+
);
|
|
3915
|
+
}
|
|
3916
|
+
for (let s = 0; s < series.length; s++) {
|
|
3917
|
+
const color = series[s].color;
|
|
3918
|
+
for (let c = 0; c < series[s].values.length; c++) {
|
|
3919
|
+
const val = series[s].values[c];
|
|
3920
|
+
const barW = val / maxVal * w;
|
|
3921
|
+
const barX = x;
|
|
3922
|
+
const barY = y + c * groupHeight + groupPadding + s * barHeight;
|
|
3923
|
+
parts.push(
|
|
3924
|
+
`<rect x="${round2(barX)}" y="${round2(barY)}" width="${round2(barW)}" height="${round2(barHeight)}" ${fillAttr(color)}/>`
|
|
3925
|
+
);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
} else {
|
|
3929
|
+
const groupWidth = w / catCount;
|
|
3930
|
+
const barWidth = groupWidth * 0.7 / series.length;
|
|
3931
|
+
const groupPadding = groupWidth * 0.15;
|
|
3932
|
+
for (let c = 0; c < catCount; c++) {
|
|
3933
|
+
const label = categories[c] ?? "";
|
|
3934
|
+
const labelX = x + c * groupWidth + groupWidth / 2;
|
|
3935
|
+
parts.push(
|
|
3936
|
+
`<text x="${round2(labelX)}" y="${round2(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
|
|
3937
|
+
);
|
|
3938
|
+
}
|
|
3939
|
+
for (let s = 0; s < series.length; s++) {
|
|
3940
|
+
const color = series[s].color;
|
|
3941
|
+
for (let c = 0; c < series[s].values.length; c++) {
|
|
3942
|
+
const val = series[s].values[c];
|
|
3943
|
+
const barH = val / maxVal * h;
|
|
3944
|
+
const barX = x + c * groupWidth + groupPadding + s * barWidth;
|
|
3945
|
+
const barY = y + h - barH;
|
|
3946
|
+
parts.push(
|
|
3947
|
+
`<rect x="${round2(barX)}" y="${round2(barY)}" width="${round2(barWidth)}" height="${round2(barH)}" ${fillAttr(color)}/>`
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
return parts.join("");
|
|
3953
|
+
}
|
|
3954
|
+
function renderLineChart(chart, x, y, w, h) {
|
|
3955
|
+
const parts = [];
|
|
3956
|
+
const { series, categories } = chart;
|
|
3957
|
+
if (series.length === 0) return "";
|
|
3958
|
+
const maxVal = getMaxValue(series);
|
|
3959
|
+
if (maxVal === 0) return "";
|
|
3960
|
+
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
3961
|
+
if (catCount === 0) return "";
|
|
3962
|
+
parts.push(
|
|
3963
|
+
`<line x1="${round2(x)}" y1="${round2(y + h)}" x2="${round2(x + w)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
3964
|
+
);
|
|
3965
|
+
parts.push(
|
|
3966
|
+
`<line x1="${round2(x)}" y1="${round2(y)}" x2="${round2(x)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
3967
|
+
);
|
|
3968
|
+
for (let c = 0; c < catCount; c++) {
|
|
3969
|
+
const label = categories[c] ?? "";
|
|
3970
|
+
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
3971
|
+
const labelX = x + c / divisor * w;
|
|
3972
|
+
parts.push(
|
|
3973
|
+
`<text x="${round2(labelX)}" y="${round2(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3976
|
+
for (let s = 0; s < series.length; s++) {
|
|
3977
|
+
const color = series[s].color;
|
|
3978
|
+
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
3979
|
+
const points = series[s].values.map((val, i) => {
|
|
3980
|
+
const px = round2(x + i / divisor * w);
|
|
3981
|
+
const py = round2(y + h - val / maxVal * h);
|
|
3982
|
+
return `${px},${py}`;
|
|
3983
|
+
});
|
|
3984
|
+
parts.push(
|
|
3985
|
+
`<polyline points="${points.join(" ")}" fill="none" stroke="${color.hex}" stroke-width="2"${color.alpha < 1 ? ` stroke-opacity="${color.alpha}"` : ""}/>`
|
|
3986
|
+
);
|
|
3987
|
+
for (const point of points) {
|
|
3988
|
+
const [px, py] = point.split(",");
|
|
3989
|
+
parts.push(`<circle cx="${px}" cy="${py}" r="3" ${fillAttr(color)}/>`);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
return parts.join("");
|
|
3993
|
+
}
|
|
3994
|
+
function renderPieChart(chart, x, y, w, h) {
|
|
3995
|
+
const parts = [];
|
|
3996
|
+
const series = chart.series[0];
|
|
3997
|
+
if (!series || series.values.length === 0) return "";
|
|
3998
|
+
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
3999
|
+
if (total === 0) return "";
|
|
4000
|
+
const cx = x + w / 2;
|
|
4001
|
+
const cy = y + h / 2;
|
|
4002
|
+
const r = Math.min(w, h) / 2 * 0.85;
|
|
4003
|
+
let currentAngle = -Math.PI / 2;
|
|
4004
|
+
for (let i = 0; i < series.values.length; i++) {
|
|
4005
|
+
const val = series.values[i];
|
|
4006
|
+
const sliceAngle = val / total * 2 * Math.PI;
|
|
4007
|
+
const color = getPieSliceColor(i, chart);
|
|
4008
|
+
if (series.values.length === 1) {
|
|
4009
|
+
parts.push(
|
|
4010
|
+
`<circle cx="${round2(cx)}" cy="${round2(cy)}" r="${round2(r)}" ${fillAttr(color)}/>`
|
|
4011
|
+
);
|
|
4012
|
+
} else {
|
|
4013
|
+
const x1 = cx + r * Math.cos(currentAngle);
|
|
4014
|
+
const y1 = cy + r * Math.sin(currentAngle);
|
|
4015
|
+
const x2 = cx + r * Math.cos(currentAngle + sliceAngle);
|
|
4016
|
+
const y2 = cy + r * Math.sin(currentAngle + sliceAngle);
|
|
4017
|
+
const largeArc = sliceAngle > Math.PI ? 1 : 0;
|
|
4018
|
+
parts.push(
|
|
4019
|
+
`<path d="M${round2(cx)},${round2(cy)} L${round2(x1)},${round2(y1)} A${round2(r)},${round2(r)} 0 ${largeArc},1 ${round2(x2)},${round2(y2)} Z" ${fillAttr(color)}/>`
|
|
4020
|
+
);
|
|
4021
|
+
}
|
|
4022
|
+
currentAngle += sliceAngle;
|
|
4023
|
+
}
|
|
4024
|
+
return parts.join("");
|
|
4025
|
+
}
|
|
4026
|
+
function renderScatterChart(chart, x, y, w, h) {
|
|
4027
|
+
const parts = [];
|
|
4028
|
+
const { series } = chart;
|
|
4029
|
+
if (series.length === 0) return "";
|
|
4030
|
+
let maxX = 0;
|
|
4031
|
+
let maxY = 0;
|
|
4032
|
+
for (const s of series) {
|
|
4033
|
+
const xVals = s.xValues ?? [];
|
|
4034
|
+
for (const v of xVals) maxX = Math.max(maxX, v);
|
|
4035
|
+
for (const v of s.values) maxY = Math.max(maxY, v);
|
|
4036
|
+
}
|
|
4037
|
+
if (maxX === 0) maxX = 1;
|
|
4038
|
+
if (maxY === 0) maxY = 1;
|
|
4039
|
+
parts.push(
|
|
4040
|
+
`<line x1="${round2(x)}" y1="${round2(y + h)}" x2="${round2(x + w)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4041
|
+
);
|
|
4042
|
+
parts.push(
|
|
4043
|
+
`<line x1="${round2(x)}" y1="${round2(y)}" x2="${round2(x)}" y2="${round2(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4044
|
+
);
|
|
4045
|
+
for (let s = 0; s < series.length; s++) {
|
|
4046
|
+
const color = series[s].color;
|
|
4047
|
+
const xVals = series[s].xValues ?? [];
|
|
4048
|
+
for (let i = 0; i < series[s].values.length; i++) {
|
|
4049
|
+
const xVal = xVals[i] ?? i;
|
|
4050
|
+
const yVal = series[s].values[i];
|
|
4051
|
+
const px = x + xVal / maxX * w;
|
|
4052
|
+
const py = y + h - yVal / maxY * h;
|
|
4053
|
+
parts.push(`<circle cx="${round2(px)}" cy="${round2(py)}" r="4" ${fillAttr(color)}/>`);
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
return parts.join("");
|
|
4057
|
+
}
|
|
4058
|
+
function renderLegend(chart, chartW, chartH, position) {
|
|
4059
|
+
const parts = [];
|
|
4060
|
+
const entries = chart.chartType === "pie" ? chart.categories.map((cat, i) => ({
|
|
4061
|
+
label: cat,
|
|
4062
|
+
color: getPieSliceColor(i, chart)
|
|
4063
|
+
})) : chart.series.map((s, i) => ({
|
|
4064
|
+
label: s.name ?? `Series ${i + 1}`,
|
|
4065
|
+
color: s.color
|
|
4066
|
+
}));
|
|
4067
|
+
if (entries.length === 0) return "";
|
|
4068
|
+
const entryWidth = 80;
|
|
4069
|
+
const totalWidth = entries.length * entryWidth;
|
|
4070
|
+
const startX = Math.max((chartW - totalWidth) / 2, 5);
|
|
4071
|
+
const legendY = position === "t" ? 25 : chartH - 15;
|
|
4072
|
+
for (let i = 0; i < entries.length; i++) {
|
|
4073
|
+
const ex = startX + i * entryWidth;
|
|
4074
|
+
parts.push(
|
|
4075
|
+
`<rect x="${round2(ex)}" y="${round2(legendY - 6)}" width="12" height="12" ${fillAttr(entries[i].color)}/>`
|
|
4076
|
+
);
|
|
4077
|
+
parts.push(
|
|
4078
|
+
`<text x="${round2(ex + 16)}" y="${round2(legendY + 4)}" font-size="10" fill="#595959">${escapeXml2(entries[i].label)}</text>`
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
return parts.join("");
|
|
4082
|
+
}
|
|
4083
|
+
function getPieSliceColor(index, chart) {
|
|
4084
|
+
const series = chart.series[0];
|
|
4085
|
+
if (series) {
|
|
4086
|
+
return DEFAULT_SERIES_COLORS[index % DEFAULT_SERIES_COLORS.length];
|
|
4087
|
+
}
|
|
4088
|
+
return DEFAULT_SERIES_COLORS[index % DEFAULT_SERIES_COLORS.length];
|
|
4089
|
+
}
|
|
4090
|
+
function getMaxValue(series) {
|
|
4091
|
+
let max = 0;
|
|
4092
|
+
for (const s of series) {
|
|
4093
|
+
for (const v of s.values) {
|
|
4094
|
+
max = Math.max(max, v);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
return max;
|
|
4098
|
+
}
|
|
4099
|
+
function fillAttr(color) {
|
|
4100
|
+
const alpha = color.alpha < 1 ? ` fill-opacity="${color.alpha}"` : "";
|
|
4101
|
+
return `fill="${color.hex}"${alpha}`;
|
|
4102
|
+
}
|
|
4103
|
+
function round2(n) {
|
|
4104
|
+
return Math.round(n * 100) / 100;
|
|
4105
|
+
}
|
|
4106
|
+
function escapeXml2(str) {
|
|
4107
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
// src/renderer/table-renderer.ts
|
|
4111
|
+
function renderTable(element, defs) {
|
|
4112
|
+
const { transform, table } = element;
|
|
4113
|
+
const transformAttr = buildTransformAttr(transform);
|
|
4114
|
+
const colWidths = table.columns.map((col) => emuToPixels(col.width));
|
|
4115
|
+
const rowHeights = table.rows.map((row) => emuToPixels(row.height));
|
|
4116
|
+
const parts = [];
|
|
4117
|
+
parts.push(`<g transform="${transformAttr}">`);
|
|
4118
|
+
let y = 0;
|
|
4119
|
+
for (let rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
|
|
4120
|
+
const row = table.rows[rowIdx];
|
|
4121
|
+
const rowH = rowHeights[rowIdx];
|
|
4122
|
+
let x = 0;
|
|
4123
|
+
let colIdx = 0;
|
|
4124
|
+
for (const cell of row.cells) {
|
|
4125
|
+
if (cell.hMerge || cell.vMerge) {
|
|
4126
|
+
x += colWidths[colIdx] ?? 0;
|
|
4127
|
+
colIdx++;
|
|
4128
|
+
continue;
|
|
4129
|
+
}
|
|
4130
|
+
const cellW = computeSpannedSize(colWidths, colIdx, cell.gridSpan);
|
|
4131
|
+
const cellH = computeSpannedSize(rowHeights, rowIdx, cell.rowSpan);
|
|
4132
|
+
const fillResult = renderFillAttrs(cell.fill);
|
|
4133
|
+
if (fillResult.defs) defs.push(fillResult.defs);
|
|
4134
|
+
parts.push(
|
|
4135
|
+
`<rect x="${x}" y="${y}" width="${cellW}" height="${cellH}" ${fillResult.attrs}/>`
|
|
4136
|
+
);
|
|
4137
|
+
if (cell.borders) {
|
|
4138
|
+
if (cell.borders.top) {
|
|
4139
|
+
const attrs = renderOutlineAttrs(cell.borders.top);
|
|
4140
|
+
parts.push(`<line x1="${x}" y1="${y}" x2="${x + cellW}" y2="${y}" ${attrs}/>`);
|
|
4141
|
+
}
|
|
4142
|
+
if (cell.borders.bottom) {
|
|
4143
|
+
const attrs = renderOutlineAttrs(cell.borders.bottom);
|
|
4144
|
+
parts.push(
|
|
4145
|
+
`<line x1="${x}" y1="${y + cellH}" x2="${x + cellW}" y2="${y + cellH}" ${attrs}/>`
|
|
4146
|
+
);
|
|
4147
|
+
}
|
|
4148
|
+
if (cell.borders.left) {
|
|
4149
|
+
const attrs = renderOutlineAttrs(cell.borders.left);
|
|
4150
|
+
parts.push(`<line x1="${x}" y1="${y}" x2="${x}" y2="${y + cellH}" ${attrs}/>`);
|
|
4151
|
+
}
|
|
4152
|
+
if (cell.borders.right) {
|
|
4153
|
+
const attrs = renderOutlineAttrs(cell.borders.right);
|
|
4154
|
+
parts.push(
|
|
4155
|
+
`<line x1="${x + cellW}" y1="${y}" x2="${x + cellW}" y2="${y + cellH}" ${attrs}/>`
|
|
4156
|
+
);
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
if (cell.textBody) {
|
|
4160
|
+
const cellTransform = {
|
|
4161
|
+
offsetX: 0,
|
|
4162
|
+
offsetY: 0,
|
|
4163
|
+
extentWidth: pixelsToEmu(cellW),
|
|
4164
|
+
extentHeight: pixelsToEmu(cellH),
|
|
4165
|
+
rotation: 0,
|
|
4166
|
+
flipH: false,
|
|
4167
|
+
flipV: false
|
|
4168
|
+
};
|
|
4169
|
+
const textSvg = renderTextBody(cell.textBody, cellTransform);
|
|
4170
|
+
if (textSvg) {
|
|
4171
|
+
parts.push(`<g transform="translate(${x}, ${y})">${textSvg}</g>`);
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
x += colWidths[colIdx] ?? 0;
|
|
4175
|
+
colIdx++;
|
|
4176
|
+
}
|
|
4177
|
+
y += rowH;
|
|
4178
|
+
}
|
|
4179
|
+
parts.push("</g>");
|
|
4180
|
+
return parts.join("");
|
|
4181
|
+
}
|
|
4182
|
+
function computeSpannedSize(sizes, startIdx, span) {
|
|
4183
|
+
let total = 0;
|
|
4184
|
+
for (let i = startIdx; i < startIdx + span && i < sizes.length; i++) {
|
|
4185
|
+
total += sizes[i];
|
|
4186
|
+
}
|
|
4187
|
+
return total;
|
|
4188
|
+
}
|
|
4189
|
+
function pixelsToEmu(px) {
|
|
4190
|
+
return px / 96 * 914400;
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
// src/renderer/svg-renderer.ts
|
|
4194
|
+
function renderSlideToSvg(slide, slideSize) {
|
|
4195
|
+
const width = emuToPixels(slideSize.width);
|
|
4196
|
+
const height = emuToPixels(slideSize.height);
|
|
4197
|
+
const parts = [];
|
|
4198
|
+
const defs = [];
|
|
4199
|
+
parts.push(
|
|
4200
|
+
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">`
|
|
4201
|
+
);
|
|
4202
|
+
if (slide.background?.fill?.type === "image") {
|
|
4203
|
+
const bg = slide.background.fill;
|
|
4204
|
+
parts.push(
|
|
4205
|
+
`<image href="data:${bg.mimeType};base64,${bg.imageData}" width="${width}" height="${height}" preserveAspectRatio="none"/>`
|
|
4206
|
+
);
|
|
4207
|
+
} else if (slide.background?.fill) {
|
|
4208
|
+
const fillResult = renderFillAttrs(slide.background.fill);
|
|
4209
|
+
if (fillResult.defs) defs.push(fillResult.defs);
|
|
4210
|
+
parts.push(`<rect width="${width}" height="${height}" ${fillResult.attrs}/>`);
|
|
4211
|
+
} else {
|
|
4212
|
+
parts.push(`<rect width="${width}" height="${height}" fill="#FFFFFF"/>`);
|
|
4213
|
+
}
|
|
4214
|
+
for (const element of slide.elements) {
|
|
4215
|
+
const rendered = renderElement(element, defs);
|
|
4216
|
+
if (rendered) parts.push(rendered);
|
|
4217
|
+
}
|
|
4218
|
+
if (defs.length > 0) {
|
|
4219
|
+
parts.splice(1, 0, `<defs>${defs.join("")}</defs>`);
|
|
4220
|
+
}
|
|
4221
|
+
parts.push("</svg>");
|
|
4222
|
+
return parts.join("");
|
|
4223
|
+
}
|
|
4224
|
+
function renderElement(element, defs) {
|
|
4225
|
+
switch (element.type) {
|
|
4226
|
+
case "shape": {
|
|
4227
|
+
const result = renderShape(element);
|
|
4228
|
+
extractDefs(result, defs);
|
|
4229
|
+
return removeDefs(result);
|
|
4230
|
+
}
|
|
4231
|
+
case "image": {
|
|
4232
|
+
const imgResult = renderImage(element);
|
|
4233
|
+
extractDefs(imgResult, defs);
|
|
4234
|
+
return removeDefs(imgResult);
|
|
4235
|
+
}
|
|
4236
|
+
case "connector": {
|
|
4237
|
+
const cxnResult = renderConnector(element);
|
|
4238
|
+
extractDefs(cxnResult, defs);
|
|
4239
|
+
return removeDefs(cxnResult);
|
|
4240
|
+
}
|
|
4241
|
+
case "group":
|
|
4242
|
+
return renderGroup(element, defs);
|
|
4243
|
+
case "chart":
|
|
4244
|
+
return renderChart(element);
|
|
4245
|
+
case "table":
|
|
4246
|
+
return renderTable(element, defs);
|
|
4247
|
+
default:
|
|
4248
|
+
return null;
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
function renderGroup(group, defs) {
|
|
4252
|
+
const x = emuToPixels(group.transform.offsetX);
|
|
4253
|
+
const y = emuToPixels(group.transform.offsetY);
|
|
4254
|
+
const w = emuToPixels(group.transform.extentWidth);
|
|
4255
|
+
const h = emuToPixels(group.transform.extentHeight);
|
|
4256
|
+
const chW = emuToPixels(group.childTransform.extentWidth);
|
|
4257
|
+
const chH = emuToPixels(group.childTransform.extentHeight);
|
|
4258
|
+
const chX = emuToPixels(group.childTransform.offsetX);
|
|
4259
|
+
const chY = emuToPixels(group.childTransform.offsetY);
|
|
4260
|
+
const scaleX = chW !== 0 ? w / chW : 1;
|
|
4261
|
+
const scaleY = chH !== 0 ? h / chH : 1;
|
|
4262
|
+
const parts = [];
|
|
4263
|
+
parts.push(
|
|
4264
|
+
`<g transform="translate(${x}, ${y}) scale(${scaleX}, ${scaleY}) translate(${-chX}, ${-chY})">`
|
|
4265
|
+
);
|
|
4266
|
+
for (const child of group.children) {
|
|
4267
|
+
const rendered = renderElement(child, defs);
|
|
4268
|
+
if (rendered) parts.push(rendered);
|
|
4269
|
+
}
|
|
4270
|
+
parts.push("</g>");
|
|
4271
|
+
return parts.join("");
|
|
4272
|
+
}
|
|
4273
|
+
function extractDefs(svgFragment, defs) {
|
|
4274
|
+
const gradientMatch = svgFragment.match(/<linearGradient[^]*?<\/linearGradient>/g);
|
|
4275
|
+
if (gradientMatch) {
|
|
4276
|
+
defs.push(...gradientMatch);
|
|
4277
|
+
}
|
|
4278
|
+
const filterMatch = svgFragment.match(/<filter[^]*?<\/filter>/g);
|
|
4279
|
+
if (filterMatch) {
|
|
4280
|
+
defs.push(...filterMatch);
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
function removeDefs(svgFragment) {
|
|
4284
|
+
return svgFragment.replace(/<linearGradient[^]*?<\/linearGradient>/g, "").replace(/<filter[^]*?<\/filter>/g, "");
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
// src/png/png-converter.ts
|
|
4288
|
+
import sharp from "sharp";
|
|
4289
|
+
async function svgToPng(svgString, options) {
|
|
4290
|
+
const svgBuffer = Buffer.from(svgString);
|
|
4291
|
+
let pipeline = sharp(svgBuffer);
|
|
4292
|
+
if (options?.width || options?.height) {
|
|
4293
|
+
pipeline = pipeline.resize({
|
|
4294
|
+
width: options.width,
|
|
4295
|
+
height: options.height,
|
|
4296
|
+
fit: "contain",
|
|
4297
|
+
background: { r: 255, g: 255, b: 255, alpha: 1 }
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
const result = await pipeline.png().toBuffer({ resolveWithObject: true });
|
|
4301
|
+
return {
|
|
4302
|
+
png: result.data,
|
|
4303
|
+
width: result.info.width,
|
|
4304
|
+
height: result.info.height
|
|
4305
|
+
};
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
// src/converter.ts
|
|
4309
|
+
async function convertPptxToSvg(input, options) {
|
|
4310
|
+
const archive = await readPptx(input);
|
|
4311
|
+
const presentationXml = archive.files.get("ppt/presentation.xml");
|
|
4312
|
+
if (!presentationXml) throw new Error("Invalid PPTX: missing ppt/presentation.xml");
|
|
4313
|
+
const presInfo = parsePresentation(presentationXml);
|
|
4314
|
+
const presRelsXml = archive.files.get("ppt/_rels/presentation.xml.rels");
|
|
4315
|
+
const presRels = presRelsXml ? parseRelationships(presRelsXml) : /* @__PURE__ */ new Map();
|
|
4316
|
+
let theme = {
|
|
4317
|
+
colorScheme: defaultColorScheme2(),
|
|
4318
|
+
fontScheme: {
|
|
4319
|
+
majorFont: "Calibri",
|
|
4320
|
+
minorFont: "Calibri",
|
|
4321
|
+
majorFontEa: null,
|
|
4322
|
+
minorFontEa: null
|
|
4323
|
+
}
|
|
4324
|
+
};
|
|
4325
|
+
for (const [, rel] of presRels) {
|
|
4326
|
+
if (rel.type.includes("theme")) {
|
|
4327
|
+
const themePath = resolveRelationshipTarget("ppt/presentation.xml", rel.target);
|
|
4328
|
+
const themeXml = archive.files.get(themePath);
|
|
4329
|
+
if (themeXml) {
|
|
4330
|
+
theme = parseTheme(themeXml);
|
|
4331
|
+
}
|
|
4332
|
+
break;
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
let colorMap = defaultColorMap();
|
|
4336
|
+
let masterPath = null;
|
|
4337
|
+
for (const [, rel] of presRels) {
|
|
4338
|
+
if (rel.type.includes("slideMaster")) {
|
|
4339
|
+
masterPath = resolveRelationshipTarget("ppt/presentation.xml", rel.target);
|
|
4340
|
+
const masterXml2 = archive.files.get(masterPath);
|
|
4341
|
+
if (masterXml2) {
|
|
4342
|
+
colorMap = parseSlideMasterColorMap(masterXml2);
|
|
4343
|
+
}
|
|
4344
|
+
break;
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
const colorResolver = new ColorResolver(theme.colorScheme, colorMap);
|
|
4348
|
+
const masterXml = masterPath ? archive.files.get(masterPath) : void 0;
|
|
4349
|
+
let masterFillContext;
|
|
4350
|
+
if (masterPath) {
|
|
4351
|
+
const masterRelsPath = buildRelsPath(masterPath);
|
|
4352
|
+
const masterRelsXml = archive.files.get(masterRelsPath);
|
|
4353
|
+
const masterRels = masterRelsXml ? parseRelationships(masterRelsXml) : /* @__PURE__ */ new Map();
|
|
4354
|
+
masterFillContext = { rels: masterRels, archive, basePath: masterPath };
|
|
4355
|
+
}
|
|
4356
|
+
const masterBackground = masterXml ? parseSlideMasterBackground(masterXml, colorResolver, masterFillContext) : null;
|
|
4357
|
+
const masterElements = masterPath && masterXml ? parseSlideMasterElements(masterXml, masterPath, archive, colorResolver) : [];
|
|
4358
|
+
const slidePaths = [];
|
|
4359
|
+
for (let i = 0; i < presInfo.slideRIds.length; i++) {
|
|
4360
|
+
const rId = presInfo.slideRIds[i];
|
|
4361
|
+
const rel = presRels.get(rId);
|
|
4362
|
+
if (rel) {
|
|
4363
|
+
const path = resolveRelationshipTarget("ppt/presentation.xml", rel.target);
|
|
4364
|
+
slidePaths.push({ slideNumber: i + 1, path });
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
const targetSlides = options?.slides ? slidePaths.filter((s) => options.slides.includes(s.slideNumber)) : slidePaths;
|
|
4368
|
+
const results = [];
|
|
4369
|
+
for (const { slideNumber, path } of targetSlides) {
|
|
4370
|
+
const slideXml = archive.files.get(path);
|
|
4371
|
+
if (!slideXml) continue;
|
|
4372
|
+
const slide = parseSlide(slideXml, path, slideNumber, archive, colorResolver);
|
|
4373
|
+
let layoutElements = [];
|
|
4374
|
+
const slideRelsPath = buildRelsPath(path);
|
|
4375
|
+
const slideRelsXml = archive.files.get(slideRelsPath);
|
|
4376
|
+
if (slideRelsXml) {
|
|
4377
|
+
const slideRels = parseRelationships(slideRelsXml);
|
|
4378
|
+
for (const [, rel] of slideRels) {
|
|
4379
|
+
if (rel.type.includes("slideLayout")) {
|
|
4380
|
+
const layoutPath = resolveRelationshipTarget(path, rel.target);
|
|
4381
|
+
const layoutXml = archive.files.get(layoutPath);
|
|
4382
|
+
if (layoutXml) {
|
|
4383
|
+
if (!slide.background) {
|
|
4384
|
+
const layoutRelsPath = buildRelsPath(layoutPath);
|
|
4385
|
+
const layoutRelsXml = archive.files.get(layoutRelsPath);
|
|
4386
|
+
const layoutRels = layoutRelsXml ? parseRelationships(layoutRelsXml) : /* @__PURE__ */ new Map();
|
|
4387
|
+
const layoutFillContext = {
|
|
4388
|
+
rels: layoutRels,
|
|
4389
|
+
archive,
|
|
4390
|
+
basePath: layoutPath
|
|
4391
|
+
};
|
|
4392
|
+
slide.background = parseSlideLayoutBackground(
|
|
4393
|
+
layoutXml,
|
|
4394
|
+
colorResolver,
|
|
4395
|
+
layoutFillContext
|
|
4396
|
+
);
|
|
4397
|
+
}
|
|
4398
|
+
layoutElements = parseSlideLayoutElements(
|
|
4399
|
+
layoutXml,
|
|
4400
|
+
layoutPath,
|
|
4401
|
+
archive,
|
|
4402
|
+
colorResolver
|
|
4403
|
+
);
|
|
4404
|
+
}
|
|
4405
|
+
break;
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
if (!slide.background) {
|
|
4410
|
+
slide.background = masterBackground;
|
|
4411
|
+
}
|
|
4412
|
+
slide.elements = mergeElements(masterElements, layoutElements, slide.elements);
|
|
4413
|
+
const svg = renderSlideToSvg(slide, presInfo.slideSize);
|
|
4414
|
+
results.push({ slideNumber, svg });
|
|
4415
|
+
}
|
|
4416
|
+
return results;
|
|
4417
|
+
}
|
|
4418
|
+
async function convertPptxToPng(input, options) {
|
|
4419
|
+
const svgResults = await convertPptxToSvg(input, options);
|
|
4420
|
+
const width = options?.width ?? DEFAULT_OUTPUT_WIDTH;
|
|
4421
|
+
const height = options?.height;
|
|
4422
|
+
const results = [];
|
|
4423
|
+
for (const { slideNumber, svg } of svgResults) {
|
|
4424
|
+
const pngResult = await svgToPng(svg, { width, height });
|
|
4425
|
+
results.push({
|
|
4426
|
+
slideNumber,
|
|
4427
|
+
png: pngResult.png,
|
|
4428
|
+
width: pngResult.width,
|
|
4429
|
+
height: pngResult.height
|
|
4430
|
+
});
|
|
4431
|
+
}
|
|
4432
|
+
return results;
|
|
4433
|
+
}
|
|
4434
|
+
function collectPlaceholderTypes(elements) {
|
|
4435
|
+
const types = /* @__PURE__ */ new Set();
|
|
4436
|
+
for (const el of elements) {
|
|
4437
|
+
if (el.type === "shape" && el.placeholderType) {
|
|
4438
|
+
types.add(el.placeholderType);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
return types;
|
|
4442
|
+
}
|
|
4443
|
+
function filterByPlaceholder(elements, excludeTypes) {
|
|
4444
|
+
return elements.filter((el) => {
|
|
4445
|
+
if (el.type !== "shape") return true;
|
|
4446
|
+
if (!el.placeholderType) return true;
|
|
4447
|
+
return !excludeTypes.has(el.placeholderType);
|
|
4448
|
+
});
|
|
4449
|
+
}
|
|
4450
|
+
function mergeElements(masterElements, layoutElements, slideElements) {
|
|
4451
|
+
const slidePh = collectPlaceholderTypes(slideElements);
|
|
4452
|
+
const layoutPh = collectPlaceholderTypes(layoutElements);
|
|
4453
|
+
const allOverrides = /* @__PURE__ */ new Set([...slidePh, ...layoutPh]);
|
|
4454
|
+
const filteredMaster = filterByPlaceholder(masterElements, allOverrides);
|
|
4455
|
+
const filteredLayout = filterByPlaceholder(layoutElements, slidePh);
|
|
4456
|
+
return [...filteredMaster, ...filteredLayout, ...slideElements];
|
|
4457
|
+
}
|
|
4458
|
+
function defaultColorScheme2() {
|
|
4459
|
+
return {
|
|
4460
|
+
dk1: "#000000",
|
|
4461
|
+
lt1: "#FFFFFF",
|
|
4462
|
+
dk2: "#44546A",
|
|
4463
|
+
lt2: "#E7E6E6",
|
|
4464
|
+
accent1: "#4472C4",
|
|
4465
|
+
accent2: "#ED7D31",
|
|
4466
|
+
accent3: "#A5A5A5",
|
|
4467
|
+
accent4: "#FFC000",
|
|
4468
|
+
accent5: "#5B9BD5",
|
|
4469
|
+
accent6: "#70AD47",
|
|
4470
|
+
hlink: "#0563C1",
|
|
4471
|
+
folHlink: "#954F72"
|
|
4472
|
+
};
|
|
4473
|
+
}
|
|
4474
|
+
function defaultColorMap() {
|
|
4475
|
+
return {
|
|
4476
|
+
bg1: "lt1",
|
|
4477
|
+
tx1: "dk1",
|
|
4478
|
+
bg2: "lt2",
|
|
4479
|
+
tx2: "dk2",
|
|
4480
|
+
accent1: "accent1",
|
|
4481
|
+
accent2: "accent2",
|
|
4482
|
+
accent3: "accent3",
|
|
4483
|
+
accent4: "accent4",
|
|
4484
|
+
accent5: "accent5",
|
|
4485
|
+
accent6: "accent6",
|
|
4486
|
+
hlink: "hlink",
|
|
4487
|
+
folHlink: "folHlink"
|
|
4488
|
+
};
|
|
4489
|
+
}
|
|
4490
|
+
export {
|
|
4491
|
+
convertPptxToPng,
|
|
4492
|
+
convertPptxToSvg
|
|
4493
|
+
};
|