@taloswork/ose-shared 0.1.10 → 0.1.12
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/package.json +3 -3
- package/skills/document/SKILL.md +143 -215
- package/skills/document/art-directions/base.css +27 -2
- package/skills/document/art-directions/cinematic-depth.css +3 -1
- package/skills/document/art-directions/collage-paper.css +5 -1
- package/skills/document/art-directions/editorial-cut.css +5 -1
- package/skills/document/art-directions/geometric-pop.css +6 -0
- package/skills/document/art-directions/gradient-mesh.css +5 -1
- package/skills/document/art-directions/illustrated-doodle.css +5 -0
- package/skills/document/art-directions/luxury-ornament.css +4 -1
- package/skills/document/art-directions/maximalist-blocks.css +6 -0
- package/skills/document/art-directions/technical-grid.css +4 -0
- package/skills/document/assets/fonts/fonts.json +2 -2
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
- package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
- package/skills/document/assets/pdf-fonts/README.md +35 -0
- package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
- package/skills/document/assets/tessdata/README.md +21 -0
- package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
- package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
- package/skills/document/bin/deck-build.cjs +2311 -41
- package/skills/document/bin/deck-chart.cjs +762 -0
- package/skills/document/bin/deck-plan.cjs +802 -0
- package/skills/document/bin/deck-qa.cjs +984 -249
- package/skills/document/bin/deck-scenario.cjs +1346 -0
- package/skills/document/bin/doc-convert.cjs +130 -39
- package/skills/document/bin/doc-fill.cjs +73121 -170
- package/skills/document/bin/doc-gen.cjs +625 -68
- package/skills/document/bin/doc-run.cjs +162 -24
- package/skills/document/bin/doc-scan.cjs +13 -0
- package/skills/document/bin/doc-text.cjs +13 -0
- package/skills/document/bin/doc-validate.cjs +718 -23
- package/skills/document/bin/doc-xml.cjs +9 -5
- package/skills/document/bin/docx-annotate.cjs +95 -19
- package/skills/document/bin/fontkit.cjs +1 -0
- package/skills/document/bin/html2pptx.cjs +1372 -121
- package/skills/document/bin/ocr-worker.cjs +1 -0
- package/skills/document/bin/ocr.cjs +131 -40
- package/skills/document/bin/pdf-form.cjs +674 -177
- package/skills/document/bin/pdf-images.cjs +13 -0
- package/skills/document/bin/pdf-ops.cjs +634 -199
- package/skills/document/bin/pdf-render.cjs +19 -3
- package/skills/document/bin/pdf-tables.cjs +13 -0
- package/skills/document/bin/pptx-clone.cjs +3 -3
- package/skills/document/bin/pptx-slide.cjs +3 -3
- package/skills/document/bin/xlsx-query.cjs +192 -61
- package/skills/document/bin-reference.md +56 -33
- package/skills/document/content-pipeline.md +129 -242
- package/skills/document/deck-design.md +355 -638
- package/skills/document/layouts/README.md +46 -33
- package/skills/document/layouts/capacity.json +1 -0
- package/skills/document/layouts/slots.json +1 -1
- package/skills/document/layouts/toc-panel.tpl +1 -1
- package/skills/document/motion/README.md +3 -2
- package/skills/document/office.md +3 -3
- package/skills/document/packs/board-brief/backdrop.html +2 -0
- package/skills/document/packs/board-brief/pack.css +2 -1
- package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
- package/skills/document/packs/mckinsey-photo/pack.css +2 -1
- package/skills/document/packs/press-room/backdrop.html +2 -0
- package/skills/document/packs/press-room/pack.css +2 -1
- package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
- package/skills/document/packs/prismatic-3d/pack.css +4 -1
- package/skills/document/pdf.md +53 -49
- package/skills/document/rewrite.md +241 -308
- package/skills/document/scenarios.json +1290 -0
- package/skills/document/scenarios.schema.json +103 -0
- package/skills/document/styles/README.md +15 -25
- package/skills/document/styles/_contract-defaults.css +25 -1
- package/skills/document/styles/anthropic/skin.css +6 -3
- package/skills/document/styles/apple/skin.css +4 -3
- package/skills/document/styles/bistro-warm/backdrop.html +2 -0
- package/skills/document/styles/bistro-warm/skin.css +4 -1
- package/skills/document/styles/black-gold/skin.css +6 -5
- package/skills/document/styles/bold-clash/backdrop.html +2 -0
- package/skills/document/styles/bold-clash/skin.css +2 -1
- package/skills/document/styles/branding/skin.css +4 -4
- package/skills/document/styles/campus-teal/backdrop.html +2 -0
- package/skills/document/styles/campus-teal/skin.css +2 -1
- package/skills/document/styles/cinereel/skin.css +7 -3
- package/skills/document/styles/clinic-care/backdrop.html +2 -0
- package/skills/document/styles/clinic-care/skin.css +2 -1
- package/skills/document/styles/cool/skin.css +6 -5
- package/skills/document/styles/dark-glass/backdrop.html +2 -0
- package/skills/document/styles/dark-glass/skin.css +2 -1
- package/skills/document/styles/gov-brief/backdrop.html +2 -0
- package/skills/document/styles/gov-brief/skin.css +2 -1
- package/skills/document/styles/grid-tech/backdrop.html +2 -0
- package/skills/document/styles/grid-tech/skin.css +2 -1
- package/skills/document/styles/hand-drawn/skin.css +3 -3
- package/skills/document/styles/ivory-gold/skin.css +7 -6
- package/skills/document/styles/legal-slate/backdrop.html +2 -0
- package/skills/document/styles/legal-slate/skin.css +2 -1
- package/skills/document/styles/magazine-ink/skin.css +4 -3
- package/skills/document/styles/mckinsey/skin.css +2 -1
- package/skills/document/styles/memphis/backdrop.html +2 -0
- package/skills/document/styles/memphis/skin.css +2 -1
- package/skills/document/styles/mono/skin.css +6 -2
- package/skills/document/styles/neon-cyber/backdrop.html +2 -0
- package/skills/document/styles/neon-cyber/skin.css +2 -1
- package/skills/document/styles/olive-gold/skin.css +7 -6
- package/skills/document/styles/organic-earth/backdrop.html +2 -0
- package/skills/document/styles/organic-earth/skin.css +2 -1
- package/skills/document/styles/pastel-saas/backdrop.html +2 -0
- package/skills/document/styles/pastel-saas/skin.css +2 -1
- package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
- package/skills/document/styles/portfolio-noir/skin.css +2 -1
- package/skills/document/styles/retro-press/backdrop.html +2 -0
- package/skills/document/styles/retro-press/skin.css +2 -1
- package/skills/document/styles/sport-arena/backdrop.html +2 -0
- package/skills/document/styles/sport-arena/skin.css +4 -1
- package/skills/document/styles/swiss-grid/skin.css +10 -5
- package/skills/document/styles/wedding-blush/backdrop.html +2 -0
- package/skills/document/styles/wedding-blush/skin.css +4 -1
- package/skills/document/theme-system.md +85 -77
- package/skills/document/themes/README.md +7 -5
- package/skills/document/vision.md +8 -5
- package/skills/document/xlsx.md +15 -25
- package/skills/cozy-static-svg/SKILL.md +0 -76
- package/skills/cozy-static-svg/references/approved-preview.png +0 -0
- package/skills/cozy-static-svg/references/cross-platform.md +0 -73
- package/skills/cozy-static-svg/references/moon_keeps_watch.svg +0 -229
- package/skills/cozy-static-svg/references/style-anchor.md +0 -271
- package/skills/cozy-static-svg/scripts/artkit.py +0 -321
- package/skills/cozy-static-svg/scripts/to_png.py +0 -333
|
@@ -12788,14 +12788,14 @@ var require_decode = __commonJS({
|
|
|
12788
12788
|
var LZWStream_1 = tslib_1.__importDefault(require_LZWStream());
|
|
12789
12789
|
var RunLengthStream_1 = tslib_1.__importDefault(require_RunLengthStream());
|
|
12790
12790
|
var Stream_1 = tslib_1.__importDefault(require_Stream());
|
|
12791
|
-
var decodeStream = function(stream, encoding,
|
|
12791
|
+
var decodeStream = function(stream, encoding, params2) {
|
|
12792
12792
|
if (encoding === PDFName_1.default.of("FlateDecode")) {
|
|
12793
12793
|
return new FlateStream_1.default(stream);
|
|
12794
12794
|
}
|
|
12795
12795
|
if (encoding === PDFName_1.default.of("LZWDecode")) {
|
|
12796
12796
|
var earlyChange = 1;
|
|
12797
|
-
if (
|
|
12798
|
-
var EarlyChange =
|
|
12797
|
+
if (params2 instanceof PDFDict_1.default) {
|
|
12798
|
+
var EarlyChange = params2.lookup(PDFName_1.default.of("EarlyChange"));
|
|
12799
12799
|
if (EarlyChange instanceof PDFNumber_1.default) {
|
|
12800
12800
|
earlyChange = EarlyChange.asNumber();
|
|
12801
12801
|
}
|
|
@@ -16677,11 +16677,11 @@ var require_svgPath = __commonJS({
|
|
|
16677
16677
|
var args = [];
|
|
16678
16678
|
var curArg = "";
|
|
16679
16679
|
var foundDecimal = false;
|
|
16680
|
-
var
|
|
16680
|
+
var params2 = 0;
|
|
16681
16681
|
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
|
|
16682
16682
|
var c = path_1[_i];
|
|
16683
16683
|
if (parameters.has(c)) {
|
|
16684
|
-
|
|
16684
|
+
params2 = parameters.get(c);
|
|
16685
16685
|
if (cmd) {
|
|
16686
16686
|
if (curArg.length > 0) {
|
|
16687
16687
|
args[args.length] = +curArg;
|
|
@@ -16696,7 +16696,7 @@ var require_svgPath = __commonJS({
|
|
|
16696
16696
|
if (curArg.length === 0) {
|
|
16697
16697
|
continue;
|
|
16698
16698
|
}
|
|
16699
|
-
if (args.length ===
|
|
16699
|
+
if (args.length === params2) {
|
|
16700
16700
|
ret[ret.length] = { cmd, args };
|
|
16701
16701
|
args = [+curArg];
|
|
16702
16702
|
if (cmd === "M") {
|
|
@@ -16718,7 +16718,7 @@ var require_svgPath = __commonJS({
|
|
|
16718
16718
|
}
|
|
16719
16719
|
}
|
|
16720
16720
|
if (curArg.length > 0) {
|
|
16721
|
-
if (args.length ===
|
|
16721
|
+
if (args.length === params2) {
|
|
16722
16722
|
ret[ret.length] = { cmd, args };
|
|
16723
16723
|
args = [+curArg];
|
|
16724
16724
|
if (cmd === "M") {
|
|
@@ -22296,7 +22296,7 @@ var require_BufferList = __commonJS({
|
|
|
22296
22296
|
this.head = this.tail = null;
|
|
22297
22297
|
this.length = 0;
|
|
22298
22298
|
};
|
|
22299
|
-
BufferList.prototype.join = function
|
|
22299
|
+
BufferList.prototype.join = function join7(s) {
|
|
22300
22300
|
if (this.length === 0) return "";
|
|
22301
22301
|
var p = this.head;
|
|
22302
22302
|
var ret = "" + p.data;
|
|
@@ -29778,20 +29778,20 @@ var require_pptxgen_cjs = __commonJS({
|
|
|
29778
29778
|
var Slide = (
|
|
29779
29779
|
/** @class */
|
|
29780
29780
|
(function() {
|
|
29781
|
-
function Slide2(
|
|
29781
|
+
function Slide2(params2) {
|
|
29782
29782
|
var _a;
|
|
29783
|
-
this.addSlide =
|
|
29784
|
-
this.getSlide =
|
|
29785
|
-
this._name = "Slide ".concat(
|
|
29786
|
-
this._presLayout =
|
|
29787
|
-
this._rId =
|
|
29783
|
+
this.addSlide = params2.addSlide;
|
|
29784
|
+
this.getSlide = params2.getSlide;
|
|
29785
|
+
this._name = "Slide ".concat(params2.slideNumber);
|
|
29786
|
+
this._presLayout = params2.presLayout;
|
|
29787
|
+
this._rId = params2.slideRId;
|
|
29788
29788
|
this._rels = [];
|
|
29789
29789
|
this._relsChart = [];
|
|
29790
29790
|
this._relsMedia = [];
|
|
29791
|
-
this._setSlideNum =
|
|
29792
|
-
this._slideId =
|
|
29793
|
-
this._slideLayout =
|
|
29794
|
-
this._slideNum =
|
|
29791
|
+
this._setSlideNum = params2.setSlideNum;
|
|
29792
|
+
this._slideId = params2.slideId;
|
|
29793
|
+
this._slideLayout = params2.slideLayout || null;
|
|
29794
|
+
this._slideNum = params2.slideNumber;
|
|
29795
29795
|
this._slideObjects = [];
|
|
29796
29796
|
this._slideNumberProps = ((_a = this._slideLayout) === null || _a === void 0 ? void 0 : _a._slideNumberProps) ? this._slideLayout._slideNumberProps : null;
|
|
29797
29797
|
}
|
|
@@ -33202,11 +33202,399 @@ var require_pptxgen_cjs = __commonJS({
|
|
|
33202
33202
|
}
|
|
33203
33203
|
});
|
|
33204
33204
|
|
|
33205
|
+
// packages/core/dist/document/pdf-encrypted.js
|
|
33206
|
+
var ENCRYPTED_PDF_MESSAGE, PdfEncryptedError;
|
|
33207
|
+
var init_pdf_encrypted = __esm({
|
|
33208
|
+
"packages/core/dist/document/pdf-encrypted.js"() {
|
|
33209
|
+
"use strict";
|
|
33210
|
+
ENCRYPTED_PDF_MESSAGE = "PDF is password-protected (encrypted); decrypt first: pdf-ops decrypt <in> <password> <out>";
|
|
33211
|
+
PdfEncryptedError = class extends Error {
|
|
33212
|
+
constructor() {
|
|
33213
|
+
super(ENCRYPTED_PDF_MESSAGE);
|
|
33214
|
+
this.name = "PdfEncryptedError";
|
|
33215
|
+
}
|
|
33216
|
+
};
|
|
33217
|
+
}
|
|
33218
|
+
});
|
|
33219
|
+
|
|
33220
|
+
// packages/core/dist/document/pdf-extract.js
|
|
33221
|
+
var pdf_extract_exports = {};
|
|
33222
|
+
__export(pdf_extract_exports, {
|
|
33223
|
+
analyzePdfDocument: () => analyzePdfDocument,
|
|
33224
|
+
countVectorOperators: () => countVectorOperators,
|
|
33225
|
+
decryptPdf: () => decryptPdf,
|
|
33226
|
+
encryptPdf: () => encryptPdf,
|
|
33227
|
+
extractPdfImages: () => extractPdfImages,
|
|
33228
|
+
extractPdfTables: () => extractPdfTables,
|
|
33229
|
+
extractPdfText: () => extractPdfText,
|
|
33230
|
+
extractPdfTextLines: () => extractPdfTextLines,
|
|
33231
|
+
pdfNeedsPassword: () => pdfNeedsPassword,
|
|
33232
|
+
renderPdfPages: () => renderPdfPages,
|
|
33233
|
+
scanPdfSignals: () => scanPdfSignals,
|
|
33234
|
+
textMathHint: () => textMathHint,
|
|
33235
|
+
textTableHint: () => textTableHint
|
|
33236
|
+
});
|
|
33237
|
+
function loadMupdf() {
|
|
33238
|
+
if (!_mupdf) {
|
|
33239
|
+
const deps = process.env.TALOS_SKILL_DEPS;
|
|
33240
|
+
const spec = deps ? (0, import_node_url.pathToFileURL)((0, import_node_path2.join)(deps, "mupdf", "dist", "mupdf.js")).href : "mupdf";
|
|
33241
|
+
_mupdf = import(spec);
|
|
33242
|
+
}
|
|
33243
|
+
return _mupdf;
|
|
33244
|
+
}
|
|
33245
|
+
function textMathHint(text) {
|
|
33246
|
+
if (!text)
|
|
33247
|
+
return false;
|
|
33248
|
+
const hits = (text.match(MATH_CHARS) || []).length;
|
|
33249
|
+
return hits >= 3 || hits / Math.max(text.length, 1) > 0.02;
|
|
33250
|
+
}
|
|
33251
|
+
function textTableHint(text) {
|
|
33252
|
+
if (!text)
|
|
33253
|
+
return false;
|
|
33254
|
+
const gaps = (text.match(/ {2,}|\t/g) || []).length;
|
|
33255
|
+
return gaps >= 6;
|
|
33256
|
+
}
|
|
33257
|
+
async function openDoc(bytes) {
|
|
33258
|
+
const mupdf = await loadMupdf();
|
|
33259
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
33260
|
+
if (doc.needsPassword())
|
|
33261
|
+
throw new PdfEncryptedError();
|
|
33262
|
+
if (doc.countPages() === 0) {
|
|
33263
|
+
throw new Error("PDF has no readable pages (corrupt, encrypted, or empty)");
|
|
33264
|
+
}
|
|
33265
|
+
return { mupdf, doc };
|
|
33266
|
+
}
|
|
33267
|
+
async function analyzePdfDocument(bytes, options = {}) {
|
|
33268
|
+
const wantText = options.text !== false;
|
|
33269
|
+
const wantSignals = options.signals === true;
|
|
33270
|
+
const { doc } = await openDoc(bytes);
|
|
33271
|
+
const pageCount = doc.countPages();
|
|
33272
|
+
const signals = [];
|
|
33273
|
+
let out = "";
|
|
33274
|
+
for (let i = 0; i < pageCount; i++) {
|
|
33275
|
+
const page = doc.loadPage(i);
|
|
33276
|
+
if (wantText) {
|
|
33277
|
+
out += `
|
|
33278
|
+
[page ${i + 1}]
|
|
33279
|
+
${page.toStructuredText().asText()}`;
|
|
33280
|
+
}
|
|
33281
|
+
if (wantSignals) {
|
|
33282
|
+
signals.push(pageSignalsFrom(page, i + 1));
|
|
33283
|
+
}
|
|
33284
|
+
}
|
|
33285
|
+
return { text: wantText ? out.trim() : "", pageCount, signals };
|
|
33286
|
+
}
|
|
33287
|
+
async function extractPdfText(bytes) {
|
|
33288
|
+
const { text, pageCount } = await analyzePdfDocument(bytes, { text: true });
|
|
33289
|
+
return { text, pageCount };
|
|
33290
|
+
}
|
|
33291
|
+
function countVectorOperators(content) {
|
|
33292
|
+
let ops = 0;
|
|
33293
|
+
let curves = 0;
|
|
33294
|
+
let fills = 0;
|
|
33295
|
+
let i = 0;
|
|
33296
|
+
const n = content.length;
|
|
33297
|
+
while (i < n) {
|
|
33298
|
+
const ch = content[i];
|
|
33299
|
+
if (ch === "(") {
|
|
33300
|
+
let depth = 1;
|
|
33301
|
+
i++;
|
|
33302
|
+
while (i < n && depth > 0) {
|
|
33303
|
+
if (content[i] === "\\")
|
|
33304
|
+
i++;
|
|
33305
|
+
else if (content[i] === "(")
|
|
33306
|
+
depth++;
|
|
33307
|
+
else if (content[i] === ")")
|
|
33308
|
+
depth--;
|
|
33309
|
+
i++;
|
|
33310
|
+
}
|
|
33311
|
+
continue;
|
|
33312
|
+
}
|
|
33313
|
+
if (ch === "<" && content[i + 1] !== "<") {
|
|
33314
|
+
while (i < n && content[i] !== ">")
|
|
33315
|
+
i++;
|
|
33316
|
+
i++;
|
|
33317
|
+
continue;
|
|
33318
|
+
}
|
|
33319
|
+
if (ch === "I" && content[i + 1] === "D" && isDelimiter(content[i + 2])) {
|
|
33320
|
+
const end = content.indexOf("EI", i + 2);
|
|
33321
|
+
i = end === -1 ? n : end + 2;
|
|
33322
|
+
continue;
|
|
33323
|
+
}
|
|
33324
|
+
if (!isDelimiter(ch)) {
|
|
33325
|
+
const start = i;
|
|
33326
|
+
while (i < n && !isDelimiter(content[i]))
|
|
33327
|
+
i++;
|
|
33328
|
+
const token = content.slice(start, i);
|
|
33329
|
+
switch (token) {
|
|
33330
|
+
case "m":
|
|
33331
|
+
case "l":
|
|
33332
|
+
case "re":
|
|
33333
|
+
ops++;
|
|
33334
|
+
break;
|
|
33335
|
+
case "c":
|
|
33336
|
+
case "v":
|
|
33337
|
+
case "y":
|
|
33338
|
+
ops++;
|
|
33339
|
+
curves++;
|
|
33340
|
+
break;
|
|
33341
|
+
case "f":
|
|
33342
|
+
case "F":
|
|
33343
|
+
case "f*":
|
|
33344
|
+
fills++;
|
|
33345
|
+
break;
|
|
33346
|
+
case "B":
|
|
33347
|
+
case "B*":
|
|
33348
|
+
case "b":
|
|
33349
|
+
case "b*":
|
|
33350
|
+
fills++;
|
|
33351
|
+
break;
|
|
33352
|
+
default:
|
|
33353
|
+
break;
|
|
33354
|
+
}
|
|
33355
|
+
continue;
|
|
33356
|
+
}
|
|
33357
|
+
i++;
|
|
33358
|
+
}
|
|
33359
|
+
return { ops, curves, fills };
|
|
33360
|
+
}
|
|
33361
|
+
function isDelimiter(ch) {
|
|
33362
|
+
return ch === void 0 || ch === " " || ch === "\n" || ch === "\r" || ch === " " || ch === "\f" || ch === "\0" || ch === "[" || ch === "]" || ch === "/" || ch === "{" || ch === "}";
|
|
33363
|
+
}
|
|
33364
|
+
function pageContentStream(page) {
|
|
33365
|
+
try {
|
|
33366
|
+
const obj = page.getObject?.();
|
|
33367
|
+
const contents = obj?.get?.("Contents");
|
|
33368
|
+
if (!contents)
|
|
33369
|
+
return "";
|
|
33370
|
+
const streams = contents.isArray?.() && contents.get ? Array.from({ length: contents.length ?? 0 }, (_, i) => contents.get(i)) : [contents];
|
|
33371
|
+
let out = "";
|
|
33372
|
+
for (const stream of streams) {
|
|
33373
|
+
const buf = stream?.readStream?.();
|
|
33374
|
+
const bytes = buf?.asUint8Array?.();
|
|
33375
|
+
if (bytes)
|
|
33376
|
+
out += Buffer.from(bytes).toString("latin1");
|
|
33377
|
+
}
|
|
33378
|
+
return out;
|
|
33379
|
+
} catch {
|
|
33380
|
+
return "";
|
|
33381
|
+
}
|
|
33382
|
+
}
|
|
33383
|
+
function pageSignalsFrom(page, pageNumber) {
|
|
33384
|
+
const json = JSON.parse(page.toStructuredText("preserve-images").asJSON());
|
|
33385
|
+
const blocks = json.blocks ?? [];
|
|
33386
|
+
const text = blocks.flatMap((b) => (b.lines ?? []).map((l) => l.text ?? "")).join(" ");
|
|
33387
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
33388
|
+
const pageArea = Math.max((x1 - x0) * (y1 - y0), 1);
|
|
33389
|
+
const imgArea = blocks.filter((b) => b.type === "image").reduce((a, b) => a + Math.max((b.bbox?.w ?? 0) * (b.bbox?.h ?? 0), 0), 0);
|
|
33390
|
+
const vector = countVectorOperators(pageContentStream(page));
|
|
33391
|
+
return {
|
|
33392
|
+
page: pageNumber,
|
|
33393
|
+
textChars: text.trim().length,
|
|
33394
|
+
imageCoverage: Math.min(imgArea / pageArea, 1),
|
|
33395
|
+
vectorOps: vector.ops,
|
|
33396
|
+
vectorCurves: vector.curves,
|
|
33397
|
+
vectorFills: vector.fills,
|
|
33398
|
+
mathHint: textMathHint(text),
|
|
33399
|
+
tableHint: textTableHint(text)
|
|
33400
|
+
};
|
|
33401
|
+
}
|
|
33402
|
+
async function scanPdfSignals(bytes) {
|
|
33403
|
+
const { signals } = await analyzePdfDocument(bytes, { text: false, signals: true });
|
|
33404
|
+
return signals;
|
|
33405
|
+
}
|
|
33406
|
+
function median(nums) {
|
|
33407
|
+
if (!nums.length)
|
|
33408
|
+
return 0;
|
|
33409
|
+
const s = [...nums].sort((a, b) => a - b);
|
|
33410
|
+
return s[Math.floor(s.length / 2)];
|
|
33411
|
+
}
|
|
33412
|
+
async function extractPdfTables(bytes, pageNum, minColumns = 2) {
|
|
33413
|
+
const { doc } = await openDoc(bytes);
|
|
33414
|
+
const total = doc.countPages();
|
|
33415
|
+
const from = pageNum ? pageNum : 1;
|
|
33416
|
+
const to = pageNum ? pageNum : total;
|
|
33417
|
+
const out = [];
|
|
33418
|
+
for (let p = from; p <= to; p++) {
|
|
33419
|
+
if (p < 1 || p > total)
|
|
33420
|
+
continue;
|
|
33421
|
+
const json = JSON.parse(doc.loadPage(p - 1).toStructuredText().asJSON());
|
|
33422
|
+
const cells = [];
|
|
33423
|
+
for (const b of json.blocks ?? []) {
|
|
33424
|
+
if (b.type === "image")
|
|
33425
|
+
continue;
|
|
33426
|
+
for (const l of b.lines ?? []) {
|
|
33427
|
+
const text = (l.text ?? "").trim();
|
|
33428
|
+
if (!text)
|
|
33429
|
+
continue;
|
|
33430
|
+
cells.push({ text, x: l.bbox?.x ?? 0, y: l.bbox?.y ?? 0, h: l.bbox?.h ?? 12 });
|
|
33431
|
+
}
|
|
33432
|
+
}
|
|
33433
|
+
if (cells.length < 2)
|
|
33434
|
+
continue;
|
|
33435
|
+
const medH = median(cells.map((c) => c.h)) || 12;
|
|
33436
|
+
const rowTol = Math.max(4, medH * 0.6);
|
|
33437
|
+
const colTol = Math.max(6, medH * 0.9);
|
|
33438
|
+
const byY = [...cells].sort((a, b) => a.y - b.y);
|
|
33439
|
+
const rowGroups = [];
|
|
33440
|
+
let rowRef = Number.NEGATIVE_INFINITY;
|
|
33441
|
+
for (const c of byY) {
|
|
33442
|
+
if (c.y - rowRef > rowTol) {
|
|
33443
|
+
rowGroups.push([]);
|
|
33444
|
+
rowRef = c.y;
|
|
33445
|
+
}
|
|
33446
|
+
rowGroups[rowGroups.length - 1].push(c);
|
|
33447
|
+
}
|
|
33448
|
+
const colRefs = [];
|
|
33449
|
+
for (const x of cells.map((c) => c.x).sort((a, b) => a - b)) {
|
|
33450
|
+
if (!colRefs.length || x - colRefs[colRefs.length - 1] > colTol)
|
|
33451
|
+
colRefs.push(x);
|
|
33452
|
+
}
|
|
33453
|
+
const colIndex = (x) => {
|
|
33454
|
+
let best = 0, bd = Infinity;
|
|
33455
|
+
colRefs.forEach((r, i) => {
|
|
33456
|
+
const d = Math.abs(x - r);
|
|
33457
|
+
if (d < bd) {
|
|
33458
|
+
bd = d;
|
|
33459
|
+
best = i;
|
|
33460
|
+
}
|
|
33461
|
+
});
|
|
33462
|
+
return best;
|
|
33463
|
+
};
|
|
33464
|
+
const rows = [];
|
|
33465
|
+
for (const group of rowGroups) {
|
|
33466
|
+
const arr = Array(colRefs.length).fill("");
|
|
33467
|
+
for (const c of group.sort((a, b) => a.x - b.x)) {
|
|
33468
|
+
const i = colIndex(c.x);
|
|
33469
|
+
arr[i] = arr[i] ? `${arr[i]} ${c.text}` : c.text;
|
|
33470
|
+
}
|
|
33471
|
+
if (arr.filter((v) => v).length >= minColumns)
|
|
33472
|
+
rows.push(arr);
|
|
33473
|
+
}
|
|
33474
|
+
if (rows.length)
|
|
33475
|
+
out.push({ page: p, rows });
|
|
33476
|
+
}
|
|
33477
|
+
return out;
|
|
33478
|
+
}
|
|
33479
|
+
async function extractPdfTextLines(bytes) {
|
|
33480
|
+
const { doc } = await openDoc(bytes);
|
|
33481
|
+
const out = [];
|
|
33482
|
+
for (let p = 1; p <= doc.countPages(); p++) {
|
|
33483
|
+
const page = doc.loadPage(p - 1);
|
|
33484
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
33485
|
+
const json = JSON.parse(page.toStructuredText().asJSON());
|
|
33486
|
+
const lines = [];
|
|
33487
|
+
for (const b of json.blocks ?? []) {
|
|
33488
|
+
if (b.type === "image")
|
|
33489
|
+
continue;
|
|
33490
|
+
for (const l of b.lines ?? []) {
|
|
33491
|
+
const text = l.text ?? "";
|
|
33492
|
+
if (!text.trim() || !l.bbox)
|
|
33493
|
+
continue;
|
|
33494
|
+
lines.push({
|
|
33495
|
+
x: l.bbox.x ?? 0,
|
|
33496
|
+
y: l.bbox.y ?? 0,
|
|
33497
|
+
w: l.bbox.w ?? 0,
|
|
33498
|
+
h: l.bbox.h ?? 0,
|
|
33499
|
+
size: l.font?.size ?? 12,
|
|
33500
|
+
text
|
|
33501
|
+
});
|
|
33502
|
+
}
|
|
33503
|
+
}
|
|
33504
|
+
out.push({ page: p, width: x1 - x0, height: y1 - y0, lines });
|
|
33505
|
+
}
|
|
33506
|
+
return out;
|
|
33507
|
+
}
|
|
33508
|
+
async function renderPdfPages(bytes, pages, scale = 2, maxEdge) {
|
|
33509
|
+
const { mupdf, doc } = await openDoc(bytes);
|
|
33510
|
+
const total = doc.countPages();
|
|
33511
|
+
const wanted = pages === "all" ? Array.from({ length: total }, (_, i) => i + 1) : pages;
|
|
33512
|
+
const out = [];
|
|
33513
|
+
for (const p of wanted) {
|
|
33514
|
+
if (p < 1 || p > total)
|
|
33515
|
+
continue;
|
|
33516
|
+
const page = doc.loadPage(p - 1);
|
|
33517
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
33518
|
+
const longest = Math.max(x1 - x0, y1 - y0);
|
|
33519
|
+
const s = maxEdge && longest > 0 ? Math.min(scale, maxEdge / longest) : scale;
|
|
33520
|
+
const pix = page.toPixmap(mupdf.Matrix.scale(s, s), mupdf.ColorSpace.DeviceRGB, false, true);
|
|
33521
|
+
out.push({ page: p, png: pix.asPNG() });
|
|
33522
|
+
}
|
|
33523
|
+
return out;
|
|
33524
|
+
}
|
|
33525
|
+
async function extractPdfImages(bytes, pages) {
|
|
33526
|
+
const { doc } = await openDoc(bytes);
|
|
33527
|
+
const total = doc.countPages();
|
|
33528
|
+
const wanted = pages && pages.length ? pages : Array.from({ length: total }, (_, i) => i + 1);
|
|
33529
|
+
const images = [];
|
|
33530
|
+
let skipped = 0;
|
|
33531
|
+
for (const p of wanted) {
|
|
33532
|
+
if (p < 1 || p > total)
|
|
33533
|
+
continue;
|
|
33534
|
+
const stext = doc.loadPage(p - 1).toStructuredText("preserve-images");
|
|
33535
|
+
let index = 0;
|
|
33536
|
+
stext.walk({
|
|
33537
|
+
onImageBlock(bbox, _matrix, image) {
|
|
33538
|
+
const slot = index++;
|
|
33539
|
+
try {
|
|
33540
|
+
const png = image.toPixmap().asPNG();
|
|
33541
|
+
images.push({
|
|
33542
|
+
page: p,
|
|
33543
|
+
index: slot,
|
|
33544
|
+
bbox: [bbox[0], bbox[1], bbox[2], bbox[3]],
|
|
33545
|
+
width: image.getWidth(),
|
|
33546
|
+
height: image.getHeight(),
|
|
33547
|
+
png
|
|
33548
|
+
});
|
|
33549
|
+
} catch {
|
|
33550
|
+
skipped++;
|
|
33551
|
+
}
|
|
33552
|
+
}
|
|
33553
|
+
});
|
|
33554
|
+
}
|
|
33555
|
+
return { images, skipped };
|
|
33556
|
+
}
|
|
33557
|
+
async function encryptPdf(bytes, userPassword, ownerPassword) {
|
|
33558
|
+
if (!userPassword)
|
|
33559
|
+
throw new Error("a non-empty user password is required");
|
|
33560
|
+
const { doc } = await openDoc(bytes);
|
|
33561
|
+
const opts = `encrypt=aes-256,user-password=${userPassword},owner-password=${ownerPassword || userPassword}`;
|
|
33562
|
+
return doc.saveToBuffer(opts).asUint8Array();
|
|
33563
|
+
}
|
|
33564
|
+
async function pdfNeedsPassword(bytes) {
|
|
33565
|
+
const mupdf = await loadMupdf();
|
|
33566
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
33567
|
+
return doc.needsPassword();
|
|
33568
|
+
}
|
|
33569
|
+
async function decryptPdf(bytes, password) {
|
|
33570
|
+
const mupdf = await loadMupdf();
|
|
33571
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
33572
|
+
if (doc.needsPassword()) {
|
|
33573
|
+
if (doc.authenticatePassword(password) === 0)
|
|
33574
|
+
throw new Error("wrong password");
|
|
33575
|
+
}
|
|
33576
|
+
if (doc.countPages() === 0)
|
|
33577
|
+
throw new Error("PDF has no readable pages (corrupt or empty)");
|
|
33578
|
+
return doc.saveToBuffer("encrypt=none").asUint8Array();
|
|
33579
|
+
}
|
|
33580
|
+
var import_node_path2, import_node_url, _mupdf, MATH_CHARS;
|
|
33581
|
+
var init_pdf_extract = __esm({
|
|
33582
|
+
"packages/core/dist/document/pdf-extract.js"() {
|
|
33583
|
+
"use strict";
|
|
33584
|
+
import_node_path2 = require("node:path");
|
|
33585
|
+
import_node_url = require("node:url");
|
|
33586
|
+
init_pdf_encrypted();
|
|
33587
|
+
_mupdf = null;
|
|
33588
|
+
MATH_CHARS = /[=∑∫√≤≥≠±∞πθλμσΩ×÷∂∇→←↔∈∉⊂⊃∀∃]/g;
|
|
33589
|
+
}
|
|
33590
|
+
});
|
|
33591
|
+
|
|
33205
33592
|
// apps/admin/data/skills.lib/document/bin-src/html2pptx.mjs
|
|
33206
33593
|
var import_promises3 = require("node:fs/promises");
|
|
33594
|
+
var import_node_crypto2 = require("node:crypto");
|
|
33207
33595
|
var import_pdf_lib2 = __toESM(require_cjs(), 1);
|
|
33208
|
-
var
|
|
33209
|
-
var
|
|
33596
|
+
var import_node_fs3 = require("node:fs");
|
|
33597
|
+
var import_node_path6 = require("node:path");
|
|
33210
33598
|
|
|
33211
33599
|
// packages/core/dist/document/html-pptx.js
|
|
33212
33600
|
var import_jszip4 = __toESM(require_lib4(), 1);
|
|
@@ -33373,7 +33761,31 @@ async function injectTransitions(buf, perSlide) {
|
|
|
33373
33761
|
// packages/core/dist/document/pptx-finalize.js
|
|
33374
33762
|
var import_jszip3 = __toESM(require_lib4(), 1);
|
|
33375
33763
|
async function finalizePptx(bytes) {
|
|
33376
|
-
return stripDanglingOverrides(await normalizeChartParts(bytes));
|
|
33764
|
+
return stripDanglingOverrides(await dedupeParagraphProps(await normalizeChartParts(bytes)));
|
|
33765
|
+
}
|
|
33766
|
+
function dedupeParagraphPropsXml(xml) {
|
|
33767
|
+
return xml.replace(/<a:p>([\s\S]*?)<\/a:p>/g, (para, inner) => {
|
|
33768
|
+
const first = inner.search(/<a:(?:r|br|fld)\b/);
|
|
33769
|
+
if (first < 0)
|
|
33770
|
+
return para;
|
|
33771
|
+
const tail = inner.slice(first).replace(/<a:pPr\b[^>]*\/>|<a:pPr\b[^>]*>[\s\S]*?<\/a:pPr>/g, "");
|
|
33772
|
+
return `<a:p>${inner.slice(0, first)}${tail}</a:p>`;
|
|
33773
|
+
});
|
|
33774
|
+
}
|
|
33775
|
+
async function dedupeParagraphProps(bytes) {
|
|
33776
|
+
const zip = await import_jszip3.default.loadAsync(Buffer.from(bytes));
|
|
33777
|
+
let changed = false;
|
|
33778
|
+
for (const name of Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n))) {
|
|
33779
|
+
const xml = await zip.file(name).async("string");
|
|
33780
|
+
const next = dedupeParagraphPropsXml(xml);
|
|
33781
|
+
if (next !== xml) {
|
|
33782
|
+
zip.file(name, next);
|
|
33783
|
+
changed = true;
|
|
33784
|
+
}
|
|
33785
|
+
}
|
|
33786
|
+
if (!changed)
|
|
33787
|
+
return bytes;
|
|
33788
|
+
return new Uint8Array(await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" }));
|
|
33377
33789
|
}
|
|
33378
33790
|
async function normalizeChartParts(bytes) {
|
|
33379
33791
|
const zip = await import_jszip3.default.loadAsync(Buffer.from(bytes));
|
|
@@ -33441,6 +33853,50 @@ async function stripDanglingOverrides(bytes) {
|
|
|
33441
33853
|
return new Uint8Array(await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" }));
|
|
33442
33854
|
}
|
|
33443
33855
|
|
|
33856
|
+
// packages/core/dist/document/text-slack.js
|
|
33857
|
+
var SLACK_CJK_RE = /[\u3000-\u303F\u3400-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
33858
|
+
var PCT = { single: { cjk: 0.22, latin: 0.16 }, multi: { cjk: 0.1, latin: 0.06 } };
|
|
33859
|
+
var FLOOR_EM = { cjk: 1, latin: 0.45 };
|
|
33860
|
+
function params(single, cjk) {
|
|
33861
|
+
const script = cjk ? "cjk" : "latin";
|
|
33862
|
+
return { pct: PCT[single ? "single" : "multi"][script], floor: FLOOR_EM[script] * (single ? 1 : 0.5) };
|
|
33863
|
+
}
|
|
33864
|
+
function textSlack(width, fontSize, single, cjk) {
|
|
33865
|
+
const { pct, floor } = params(single, cjk);
|
|
33866
|
+
return Math.max(width * pct, floor * fontSize);
|
|
33867
|
+
}
|
|
33868
|
+
function unpaddedWidth(padded, fontSize, single, cjk) {
|
|
33869
|
+
const { pct, floor } = params(single, cjk);
|
|
33870
|
+
const floorAbs = floor * fontSize;
|
|
33871
|
+
const viaPct = padded / (1 + pct);
|
|
33872
|
+
return viaPct * pct >= floorAbs ? viaPct : Math.max(0, padded - floorAbs);
|
|
33873
|
+
}
|
|
33874
|
+
var CHART_TEXT_NAME = "talos-chart-text";
|
|
33875
|
+
|
|
33876
|
+
// packages/core/dist/document/deck-chart-svg.js
|
|
33877
|
+
var RAMP = [
|
|
33878
|
+
"var(--series-1, var(--c1, var(--accent)))",
|
|
33879
|
+
"var(--series-2, var(--c2, var(--muted)))",
|
|
33880
|
+
"var(--series-3, var(--c3, var(--title)))",
|
|
33881
|
+
"var(--series-4, var(--c4, var(--body)))"
|
|
33882
|
+
];
|
|
33883
|
+
var MAX_SERIES = RAMP.length;
|
|
33884
|
+
function chartDataMissing(spec) {
|
|
33885
|
+
if (!spec || typeof spec !== "object" || Array.isArray(spec))
|
|
33886
|
+
return "the chart is not a {type, labels, series} object";
|
|
33887
|
+
const c = spec;
|
|
33888
|
+
const series = Array.isArray(c.series) ? c.series.filter((s) => s && typeof s === "object") : [];
|
|
33889
|
+
const labels = Array.isArray(c.labels) ? c.labels : [];
|
|
33890
|
+
const withValues = series.some((s) => Array.isArray(s.values) && s.values.length > 0);
|
|
33891
|
+
if (c.type === "scatter" && series.some((s) => Array.isArray(s.points) && s.points.length > 0))
|
|
33892
|
+
return null;
|
|
33893
|
+
if (!labels.length)
|
|
33894
|
+
return "the chart has no labels";
|
|
33895
|
+
if (!withValues)
|
|
33896
|
+
return "the chart has no series with values";
|
|
33897
|
+
return null;
|
|
33898
|
+
}
|
|
33899
|
+
|
|
33444
33900
|
// packages/core/dist/document/html-pptx.js
|
|
33445
33901
|
var DEFAULT_CHART_RAMP = ["2563EB", "10B981", "F59E0B", "EF4444", "8B5CF6", "06B6D4"];
|
|
33446
33902
|
var SLIDE_W_IN = 13.333;
|
|
@@ -33476,6 +33932,10 @@ function cssAlpha(value) {
|
|
|
33476
33932
|
const a = parseFloat(m[1]);
|
|
33477
33933
|
return Number.isFinite(a) ? Math.max(0, Math.min(1, a)) : 1;
|
|
33478
33934
|
}
|
|
33935
|
+
function lineAlpha(value) {
|
|
33936
|
+
const a = cssAlpha(value);
|
|
33937
|
+
return a < 1 ? { transparency: Math.round((1 - a) * 100) } : {};
|
|
33938
|
+
}
|
|
33479
33939
|
function lintHardcodedColors(html) {
|
|
33480
33940
|
const noSvg = html.replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
33481
33941
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -33497,6 +33957,28 @@ function lintHardcodedColors(html) {
|
|
|
33497
33957
|
`${list.length} hardcoded brand color(s) bypass theme vars (e.g. ${list.slice(0, 4).join(", ")}) \u2014 deck won't re-theme with --theme. Route brand colors through var(--accent/--secondary/--title-color/--slide-bg/\u2026, fallback); SVG semantic colors are exempt.`
|
|
33498
33958
|
];
|
|
33499
33959
|
}
|
|
33960
|
+
function withoutPrintNoise(html) {
|
|
33961
|
+
return html.replace(/<svg\b[^>]*>(?:(?!<\/svg>)[\s\S])*?feTurbulence[\s\S]*?<\/svg>/g, "");
|
|
33962
|
+
}
|
|
33963
|
+
var CHART_PLACEHOLDER_LABEL = "\u56FE\u8868\u5360\u4F4D";
|
|
33964
|
+
var decodeAttr = (v) => v.replace(/'|'/g, "'").replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
33965
|
+
function markEmptyCharts(html) {
|
|
33966
|
+
const empty = [];
|
|
33967
|
+
const out = html.replace(/<div\b([^>]*?)\sdata-chart=(['"])([\s\S]*?)\2([^>]*)>\s*<\/div>/g, (full, before, _q, raw, after) => {
|
|
33968
|
+
let spec;
|
|
33969
|
+
try {
|
|
33970
|
+
spec = JSON.parse(decodeAttr(raw));
|
|
33971
|
+
} catch {
|
|
33972
|
+
spec = void 0;
|
|
33973
|
+
}
|
|
33974
|
+
const why = spec === void 0 ? "invalid data-chart JSON" : chartDataMissing(spec);
|
|
33975
|
+
if (!why)
|
|
33976
|
+
return full;
|
|
33977
|
+
empty.push(why);
|
|
33978
|
+
return `<div${before} data-chart-empty="1"${after}><span class="surface-label">${CHART_PLACEHOLDER_LABEL}</span></div>`;
|
|
33979
|
+
});
|
|
33980
|
+
return { html: out, empty };
|
|
33981
|
+
}
|
|
33500
33982
|
var STRUCTURAL_LAYOUTS = /* @__PURE__ */ new Set(["cover", "toc", "section", "transition", "ending", "title"]);
|
|
33501
33983
|
var isStructuralLayout = (layout) => STRUCTURAL_LAYOUTS.has(layout) || STRUCTURAL_LAYOUTS.has(layout.split("-")[0]);
|
|
33502
33984
|
function lintRepeatedLayouts(html) {
|
|
@@ -33586,6 +34068,75 @@ function gradFillXml(g) {
|
|
|
33586
34068
|
const deg = (((g.angle ?? 180) - 90) % 360 + 360) % 360;
|
|
33587
34069
|
return `<a:gradFill rotWithShape="1"><a:gsLst>${gs}</a:gsLst><a:lin ang="${Math.round(deg * 6e4)}" scaled="1"/></a:gradFill>`;
|
|
33588
34070
|
}
|
|
34071
|
+
var KANA_RE = /[-ヿㇰ-ㇿヲ-゚]/;
|
|
34072
|
+
var HANGUL_RE = /[ᄀ-ᇿ-가-]/;
|
|
34073
|
+
var HAN_RE = /[㐀-䶿一-鿿豈-]/;
|
|
34074
|
+
var SIMPLIFIED_ONLY = "\u4EEC\u8FD9\u4E2A\u6765\u65F6\u4F1A\u8BF4\u5BF9\u8FC7\u8FD8\u5F00\u53D1\u7ECF\u5B66\u70B9\u56FD\u5B9E\u73B0\u957F\u95EE\u9898\u7535\u8BDD\u673A\u4E0E\u4E3A\u4ECE\u5F53\u5E94\u5173\u4E48\u4E1A\u4F53\u4E1C\u8F66\u95E8\u89C1\u65E0\u9875\u8D44\u8BAF\u5C06\u4E13\u533A\u52A8\u4EA7\u8FDB\u6837\u79CD\u8BA9\u8BA4\u8BC6\u8BBE\u8BA1\u52A1\u5458\u62A5\u573A\u7EA7\u603B\u7ED3\u6784\u8BBA\u8D28\u5386\u4E07\u4EBF\u534E\u4E24\u7F51\u7EDC\u6570\u636E\u7EDF\u4F18\u52BF\u4EF7\u8D2D\u4E70\u5356\u9500\u8D39\u8D38\u8FBE\u8FD0\u8425\u5C42\u5E7F\u6807\u89C4\u5212\u9879\u9009\u62E9\u672F\u5907\u73AF\u8282\u6218\u98CE\u9669\u56E2\u961F\u9886\u5BFC\u7EC4\u7EC7\u6237\u5E2E\u5904\u6761\u7EBF\u53D8\u7EA6\u89C2\u4F17\u89C6\u9891\u56FE\u4E66\u8BFB\u5199\u8BED\u7B80\u5355\u6742\u96BE\u70ED\u620F\u5267\u4E50\u6B22\u7231\u4EB2\u5988\u513F\u5C81\u8BB0\u5FC6\u68A6\u98DE\u9E1F\u9C7C\u9A6C\u9F99\u533B\u836F\u7597\u536B\u62A4\u8BC1\u94F6\u94B1\u8D27\u5E01\u6C47\u5151\u6DA8\u76D8\u503A\u6743\u6DA6\u51C0\u989D\u7A0E\u8D26\u7B7E\u7EED\u7EE7\u7EAA\u5F55\u6D4B\u8BD5\u9A8C\u8BC4\u5BA1\u95FB\u8BBF\u8C08\u8BB2\u8BFE\u8BAD\u7EC3\u4E60\u9645\u8054\u8FDE\u8F7D\u56F4\u6C14\u6E29\u6E7F\u706F\u8111\u8F6F\u6863\u7801\u8BCD\u6C49\u5C14\u5E86\u5B81\u82CF\u6E7E\u5385\u697C\u56ED\u9986\u94C1\u949F\u9547\u53BF\u4E61\u519C\u517B\u996D\u996E\u6E10\u72B6\u6001\u786E\u987B\u5934\u8138\u58F0\u54CD\u8BE5\u5417\u8C01\u7ED9\u8FDC\u8FB9\u521A\u53F7\u6362\u663E\u9690\u95ED\u95F4\u804C\u8D23\u8D1F\u62C5\u83B7\u5956\u52B1\u8D5B\u80DC\u8D25\u8F93\u8D62\u9012\u4F20\u8F6C\u94FE\u8BA8\u8BAE\u51B3\u5219\u534F\u8C03\u521B\u6EE1\u5F20";
|
|
34075
|
+
var TRADITIONAL_ONLY = "\u5011\u9019\u500B\u4F86\u6642\u6703\u8AAA\u5C0D\u904E\u9084\u958B\u767C\u7D93\u5B78\u9EDE\u570B\u5BE6\u73FE\u9577\u554F\u984C\u96FB\u8A71\u6A5F\u8207\u70BA\u5F9E\u7576\u61C9\u95DC\u9EBC\u696D\u9AD4\u6771\u8ECA\u9580\u898B\u7121\u9801\u8CC7\u8A0A\u5C07\u5C08\u5340\u52D5\u7522\u9032\u6A23\u7A2E\u8B93\u8A8D\u8B58\u8A2D\u8A08\u52D9\u54E1\u5831\u5834\u7D1A\u7E3D\u7D50\u69CB\u8AD6\u8CEA\u6B77\u842C\u5104\u83EF\u5169\u7DB2\u7D61\u6578\u64DA\u7D71\u512A\u52E2\u50F9\u8CFC\u8CB7\u8CE3\u92B7\u8CBB\u8CBF\u9054\u904B\u71DF\u5C64\u5EE3\u6A19\u898F\u5283\u9805\u9078\u64C7\u8853\u5099\u74B0\u7BC0\u6230\u98A8\u96AA\u5718\u968A\u9818\u5C0E\u7D44\u7E54\u6236\u5E6B\u8655\u689D\u7DDA\u8B8A\u7D04\u89C0\u773E\u8996\u983B\u5716\u66F8\u8B80\u5BEB\u8A9E\u7C21\u55AE\u96DC\u96E3\u71B1\u6232\u5287\u6A02\u6B61\u611B\u89AA\u5ABD\u5152\u6B72\u8A18\u61B6\u5922\u98DB\u9CE5\u9B5A\u99AC\u9F8D\u91AB\u85E5\u7642\u885B\u8B77\u8B49\u9280\u9322\u8CA8\u5E63\u532F\u514C\u6F32\u76E4\u50B5\u6B0A\u6F64\u6DE8\u984D\u7A05\u8CEC\u7C3D\u7E8C\u7E7C\u7D00\u9304\u6E2C\u8A66\u9A57\u8A55\u5BE9\u805E\u8A2A\u8AC7\u8B1B\u8AB2\u8A13\u7DF4\u7FD2\u969B\u806F\u9023\u8F09\u570D\u6C23\u6EAB\u6FD5\u71C8\u8166\u8EDF\u6A94\u78BC\u8A5E\u6F22\u723E\u6176\u5BE7\u8607\u7063\u5EF3\u6A13\u5712\u9928\u9435\u9418\u93AE\u7E23\u9109\u8FB2\u990A\u98EF\u98F2\u6F38\u72C0\u614B\u78BA\u9808\u982D\u81C9\u8072\u97FF\u8A72\u55CE\u8AB0\u7D66\u9060\u908A\u525B\u865F\u63DB\u986F\u96B1\u9589\u9593\u8077\u8CAC\u8CA0\u64D4\u7372\u734E\u52F5\u8CFD\u52DD\u6557\u8F38\u8D0F\u905E\u50B3\u8F49\u93C8\u8A0E\u8B70\u6C7A\u5247\u5354\u8ABF\u5275\u6EFF\u5F35";
|
|
34076
|
+
var SIMPLIFIED_SET = new Set(SIMPLIFIED_ONLY);
|
|
34077
|
+
var TRADITIONAL_SET = new Set(TRADITIONAL_ONLY);
|
|
34078
|
+
function chineseScript(text) {
|
|
34079
|
+
let simp = 0;
|
|
34080
|
+
let trad = 0;
|
|
34081
|
+
for (const ch of text) {
|
|
34082
|
+
if (SIMPLIFIED_SET.has(ch))
|
|
34083
|
+
simp++;
|
|
34084
|
+
else if (TRADITIONAL_SET.has(ch))
|
|
34085
|
+
trad++;
|
|
34086
|
+
}
|
|
34087
|
+
return trad > simp ? "zh-TW" : simp > trad ? "zh-CN" : void 0;
|
|
34088
|
+
}
|
|
34089
|
+
function chineseLangHint(lang) {
|
|
34090
|
+
const l = (lang ?? "").trim().toLowerCase();
|
|
34091
|
+
if (/^zh-(?:hant|tw|hk|mo)\b/.test(l))
|
|
34092
|
+
return "zh-TW";
|
|
34093
|
+
if (/^zh(?:-(?:hans|cn|sg|my)\b|$)/.test(l))
|
|
34094
|
+
return "zh-CN";
|
|
34095
|
+
return void 0;
|
|
34096
|
+
}
|
|
34097
|
+
function cjkLang(text, deckZh = "zh-CN") {
|
|
34098
|
+
if (KANA_RE.test(text))
|
|
34099
|
+
return "ja-JP";
|
|
34100
|
+
if (HANGUL_RE.test(text))
|
|
34101
|
+
return "ko-KR";
|
|
34102
|
+
if (HAN_RE.test(text))
|
|
34103
|
+
return chineseScript(text) ?? deckZh;
|
|
34104
|
+
return void 0;
|
|
34105
|
+
}
|
|
34106
|
+
function deckChineseLang(texts, hint) {
|
|
34107
|
+
const fromHint = chineseLangHint(hint);
|
|
34108
|
+
if (fromHint)
|
|
34109
|
+
return fromHint;
|
|
34110
|
+
let tw = 0;
|
|
34111
|
+
let cn = 0;
|
|
34112
|
+
for (const t of texts) {
|
|
34113
|
+
const v = chineseScript(t);
|
|
34114
|
+
if (v === "zh-TW")
|
|
34115
|
+
tw++;
|
|
34116
|
+
else if (v === "zh-CN")
|
|
34117
|
+
cn++;
|
|
34118
|
+
}
|
|
34119
|
+
return tw > cn ? "zh-TW" : "zh-CN";
|
|
34120
|
+
}
|
|
34121
|
+
var CJK_THEME_EA_FONT = "Microsoft YaHei";
|
|
34122
|
+
var CJK_THEME_EA_FONT_TC = "Microsoft JhengHei";
|
|
34123
|
+
var CJK_THEME_EA_XML = {
|
|
34124
|
+
"zh-CN": `<a:ea typeface="${CJK_THEME_EA_FONT}" panose="020B0503020204020204" pitchFamily="34" charset="-122"/>`,
|
|
34125
|
+
"zh-TW": `<a:ea typeface="${CJK_THEME_EA_FONT_TC}" panose="020B0604030504040204" pitchFamily="34" charset="-120"/>`
|
|
34126
|
+
};
|
|
34127
|
+
async function withCjkThemeFont(buf, langs, deckZh = langs.has("zh-CN") ? "zh-CN" : "zh-TW") {
|
|
34128
|
+
if (!(langs.has("zh-CN") || langs.has("zh-TW")) || langs.has("ja-JP") || langs.has("ko-KR"))
|
|
34129
|
+
return buf;
|
|
34130
|
+
const zip = await import_jszip4.default.loadAsync(buf);
|
|
34131
|
+
const themes = Object.keys(zip.files).filter((p) => /^ppt\/theme\/theme\d+\.xml$/.test(p));
|
|
34132
|
+
for (const path of themes) {
|
|
34133
|
+
const xml = await zip.file(path).async("string");
|
|
34134
|
+
const next = xml.replace(/(<a:(?:major|minor)Font>\s*<a:latin\b[^>]*\/>\s*)<a:ea typeface=""\s*\/>/g, `$1${CJK_THEME_EA_XML[deckZh]}`);
|
|
34135
|
+
if (next !== xml)
|
|
34136
|
+
zip.file(path, next);
|
|
34137
|
+
}
|
|
34138
|
+
return await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
34139
|
+
}
|
|
33589
34140
|
async function injectGradients(buf, grads) {
|
|
33590
34141
|
const zip = await import_jszip4.default.loadAsync(buf);
|
|
33591
34142
|
for (const path of Object.keys(zip.files).filter((p) => /^ppt\/slides\/slide\d+\.xml$/.test(p))) {
|
|
@@ -33603,18 +34154,15 @@ async function injectGradients(buf, grads) {
|
|
|
33603
34154
|
}
|
|
33604
34155
|
return await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
33605
34156
|
}
|
|
33606
|
-
var CJK_RE = /[ -〿㐀-鿿豈--]/;
|
|
33607
34157
|
function isSingleLineBox(el) {
|
|
33608
34158
|
const linePx = isFiniteNum(el.lineHeightPx) && el.lineHeightPx > 0 ? el.lineHeightPx : el.fontSizePx * 1.3;
|
|
33609
34159
|
return el.frame.h <= linePx * 1.5;
|
|
33610
34160
|
}
|
|
33611
34161
|
function compensateTextWidth(el, frame) {
|
|
33612
34162
|
const single = isSingleLineBox(el);
|
|
33613
|
-
const cjk =
|
|
34163
|
+
const cjk = SLACK_CJK_RE.test(el.text);
|
|
33614
34164
|
const pxToIn = el.frame.w > 0 ? frame.w / el.frame.w : 0;
|
|
33615
|
-
const
|
|
33616
|
-
const pct = single ? cjk ? 0.22 : 0.16 : cjk ? 0.1 : 0.06;
|
|
33617
|
-
const pad = Math.max(frame.w * pct, floor);
|
|
34165
|
+
const pad = textSlack(frame.w, el.fontSizePx * pxToIn, single, cjk);
|
|
33618
34166
|
const align = el.align ?? "left";
|
|
33619
34167
|
let x = frame.x;
|
|
33620
34168
|
if (align === "right")
|
|
@@ -33656,6 +34204,8 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33656
34204
|
const gradients = [];
|
|
33657
34205
|
const perSlideAnims = [];
|
|
33658
34206
|
const perSlideTransitions = [];
|
|
34207
|
+
const deckLangs = /* @__PURE__ */ new Set();
|
|
34208
|
+
const deckZh = deckChineseLang(snapshot.slides.flatMap((sl) => sl.elements.flatMap((e) => e.kind === "text" ? [String(e.text ?? "")] : [])), snapshot.lang);
|
|
33659
34209
|
for (const [slideNo, slideSpec] of snapshot.slides.entries()) {
|
|
33660
34210
|
const slide = pptx.addSlide();
|
|
33661
34211
|
const bg = cssColorToHex(slideSpec.background);
|
|
@@ -33701,7 +34251,20 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33701
34251
|
break;
|
|
33702
34252
|
}
|
|
33703
34253
|
const tf = compensateTextWidth(el, frame);
|
|
33704
|
-
|
|
34254
|
+
const lang = cjkLang(text, deckZh);
|
|
34255
|
+
if (lang)
|
|
34256
|
+
deckLangs.add(lang);
|
|
34257
|
+
const runs = Array.isArray(el.runs) ? el.runs.filter((r) => r && typeof r.text === "string" && r.text) : [];
|
|
34258
|
+
const pt = (px) => Math.round(px * PT_PER_PX * 10) / 10;
|
|
34259
|
+
const body = runs.length > 1 ? runs.map((r) => ({
|
|
34260
|
+
text: r.text,
|
|
34261
|
+
options: {
|
|
34262
|
+
...cssColorToHex(r.color) ? { color: cssColorToHex(r.color) } : {},
|
|
34263
|
+
...typeof r.bold === "boolean" ? { bold: r.bold } : {},
|
|
34264
|
+
...isFiniteNum(r.fontSizePx) && r.fontSizePx > 0 ? { fontSize: pt(r.fontSizePx) } : {}
|
|
34265
|
+
}
|
|
34266
|
+
})) : text;
|
|
34267
|
+
slide.addText(body, {
|
|
33705
34268
|
x: tf.x,
|
|
33706
34269
|
y: tf.y,
|
|
33707
34270
|
w: tf.w,
|
|
@@ -33719,16 +34282,20 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33719
34282
|
valign: el.valign ?? (isSingleLineBox(el) ? "middle" : "top"),
|
|
33720
34283
|
// CJK text: omit fontFace entirely — any specific family (even a CJK
|
|
33721
34284
|
// one the exporter lacks) makes LibreOffice / Google Slides substitute
|
|
33722
|
-
// and drop glyphs.
|
|
33723
|
-
//
|
|
33724
|
-
//
|
|
33725
|
-
//
|
|
34285
|
+
// and drop glyphs. (Tried an allowlisted-CJK-serif exception — STSong
|
|
34286
|
+
// renders in --editable but VANISHES in hybrid, the default deck mode;
|
|
34287
|
+
// so no CJK font may be pinned. Serif CJK titles are not achievable
|
|
34288
|
+
// here.) The CJK face comes from the theme's East Asian slot instead
|
|
34289
|
+
// (withCjkThemeFont) and the run's `lang` (cjkLang).
|
|
33726
34290
|
// Latin text: first real family in the stack.
|
|
33727
|
-
.../* @__PURE__ */ ((f2) => f2 ? { fontFace: f2 } : {})(
|
|
34291
|
+
.../* @__PURE__ */ ((f2) => f2 ? { fontFace: f2 } : {})(SLACK_CJK_RE.test(text) ? void 0 : pickFontFace(el.fontFamily)),
|
|
34292
|
+
...lang ? { lang } : {},
|
|
33728
34293
|
...isFiniteNum(el.lineHeightPx) ? { lineSpacing: Math.round(el.lineHeightPx * PT_PER_PX * 10) / 10 } : {},
|
|
33729
34294
|
// Boxes are browser-measured: kill pptx's own internal padding so
|
|
33730
34295
|
// the text sits where Chromium put it.
|
|
33731
|
-
margin: 0
|
|
34296
|
+
margin: 0,
|
|
34297
|
+
...isFiniteNum(el.rotateDeg) && el.rotateDeg % 360 !== 0 ? { rotate: el.rotateDeg } : {},
|
|
34298
|
+
...el.chartText ? { objectName: `${CHART_TEXT_NAME} ${elNo + 1}` } : {}
|
|
33732
34299
|
});
|
|
33733
34300
|
recordShape(el);
|
|
33734
34301
|
break;
|
|
@@ -33767,7 +34334,9 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33767
34334
|
h: frame.h,
|
|
33768
34335
|
...radius > 0 ? { rectRadius: radius } : {},
|
|
33769
34336
|
fill: fill ? { color: fill, ...transparency > 0 ? { transparency } : {} } : { type: "none" },
|
|
33770
|
-
|
|
34337
|
+
// An rgba hairline (glass cards' rgba(255,255,255,.08)) keeps its alpha —
|
|
34338
|
+
// dropped, it turned into a solid white outline in --editable decks.
|
|
34339
|
+
line: line ? { color: line, width: Math.max(0.25, (el.borderWidthPx ?? 1) * PT_PER_PX), ...lineAlpha(el.borderColor), ...el.borderDash ? { dashType: "dash" } : {} } : { type: "none" },
|
|
33771
34340
|
...shadow ? { shadow } : {}
|
|
33772
34341
|
});
|
|
33773
34342
|
recordShape(el);
|
|
@@ -33796,7 +34365,7 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33796
34365
|
y: frame.y,
|
|
33797
34366
|
w: frame.w,
|
|
33798
34367
|
h: frame.h,
|
|
33799
|
-
line: { color: cssColorToHex(el.color) ?? "1A1A1A", width: Math.max(0.25, (el.widthPx ?? 1) * PT_PER_PX) }
|
|
34368
|
+
line: { color: cssColorToHex(el.color) ?? "1A1A1A", width: Math.max(0.25, (el.widthPx ?? 1) * PT_PER_PX), ...lineAlpha(el.color) }
|
|
33800
34369
|
});
|
|
33801
34370
|
recordShape(el);
|
|
33802
34371
|
break;
|
|
@@ -33848,6 +34417,10 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33848
34417
|
valAxisLineColor: "C9D0DA",
|
|
33849
34418
|
valGridLine: { color: "E2E8F0", style: "dash", size: 0.5 },
|
|
33850
34419
|
...horizontal ? { barDir: "bar" } : {},
|
|
34420
|
+
// `zeroBaseline: true` pins the value axis at 0 (Office would zoom a far-from-zero line).
|
|
34421
|
+
...spec.zeroBaseline === true && type !== "pie" && type !== "doughnut" ? { valAxisMinVal: 0 } : {},
|
|
34422
|
+
// radar `max` = the outer ring (deck-build passes it through from the spec).
|
|
34423
|
+
...type === "radar" && Number(spec.max) > 0 ? { valAxisMinVal: 0, valAxisMaxVal: Number(spec.max) } : {},
|
|
33851
34424
|
// `showValues: true` in the data-chart prints each point's number on
|
|
33852
34425
|
// the mark (opt-in: on a dense series the labels collide).
|
|
33853
34426
|
...spec.showValues === true ? { showValue: true } : {},
|
|
@@ -33868,6 +34441,7 @@ async function convertLayoutToPptx(snapshot) {
|
|
|
33868
34441
|
let buf = await pptx.write("nodebuffer");
|
|
33869
34442
|
if (gradients.length)
|
|
33870
34443
|
buf = await injectGradients(buf, gradients);
|
|
34444
|
+
buf = await withCjkThemeFont(buf, deckLangs, deckZh);
|
|
33871
34445
|
buf = await injectTransitions(buf, perSlideTransitions);
|
|
33872
34446
|
buf = await injectAnimations(buf, perSlideAnims);
|
|
33873
34447
|
return { bytes: await finalizePptx(new Uint8Array(buf)), warnings };
|
|
@@ -33890,14 +34464,109 @@ async function imagesToPptx(imagesBase64Png) {
|
|
|
33890
34464
|
return finalizePptx(new Uint8Array(buf));
|
|
33891
34465
|
}
|
|
33892
34466
|
|
|
34467
|
+
// packages/core/dist/document/html-pptx-headless.js
|
|
34468
|
+
var import_node_crypto = require("node:crypto");
|
|
34469
|
+
|
|
33893
34470
|
// packages/core/dist/document/html-pptx-walker.js
|
|
33894
34471
|
var DOCUMENT_CAPTURE_DENSITY = 2;
|
|
34472
|
+
var CHART_SVG_SELECTOR = "svg.talos-chart-svg";
|
|
34473
|
+
var CHART_TEXT_HIDE_CSS = `${CHART_SVG_SELECTOR} text,${CHART_SVG_SELECTOR} text *{fill:transparent !important}`;
|
|
34474
|
+
var BACKDROP_HIDE_CSS = "*{color:transparent !important;-webkit-text-fill-color:transparent !important;text-shadow:none !important}" + CHART_TEXT_HIDE_CSS;
|
|
34475
|
+
var CAPTURE_HIDE_TEXT_JS = `(() => {
|
|
34476
|
+
if (document.getElementById("__talos_cap_hide")) return true;
|
|
34477
|
+
document.querySelectorAll("svg").forEach((el) => {
|
|
34478
|
+
if (el.parentElement && el.parentElement.closest("svg")) return;
|
|
34479
|
+
el.setAttribute("data-talos-cap-color", el.style.getPropertyValue("color") + "|" + el.style.getPropertyPriority("color"));
|
|
34480
|
+
el.style.setProperty("color", getComputedStyle(el).color, "important");
|
|
34481
|
+
});
|
|
34482
|
+
document.querySelectorAll("section.slide *:not(svg):not(svg *)").forEach((el) => {
|
|
34483
|
+
const cs = getComputedStyle(el);
|
|
34484
|
+
if (cs.webkitBackgroundClip === "text" || cs.backgroundClip === "text") el.setAttribute("data-talos-cap-clip", "");
|
|
34485
|
+
});
|
|
34486
|
+
const s = document.createElement("style");
|
|
34487
|
+
s.id = "__talos_cap_hide";
|
|
34488
|
+
s.textContent = "section.slide :not(svg):not(svg *){color:transparent !important;-webkit-text-fill-color:transparent !important;text-shadow:none !important}" +
|
|
34489
|
+
"[data-talos-cap-clip]{visibility:hidden !important}" + ${JSON.stringify(CHART_TEXT_HIDE_CSS)};
|
|
34490
|
+
document.head.appendChild(s);
|
|
34491
|
+
return true;
|
|
34492
|
+
})()`;
|
|
34493
|
+
var CAPTURE_SHOW_TEXT_JS = `(() => {
|
|
34494
|
+
document.getElementById("__talos_cap_hide")?.remove();
|
|
34495
|
+
document.querySelectorAll("[data-talos-cap-clip]").forEach((el) => el.removeAttribute("data-talos-cap-clip"));
|
|
34496
|
+
document.querySelectorAll("[data-talos-cap-color]").forEach((el) => {
|
|
34497
|
+
const [v, p] = el.getAttribute("data-talos-cap-color").split("|");
|
|
34498
|
+
if (v) el.style.setProperty("color", v, p); else el.style.removeProperty("color");
|
|
34499
|
+
el.removeAttribute("data-talos-cap-color");
|
|
34500
|
+
});
|
|
34501
|
+
return true;
|
|
34502
|
+
})()`;
|
|
34503
|
+
var captureIsolateJs = (capId) => `(() => {
|
|
34504
|
+
const t = document.querySelector('[data-talos-cap-id="${capId.replace(/[^0-9]/g, "")}"]');
|
|
34505
|
+
if (!t) return false;
|
|
34506
|
+
const slide = t.closest("section.slide") || document.body;
|
|
34507
|
+
slide.querySelectorAll("*").forEach((el) => {
|
|
34508
|
+
if (el === t || t.contains(el) || el.contains(t)) return;
|
|
34509
|
+
if (t.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) el.setAttribute("data-talos-cap-after", "");
|
|
34510
|
+
});
|
|
34511
|
+
if (!document.getElementById("__talos_cap_after")) {
|
|
34512
|
+
const s = document.createElement("style");
|
|
34513
|
+
s.id = "__talos_cap_after";
|
|
34514
|
+
s.textContent = "[data-talos-cap-after]{visibility:hidden !important}";
|
|
34515
|
+
document.head.appendChild(s);
|
|
34516
|
+
}
|
|
34517
|
+
return true;
|
|
34518
|
+
})()`;
|
|
34519
|
+
var CAPTURE_RELEASE_JS = `(() => {
|
|
34520
|
+
document.querySelectorAll("[data-talos-cap-after]").forEach((el) => el.removeAttribute("data-talos-cap-after"));
|
|
34521
|
+
return true;
|
|
34522
|
+
})()`;
|
|
34523
|
+
var DECOR_LAYER_SELECTOR = ".slide-backdrop";
|
|
34524
|
+
var DECOR_RASTER_MARKERS = ".theme-art-layer, .sig-layer";
|
|
34525
|
+
var decorIsolateJs = (si) => `(() => {
|
|
34526
|
+
const slide = document.querySelectorAll("section.slide")[${Math.max(0, Math.floor(si))}];
|
|
34527
|
+
if (!slide) return false;
|
|
34528
|
+
const layers = [...slide.querySelectorAll("[data-talos-decor-id]")];
|
|
34529
|
+
if (!layers.length) return false;
|
|
34530
|
+
slide.querySelectorAll("*").forEach((el) => {
|
|
34531
|
+
if (layers.some((l) => l === el || l.contains(el))) return;
|
|
34532
|
+
el.setAttribute(layers.some((l) => el.contains(l)) ? "data-talos-decor-wrap" : "data-talos-decor-hide", "");
|
|
34533
|
+
});
|
|
34534
|
+
if (!document.getElementById("__talos_decor_hide")) {
|
|
34535
|
+
const s = document.createElement("style");
|
|
34536
|
+
s.id = "__talos_decor_hide";
|
|
34537
|
+
s.textContent = "[data-talos-decor-hide]{visibility:hidden !important}" +
|
|
34538
|
+
"[data-talos-decor-wrap]{background:none !important;border-color:transparent !important;box-shadow:none !important;outline:none !important}";
|
|
34539
|
+
document.head.appendChild(s);
|
|
34540
|
+
}
|
|
34541
|
+
return true;
|
|
34542
|
+
})()`;
|
|
34543
|
+
var DECOR_RELEASE_JS = `(() => {
|
|
34544
|
+
document.getElementById("__talos_decor_hide")?.remove();
|
|
34545
|
+
document.querySelectorAll("[data-talos-decor-hide]").forEach((el) => el.removeAttribute("data-talos-decor-hide"));
|
|
34546
|
+
document.querySelectorAll("[data-talos-decor-wrap]").forEach((el) => el.removeAttribute("data-talos-decor-wrap"));
|
|
34547
|
+
return true;
|
|
34548
|
+
})()`;
|
|
34549
|
+
function placeDecorPicture(elements, dataUri) {
|
|
34550
|
+
const rest = elements.filter((e) => !e || e.kind !== "decor");
|
|
34551
|
+
return dataUri ? [{ kind: "image", frame: { x: 0, y: 0, w: 1280, h: 720 }, data: dataUri }, ...rest] : rest;
|
|
34552
|
+
}
|
|
33895
34553
|
var LAYOUT_WALKER_JS = `
|
|
33896
34554
|
(() => {
|
|
33897
34555
|
const sections = [...document.querySelectorAll("section.slide")];
|
|
33898
34556
|
if (!sections.length) return { error: "no <section class=\\"slide\\"> found" };
|
|
33899
34557
|
const notes = [];
|
|
34558
|
+
// Hybrid/backdrop mode (see header). typeof-guarded: in-app it is a script-level
|
|
34559
|
+
// const (not a window property), headless it is window.__TALOS_BD.
|
|
34560
|
+
const hybrid = typeof __TALOS_BD !== "undefined" && !!__TALOS_BD;
|
|
33900
34561
|
const color = (c) => (c && c !== "rgba(0, 0, 0, 0)" ? c : undefined);
|
|
34562
|
+
// Alpha of a computed CSS color ("rgb(..)" \u2192 1, "rgba(.., 0)" / "transparent" \u2192 0).
|
|
34563
|
+
const fillAlpha = (c) => {
|
|
34564
|
+
if (!c || c === "transparent") return 0;
|
|
34565
|
+
const m = c.match(/rgba?\\(([^)]*)\\)/);
|
|
34566
|
+
if (!m) return 1;
|
|
34567
|
+
const parts = m[1].split(/[\\s,\\/]+/).filter(Boolean);
|
|
34568
|
+
return parts.length >= 4 ? parseFloat(parts[3]) : 1;
|
|
34569
|
+
};
|
|
33901
34570
|
const alignOf = (v) => (v === "center" ? "center" : v === "right" || v === "end" ? "right" : "left");
|
|
33902
34571
|
// Entrance-animation intent: an element (or, for text, its nearest ancestor)
|
|
33903
34572
|
// carrying data-anim="<effect>" [+ -seq/-start/-delay/-dur]. Emitted as {anim}
|
|
@@ -33922,6 +34591,60 @@ var LAYOUT_WALKER_JS = `
|
|
|
33922
34591
|
const du = num("data-anim-dur"); if (du !== undefined) o.durMs = du;
|
|
33923
34592
|
return o;
|
|
33924
34593
|
};
|
|
34594
|
+
// deck-chart-svg text \u2192 native text elements. One element per <text>; a <tspan>
|
|
34595
|
+
// inside it (legend value, radar score) is a run of that element, so Office lays
|
|
34596
|
+
// the line out as one piece and the anchor holds for all of it. Frame = the
|
|
34597
|
+
// text's rendered box; font size scaled by the svg's viewBox\u2192screen factor;
|
|
34598
|
+
// paint = the computed fill; text-anchor \u2192 alignment (the side that stays put
|
|
34599
|
+
// when the box is widened for Office's metrics). A rotated <text> keeps its
|
|
34600
|
+
// unrotated box (getBBox through the CTM) plus the angle.
|
|
34601
|
+
const ANCHOR_ALIGN = { start: "left", middle: "center", end: "right" };
|
|
34602
|
+
const chartText = (svg, org, out, ea) => {
|
|
34603
|
+
for (const t of svg.querySelectorAll("text")) {
|
|
34604
|
+
const tcs = getComputedStyle(t);
|
|
34605
|
+
if (tcs.display === "none" || tcs.visibility === "hidden" || parseFloat(tcs.opacity) === 0) continue;
|
|
34606
|
+
const ctm = t.getScreenCTM();
|
|
34607
|
+
if (!ctm) continue;
|
|
34608
|
+
const scale = Math.hypot(ctm.a, ctm.b) || 1;
|
|
34609
|
+
const runs = [];
|
|
34610
|
+
for (const n of t.childNodes) {
|
|
34611
|
+
const host = n.nodeType === Node.TEXT_NODE ? t : n.nodeType === Node.ELEMENT_NODE ? n : null;
|
|
34612
|
+
if (!host) continue;
|
|
34613
|
+
const raw = (n.textContent || "").replace(/\\s+/g, " ");
|
|
34614
|
+
if (!raw.trim()) continue;
|
|
34615
|
+
const cs = host === t ? tcs : getComputedStyle(host);
|
|
34616
|
+
if (fillAlpha(cs.fill) === 0) continue;
|
|
34617
|
+
// A tspan pushed off by dx (the gap the generator leaves before a value) reads as a space.
|
|
34618
|
+
const gap = runs.length && host !== t && parseFloat(host.getAttribute("dx")) > 0 ? " " : "";
|
|
34619
|
+
runs.push({ text: gap + raw, color: cs.fill, bold: (parseInt(cs.fontWeight, 10) || 400) >= 600, fontSizePx: parseFloat(cs.fontSize) * scale });
|
|
34620
|
+
}
|
|
34621
|
+
const text = runs.map((r) => r.text).join("").replace(/\\s+/g, " ").trim();
|
|
34622
|
+
if (!text) continue;
|
|
34623
|
+
runs[0].text = runs[0].text.replace(/^\\s+/, "");
|
|
34624
|
+
runs[runs.length - 1].text = runs[runs.length - 1].text.replace(/\\s+$/, "");
|
|
34625
|
+
const deg = (Math.atan2(ctm.b, ctm.a) * 180) / Math.PI;
|
|
34626
|
+
let frame;
|
|
34627
|
+
if (Math.abs(deg) > 0.5) {
|
|
34628
|
+
const b = t.getBBox();
|
|
34629
|
+
const c = new DOMPoint(b.x + b.width / 2, b.y + b.height / 2).matrixTransform(ctm);
|
|
34630
|
+
const w = b.width * scale, h = b.height * scale;
|
|
34631
|
+
frame = { x: c.x - org.left - w / 2, y: c.y - org.top - h / 2, w, h };
|
|
34632
|
+
} else {
|
|
34633
|
+
const r = t.getBoundingClientRect();
|
|
34634
|
+
if (!r.width || !r.height) continue;
|
|
34635
|
+
frame = { x: r.left - org.left, y: r.top - org.top, w: r.width, h: r.height };
|
|
34636
|
+
}
|
|
34637
|
+
out.push({
|
|
34638
|
+
kind: "text", frame, text,
|
|
34639
|
+
fontSizePx: Math.max(...runs.map((r) => r.fontSizePx)), color: runs[0].color, fontFamily: tcs.fontFamily,
|
|
34640
|
+
bold: runs.every((r) => r.bold), italic: tcs.fontStyle.includes("italic"),
|
|
34641
|
+
align: ANCHOR_ALIGN[tcs.textAnchor] || "left", chartText: true,
|
|
34642
|
+
...(runs.length > 1 ? { runs } : {}),
|
|
34643
|
+
...(Math.abs(deg) > 0.5 ? { rotateDeg: Math.round(((deg % 360) + 360) % 360 * 10) / 10 } : {}),
|
|
34644
|
+
...(ea ? { anim: ea } : {}),
|
|
34645
|
+
});
|
|
34646
|
+
}
|
|
34647
|
+
};
|
|
33925
34648
|
const slides = sections.map((sec, si) => {
|
|
33926
34649
|
const org = sec.getBoundingClientRect();
|
|
33927
34650
|
const rel = (r) => ({ x: r.left - org.left, y: r.top - org.top, w: r.width, h: r.height });
|
|
@@ -33949,6 +34672,12 @@ var LAYOUT_WALKER_JS = `
|
|
|
33949
34672
|
(cs.webkitBackgroundClip === "text" || cs.backgroundClip === "text" ||
|
|
33950
34673
|
cs.getPropertyValue("--talos-bake").trim() === "1" ||
|
|
33951
34674
|
(child.parentElement.closest && child.parentElement.closest("[data-bake]")))) continue;
|
|
34675
|
+
// Text with no visible fill is not text a reader sees as text: outline
|
|
34676
|
+
// display type (color/-webkit-text-fill-color: transparent plus a
|
|
34677
|
+
// -webkit-text-stroke) \u2014 section's ghost chapter numeral. In hybrid the
|
|
34678
|
+
// stroke is already in the backdrop; emitted as native text the
|
|
34679
|
+
// transparency was lost and PowerPoint drew it SOLID across the slide.
|
|
34680
|
+
if (fillAlpha(cs.webkitTextFillColor || cs.color) === 0) continue;
|
|
33952
34681
|
const lh = parseFloat(cs.lineHeight);
|
|
33953
34682
|
const ta = animOf(child.parentElement);
|
|
33954
34683
|
elements.push({
|
|
@@ -33970,6 +34699,15 @@ var LAYOUT_WALKER_JS = `
|
|
|
33970
34699
|
// SVG elements keep their original case ("svg"), unlike HTML ("DIV") \u2014
|
|
33971
34700
|
// normalize so the tag checks below (esp. SVG) match either way.
|
|
33972
34701
|
const tag = child.tagName.toUpperCase();
|
|
34702
|
+
// Native mode: a decoration layer carrying art-direction / signature markup
|
|
34703
|
+
// is one full-slide picture, not native shapes (DECOR_LAYER_SELECTOR above).
|
|
34704
|
+
// Hybrid shoots it into the backdrop with everything else, so it walks on.
|
|
34705
|
+
if (!hybrid && child.matches && child.matches(${JSON.stringify(DECOR_LAYER_SELECTOR)}) && child.querySelector(${JSON.stringify(DECOR_RASTER_MARKERS)})) {
|
|
34706
|
+
const decorId = String(window.__TALOS_DECOR_N = (window.__TALOS_DECOR_N || 0) + 1);
|
|
34707
|
+
child.setAttribute("data-talos-decor-id", decorId);
|
|
34708
|
+
elements.push({ kind: "decor", frame: rel(r), decorId });
|
|
34709
|
+
continue;
|
|
34710
|
+
}
|
|
33973
34711
|
// data-chart placeholder \u2192 a native pptx chart (Stage B), not a raster.
|
|
33974
34712
|
const dc = child.getAttribute ? child.getAttribute("data-chart") : null;
|
|
33975
34713
|
if (dc) { if (r.width && r.height) elements.push({ kind: "chart", frame: rel(r), chart: dc, ...(ea ? { anim: ea } : {}) }); continue; }
|
|
@@ -33986,7 +34724,20 @@ var LAYOUT_WALKER_JS = `
|
|
|
33986
34724
|
if (tag === "SVG" || tag === "CANVAS") {
|
|
33987
34725
|
// Marked for element-level rasterization on the Node side (below):
|
|
33988
34726
|
// only this box becomes a PNG; the rest of the slide stays native text.
|
|
33989
|
-
|
|
34727
|
+
// Native mode tags it so the capture can hide what is emitted after it
|
|
34728
|
+
// (captureIsolateJs). Hybrid never captures per element \u2014 and its backdrop
|
|
34729
|
+
// cache key reads svg outerHTML, so it must not see the attribute.
|
|
34730
|
+
if (r.width && r.height) {
|
|
34731
|
+
let capId;
|
|
34732
|
+
if (!window.__TALOS_BD) {
|
|
34733
|
+
capId = String(window.__TALOS_CAP_N = (window.__TALOS_CAP_N || 0) + 1);
|
|
34734
|
+
child.setAttribute("data-talos-cap-id", capId);
|
|
34735
|
+
}
|
|
34736
|
+
elements.push({ kind: "capture", frame: rel(r), tag: tag.toLowerCase(), ...(capId ? { capId } : {}) });
|
|
34737
|
+
// A deck-chart-svg chart's text goes native ON TOP of its picture (both
|
|
34738
|
+
// modes hide it while shooting \u2014 CHART_TEXT_HIDE_CSS).
|
|
34739
|
+
if (tag === "SVG" && child.matches(${JSON.stringify(CHART_SVG_SELECTOR)})) chartText(child, org, elements, ea);
|
|
34740
|
+
}
|
|
33990
34741
|
continue;
|
|
33991
34742
|
}
|
|
33992
34743
|
if (tag === "VIDEO") {
|
|
@@ -33996,13 +34747,19 @@ var LAYOUT_WALKER_JS = `
|
|
|
33996
34747
|
const fill = color(cs.backgroundColor);
|
|
33997
34748
|
const bw = parseFloat(cs.borderTopWidth);
|
|
33998
34749
|
const borderColor = bw > 0 && cs.borderTopStyle !== "none" ? color(cs.borderTopColor) : undefined;
|
|
33999
|
-
// box-shadow \u2192 native pptx outer shadow
|
|
34000
|
-
//
|
|
34750
|
+
// box-shadow \u2192 native pptx outer shadow: the FIRST NON-INSET layer (computed
|
|
34751
|
+
// serialization is "color offX offY blur spread[ inset]", layers comma-joined).
|
|
34752
|
+
// An inset layer beside it (cool/apple cards' 1px top highlight) is dropped \u2014
|
|
34753
|
+
// it used to void the whole list, so those cards lost their drop shadow.
|
|
34754
|
+
// A list of only inset layers has no native mapping (pptxgenjs writes an
|
|
34755
|
+
// <a:innerShdw> closed by </a:outerShdw>, malformed XML) \u2192 dropped with a note.
|
|
34001
34756
|
let shadow;
|
|
34002
34757
|
const bs = cs.boxShadow;
|
|
34003
|
-
if (bs && bs !== "none"
|
|
34004
|
-
const
|
|
34758
|
+
if (bs && bs !== "none") {
|
|
34759
|
+
const outer = bs.split(/,(?![^(]*\\))/).map((l) => l.trim()).find((l) => !/\\binset\\b/.test(l));
|
|
34760
|
+
const m = outer && outer.match(/(rgba?\\([^)]*\\)|#[0-9a-fA-F]+)\\s+(-?[\\d.]+)px\\s+(-?[\\d.]+)px\\s+(-?[\\d.]+)px/);
|
|
34005
34761
|
if (m) shadow = { color: m[1], offX: parseFloat(m[2]), offY: parseFloat(m[3]), blur: parseFloat(m[4]) };
|
|
34762
|
+
else if (!outer && !hybrid) notes.push("slide " + (si + 1) + ": inset box-shadow with no outer layer has no native pptx equivalent \u2014 dropped");
|
|
34006
34763
|
}
|
|
34007
34764
|
// CSS linear/radial-gradient \u2192 native pptx gradient fill (Stage B injects
|
|
34008
34765
|
// <a:gradFill>). computed backgroundImage: "linear-gradient(135deg, rgb(..) 0%, rgb(..) 100%)".
|
|
@@ -34020,10 +34777,14 @@ var LAYOUT_WALKER_JS = `
|
|
|
34020
34777
|
if (stops.length >= 2) gradient = { type: isRadial ? "radial" : "linear", angle: angle, stops: stops };
|
|
34021
34778
|
}
|
|
34022
34779
|
if (fill || borderColor || shadow || gradient) {
|
|
34023
|
-
|
|
34780
|
+
// Native (--editable) only: hybrid replaces every rect with the full-slide
|
|
34781
|
+
// backdrop raster, which already carries the background-image \u2014 there is
|
|
34782
|
+
// nothing flattened, and the note read as a failure to the model (B4).
|
|
34783
|
+
if (!hybrid && cs.backgroundImage && cs.backgroundImage !== "none" && !gradient) notes.push("slide " + (si + 1) + ": background-image flattened to backgroundColor");
|
|
34024
34784
|
elements.push({
|
|
34025
34785
|
kind: "rect", frame: rel(r), fill, borderColor,
|
|
34026
34786
|
...(borderColor ? { borderWidthPx: bw } : {}),
|
|
34787
|
+
...(borderColor && (cs.borderTopStyle === "dashed" || cs.borderTopStyle === "dotted") ? { borderDash: true } : {}),
|
|
34027
34788
|
...(shadow ? { shadow } : {}),
|
|
34028
34789
|
...(gradient ? { gradient } : {}),
|
|
34029
34790
|
radiusPx: parseFloat(cs.borderTopLeftRadius) || 0,
|
|
@@ -34050,7 +34811,9 @@ var LAYOUT_WALKER_JS = `
|
|
|
34050
34811
|
}
|
|
34051
34812
|
return { background: color(getComputedStyle(sec).backgroundColor) ?? "#ffffff", elements, ...(speakerNotes ? { speakerNotes } : {}), ...(transition ? { transition } : {}) };
|
|
34052
34813
|
});
|
|
34053
|
-
|
|
34814
|
+
const langEl = document.documentElement.lang ? document.documentElement : document.body && document.body.querySelector("[lang]");
|
|
34815
|
+
const lang = langEl ? langEl.getAttribute("lang") : "";
|
|
34816
|
+
return { width: 1280, height: 720, slides, notes, ...(lang ? { lang } : {}) };
|
|
34054
34817
|
})()
|
|
34055
34818
|
`;
|
|
34056
34819
|
|
|
@@ -34077,6 +34840,62 @@ var CAPTURE_SETTLE_MS = Number.isFinite(configuredSettleMs) ? Math.max(0, Math.m
|
|
|
34077
34840
|
var pngDataUri = (bytes) => "data:image/png;base64," + Buffer.from(bytes).toString("base64");
|
|
34078
34841
|
var isolateSlide = (si) => `new Promise((done) => { const secs = document.querySelectorAll("section.slide"); secs.forEach((s, j) => { s.style.display = j === ${si} ? "" : "none"; }); window.scrollTo(0, 0); requestAnimationFrame(() => requestAnimationFrame(() => done(true))); })`;
|
|
34079
34842
|
var showAllSlides = `document.querySelectorAll("section.slide").forEach((s) => { s.style.display = ""; }); document.getElementById("__talos_hide")?.remove(); true`;
|
|
34843
|
+
var backdropKeyScript = (si, scope = "slide") => `(() => {
|
|
34844
|
+
const sec = document.querySelectorAll("section.slide")[${si}];
|
|
34845
|
+
if (!sec) return "";
|
|
34846
|
+
const decorOnly = ${JSON.stringify(scope === "decor")};
|
|
34847
|
+
const org = sec.getBoundingClientRect();
|
|
34848
|
+
const PAINT = ["backgroundColor","backgroundImage","backgroundSize","backgroundPosition","backgroundRepeat","borderTop","borderRight","borderBottom","borderLeft","borderRadius","boxShadow","outline","opacity","transform","filter","backdropFilter","mixBlendMode","clipPath","maskImage","visibility","zIndex"];
|
|
34849
|
+
const px = (v) => parseFloat(v) || 0;
|
|
34850
|
+
const paints = (cs) => (cs.backgroundColor !== "rgba(0, 0, 0, 0)" && cs.backgroundColor !== "transparent") || cs.backgroundImage !== "none"
|
|
34851
|
+
|| px(cs.borderTopWidth) + px(cs.borderRightWidth) + px(cs.borderBottomWidth) + px(cs.borderLeftWidth) > 0
|
|
34852
|
+
|| cs.boxShadow !== "none" || (cs.outlineStyle !== "none" && px(cs.outlineWidth) > 0) || cs.filter !== "none" || cs.backdropFilter !== "none";
|
|
34853
|
+
const visibleText = (el, cs) => px(cs.webkitTextStrokeWidth) > 0 || cs.webkitBackgroundClip === "text" || cs.backgroundClip === "text"
|
|
34854
|
+
|| cs.getPropertyValue("--talos-bake").trim() === "1" || (el.closest && el.closest("[data-bake]"));
|
|
34855
|
+
const rect = (el) => { const r = el.getBoundingClientRect(); return [r.left - org.left, r.top - org.top, r.width, r.height].map(Math.round).join(","); };
|
|
34856
|
+
const out = [];
|
|
34857
|
+
const visit = (el, depth) => {
|
|
34858
|
+
const cs = getComputedStyle(el);
|
|
34859
|
+
if (cs.display === "none") { out.push(depth + "<" + el.tagName + " hidden"); return; }
|
|
34860
|
+
const tag = el.tagName.toUpperCase();
|
|
34861
|
+
let line = depth + "<" + tag;
|
|
34862
|
+
const pseudo = [];
|
|
34863
|
+
for (const ps of ["::before", "::after"]) {
|
|
34864
|
+
const pcs = getComputedStyle(el, ps);
|
|
34865
|
+
if (pcs.content && pcs.content !== "none" && pcs.content !== "normal") pseudo.push(ps + "{" + pcs.content + "|" + pcs.width + "x" + pcs.height + "|" + pcs.top + "," + pcs.left + "|" + PAINT.map((p) => pcs[p]).join("|") + "}");
|
|
34866
|
+
}
|
|
34867
|
+
const media = tag === "IMG" || tag === "SVG" || tag === "CANVAS" || tag === "VIDEO";
|
|
34868
|
+
const text = visibleText(el, cs);
|
|
34869
|
+
if (paints(cs) || media || pseudo.length || text) line += " @" + rect(el) + " " + PAINT.map((p) => cs[p]).join("|") + pseudo.join("");
|
|
34870
|
+
if (text) line += " text=" + el.textContent + "|" + cs.font + "|" + cs.webkitTextStroke + "|" + cs.color;
|
|
34871
|
+
if (tag === "IMG") line += " src=" + (el.currentSrc || el.src);
|
|
34872
|
+
if (tag === "CANVAS") { try { line += " px=" + el.toDataURL(); } catch (e) { line += " px=?"; } }
|
|
34873
|
+
if (tag === "SVG") { out.push(line + " svg=" + el.outerHTML); return; }
|
|
34874
|
+
out.push(line);
|
|
34875
|
+
for (const child of el.children) visit(child, depth + 1);
|
|
34876
|
+
};
|
|
34877
|
+
if (decorOnly) {
|
|
34878
|
+
// --editable decoration picture (decorIsolateJs): only the section's own paint
|
|
34879
|
+
// and its decoration layer(s) are in it \u2014 content is native \u2014 so a content
|
|
34880
|
+
// edit must not miss. The section line is its paint alone, no children.
|
|
34881
|
+
const cs = getComputedStyle(sec);
|
|
34882
|
+
const pseudo = ["::before", "::after"].map((ps) => { const p = getComputedStyle(sec, ps); return p.content + "|" + p.width + "x" + p.height + "|" + PAINT.map((k) => p[k]).join("|"); }).join("");
|
|
34883
|
+
out.push("<SECTION @" + rect(sec) + " " + PAINT.map((p) => cs[p]).join("|") + pseudo);
|
|
34884
|
+
for (const layer of sec.querySelectorAll("[data-talos-decor-id]")) { out.push("@" + rect(layer)); visit(layer, 1); }
|
|
34885
|
+
} else {
|
|
34886
|
+
visit(sec, 0);
|
|
34887
|
+
}
|
|
34888
|
+
// Long data: URIs stand in as length + FNV-1a, so the string stays small.
|
|
34889
|
+
return out.join("\\n").replace(/data:[^"')\\s]{64,}/g, (u) => {
|
|
34890
|
+
let h = 2166136261;
|
|
34891
|
+
for (let i = 0; i < u.length; i++) { h ^= u.charCodeAt(i); h = Math.imul(h, 16777619); }
|
|
34892
|
+
return "data#" + u.length + "#" + (h >>> 0).toString(16);
|
|
34893
|
+
});
|
|
34894
|
+
})()`;
|
|
34895
|
+
var BACKDROP_KEY_VERSION = "talos-backdrop-v2";
|
|
34896
|
+
var DECOR_KEY_VERSION = "talos-decor-v1";
|
|
34897
|
+
var cacheKey = (version, salt, descriptor) => (0, import_node_crypto.createHash)("sha256").update(version).update("\0").update(salt).update("\0").update(descriptor).digest("hex");
|
|
34898
|
+
var sameBytes = (a, b) => a.length === b.length && Buffer.from(a).equals(Buffer.from(b));
|
|
34080
34899
|
async function snapshotHeadless(page, opts = {}) {
|
|
34081
34900
|
const backdrop = opts.backdrop === true;
|
|
34082
34901
|
await page.evaluate(`window.__TALOS_BD = ${JSON.stringify(backdrop)}; true`);
|
|
@@ -34085,11 +34904,30 @@ async function snapshotHeadless(page, opts = {}) {
|
|
|
34085
34904
|
throw new Error(String(snapshot.error));
|
|
34086
34905
|
}
|
|
34087
34906
|
if (backdrop && Array.isArray(snapshot.slides)) {
|
|
34088
|
-
await page.evaluate(`(() => { document.querySelectorAll("*").forEach((el) => { const bake = getComputedStyle(el).getPropertyValue("--talos-bake").trim() === "1" || (el.closest && el.closest("[data-bake]")); if (bake) { const c = getComputedStyle(el).color; el.style.setProperty("color", c, "important"); el.style.setProperty("-webkit-text-fill-color", c, "important"); } }); const s = document.createElement("style"); s.id = "__talos_hide"; s.textContent =
|
|
34907
|
+
await page.evaluate(`(() => { document.querySelectorAll("*").forEach((el) => { const bake = getComputedStyle(el).getPropertyValue("--talos-bake").trim() === "1" || (el.closest && el.closest("[data-bake]")); if (bake) { const c = getComputedStyle(el).color; el.style.setProperty("color", c, "important"); el.style.setProperty("-webkit-text-fill-color", c, "important"); } }); const s = document.createElement("style"); s.id = "__talos_hide"; s.textContent = ${JSON.stringify(BACKDROP_HIDE_CSS)}; document.head.appendChild(s); return true; })()`);
|
|
34908
|
+
const cache = opts.cache;
|
|
34909
|
+
const keys = [];
|
|
34910
|
+
if (cache) {
|
|
34911
|
+
for (let si = 0; si < snapshot.slides.length; si++) {
|
|
34912
|
+
const descriptor = String(await page.evaluate(backdropKeyScript(si)));
|
|
34913
|
+
keys.push(cacheKey(BACKDROP_KEY_VERSION, cache.salt, descriptor));
|
|
34914
|
+
}
|
|
34915
|
+
}
|
|
34089
34916
|
for (let si = 0; si < snapshot.slides.length; si++) {
|
|
34090
|
-
await
|
|
34091
|
-
|
|
34092
|
-
|
|
34917
|
+
const cached = cache ? await cache.get(keys[si]) : void 0;
|
|
34918
|
+
let png;
|
|
34919
|
+
if (cached && !cache.verify) {
|
|
34920
|
+
png = cached;
|
|
34921
|
+
} else {
|
|
34922
|
+
await page.evaluate(isolateSlide(si));
|
|
34923
|
+
await sleep(CAPTURE_SETTLE_MS);
|
|
34924
|
+
png = await page.screenshot({ x: 0, y: 0, width: 1280, height: 720 });
|
|
34925
|
+
if (cached && !sameBytes(cached, png)) {
|
|
34926
|
+
throw new Error(`backdrop cache key ${keys[si].slice(0, 12)} served different pixels on slide ${si + 1} \u2014 something that paints is missing from backdropKeyScript`);
|
|
34927
|
+
}
|
|
34928
|
+
if (cache && !cached)
|
|
34929
|
+
await cache.set(keys[si], png);
|
|
34930
|
+
}
|
|
34093
34931
|
const backdropEl = { kind: "image", frame: { x: 0, y: 0, w: 1280, h: 720 }, data: pngDataUri(png) };
|
|
34094
34932
|
const keepEls = snapshot.slides[si].elements.filter((e) => e && (e.kind === "text" || e.kind === "chart"));
|
|
34095
34933
|
snapshot.slides[si].elements = [backdropEl, ...keepEls];
|
|
@@ -34097,7 +34935,39 @@ async function snapshotHeadless(page, opts = {}) {
|
|
|
34097
34935
|
await page.evaluate(showAllSlides);
|
|
34098
34936
|
return snapshot;
|
|
34099
34937
|
}
|
|
34100
|
-
if (Array.isArray(snapshot.slides)) {
|
|
34938
|
+
if (Array.isArray(snapshot.slides) && snapshot.slides.some((s) => Array.isArray(s.elements) && s.elements.some((e) => e && e.kind === "decor"))) {
|
|
34939
|
+
const cache = opts.cache;
|
|
34940
|
+
for (let si = 0; si < snapshot.slides.length; si++) {
|
|
34941
|
+
const els = snapshot.slides[si].elements;
|
|
34942
|
+
if (!Array.isArray(els) || !els.some((e) => e && e.kind === "decor"))
|
|
34943
|
+
continue;
|
|
34944
|
+
const key = cache ? cacheKey(DECOR_KEY_VERSION, cache.salt, String(await page.evaluate(backdropKeyScript(si, "decor")))) : void 0;
|
|
34945
|
+
const cached = cache ? await cache.get(key) : void 0;
|
|
34946
|
+
let png = cached && !cache.verify ? cached : null;
|
|
34947
|
+
if (!png) {
|
|
34948
|
+
try {
|
|
34949
|
+
await page.evaluate(decorIsolateJs(si));
|
|
34950
|
+
await page.evaluate(isolateSlide(si));
|
|
34951
|
+
await sleep(CAPTURE_SETTLE_MS);
|
|
34952
|
+
png = await page.screenshot({ x: 0, y: 0, width: 1280, height: 720 });
|
|
34953
|
+
} catch (e) {
|
|
34954
|
+
(snapshot.notes ??= []).push("slide " + (si + 1) + ": the decoration layer could not be rasterized (" + e.message + ") \u2014 it is left out");
|
|
34955
|
+
} finally {
|
|
34956
|
+
await page.evaluate(DECOR_RELEASE_JS).catch(() => {
|
|
34957
|
+
});
|
|
34958
|
+
}
|
|
34959
|
+
if (png && cached && !sameBytes(cached, png)) {
|
|
34960
|
+
throw new Error(`decoration cache key ${key.slice(0, 12)} served different pixels on slide ${si + 1} \u2014 something that paints is missing from backdropKeyScript`);
|
|
34961
|
+
}
|
|
34962
|
+
if (png && cache && !cached)
|
|
34963
|
+
await cache.set(key, png);
|
|
34964
|
+
}
|
|
34965
|
+
snapshot.slides[si].elements = placeDecorPicture(els, png ? pngDataUri(png) : null);
|
|
34966
|
+
}
|
|
34967
|
+
await page.evaluate(showAllSlides);
|
|
34968
|
+
}
|
|
34969
|
+
if (Array.isArray(snapshot.slides) && snapshot.slides.some((s) => Array.isArray(s.elements) && s.elements.some((e) => e && e.kind === "capture"))) {
|
|
34970
|
+
await page.evaluate(CAPTURE_HIDE_TEXT_JS);
|
|
34101
34971
|
for (let si = 0; si < snapshot.slides.length; si++) {
|
|
34102
34972
|
const els = snapshot.slides[si].elements;
|
|
34103
34973
|
if (!Array.isArray(els) || !els.some((e) => e && e.kind === "capture"))
|
|
@@ -34117,17 +34987,25 @@ async function snapshotHeadless(page, opts = {}) {
|
|
|
34117
34987
|
continue;
|
|
34118
34988
|
}
|
|
34119
34989
|
try {
|
|
34990
|
+
if (el.capId && await page.evaluate(captureIsolateJs(String(el.capId))))
|
|
34991
|
+
await sleep(CAPTURE_SETTLE_MS);
|
|
34120
34992
|
const png = await page.screenshot({ x, y, width: w, height: h });
|
|
34993
|
+
if (el.capId)
|
|
34994
|
+
await page.evaluate(CAPTURE_RELEASE_JS);
|
|
34121
34995
|
el.kind = "image";
|
|
34122
34996
|
el.data = pngDataUri(png);
|
|
34123
34997
|
delete el.tag;
|
|
34998
|
+
delete el.capId;
|
|
34124
34999
|
} catch (e) {
|
|
34125
35000
|
el.kind = "__drop";
|
|
35001
|
+
await page.evaluate(CAPTURE_RELEASE_JS).catch(() => {
|
|
35002
|
+
});
|
|
34126
35003
|
(snapshot.notes ??= []).push("slide " + (si + 1) + ": <" + (el.tag ?? "svg") + "> could not be rasterized (" + e.message + ")");
|
|
34127
35004
|
}
|
|
34128
35005
|
}
|
|
34129
35006
|
snapshot.slides[si].elements = els.filter((e) => e.kind !== "__drop");
|
|
34130
35007
|
}
|
|
35008
|
+
await page.evaluate(CAPTURE_SHOW_TEXT_JS);
|
|
34131
35009
|
await page.evaluate(showAllSlides);
|
|
34132
35010
|
}
|
|
34133
35011
|
return snapshot;
|
|
@@ -34629,7 +35507,7 @@ async function launchHeadlessChromium() {
|
|
|
34629
35507
|
return null;
|
|
34630
35508
|
}
|
|
34631
35509
|
}
|
|
34632
|
-
var
|
|
35510
|
+
var CJK_RE = /[\u2E80-\u2FFF\u3000-\u303F\u3040-\u30FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
34633
35511
|
var _fontManifest;
|
|
34634
35512
|
async function fontManifest() {
|
|
34635
35513
|
if (_fontManifest !== void 0) return _fontManifest;
|
|
@@ -34642,6 +35520,11 @@ async function fontManifest() {
|
|
|
34642
35520
|
}
|
|
34643
35521
|
return _fontManifest;
|
|
34644
35522
|
}
|
|
35523
|
+
var _faceB64 = /* @__PURE__ */ new Map();
|
|
35524
|
+
async function faceBase64(path) {
|
|
35525
|
+
if (!_faceB64.has(path)) _faceB64.set(path, (await (0, import_promises.readFile)(path).catch(() => null))?.toString("base64") ?? null);
|
|
35526
|
+
return _faceB64.get(path);
|
|
35527
|
+
}
|
|
34645
35528
|
async function faceCss(entries, range, doc) {
|
|
34646
35529
|
const { dir } = await fontManifest();
|
|
34647
35530
|
const rules = [];
|
|
@@ -34650,7 +35533,7 @@ async function faceCss(entries, range, doc) {
|
|
|
34650
35533
|
if (!families.length) continue;
|
|
34651
35534
|
const path = (0, import_node_path.join)(dir, entry.file);
|
|
34652
35535
|
if (!(0, import_node_fs.existsSync)(path)) continue;
|
|
34653
|
-
const b64 =
|
|
35536
|
+
const b64 = await faceBase64(path);
|
|
34654
35537
|
if (!b64) continue;
|
|
34655
35538
|
for (const family of families) {
|
|
34656
35539
|
rules.push(
|
|
@@ -34660,26 +35543,25 @@ async function faceCss(entries, range, doc) {
|
|
|
34660
35543
|
}
|
|
34661
35544
|
return rules.join("");
|
|
34662
35545
|
}
|
|
34663
|
-
var
|
|
35546
|
+
var _faceCssBySet = /* @__PURE__ */ new Map();
|
|
35547
|
+
async function cachedFaceCss(group, entries, range, doc) {
|
|
35548
|
+
const named = (entries ?? []).flatMap((e) => (e.families ?? []).filter((f) => doc.includes(f)));
|
|
35549
|
+
const key = `${group}|${named.join("|")}`;
|
|
35550
|
+
if (!_faceCssBySet.has(key)) _faceCssBySet.set(key, await faceCss(entries, range, doc));
|
|
35551
|
+
return _faceCssBySet.get(key);
|
|
35552
|
+
}
|
|
34664
35553
|
async function cjkFontFaceCss(doc) {
|
|
34665
|
-
if (
|
|
34666
|
-
|
|
34667
|
-
|
|
34668
|
-
}
|
|
34669
|
-
return _cjkFontCss;
|
|
35554
|
+
if (process.env.TALOS_CJK_FONTS === "system") return "";
|
|
35555
|
+
const m = await fontManifest();
|
|
35556
|
+
return cachedFaceCss("cjk", m.cjk, m.cjkUnicodeRange, doc);
|
|
34670
35557
|
}
|
|
34671
|
-
var _latinFontCss;
|
|
34672
35558
|
async function latinFontFaceCss(doc) {
|
|
34673
|
-
|
|
34674
|
-
|
|
34675
|
-
const wanted = (m.latin ?? []).filter((e) => (e.families ?? []).some((f) => doc.includes(f)));
|
|
34676
|
-
_latinFontCss = await faceCss(wanted, void 0);
|
|
34677
|
-
}
|
|
34678
|
-
return _latinFontCss;
|
|
35559
|
+
const m = await fontManifest();
|
|
35560
|
+
return cachedFaceCss("latin", m.latin, void 0, doc);
|
|
34679
35561
|
}
|
|
34680
35562
|
|
|
34681
35563
|
// apps/admin/data/skills.lib/document/bin-src/lib/check-pptx.mjs
|
|
34682
|
-
var
|
|
35564
|
+
var import_node_path4 = require("node:path");
|
|
34683
35565
|
|
|
34684
35566
|
// packages/core/dist/document/doc-validate.js
|
|
34685
35567
|
var import_jszip5 = __toESM(require_lib4(), 1);
|
|
@@ -34695,6 +35577,17 @@ async function validatePdf(bytes) {
|
|
|
34695
35577
|
try {
|
|
34696
35578
|
const doc = await import_pdf_lib.PDFDocument.load(bytes, { ignoreEncryption: true, throwOnInvalidObject: true, updateMetadata: false });
|
|
34697
35579
|
pages = doc.getPageCount();
|
|
35580
|
+
if (doc.isEncrypted) {
|
|
35581
|
+
const { pdfNeedsPassword: pdfNeedsPassword2 } = await Promise.resolve().then(() => (init_pdf_extract(), pdf_extract_exports));
|
|
35582
|
+
if (await pdfNeedsPassword2(bytes).catch(() => true)) {
|
|
35583
|
+
errors.push({
|
|
35584
|
+
code: "pdf-encrypted",
|
|
35585
|
+
message: "the PDF needs a password to open, so its content cannot be checked or rendered \u2014 validate (and render) the unencrypted file before `pdf-ops encrypt`; to inspect this one, `pdf-ops decrypt <in> <password> <out>` first"
|
|
35586
|
+
});
|
|
35587
|
+
return { ok: false, kind: "pdf", errors, warnings, pages };
|
|
35588
|
+
}
|
|
35589
|
+
warnings.push({ code: "pdf-encrypted", message: 'the PDF is encrypted with an owner password only: it opens without a password, but pdf-form / pdf-ops cannot edit it until `pdf-ops decrypt <in> "" <out>`' });
|
|
35590
|
+
}
|
|
34698
35591
|
if (pages === 0)
|
|
34699
35592
|
errors.push({ code: "pdf-no-pages", message: "the PDF parses but has no pages" });
|
|
34700
35593
|
else
|
|
@@ -34924,15 +35817,15 @@ async function validateOoxml(bytes) {
|
|
|
34924
35817
|
}
|
|
34925
35818
|
|
|
34926
35819
|
// packages/core/dist/document/doc-xsd.js
|
|
34927
|
-
var
|
|
34928
|
-
var
|
|
35820
|
+
var import_node_path3 = require("node:path");
|
|
35821
|
+
var import_node_url2 = require("node:url");
|
|
34929
35822
|
var import_promises2 = require("node:fs/promises");
|
|
34930
35823
|
var import_jszip6 = __toESM(require_lib4(), 1);
|
|
34931
35824
|
var _xmllint = null;
|
|
34932
35825
|
function loadXmllint() {
|
|
34933
35826
|
if (!_xmllint) {
|
|
34934
35827
|
const deps = process.env.TALOS_SKILL_DEPS;
|
|
34935
|
-
const spec = deps ? (0,
|
|
35828
|
+
const spec = deps ? (0, import_node_url2.pathToFileURL)((0, import_node_path3.join)(deps, "xmllint-wasm", "index-node.js")).href : "xmllint-wasm";
|
|
34936
35829
|
_xmllint = import(spec);
|
|
34937
35830
|
}
|
|
34938
35831
|
return _xmllint;
|
|
@@ -34975,7 +35868,7 @@ async function loadSchemaSet(schemaDir) {
|
|
|
34975
35868
|
if (!files.length)
|
|
34976
35869
|
throw new Error(`no .xsd schemas found in ${schemaDir}`);
|
|
34977
35870
|
const set = /* @__PURE__ */ new Map();
|
|
34978
|
-
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises2.readFile)((0,
|
|
35871
|
+
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises2.readFile)((0, import_node_path3.join)(schemaDir, f), "utf8"))));
|
|
34979
35872
|
return set;
|
|
34980
35873
|
}
|
|
34981
35874
|
function errText(e) {
|
|
@@ -35079,6 +35972,102 @@ async function slidePartsInOrder(zip) {
|
|
|
35079
35972
|
return toParts(complete ? ordered : byNumber);
|
|
35080
35973
|
}
|
|
35081
35974
|
|
|
35975
|
+
// packages/core/dist/document/png-decode.js
|
|
35976
|
+
var import_node_zlib = require("node:zlib");
|
|
35977
|
+
var SIG = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
35978
|
+
var CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
|
|
35979
|
+
function decodePng(bytes) {
|
|
35980
|
+
try {
|
|
35981
|
+
if (bytes.length < 33 || SIG.some((b, i) => bytes[i] !== b))
|
|
35982
|
+
return void 0;
|
|
35983
|
+
const buf = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
35984
|
+
let width = 0, height = 0, depth = 0, type = -1, interlace = 0;
|
|
35985
|
+
let palette;
|
|
35986
|
+
const idat = [];
|
|
35987
|
+
for (let p = 8; p + 8 <= buf.length; ) {
|
|
35988
|
+
const len = buf.readUInt32BE(p);
|
|
35989
|
+
const kind = buf.toString("latin1", p + 4, p + 8);
|
|
35990
|
+
const data = buf.subarray(p + 8, p + 8 + len);
|
|
35991
|
+
if (kind === "IHDR") {
|
|
35992
|
+
width = data.readUInt32BE(0);
|
|
35993
|
+
height = data.readUInt32BE(4);
|
|
35994
|
+
depth = data[8];
|
|
35995
|
+
type = data[9];
|
|
35996
|
+
interlace = data[12];
|
|
35997
|
+
} else if (kind === "PLTE")
|
|
35998
|
+
palette = data;
|
|
35999
|
+
else if (kind === "IDAT")
|
|
36000
|
+
idat.push(data);
|
|
36001
|
+
else if (kind === "IEND")
|
|
36002
|
+
break;
|
|
36003
|
+
p += 12 + len;
|
|
36004
|
+
}
|
|
36005
|
+
const ch = CHANNELS[type];
|
|
36006
|
+
if (!width || !height || depth !== 8 || interlace !== 0 || ch === void 0 || type === 3 && !palette)
|
|
36007
|
+
return void 0;
|
|
36008
|
+
const raw = (0, import_node_zlib.inflateSync)(Buffer.concat(idat));
|
|
36009
|
+
const stride = width * ch;
|
|
36010
|
+
if (raw.length < height * (stride + 1))
|
|
36011
|
+
return void 0;
|
|
36012
|
+
const cur = new Uint8Array(stride);
|
|
36013
|
+
const prev = new Uint8Array(stride);
|
|
36014
|
+
const rgb = new Uint8Array(width * height * 3);
|
|
36015
|
+
for (let y = 0; y < height; y++) {
|
|
36016
|
+
const off = y * (stride + 1);
|
|
36017
|
+
const filter = raw[off];
|
|
36018
|
+
for (let i = 0; i < stride; i++) {
|
|
36019
|
+
const x = raw[off + 1 + i];
|
|
36020
|
+
const a = i >= ch ? cur[i - ch] : 0;
|
|
36021
|
+
const b = prev[i];
|
|
36022
|
+
const c = i >= ch ? prev[i - ch] : 0;
|
|
36023
|
+
let v;
|
|
36024
|
+
switch (filter) {
|
|
36025
|
+
case 0:
|
|
36026
|
+
v = x;
|
|
36027
|
+
break;
|
|
36028
|
+
case 1:
|
|
36029
|
+
v = x + a;
|
|
36030
|
+
break;
|
|
36031
|
+
case 2:
|
|
36032
|
+
v = x + b;
|
|
36033
|
+
break;
|
|
36034
|
+
case 3:
|
|
36035
|
+
v = x + (a + b >> 1);
|
|
36036
|
+
break;
|
|
36037
|
+
case 4: {
|
|
36038
|
+
const pa = Math.abs(b - c), pb = Math.abs(a - c), pc = Math.abs(a + b - 2 * c);
|
|
36039
|
+
v = x + (pa <= pb && pa <= pc ? a : pb <= pc ? b : c);
|
|
36040
|
+
break;
|
|
36041
|
+
}
|
|
36042
|
+
default:
|
|
36043
|
+
return void 0;
|
|
36044
|
+
}
|
|
36045
|
+
cur[i] = v & 255;
|
|
36046
|
+
}
|
|
36047
|
+
for (let px = 0; px < width; px++) {
|
|
36048
|
+
const o = (y * width + px) * 3;
|
|
36049
|
+
const s = px * ch;
|
|
36050
|
+
if (type === 3) {
|
|
36051
|
+
const k = cur[s] * 3;
|
|
36052
|
+
rgb[o] = palette[k];
|
|
36053
|
+
rgb[o + 1] = palette[k + 1];
|
|
36054
|
+
rgb[o + 2] = palette[k + 2];
|
|
36055
|
+
} else if (ch <= 2) {
|
|
36056
|
+
rgb[o] = rgb[o + 1] = rgb[o + 2] = cur[s];
|
|
36057
|
+
} else {
|
|
36058
|
+
rgb[o] = cur[s];
|
|
36059
|
+
rgb[o + 1] = cur[s + 1];
|
|
36060
|
+
rgb[o + 2] = cur[s + 2];
|
|
36061
|
+
}
|
|
36062
|
+
}
|
|
36063
|
+
prev.set(cur);
|
|
36064
|
+
}
|
|
36065
|
+
return { width, height, rgb };
|
|
36066
|
+
} catch {
|
|
36067
|
+
return void 0;
|
|
36068
|
+
}
|
|
36069
|
+
}
|
|
36070
|
+
|
|
35082
36071
|
// packages/core/dist/document/doc-design-lint.js
|
|
35083
36072
|
var PLACEHOLDER_RE = /替换为|在这里写|占位|示例文本|Lorem ipsum/i;
|
|
35084
36073
|
var CJK_TEXT_RE = /[-ヿ㐀-䶿一-鿿가-豈-]/;
|
|
@@ -35219,7 +36208,7 @@ function lintSlideXml(slideXml, slideNumber, masterBg, expectedLang = detectDeck
|
|
|
35219
36208
|
}
|
|
35220
36209
|
}
|
|
35221
36210
|
}
|
|
35222
|
-
if (worst && !worst.onShapeFill && anyVisualShape)
|
|
36211
|
+
if (worst && !worst.onShapeFill && (anyVisualShape || hasPic))
|
|
35223
36212
|
worst = void 0;
|
|
35224
36213
|
if (placeholder) {
|
|
35225
36214
|
const snippet = placeholder.length > 24 ? `${placeholder.slice(0, 24)}\u2026` : placeholder;
|
|
@@ -35259,7 +36248,10 @@ var EMU_PER_INCH = 914400;
|
|
|
35259
36248
|
var DEFAULT_CANVAS = { w: 12192e3, h: 6858e3 };
|
|
35260
36249
|
var OFFCANVAS_TOL = EMU_PER_INCH / 10;
|
|
35261
36250
|
var MIN_FONT_HUNDREDTHS = 1e3;
|
|
35262
|
-
var
|
|
36251
|
+
var LABEL_MAX_CHARS = 24;
|
|
36252
|
+
var MIN_LABEL_FONT_HUNDREDTHS = 800;
|
|
36253
|
+
var CHART_TEXT_RE = new RegExp(`<p:cNvPr\\b[^>]*\\bname="${CHART_TEXT_NAME}\\b`);
|
|
36254
|
+
var OVERLAP_RATIO = 0.3;
|
|
35263
36255
|
function firstBox(block) {
|
|
35264
36256
|
const xf = /<(?:a|p):xfrm\b[^>]*>[\s\S]*?<\/(?:a|p):xfrm>/.exec(block);
|
|
35265
36257
|
const scope = xf ? xf[0] : block;
|
|
@@ -35277,6 +36269,51 @@ function overlapArea(a, b) {
|
|
|
35277
36269
|
const iy = Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
|
|
35278
36270
|
return ix * iy;
|
|
35279
36271
|
}
|
|
36272
|
+
var CJK_WIDE_RE = /[\u2E80-\u9FFF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
36273
|
+
var EMU_PER_PT = 12700;
|
|
36274
|
+
function inkBox(block, box) {
|
|
36275
|
+
let maxSz = 0;
|
|
36276
|
+
let inkW = 0;
|
|
36277
|
+
let lines = 0;
|
|
36278
|
+
for (const para of block.match(/<a:p>[\s\S]*?<\/a:p>|<a:p\b[^>]*>[\s\S]*?<\/a:p>/g) ?? []) {
|
|
36279
|
+
let paraW = 0;
|
|
36280
|
+
let paraSz = 0;
|
|
36281
|
+
for (const run of para.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
36282
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
36283
|
+
if (raw === void 0)
|
|
36284
|
+
continue;
|
|
36285
|
+
const sz = +(/<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1] ?? 1800) / 100;
|
|
36286
|
+
paraSz = Math.max(paraSz, sz);
|
|
36287
|
+
for (const ch of unescXml(raw))
|
|
36288
|
+
paraW += (CJK_WIDE_RE.test(ch) ? 1 : ch === " " ? 0.3 : 0.55) * sz * EMU_PER_PT;
|
|
36289
|
+
}
|
|
36290
|
+
if (!paraW)
|
|
36291
|
+
continue;
|
|
36292
|
+
maxSz = Math.max(maxSz, paraSz);
|
|
36293
|
+
const paraLines = box.w > 0 ? Math.max(1, Math.ceil(paraW / box.w)) : 1;
|
|
36294
|
+
lines += paraLines;
|
|
36295
|
+
inkW = Math.max(inkW, Math.min(paraW, box.w));
|
|
36296
|
+
}
|
|
36297
|
+
if (!lines)
|
|
36298
|
+
return void 0;
|
|
36299
|
+
const paras = (block.match(/<a:p>[\s\S]*?<\/a:p>|<a:p\b[^>]*>[\s\S]*?<\/a:p>/g) ?? []).filter((p) => /<a:t\b[^>]*>[^<]*\S/.test(p));
|
|
36300
|
+
if (paras.length === 1 && maxSz > 0) {
|
|
36301
|
+
const lnSpc = /<a:lnSpc>\s*<a:spcPts\b[^>]*\bval="(\d+)"/.exec(block)?.[1];
|
|
36302
|
+
const lineH = (lnSpc ? +lnSpc / 100 : maxSz * 1.3) * EMU_PER_PT;
|
|
36303
|
+
const clamped = box.x + box.w >= DEFAULT_CANVAS.w - EMU_PER_PT;
|
|
36304
|
+
if (box.h <= lineH * 1.5 && !clamped) {
|
|
36305
|
+
const text = unescXml((block.match(/<a:t\b[^>]*>([\s\S]*?)<\/a:t>/g) ?? []).join(""));
|
|
36306
|
+
lines = 1;
|
|
36307
|
+
inkW = Math.min(inkW, unpaddedWidth(box.w, maxSz * EMU_PER_PT, true, SLACK_CJK_RE.test(text)));
|
|
36308
|
+
}
|
|
36309
|
+
}
|
|
36310
|
+
const inkH = Math.min(box.h, lines * maxSz * 1.2 * EMU_PER_PT);
|
|
36311
|
+
const algn = /<a:pPr\b[^>]*\balgn="(\w+)"/.exec(block)?.[1];
|
|
36312
|
+
const anchor = /<a:bodyPr\b[^>]*\banchor="(\w+)"/.exec(block)?.[1];
|
|
36313
|
+
const x = algn === "ctr" ? box.x + (box.w - inkW) / 2 : algn === "r" ? box.x + box.w - inkW : box.x;
|
|
36314
|
+
const y = anchor === "ctr" ? box.y + (box.h - inkH) / 2 : anchor === "b" ? box.y + box.h - inkH : box.y;
|
|
36315
|
+
return { x, y, w: inkW, h: inkH };
|
|
36316
|
+
}
|
|
35280
36317
|
function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
35281
36318
|
const findings = [];
|
|
35282
36319
|
const blocks = slideXml.match(/<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g) ?? [];
|
|
@@ -35300,16 +36337,23 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
35300
36337
|
worstOff = { by, edge };
|
|
35301
36338
|
}
|
|
35302
36339
|
}
|
|
35303
|
-
if (hasText && box.w > 0 && box.h > 0)
|
|
35304
|
-
|
|
36340
|
+
if (hasText && box.w > 0 && box.h > 0) {
|
|
36341
|
+
const ink = inkBox(b, box);
|
|
36342
|
+
if (ink)
|
|
36343
|
+
textBoxes.push(ink);
|
|
36344
|
+
}
|
|
35305
36345
|
}
|
|
36346
|
+
if (CHART_TEXT_RE.test(b))
|
|
36347
|
+
continue;
|
|
35306
36348
|
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
35307
36349
|
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
35308
36350
|
if (raw === void 0 || !unescXml(raw).trim())
|
|
35309
36351
|
continue;
|
|
36352
|
+
const text = unescXml(raw).trim();
|
|
35310
36353
|
const sz = /<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1];
|
|
35311
|
-
|
|
35312
|
-
|
|
36354
|
+
const floor = [...text].length <= LABEL_MAX_CHARS ? MIN_LABEL_FONT_HUNDREDTHS : MIN_FONT_HUNDREDTHS;
|
|
36355
|
+
if (sz && +sz < floor && (!smallest || +sz < smallest.sz)) {
|
|
36356
|
+
smallest = { sz: +sz, text, floor };
|
|
35313
36357
|
}
|
|
35314
36358
|
}
|
|
35315
36359
|
}
|
|
@@ -35325,7 +36369,7 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
35325
36369
|
findings.push({
|
|
35326
36370
|
slide: slideNumber,
|
|
35327
36371
|
code: "tiny-font",
|
|
35328
|
-
message: `text "${snippet}" is ${(smallest.sz / 100).toFixed(
|
|
36372
|
+
message: `text "${snippet}" is ${+(smallest.sz / 100).toFixed(1)}pt \u2014 below the ~${smallest.floor / 100}pt readable floor for ${smallest.floor < MIN_FONT_HUNDREDTHS ? "a short label" : "text"} on a projected slide; enlarge it or cut the text`
|
|
35329
36373
|
});
|
|
35330
36374
|
}
|
|
35331
36375
|
let worstPair;
|
|
@@ -35350,6 +36394,110 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
35350
36394
|
}
|
|
35351
36395
|
return findings;
|
|
35352
36396
|
}
|
|
36397
|
+
var BACKDROP_COVER = 0.98;
|
|
36398
|
+
var BACKDROP_FLOOR = 2;
|
|
36399
|
+
var BACKDROP_SAMPLES = 2500;
|
|
36400
|
+
var LIN = Array.from({ length: 256 }, (_, i) => {
|
|
36401
|
+
const c = i / 255;
|
|
36402
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
36403
|
+
});
|
|
36404
|
+
var lumRgb = (r, g, b) => 0.2126 * LIN[r] + 0.7152 * LIN[g] + 0.0722 * LIN[b];
|
|
36405
|
+
var ratioOf = (a, b) => (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
36406
|
+
var hex2 = (n) => n.toString(16).padStart(2, "0").toUpperCase();
|
|
36407
|
+
var BLOCK_RE = /<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g;
|
|
36408
|
+
function backdropPicEmbed(slideXml, canvas = DEFAULT_CANVAS) {
|
|
36409
|
+
const first = (slideXml.match(BLOCK_RE) ?? [])[0];
|
|
36410
|
+
if (!first || !first.startsWith("<p:pic"))
|
|
36411
|
+
return void 0;
|
|
36412
|
+
const box = firstBox(first);
|
|
36413
|
+
if (!box || box.x > canvas.w * (1 - BACKDROP_COVER) || box.y > canvas.h * (1 - BACKDROP_COVER))
|
|
36414
|
+
return void 0;
|
|
36415
|
+
if (box.w < canvas.w * BACKDROP_COVER || box.h < canvas.h * BACKDROP_COVER)
|
|
36416
|
+
return void 0;
|
|
36417
|
+
return /<a:blip\b[^>]*\br:embed="([^"]+)"/.exec(first)?.[1];
|
|
36418
|
+
}
|
|
36419
|
+
function lintBackdropContrast(slideXml, slideNumber, backdrop, canvas = DEFAULT_CANVAS) {
|
|
36420
|
+
const blocks = slideXml.match(BLOCK_RE) ?? [];
|
|
36421
|
+
const sx = backdrop.width / canvas.w;
|
|
36422
|
+
const sy = backdrop.height / canvas.h;
|
|
36423
|
+
const occluders = [];
|
|
36424
|
+
let worst;
|
|
36425
|
+
for (let bi = 1; bi < blocks.length; bi++) {
|
|
36426
|
+
const b = blocks[bi];
|
|
36427
|
+
const box = firstBox(b);
|
|
36428
|
+
if (!box)
|
|
36429
|
+
continue;
|
|
36430
|
+
const txIdx = b.indexOf("<p:txBody>");
|
|
36431
|
+
const spPr = txIdx >= 0 ? b.slice(0, txIdx) : b;
|
|
36432
|
+
const prst = /<a:prstGeom\b[^>]*\bprst="([^"]+)"/.exec(spPr)?.[1];
|
|
36433
|
+
const visual = !b.startsWith("<p:sp") || FILL_RE.test(spPr) || prst !== void 0 && prst !== "rect";
|
|
36434
|
+
const hasText = txIdx >= 0 && /<a:t\b[^>]*>\s*\S/.test(b);
|
|
36435
|
+
if (visual) {
|
|
36436
|
+
occluders.push(box);
|
|
36437
|
+
continue;
|
|
36438
|
+
}
|
|
36439
|
+
if (!hasText)
|
|
36440
|
+
continue;
|
|
36441
|
+
const ink = inkBox(b, box);
|
|
36442
|
+
if (!ink || ink.w <= 0 || ink.h <= 0)
|
|
36443
|
+
continue;
|
|
36444
|
+
if (occluders.some((o2) => overlapArea(o2, ink) > 0))
|
|
36445
|
+
continue;
|
|
36446
|
+
let fg;
|
|
36447
|
+
let alpha = 1;
|
|
36448
|
+
let text = "";
|
|
36449
|
+
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
36450
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
36451
|
+
if (raw === void 0 || !unescXml(raw).trim())
|
|
36452
|
+
continue;
|
|
36453
|
+
text += unescXml(raw);
|
|
36454
|
+
if (fg)
|
|
36455
|
+
continue;
|
|
36456
|
+
const rPr = run.slice(0, Math.max(0, run.indexOf("</a:rPr>")));
|
|
36457
|
+
fg = firstSrgb(rPr);
|
|
36458
|
+
const a = /<a:srgbClr\b[^>]*>\s*<a:alpha\b[^>]*\bval="(\d+)"/.exec(rPr)?.[1];
|
|
36459
|
+
if (fg && a)
|
|
36460
|
+
alpha = Math.min(1, +a / 1e5);
|
|
36461
|
+
}
|
|
36462
|
+
if (!fg)
|
|
36463
|
+
continue;
|
|
36464
|
+
const x0 = Math.max(0, Math.floor(ink.x * sx));
|
|
36465
|
+
const y0 = Math.max(0, Math.floor(ink.y * sy));
|
|
36466
|
+
const x1 = Math.min(backdrop.width, Math.ceil((ink.x + ink.w) * sx));
|
|
36467
|
+
const y1 = Math.min(backdrop.height, Math.ceil((ink.y + ink.h) * sy));
|
|
36468
|
+
if (x1 <= x0 || y1 <= y0)
|
|
36469
|
+
continue;
|
|
36470
|
+
const step = Math.max(1, Math.floor(Math.sqrt((x1 - x0) * (y1 - y0) / BACKDROP_SAMPLES)));
|
|
36471
|
+
const fr = parseInt(fg.slice(0, 2), 16), fgG = parseInt(fg.slice(2, 4), 16), fb = parseInt(fg.slice(4, 6), 16);
|
|
36472
|
+
const fgLum = lumRgb(fr, fgG, fb);
|
|
36473
|
+
const samples = [];
|
|
36474
|
+
for (let y = y0; y < y1; y += step) {
|
|
36475
|
+
for (let x = x0; x < x1; x += step) {
|
|
36476
|
+
const o2 = (y * backdrop.width + x) * 3;
|
|
36477
|
+
const r = backdrop.rgb[o2], g = backdrop.rgb[o2 + 1], bl = backdrop.rgb[o2 + 2];
|
|
36478
|
+
const l = lumRgb(r, g, bl);
|
|
36479
|
+
const tl = alpha >= 1 ? fgLum : lumRgb(Math.round(alpha * fr + (1 - alpha) * r), Math.round(alpha * fgG + (1 - alpha) * g), Math.round(alpha * fb + (1 - alpha) * bl));
|
|
36480
|
+
samples.push([ratioOf(tl, l), o2]);
|
|
36481
|
+
}
|
|
36482
|
+
}
|
|
36483
|
+
if (!samples.length)
|
|
36484
|
+
continue;
|
|
36485
|
+
samples.sort((p, q) => p[0] - q[0]);
|
|
36486
|
+
const [ratio, o] = samples[samples.length >> 1];
|
|
36487
|
+
if (ratio < BACKDROP_FLOOR && (!worst || ratio < worst.ratio)) {
|
|
36488
|
+
const bg = hex2(backdrop.rgb[o]) + hex2(backdrop.rgb[o + 1]) + hex2(backdrop.rgb[o + 2]);
|
|
36489
|
+
worst = { ratio, text: text.trim(), fg, bg };
|
|
36490
|
+
}
|
|
36491
|
+
}
|
|
36492
|
+
if (!worst)
|
|
36493
|
+
return [];
|
|
36494
|
+
const snippet = worst.text.length > 24 ? `${worst.text.slice(0, 24)}\u2026` : worst.text;
|
|
36495
|
+
return [{
|
|
36496
|
+
slide: slideNumber,
|
|
36497
|
+
code: "low-contrast-text",
|
|
36498
|
+
message: `text "${snippet}" is #${worst.fg} over the slide's rendered background (\u2248#${worst.bg} under it) \u2014 median contrast ${worst.ratio.toFixed(1)}:1, below the ${BACKDROP_FLOOR.toFixed(1)} floor \u2014 it can barely be seen; text over a photo or panel takes that surface's text colour (light on a dark photo/scrim), not the skin's page text colour`
|
|
36499
|
+
}];
|
|
36500
|
+
}
|
|
35353
36501
|
function canvasFromPresentation(xml) {
|
|
35354
36502
|
if (!xml)
|
|
35355
36503
|
return DEFAULT_CANVAS;
|
|
@@ -35357,6 +36505,26 @@ function canvasFromPresentation(xml) {
|
|
|
35357
36505
|
const cy = /<p:sldSz\b[^>]*\bcy="(\d+)"/.exec(xml)?.[1];
|
|
35358
36506
|
return cx && cy ? { w: +cx, h: +cy } : DEFAULT_CANVAS;
|
|
35359
36507
|
}
|
|
36508
|
+
async function backdropRaster(zip, slideXml, relsPath, canvas) {
|
|
36509
|
+
const rId = backdropPicEmbed(slideXml, canvas);
|
|
36510
|
+
const rels = rId ? zip.file(relsPath) : null;
|
|
36511
|
+
if (!rId || !rels)
|
|
36512
|
+
return void 0;
|
|
36513
|
+
const relsXml = await rels.async("string");
|
|
36514
|
+
const rel = [...relsXml.matchAll(/<Relationship\b[^>]*>/g)].map((m) => m[0]).find((r) => new RegExp(`\\bId="${rId}"`).test(r));
|
|
36515
|
+
const target = rel && /\bTarget="([^"]+)"/.exec(rel)?.[1];
|
|
36516
|
+
if (!target || !/\.png$/i.test(target))
|
|
36517
|
+
return void 0;
|
|
36518
|
+
const parts = ["ppt", "slides"];
|
|
36519
|
+
for (const seg of target.split("/")) {
|
|
36520
|
+
if (seg === "..")
|
|
36521
|
+
parts.pop();
|
|
36522
|
+
else if (seg && seg !== ".")
|
|
36523
|
+
parts.push(seg);
|
|
36524
|
+
}
|
|
36525
|
+
const media = zip.file(target.startsWith("/") ? target.slice(1) : parts.join("/"));
|
|
36526
|
+
return media ? decodePng(await media.async("uint8array")) : void 0;
|
|
36527
|
+
}
|
|
35360
36528
|
async function designLintPptx(bytes) {
|
|
35361
36529
|
const zip = await import_jszip7.default.loadAsync(Buffer.from(bytes));
|
|
35362
36530
|
const findings = [];
|
|
@@ -35367,9 +36535,15 @@ async function designLintPptx(bytes) {
|
|
|
35367
36535
|
const presFile = zip.file("ppt/presentation.xml");
|
|
35368
36536
|
const canvas = canvasFromPresentation(presFile ? await presFile.async("string") : void 0);
|
|
35369
36537
|
const lang = detectDeckLang();
|
|
35370
|
-
for (const { position, path } of await slidePartsInOrder(zip)) {
|
|
36538
|
+
for (const { position, path, relsPath } of await slidePartsInOrder(zip)) {
|
|
35371
36539
|
const xml = await zip.file(path).async("string");
|
|
35372
|
-
|
|
36540
|
+
const own = lintSlideXml(xml, position, masterBg, lang);
|
|
36541
|
+
findings.push(...own);
|
|
36542
|
+
if (!own.some((f) => f.code === "low-contrast-text")) {
|
|
36543
|
+
const backdrop = await backdropRaster(zip, xml, relsPath, canvas);
|
|
36544
|
+
if (backdrop)
|
|
36545
|
+
findings.push(...lintBackdropContrast(xml, position, backdrop, canvas));
|
|
36546
|
+
}
|
|
35373
36547
|
findings.push(...lintSlideGeometry(xml, position, canvas));
|
|
35374
36548
|
}
|
|
35375
36549
|
return { ok: findings.length === 0, findings };
|
|
@@ -35381,7 +36555,7 @@ async function checkPptx(bytes, options = {}) {
|
|
|
35381
36555
|
const report = { valid: validation.ok };
|
|
35382
36556
|
if (!validation.ok) report.errors = validation.errors;
|
|
35383
36557
|
if (options.strict && validation.ok) {
|
|
35384
|
-
const schemaDir = process.env.TALOS_XSD_DIR || (0,
|
|
36558
|
+
const schemaDir = process.env.TALOS_XSD_DIR || (0, import_node_path4.join)((0, import_node_path4.dirname)(process.argv[1]), "..", "schemas");
|
|
35385
36559
|
const strict = await validateOoxmlStrict(bytes, schemaDir);
|
|
35386
36560
|
report.strict = strict.ok ? { ok: true } : { ok: false, errors: strict.errors };
|
|
35387
36561
|
}
|
|
@@ -35389,6 +36563,24 @@ async function checkPptx(bytes, options = {}) {
|
|
|
35389
36563
|
return report;
|
|
35390
36564
|
}
|
|
35391
36565
|
|
|
36566
|
+
// apps/admin/data/skills.lib/document/bin-src/lib/user-styles.mjs
|
|
36567
|
+
var import_node_fs2 = require("node:fs");
|
|
36568
|
+
var import_node_path5 = require("node:path");
|
|
36569
|
+
var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
36570
|
+
function userStylesDir() {
|
|
36571
|
+
return process.env.TALOS_DOC_STYLES_DIR?.trim() || null;
|
|
36572
|
+
}
|
|
36573
|
+
function userSkinDir(name) {
|
|
36574
|
+
const root = userStylesDir();
|
|
36575
|
+
if (!root || !SAFE_NAME.test(name)) return null;
|
|
36576
|
+
const dir = (0, import_node_path5.join)(root, "styles", name);
|
|
36577
|
+
return (0, import_node_fs2.existsSync)((0, import_node_path5.join)(dir, "skin.css")) ? dir : null;
|
|
36578
|
+
}
|
|
36579
|
+
function userSkinLookup(name) {
|
|
36580
|
+
const root = userStylesDir();
|
|
36581
|
+
return root ? (0, import_node_path5.join)(root, "styles", name, "skin.css") : null;
|
|
36582
|
+
}
|
|
36583
|
+
|
|
35392
36584
|
// apps/admin/data/skills.lib/document/bin-src/html2pptx.mjs
|
|
35393
36585
|
function imageResolver(baseDir) {
|
|
35394
36586
|
return async (src) => {
|
|
@@ -35397,7 +36589,7 @@ function imageResolver(baseDir) {
|
|
|
35397
36589
|
if (!r || !r.ok) return null;
|
|
35398
36590
|
return { bytes: new Uint8Array(await r.arrayBuffer()), mime: (r.headers.get("content-type") || "").split(";")[0] || guessImageMime(src) };
|
|
35399
36591
|
}
|
|
35400
|
-
const p = (0,
|
|
36592
|
+
const p = (0, import_node_path6.isAbsolute)(src) ? src : (0, import_node_path6.join)(baseDir, src);
|
|
35401
36593
|
const buf = await (0, import_promises3.readFile)(p).catch(() => null);
|
|
35402
36594
|
return buf ? { bytes: new Uint8Array(buf), mime: guessImageMime(p) } : null;
|
|
35403
36595
|
};
|
|
@@ -35423,8 +36615,8 @@ function parsePreviewPages(spec) {
|
|
|
35423
36615
|
var _tailwindCss;
|
|
35424
36616
|
async function tailwindCss() {
|
|
35425
36617
|
if (_tailwindCss !== void 0) return _tailwindCss;
|
|
35426
|
-
const file = process.env.TALOS_TAILWIND_CSS || (0,
|
|
35427
|
-
_tailwindCss = (0,
|
|
36618
|
+
const file = process.env.TALOS_TAILWIND_CSS || (0, import_node_path6.join)((0, import_node_path6.dirname)(process.argv[1]), "..", "assets", "tailwind.min.css");
|
|
36619
|
+
_tailwindCss = (0, import_node_fs3.existsSync)(file) ? await (0, import_promises3.readFile)(file, "utf8").catch(() => "") : "";
|
|
35428
36620
|
return _tailwindCss;
|
|
35429
36621
|
}
|
|
35430
36622
|
async function withHeadlessPage(html, fn, density = DOCUMENT_CAPTURE_DENSITY) {
|
|
@@ -35439,6 +36631,8 @@ async function withHeadlessPage(html, fn, density = DOCUMENT_CAPTURE_DENSITY) {
|
|
|
35439
36631
|
await page.evaluate("document.fonts && document.fonts.ready").catch(() => {
|
|
35440
36632
|
});
|
|
35441
36633
|
const adapter = {
|
|
36634
|
+
// Part of the backdrop-cache salt: another Chromium may paint other pixels.
|
|
36635
|
+
browserVersion: browser.version(),
|
|
35442
36636
|
evaluate: (script) => page.evaluate(script),
|
|
35443
36637
|
screenshot: async (clip) => {
|
|
35444
36638
|
const png = await page.screenshot({ clip });
|
|
@@ -35495,13 +36689,25 @@ body{width:1280px;margin:0}
|
|
|
35495
36689
|
section.slide{position:relative;width:1280px;height:720px;overflow:hidden;
|
|
35496
36690
|
background:var(--slide-bg,#ffffff);color:var(--body-color,#1f2328);
|
|
35497
36691
|
font-family:var(--font-body,-apple-system,'Helvetica Neue','PingFang SC','Microsoft YaHei',sans-serif)}
|
|
35498
|
-
section.slide h1,section.slide h2,section.slide h3{
|
|
36692
|
+
section.slide h1,section.slide h2,section.slide h3{
|
|
35499
36693
|
font-family:var(--font-heading,var(--font-body,-apple-system,'PingFang SC',sans-serif))}
|
|
36694
|
+
/* Title colour from the theme vars \u2014 except text laid over a full-bleed photo
|
|
36695
|
+
(.overlay-title): that follows the on-image rule (the skin's .overlay-title, else
|
|
36696
|
+
_contract-defaults.css), never the slide's title colour. Without the exemption
|
|
36697
|
+
this rule out-specifies the bare class and a light skin's photo title came out
|
|
36698
|
+
dark-on-dark. :where() keeps the specificity at (0,1,2) so every other skin
|
|
36699
|
+
rule wins or loses exactly as before. Mirrored in scripts/lib/deck-compose.mjs. */
|
|
36700
|
+
section.slide :is(h1,h2,h3):where(:not(.overlay-title)){color:var(--title-color,#1a1a1a)}
|
|
35500
36701
|
/* Structural type scale (D2, mirrors Manus's [size=N]): a constrained 7-step
|
|
35501
36702
|
ladder so titles/body/captions land on-scale instead of ad-hoc font sizes.
|
|
35502
36703
|
Author with size="1".."7"; the walker measures the computed px \u2192 native pt. */
|
|
35503
36704
|
[size="1"]{font-size:12px}[size="2"]{font-size:14px}[size="3"]{font-size:16px}
|
|
35504
36705
|
[size="4"]{font-size:24px}[size="5"]{font-size:32px}[size="6"]{font-size:48px}[size="7"]{font-size:72px}
|
|
36706
|
+
/* A chart box with nothing to plot (markEmptyCharts): a light dashed frame + label \u2014
|
|
36707
|
+
and a contrast .panel around it (stats-chart) steps back, so the slide never shows
|
|
36708
|
+
a solid block of colour where the chart should be. */
|
|
36709
|
+
[data-chart-empty]{display:flex;align-items:center;justify-content:center;border:1px dashed var(--muted,#9aa0a6);border-radius:var(--card-radius,6px);color:var(--muted,#9aa0a6);font-size:16px;letter-spacing:.08em}
|
|
36710
|
+
.panel:has([data-chart-empty]){background:transparent!important;box-shadow:none!important;border-color:transparent!important}
|
|
35505
36711
|
`;
|
|
35506
36712
|
function injectHead(html, extra) {
|
|
35507
36713
|
if (html.includes("</head>")) return html.replace("</head>", `${extra}</head>`);
|
|
@@ -35549,6 +36755,9 @@ async function main() {
|
|
|
35549
36755
|
const mediaStyleArg = mediaIdx >= 0 ? args[mediaIdx + 1] : void 0;
|
|
35550
36756
|
const bdIdx = args.indexOf("--backdrop");
|
|
35551
36757
|
const bdVariant = bdIdx >= 0 ? args[bdIdx + 1] : void 0;
|
|
36758
|
+
const bdCacheIdx = args.indexOf("--backdrop-cache");
|
|
36759
|
+
const bdCacheDir = bdCacheIdx >= 0 ? args[bdCacheIdx + 1] : void 0;
|
|
36760
|
+
const bdCacheVerify = args.includes("--backdrop-cache-verify");
|
|
35552
36761
|
const lineIdx = args.indexOf("--line");
|
|
35553
36762
|
const lineArg = lineIdx >= 0 ? args[lineIdx + 1] : void 0;
|
|
35554
36763
|
const notesIdx = args.indexOf("--notes-doc");
|
|
@@ -35561,8 +36770,8 @@ async function main() {
|
|
|
35561
36770
|
const previewDensityIdx = args.indexOf("--preview-density");
|
|
35562
36771
|
const previewDensityArg = previewDensityIdx >= 0 ? args[previewDensityIdx + 1] : void 0;
|
|
35563
36772
|
const previewDensity = previewDensityArg === void 0 ? DOCUMENT_CAPTURE_DENSITY : Number(previewDensityArg);
|
|
35564
|
-
const consumed = /* @__PURE__ */ new Set([themeIdx, themeIdx >= 0 ? themeIdx + 1 : -1, styleIdx, styleIdx >= 0 ? styleIdx + 1 : -1, mediaIdx, mediaIdx >= 0 ? mediaIdx + 1 : -1, bdIdx, bdIdx >= 0 ? bdIdx + 1 : -1, lineIdx, lineIdx >= 0 ? lineIdx + 1 : -1, modeIdx, modeIdx >= 0 ? modeIdx + 1 : -1, notesIdx, notesIdx >= 0 ? notesIdx + 1 : -1, previewIdx, previewIdx >= 0 ? previewIdx + 1 : -1, previewPagesIdx, previewPagesIdx >= 0 ? previewPagesIdx + 1 : -1, previewDensityIdx, previewDensityIdx >= 0 ? previewDensityIdx + 1 : -1]);
|
|
35565
|
-
const positional = args.filter((a, i) => a !== "--rasterize" && a !== "--editable" && a !== "--theme-v2" && !consumed.has(i));
|
|
36773
|
+
const consumed = /* @__PURE__ */ new Set([themeIdx, themeIdx >= 0 ? themeIdx + 1 : -1, styleIdx, styleIdx >= 0 ? styleIdx + 1 : -1, mediaIdx, mediaIdx >= 0 ? mediaIdx + 1 : -1, bdIdx, bdIdx >= 0 ? bdIdx + 1 : -1, lineIdx, lineIdx >= 0 ? lineIdx + 1 : -1, modeIdx, modeIdx >= 0 ? modeIdx + 1 : -1, notesIdx, notesIdx >= 0 ? notesIdx + 1 : -1, previewIdx, previewIdx >= 0 ? previewIdx + 1 : -1, previewPagesIdx, previewPagesIdx >= 0 ? previewPagesIdx + 1 : -1, previewDensityIdx, previewDensityIdx >= 0 ? previewDensityIdx + 1 : -1, bdCacheIdx, bdCacheIdx >= 0 ? bdCacheIdx + 1 : -1]);
|
|
36774
|
+
const positional = args.filter((a, i) => a !== "--rasterize" && a !== "--editable" && a !== "--theme-v2" && a !== "--backdrop-cache-verify" && !consumed.has(i));
|
|
35566
36775
|
const [input, out] = positional;
|
|
35567
36776
|
if (!input || !out && !previewDir) die("usage: html2pptx <in.html|in.layout.json> [out.pptx|out.pdf] [--theme <theme.json>] [--style <name|dir>] [--theme-v2] [--media-style <id>] [--backdrop <variant>] [--line <name|none>] [--editable] [--rasterize] [--notes-doc <out.md>] [--preview-dir <dir>] [--preview-pages <all|N,N-M>] [--preview-density <1..4>]", 64);
|
|
35568
36777
|
if (previewPagesArg && !previewDir) die("--preview-pages requires --preview-dir", 64);
|
|
@@ -35586,6 +36795,8 @@ async function main() {
|
|
|
35586
36795
|
die(`not valid JSON: ${e.message}`, 65);
|
|
35587
36796
|
}
|
|
35588
36797
|
const { bytes: bytes2, warnings: warnings2 } = await convertLayoutToPptx(snapshot2).catch((e) => die(e.message, 65));
|
|
36798
|
+
const inert = [themePath && "--theme", styleArg && "--style", themeV2 && "--theme-v2", mediaStyleArg && "--media-style"].filter(Boolean);
|
|
36799
|
+
if (inert.length) warnings2.unshift(`${inert.join(" / ")} ignored: a .layout.json snapshot is already styled \u2014 pass them when rendering the HTML instead`);
|
|
35589
36800
|
for (const w of [...snapshot2.notes ?? [], ...warnings2]) process.stderr.write(`html2pptx: ${w}
|
|
35590
36801
|
`);
|
|
35591
36802
|
await (0, import_promises3.writeFile)(out, Buffer.from(bytes2));
|
|
@@ -35616,40 +36827,42 @@ async function main() {
|
|
|
35616
36827
|
themeCss = themeCssVars(theme);
|
|
35617
36828
|
}
|
|
35618
36829
|
let skinCss = "", backdropHtml = "", skinAccent = "", skinAxis = "", skinChart1 = "";
|
|
36830
|
+
let backdropVariant;
|
|
35619
36831
|
if (styleArg) {
|
|
35620
|
-
const skillDir = (0,
|
|
36832
|
+
const skillDir = (0, import_node_path6.dirname)((0, import_node_path6.dirname)(process.argv[1] || ""));
|
|
35621
36833
|
let styleDir, cssName = "skin.css", entry = null, catalog = null;
|
|
35622
|
-
if ((0,
|
|
36834
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path6.join)(styleArg, "skin.css"))) {
|
|
35623
36835
|
styleDir = styleArg;
|
|
35624
|
-
} else if ((0,
|
|
36836
|
+
} else if ((0, import_node_fs3.existsSync)((0, import_node_path6.join)(styleArg, "pack.css"))) {
|
|
35625
36837
|
styleDir = styleArg;
|
|
35626
36838
|
cssName = "pack.css";
|
|
35627
36839
|
} else {
|
|
35628
|
-
const catalogPath = (0,
|
|
35629
|
-
if ((0,
|
|
36840
|
+
const catalogPath = (0, import_node_path6.join)(skillDir, "catalog.json");
|
|
36841
|
+
if ((0, import_node_fs3.existsSync)(catalogPath)) {
|
|
35630
36842
|
try {
|
|
35631
|
-
({ catalog } = parseCatalog(JSON.parse((0,
|
|
36843
|
+
({ catalog } = parseCatalog(JSON.parse((0, import_node_fs3.readFileSync)(catalogPath, "utf8"))));
|
|
35632
36844
|
if (catalog) entry = resolveEntry(catalog, styleArg);
|
|
35633
36845
|
} catch {
|
|
35634
36846
|
}
|
|
35635
36847
|
}
|
|
35636
|
-
styleDir = entry ? (0,
|
|
36848
|
+
styleDir = entry ? (0, import_node_path6.join)(skillDir, assetDirOf(entry)) : userSkinDir(styleArg) ?? (0, import_node_path6.join)(skillDir, "styles", styleArg);
|
|
35637
36849
|
if (entry) cssName = cssFileOf(entry);
|
|
35638
36850
|
}
|
|
35639
|
-
const skinPath = (0,
|
|
36851
|
+
const skinPath = (0, import_node_path6.join)(styleDir, cssName);
|
|
35640
36852
|
try {
|
|
35641
36853
|
skinCss = await (0, import_promises3.readFile)(skinPath, "utf8");
|
|
35642
36854
|
} catch (e) {
|
|
35643
|
-
|
|
36855
|
+
const user = userSkinLookup(styleArg);
|
|
36856
|
+
die(`cannot read style ${skinPath}: ${e.message}${user && !(0, import_node_path6.isAbsolute)(styleArg) ? ` (not a catalog id, and no user style at ${user})` : ""}`, 66);
|
|
35644
36857
|
}
|
|
35645
|
-
const defaultsPath = (0,
|
|
36858
|
+
const defaultsPath = (0, import_node_path6.join)(skillDir, "styles", "_contract-defaults.css");
|
|
35646
36859
|
let v2Css = "";
|
|
35647
36860
|
if (themeV2) {
|
|
35648
36861
|
if (!entry) die("--theme-v2 requires a catalog style id (explicit style directories are V1 only)", 64);
|
|
35649
36862
|
if (catalog && renderModeOf(catalog, entry.id) === "classic-v1") {
|
|
35650
36863
|
die(`style "${entry.id}" is frozen in collection "original-13" as classic-v1; omit --theme-v2 and --media-style`, 64);
|
|
35651
36864
|
}
|
|
35652
|
-
const registryPath = (0,
|
|
36865
|
+
const registryPath = (0, import_node_path6.join)(skillDir, "theme-system.json");
|
|
35653
36866
|
let parsed;
|
|
35654
36867
|
try {
|
|
35655
36868
|
parsed = parseThemeSystem(JSON.parse(await (0, import_promises3.readFile)(registryPath, "utf8")));
|
|
@@ -35659,45 +36872,51 @@ async function main() {
|
|
|
35659
36872
|
if (!parsed.system) die(`invalid Theme V2 registry: ${parsed.errors.join("; ")}`, 66);
|
|
35660
36873
|
const layers = resolveThemeLayers(parsed.system, entry.id, mediaStyleArg);
|
|
35661
36874
|
if (!layers) die(`Theme V2 cannot resolve style "${entry.id}"${mediaStyleArg ? ` with media style "${mediaStyleArg}"` : ""}`, 66);
|
|
35662
|
-
for (const rel of layers.paths) v2Css += await (0, import_promises3.readFile)((0,
|
|
36875
|
+
for (const rel of layers.paths) v2Css += await (0, import_promises3.readFile)((0, import_node_path6.join)(skillDir, rel), "utf8") + "\n";
|
|
35663
36876
|
process.stderr.write(`html2pptx: Theme V2 ${entry.id} = components:${layers.componentProfile} + media:${layers.mediaStyle} + art:${layers.artDirection}
|
|
35664
36877
|
`);
|
|
35665
36878
|
}
|
|
35666
|
-
skinCss = ((0,
|
|
35667
|
-
const sharedBackdropsDir = (0,
|
|
36879
|
+
skinCss = ((0, import_node_fs3.existsSync)(defaultsPath) ? await (0, import_promises3.readFile)(defaultsPath, "utf8") + "\n" : "") + v2Css + skinCss;
|
|
36880
|
+
const sharedBackdropsDir = (0, import_node_path6.join)(skillDir, "styles", "_backdrops");
|
|
35668
36881
|
const NO_AUTO_BACKDROP_VARIETY = /* @__PURE__ */ new Set(["mckinsey", "mono", "cinereel"]);
|
|
35669
36882
|
let bdPath;
|
|
35670
|
-
if (bdVariant) {
|
|
35671
|
-
|
|
35672
|
-
|
|
35673
|
-
|
|
35674
|
-
|
|
36883
|
+
if (bdVariant === "default") {
|
|
36884
|
+
bdPath = (0, import_node_path6.join)(styleDir, "backdrop.html");
|
|
36885
|
+
} else if (bdVariant) {
|
|
36886
|
+
const ownVariantPath = (0, import_node_path6.join)(styleDir, `backdrop-${bdVariant}.html`);
|
|
36887
|
+
const sharedVariantPath = (0, import_node_path6.join)(sharedBackdropsDir, `${bdVariant}.html`);
|
|
36888
|
+
if ((0, import_node_fs3.existsSync)(ownVariantPath)) bdPath = ownVariantPath;
|
|
36889
|
+
else if ((0, import_node_fs3.existsSync)(sharedVariantPath)) bdPath = sharedVariantPath;
|
|
35675
36890
|
else {
|
|
35676
36891
|
process.stderr.write(`html2pptx: backdrop variant "${bdVariant}" not found for ${styleArg}, using default backdrop.html
|
|
35677
36892
|
`);
|
|
35678
|
-
bdPath = (0,
|
|
36893
|
+
bdPath = (0, import_node_path6.join)(styleDir, "backdrop.html");
|
|
35679
36894
|
}
|
|
35680
|
-
} else if (themeV2 || NO_AUTO_BACKDROP_VARIETY.has((0,
|
|
35681
|
-
bdPath = (0,
|
|
36895
|
+
} else if (themeV2 || NO_AUTO_BACKDROP_VARIETY.has((0, import_node_path6.basename)(styleDir))) {
|
|
36896
|
+
bdPath = (0, import_node_path6.join)(styleDir, "backdrop.html");
|
|
35682
36897
|
} else {
|
|
35683
|
-
const ownVariants = (0,
|
|
35684
|
-
const sharedVariants = (0,
|
|
36898
|
+
const ownVariants = (0, import_node_fs3.existsSync)(styleDir) ? (0, import_node_fs3.readdirSync)(styleDir).filter((f) => /^backdrop.*\.html$/.test(f)).map((f) => (0, import_node_path6.join)(styleDir, f)) : [];
|
|
36899
|
+
const sharedVariants = (0, import_node_fs3.existsSync)(sharedBackdropsDir) ? (0, import_node_fs3.readdirSync)(sharedBackdropsDir).filter((f) => f.endsWith(".html")).map((f) => (0, import_node_path6.join)(sharedBackdropsDir, f)) : [];
|
|
35685
36900
|
const pool = [...ownVariants, ...sharedVariants].sort();
|
|
35686
36901
|
if (pool.length > 0) {
|
|
35687
36902
|
let h = 0;
|
|
35688
36903
|
for (let i = 0; i < raw.length; i++) h = h * 31 + raw.charCodeAt(i) >>> 0;
|
|
35689
36904
|
bdPath = pool[h % pool.length];
|
|
35690
36905
|
} else {
|
|
35691
|
-
bdPath = (0,
|
|
36906
|
+
bdPath = (0, import_node_path6.join)(styleDir, "backdrop.html");
|
|
35692
36907
|
}
|
|
35693
36908
|
}
|
|
35694
|
-
if ((0,
|
|
36909
|
+
if ((0, import_node_fs3.existsSync)(bdPath)) {
|
|
36910
|
+
backdropHtml = await (0, import_promises3.readFile)(bdPath, "utf8");
|
|
36911
|
+
const name = (0, import_node_path6.basename)(bdPath, ".html");
|
|
36912
|
+
backdropVariant = name === "backdrop" ? "default" : name.replace(/^backdrop-/, "");
|
|
36913
|
+
}
|
|
35695
36914
|
const stripLine = (h) => h.replace(/<div class="bd-arc"[\s\S]*?<\/svg><\/div>/g, "");
|
|
35696
36915
|
if (lineArg === "none") {
|
|
35697
36916
|
backdropHtml = stripLine(backdropHtml);
|
|
35698
36917
|
} else if (lineArg) {
|
|
35699
|
-
const linePath = (0,
|
|
35700
|
-
if ((0,
|
|
36918
|
+
const linePath = (0, import_node_path6.join)(skillDir, "styles", "_lines", `${lineArg}.html`);
|
|
36919
|
+
if ((0, import_node_fs3.existsSync)(linePath)) backdropHtml = stripLine(backdropHtml) + await (0, import_promises3.readFile)(linePath, "utf8");
|
|
35701
36920
|
else process.stderr.write(`html2pptx: line motif "${lineArg}" not found in styles/_lines, keeping skin default line
|
|
35702
36921
|
`);
|
|
35703
36922
|
}
|
|
@@ -35705,22 +36924,37 @@ async function main() {
|
|
|
35705
36924
|
skinAxis = (skinCss.match(/--muted\s*:\s*([^;}]+)/) || [])[1]?.trim() || "";
|
|
35706
36925
|
skinChart1 = (skinCss.match(/--chart-1\s*:\s*([^;}]+)/) || [])[1]?.trim() || skinAccent;
|
|
35707
36926
|
}
|
|
35708
|
-
|
|
36927
|
+
if (themePath && styleArg) {
|
|
36928
|
+
const w = `--theme ${(0, import_node_path6.basename)(themePath)} has no effect on colours/fonts: --style ${styleArg}${themeV2 ? " (Theme V2)" : ""} redefines the same tokens and overrides it. To recolour a --style deck, save the theme's palette as a user skin ($TALOS_DOC_STYLES_DIR/styles/<name>/skin.css \u2014 deck-design.md "Custom skins") and pass --style <name> without --theme`;
|
|
36929
|
+
process.stderr.write(`html2pptx: ${w}
|
|
36930
|
+
`);
|
|
36931
|
+
colorWarnings.push(w);
|
|
36932
|
+
}
|
|
36933
|
+
let { html: rawInlined, inlined, skipped } = await inlineHtmlImages(raw, imageResolver((0, import_node_path6.dirname)(input)));
|
|
35709
36934
|
if (inlined) process.stderr.write(`html2pptx: inlined ${inlined} image(s) as data: URIs
|
|
35710
36935
|
`);
|
|
35711
36936
|
for (const s of skipped) process.stderr.write(`html2pptx: <img src="${s}"> not found/unfetchable \u2014 the render will drop it
|
|
35712
36937
|
`);
|
|
35713
36938
|
if (styleArg) {
|
|
35714
|
-
const artLayer = themeV2 ? '<div class="theme-art-layer" aria-hidden="true"><i class="art-shape art-a"></i><i class="art-shape art-b"></i><i class="art-shape art-c"></i><i class="art-pattern"></i></div>' : "";
|
|
36939
|
+
const artLayer = themeV2 ? '<div class="theme-art-layer" aria-hidden="true"><i class="art-slot art-slot-a"><i class="art-shape art-a"></i></i><i class="art-slot art-slot-b"><i class="art-shape art-b"></i></i><i class="art-slot art-slot-c"><i class="art-shape art-c"></i></i><i class="art-slot art-slot-pattern"><i class="art-pattern"></i></i></div>' : "";
|
|
35715
36940
|
if (backdropHtml || artLayer) rawInlined = rawInlined.replace(/<div class="slide-backdrop">\s*<\/div>/g, `<div class="slide-backdrop">${backdropHtml}${artLayer}</div>`);
|
|
35716
36941
|
if (skinAccent) rawInlined = rawInlined.replaceAll("__ACCENT__", skinAccent);
|
|
35717
36942
|
if (skinAxis) rawInlined = rawInlined.replaceAll("__AXIS__", skinAxis);
|
|
35718
36943
|
if (skinChart1) rawInlined = rawInlined.replaceAll("__CHART1__", skinChart1);
|
|
35719
36944
|
}
|
|
36945
|
+
const charts = markEmptyCharts(rawInlined);
|
|
36946
|
+
rawInlined = charts.html;
|
|
36947
|
+
charts.empty.forEach((why, i) => {
|
|
36948
|
+
const w = `empty chart ${i + 1} of ${charts.empty.length}: ${why} \u2014 drawn as a dashed "\u56FE\u8868\u5360\u4F4D" placeholder; give it {type, labels, series:[{values}]}`;
|
|
36949
|
+
process.stderr.write(`html2pptx: ${w}
|
|
36950
|
+
`);
|
|
36951
|
+
colorWarnings.push(w);
|
|
36952
|
+
});
|
|
35720
36953
|
const tw = await tailwindCss();
|
|
35721
|
-
const cjk = !hasService &&
|
|
36954
|
+
const cjk = !hasService && CJK_RE.test(rawInlined) ? await cjkFontFaceCss(rawInlined + skinCss + themeCss + SKELETON_CSS) : "";
|
|
35722
36955
|
const latin = await latinFontFaceCss(rawInlined + skinCss);
|
|
35723
36956
|
const fontCss = cjk + latin;
|
|
36957
|
+
const backdropSalt = (0, import_node_crypto2.createHash)("sha256").update(String(DOCUMENT_CAPTURE_DENSITY)).update("\0").update(fontCss).update("\0").update(themeCss).update("\0").update(SKELETON_CSS).update("\0").update(skinCss).digest("hex");
|
|
35724
36958
|
const html = injectHead(rawInlined, `${fontCss ? `<style>${fontCss}</style>` : ""}${tw ? `<style>${tw}</style>` : ""}<style>${themeCss}${SKELETON_CSS}${skinCss}</style>`);
|
|
35725
36959
|
if (previewDir) {
|
|
35726
36960
|
let images;
|
|
@@ -35736,15 +36970,15 @@ async function main() {
|
|
|
35736
36970
|
const pageNumbers = previewIndexes ? previewIndexes.map((i) => i + 1) : images.map((_, i) => i + 1);
|
|
35737
36971
|
const files = [];
|
|
35738
36972
|
for (let i = 0; i < images.length; i++) {
|
|
35739
|
-
const path = (0,
|
|
36973
|
+
const path = (0, import_node_path6.join)(previewDir, `slide-${String(pageNumbers[i]).padStart(3, "0")}.png`);
|
|
35740
36974
|
await (0, import_promises3.writeFile)(path, Buffer.from(images[i], "base64"));
|
|
35741
36975
|
files.push(path);
|
|
35742
36976
|
}
|
|
35743
|
-
process.stdout.write(JSON.stringify({ ok: true, engine: hasService ? "preview" : "preview (headless)", slides: images.length, pages: pageNumbers, density: previewDensity, files }, null, 1) + "\n");
|
|
36977
|
+
process.stdout.write(JSON.stringify({ ok: true, engine: hasService ? "preview" : "preview (headless)", slides: images.length, pages: pageNumbers, density: previewDensity, files, warnings: colorWarnings }, null, 1) + "\n");
|
|
35744
36978
|
return;
|
|
35745
36979
|
}
|
|
35746
36980
|
if (pdfOut) {
|
|
35747
|
-
const printHtml = injectHead(html, `<style>${PRINT_CSS}</style>`);
|
|
36981
|
+
const printHtml = injectHead(withoutPrintNoise(html), `<style>${PRINT_CSS}</style>`);
|
|
35748
36982
|
let pdf;
|
|
35749
36983
|
if (hasService) pdf = await printService(printHtml).catch((e) => die(e.message, 1));
|
|
35750
36984
|
else {
|
|
@@ -35771,13 +37005,17 @@ async function main() {
|
|
|
35771
37005
|
const bytes2 = await imagesToPptx(images).catch((e) => die(e.message, 1));
|
|
35772
37006
|
await (0, import_promises3.writeFile)(out, Buffer.from(bytes2));
|
|
35773
37007
|
process.stderr.write("html2pptx: --rasterize output is image slides \u2014 pixel-perfect but NOT editable/searchable text\n");
|
|
35774
|
-
await reportDeck({ ok: true, out, engine: hasService ? "rasterize" : "rasterize (headless)", slides: images.length }, bytes2);
|
|
37008
|
+
await reportDeck({ ok: true, out, engine: hasService ? "rasterize" : "rasterize (headless)", slides: images.length, warnings: colorWarnings }, bytes2);
|
|
35775
37009
|
return;
|
|
35776
37010
|
}
|
|
35777
37011
|
let snapshot = null;
|
|
35778
37012
|
let engine = "layout";
|
|
35779
37013
|
const suffix = hasService ? "" : " (headless)";
|
|
35780
|
-
const getLayout = (backdrop) => hasService ? callService("/layout", html, backdrop ? { backdrop: true } : {}) : withHeadlessPage(html, (p) => snapshotHeadless(p, {
|
|
37014
|
+
const getLayout = (backdrop) => hasService ? callService("/layout", html, backdrop ? { backdrop: true } : {}) : withHeadlessPage(html, (p) => snapshotHeadless(p, {
|
|
37015
|
+
backdrop,
|
|
37016
|
+
// Hybrid caches its backdrops; --editable its decoration-layer pictures (own key space).
|
|
37017
|
+
...bdCacheDir ? { cache: fsBackdropCache(bdCacheDir, `${backdropSalt}\0${p.browserVersion}`, bdCacheVerify) } : {}
|
|
37018
|
+
}));
|
|
35781
37019
|
if (hybrid) {
|
|
35782
37020
|
try {
|
|
35783
37021
|
snapshot = await getLayout(true);
|
|
@@ -35793,13 +37031,26 @@ async function main() {
|
|
|
35793
37031
|
if (engine === "layout") engine = "layout" + suffix;
|
|
35794
37032
|
const { bytes, warnings } = await convertLayoutToPptx(snapshot).catch((e) => die(e.message, 65));
|
|
35795
37033
|
const allWarnings = [...colorWarnings, ...snapshot.notes ?? [], ...warnings];
|
|
35796
|
-
for (const w of allWarnings) process.stderr.write(`html2pptx: ${w}
|
|
37034
|
+
for (const w of allWarnings.slice(colorWarnings.length)) process.stderr.write(`html2pptx: ${w}
|
|
35797
37035
|
`);
|
|
35798
37036
|
await (0, import_promises3.writeFile)(out, Buffer.from(bytes));
|
|
35799
37037
|
if (notesDocPath) await writeNotesDoc(notesDocPath, snapshot.slides).catch((e) => process.stderr.write(`html2pptx: notes-doc: ${e.message}
|
|
35800
37038
|
`));
|
|
35801
37039
|
if (engine.startsWith("hybrid")) process.stderr.write("html2pptx: \u9ED8\u8BA4 hybrid \u2014 \u6BCF\u9875\u89C6\u89C9\u4E3A\u9AD8\u4FDD\u771F\u6805\u683C\u5E95\u56FE,\u6587\u5B57\u4FDD\u6301\u539F\u751F\u53EF\u7F16\u8F91;\u9700\u7EAF\u53EF\u7F16\u8F91\u7528 --editable\n");
|
|
35802
|
-
await reportDeck({ ok: true, out, engine, slides: snapshot.slides?.length ?? 0, ...notesDocPath ? { notesDoc: notesDocPath } : {}, warnings: allWarnings }, bytes);
|
|
37040
|
+
await reportDeck({ ok: true, out, engine, slides: snapshot.slides?.length ?? 0, ...backdropVariant ? { backdrop: backdropVariant } : {}, ...notesDocPath ? { notesDoc: notesDocPath } : {}, warnings: allWarnings }, bytes);
|
|
37041
|
+
}
|
|
37042
|
+
function fsBackdropCache(dir, salt, verify) {
|
|
37043
|
+
return {
|
|
37044
|
+
salt,
|
|
37045
|
+
verify,
|
|
37046
|
+
get: (key) => (0, import_promises3.readFile)((0, import_node_path6.join)(dir, `${key}.png`)).then((b) => new Uint8Array(b)).catch(() => void 0),
|
|
37047
|
+
set: async (key, png) => {
|
|
37048
|
+
await (0, import_promises3.mkdir)(dir, { recursive: true });
|
|
37049
|
+
const tmp = (0, import_node_path6.join)(dir, `${key}.${process.pid}.tmp`);
|
|
37050
|
+
await (0, import_promises3.writeFile)(tmp, png);
|
|
37051
|
+
await (0, import_promises3.rename)(tmp, (0, import_node_path6.join)(dir, `${key}.png`));
|
|
37052
|
+
}
|
|
37053
|
+
};
|
|
35803
37054
|
}
|
|
35804
37055
|
async function reportDeck(result, bytes) {
|
|
35805
37056
|
const check = await checkPptx(bytes);
|