@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
|
@@ -315,7 +315,7 @@ var require_BufferList = __commonJS({
|
|
|
315
315
|
this.head = this.tail = null;
|
|
316
316
|
this.length = 0;
|
|
317
317
|
};
|
|
318
|
-
BufferList.prototype.join = function
|
|
318
|
+
BufferList.prototype.join = function join4(s) {
|
|
319
319
|
if (this.length === 0) return "";
|
|
320
320
|
var p = this.head;
|
|
321
321
|
var ret = "" + p.data;
|
|
@@ -18354,14 +18354,14 @@ var require_decode = __commonJS({
|
|
|
18354
18354
|
var LZWStream_1 = tslib_1.__importDefault(require_LZWStream());
|
|
18355
18355
|
var RunLengthStream_1 = tslib_1.__importDefault(require_RunLengthStream());
|
|
18356
18356
|
var Stream_1 = tslib_1.__importDefault(require_Stream());
|
|
18357
|
-
var decodeStream = function(stream, encoding,
|
|
18357
|
+
var decodeStream = function(stream, encoding, params2) {
|
|
18358
18358
|
if (encoding === PDFName_1.default.of("FlateDecode")) {
|
|
18359
18359
|
return new FlateStream_1.default(stream);
|
|
18360
18360
|
}
|
|
18361
18361
|
if (encoding === PDFName_1.default.of("LZWDecode")) {
|
|
18362
18362
|
var earlyChange = 1;
|
|
18363
|
-
if (
|
|
18364
|
-
var EarlyChange =
|
|
18363
|
+
if (params2 instanceof PDFDict_1.default) {
|
|
18364
|
+
var EarlyChange = params2.lookup(PDFName_1.default.of("EarlyChange"));
|
|
18365
18365
|
if (EarlyChange instanceof PDFNumber_1.default) {
|
|
18366
18366
|
earlyChange = EarlyChange.asNumber();
|
|
18367
18367
|
}
|
|
@@ -22243,11 +22243,11 @@ var require_svgPath = __commonJS({
|
|
|
22243
22243
|
var args = [];
|
|
22244
22244
|
var curArg = "";
|
|
22245
22245
|
var foundDecimal = false;
|
|
22246
|
-
var
|
|
22246
|
+
var params2 = 0;
|
|
22247
22247
|
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
|
|
22248
22248
|
var c = path_1[_i];
|
|
22249
22249
|
if (parameters.has(c)) {
|
|
22250
|
-
|
|
22250
|
+
params2 = parameters.get(c);
|
|
22251
22251
|
if (cmd) {
|
|
22252
22252
|
if (curArg.length > 0) {
|
|
22253
22253
|
args[args.length] = +curArg;
|
|
@@ -22262,7 +22262,7 @@ var require_svgPath = __commonJS({
|
|
|
22262
22262
|
if (curArg.length === 0) {
|
|
22263
22263
|
continue;
|
|
22264
22264
|
}
|
|
22265
|
-
if (args.length ===
|
|
22265
|
+
if (args.length === params2) {
|
|
22266
22266
|
ret[ret.length] = { cmd, args };
|
|
22267
22267
|
args = [+curArg];
|
|
22268
22268
|
if (cmd === "M") {
|
|
@@ -22284,7 +22284,7 @@ var require_svgPath = __commonJS({
|
|
|
22284
22284
|
}
|
|
22285
22285
|
}
|
|
22286
22286
|
if (curArg.length > 0) {
|
|
22287
|
-
if (args.length ===
|
|
22287
|
+
if (args.length === params2) {
|
|
22288
22288
|
ret[ret.length] = { cmd, args };
|
|
22289
22289
|
args = [+curArg];
|
|
22290
22290
|
if (cmd === "M") {
|
|
@@ -27581,9 +27581,396 @@ var require_cjs = __commonJS({
|
|
|
27581
27581
|
}
|
|
27582
27582
|
});
|
|
27583
27583
|
|
|
27584
|
+
// packages/core/dist/document/pdf-encrypted.js
|
|
27585
|
+
var ENCRYPTED_PDF_MESSAGE, PdfEncryptedError;
|
|
27586
|
+
var init_pdf_encrypted = __esm({
|
|
27587
|
+
"packages/core/dist/document/pdf-encrypted.js"() {
|
|
27588
|
+
"use strict";
|
|
27589
|
+
ENCRYPTED_PDF_MESSAGE = "PDF is password-protected (encrypted); decrypt first: pdf-ops decrypt <in> <password> <out>";
|
|
27590
|
+
PdfEncryptedError = class extends Error {
|
|
27591
|
+
constructor() {
|
|
27592
|
+
super(ENCRYPTED_PDF_MESSAGE);
|
|
27593
|
+
this.name = "PdfEncryptedError";
|
|
27594
|
+
}
|
|
27595
|
+
};
|
|
27596
|
+
}
|
|
27597
|
+
});
|
|
27598
|
+
|
|
27599
|
+
// packages/core/dist/document/pdf-extract.js
|
|
27600
|
+
var pdf_extract_exports = {};
|
|
27601
|
+
__export(pdf_extract_exports, {
|
|
27602
|
+
analyzePdfDocument: () => analyzePdfDocument,
|
|
27603
|
+
countVectorOperators: () => countVectorOperators,
|
|
27604
|
+
decryptPdf: () => decryptPdf,
|
|
27605
|
+
encryptPdf: () => encryptPdf,
|
|
27606
|
+
extractPdfImages: () => extractPdfImages,
|
|
27607
|
+
extractPdfTables: () => extractPdfTables,
|
|
27608
|
+
extractPdfText: () => extractPdfText,
|
|
27609
|
+
extractPdfTextLines: () => extractPdfTextLines,
|
|
27610
|
+
pdfNeedsPassword: () => pdfNeedsPassword,
|
|
27611
|
+
renderPdfPages: () => renderPdfPages,
|
|
27612
|
+
scanPdfSignals: () => scanPdfSignals,
|
|
27613
|
+
textMathHint: () => textMathHint,
|
|
27614
|
+
textTableHint: () => textTableHint
|
|
27615
|
+
});
|
|
27616
|
+
function loadMupdf() {
|
|
27617
|
+
if (!_mupdf) {
|
|
27618
|
+
const deps = process.env.TALOS_SKILL_DEPS;
|
|
27619
|
+
const spec = deps ? (0, import_node_url.pathToFileURL)((0, import_node_path.join)(deps, "mupdf", "dist", "mupdf.js")).href : "mupdf";
|
|
27620
|
+
_mupdf = import(spec);
|
|
27621
|
+
}
|
|
27622
|
+
return _mupdf;
|
|
27623
|
+
}
|
|
27624
|
+
function textMathHint(text) {
|
|
27625
|
+
if (!text)
|
|
27626
|
+
return false;
|
|
27627
|
+
const hits = (text.match(MATH_CHARS) || []).length;
|
|
27628
|
+
return hits >= 3 || hits / Math.max(text.length, 1) > 0.02;
|
|
27629
|
+
}
|
|
27630
|
+
function textTableHint(text) {
|
|
27631
|
+
if (!text)
|
|
27632
|
+
return false;
|
|
27633
|
+
const gaps = (text.match(/ {2,}|\t/g) || []).length;
|
|
27634
|
+
return gaps >= 6;
|
|
27635
|
+
}
|
|
27636
|
+
async function openDoc(bytes) {
|
|
27637
|
+
const mupdf = await loadMupdf();
|
|
27638
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27639
|
+
if (doc.needsPassword())
|
|
27640
|
+
throw new PdfEncryptedError();
|
|
27641
|
+
if (doc.countPages() === 0) {
|
|
27642
|
+
throw new Error("PDF has no readable pages (corrupt, encrypted, or empty)");
|
|
27643
|
+
}
|
|
27644
|
+
return { mupdf, doc };
|
|
27645
|
+
}
|
|
27646
|
+
async function analyzePdfDocument(bytes, options = {}) {
|
|
27647
|
+
const wantText = options.text !== false;
|
|
27648
|
+
const wantSignals = options.signals === true;
|
|
27649
|
+
const { doc } = await openDoc(bytes);
|
|
27650
|
+
const pageCount = doc.countPages();
|
|
27651
|
+
const signals = [];
|
|
27652
|
+
let out = "";
|
|
27653
|
+
for (let i = 0; i < pageCount; i++) {
|
|
27654
|
+
const page = doc.loadPage(i);
|
|
27655
|
+
if (wantText) {
|
|
27656
|
+
out += `
|
|
27657
|
+
[page ${i + 1}]
|
|
27658
|
+
${page.toStructuredText().asText()}`;
|
|
27659
|
+
}
|
|
27660
|
+
if (wantSignals) {
|
|
27661
|
+
signals.push(pageSignalsFrom(page, i + 1));
|
|
27662
|
+
}
|
|
27663
|
+
}
|
|
27664
|
+
return { text: wantText ? out.trim() : "", pageCount, signals };
|
|
27665
|
+
}
|
|
27666
|
+
async function extractPdfText(bytes) {
|
|
27667
|
+
const { text, pageCount } = await analyzePdfDocument(bytes, { text: true });
|
|
27668
|
+
return { text, pageCount };
|
|
27669
|
+
}
|
|
27670
|
+
function countVectorOperators(content) {
|
|
27671
|
+
let ops = 0;
|
|
27672
|
+
let curves = 0;
|
|
27673
|
+
let fills = 0;
|
|
27674
|
+
let i = 0;
|
|
27675
|
+
const n = content.length;
|
|
27676
|
+
while (i < n) {
|
|
27677
|
+
const ch = content[i];
|
|
27678
|
+
if (ch === "(") {
|
|
27679
|
+
let depth = 1;
|
|
27680
|
+
i++;
|
|
27681
|
+
while (i < n && depth > 0) {
|
|
27682
|
+
if (content[i] === "\\")
|
|
27683
|
+
i++;
|
|
27684
|
+
else if (content[i] === "(")
|
|
27685
|
+
depth++;
|
|
27686
|
+
else if (content[i] === ")")
|
|
27687
|
+
depth--;
|
|
27688
|
+
i++;
|
|
27689
|
+
}
|
|
27690
|
+
continue;
|
|
27691
|
+
}
|
|
27692
|
+
if (ch === "<" && content[i + 1] !== "<") {
|
|
27693
|
+
while (i < n && content[i] !== ">")
|
|
27694
|
+
i++;
|
|
27695
|
+
i++;
|
|
27696
|
+
continue;
|
|
27697
|
+
}
|
|
27698
|
+
if (ch === "I" && content[i + 1] === "D" && isDelimiter(content[i + 2])) {
|
|
27699
|
+
const end = content.indexOf("EI", i + 2);
|
|
27700
|
+
i = end === -1 ? n : end + 2;
|
|
27701
|
+
continue;
|
|
27702
|
+
}
|
|
27703
|
+
if (!isDelimiter(ch)) {
|
|
27704
|
+
const start = i;
|
|
27705
|
+
while (i < n && !isDelimiter(content[i]))
|
|
27706
|
+
i++;
|
|
27707
|
+
const token = content.slice(start, i);
|
|
27708
|
+
switch (token) {
|
|
27709
|
+
case "m":
|
|
27710
|
+
case "l":
|
|
27711
|
+
case "re":
|
|
27712
|
+
ops++;
|
|
27713
|
+
break;
|
|
27714
|
+
case "c":
|
|
27715
|
+
case "v":
|
|
27716
|
+
case "y":
|
|
27717
|
+
ops++;
|
|
27718
|
+
curves++;
|
|
27719
|
+
break;
|
|
27720
|
+
case "f":
|
|
27721
|
+
case "F":
|
|
27722
|
+
case "f*":
|
|
27723
|
+
fills++;
|
|
27724
|
+
break;
|
|
27725
|
+
case "B":
|
|
27726
|
+
case "B*":
|
|
27727
|
+
case "b":
|
|
27728
|
+
case "b*":
|
|
27729
|
+
fills++;
|
|
27730
|
+
break;
|
|
27731
|
+
default:
|
|
27732
|
+
break;
|
|
27733
|
+
}
|
|
27734
|
+
continue;
|
|
27735
|
+
}
|
|
27736
|
+
i++;
|
|
27737
|
+
}
|
|
27738
|
+
return { ops, curves, fills };
|
|
27739
|
+
}
|
|
27740
|
+
function isDelimiter(ch) {
|
|
27741
|
+
return ch === void 0 || ch === " " || ch === "\n" || ch === "\r" || ch === " " || ch === "\f" || ch === "\0" || ch === "[" || ch === "]" || ch === "/" || ch === "{" || ch === "}";
|
|
27742
|
+
}
|
|
27743
|
+
function pageContentStream(page) {
|
|
27744
|
+
try {
|
|
27745
|
+
const obj = page.getObject?.();
|
|
27746
|
+
const contents = obj?.get?.("Contents");
|
|
27747
|
+
if (!contents)
|
|
27748
|
+
return "";
|
|
27749
|
+
const streams = contents.isArray?.() && contents.get ? Array.from({ length: contents.length ?? 0 }, (_, i) => contents.get(i)) : [contents];
|
|
27750
|
+
let out = "";
|
|
27751
|
+
for (const stream of streams) {
|
|
27752
|
+
const buf = stream?.readStream?.();
|
|
27753
|
+
const bytes = buf?.asUint8Array?.();
|
|
27754
|
+
if (bytes)
|
|
27755
|
+
out += Buffer.from(bytes).toString("latin1");
|
|
27756
|
+
}
|
|
27757
|
+
return out;
|
|
27758
|
+
} catch {
|
|
27759
|
+
return "";
|
|
27760
|
+
}
|
|
27761
|
+
}
|
|
27762
|
+
function pageSignalsFrom(page, pageNumber) {
|
|
27763
|
+
const json = JSON.parse(page.toStructuredText("preserve-images").asJSON());
|
|
27764
|
+
const blocks = json.blocks ?? [];
|
|
27765
|
+
const text = blocks.flatMap((b) => (b.lines ?? []).map((l) => l.text ?? "")).join(" ");
|
|
27766
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27767
|
+
const pageArea = Math.max((x1 - x0) * (y1 - y0), 1);
|
|
27768
|
+
const imgArea = blocks.filter((b) => b.type === "image").reduce((a, b) => a + Math.max((b.bbox?.w ?? 0) * (b.bbox?.h ?? 0), 0), 0);
|
|
27769
|
+
const vector = countVectorOperators(pageContentStream(page));
|
|
27770
|
+
return {
|
|
27771
|
+
page: pageNumber,
|
|
27772
|
+
textChars: text.trim().length,
|
|
27773
|
+
imageCoverage: Math.min(imgArea / pageArea, 1),
|
|
27774
|
+
vectorOps: vector.ops,
|
|
27775
|
+
vectorCurves: vector.curves,
|
|
27776
|
+
vectorFills: vector.fills,
|
|
27777
|
+
mathHint: textMathHint(text),
|
|
27778
|
+
tableHint: textTableHint(text)
|
|
27779
|
+
};
|
|
27780
|
+
}
|
|
27781
|
+
async function scanPdfSignals(bytes) {
|
|
27782
|
+
const { signals } = await analyzePdfDocument(bytes, { text: false, signals: true });
|
|
27783
|
+
return signals;
|
|
27784
|
+
}
|
|
27785
|
+
function median(nums) {
|
|
27786
|
+
if (!nums.length)
|
|
27787
|
+
return 0;
|
|
27788
|
+
const s = [...nums].sort((a, b) => a - b);
|
|
27789
|
+
return s[Math.floor(s.length / 2)];
|
|
27790
|
+
}
|
|
27791
|
+
async function extractPdfTables(bytes, pageNum, minColumns = 2) {
|
|
27792
|
+
const { doc } = await openDoc(bytes);
|
|
27793
|
+
const total = doc.countPages();
|
|
27794
|
+
const from = pageNum ? pageNum : 1;
|
|
27795
|
+
const to = pageNum ? pageNum : total;
|
|
27796
|
+
const out = [];
|
|
27797
|
+
for (let p = from; p <= to; p++) {
|
|
27798
|
+
if (p < 1 || p > total)
|
|
27799
|
+
continue;
|
|
27800
|
+
const json = JSON.parse(doc.loadPage(p - 1).toStructuredText().asJSON());
|
|
27801
|
+
const cells = [];
|
|
27802
|
+
for (const b of json.blocks ?? []) {
|
|
27803
|
+
if (b.type === "image")
|
|
27804
|
+
continue;
|
|
27805
|
+
for (const l of b.lines ?? []) {
|
|
27806
|
+
const text = (l.text ?? "").trim();
|
|
27807
|
+
if (!text)
|
|
27808
|
+
continue;
|
|
27809
|
+
cells.push({ text, x: l.bbox?.x ?? 0, y: l.bbox?.y ?? 0, h: l.bbox?.h ?? 12 });
|
|
27810
|
+
}
|
|
27811
|
+
}
|
|
27812
|
+
if (cells.length < 2)
|
|
27813
|
+
continue;
|
|
27814
|
+
const medH = median(cells.map((c) => c.h)) || 12;
|
|
27815
|
+
const rowTol = Math.max(4, medH * 0.6);
|
|
27816
|
+
const colTol = Math.max(6, medH * 0.9);
|
|
27817
|
+
const byY = [...cells].sort((a, b) => a.y - b.y);
|
|
27818
|
+
const rowGroups = [];
|
|
27819
|
+
let rowRef = Number.NEGATIVE_INFINITY;
|
|
27820
|
+
for (const c of byY) {
|
|
27821
|
+
if (c.y - rowRef > rowTol) {
|
|
27822
|
+
rowGroups.push([]);
|
|
27823
|
+
rowRef = c.y;
|
|
27824
|
+
}
|
|
27825
|
+
rowGroups[rowGroups.length - 1].push(c);
|
|
27826
|
+
}
|
|
27827
|
+
const colRefs = [];
|
|
27828
|
+
for (const x of cells.map((c) => c.x).sort((a, b) => a - b)) {
|
|
27829
|
+
if (!colRefs.length || x - colRefs[colRefs.length - 1] > colTol)
|
|
27830
|
+
colRefs.push(x);
|
|
27831
|
+
}
|
|
27832
|
+
const colIndex = (x) => {
|
|
27833
|
+
let best = 0, bd = Infinity;
|
|
27834
|
+
colRefs.forEach((r, i) => {
|
|
27835
|
+
const d = Math.abs(x - r);
|
|
27836
|
+
if (d < bd) {
|
|
27837
|
+
bd = d;
|
|
27838
|
+
best = i;
|
|
27839
|
+
}
|
|
27840
|
+
});
|
|
27841
|
+
return best;
|
|
27842
|
+
};
|
|
27843
|
+
const rows = [];
|
|
27844
|
+
for (const group of rowGroups) {
|
|
27845
|
+
const arr = Array(colRefs.length).fill("");
|
|
27846
|
+
for (const c of group.sort((a, b) => a.x - b.x)) {
|
|
27847
|
+
const i = colIndex(c.x);
|
|
27848
|
+
arr[i] = arr[i] ? `${arr[i]} ${c.text}` : c.text;
|
|
27849
|
+
}
|
|
27850
|
+
if (arr.filter((v) => v).length >= minColumns)
|
|
27851
|
+
rows.push(arr);
|
|
27852
|
+
}
|
|
27853
|
+
if (rows.length)
|
|
27854
|
+
out.push({ page: p, rows });
|
|
27855
|
+
}
|
|
27856
|
+
return out;
|
|
27857
|
+
}
|
|
27858
|
+
async function extractPdfTextLines(bytes) {
|
|
27859
|
+
const { doc } = await openDoc(bytes);
|
|
27860
|
+
const out = [];
|
|
27861
|
+
for (let p = 1; p <= doc.countPages(); p++) {
|
|
27862
|
+
const page = doc.loadPage(p - 1);
|
|
27863
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27864
|
+
const json = JSON.parse(page.toStructuredText().asJSON());
|
|
27865
|
+
const lines = [];
|
|
27866
|
+
for (const b of json.blocks ?? []) {
|
|
27867
|
+
if (b.type === "image")
|
|
27868
|
+
continue;
|
|
27869
|
+
for (const l of b.lines ?? []) {
|
|
27870
|
+
const text = l.text ?? "";
|
|
27871
|
+
if (!text.trim() || !l.bbox)
|
|
27872
|
+
continue;
|
|
27873
|
+
lines.push({
|
|
27874
|
+
x: l.bbox.x ?? 0,
|
|
27875
|
+
y: l.bbox.y ?? 0,
|
|
27876
|
+
w: l.bbox.w ?? 0,
|
|
27877
|
+
h: l.bbox.h ?? 0,
|
|
27878
|
+
size: l.font?.size ?? 12,
|
|
27879
|
+
text
|
|
27880
|
+
});
|
|
27881
|
+
}
|
|
27882
|
+
}
|
|
27883
|
+
out.push({ page: p, width: x1 - x0, height: y1 - y0, lines });
|
|
27884
|
+
}
|
|
27885
|
+
return out;
|
|
27886
|
+
}
|
|
27887
|
+
async function renderPdfPages(bytes, pages, scale = 2, maxEdge) {
|
|
27888
|
+
const { mupdf, doc } = await openDoc(bytes);
|
|
27889
|
+
const total = doc.countPages();
|
|
27890
|
+
const wanted = pages === "all" ? Array.from({ length: total }, (_, i) => i + 1) : pages;
|
|
27891
|
+
const out = [];
|
|
27892
|
+
for (const p of wanted) {
|
|
27893
|
+
if (p < 1 || p > total)
|
|
27894
|
+
continue;
|
|
27895
|
+
const page = doc.loadPage(p - 1);
|
|
27896
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27897
|
+
const longest = Math.max(x1 - x0, y1 - y0);
|
|
27898
|
+
const s = maxEdge && longest > 0 ? Math.min(scale, maxEdge / longest) : scale;
|
|
27899
|
+
const pix = page.toPixmap(mupdf.Matrix.scale(s, s), mupdf.ColorSpace.DeviceRGB, false, true);
|
|
27900
|
+
out.push({ page: p, png: pix.asPNG() });
|
|
27901
|
+
}
|
|
27902
|
+
return out;
|
|
27903
|
+
}
|
|
27904
|
+
async function extractPdfImages(bytes, pages) {
|
|
27905
|
+
const { doc } = await openDoc(bytes);
|
|
27906
|
+
const total = doc.countPages();
|
|
27907
|
+
const wanted = pages && pages.length ? pages : Array.from({ length: total }, (_, i) => i + 1);
|
|
27908
|
+
const images = [];
|
|
27909
|
+
let skipped = 0;
|
|
27910
|
+
for (const p of wanted) {
|
|
27911
|
+
if (p < 1 || p > total)
|
|
27912
|
+
continue;
|
|
27913
|
+
const stext = doc.loadPage(p - 1).toStructuredText("preserve-images");
|
|
27914
|
+
let index = 0;
|
|
27915
|
+
stext.walk({
|
|
27916
|
+
onImageBlock(bbox, _matrix, image) {
|
|
27917
|
+
const slot = index++;
|
|
27918
|
+
try {
|
|
27919
|
+
const png = image.toPixmap().asPNG();
|
|
27920
|
+
images.push({
|
|
27921
|
+
page: p,
|
|
27922
|
+
index: slot,
|
|
27923
|
+
bbox: [bbox[0], bbox[1], bbox[2], bbox[3]],
|
|
27924
|
+
width: image.getWidth(),
|
|
27925
|
+
height: image.getHeight(),
|
|
27926
|
+
png
|
|
27927
|
+
});
|
|
27928
|
+
} catch {
|
|
27929
|
+
skipped++;
|
|
27930
|
+
}
|
|
27931
|
+
}
|
|
27932
|
+
});
|
|
27933
|
+
}
|
|
27934
|
+
return { images, skipped };
|
|
27935
|
+
}
|
|
27936
|
+
async function encryptPdf(bytes, userPassword, ownerPassword) {
|
|
27937
|
+
if (!userPassword)
|
|
27938
|
+
throw new Error("a non-empty user password is required");
|
|
27939
|
+
const { doc } = await openDoc(bytes);
|
|
27940
|
+
const opts = `encrypt=aes-256,user-password=${userPassword},owner-password=${ownerPassword || userPassword}`;
|
|
27941
|
+
return doc.saveToBuffer(opts).asUint8Array();
|
|
27942
|
+
}
|
|
27943
|
+
async function pdfNeedsPassword(bytes) {
|
|
27944
|
+
const mupdf = await loadMupdf();
|
|
27945
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27946
|
+
return doc.needsPassword();
|
|
27947
|
+
}
|
|
27948
|
+
async function decryptPdf(bytes, password) {
|
|
27949
|
+
const mupdf = await loadMupdf();
|
|
27950
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27951
|
+
if (doc.needsPassword()) {
|
|
27952
|
+
if (doc.authenticatePassword(password) === 0)
|
|
27953
|
+
throw new Error("wrong password");
|
|
27954
|
+
}
|
|
27955
|
+
if (doc.countPages() === 0)
|
|
27956
|
+
throw new Error("PDF has no readable pages (corrupt or empty)");
|
|
27957
|
+
return doc.saveToBuffer("encrypt=none").asUint8Array();
|
|
27958
|
+
}
|
|
27959
|
+
var import_node_path, import_node_url, _mupdf, MATH_CHARS;
|
|
27960
|
+
var init_pdf_extract = __esm({
|
|
27961
|
+
"packages/core/dist/document/pdf-extract.js"() {
|
|
27962
|
+
"use strict";
|
|
27963
|
+
import_node_path = require("node:path");
|
|
27964
|
+
import_node_url = require("node:url");
|
|
27965
|
+
init_pdf_encrypted();
|
|
27966
|
+
_mupdf = null;
|
|
27967
|
+
MATH_CHARS = /[=∑∫√≤≥≠±∞πθλμσΩ×÷∂∇→←↔∈∉⊂⊃∀∃]/g;
|
|
27968
|
+
}
|
|
27969
|
+
});
|
|
27970
|
+
|
|
27584
27971
|
// apps/admin/data/skills.lib/document/bin-src/doc-validate.mjs
|
|
27585
27972
|
var import_promises2 = require("node:fs/promises");
|
|
27586
|
-
var
|
|
27973
|
+
var import_node_path3 = require("node:path");
|
|
27587
27974
|
|
|
27588
27975
|
// packages/core/dist/document/doc-validate.js
|
|
27589
27976
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
@@ -27599,6 +27986,17 @@ async function validatePdf(bytes) {
|
|
|
27599
27986
|
try {
|
|
27600
27987
|
const doc = await import_pdf_lib.PDFDocument.load(bytes, { ignoreEncryption: true, throwOnInvalidObject: true, updateMetadata: false });
|
|
27601
27988
|
pages = doc.getPageCount();
|
|
27989
|
+
if (doc.isEncrypted) {
|
|
27990
|
+
const { pdfNeedsPassword: pdfNeedsPassword2 } = await Promise.resolve().then(() => (init_pdf_extract(), pdf_extract_exports));
|
|
27991
|
+
if (await pdfNeedsPassword2(bytes).catch(() => true)) {
|
|
27992
|
+
errors.push({
|
|
27993
|
+
code: "pdf-encrypted",
|
|
27994
|
+
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"
|
|
27995
|
+
});
|
|
27996
|
+
return { ok: false, kind: "pdf", errors, warnings, pages };
|
|
27997
|
+
}
|
|
27998
|
+
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>`' });
|
|
27999
|
+
}
|
|
27602
28000
|
if (pages === 0)
|
|
27603
28001
|
errors.push({ code: "pdf-no-pages", message: "the PDF parses but has no pages" });
|
|
27604
28002
|
else
|
|
@@ -27828,15 +28226,15 @@ async function validateOoxml(bytes) {
|
|
|
27828
28226
|
}
|
|
27829
28227
|
|
|
27830
28228
|
// packages/core/dist/document/doc-xsd.js
|
|
27831
|
-
var
|
|
27832
|
-
var
|
|
28229
|
+
var import_node_path2 = require("node:path");
|
|
28230
|
+
var import_node_url2 = require("node:url");
|
|
27833
28231
|
var import_promises = require("node:fs/promises");
|
|
27834
28232
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
27835
28233
|
var _xmllint = null;
|
|
27836
28234
|
function loadXmllint() {
|
|
27837
28235
|
if (!_xmllint) {
|
|
27838
28236
|
const deps = process.env.TALOS_SKILL_DEPS;
|
|
27839
|
-
const spec = deps ? (0,
|
|
28237
|
+
const spec = deps ? (0, import_node_url2.pathToFileURL)((0, import_node_path2.join)(deps, "xmllint-wasm", "index-node.js")).href : "xmllint-wasm";
|
|
27840
28238
|
_xmllint = import(spec);
|
|
27841
28239
|
}
|
|
27842
28240
|
return _xmllint;
|
|
@@ -27879,7 +28277,7 @@ async function loadSchemaSet(schemaDir) {
|
|
|
27879
28277
|
if (!files.length)
|
|
27880
28278
|
throw new Error(`no .xsd schemas found in ${schemaDir}`);
|
|
27881
28279
|
const set = /* @__PURE__ */ new Map();
|
|
27882
|
-
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises.readFile)((0,
|
|
28280
|
+
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises.readFile)((0, import_node_path2.join)(schemaDir, f), "utf8"))));
|
|
27883
28281
|
return set;
|
|
27884
28282
|
}
|
|
27885
28283
|
function errText(e) {
|
|
@@ -27983,6 +28381,118 @@ async function slidePartsInOrder(zip) {
|
|
|
27983
28381
|
return toParts(complete ? ordered : byNumber);
|
|
27984
28382
|
}
|
|
27985
28383
|
|
|
28384
|
+
// packages/core/dist/document/png-decode.js
|
|
28385
|
+
var import_node_zlib = require("node:zlib");
|
|
28386
|
+
var SIG = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
28387
|
+
var CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
|
|
28388
|
+
function decodePng(bytes) {
|
|
28389
|
+
try {
|
|
28390
|
+
if (bytes.length < 33 || SIG.some((b, i) => bytes[i] !== b))
|
|
28391
|
+
return void 0;
|
|
28392
|
+
const buf = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
28393
|
+
let width = 0, height = 0, depth = 0, type = -1, interlace = 0;
|
|
28394
|
+
let palette;
|
|
28395
|
+
const idat = [];
|
|
28396
|
+
for (let p = 8; p + 8 <= buf.length; ) {
|
|
28397
|
+
const len = buf.readUInt32BE(p);
|
|
28398
|
+
const kind = buf.toString("latin1", p + 4, p + 8);
|
|
28399
|
+
const data = buf.subarray(p + 8, p + 8 + len);
|
|
28400
|
+
if (kind === "IHDR") {
|
|
28401
|
+
width = data.readUInt32BE(0);
|
|
28402
|
+
height = data.readUInt32BE(4);
|
|
28403
|
+
depth = data[8];
|
|
28404
|
+
type = data[9];
|
|
28405
|
+
interlace = data[12];
|
|
28406
|
+
} else if (kind === "PLTE")
|
|
28407
|
+
palette = data;
|
|
28408
|
+
else if (kind === "IDAT")
|
|
28409
|
+
idat.push(data);
|
|
28410
|
+
else if (kind === "IEND")
|
|
28411
|
+
break;
|
|
28412
|
+
p += 12 + len;
|
|
28413
|
+
}
|
|
28414
|
+
const ch = CHANNELS[type];
|
|
28415
|
+
if (!width || !height || depth !== 8 || interlace !== 0 || ch === void 0 || type === 3 && !palette)
|
|
28416
|
+
return void 0;
|
|
28417
|
+
const raw = (0, import_node_zlib.inflateSync)(Buffer.concat(idat));
|
|
28418
|
+
const stride = width * ch;
|
|
28419
|
+
if (raw.length < height * (stride + 1))
|
|
28420
|
+
return void 0;
|
|
28421
|
+
const cur = new Uint8Array(stride);
|
|
28422
|
+
const prev = new Uint8Array(stride);
|
|
28423
|
+
const rgb = new Uint8Array(width * height * 3);
|
|
28424
|
+
for (let y = 0; y < height; y++) {
|
|
28425
|
+
const off = y * (stride + 1);
|
|
28426
|
+
const filter = raw[off];
|
|
28427
|
+
for (let i = 0; i < stride; i++) {
|
|
28428
|
+
const x = raw[off + 1 + i];
|
|
28429
|
+
const a = i >= ch ? cur[i - ch] : 0;
|
|
28430
|
+
const b = prev[i];
|
|
28431
|
+
const c = i >= ch ? prev[i - ch] : 0;
|
|
28432
|
+
let v;
|
|
28433
|
+
switch (filter) {
|
|
28434
|
+
case 0:
|
|
28435
|
+
v = x;
|
|
28436
|
+
break;
|
|
28437
|
+
case 1:
|
|
28438
|
+
v = x + a;
|
|
28439
|
+
break;
|
|
28440
|
+
case 2:
|
|
28441
|
+
v = x + b;
|
|
28442
|
+
break;
|
|
28443
|
+
case 3:
|
|
28444
|
+
v = x + (a + b >> 1);
|
|
28445
|
+
break;
|
|
28446
|
+
case 4: {
|
|
28447
|
+
const pa = Math.abs(b - c), pb = Math.abs(a - c), pc = Math.abs(a + b - 2 * c);
|
|
28448
|
+
v = x + (pa <= pb && pa <= pc ? a : pb <= pc ? b : c);
|
|
28449
|
+
break;
|
|
28450
|
+
}
|
|
28451
|
+
default:
|
|
28452
|
+
return void 0;
|
|
28453
|
+
}
|
|
28454
|
+
cur[i] = v & 255;
|
|
28455
|
+
}
|
|
28456
|
+
for (let px = 0; px < width; px++) {
|
|
28457
|
+
const o = (y * width + px) * 3;
|
|
28458
|
+
const s = px * ch;
|
|
28459
|
+
if (type === 3) {
|
|
28460
|
+
const k = cur[s] * 3;
|
|
28461
|
+
rgb[o] = palette[k];
|
|
28462
|
+
rgb[o + 1] = palette[k + 1];
|
|
28463
|
+
rgb[o + 2] = palette[k + 2];
|
|
28464
|
+
} else if (ch <= 2) {
|
|
28465
|
+
rgb[o] = rgb[o + 1] = rgb[o + 2] = cur[s];
|
|
28466
|
+
} else {
|
|
28467
|
+
rgb[o] = cur[s];
|
|
28468
|
+
rgb[o + 1] = cur[s + 1];
|
|
28469
|
+
rgb[o + 2] = cur[s + 2];
|
|
28470
|
+
}
|
|
28471
|
+
}
|
|
28472
|
+
prev.set(cur);
|
|
28473
|
+
}
|
|
28474
|
+
return { width, height, rgb };
|
|
28475
|
+
} catch {
|
|
28476
|
+
return void 0;
|
|
28477
|
+
}
|
|
28478
|
+
}
|
|
28479
|
+
|
|
28480
|
+
// packages/core/dist/document/text-slack.js
|
|
28481
|
+
var SLACK_CJK_RE = /[\u3000-\u303F\u3400-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
28482
|
+
var PCT = { single: { cjk: 0.22, latin: 0.16 }, multi: { cjk: 0.1, latin: 0.06 } };
|
|
28483
|
+
var FLOOR_EM = { cjk: 1, latin: 0.45 };
|
|
28484
|
+
function params(single, cjk) {
|
|
28485
|
+
const script = cjk ? "cjk" : "latin";
|
|
28486
|
+
return { pct: PCT[single ? "single" : "multi"][script], floor: FLOOR_EM[script] * (single ? 1 : 0.5) };
|
|
28487
|
+
}
|
|
28488
|
+
function unpaddedWidth(padded, fontSize, single, cjk) {
|
|
28489
|
+
const { pct, floor } = params(single, cjk);
|
|
28490
|
+
const floorAbs = floor * fontSize;
|
|
28491
|
+
const viaPct = padded / (1 + pct);
|
|
28492
|
+
return viaPct * pct >= floorAbs ? viaPct : Math.max(0, padded - floorAbs);
|
|
28493
|
+
}
|
|
28494
|
+
var CHART_TEXT_NAME = "talos-chart-text";
|
|
28495
|
+
|
|
27986
28496
|
// packages/core/dist/document/doc-design-lint.js
|
|
27987
28497
|
var PLACEHOLDER_RE = /替换为|在这里写|占位|示例文本|Lorem ipsum/i;
|
|
27988
28498
|
var CJK_TEXT_RE = /[-ヿ㐀-䶿一-鿿가-豈-]/;
|
|
@@ -28123,7 +28633,7 @@ function lintSlideXml(slideXml, slideNumber, masterBg, expectedLang = detectDeck
|
|
|
28123
28633
|
}
|
|
28124
28634
|
}
|
|
28125
28635
|
}
|
|
28126
|
-
if (worst && !worst.onShapeFill && anyVisualShape)
|
|
28636
|
+
if (worst && !worst.onShapeFill && (anyVisualShape || hasPic))
|
|
28127
28637
|
worst = void 0;
|
|
28128
28638
|
if (placeholder) {
|
|
28129
28639
|
const snippet = placeholder.length > 24 ? `${placeholder.slice(0, 24)}\u2026` : placeholder;
|
|
@@ -28163,7 +28673,10 @@ var EMU_PER_INCH = 914400;
|
|
|
28163
28673
|
var DEFAULT_CANVAS = { w: 12192e3, h: 6858e3 };
|
|
28164
28674
|
var OFFCANVAS_TOL = EMU_PER_INCH / 10;
|
|
28165
28675
|
var MIN_FONT_HUNDREDTHS = 1e3;
|
|
28166
|
-
var
|
|
28676
|
+
var LABEL_MAX_CHARS = 24;
|
|
28677
|
+
var MIN_LABEL_FONT_HUNDREDTHS = 800;
|
|
28678
|
+
var CHART_TEXT_RE = new RegExp(`<p:cNvPr\\b[^>]*\\bname="${CHART_TEXT_NAME}\\b`);
|
|
28679
|
+
var OVERLAP_RATIO = 0.3;
|
|
28167
28680
|
function firstBox(block) {
|
|
28168
28681
|
const xf = /<(?:a|p):xfrm\b[^>]*>[\s\S]*?<\/(?:a|p):xfrm>/.exec(block);
|
|
28169
28682
|
const scope = xf ? xf[0] : block;
|
|
@@ -28181,6 +28694,51 @@ function overlapArea(a, b) {
|
|
|
28181
28694
|
const iy = Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
|
|
28182
28695
|
return ix * iy;
|
|
28183
28696
|
}
|
|
28697
|
+
var CJK_WIDE_RE = /[\u2E80-\u9FFF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
28698
|
+
var EMU_PER_PT = 12700;
|
|
28699
|
+
function inkBox(block, box) {
|
|
28700
|
+
let maxSz = 0;
|
|
28701
|
+
let inkW = 0;
|
|
28702
|
+
let lines = 0;
|
|
28703
|
+
for (const para of block.match(/<a:p>[\s\S]*?<\/a:p>|<a:p\b[^>]*>[\s\S]*?<\/a:p>/g) ?? []) {
|
|
28704
|
+
let paraW = 0;
|
|
28705
|
+
let paraSz = 0;
|
|
28706
|
+
for (const run of para.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28707
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28708
|
+
if (raw === void 0)
|
|
28709
|
+
continue;
|
|
28710
|
+
const sz = +(/<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1] ?? 1800) / 100;
|
|
28711
|
+
paraSz = Math.max(paraSz, sz);
|
|
28712
|
+
for (const ch of unescXml(raw))
|
|
28713
|
+
paraW += (CJK_WIDE_RE.test(ch) ? 1 : ch === " " ? 0.3 : 0.55) * sz * EMU_PER_PT;
|
|
28714
|
+
}
|
|
28715
|
+
if (!paraW)
|
|
28716
|
+
continue;
|
|
28717
|
+
maxSz = Math.max(maxSz, paraSz);
|
|
28718
|
+
const paraLines = box.w > 0 ? Math.max(1, Math.ceil(paraW / box.w)) : 1;
|
|
28719
|
+
lines += paraLines;
|
|
28720
|
+
inkW = Math.max(inkW, Math.min(paraW, box.w));
|
|
28721
|
+
}
|
|
28722
|
+
if (!lines)
|
|
28723
|
+
return void 0;
|
|
28724
|
+
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));
|
|
28725
|
+
if (paras.length === 1 && maxSz > 0) {
|
|
28726
|
+
const lnSpc = /<a:lnSpc>\s*<a:spcPts\b[^>]*\bval="(\d+)"/.exec(block)?.[1];
|
|
28727
|
+
const lineH = (lnSpc ? +lnSpc / 100 : maxSz * 1.3) * EMU_PER_PT;
|
|
28728
|
+
const clamped = box.x + box.w >= DEFAULT_CANVAS.w - EMU_PER_PT;
|
|
28729
|
+
if (box.h <= lineH * 1.5 && !clamped) {
|
|
28730
|
+
const text = unescXml((block.match(/<a:t\b[^>]*>([\s\S]*?)<\/a:t>/g) ?? []).join(""));
|
|
28731
|
+
lines = 1;
|
|
28732
|
+
inkW = Math.min(inkW, unpaddedWidth(box.w, maxSz * EMU_PER_PT, true, SLACK_CJK_RE.test(text)));
|
|
28733
|
+
}
|
|
28734
|
+
}
|
|
28735
|
+
const inkH = Math.min(box.h, lines * maxSz * 1.2 * EMU_PER_PT);
|
|
28736
|
+
const algn = /<a:pPr\b[^>]*\balgn="(\w+)"/.exec(block)?.[1];
|
|
28737
|
+
const anchor = /<a:bodyPr\b[^>]*\banchor="(\w+)"/.exec(block)?.[1];
|
|
28738
|
+
const x = algn === "ctr" ? box.x + (box.w - inkW) / 2 : algn === "r" ? box.x + box.w - inkW : box.x;
|
|
28739
|
+
const y = anchor === "ctr" ? box.y + (box.h - inkH) / 2 : anchor === "b" ? box.y + box.h - inkH : box.y;
|
|
28740
|
+
return { x, y, w: inkW, h: inkH };
|
|
28741
|
+
}
|
|
28184
28742
|
function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
28185
28743
|
const findings = [];
|
|
28186
28744
|
const blocks = slideXml.match(/<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g) ?? [];
|
|
@@ -28204,16 +28762,23 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28204
28762
|
worstOff = { by, edge };
|
|
28205
28763
|
}
|
|
28206
28764
|
}
|
|
28207
|
-
if (hasText && box.w > 0 && box.h > 0)
|
|
28208
|
-
|
|
28765
|
+
if (hasText && box.w > 0 && box.h > 0) {
|
|
28766
|
+
const ink = inkBox(b, box);
|
|
28767
|
+
if (ink)
|
|
28768
|
+
textBoxes.push(ink);
|
|
28769
|
+
}
|
|
28209
28770
|
}
|
|
28771
|
+
if (CHART_TEXT_RE.test(b))
|
|
28772
|
+
continue;
|
|
28210
28773
|
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28211
28774
|
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28212
28775
|
if (raw === void 0 || !unescXml(raw).trim())
|
|
28213
28776
|
continue;
|
|
28777
|
+
const text = unescXml(raw).trim();
|
|
28214
28778
|
const sz = /<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1];
|
|
28215
|
-
|
|
28216
|
-
|
|
28779
|
+
const floor = [...text].length <= LABEL_MAX_CHARS ? MIN_LABEL_FONT_HUNDREDTHS : MIN_FONT_HUNDREDTHS;
|
|
28780
|
+
if (sz && +sz < floor && (!smallest || +sz < smallest.sz)) {
|
|
28781
|
+
smallest = { sz: +sz, text, floor };
|
|
28217
28782
|
}
|
|
28218
28783
|
}
|
|
28219
28784
|
}
|
|
@@ -28229,7 +28794,7 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28229
28794
|
findings.push({
|
|
28230
28795
|
slide: slideNumber,
|
|
28231
28796
|
code: "tiny-font",
|
|
28232
|
-
message: `text "${snippet}" is ${(smallest.sz / 100).toFixed(
|
|
28797
|
+
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`
|
|
28233
28798
|
});
|
|
28234
28799
|
}
|
|
28235
28800
|
let worstPair;
|
|
@@ -28254,6 +28819,110 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28254
28819
|
}
|
|
28255
28820
|
return findings;
|
|
28256
28821
|
}
|
|
28822
|
+
var BACKDROP_COVER = 0.98;
|
|
28823
|
+
var BACKDROP_FLOOR = 2;
|
|
28824
|
+
var BACKDROP_SAMPLES = 2500;
|
|
28825
|
+
var LIN = Array.from({ length: 256 }, (_, i) => {
|
|
28826
|
+
const c = i / 255;
|
|
28827
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
28828
|
+
});
|
|
28829
|
+
var lumRgb = (r, g, b) => 0.2126 * LIN[r] + 0.7152 * LIN[g] + 0.0722 * LIN[b];
|
|
28830
|
+
var ratioOf = (a, b) => (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
28831
|
+
var hex2 = (n) => n.toString(16).padStart(2, "0").toUpperCase();
|
|
28832
|
+
var BLOCK_RE = /<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g;
|
|
28833
|
+
function backdropPicEmbed(slideXml, canvas = DEFAULT_CANVAS) {
|
|
28834
|
+
const first = (slideXml.match(BLOCK_RE) ?? [])[0];
|
|
28835
|
+
if (!first || !first.startsWith("<p:pic"))
|
|
28836
|
+
return void 0;
|
|
28837
|
+
const box = firstBox(first);
|
|
28838
|
+
if (!box || box.x > canvas.w * (1 - BACKDROP_COVER) || box.y > canvas.h * (1 - BACKDROP_COVER))
|
|
28839
|
+
return void 0;
|
|
28840
|
+
if (box.w < canvas.w * BACKDROP_COVER || box.h < canvas.h * BACKDROP_COVER)
|
|
28841
|
+
return void 0;
|
|
28842
|
+
return /<a:blip\b[^>]*\br:embed="([^"]+)"/.exec(first)?.[1];
|
|
28843
|
+
}
|
|
28844
|
+
function lintBackdropContrast(slideXml, slideNumber, backdrop, canvas = DEFAULT_CANVAS) {
|
|
28845
|
+
const blocks = slideXml.match(BLOCK_RE) ?? [];
|
|
28846
|
+
const sx = backdrop.width / canvas.w;
|
|
28847
|
+
const sy = backdrop.height / canvas.h;
|
|
28848
|
+
const occluders = [];
|
|
28849
|
+
let worst;
|
|
28850
|
+
for (let bi = 1; bi < blocks.length; bi++) {
|
|
28851
|
+
const b = blocks[bi];
|
|
28852
|
+
const box = firstBox(b);
|
|
28853
|
+
if (!box)
|
|
28854
|
+
continue;
|
|
28855
|
+
const txIdx = b.indexOf("<p:txBody>");
|
|
28856
|
+
const spPr = txIdx >= 0 ? b.slice(0, txIdx) : b;
|
|
28857
|
+
const prst = /<a:prstGeom\b[^>]*\bprst="([^"]+)"/.exec(spPr)?.[1];
|
|
28858
|
+
const visual = !b.startsWith("<p:sp") || FILL_RE.test(spPr) || prst !== void 0 && prst !== "rect";
|
|
28859
|
+
const hasText = txIdx >= 0 && /<a:t\b[^>]*>\s*\S/.test(b);
|
|
28860
|
+
if (visual) {
|
|
28861
|
+
occluders.push(box);
|
|
28862
|
+
continue;
|
|
28863
|
+
}
|
|
28864
|
+
if (!hasText)
|
|
28865
|
+
continue;
|
|
28866
|
+
const ink = inkBox(b, box);
|
|
28867
|
+
if (!ink || ink.w <= 0 || ink.h <= 0)
|
|
28868
|
+
continue;
|
|
28869
|
+
if (occluders.some((o2) => overlapArea(o2, ink) > 0))
|
|
28870
|
+
continue;
|
|
28871
|
+
let fg;
|
|
28872
|
+
let alpha = 1;
|
|
28873
|
+
let text = "";
|
|
28874
|
+
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28875
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28876
|
+
if (raw === void 0 || !unescXml(raw).trim())
|
|
28877
|
+
continue;
|
|
28878
|
+
text += unescXml(raw);
|
|
28879
|
+
if (fg)
|
|
28880
|
+
continue;
|
|
28881
|
+
const rPr = run.slice(0, Math.max(0, run.indexOf("</a:rPr>")));
|
|
28882
|
+
fg = firstSrgb(rPr);
|
|
28883
|
+
const a = /<a:srgbClr\b[^>]*>\s*<a:alpha\b[^>]*\bval="(\d+)"/.exec(rPr)?.[1];
|
|
28884
|
+
if (fg && a)
|
|
28885
|
+
alpha = Math.min(1, +a / 1e5);
|
|
28886
|
+
}
|
|
28887
|
+
if (!fg)
|
|
28888
|
+
continue;
|
|
28889
|
+
const x0 = Math.max(0, Math.floor(ink.x * sx));
|
|
28890
|
+
const y0 = Math.max(0, Math.floor(ink.y * sy));
|
|
28891
|
+
const x1 = Math.min(backdrop.width, Math.ceil((ink.x + ink.w) * sx));
|
|
28892
|
+
const y1 = Math.min(backdrop.height, Math.ceil((ink.y + ink.h) * sy));
|
|
28893
|
+
if (x1 <= x0 || y1 <= y0)
|
|
28894
|
+
continue;
|
|
28895
|
+
const step = Math.max(1, Math.floor(Math.sqrt((x1 - x0) * (y1 - y0) / BACKDROP_SAMPLES)));
|
|
28896
|
+
const fr = parseInt(fg.slice(0, 2), 16), fgG = parseInt(fg.slice(2, 4), 16), fb = parseInt(fg.slice(4, 6), 16);
|
|
28897
|
+
const fgLum = lumRgb(fr, fgG, fb);
|
|
28898
|
+
const samples = [];
|
|
28899
|
+
for (let y = y0; y < y1; y += step) {
|
|
28900
|
+
for (let x = x0; x < x1; x += step) {
|
|
28901
|
+
const o2 = (y * backdrop.width + x) * 3;
|
|
28902
|
+
const r = backdrop.rgb[o2], g = backdrop.rgb[o2 + 1], bl = backdrop.rgb[o2 + 2];
|
|
28903
|
+
const l = lumRgb(r, g, bl);
|
|
28904
|
+
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));
|
|
28905
|
+
samples.push([ratioOf(tl, l), o2]);
|
|
28906
|
+
}
|
|
28907
|
+
}
|
|
28908
|
+
if (!samples.length)
|
|
28909
|
+
continue;
|
|
28910
|
+
samples.sort((p, q) => p[0] - q[0]);
|
|
28911
|
+
const [ratio, o] = samples[samples.length >> 1];
|
|
28912
|
+
if (ratio < BACKDROP_FLOOR && (!worst || ratio < worst.ratio)) {
|
|
28913
|
+
const bg = hex2(backdrop.rgb[o]) + hex2(backdrop.rgb[o + 1]) + hex2(backdrop.rgb[o + 2]);
|
|
28914
|
+
worst = { ratio, text: text.trim(), fg, bg };
|
|
28915
|
+
}
|
|
28916
|
+
}
|
|
28917
|
+
if (!worst)
|
|
28918
|
+
return [];
|
|
28919
|
+
const snippet = worst.text.length > 24 ? `${worst.text.slice(0, 24)}\u2026` : worst.text;
|
|
28920
|
+
return [{
|
|
28921
|
+
slide: slideNumber,
|
|
28922
|
+
code: "low-contrast-text",
|
|
28923
|
+
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`
|
|
28924
|
+
}];
|
|
28925
|
+
}
|
|
28257
28926
|
function canvasFromPresentation(xml) {
|
|
28258
28927
|
if (!xml)
|
|
28259
28928
|
return DEFAULT_CANVAS;
|
|
@@ -28261,6 +28930,26 @@ function canvasFromPresentation(xml) {
|
|
|
28261
28930
|
const cy = /<p:sldSz\b[^>]*\bcy="(\d+)"/.exec(xml)?.[1];
|
|
28262
28931
|
return cx && cy ? { w: +cx, h: +cy } : DEFAULT_CANVAS;
|
|
28263
28932
|
}
|
|
28933
|
+
async function backdropRaster(zip, slideXml, relsPath, canvas) {
|
|
28934
|
+
const rId = backdropPicEmbed(slideXml, canvas);
|
|
28935
|
+
const rels = rId ? zip.file(relsPath) : null;
|
|
28936
|
+
if (!rId || !rels)
|
|
28937
|
+
return void 0;
|
|
28938
|
+
const relsXml = await rels.async("string");
|
|
28939
|
+
const rel = [...relsXml.matchAll(/<Relationship\b[^>]*>/g)].map((m) => m[0]).find((r) => new RegExp(`\\bId="${rId}"`).test(r));
|
|
28940
|
+
const target = rel && /\bTarget="([^"]+)"/.exec(rel)?.[1];
|
|
28941
|
+
if (!target || !/\.png$/i.test(target))
|
|
28942
|
+
return void 0;
|
|
28943
|
+
const parts = ["ppt", "slides"];
|
|
28944
|
+
for (const seg of target.split("/")) {
|
|
28945
|
+
if (seg === "..")
|
|
28946
|
+
parts.pop();
|
|
28947
|
+
else if (seg && seg !== ".")
|
|
28948
|
+
parts.push(seg);
|
|
28949
|
+
}
|
|
28950
|
+
const media = zip.file(target.startsWith("/") ? target.slice(1) : parts.join("/"));
|
|
28951
|
+
return media ? decodePng(await media.async("uint8array")) : void 0;
|
|
28952
|
+
}
|
|
28264
28953
|
async function designLintPptx(bytes) {
|
|
28265
28954
|
const zip = await import_jszip3.default.loadAsync(Buffer.from(bytes));
|
|
28266
28955
|
const findings = [];
|
|
@@ -28271,9 +28960,15 @@ async function designLintPptx(bytes) {
|
|
|
28271
28960
|
const presFile = zip.file("ppt/presentation.xml");
|
|
28272
28961
|
const canvas = canvasFromPresentation(presFile ? await presFile.async("string") : void 0);
|
|
28273
28962
|
const lang = detectDeckLang();
|
|
28274
|
-
for (const { position, path } of await slidePartsInOrder(zip)) {
|
|
28963
|
+
for (const { position, path, relsPath } of await slidePartsInOrder(zip)) {
|
|
28275
28964
|
const xml = await zip.file(path).async("string");
|
|
28276
|
-
|
|
28965
|
+
const own = lintSlideXml(xml, position, masterBg, lang);
|
|
28966
|
+
findings.push(...own);
|
|
28967
|
+
if (!own.some((f) => f.code === "low-contrast-text")) {
|
|
28968
|
+
const backdrop = await backdropRaster(zip, xml, relsPath, canvas);
|
|
28969
|
+
if (backdrop)
|
|
28970
|
+
findings.push(...lintBackdropContrast(xml, position, backdrop, canvas));
|
|
28971
|
+
}
|
|
28277
28972
|
findings.push(...lintSlideGeometry(xml, position, canvas));
|
|
28278
28973
|
}
|
|
28279
28974
|
return { ok: findings.length === 0, findings };
|
|
@@ -28302,7 +28997,7 @@ async function main() {
|
|
|
28302
28997
|
}];
|
|
28303
28998
|
}
|
|
28304
28999
|
if (strict && strictApplies) {
|
|
28305
|
-
const schemaDir = process.env.TALOS_XSD_DIR || (0,
|
|
29000
|
+
const schemaDir = process.env.TALOS_XSD_DIR || (0, import_node_path3.join)((0, import_node_path3.dirname)(process.argv[1]), "..", "schemas");
|
|
28306
29001
|
strictResult = await validateOoxmlStrict(bytes, schemaDir).catch((e) => {
|
|
28307
29002
|
die(`--strict failed to run: ${e.message}`);
|
|
28308
29003
|
});
|