html-native-pptx 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 +124 -0
- package/dist/index.d.mts +337 -0
- package/dist/index.d.ts +337 -0
- package/dist/index.js +1090 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1037 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs2 = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var _ttf2eot = require('ttf2eot');
|
|
6
|
+
var puppeteer = require('puppeteer');
|
|
7
|
+
var JSZip = require('jszip');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
function _interopNamespace(e) {
|
|
12
|
+
if (e && e.__esModule) return e;
|
|
13
|
+
var n = Object.create(null);
|
|
14
|
+
if (e) {
|
|
15
|
+
Object.keys(e).forEach(function (k) {
|
|
16
|
+
if (k !== 'default') {
|
|
17
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return e[k]; }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
n.default = e;
|
|
26
|
+
return Object.freeze(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
|
|
30
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
31
|
+
var _ttf2eot__default = /*#__PURE__*/_interopDefault(_ttf2eot);
|
|
32
|
+
var puppeteer__default = /*#__PURE__*/_interopDefault(puppeteer);
|
|
33
|
+
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
34
|
+
|
|
35
|
+
// src/types/ir.ts
|
|
36
|
+
var UNITS = {
|
|
37
|
+
DPI: 96,
|
|
38
|
+
PT_PER_INCH: 72,
|
|
39
|
+
EMU_PER_INCH: 914400,
|
|
40
|
+
SLIDE_16_9_WIDTH_INCHES: 13.333333,
|
|
41
|
+
SLIDE_16_9_HEIGHT_INCHES: 7.5,
|
|
42
|
+
SLIDE_16_9_WIDTH_EMU: 12192e3,
|
|
43
|
+
SLIDE_16_9_HEIGHT_EMU: 6858e3,
|
|
44
|
+
SLIDE_4_3_WIDTH_INCHES: 10,
|
|
45
|
+
SLIDE_4_3_HEIGHT_INCHES: 7.5,
|
|
46
|
+
SLIDE_4_3_WIDTH_EMU: 9144e3,
|
|
47
|
+
SLIDE_4_3_HEIGHT_EMU: 6858e3
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/normalizer/units.ts
|
|
51
|
+
function pxToInches(px, dpi = UNITS.DPI, scale = 1) {
|
|
52
|
+
return px / dpi * scale;
|
|
53
|
+
}
|
|
54
|
+
function inchesToEmu(inches) {
|
|
55
|
+
return Math.round(inches * UNITS.EMU_PER_INCH);
|
|
56
|
+
}
|
|
57
|
+
function pxToEmu(px, dpi = UNITS.DPI, scale = 1) {
|
|
58
|
+
return inchesToEmu(pxToInches(px, dpi, scale));
|
|
59
|
+
}
|
|
60
|
+
function pxToPt(px, dpi = UNITS.DPI) {
|
|
61
|
+
return px * UNITS.PT_PER_INCH / dpi;
|
|
62
|
+
}
|
|
63
|
+
function ptToHundredthPt(pt) {
|
|
64
|
+
return Math.round(pt * 100);
|
|
65
|
+
}
|
|
66
|
+
function borderRadiusToGuide(radiusPx, widthPx, heightPx) {
|
|
67
|
+
const minSide = Math.min(widthPx, heightPx);
|
|
68
|
+
if (minSide <= 0 || radiusPx <= 0) return 0;
|
|
69
|
+
const maxRadius = minSide / 2;
|
|
70
|
+
const ratio = Math.min(1, Math.max(0, radiusPx / maxRadius));
|
|
71
|
+
return Math.round(ratio * 5e4);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/normalizer/styles.ts
|
|
75
|
+
var NAMED_COLORS = {
|
|
76
|
+
black: "000000",
|
|
77
|
+
white: "FFFFFF",
|
|
78
|
+
red: "FF0000",
|
|
79
|
+
green: "008000",
|
|
80
|
+
blue: "0000FF",
|
|
81
|
+
yellow: "FFFF00",
|
|
82
|
+
cyan: "00FFFF",
|
|
83
|
+
magenta: "FF00FF",
|
|
84
|
+
silver: "C0C0C0",
|
|
85
|
+
gray: "808080",
|
|
86
|
+
grey: "808080",
|
|
87
|
+
maroon: "800000",
|
|
88
|
+
olive: "808000",
|
|
89
|
+
purple: "800080",
|
|
90
|
+
teal: "008080",
|
|
91
|
+
navy: "000080",
|
|
92
|
+
orange: "FFA500",
|
|
93
|
+
pink: "FFC0CB"
|
|
94
|
+
};
|
|
95
|
+
function normalizeHexColor(cssColor) {
|
|
96
|
+
if (!cssColor) return void 0;
|
|
97
|
+
const trimmed = cssColor.trim().toLowerCase();
|
|
98
|
+
if (trimmed === "transparent" || trimmed === "rgba(0, 0, 0, 0)" || trimmed === "none") {
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
if (NAMED_COLORS[trimmed]) {
|
|
102
|
+
return { hex: NAMED_COLORS[trimmed], opacity: 1 };
|
|
103
|
+
}
|
|
104
|
+
const rgbMatch = trimmed.match(
|
|
105
|
+
/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/
|
|
106
|
+
);
|
|
107
|
+
if (rgbMatch) {
|
|
108
|
+
const r = Math.min(255, Math.max(0, parseInt(rgbMatch[1], 10)));
|
|
109
|
+
const g = Math.min(255, Math.max(0, parseInt(rgbMatch[2], 10)));
|
|
110
|
+
const b = Math.min(255, Math.max(0, parseInt(rgbMatch[3], 10)));
|
|
111
|
+
const a = rgbMatch[4] !== void 0 ? parseFloat(rgbMatch[4]) : 1;
|
|
112
|
+
if (a <= 0) return void 0;
|
|
113
|
+
const hex = [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
114
|
+
return { hex, opacity: a };
|
|
115
|
+
}
|
|
116
|
+
if (trimmed.startsWith("#")) {
|
|
117
|
+
const raw = trimmed.slice(1);
|
|
118
|
+
if (raw.length === 3) {
|
|
119
|
+
const hex = raw.split("").map((c) => c + c).join("").toUpperCase();
|
|
120
|
+
return { hex, opacity: 1 };
|
|
121
|
+
}
|
|
122
|
+
if (raw.length === 4) {
|
|
123
|
+
const hex = raw.slice(0, 3).split("").map((c) => c + c).join("").toUpperCase();
|
|
124
|
+
const alphaHex = raw[3] + raw[3];
|
|
125
|
+
const opacity = parseInt(alphaHex, 16) / 255;
|
|
126
|
+
return opacity <= 0 ? void 0 : { hex, opacity };
|
|
127
|
+
}
|
|
128
|
+
if (raw.length === 6) {
|
|
129
|
+
return { hex: raw.toUpperCase(), opacity: 1 };
|
|
130
|
+
}
|
|
131
|
+
if (raw.length === 8) {
|
|
132
|
+
const hex = raw.slice(0, 6).toUpperCase();
|
|
133
|
+
const opacity = parseInt(raw.slice(6, 8), 16) / 255;
|
|
134
|
+
return opacity <= 0 ? void 0 : { hex, opacity };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function normalizeFontWeight(fontWeight) {
|
|
140
|
+
if (!fontWeight) return false;
|
|
141
|
+
if (typeof fontWeight === "number") return fontWeight >= 600;
|
|
142
|
+
const lower = fontWeight.trim().toLowerCase();
|
|
143
|
+
if (lower === "bold" || lower === "bolder") return true;
|
|
144
|
+
const num = parseInt(lower, 10);
|
|
145
|
+
return !isNaN(num) && num >= 600;
|
|
146
|
+
}
|
|
147
|
+
function normalizeFontStyle(fontStyle) {
|
|
148
|
+
if (!fontStyle) return false;
|
|
149
|
+
const lower = fontStyle.trim().toLowerCase();
|
|
150
|
+
return lower === "italic" || lower === "oblique";
|
|
151
|
+
}
|
|
152
|
+
function normalizeFontFamily(fontFamily) {
|
|
153
|
+
if (!fontFamily) return "Arial";
|
|
154
|
+
const first = fontFamily.split(",")[0].trim();
|
|
155
|
+
const cleaned = first.replace(/^['"]+|['"]+$/g, "");
|
|
156
|
+
return cleaned || "Arial";
|
|
157
|
+
}
|
|
158
|
+
function normalizeTextAlign(textAlign) {
|
|
159
|
+
if (!textAlign) return "left";
|
|
160
|
+
const lower = textAlign.trim().toLowerCase();
|
|
161
|
+
if (lower === "center") return "center";
|
|
162
|
+
if (lower === "right") return "right";
|
|
163
|
+
if (lower === "justify") return "justify";
|
|
164
|
+
return "left";
|
|
165
|
+
}
|
|
166
|
+
function normalizeBorderStyle(borderStyle) {
|
|
167
|
+
if (!borderStyle) return "solid";
|
|
168
|
+
const lower = borderStyle.trim().toLowerCase();
|
|
169
|
+
if (lower === "dashed") return "dashed";
|
|
170
|
+
if (lower === "dotted") return "dotted";
|
|
171
|
+
return "solid";
|
|
172
|
+
}
|
|
173
|
+
var ttf2eot = _ttf2eot__default.default.default || _ttf2eot__default.default;
|
|
174
|
+
function cleanTypeface(name) {
|
|
175
|
+
if (!name) return "";
|
|
176
|
+
const first = name.split(",")[0].trim();
|
|
177
|
+
return first.replace(/^['"]+|['"]+$/g, "").trim();
|
|
178
|
+
}
|
|
179
|
+
function isEotBuffer(buf) {
|
|
180
|
+
if (!buf || buf.length < 36) return false;
|
|
181
|
+
const magic = buf.readUInt16LE(34);
|
|
182
|
+
return magic === 20556;
|
|
183
|
+
}
|
|
184
|
+
function convertTtfToEot(buf) {
|
|
185
|
+
if (isEotBuffer(buf)) {
|
|
186
|
+
return buf;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const uint8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
190
|
+
const result = ttf2eot(uint8);
|
|
191
|
+
return Buffer.from(result.buffer, result.byteOffset, result.byteLength);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
return buf;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function loadFontBuffer(src, basePath) {
|
|
197
|
+
if (Buffer.isBuffer(src)) {
|
|
198
|
+
return src;
|
|
199
|
+
}
|
|
200
|
+
if (typeof src !== "string" || src.trim().length === 0) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const trimmed = src.trim();
|
|
204
|
+
if (trimmed.startsWith("data:")) {
|
|
205
|
+
const commaIdx = trimmed.indexOf(",");
|
|
206
|
+
if (commaIdx !== -1) {
|
|
207
|
+
return Buffer.from(trimmed.slice(commaIdx + 1), "base64");
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
212
|
+
try {
|
|
213
|
+
const res = await fetch(trimmed);
|
|
214
|
+
if (!res.ok) return null;
|
|
215
|
+
const arrayBuf = await res.arrayBuffer();
|
|
216
|
+
return Buffer.from(arrayBuf);
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const cleanPath = trimmed.split("?")[0].split("#")[0];
|
|
223
|
+
const candidatePaths = [
|
|
224
|
+
path__namespace.isAbsolute(cleanPath) ? cleanPath : path__namespace.resolve(basePath || process.cwd(), cleanPath),
|
|
225
|
+
basePath ? path__namespace.resolve(basePath, path__namespace.basename(cleanPath)) : null,
|
|
226
|
+
path__namespace.resolve(process.cwd(), cleanPath)
|
|
227
|
+
].filter(Boolean);
|
|
228
|
+
for (const p of candidatePaths) {
|
|
229
|
+
if (fs2__namespace.existsSync(p)) {
|
|
230
|
+
return await fs2__namespace.promises.readFile(p);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function extractFontFacesFromHtml(html) {
|
|
238
|
+
const results = [];
|
|
239
|
+
if (!html) return results;
|
|
240
|
+
const fontFaceRegex = /@font-face\s*\{([^}]+)\}/gi;
|
|
241
|
+
let match;
|
|
242
|
+
while ((match = fontFaceRegex.exec(html)) !== null) {
|
|
243
|
+
const block = match[1];
|
|
244
|
+
const familyMatch = block.match(/font-family\s*:\s*['"]?([^'";\n\r]+)['"]?/i);
|
|
245
|
+
const srcMatch = block.match(/src\s*:\s*[^;]*url\((['"]?)(.*?)\1\)/i);
|
|
246
|
+
if (familyMatch && srcMatch) {
|
|
247
|
+
const typeface = cleanTypeface(familyMatch[1]);
|
|
248
|
+
const src = srcMatch[2].trim();
|
|
249
|
+
if (typeface && src) {
|
|
250
|
+
results.push({ typeface, src });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return results;
|
|
255
|
+
}
|
|
256
|
+
async function resolveAndEmbedFonts(options) {
|
|
257
|
+
const fontMap = /* @__PURE__ */ new Map();
|
|
258
|
+
if (options.autoEmbedFonts !== false) {
|
|
259
|
+
if (options.html) {
|
|
260
|
+
const fromHtml = extractFontFacesFromHtml(options.html);
|
|
261
|
+
for (const item of fromHtml) {
|
|
262
|
+
fontMap.set(item.typeface.toLowerCase(), item);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (options.discoveredFonts) {
|
|
266
|
+
for (const item of options.discoveredFonts) {
|
|
267
|
+
const cleaned = cleanTypeface(item.typeface);
|
|
268
|
+
if (cleaned && item.src) {
|
|
269
|
+
fontMap.set(cleaned.toLowerCase(), { typeface: cleaned, src: item.src });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (options.userFonts) {
|
|
275
|
+
for (const f of options.userFonts) {
|
|
276
|
+
const cleaned = cleanTypeface(f.typeface);
|
|
277
|
+
if (cleaned) {
|
|
278
|
+
fontMap.set(cleaned.toLowerCase(), { typeface: cleaned, src: f.src });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const fontOpt of fontMap.values()) {
|
|
284
|
+
try {
|
|
285
|
+
const buf = await loadFontBuffer(fontOpt.src, options.basePath);
|
|
286
|
+
if (!buf || buf.length === 0) continue;
|
|
287
|
+
const fntData = convertTtfToEot(buf);
|
|
288
|
+
results.push({
|
|
289
|
+
typeface: fontOpt.typeface,
|
|
290
|
+
fntData
|
|
291
|
+
});
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return results;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/harvester/traverser.ts
|
|
299
|
+
function extractDomToSlideIR(options) {
|
|
300
|
+
const DPI = 96;
|
|
301
|
+
const is16_9 = options.aspect !== "4:3";
|
|
302
|
+
const SLIDE_W = is16_9 ? 13.333333 : 10;
|
|
303
|
+
const SLIDE_H = 7.5;
|
|
304
|
+
SLIDE_W / (options.viewportWidth / DPI);
|
|
305
|
+
SLIDE_H / (options.viewportHeight / DPI);
|
|
306
|
+
const nodes = [];
|
|
307
|
+
let nodeIdCounter = 1;
|
|
308
|
+
function toHex(cssColor) {
|
|
309
|
+
if (!cssColor) return void 0;
|
|
310
|
+
const trimmed = cssColor.trim().toLowerCase();
|
|
311
|
+
if (trimmed === "transparent" || trimmed === "rgba(0, 0, 0, 0)" || trimmed === "none") {
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
const rgbMatch = trimmed.match(
|
|
315
|
+
/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/
|
|
316
|
+
);
|
|
317
|
+
if (rgbMatch) {
|
|
318
|
+
const a = rgbMatch[4] !== void 0 ? parseFloat(rgbMatch[4]) : 1;
|
|
319
|
+
if (a <= 0) return void 0;
|
|
320
|
+
const r = parseInt(rgbMatch[1], 10);
|
|
321
|
+
const g = parseInt(rgbMatch[2], 10);
|
|
322
|
+
const b = parseInt(rgbMatch[3], 10);
|
|
323
|
+
return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
|
|
324
|
+
}
|
|
325
|
+
if (trimmed.startsWith("#")) {
|
|
326
|
+
const raw = trimmed.slice(1);
|
|
327
|
+
if (raw.length === 3) {
|
|
328
|
+
return raw.split("").map((c) => c + c).join("").toUpperCase();
|
|
329
|
+
}
|
|
330
|
+
if (raw.length >= 6) {
|
|
331
|
+
return raw.slice(0, 6).toUpperCase();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return void 0;
|
|
335
|
+
}
|
|
336
|
+
function cleanFontFamily(fontFamily) {
|
|
337
|
+
const first = fontFamily.split(",")[0].trim();
|
|
338
|
+
return first.replace(/^['"]+|['"]+$/g, "") || "Arial";
|
|
339
|
+
}
|
|
340
|
+
function isBold(weight) {
|
|
341
|
+
if (weight === "bold" || weight === "bolder") return true;
|
|
342
|
+
const num = parseInt(weight, 10);
|
|
343
|
+
return !isNaN(num) && num >= 600;
|
|
344
|
+
}
|
|
345
|
+
function isInlineFormattingTag(tagName) {
|
|
346
|
+
return ["SPAN", "B", "STRONG", "I", "EM", "U", "S", "A", "CODE", "MARK", "SMALL"].includes(
|
|
347
|
+
tagName
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
function isTextBlockTag(tagName) {
|
|
351
|
+
return ["P", "H1", "H2", "H3", "H4", "H5", "H6", "LI", "BLOCKQUOTE"].includes(
|
|
352
|
+
tagName
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
const root = (options.selector ? document.querySelector(options.selector) : null) || document.body;
|
|
356
|
+
const rootStyle = window.getComputedStyle(root);
|
|
357
|
+
const slideBg = toHex(rootStyle.backgroundColor);
|
|
358
|
+
const rootRect = root.getBoundingClientRect();
|
|
359
|
+
const rootW = rootRect.width > 0 ? rootRect.width : options.viewportWidth;
|
|
360
|
+
const rootH = rootRect.height > 0 ? rootRect.height : options.viewportHeight;
|
|
361
|
+
function collectTextRuns(parentEl) {
|
|
362
|
+
const runs = [];
|
|
363
|
+
function traverseNodes(node) {
|
|
364
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
365
|
+
const text = node.textContent;
|
|
366
|
+
if (text && text.length > 0) {
|
|
367
|
+
const parent = node.parentElement || parentEl;
|
|
368
|
+
const style = window.getComputedStyle(parent);
|
|
369
|
+
runs.push({
|
|
370
|
+
content: text,
|
|
371
|
+
fontFamily: cleanFontFamily(style.fontFamily),
|
|
372
|
+
fontSize: parseFloat(style.fontSize) * 72 / DPI,
|
|
373
|
+
color: toHex(style.color) || "000000",
|
|
374
|
+
bold: isBold(style.fontWeight),
|
|
375
|
+
italic: style.fontStyle === "italic" || style.fontStyle === "oblique",
|
|
376
|
+
underline: style.textDecorationLine?.includes("underline"),
|
|
377
|
+
strikethrough: style.textDecorationLine?.includes("line-through")
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
381
|
+
const el = node;
|
|
382
|
+
const style = window.getComputedStyle(el);
|
|
383
|
+
if (style.display === "none" || style.visibility === "hidden") return;
|
|
384
|
+
Array.from(node.childNodes).forEach(traverseNodes);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
Array.from(parentEl.childNodes).forEach(traverseNodes);
|
|
388
|
+
return runs;
|
|
389
|
+
}
|
|
390
|
+
function walk(el) {
|
|
391
|
+
if (el === root) {
|
|
392
|
+
Array.from(el.children).forEach((child) => walk(child));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const style = window.getComputedStyle(el);
|
|
396
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const rect = el.getBoundingClientRect();
|
|
400
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
401
|
+
const box = {
|
|
402
|
+
x: (rect.left - rootRect.left) / rootW * SLIDE_W,
|
|
403
|
+
y: (rect.top - rootRect.top) / rootH * SLIDE_H,
|
|
404
|
+
w: rect.width / rootW * SLIDE_W,
|
|
405
|
+
h: rect.height / rootH * SLIDE_H
|
|
406
|
+
};
|
|
407
|
+
const hasBg = style.backgroundColor && style.backgroundColor !== "transparent" && style.backgroundColor !== "rgba(0, 0, 0, 0)";
|
|
408
|
+
const hasBorder = parseFloat(style.borderWidth) > 0 && style.borderStyle !== "none";
|
|
409
|
+
if (hasBg || hasBorder) {
|
|
410
|
+
nodes.push({
|
|
411
|
+
id: nodeIdCounter++,
|
|
412
|
+
name: `${el.tagName.toLowerCase()}-box`,
|
|
413
|
+
type: "container",
|
|
414
|
+
box,
|
|
415
|
+
shapeStyle: {
|
|
416
|
+
fillColor: hasBg ? toHex(style.backgroundColor) : void 0,
|
|
417
|
+
borderColor: hasBorder ? toHex(style.borderColor) : void 0,
|
|
418
|
+
borderWidth: hasBorder ? parseFloat(style.borderWidth) * 72 / DPI : 0,
|
|
419
|
+
radius: parseFloat(style.borderRadius) || 0
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (el.tagName === "IMG") {
|
|
424
|
+
const img = el;
|
|
425
|
+
if (img.src) {
|
|
426
|
+
nodes.push({
|
|
427
|
+
id: nodeIdCounter++,
|
|
428
|
+
name: "image",
|
|
429
|
+
type: "image",
|
|
430
|
+
box,
|
|
431
|
+
content: img.src
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const hasText = (el.textContent || "").trim().length > 0;
|
|
437
|
+
const isExplicitTextBlock = isTextBlockTag(el.tagName);
|
|
438
|
+
const hasOnlyInlineChildren = el.childElementCount > 0 && Array.from(el.children).every((c) => isInlineFormattingTag(c.tagName));
|
|
439
|
+
const isLeafText = el.childElementCount === 0 && hasText;
|
|
440
|
+
if (hasText && (isExplicitTextBlock || isLeafText || hasOnlyInlineChildren)) {
|
|
441
|
+
const runs = collectTextRuns(el);
|
|
442
|
+
if (runs.length > 0) {
|
|
443
|
+
const primaryRun = runs[0];
|
|
444
|
+
const textAlign = style.textAlign || "left";
|
|
445
|
+
nodes.push({
|
|
446
|
+
id: nodeIdCounter++,
|
|
447
|
+
name: `${el.tagName.toLowerCase()}-text`,
|
|
448
|
+
type: "text",
|
|
449
|
+
box,
|
|
450
|
+
content: el.textContent?.trim() || "",
|
|
451
|
+
textStyle: {
|
|
452
|
+
fontFamily: primaryRun.fontFamily || "Arial",
|
|
453
|
+
fontSize: primaryRun.fontSize || 16,
|
|
454
|
+
color: primaryRun.color || "000000",
|
|
455
|
+
bold: primaryRun.bold || false,
|
|
456
|
+
italic: primaryRun.italic || false,
|
|
457
|
+
underline: primaryRun.underline || false,
|
|
458
|
+
align: textAlign
|
|
459
|
+
},
|
|
460
|
+
paragraphs: [
|
|
461
|
+
{
|
|
462
|
+
align: textAlign,
|
|
463
|
+
runs
|
|
464
|
+
}
|
|
465
|
+
]
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
Array.from(el.children).forEach((child) => walk(child));
|
|
471
|
+
}
|
|
472
|
+
if (root) {
|
|
473
|
+
walk(root);
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
width: SLIDE_W,
|
|
477
|
+
height: SLIDE_H,
|
|
478
|
+
backgroundColor: slideBg,
|
|
479
|
+
elements: nodes
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/harvester/browser.ts
|
|
484
|
+
async function harvestHtmlToIR(htmlOrUrl, options = {}) {
|
|
485
|
+
const is16_9 = options.aspect !== "4:3";
|
|
486
|
+
const defaultViewport = is16_9 ? { width: 1920, height: 1080, deviceScaleFactor: 1 } : { width: 1024, height: 768, deviceScaleFactor: 1 };
|
|
487
|
+
const viewport = {
|
|
488
|
+
...defaultViewport,
|
|
489
|
+
...options.viewport || {}
|
|
490
|
+
};
|
|
491
|
+
let effectiveBasePath = options.basePath;
|
|
492
|
+
let contentToRender = htmlOrUrl;
|
|
493
|
+
try {
|
|
494
|
+
if (!htmlOrUrl.startsWith("http://") && !htmlOrUrl.startsWith("https://") && !htmlOrUrl.startsWith("file://") && !htmlOrUrl.trim().startsWith("<") && fs2__namespace.existsSync(htmlOrUrl) && fs2__namespace.statSync(htmlOrUrl).isFile()) {
|
|
495
|
+
if (!effectiveBasePath) {
|
|
496
|
+
effectiveBasePath = path__namespace.dirname(path__namespace.resolve(htmlOrUrl));
|
|
497
|
+
}
|
|
498
|
+
contentToRender = await fs2__namespace.promises.readFile(htmlOrUrl, "utf-8");
|
|
499
|
+
}
|
|
500
|
+
} catch {
|
|
501
|
+
}
|
|
502
|
+
let browser = null;
|
|
503
|
+
let shouldCloseBrowser = false;
|
|
504
|
+
try {
|
|
505
|
+
if (options.browser) {
|
|
506
|
+
browser = options.browser;
|
|
507
|
+
} else if (options.browserWSEndpoint) {
|
|
508
|
+
browser = await puppeteer__default.default.connect({
|
|
509
|
+
browserWSEndpoint: options.browserWSEndpoint
|
|
510
|
+
});
|
|
511
|
+
shouldCloseBrowser = true;
|
|
512
|
+
} else {
|
|
513
|
+
browser = await puppeteer__default.default.launch({
|
|
514
|
+
headless: true,
|
|
515
|
+
args: [
|
|
516
|
+
"--no-sandbox",
|
|
517
|
+
"--disable-setuid-sandbox",
|
|
518
|
+
"--disable-dev-shm-usage",
|
|
519
|
+
"--disable-gpu"
|
|
520
|
+
]
|
|
521
|
+
});
|
|
522
|
+
shouldCloseBrowser = true;
|
|
523
|
+
}
|
|
524
|
+
const page = await browser.newPage();
|
|
525
|
+
await page.setViewport(viewport);
|
|
526
|
+
const isUrl = contentToRender.startsWith("http://") || contentToRender.startsWith("https://") || contentToRender.startsWith("file://");
|
|
527
|
+
const waitUntil = options.waitUntil || "load";
|
|
528
|
+
const timeout = options.timeout || 3e4;
|
|
529
|
+
if (isUrl) {
|
|
530
|
+
await page.goto(contentToRender, { waitUntil, timeout });
|
|
531
|
+
} else {
|
|
532
|
+
await page.setContent(contentToRender, { waitUntil, timeout });
|
|
533
|
+
}
|
|
534
|
+
await page.evaluate(() => {
|
|
535
|
+
window.__name = (t) => t;
|
|
536
|
+
globalThis.__name = (t) => t;
|
|
537
|
+
});
|
|
538
|
+
const slideSelector = options.slideSelector;
|
|
539
|
+
let slideElementsCount = 0;
|
|
540
|
+
if (slideSelector) {
|
|
541
|
+
slideElementsCount = await page.evaluate((sel) => {
|
|
542
|
+
return document.querySelectorAll(sel).length;
|
|
543
|
+
}, slideSelector);
|
|
544
|
+
}
|
|
545
|
+
let discoveredFonts = [];
|
|
546
|
+
if (options.autoEmbedFonts !== false) {
|
|
547
|
+
try {
|
|
548
|
+
discoveredFonts = await page.evaluate(() => {
|
|
549
|
+
const results = [];
|
|
550
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
551
|
+
try {
|
|
552
|
+
for (const rule of Array.from(sheet.cssRules || [])) {
|
|
553
|
+
if (rule instanceof CSSFontFaceRule) {
|
|
554
|
+
const family = rule.style.fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
555
|
+
const styleAny = rule.style;
|
|
556
|
+
const srcVal = styleAny.src || rule.style.getPropertyValue("src") || "";
|
|
557
|
+
const srcMatch = srcVal.match(/url\((['"]?)(.*?)\1\)/);
|
|
558
|
+
if (srcMatch && srcMatch[2]) {
|
|
559
|
+
results.push({ typeface: family, src: srcMatch[2] });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return results;
|
|
567
|
+
});
|
|
568
|
+
} catch {
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const embeddedFonts = await resolveAndEmbedFonts({
|
|
572
|
+
html: isUrl ? void 0 : contentToRender,
|
|
573
|
+
userFonts: options.fonts,
|
|
574
|
+
autoEmbedFonts: options.autoEmbedFonts,
|
|
575
|
+
basePath: effectiveBasePath,
|
|
576
|
+
discoveredFonts
|
|
577
|
+
});
|
|
578
|
+
if (slideSelector && slideElementsCount > 1) {
|
|
579
|
+
const slides = [];
|
|
580
|
+
for (let i = 0; i < slideElementsCount; i++) {
|
|
581
|
+
await page.evaluate(
|
|
582
|
+
({ sel, activeIdx }) => {
|
|
583
|
+
const allSlides = document.querySelectorAll(sel);
|
|
584
|
+
allSlides.forEach((el, idx) => {
|
|
585
|
+
const htmlEl = el;
|
|
586
|
+
if (idx === activeIdx) {
|
|
587
|
+
htmlEl.classList.add("active");
|
|
588
|
+
htmlEl.style.display = "flex";
|
|
589
|
+
htmlEl.style.visibility = "visible";
|
|
590
|
+
htmlEl.setAttribute("data-current-harvest", "true");
|
|
591
|
+
} else {
|
|
592
|
+
htmlEl.classList.remove("active");
|
|
593
|
+
htmlEl.style.display = "none";
|
|
594
|
+
htmlEl.removeAttribute("data-current-harvest");
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
},
|
|
598
|
+
{ sel: slideSelector, activeIdx: i }
|
|
599
|
+
);
|
|
600
|
+
const harvestOptions2 = {
|
|
601
|
+
selector: `${slideSelector}[data-current-harvest="true"]`,
|
|
602
|
+
viewportWidth: viewport.width,
|
|
603
|
+
viewportHeight: viewport.height,
|
|
604
|
+
aspect: options.aspect || "16:9"
|
|
605
|
+
};
|
|
606
|
+
const slideIR = await page.evaluate(extractDomToSlideIR, harvestOptions2);
|
|
607
|
+
slides.push(slideIR);
|
|
608
|
+
}
|
|
609
|
+
if (slides.length > 0) {
|
|
610
|
+
slides[0].fonts = embeddedFonts;
|
|
611
|
+
}
|
|
612
|
+
await page.close();
|
|
613
|
+
return slides;
|
|
614
|
+
}
|
|
615
|
+
const harvestOptions = {
|
|
616
|
+
selector: options.selector || "body",
|
|
617
|
+
viewportWidth: viewport.width,
|
|
618
|
+
viewportHeight: viewport.height,
|
|
619
|
+
aspect: options.aspect || "16:9"
|
|
620
|
+
};
|
|
621
|
+
const singleSlideIR = await page.evaluate(extractDomToSlideIR, harvestOptions);
|
|
622
|
+
singleSlideIR.fonts = embeddedFonts;
|
|
623
|
+
await page.close();
|
|
624
|
+
return singleSlideIR;
|
|
625
|
+
} finally {
|
|
626
|
+
if (shouldCloseBrowser && browser) {
|
|
627
|
+
await browser.close().catch(() => {
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// src/compiler/templates.ts
|
|
634
|
+
var ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
635
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
636
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
|
637
|
+
</Relationships>`;
|
|
638
|
+
var SLIDE_LAYOUT_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
639
|
+
<p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank" preserve="1">
|
|
640
|
+
<p:cSld name="Blank">
|
|
641
|
+
<p:spTree>
|
|
642
|
+
<p:nvGrpSpPr>
|
|
643
|
+
<p:cNvPr id="1" name=""/>
|
|
644
|
+
<p:cNvGrpSpPr/>
|
|
645
|
+
<p:nvPr/>
|
|
646
|
+
</p:nvGrpSpPr>
|
|
647
|
+
<p:grpSpPr>
|
|
648
|
+
<a:xfrm>
|
|
649
|
+
<a:off x="0" y="0"/>
|
|
650
|
+
<a:ext cx="0" cy="0"/>
|
|
651
|
+
<a:chOff x="0" y="0"/>
|
|
652
|
+
<a:chExt cx="0" cy="0"/>
|
|
653
|
+
</a:xfrm>
|
|
654
|
+
</p:grpSpPr>
|
|
655
|
+
</p:spTree>
|
|
656
|
+
</p:cSld>
|
|
657
|
+
<p:clrMapOvr>
|
|
658
|
+
<a:masterClrMapping/>
|
|
659
|
+
</p:clrMapOvr>
|
|
660
|
+
</p:sldLayout>`;
|
|
661
|
+
var SLIDE_LAYOUT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
662
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
663
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
|
|
664
|
+
</Relationships>`;
|
|
665
|
+
var SLIDE_MASTER_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
666
|
+
<p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
|
667
|
+
<p:cSld name="Master">
|
|
668
|
+
<p:spTree>
|
|
669
|
+
<p:nvGrpSpPr>
|
|
670
|
+
<p:cNvPr id="1" name=""/>
|
|
671
|
+
<p:cNvGrpSpPr/>
|
|
672
|
+
<p:nvPr/>
|
|
673
|
+
</p:nvGrpSpPr>
|
|
674
|
+
<p:grpSpPr>
|
|
675
|
+
<a:xfrm>
|
|
676
|
+
<a:off x="0" y="0"/>
|
|
677
|
+
<a:ext cx="0" cy="0"/>
|
|
678
|
+
<a:chOff x="0" y="0"/>
|
|
679
|
+
<a:chExt cx="0" cy="0"/>
|
|
680
|
+
</a:xfrm>
|
|
681
|
+
</p:grpSpPr>
|
|
682
|
+
</p:spTree>
|
|
683
|
+
</p:cSld>
|
|
684
|
+
<p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>
|
|
685
|
+
<p:sldLayoutIdLst>
|
|
686
|
+
<p:sldLayoutId id="2147483649" r:id="rId1"/>
|
|
687
|
+
</p:sldLayoutIdLst>
|
|
688
|
+
<p:txStyles>
|
|
689
|
+
<p:titleStyle/>
|
|
690
|
+
<p:bodyStyle/>
|
|
691
|
+
<p:otherStyle/>
|
|
692
|
+
</p:txStyles>
|
|
693
|
+
</p:sldMaster>`;
|
|
694
|
+
var SLIDE_MASTER_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
695
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
696
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
|
697
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
|
|
698
|
+
</Relationships>`;
|
|
699
|
+
var THEME_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
700
|
+
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:srgbClr val="000000"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="0E2841"/></a:dk2><a:lt2><a:srgbClr val="E8E8E8"/></a:lt2><a:accent1><a:srgbClr val="156082"/></a:accent1><a:accent2><a:srgbClr val="E97132"/></a:accent2><a:accent3><a:srgbClr val="196B24"/></a:accent3><a:accent4><a:srgbClr val="0F9ED5"/></a:accent4><a:accent5><a:srgbClr val="A02B93"/></a:accent5><a:accent6><a:srgbClr val="4EA72E"/></a:accent6><a:hlink><a:srgbClr val="467886"/></a:hlink><a:folHlink><a:srgbClr val="96607D"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Calibri Light"/><a:ea typeface="Calibri Light"/><a:cs typeface="Calibri Light"/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface="Calibri"/><a:cs typeface="Calibri"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:solidFill><a:schemeClr val="dk1"/></a:solidFill><a:solidFill><a:schemeClr val="accent1"/></a:solidFill></a:fillStyleLst><a:lnStyleLst><a:ln w="12700"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="19050"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="25400"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000"><a:srgbClr val="000000"><a:alpha val="16000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="19050" dist="9525" dir="5400000"><a:srgbClr val="000000"><a:alpha val="6000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="28575" dist="9525" dir="5400000"><a:srgbClr val="000000"><a:alpha val="8000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000"><a:srgbClr val="000000"><a:alpha val="10000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="114300" dist="38100" dir="5400000"><a:srgbClr val="000000"><a:alpha val="12000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="171450" dist="57150" dir="5400000"><a:srgbClr val="000000"><a:alpha val="16000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="266700" dist="95250" dir="5400000"><a:srgbClr val="000000"><a:alpha val="24000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill><a:gradFill><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:shade val="98000"/><a:lumMod val="102000"/><a:satMod val="150000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:shade val="90000"/><a:lumMod val="103000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/></a:theme>`;
|
|
701
|
+
var SLIDE_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
702
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
703
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
|
704
|
+
</Relationships>`;
|
|
705
|
+
function escapeXml(unsafe) {
|
|
706
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
707
|
+
}
|
|
708
|
+
function createPresentationXml(widthEmu, heightEmu, is16_9 = true, slideCount = 1, fonts) {
|
|
709
|
+
const typeAttr = is16_9 ? "screen16x9" : "screen4x3";
|
|
710
|
+
const sldIdLst = Array.from({ length: slideCount }, (_, idx) => {
|
|
711
|
+
return ` <p:sldId id="${256 + idx}" r:id="rId${idx + 1}"/>`;
|
|
712
|
+
}).join("\n");
|
|
713
|
+
const hasFonts = fonts && fonts.length > 0;
|
|
714
|
+
const embedAttr = hasFonts ? ' embedTrueTypeFonts="true"' : "";
|
|
715
|
+
let embeddedFontLstXml = "";
|
|
716
|
+
if (hasFonts) {
|
|
717
|
+
const fontItems = fonts.map(
|
|
718
|
+
(f, idx) => ` <p:embeddedFont>
|
|
719
|
+
<p:font typeface="${escapeXml(f.typeface)}"/>
|
|
720
|
+
<p:regular r:id="rIdFont${idx + 1}"/>
|
|
721
|
+
</p:embeddedFont>`
|
|
722
|
+
).join("\n");
|
|
723
|
+
embeddedFontLstXml = `
|
|
724
|
+
<p:embeddedFontLst>
|
|
725
|
+
${fontItems}
|
|
726
|
+
</p:embeddedFontLst>`;
|
|
727
|
+
}
|
|
728
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
729
|
+
<p:presentation${embedAttr} xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
|
730
|
+
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
731
|
+
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
|
732
|
+
<p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rIdMaster1"/></p:sldMasterIdLst>
|
|
733
|
+
<p:sldIdLst>
|
|
734
|
+
${sldIdLst}
|
|
735
|
+
</p:sldIdLst>
|
|
736
|
+
<p:sldSz cx="${widthEmu}" cy="${heightEmu}" type="${typeAttr}"/>
|
|
737
|
+
<p:notesSz cx="6858000" cy="9144000"/>${embeddedFontLstXml}
|
|
738
|
+
</p:presentation>`;
|
|
739
|
+
}
|
|
740
|
+
function createPresentationRelsXml(slideCount = 1, fonts) {
|
|
741
|
+
const slideRels = Array.from({ length: slideCount }, (_, idx) => {
|
|
742
|
+
return ` <Relationship Id="rId${idx + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${idx + 1}.xml"/>`;
|
|
743
|
+
}).join("\n");
|
|
744
|
+
let fontRels = "";
|
|
745
|
+
if (fonts && fonts.length > 0) {
|
|
746
|
+
fontRels = "\n" + fonts.map(
|
|
747
|
+
(_, idx) => ` <Relationship Id="rIdFont${idx + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/font" Target="fonts/font${idx + 1}.fntdata"/>`
|
|
748
|
+
).join("\n");
|
|
749
|
+
}
|
|
750
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
751
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
752
|
+
<Relationship Id="rIdMaster1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
|
|
753
|
+
${slideRels}${fontRels}
|
|
754
|
+
</Relationships>`;
|
|
755
|
+
}
|
|
756
|
+
function createContentTypesXml(slideCount = 1, hasFonts = false) {
|
|
757
|
+
const slideOverrides = Array.from({ length: slideCount }, (_, idx) => {
|
|
758
|
+
return ` <Override PartName="/ppt/slides/slide${idx + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`;
|
|
759
|
+
}).join("\n");
|
|
760
|
+
const fontExtension = hasFonts ? ' <Default Extension="fntdata" ContentType="application/x-fontdata"/>\n' : "";
|
|
761
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
762
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
763
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
764
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
765
|
+
${fontExtension} <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
|
766
|
+
<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
|
|
767
|
+
<Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
|
|
768
|
+
<Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
|
|
769
|
+
${slideOverrides}
|
|
770
|
+
</Types>`;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/compiler/shapes.ts
|
|
774
|
+
function escapeXml2(unsafe) {
|
|
775
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
776
|
+
}
|
|
777
|
+
function compileContainerShape(node, id) {
|
|
778
|
+
const x = inchesToEmu(node.box.x);
|
|
779
|
+
const y = inchesToEmu(node.box.y);
|
|
780
|
+
const cx = inchesToEmu(node.box.w);
|
|
781
|
+
const cy = inchesToEmu(node.box.h);
|
|
782
|
+
const name = escapeXml2(node.name || `Box ${id}`);
|
|
783
|
+
const shapeStyle = node.shapeStyle || {};
|
|
784
|
+
let geometry = "rect";
|
|
785
|
+
let avLst = "<a:avLst/>";
|
|
786
|
+
if (shapeStyle.radius && shapeStyle.radius > 0) {
|
|
787
|
+
geometry = "roundRect";
|
|
788
|
+
const minDimensionInches = Math.min(node.box.w, node.box.h);
|
|
789
|
+
if (minDimensionInches > 0) {
|
|
790
|
+
const radiusInches = shapeStyle.radius / UNITS.DPI;
|
|
791
|
+
const maxRadiusInches = minDimensionInches / 2;
|
|
792
|
+
const ratio = Math.min(1, Math.max(0, radiusInches / maxRadiusInches));
|
|
793
|
+
const adj = Math.round(ratio * 5e4);
|
|
794
|
+
avLst = `<a:avLst><a:gd name="adj" fmla="val ${adj}"/></a:avLst>`;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
let fillXml = "<a:noFill/>";
|
|
798
|
+
if (shapeStyle.fillColor) {
|
|
799
|
+
const opacityVal = shapeStyle.fillOpacity !== void 0 && shapeStyle.fillOpacity < 1 ? `<a:alpha val="${Math.round(shapeStyle.fillOpacity * 1e5)}"/>` : "";
|
|
800
|
+
fillXml = `<a:solidFill><a:srgbClr val="${shapeStyle.fillColor}">${opacityVal}</a:srgbClr></a:solidFill>`;
|
|
801
|
+
}
|
|
802
|
+
let borderXml = "";
|
|
803
|
+
if (shapeStyle.borderColor && shapeStyle.borderWidth && shapeStyle.borderWidth > 0) {
|
|
804
|
+
const borderEmu = Math.round(shapeStyle.borderWidth * 12700);
|
|
805
|
+
borderXml = `<a:ln w="${borderEmu}"><a:solidFill><a:srgbClr val="${shapeStyle.borderColor}"/></a:solidFill></a:ln>`;
|
|
806
|
+
}
|
|
807
|
+
return `
|
|
808
|
+
<p:sp>
|
|
809
|
+
<p:nvSpPr>
|
|
810
|
+
<p:cNvPr id="${id}" name="${name}"/>
|
|
811
|
+
<p:cNvSpPr/>
|
|
812
|
+
<p:nvPr/>
|
|
813
|
+
</p:nvSpPr>
|
|
814
|
+
<p:spPr>
|
|
815
|
+
<a:xfrm>
|
|
816
|
+
<a:off x="${x}" y="${y}"/>
|
|
817
|
+
<a:ext cx="${cx}" cy="${cy}"/>
|
|
818
|
+
</a:xfrm>
|
|
819
|
+
<a:prstGeom prst="${geometry}">
|
|
820
|
+
${avLst}
|
|
821
|
+
</a:prstGeom>
|
|
822
|
+
${fillXml}
|
|
823
|
+
${borderXml}
|
|
824
|
+
</p:spPr>
|
|
825
|
+
</p:sp>`.trim();
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// src/compiler/texts.ts
|
|
829
|
+
function escapeXml3(unsafe) {
|
|
830
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
831
|
+
}
|
|
832
|
+
function mapAlign(align) {
|
|
833
|
+
switch (align) {
|
|
834
|
+
case "center":
|
|
835
|
+
return "ctr";
|
|
836
|
+
case "right":
|
|
837
|
+
return "r";
|
|
838
|
+
case "justify":
|
|
839
|
+
return "just";
|
|
840
|
+
case "left":
|
|
841
|
+
default:
|
|
842
|
+
return "l";
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function compileRun(run, fallbackFont, fallbackColor, fallbackSize) {
|
|
846
|
+
const font = escapeXml3(run.fontFamily || fallbackFont);
|
|
847
|
+
const color = run.color || fallbackColor;
|
|
848
|
+
const sz = ptToHundredthPt(run.fontSize || fallbackSize);
|
|
849
|
+
const b = run.bold ? ' b="1"' : ' b="0"';
|
|
850
|
+
const i = run.italic ? ' i="1"' : ' i="0"';
|
|
851
|
+
const u = run.underline ? ' u="sng"' : "";
|
|
852
|
+
const strike = run.strikethrough ? ' strike="sngStrike"' : "";
|
|
853
|
+
const text = escapeXml3(run.content);
|
|
854
|
+
return `
|
|
855
|
+
<a:r>
|
|
856
|
+
<a:rPr${b}${i}${u}${strike} sz="${sz}">
|
|
857
|
+
<a:solidFill>
|
|
858
|
+
<a:srgbClr val="${color}"/>
|
|
859
|
+
</a:solidFill>
|
|
860
|
+
<a:latin typeface="${font}"/>
|
|
861
|
+
</a:rPr>
|
|
862
|
+
<a:t>${text}</a:t>
|
|
863
|
+
</a:r>`.trim();
|
|
864
|
+
}
|
|
865
|
+
function compileParagraph(para, fallbackFont, fallbackColor, fallbackSize) {
|
|
866
|
+
const algn = mapAlign(para.align);
|
|
867
|
+
const runsXml = para.runs.map((run) => compileRun(run, fallbackFont, fallbackColor, fallbackSize)).join("\n");
|
|
868
|
+
return `
|
|
869
|
+
<a:p>
|
|
870
|
+
<a:pPr algn="${algn}"/>
|
|
871
|
+
${runsXml}
|
|
872
|
+
</a:p>`.trim();
|
|
873
|
+
}
|
|
874
|
+
function compileTextShape(node, id) {
|
|
875
|
+
const x = inchesToEmu(node.box.x);
|
|
876
|
+
const y = inchesToEmu(node.box.y);
|
|
877
|
+
const cx = inchesToEmu(node.box.w);
|
|
878
|
+
const cy = inchesToEmu(node.box.h);
|
|
879
|
+
const name = escapeXml3(node.name || `Text ${id}`);
|
|
880
|
+
const defaultFont = node.textStyle?.fontFamily || "Arial";
|
|
881
|
+
const defaultColor = node.textStyle?.color || "000000";
|
|
882
|
+
const defaultSize = node.textStyle?.fontSize || 16;
|
|
883
|
+
const defaultAlign = node.textStyle?.align || "left";
|
|
884
|
+
let paragraphsXml = "";
|
|
885
|
+
if (node.paragraphs && node.paragraphs.length > 0) {
|
|
886
|
+
paragraphsXml = node.paragraphs.map((p) => compileParagraph(p, defaultFont, defaultColor, defaultSize)).join("\n");
|
|
887
|
+
} else {
|
|
888
|
+
const singleRun = {
|
|
889
|
+
content: node.content || "",
|
|
890
|
+
fontFamily: defaultFont,
|
|
891
|
+
fontSize: defaultSize,
|
|
892
|
+
color: defaultColor,
|
|
893
|
+
bold: node.textStyle?.bold,
|
|
894
|
+
italic: node.textStyle?.italic,
|
|
895
|
+
underline: node.textStyle?.underline
|
|
896
|
+
};
|
|
897
|
+
paragraphsXml = compileParagraph(
|
|
898
|
+
{ align: defaultAlign, runs: [singleRun] },
|
|
899
|
+
defaultFont,
|
|
900
|
+
defaultColor,
|
|
901
|
+
defaultSize
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
return `
|
|
905
|
+
<p:sp>
|
|
906
|
+
<p:nvSpPr>
|
|
907
|
+
<p:cNvPr id="${id}" name="${name}"/>
|
|
908
|
+
<p:cNvSpPr txBox="1"/>
|
|
909
|
+
<p:nvPr/>
|
|
910
|
+
</p:nvSpPr>
|
|
911
|
+
<p:spPr>
|
|
912
|
+
<a:xfrm>
|
|
913
|
+
<a:off x="${x}" y="${y}"/>
|
|
914
|
+
<a:ext cx="${cx}" cy="${cy}"/>
|
|
915
|
+
</a:xfrm>
|
|
916
|
+
<a:prstGeom prst="rect">
|
|
917
|
+
<a:avLst/>
|
|
918
|
+
</a:prstGeom>
|
|
919
|
+
<a:noFill/>
|
|
920
|
+
</p:spPr>
|
|
921
|
+
<p:txBody>
|
|
922
|
+
<a:bodyPr wrap="square" rtlCol="0" lIns="0" tIns="0" rIns="0" bIns="0">
|
|
923
|
+
<a:spAutoFit/>
|
|
924
|
+
</a:bodyPr>
|
|
925
|
+
<a:lstStyle/>
|
|
926
|
+
${paragraphsXml}
|
|
927
|
+
</p:txBody>
|
|
928
|
+
</p:sp>`.trim();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/compiler/packager.ts
|
|
932
|
+
async function compileSlideToPptx(slideOrSlidesOrPres, options) {
|
|
933
|
+
let slides;
|
|
934
|
+
let irFonts = [];
|
|
935
|
+
if ("slides" in slideOrSlidesOrPres) {
|
|
936
|
+
slides = slideOrSlidesOrPres.slides;
|
|
937
|
+
irFonts = slideOrSlidesOrPres.fonts || [];
|
|
938
|
+
} else if (Array.isArray(slideOrSlidesOrPres)) {
|
|
939
|
+
slides = slideOrSlidesOrPres;
|
|
940
|
+
irFonts = slides.flatMap((s) => s.fonts || []);
|
|
941
|
+
} else {
|
|
942
|
+
slides = [slideOrSlidesOrPres];
|
|
943
|
+
irFonts = slideOrSlidesOrPres.fonts || [];
|
|
944
|
+
}
|
|
945
|
+
if (slides.length === 0) {
|
|
946
|
+
throw new Error("Cannot compile empty presentation without slides.");
|
|
947
|
+
}
|
|
948
|
+
const fontMap = /* @__PURE__ */ new Map();
|
|
949
|
+
for (const f of [...irFonts, ...options?.fonts || []]) {
|
|
950
|
+
if (f && f.typeface && f.fntData) {
|
|
951
|
+
fontMap.set(f.typeface.toLowerCase(), f);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
const fonts = Array.from(fontMap.values());
|
|
955
|
+
const hasFonts = fonts.length > 0;
|
|
956
|
+
const primarySlide = slides[0];
|
|
957
|
+
const widthEmu = inchesToEmu(primarySlide.width);
|
|
958
|
+
const heightEmu = inchesToEmu(primarySlide.height);
|
|
959
|
+
const is16_9 = Math.abs(primarySlide.width / primarySlide.height - 16 / 9) < 0.1;
|
|
960
|
+
const slideCount = slides.length;
|
|
961
|
+
const zip = new JSZip__default.default();
|
|
962
|
+
zip.file("[Content_Types].xml", createContentTypesXml(slideCount, hasFonts));
|
|
963
|
+
zip.folder("_rels")?.file(".rels", ROOT_RELS_XML);
|
|
964
|
+
const pptFolder = zip.folder("ppt");
|
|
965
|
+
pptFolder?.file(
|
|
966
|
+
"presentation.xml",
|
|
967
|
+
createPresentationXml(widthEmu, heightEmu, is16_9, slideCount, fonts)
|
|
968
|
+
);
|
|
969
|
+
pptFolder?.folder("_rels")?.file("presentation.xml.rels", createPresentationRelsXml(slideCount, fonts));
|
|
970
|
+
if (hasFonts) {
|
|
971
|
+
const fontsFolder = pptFolder?.folder("fonts");
|
|
972
|
+
fonts.forEach((font, idx) => {
|
|
973
|
+
fontsFolder?.file(`font${idx + 1}.fntdata`, font.fntData);
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
pptFolder?.folder("theme")?.file("theme1.xml", THEME_XML);
|
|
977
|
+
const masterFolder = pptFolder?.folder("slideMasters");
|
|
978
|
+
masterFolder?.file("slideMaster1.xml", SLIDE_MASTER_XML);
|
|
979
|
+
masterFolder?.folder("_rels")?.file("slideMaster1.xml.rels", SLIDE_MASTER_RELS_XML);
|
|
980
|
+
const layoutFolder = pptFolder?.folder("slideLayouts");
|
|
981
|
+
layoutFolder?.file("slideLayout1.xml", SLIDE_LAYOUT_XML);
|
|
982
|
+
layoutFolder?.folder("_rels")?.file("slideLayout1.xml.rels", SLIDE_LAYOUT_RELS_XML);
|
|
983
|
+
const slidesFolder = pptFolder?.folder("slides");
|
|
984
|
+
const slidesRelsFolder = slidesFolder?.folder("_rels");
|
|
985
|
+
slides.forEach((slide, idx) => {
|
|
986
|
+
let shapeIdCounter = 2;
|
|
987
|
+
const shapesXmlArray = [];
|
|
988
|
+
for (const node of slide.elements) {
|
|
989
|
+
if (node.type === "container") {
|
|
990
|
+
shapesXmlArray.push(compileContainerShape(node, shapeIdCounter++));
|
|
991
|
+
} else if (node.type === "text") {
|
|
992
|
+
shapesXmlArray.push(compileTextShape(node, shapeIdCounter++));
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
const shapesXml = shapesXmlArray.join("\n");
|
|
996
|
+
let bgXml = "";
|
|
997
|
+
if (slide.backgroundColor) {
|
|
998
|
+
bgXml = `
|
|
999
|
+
<p:bg>
|
|
1000
|
+
<p:bgPr>
|
|
1001
|
+
<a:solidFill>
|
|
1002
|
+
<a:srgbClr val="${slide.backgroundColor}"/>
|
|
1003
|
+
</a:solidFill>
|
|
1004
|
+
<a:effectLst/>
|
|
1005
|
+
</p:bgPr>
|
|
1006
|
+
</p:bg>`;
|
|
1007
|
+
}
|
|
1008
|
+
const slideXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1009
|
+
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
|
1010
|
+
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
1011
|
+
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
|
1012
|
+
<p:cSld>${bgXml}
|
|
1013
|
+
<p:spTree>
|
|
1014
|
+
<p:nvGrpSpPr>
|
|
1015
|
+
<p:cNvPr id="1" name=""/>
|
|
1016
|
+
<p:cNvGrpSpPr/>
|
|
1017
|
+
<p:nvPr/>
|
|
1018
|
+
</p:nvGrpSpPr>
|
|
1019
|
+
<p:grpSpPr>
|
|
1020
|
+
<a:xfrm>
|
|
1021
|
+
<a:off x="0" y="0"/>
|
|
1022
|
+
<a:ext cx="0" cy="0"/>
|
|
1023
|
+
<a:chOff x="0" y="0"/>
|
|
1024
|
+
<a:chExt cx="0" cy="0"/>
|
|
1025
|
+
</a:xfrm>
|
|
1026
|
+
</p:grpSpPr>
|
|
1027
|
+
${shapesXml}
|
|
1028
|
+
</p:spTree>
|
|
1029
|
+
</p:cSld>
|
|
1030
|
+
<p:clrMapOvr>
|
|
1031
|
+
<a:masterClrMapping/>
|
|
1032
|
+
</p:clrMapOvr>
|
|
1033
|
+
</p:sld>`;
|
|
1034
|
+
slidesFolder?.file(`slide${idx + 1}.xml`, slideXml);
|
|
1035
|
+
slidesRelsFolder?.file(`slide${idx + 1}.xml.rels`, SLIDE_RELS_XML);
|
|
1036
|
+
});
|
|
1037
|
+
const buffer = await zip.generateAsync({
|
|
1038
|
+
type: "nodebuffer",
|
|
1039
|
+
compression: "DEFLATE",
|
|
1040
|
+
compressionOptions: { level: 6 }
|
|
1041
|
+
});
|
|
1042
|
+
return buffer;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// src/index.ts
|
|
1046
|
+
async function convertHtmlToPptx(html, options) {
|
|
1047
|
+
if (!html || typeof html !== "string" || html.trim().length === 0) {
|
|
1048
|
+
throw new Error("Input HTML string or URL cannot be empty.");
|
|
1049
|
+
}
|
|
1050
|
+
const slideIR = await harvestHtmlToIR(html, options);
|
|
1051
|
+
const pptxBuffer = await compileSlideToPptx(slideIR);
|
|
1052
|
+
return pptxBuffer;
|
|
1053
|
+
}
|
|
1054
|
+
function createSlideIR(aspect = "16:9") {
|
|
1055
|
+
const is16_9 = aspect === "16:9";
|
|
1056
|
+
return {
|
|
1057
|
+
width: is16_9 ? 13.333333 : 10,
|
|
1058
|
+
height: 7.5,
|
|
1059
|
+
elements: []
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
exports.UNITS = UNITS;
|
|
1064
|
+
exports.borderRadiusToGuide = borderRadiusToGuide;
|
|
1065
|
+
exports.cleanTypeface = cleanTypeface;
|
|
1066
|
+
exports.compileContainerShape = compileContainerShape;
|
|
1067
|
+
exports.compileSlideToPptx = compileSlideToPptx;
|
|
1068
|
+
exports.compileTextShape = compileTextShape;
|
|
1069
|
+
exports.convertHtmlToPptx = convertHtmlToPptx;
|
|
1070
|
+
exports.convertTtfToEot = convertTtfToEot;
|
|
1071
|
+
exports.createSlideIR = createSlideIR;
|
|
1072
|
+
exports.extractDomToSlideIR = extractDomToSlideIR;
|
|
1073
|
+
exports.extractFontFacesFromHtml = extractFontFacesFromHtml;
|
|
1074
|
+
exports.harvestHtmlToIR = harvestHtmlToIR;
|
|
1075
|
+
exports.inchesToEmu = inchesToEmu;
|
|
1076
|
+
exports.isEotBuffer = isEotBuffer;
|
|
1077
|
+
exports.loadFontBuffer = loadFontBuffer;
|
|
1078
|
+
exports.normalizeBorderStyle = normalizeBorderStyle;
|
|
1079
|
+
exports.normalizeFontFamily = normalizeFontFamily;
|
|
1080
|
+
exports.normalizeFontStyle = normalizeFontStyle;
|
|
1081
|
+
exports.normalizeFontWeight = normalizeFontWeight;
|
|
1082
|
+
exports.normalizeHexColor = normalizeHexColor;
|
|
1083
|
+
exports.normalizeTextAlign = normalizeTextAlign;
|
|
1084
|
+
exports.ptToHundredthPt = ptToHundredthPt;
|
|
1085
|
+
exports.pxToEmu = pxToEmu;
|
|
1086
|
+
exports.pxToInches = pxToInches;
|
|
1087
|
+
exports.pxToPt = pxToPt;
|
|
1088
|
+
exports.resolveAndEmbedFonts = resolveAndEmbedFonts;
|
|
1089
|
+
//# sourceMappingURL=index.js.map
|
|
1090
|
+
//# sourceMappingURL=index.js.map
|