@taloswork/ose-shared 0.1.11 → 0.1.13
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 +135 -219
- 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 +12563 -83
- package/skills/document/bin/deck-chart.cjs +771 -0
- package/skills/document/bin/deck-plan.cjs +12414 -0
- package/skills/document/bin/deck-qa.cjs +1075 -266
- package/skills/document/bin/deck-scenario.cjs +1363 -0
- package/skills/document/bin/deck-styles.cjs +1604 -0
- package/skills/document/bin/doc-convert.cjs +308 -292
- package/skills/document/bin/doc-fill.cjs +73264 -192
- package/skills/document/bin/doc-gen.cjs +2491 -312
- 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 +838 -68
- package/skills/document/bin/doc-xml.cjs +9 -5
- package/skills/document/bin/docx-annotate.cjs +95 -19
- package/skills/document/bin/docx-defaults.cjs +9907 -0
- package/skills/document/bin/fontkit.cjs +1 -0
- package/skills/document/bin/html2pptx.cjs +36622 -31394
- package/skills/document/bin/ocr-worker.cjs +1 -0
- package/skills/document/bin/ocr.cjs +131 -40
- package/skills/document/bin/pdf-form.cjs +682 -177
- package/skills/document/bin/pdf-images.cjs +13 -0
- package/skills/document/bin/pdf-ops.cjs +642 -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-notes.cjs +10038 -0
- package/skills/document/bin/pptx-slide.cjs +3 -3
- package/skills/document/bin/pptx-tokenize.cjs +19 -4
- package/skills/document/bin/template-skin.cjs +10910 -0
- package/skills/document/bin/xlsx-query.cjs +192 -61
- package/skills/document/bin-reference.md +61 -33
- package/skills/document/catalog.json +33 -32
- package/skills/document/content-pipeline.md +179 -257
- package/skills/document/deck-design.md +378 -638
- package/skills/document/generated-visuals.md +99 -0
- package/skills/document/layouts/MAINTAINERS.md +33 -0
- package/skills/document/layouts/README.md +49 -36
- package/skills/document/layouts/big-stat.tpl +1 -1
- package/skills/document/layouts/capacity.json +1 -0
- package/skills/document/layouts/cards-3.tpl +1 -1
- package/skills/document/layouts/chart-full.tpl +1 -1
- package/skills/document/layouts/chart-kpi.tpl +1 -1
- package/skills/document/layouts/comparison.tpl +1 -1
- package/skills/document/layouts/data-table.tpl +1 -1
- package/skills/document/layouts/funnel.tpl +1 -1
- package/skills/document/layouts/gantt.tpl +1 -1
- package/skills/document/layouts/hub-flow.tpl +4 -0
- package/skills/document/layouts/icon-row.tpl +1 -1
- package/skills/document/layouts/image-grid.tpl +1 -1
- package/skills/document/layouts/matrix-2x2.tpl +1 -1
- package/skills/document/layouts/mock-qa.tpl +4 -0
- package/skills/document/layouts/profiles.json +2 -2
- package/skills/document/layouts/pyramid.tpl +1 -1
- package/skills/document/layouts/radar.tpl +1 -1
- package/skills/document/layouts/ring-progress.tpl +1 -1
- package/skills/document/layouts/slots.json +1 -1
- package/skills/document/layouts/steps-vertical.tpl +1 -1
- package/skills/document/layouts/steps.tpl +1 -1
- package/skills/document/layouts/three-kpi.tpl +1 -1
- package/skills/document/layouts/timeline.tpl +1 -1
- package/skills/document/layouts/toc-panel.tpl +2 -2
- package/skills/document/layouts/two-kpi.tpl +2 -2
- package/skills/document/layouts/versus.tpl +1 -1
- package/skills/document/motion/README.md +3 -2
- package/skills/document/office.md +3 -3
- package/skills/document/packs/README.md +1 -1
- 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 +60 -43
- package/skills/document/rewrite.md +257 -309
- package/skills/document/scenarios.json +1307 -0
- package/skills/document/scenarios.schema.json +104 -0
- package/skills/document/styles/README.md +15 -25
- package/skills/document/styles/_backdrops/rimlight.html +3 -2
- package/skills/document/styles/_contract-defaults.css +31 -2
- package/skills/document/styles/_samples/_groups/biz-premium.webp +0 -0
- package/skills/document/styles/_samples/mckinsey.webp +0 -0
- 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/backdrop.html +7 -5
- 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/grill-me/SKILL.md +6 -0
- package/skills/grill-me/agents/openai.yaml +5 -0
- package/skills/grilling/SKILL.md +28 -0
- package/skills/grilling/agents/openai.yaml +3 -0
|
@@ -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;
|
|
@@ -9790,9 +9790,9 @@ var require_load = __commonJS({
|
|
|
9790
9790
|
var require_lib3 = __commonJS({
|
|
9791
9791
|
"node_modules/jszip/lib/index.js"(exports2, module2) {
|
|
9792
9792
|
"use strict";
|
|
9793
|
-
function
|
|
9794
|
-
if (!(this instanceof
|
|
9795
|
-
return new
|
|
9793
|
+
function JSZip6() {
|
|
9794
|
+
if (!(this instanceof JSZip6)) {
|
|
9795
|
+
return new JSZip6();
|
|
9796
9796
|
}
|
|
9797
9797
|
if (arguments.length) {
|
|
9798
9798
|
throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
|
|
@@ -9801,7 +9801,7 @@ var require_lib3 = __commonJS({
|
|
|
9801
9801
|
this.comment = null;
|
|
9802
9802
|
this.root = "";
|
|
9803
9803
|
this.clone = function() {
|
|
9804
|
-
var newObj = new
|
|
9804
|
+
var newObj = new JSZip6();
|
|
9805
9805
|
for (var i in this) {
|
|
9806
9806
|
if (typeof this[i] !== "function") {
|
|
9807
9807
|
newObj[i] = this[i];
|
|
@@ -9810,16 +9810,16 @@ var require_lib3 = __commonJS({
|
|
|
9810
9810
|
return newObj;
|
|
9811
9811
|
};
|
|
9812
9812
|
}
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
return new
|
|
9813
|
+
JSZip6.prototype = require_object();
|
|
9814
|
+
JSZip6.prototype.loadAsync = require_load();
|
|
9815
|
+
JSZip6.support = require_support();
|
|
9816
|
+
JSZip6.defaults = require_defaults();
|
|
9817
|
+
JSZip6.version = "3.10.1";
|
|
9818
|
+
JSZip6.loadAsync = function(content, options) {
|
|
9819
|
+
return new JSZip6().loadAsync(content, options);
|
|
9820
9820
|
};
|
|
9821
|
-
|
|
9822
|
-
module2.exports =
|
|
9821
|
+
JSZip6.external = require_external();
|
|
9822
|
+
module2.exports = JSZip6;
|
|
9823
9823
|
}
|
|
9824
9824
|
});
|
|
9825
9825
|
|
|
@@ -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,13 +27581,474 @@ 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
|
+
pngPixels: () => pngPixels,
|
|
27612
|
+
renderPdfPages: () => renderPdfPages,
|
|
27613
|
+
scanPdfSignals: () => scanPdfSignals,
|
|
27614
|
+
textMathHint: () => textMathHint,
|
|
27615
|
+
textTableHint: () => textTableHint
|
|
27616
|
+
});
|
|
27617
|
+
function loadMupdf() {
|
|
27618
|
+
if (!_mupdf) {
|
|
27619
|
+
const deps = process.env.TALOS_SKILL_DEPS;
|
|
27620
|
+
const spec = deps ? (0, import_node_url.pathToFileURL)((0, import_node_path.join)(deps, "mupdf", "dist", "mupdf.js")).href : "mupdf";
|
|
27621
|
+
_mupdf = import(spec);
|
|
27622
|
+
}
|
|
27623
|
+
return _mupdf;
|
|
27624
|
+
}
|
|
27625
|
+
function textMathHint(text) {
|
|
27626
|
+
if (!text)
|
|
27627
|
+
return false;
|
|
27628
|
+
const hits = (text.match(MATH_CHARS) || []).length;
|
|
27629
|
+
return hits >= 3 || hits / Math.max(text.length, 1) > 0.02;
|
|
27630
|
+
}
|
|
27631
|
+
function textTableHint(text) {
|
|
27632
|
+
if (!text)
|
|
27633
|
+
return false;
|
|
27634
|
+
const gaps = (text.match(/ {2,}|\t/g) || []).length;
|
|
27635
|
+
return gaps >= 6;
|
|
27636
|
+
}
|
|
27637
|
+
async function openDoc(bytes) {
|
|
27638
|
+
const mupdf = await loadMupdf();
|
|
27639
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27640
|
+
if (doc.needsPassword())
|
|
27641
|
+
throw new PdfEncryptedError();
|
|
27642
|
+
if (doc.countPages() === 0) {
|
|
27643
|
+
throw new Error("PDF has no readable pages (corrupt, encrypted, or empty)");
|
|
27644
|
+
}
|
|
27645
|
+
return { mupdf, doc };
|
|
27646
|
+
}
|
|
27647
|
+
async function analyzePdfDocument(bytes, options = {}) {
|
|
27648
|
+
const wantText = options.text !== false;
|
|
27649
|
+
const wantSignals = options.signals === true;
|
|
27650
|
+
const { doc } = await openDoc(bytes);
|
|
27651
|
+
const pageCount = doc.countPages();
|
|
27652
|
+
const signals = [];
|
|
27653
|
+
let out = "";
|
|
27654
|
+
for (let i = 0; i < pageCount; i++) {
|
|
27655
|
+
const page = doc.loadPage(i);
|
|
27656
|
+
if (wantText) {
|
|
27657
|
+
out += `
|
|
27658
|
+
[page ${i + 1}]
|
|
27659
|
+
${page.toStructuredText().asText()}`;
|
|
27660
|
+
}
|
|
27661
|
+
if (wantSignals) {
|
|
27662
|
+
signals.push(pageSignalsFrom(page, i + 1));
|
|
27663
|
+
}
|
|
27664
|
+
}
|
|
27665
|
+
return { text: wantText ? out.trim() : "", pageCount, signals };
|
|
27666
|
+
}
|
|
27667
|
+
async function extractPdfText(bytes) {
|
|
27668
|
+
const { text, pageCount } = await analyzePdfDocument(bytes, { text: true });
|
|
27669
|
+
return { text, pageCount };
|
|
27670
|
+
}
|
|
27671
|
+
function countVectorOperators(content) {
|
|
27672
|
+
let ops = 0;
|
|
27673
|
+
let curves = 0;
|
|
27674
|
+
let fills = 0;
|
|
27675
|
+
let i = 0;
|
|
27676
|
+
const n = content.length;
|
|
27677
|
+
while (i < n) {
|
|
27678
|
+
const ch = content[i];
|
|
27679
|
+
if (ch === "(") {
|
|
27680
|
+
let depth = 1;
|
|
27681
|
+
i++;
|
|
27682
|
+
while (i < n && depth > 0) {
|
|
27683
|
+
if (content[i] === "\\")
|
|
27684
|
+
i++;
|
|
27685
|
+
else if (content[i] === "(")
|
|
27686
|
+
depth++;
|
|
27687
|
+
else if (content[i] === ")")
|
|
27688
|
+
depth--;
|
|
27689
|
+
i++;
|
|
27690
|
+
}
|
|
27691
|
+
continue;
|
|
27692
|
+
}
|
|
27693
|
+
if (ch === "<" && content[i + 1] !== "<") {
|
|
27694
|
+
while (i < n && content[i] !== ">")
|
|
27695
|
+
i++;
|
|
27696
|
+
i++;
|
|
27697
|
+
continue;
|
|
27698
|
+
}
|
|
27699
|
+
if (ch === "I" && content[i + 1] === "D" && isDelimiter(content[i + 2])) {
|
|
27700
|
+
const end = content.indexOf("EI", i + 2);
|
|
27701
|
+
i = end === -1 ? n : end + 2;
|
|
27702
|
+
continue;
|
|
27703
|
+
}
|
|
27704
|
+
if (!isDelimiter(ch)) {
|
|
27705
|
+
const start = i;
|
|
27706
|
+
while (i < n && !isDelimiter(content[i]))
|
|
27707
|
+
i++;
|
|
27708
|
+
const token = content.slice(start, i);
|
|
27709
|
+
switch (token) {
|
|
27710
|
+
case "m":
|
|
27711
|
+
case "l":
|
|
27712
|
+
case "re":
|
|
27713
|
+
ops++;
|
|
27714
|
+
break;
|
|
27715
|
+
case "c":
|
|
27716
|
+
case "v":
|
|
27717
|
+
case "y":
|
|
27718
|
+
ops++;
|
|
27719
|
+
curves++;
|
|
27720
|
+
break;
|
|
27721
|
+
case "f":
|
|
27722
|
+
case "F":
|
|
27723
|
+
case "f*":
|
|
27724
|
+
fills++;
|
|
27725
|
+
break;
|
|
27726
|
+
case "B":
|
|
27727
|
+
case "B*":
|
|
27728
|
+
case "b":
|
|
27729
|
+
case "b*":
|
|
27730
|
+
fills++;
|
|
27731
|
+
break;
|
|
27732
|
+
default:
|
|
27733
|
+
break;
|
|
27734
|
+
}
|
|
27735
|
+
continue;
|
|
27736
|
+
}
|
|
27737
|
+
i++;
|
|
27738
|
+
}
|
|
27739
|
+
return { ops, curves, fills };
|
|
27740
|
+
}
|
|
27741
|
+
function isDelimiter(ch) {
|
|
27742
|
+
return ch === void 0 || ch === " " || ch === "\n" || ch === "\r" || ch === " " || ch === "\f" || ch === "\0" || ch === "[" || ch === "]" || ch === "/" || ch === "{" || ch === "}";
|
|
27743
|
+
}
|
|
27744
|
+
function pageContentStream(page) {
|
|
27745
|
+
try {
|
|
27746
|
+
const obj = page.getObject?.();
|
|
27747
|
+
const contents = obj?.get?.("Contents");
|
|
27748
|
+
if (!contents)
|
|
27749
|
+
return "";
|
|
27750
|
+
const streams = contents.isArray?.() && contents.get ? Array.from({ length: contents.length ?? 0 }, (_, i) => contents.get(i)) : [contents];
|
|
27751
|
+
let out = "";
|
|
27752
|
+
for (const stream of streams) {
|
|
27753
|
+
const buf = stream?.readStream?.();
|
|
27754
|
+
const bytes = buf?.asUint8Array?.();
|
|
27755
|
+
if (bytes)
|
|
27756
|
+
out += Buffer.from(bytes).toString("latin1");
|
|
27757
|
+
}
|
|
27758
|
+
return out;
|
|
27759
|
+
} catch {
|
|
27760
|
+
return "";
|
|
27761
|
+
}
|
|
27762
|
+
}
|
|
27763
|
+
function pageSignalsFrom(page, pageNumber) {
|
|
27764
|
+
const json = JSON.parse(page.toStructuredText("preserve-images").asJSON());
|
|
27765
|
+
const blocks = json.blocks ?? [];
|
|
27766
|
+
const text = blocks.flatMap((b) => (b.lines ?? []).map((l) => l.text ?? "")).join(" ");
|
|
27767
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27768
|
+
const pageArea = Math.max((x1 - x0) * (y1 - y0), 1);
|
|
27769
|
+
const imgArea = blocks.filter((b) => b.type === "image").reduce((a, b) => a + Math.max((b.bbox?.w ?? 0) * (b.bbox?.h ?? 0), 0), 0);
|
|
27770
|
+
const vector = countVectorOperators(pageContentStream(page));
|
|
27771
|
+
return {
|
|
27772
|
+
page: pageNumber,
|
|
27773
|
+
textChars: text.trim().length,
|
|
27774
|
+
imageCoverage: Math.min(imgArea / pageArea, 1),
|
|
27775
|
+
vectorOps: vector.ops,
|
|
27776
|
+
vectorCurves: vector.curves,
|
|
27777
|
+
vectorFills: vector.fills,
|
|
27778
|
+
mathHint: textMathHint(text),
|
|
27779
|
+
tableHint: textTableHint(text)
|
|
27780
|
+
};
|
|
27781
|
+
}
|
|
27782
|
+
async function scanPdfSignals(bytes) {
|
|
27783
|
+
const { signals } = await analyzePdfDocument(bytes, { text: false, signals: true });
|
|
27784
|
+
return signals;
|
|
27785
|
+
}
|
|
27786
|
+
function median(nums) {
|
|
27787
|
+
if (!nums.length)
|
|
27788
|
+
return 0;
|
|
27789
|
+
const s = [...nums].sort((a, b) => a - b);
|
|
27790
|
+
return s[Math.floor(s.length / 2)];
|
|
27791
|
+
}
|
|
27792
|
+
async function extractPdfTables(bytes, pageNum, minColumns = 2) {
|
|
27793
|
+
const { doc } = await openDoc(bytes);
|
|
27794
|
+
const total = doc.countPages();
|
|
27795
|
+
const from = pageNum ? pageNum : 1;
|
|
27796
|
+
const to = pageNum ? pageNum : total;
|
|
27797
|
+
const out = [];
|
|
27798
|
+
for (let p = from; p <= to; p++) {
|
|
27799
|
+
if (p < 1 || p > total)
|
|
27800
|
+
continue;
|
|
27801
|
+
const json = JSON.parse(doc.loadPage(p - 1).toStructuredText().asJSON());
|
|
27802
|
+
const cells = [];
|
|
27803
|
+
for (const b of json.blocks ?? []) {
|
|
27804
|
+
if (b.type === "image")
|
|
27805
|
+
continue;
|
|
27806
|
+
for (const l of b.lines ?? []) {
|
|
27807
|
+
const text = (l.text ?? "").trim();
|
|
27808
|
+
if (!text)
|
|
27809
|
+
continue;
|
|
27810
|
+
cells.push({ text, x: l.bbox?.x ?? 0, y: l.bbox?.y ?? 0, h: l.bbox?.h ?? 12 });
|
|
27811
|
+
}
|
|
27812
|
+
}
|
|
27813
|
+
if (cells.length < 2)
|
|
27814
|
+
continue;
|
|
27815
|
+
const medH = median(cells.map((c) => c.h)) || 12;
|
|
27816
|
+
const rowTol = Math.max(4, medH * 0.6);
|
|
27817
|
+
const colTol = Math.max(6, medH * 0.9);
|
|
27818
|
+
const byY = [...cells].sort((a, b) => a.y - b.y);
|
|
27819
|
+
const rowGroups = [];
|
|
27820
|
+
let rowRef = Number.NEGATIVE_INFINITY;
|
|
27821
|
+
for (const c of byY) {
|
|
27822
|
+
if (c.y - rowRef > rowTol) {
|
|
27823
|
+
rowGroups.push([]);
|
|
27824
|
+
rowRef = c.y;
|
|
27825
|
+
}
|
|
27826
|
+
rowGroups[rowGroups.length - 1].push(c);
|
|
27827
|
+
}
|
|
27828
|
+
const colRefs = [];
|
|
27829
|
+
for (const x of cells.map((c) => c.x).sort((a, b) => a - b)) {
|
|
27830
|
+
if (!colRefs.length || x - colRefs[colRefs.length - 1] > colTol)
|
|
27831
|
+
colRefs.push(x);
|
|
27832
|
+
}
|
|
27833
|
+
const colIndex = (x) => {
|
|
27834
|
+
let best = 0, bd = Infinity;
|
|
27835
|
+
colRefs.forEach((r, i) => {
|
|
27836
|
+
const d = Math.abs(x - r);
|
|
27837
|
+
if (d < bd) {
|
|
27838
|
+
bd = d;
|
|
27839
|
+
best = i;
|
|
27840
|
+
}
|
|
27841
|
+
});
|
|
27842
|
+
return best;
|
|
27843
|
+
};
|
|
27844
|
+
const rows = [];
|
|
27845
|
+
for (const group of rowGroups) {
|
|
27846
|
+
const arr = Array(colRefs.length).fill("");
|
|
27847
|
+
for (const c of group.sort((a, b) => a.x - b.x)) {
|
|
27848
|
+
const i = colIndex(c.x);
|
|
27849
|
+
arr[i] = arr[i] ? `${arr[i]} ${c.text}` : c.text;
|
|
27850
|
+
}
|
|
27851
|
+
if (arr.filter((v) => v).length >= minColumns)
|
|
27852
|
+
rows.push(arr);
|
|
27853
|
+
}
|
|
27854
|
+
if (rows.length)
|
|
27855
|
+
out.push({ page: p, rows });
|
|
27856
|
+
}
|
|
27857
|
+
return out;
|
|
27858
|
+
}
|
|
27859
|
+
async function extractPdfTextLines(bytes) {
|
|
27860
|
+
const { doc } = await openDoc(bytes);
|
|
27861
|
+
const out = [];
|
|
27862
|
+
for (let p = 1; p <= doc.countPages(); p++) {
|
|
27863
|
+
const page = doc.loadPage(p - 1);
|
|
27864
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27865
|
+
const json = JSON.parse(page.toStructuredText().asJSON());
|
|
27866
|
+
const lines = [];
|
|
27867
|
+
for (const b of json.blocks ?? []) {
|
|
27868
|
+
if (b.type === "image")
|
|
27869
|
+
continue;
|
|
27870
|
+
for (const l of b.lines ?? []) {
|
|
27871
|
+
const text = l.text ?? "";
|
|
27872
|
+
if (!text.trim() || !l.bbox)
|
|
27873
|
+
continue;
|
|
27874
|
+
lines.push({
|
|
27875
|
+
x: l.bbox.x ?? 0,
|
|
27876
|
+
y: l.bbox.y ?? 0,
|
|
27877
|
+
w: l.bbox.w ?? 0,
|
|
27878
|
+
h: l.bbox.h ?? 0,
|
|
27879
|
+
size: l.font?.size ?? 12,
|
|
27880
|
+
text
|
|
27881
|
+
});
|
|
27882
|
+
}
|
|
27883
|
+
}
|
|
27884
|
+
out.push({ page: p, width: x1 - x0, height: y1 - y0, lines });
|
|
27885
|
+
}
|
|
27886
|
+
return out;
|
|
27887
|
+
}
|
|
27888
|
+
async function renderPdfPages(bytes, pages, scale = 2, maxEdge) {
|
|
27889
|
+
const { mupdf, doc } = await openDoc(bytes);
|
|
27890
|
+
const total = doc.countPages();
|
|
27891
|
+
const wanted = pages === "all" ? Array.from({ length: total }, (_, i) => i + 1) : pages;
|
|
27892
|
+
const out = [];
|
|
27893
|
+
for (const p of wanted) {
|
|
27894
|
+
if (p < 1 || p > total)
|
|
27895
|
+
continue;
|
|
27896
|
+
const page = doc.loadPage(p - 1);
|
|
27897
|
+
const [x0, y0, x1, y1] = page.getBounds();
|
|
27898
|
+
const longest = Math.max(x1 - x0, y1 - y0);
|
|
27899
|
+
const s = maxEdge && longest > 0 ? Math.min(scale, maxEdge / longest) : scale;
|
|
27900
|
+
const pix = page.toPixmap(mupdf.Matrix.scale(s, s), mupdf.ColorSpace.DeviceRGB, false, true);
|
|
27901
|
+
out.push({ page: p, png: pix.asPNG() });
|
|
27902
|
+
}
|
|
27903
|
+
return out;
|
|
27904
|
+
}
|
|
27905
|
+
async function pngPixels(png) {
|
|
27906
|
+
const mupdf = await loadMupdf();
|
|
27907
|
+
const pix = new mupdf.Image(png).toPixmap();
|
|
27908
|
+
const width = pix.getWidth(), height = pix.getHeight();
|
|
27909
|
+
const channels = pix.getStride() / width;
|
|
27910
|
+
return { width, height, channels, data: pix.getPixels() };
|
|
27911
|
+
}
|
|
27912
|
+
async function extractPdfImages(bytes, pages) {
|
|
27913
|
+
const { doc } = await openDoc(bytes);
|
|
27914
|
+
const total = doc.countPages();
|
|
27915
|
+
const wanted = pages && pages.length ? pages : Array.from({ length: total }, (_, i) => i + 1);
|
|
27916
|
+
const images = [];
|
|
27917
|
+
let skipped = 0;
|
|
27918
|
+
for (const p of wanted) {
|
|
27919
|
+
if (p < 1 || p > total)
|
|
27920
|
+
continue;
|
|
27921
|
+
const stext = doc.loadPage(p - 1).toStructuredText("preserve-images");
|
|
27922
|
+
let index = 0;
|
|
27923
|
+
stext.walk({
|
|
27924
|
+
onImageBlock(bbox, _matrix, image) {
|
|
27925
|
+
const slot = index++;
|
|
27926
|
+
try {
|
|
27927
|
+
const png = image.toPixmap().asPNG();
|
|
27928
|
+
images.push({
|
|
27929
|
+
page: p,
|
|
27930
|
+
index: slot,
|
|
27931
|
+
bbox: [bbox[0], bbox[1], bbox[2], bbox[3]],
|
|
27932
|
+
width: image.getWidth(),
|
|
27933
|
+
height: image.getHeight(),
|
|
27934
|
+
png
|
|
27935
|
+
});
|
|
27936
|
+
} catch {
|
|
27937
|
+
skipped++;
|
|
27938
|
+
}
|
|
27939
|
+
}
|
|
27940
|
+
});
|
|
27941
|
+
}
|
|
27942
|
+
return { images, skipped };
|
|
27943
|
+
}
|
|
27944
|
+
async function encryptPdf(bytes, userPassword, ownerPassword) {
|
|
27945
|
+
if (!userPassword)
|
|
27946
|
+
throw new Error("a non-empty user password is required");
|
|
27947
|
+
const { doc } = await openDoc(bytes);
|
|
27948
|
+
const opts = `encrypt=aes-256,user-password=${userPassword},owner-password=${ownerPassword || userPassword}`;
|
|
27949
|
+
return doc.saveToBuffer(opts).asUint8Array();
|
|
27950
|
+
}
|
|
27951
|
+
async function pdfNeedsPassword(bytes) {
|
|
27952
|
+
const mupdf = await loadMupdf();
|
|
27953
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27954
|
+
return doc.needsPassword();
|
|
27955
|
+
}
|
|
27956
|
+
async function decryptPdf(bytes, password) {
|
|
27957
|
+
const mupdf = await loadMupdf();
|
|
27958
|
+
const doc = mupdf.Document.openDocument(bytes, "application/pdf");
|
|
27959
|
+
if (doc.needsPassword()) {
|
|
27960
|
+
if (doc.authenticatePassword(password) === 0)
|
|
27961
|
+
throw new Error("wrong password");
|
|
27962
|
+
}
|
|
27963
|
+
if (doc.countPages() === 0)
|
|
27964
|
+
throw new Error("PDF has no readable pages (corrupt or empty)");
|
|
27965
|
+
return doc.saveToBuffer("encrypt=none").asUint8Array();
|
|
27966
|
+
}
|
|
27967
|
+
var import_node_path, import_node_url, _mupdf, MATH_CHARS;
|
|
27968
|
+
var init_pdf_extract = __esm({
|
|
27969
|
+
"packages/core/dist/document/pdf-extract.js"() {
|
|
27970
|
+
"use strict";
|
|
27971
|
+
import_node_path = require("node:path");
|
|
27972
|
+
import_node_url = require("node:url");
|
|
27973
|
+
init_pdf_encrypted();
|
|
27974
|
+
_mupdf = null;
|
|
27975
|
+
MATH_CHARS = /[=∑∫√≤≥≠±∞πθλμσΩ×÷∂∇→←↔∈∉⊂⊃∀∃]/g;
|
|
27976
|
+
}
|
|
27977
|
+
});
|
|
27978
|
+
|
|
27584
27979
|
// apps/admin/data/skills.lib/document/bin-src/doc-validate.mjs
|
|
27585
27980
|
var import_promises2 = require("node:fs/promises");
|
|
27586
|
-
var
|
|
27981
|
+
var import_node_path3 = require("node:path");
|
|
27587
27982
|
|
|
27588
27983
|
// packages/core/dist/document/doc-validate.js
|
|
27589
|
-
var
|
|
27984
|
+
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
27590
27985
|
var import_pdf_lib = __toESM(require_cjs(), 1);
|
|
27986
|
+
|
|
27987
|
+
// packages/core/dist/document/pptx-notes.js
|
|
27988
|
+
var import_jszip = __toESM(require_lib3(), 1);
|
|
27989
|
+
|
|
27990
|
+
// packages/core/dist/document/pptx-parts.js
|
|
27991
|
+
var SLIDE_PART = /^ppt\/slides\/(slide\d+\.xml)$/;
|
|
27992
|
+
function relsPathFor(path) {
|
|
27993
|
+
return path.replace(SLIDE_PART, "ppt/slides/_rels/$1.rels");
|
|
27994
|
+
}
|
|
27995
|
+
function attr(el, name) {
|
|
27996
|
+
return new RegExp(`\\b${name}="([^"]*)"`).exec(el)?.[1];
|
|
27997
|
+
}
|
|
27998
|
+
async function slidePartsInOrder(zip) {
|
|
27999
|
+
const byNumber = Object.keys(zip.files).filter((p) => SLIDE_PART.test(p)).sort((a, b) => Number(/(\d+)\.xml$/.exec(a)[1]) - Number(/(\d+)\.xml$/.exec(b)[1]));
|
|
28000
|
+
const toParts = (paths) => paths.map((path, i) => ({ position: i + 1, path, relsPath: relsPathFor(path) }));
|
|
28001
|
+
const pres = await zip.file("ppt/presentation.xml")?.async("string");
|
|
28002
|
+
const rels = await zip.file("ppt/_rels/presentation.xml.rels")?.async("string");
|
|
28003
|
+
if (!pres || !rels)
|
|
28004
|
+
return toParts(byNumber);
|
|
28005
|
+
const targetById = /* @__PURE__ */ new Map();
|
|
28006
|
+
for (const [el] of rels.matchAll(/<Relationship\b[^>]*>/g)) {
|
|
28007
|
+
const id = attr(el, "Id");
|
|
28008
|
+
const target = attr(el, "Target");
|
|
28009
|
+
if (!id || !target)
|
|
28010
|
+
continue;
|
|
28011
|
+
targetById.set(id, target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.\//, "")}`);
|
|
28012
|
+
}
|
|
28013
|
+
const list = /<p:sldIdLst\b[^>]*>([\s\S]*?)<\/p:sldIdLst>/.exec(pres)?.[1] ?? "";
|
|
28014
|
+
const ordered = [...list.matchAll(/<p:sldId\b[^>]*>/g)].map(([el]) => targetById.get(attr(el, "r:id") ?? "")).filter((p) => p !== void 0 && SLIDE_PART.test(p) && zip.file(p) !== null);
|
|
28015
|
+
const complete = ordered.length === byNumber.length && new Set(ordered).size === ordered.length;
|
|
28016
|
+
return toParts(complete ? ordered : byNumber);
|
|
28017
|
+
}
|
|
28018
|
+
|
|
28019
|
+
// packages/core/dist/document/pptx-notes.js
|
|
28020
|
+
var REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
28021
|
+
var NOTES_SLIDE_REL = `${REL_NS}/notesSlide`;
|
|
28022
|
+
var NOTES_MASTER_REL = `${REL_NS}/notesMaster`;
|
|
28023
|
+
var SLIDE_REL = `${REL_NS}/slide`;
|
|
28024
|
+
async function notesKeynoteIssues(zip) {
|
|
28025
|
+
const out = [];
|
|
28026
|
+
for (const p of Object.keys(zip.files).filter((p2) => /^ppt\/notesSlides\/notesSlide\d+\.xml$/.test(p2)).sort()) {
|
|
28027
|
+
const xml = await zip.file(p).async("string");
|
|
28028
|
+
if (!/<p:clrMapOvr\b/.test(xml))
|
|
28029
|
+
out.push(p);
|
|
28030
|
+
}
|
|
28031
|
+
return out;
|
|
28032
|
+
}
|
|
28033
|
+
async function unlistedNotesMaster(zip) {
|
|
28034
|
+
const relsFile = zip.file("ppt/_rels/presentation.xml.rels");
|
|
28035
|
+
const presFile = zip.file("ppt/presentation.xml");
|
|
28036
|
+
if (!relsFile || !presFile)
|
|
28037
|
+
return null;
|
|
28038
|
+
const rel = new RegExp(`<Relationship\\b[^>]*Type="${NOTES_MASTER_REL}"[^>]*>`).exec(await relsFile.async("string"))?.[0];
|
|
28039
|
+
const rid = rel ? /\bId="([^"]+)"/.exec(rel)?.[1] : void 0;
|
|
28040
|
+
if (!rid)
|
|
28041
|
+
return null;
|
|
28042
|
+
return /<p:notesMasterId\b/.test(await presFile.async("string")) ? null : rid;
|
|
28043
|
+
}
|
|
28044
|
+
|
|
28045
|
+
// packages/core/dist/document/docx-defaults.js
|
|
28046
|
+
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
28047
|
+
function hasDefaultParagraphStyle(stylesXml) {
|
|
28048
|
+
return [...stylesXml.matchAll(/<w:style\b[^>]*>/g)].some(([tag]) => /\bw:type="paragraph"/.test(tag) && /\bw:default="(1|true|on)"/.test(tag));
|
|
28049
|
+
}
|
|
28050
|
+
|
|
28051
|
+
// packages/core/dist/document/doc-validate.js
|
|
27591
28052
|
function looksLikePdf(bytes) {
|
|
27592
28053
|
const head = Buffer.from(bytes.subarray(0, 1024)).toString("latin1");
|
|
27593
28054
|
return head.includes("%PDF-");
|
|
@@ -27599,6 +28060,17 @@ async function validatePdf(bytes) {
|
|
|
27599
28060
|
try {
|
|
27600
28061
|
const doc = await import_pdf_lib.PDFDocument.load(bytes, { ignoreEncryption: true, throwOnInvalidObject: true, updateMetadata: false });
|
|
27601
28062
|
pages = doc.getPageCount();
|
|
28063
|
+
if (doc.isEncrypted) {
|
|
28064
|
+
const { pdfNeedsPassword: pdfNeedsPassword2 } = await Promise.resolve().then(() => (init_pdf_extract(), pdf_extract_exports));
|
|
28065
|
+
if (await pdfNeedsPassword2(bytes).catch(() => true)) {
|
|
28066
|
+
errors.push({
|
|
28067
|
+
code: "pdf-encrypted",
|
|
28068
|
+
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"
|
|
28069
|
+
});
|
|
28070
|
+
return { ok: false, kind: "pdf", errors, warnings, pages };
|
|
28071
|
+
}
|
|
28072
|
+
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>`' });
|
|
28073
|
+
}
|
|
27602
28074
|
if (pages === 0)
|
|
27603
28075
|
errors.push({ code: "pdf-no-pages", message: "the PDF parses but has no pages" });
|
|
27604
28076
|
else
|
|
@@ -27756,7 +28228,7 @@ async function validateOoxml(bytes) {
|
|
|
27756
28228
|
const result = (kind) => ({ ok: errors.length === 0, kind, errors, warnings });
|
|
27757
28229
|
let zip;
|
|
27758
28230
|
try {
|
|
27759
|
-
zip = await
|
|
28231
|
+
zip = await import_jszip3.default.loadAsync(Buffer.from(bytes));
|
|
27760
28232
|
} catch (e) {
|
|
27761
28233
|
errors.push({ code: "zip-unreadable", message: `not a readable OOXML zip: ${e.message}` });
|
|
27762
28234
|
return result("unknown");
|
|
@@ -27824,19 +28296,43 @@ async function validateOoxml(bytes) {
|
|
|
27824
28296
|
}
|
|
27825
28297
|
}
|
|
27826
28298
|
}
|
|
28299
|
+
if (root.kind === "pptx") {
|
|
28300
|
+
const bad = await notesKeynoteIssues(zip);
|
|
28301
|
+
if (bad.length) {
|
|
28302
|
+
errors.push({
|
|
28303
|
+
code: "notes-keynote",
|
|
28304
|
+
part: bad[0],
|
|
28305
|
+
message: `${bad.length} notes slide(s) lack <p:clrMapOvr> (${bad.slice(0, 3).join(", ")}${bad.length > 3 ? ", \u2026" : ""}) \u2014 Keynote refuses the whole file ("\u6587\u4EF6\u683C\u5F0F\u65E0\u6548"). Repair with \`pptx-notes <in.pptx> {} <out.pptx>\`; write notes with pptx-notes, never by hand`
|
|
28306
|
+
});
|
|
28307
|
+
}
|
|
28308
|
+
}
|
|
28309
|
+
if (root.kind === "pptx" && await unlistedNotesMaster(zip)) {
|
|
28310
|
+
errors.push({
|
|
28311
|
+
code: "notes-keynote",
|
|
28312
|
+
part: "ppt/presentation.xml",
|
|
28313
|
+
message: 'the notes master is not listed in <p:notesMasterIdLst> (python-pptx leaves it out) \u2014 Keynote refuses the whole file ("\u6587\u4EF6\u683C\u5F0F\u65E0\u6548"). Repair with `pptx-notes <in.pptx> {} <out.pptx>`'
|
|
28314
|
+
});
|
|
28315
|
+
}
|
|
28316
|
+
if (root.kind === "docx" && has("word/styles.xml") && !hasDefaultParagraphStyle(await text("word/styles.xml"))) {
|
|
28317
|
+
errors.push({
|
|
28318
|
+
code: "docx-default-style",
|
|
28319
|
+
part: "word/styles.xml",
|
|
28320
|
+
message: 'no default paragraph style (w:default="1", usually "Normal") \u2014 Word fills the gap, but Pages lays every unstyled paragraph out like the one before it (a centred title turns the whole body huge and centred). Repair with `docx-defaults <in.docx> <out.docx>`; docx from doc-gen already has it'
|
|
28321
|
+
});
|
|
28322
|
+
}
|
|
27827
28323
|
return result(root.kind);
|
|
27828
28324
|
}
|
|
27829
28325
|
|
|
27830
28326
|
// packages/core/dist/document/doc-xsd.js
|
|
27831
|
-
var
|
|
27832
|
-
var
|
|
28327
|
+
var import_node_path2 = require("node:path");
|
|
28328
|
+
var import_node_url2 = require("node:url");
|
|
27833
28329
|
var import_promises = require("node:fs/promises");
|
|
27834
|
-
var
|
|
28330
|
+
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
27835
28331
|
var _xmllint = null;
|
|
27836
28332
|
function loadXmllint() {
|
|
27837
28333
|
if (!_xmllint) {
|
|
27838
28334
|
const deps = process.env.TALOS_SKILL_DEPS;
|
|
27839
|
-
const spec = deps ? (0,
|
|
28335
|
+
const spec = deps ? (0, import_node_url2.pathToFileURL)((0, import_node_path2.join)(deps, "xmllint-wasm", "index-node.js")).href : "xmllint-wasm";
|
|
27840
28336
|
_xmllint = import(spec);
|
|
27841
28337
|
}
|
|
27842
28338
|
return _xmllint;
|
|
@@ -27879,7 +28375,7 @@ async function loadSchemaSet(schemaDir) {
|
|
|
27879
28375
|
if (!files.length)
|
|
27880
28376
|
throw new Error(`no .xsd schemas found in ${schemaDir}`);
|
|
27881
28377
|
const set = /* @__PURE__ */ new Map();
|
|
27882
|
-
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises.readFile)((0,
|
|
28378
|
+
await Promise.all(files.map(async (f) => set.set(f, await (0, import_promises.readFile)((0, import_node_path2.join)(schemaDir, f), "utf8"))));
|
|
27883
28379
|
return set;
|
|
27884
28380
|
}
|
|
27885
28381
|
function errText(e) {
|
|
@@ -27887,7 +28383,7 @@ function errText(e) {
|
|
|
27887
28383
|
}
|
|
27888
28384
|
async function validateOoxmlStrict(bytes, schemaDir) {
|
|
27889
28385
|
const [{ validateXML }, schemas] = await Promise.all([loadXmllint(), loadSchemaSet(schemaDir)]);
|
|
27890
|
-
const zip = await
|
|
28386
|
+
const zip = await import_jszip4.default.loadAsync(Buffer.from(bytes));
|
|
27891
28387
|
const checked = [];
|
|
27892
28388
|
const skipped = [];
|
|
27893
28389
|
const errors = [];
|
|
@@ -27930,7 +28426,7 @@ async function validateOoxmlStrict(bytes, schemaDir) {
|
|
|
27930
28426
|
}
|
|
27931
28427
|
|
|
27932
28428
|
// packages/core/dist/document/doc-design-lint.js
|
|
27933
|
-
var
|
|
28429
|
+
var import_jszip5 = __toESM(require_lib3(), 1);
|
|
27934
28430
|
|
|
27935
28431
|
// packages/core/dist/document/ooxml-util.js
|
|
27936
28432
|
function unescXml(s) {
|
|
@@ -27954,34 +28450,125 @@ function unescXml(s) {
|
|
|
27954
28450
|
});
|
|
27955
28451
|
}
|
|
27956
28452
|
|
|
27957
|
-
// packages/core/dist/document/
|
|
27958
|
-
var
|
|
27959
|
-
|
|
27960
|
-
|
|
28453
|
+
// packages/core/dist/document/png-decode.js
|
|
28454
|
+
var import_node_zlib = require("node:zlib");
|
|
28455
|
+
var SIG = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
28456
|
+
var CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
|
|
28457
|
+
function decodePng(bytes) {
|
|
28458
|
+
try {
|
|
28459
|
+
if (bytes.length < 33 || SIG.some((b, i) => bytes[i] !== b))
|
|
28460
|
+
return void 0;
|
|
28461
|
+
const buf = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
28462
|
+
let width = 0, height = 0, depth = 0, type = -1, interlace = 0;
|
|
28463
|
+
let palette;
|
|
28464
|
+
const idat = [];
|
|
28465
|
+
for (let p = 8; p + 8 <= buf.length; ) {
|
|
28466
|
+
const len = buf.readUInt32BE(p);
|
|
28467
|
+
const kind = buf.toString("latin1", p + 4, p + 8);
|
|
28468
|
+
const data = buf.subarray(p + 8, p + 8 + len);
|
|
28469
|
+
if (kind === "IHDR") {
|
|
28470
|
+
width = data.readUInt32BE(0);
|
|
28471
|
+
height = data.readUInt32BE(4);
|
|
28472
|
+
depth = data[8];
|
|
28473
|
+
type = data[9];
|
|
28474
|
+
interlace = data[12];
|
|
28475
|
+
} else if (kind === "PLTE")
|
|
28476
|
+
palette = data;
|
|
28477
|
+
else if (kind === "IDAT")
|
|
28478
|
+
idat.push(data);
|
|
28479
|
+
else if (kind === "IEND")
|
|
28480
|
+
break;
|
|
28481
|
+
p += 12 + len;
|
|
28482
|
+
}
|
|
28483
|
+
const ch = CHANNELS[type];
|
|
28484
|
+
if (!width || !height || depth !== 8 || interlace !== 0 || ch === void 0 || type === 3 && !palette)
|
|
28485
|
+
return void 0;
|
|
28486
|
+
const raw = (0, import_node_zlib.inflateSync)(Buffer.concat(idat));
|
|
28487
|
+
const stride = width * ch;
|
|
28488
|
+
if (raw.length < height * (stride + 1))
|
|
28489
|
+
return void 0;
|
|
28490
|
+
const cur = new Uint8Array(stride);
|
|
28491
|
+
const prev = new Uint8Array(stride);
|
|
28492
|
+
const rgb = new Uint8Array(width * height * 3);
|
|
28493
|
+
for (let y = 0; y < height; y++) {
|
|
28494
|
+
const off = y * (stride + 1);
|
|
28495
|
+
const filter = raw[off];
|
|
28496
|
+
for (let i = 0; i < stride; i++) {
|
|
28497
|
+
const x = raw[off + 1 + i];
|
|
28498
|
+
const a = i >= ch ? cur[i - ch] : 0;
|
|
28499
|
+
const b = prev[i];
|
|
28500
|
+
const c = i >= ch ? prev[i - ch] : 0;
|
|
28501
|
+
let v;
|
|
28502
|
+
switch (filter) {
|
|
28503
|
+
case 0:
|
|
28504
|
+
v = x;
|
|
28505
|
+
break;
|
|
28506
|
+
case 1:
|
|
28507
|
+
v = x + a;
|
|
28508
|
+
break;
|
|
28509
|
+
case 2:
|
|
28510
|
+
v = x + b;
|
|
28511
|
+
break;
|
|
28512
|
+
case 3:
|
|
28513
|
+
v = x + (a + b >> 1);
|
|
28514
|
+
break;
|
|
28515
|
+
case 4: {
|
|
28516
|
+
const pa = Math.abs(b - c), pb = Math.abs(a - c), pc = Math.abs(a + b - 2 * c);
|
|
28517
|
+
v = x + (pa <= pb && pa <= pc ? a : pb <= pc ? b : c);
|
|
28518
|
+
break;
|
|
28519
|
+
}
|
|
28520
|
+
default:
|
|
28521
|
+
return void 0;
|
|
28522
|
+
}
|
|
28523
|
+
cur[i] = v & 255;
|
|
28524
|
+
}
|
|
28525
|
+
for (let px = 0; px < width; px++) {
|
|
28526
|
+
const o = (y * width + px) * 3;
|
|
28527
|
+
const s = px * ch;
|
|
28528
|
+
if (type === 3) {
|
|
28529
|
+
const k = cur[s] * 3;
|
|
28530
|
+
rgb[o] = palette[k];
|
|
28531
|
+
rgb[o + 1] = palette[k + 1];
|
|
28532
|
+
rgb[o + 2] = palette[k + 2];
|
|
28533
|
+
} else if (ch <= 2) {
|
|
28534
|
+
rgb[o] = rgb[o + 1] = rgb[o + 2] = cur[s];
|
|
28535
|
+
} else {
|
|
28536
|
+
rgb[o] = cur[s];
|
|
28537
|
+
rgb[o + 1] = cur[s + 1];
|
|
28538
|
+
rgb[o + 2] = cur[s + 2];
|
|
28539
|
+
}
|
|
28540
|
+
}
|
|
28541
|
+
prev.set(cur);
|
|
28542
|
+
}
|
|
28543
|
+
return { width, height, rgb };
|
|
28544
|
+
} catch {
|
|
28545
|
+
return void 0;
|
|
28546
|
+
}
|
|
27961
28547
|
}
|
|
27962
|
-
|
|
27963
|
-
|
|
28548
|
+
|
|
28549
|
+
// packages/core/dist/document/readable-type.js
|
|
28550
|
+
var MIN_FONT_PT = 10;
|
|
28551
|
+
var LABEL_MAX_CHARS = 24;
|
|
28552
|
+
var MIN_LABEL_FONT_PT = 8;
|
|
28553
|
+
function readableFloorPt(text) {
|
|
28554
|
+
return [...text.trim()].length <= LABEL_MAX_CHARS ? MIN_LABEL_FONT_PT : MIN_FONT_PT;
|
|
27964
28555
|
}
|
|
27965
|
-
|
|
27966
|
-
|
|
27967
|
-
|
|
27968
|
-
|
|
27969
|
-
|
|
27970
|
-
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
27976
|
-
|
|
27977
|
-
|
|
27978
|
-
|
|
27979
|
-
}
|
|
27980
|
-
const list = /<p:sldIdLst\b[^>]*>([\s\S]*?)<\/p:sldIdLst>/.exec(pres)?.[1] ?? "";
|
|
27981
|
-
const ordered = [...list.matchAll(/<p:sldId\b[^>]*>/g)].map(([el]) => targetById.get(attr(el, "r:id") ?? "")).filter((p) => p !== void 0 && SLIDE_PART.test(p) && zip.file(p) !== null);
|
|
27982
|
-
const complete = ordered.length === byNumber.length && new Set(ordered).size === ordered.length;
|
|
27983
|
-
return toParts(complete ? ordered : byNumber);
|
|
28556
|
+
|
|
28557
|
+
// packages/core/dist/document/text-slack.js
|
|
28558
|
+
var SLACK_CJK_RE = /[\u3000-\u303F\u3400-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
28559
|
+
var PCT = { single: { cjk: 0.22, latin: 0.16 }, multi: { cjk: 0.1, latin: 0.06 } };
|
|
28560
|
+
var FLOOR_EM = { cjk: 1, latin: 0.45 };
|
|
28561
|
+
function params(single, cjk) {
|
|
28562
|
+
const script = cjk ? "cjk" : "latin";
|
|
28563
|
+
return { pct: PCT[single ? "single" : "multi"][script], floor: FLOOR_EM[script] * (single ? 1 : 0.5) };
|
|
28564
|
+
}
|
|
28565
|
+
function unpaddedWidth(padded, fontSize, single, cjk) {
|
|
28566
|
+
const { pct, floor } = params(single, cjk);
|
|
28567
|
+
const floorAbs = floor * fontSize;
|
|
28568
|
+
const viaPct = padded / (1 + pct);
|
|
28569
|
+
return viaPct * pct >= floorAbs ? viaPct : Math.max(0, padded - floorAbs);
|
|
27984
28570
|
}
|
|
28571
|
+
var CHART_TEXT_NAME = "talos-chart-text";
|
|
27985
28572
|
|
|
27986
28573
|
// packages/core/dist/document/doc-design-lint.js
|
|
27987
28574
|
var PLACEHOLDER_RE = /替换为|在这里写|占位|示例文本|Lorem ipsum/i;
|
|
@@ -28123,7 +28710,7 @@ function lintSlideXml(slideXml, slideNumber, masterBg, expectedLang = detectDeck
|
|
|
28123
28710
|
}
|
|
28124
28711
|
}
|
|
28125
28712
|
}
|
|
28126
|
-
if (worst && !worst.onShapeFill && anyVisualShape)
|
|
28713
|
+
if (worst && !worst.onShapeFill && (anyVisualShape || hasPic))
|
|
28127
28714
|
worst = void 0;
|
|
28128
28715
|
if (placeholder) {
|
|
28129
28716
|
const snippet = placeholder.length > 24 ? `${placeholder.slice(0, 24)}\u2026` : placeholder;
|
|
@@ -28162,8 +28749,9 @@ function lintSlideXml(slideXml, slideNumber, masterBg, expectedLang = detectDeck
|
|
|
28162
28749
|
var EMU_PER_INCH = 914400;
|
|
28163
28750
|
var DEFAULT_CANVAS = { w: 12192e3, h: 6858e3 };
|
|
28164
28751
|
var OFFCANVAS_TOL = EMU_PER_INCH / 10;
|
|
28165
|
-
var MIN_FONT_HUNDREDTHS =
|
|
28166
|
-
var
|
|
28752
|
+
var MIN_FONT_HUNDREDTHS = MIN_FONT_PT * 100;
|
|
28753
|
+
var CHART_TEXT_RE = new RegExp(`<p:cNvPr\\b[^>]*\\bname="${CHART_TEXT_NAME}\\b`);
|
|
28754
|
+
var OVERLAP_RATIO = 0.3;
|
|
28167
28755
|
function firstBox(block) {
|
|
28168
28756
|
const xf = /<(?:a|p):xfrm\b[^>]*>[\s\S]*?<\/(?:a|p):xfrm>/.exec(block);
|
|
28169
28757
|
const scope = xf ? xf[0] : block;
|
|
@@ -28181,6 +28769,51 @@ function overlapArea(a, b) {
|
|
|
28181
28769
|
const iy = Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
|
|
28182
28770
|
return ix * iy;
|
|
28183
28771
|
}
|
|
28772
|
+
var CJK_WIDE_RE = /[\u2E80-\u9FFF\uAC00-\uD7AF\uF900-\uFAFF\uFF00-\uFFEF]/;
|
|
28773
|
+
var EMU_PER_PT = 12700;
|
|
28774
|
+
function inkBox(block, box) {
|
|
28775
|
+
let maxSz = 0;
|
|
28776
|
+
let inkW = 0;
|
|
28777
|
+
let lines = 0;
|
|
28778
|
+
for (const para of block.match(/<a:p>[\s\S]*?<\/a:p>|<a:p\b[^>]*>[\s\S]*?<\/a:p>/g) ?? []) {
|
|
28779
|
+
let paraW = 0;
|
|
28780
|
+
let paraSz = 0;
|
|
28781
|
+
for (const run of para.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28782
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28783
|
+
if (raw === void 0)
|
|
28784
|
+
continue;
|
|
28785
|
+
const sz = +(/<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1] ?? 1800) / 100;
|
|
28786
|
+
paraSz = Math.max(paraSz, sz);
|
|
28787
|
+
for (const ch of unescXml(raw))
|
|
28788
|
+
paraW += (CJK_WIDE_RE.test(ch) ? 1 : ch === " " ? 0.3 : 0.55) * sz * EMU_PER_PT;
|
|
28789
|
+
}
|
|
28790
|
+
if (!paraW)
|
|
28791
|
+
continue;
|
|
28792
|
+
maxSz = Math.max(maxSz, paraSz);
|
|
28793
|
+
const paraLines = box.w > 0 ? Math.max(1, Math.ceil(paraW / box.w)) : 1;
|
|
28794
|
+
lines += paraLines;
|
|
28795
|
+
inkW = Math.max(inkW, Math.min(paraW, box.w));
|
|
28796
|
+
}
|
|
28797
|
+
if (!lines)
|
|
28798
|
+
return void 0;
|
|
28799
|
+
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));
|
|
28800
|
+
if (paras.length === 1 && maxSz > 0) {
|
|
28801
|
+
const lnSpc = /<a:lnSpc>\s*<a:spcPts\b[^>]*\bval="(\d+)"/.exec(block)?.[1];
|
|
28802
|
+
const lineH = (lnSpc ? +lnSpc / 100 : maxSz * 1.3) * EMU_PER_PT;
|
|
28803
|
+
const clamped = box.x + box.w >= DEFAULT_CANVAS.w - EMU_PER_PT;
|
|
28804
|
+
if (box.h <= lineH * 1.5 && !clamped) {
|
|
28805
|
+
const text = unescXml((block.match(/<a:t\b[^>]*>([\s\S]*?)<\/a:t>/g) ?? []).join(""));
|
|
28806
|
+
lines = 1;
|
|
28807
|
+
inkW = Math.min(inkW, unpaddedWidth(box.w, maxSz * EMU_PER_PT, true, SLACK_CJK_RE.test(text)));
|
|
28808
|
+
}
|
|
28809
|
+
}
|
|
28810
|
+
const inkH = Math.min(box.h, lines * maxSz * 1.2 * EMU_PER_PT);
|
|
28811
|
+
const algn = /<a:pPr\b[^>]*\balgn="(\w+)"/.exec(block)?.[1];
|
|
28812
|
+
const anchor = /<a:bodyPr\b[^>]*\banchor="(\w+)"/.exec(block)?.[1];
|
|
28813
|
+
const x = algn === "ctr" ? box.x + (box.w - inkW) / 2 : algn === "r" ? box.x + box.w - inkW : box.x;
|
|
28814
|
+
const y = anchor === "ctr" ? box.y + (box.h - inkH) / 2 : anchor === "b" ? box.y + box.h - inkH : box.y;
|
|
28815
|
+
return { x, y, w: inkW, h: inkH };
|
|
28816
|
+
}
|
|
28184
28817
|
function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
28185
28818
|
const findings = [];
|
|
28186
28819
|
const blocks = slideXml.match(/<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g) ?? [];
|
|
@@ -28204,16 +28837,23 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28204
28837
|
worstOff = { by, edge };
|
|
28205
28838
|
}
|
|
28206
28839
|
}
|
|
28207
|
-
if (hasText && box.w > 0 && box.h > 0)
|
|
28208
|
-
|
|
28840
|
+
if (hasText && box.w > 0 && box.h > 0) {
|
|
28841
|
+
const ink = inkBox(b, box);
|
|
28842
|
+
if (ink)
|
|
28843
|
+
textBoxes.push(ink);
|
|
28844
|
+
}
|
|
28209
28845
|
}
|
|
28846
|
+
if (CHART_TEXT_RE.test(b))
|
|
28847
|
+
continue;
|
|
28210
28848
|
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28211
28849
|
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28212
28850
|
if (raw === void 0 || !unescXml(raw).trim())
|
|
28213
28851
|
continue;
|
|
28852
|
+
const text = unescXml(raw).trim();
|
|
28214
28853
|
const sz = /<a:rPr\b[^>]*\bsz="(\d+)"/.exec(run)?.[1];
|
|
28215
|
-
|
|
28216
|
-
|
|
28854
|
+
const floor = readableFloorPt(text) * 100;
|
|
28855
|
+
if (sz && +sz < floor && (!smallest || +sz < smallest.sz)) {
|
|
28856
|
+
smallest = { sz: +sz, text, floor };
|
|
28217
28857
|
}
|
|
28218
28858
|
}
|
|
28219
28859
|
}
|
|
@@ -28229,7 +28869,7 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28229
28869
|
findings.push({
|
|
28230
28870
|
slide: slideNumber,
|
|
28231
28871
|
code: "tiny-font",
|
|
28232
|
-
message: `text "${snippet}" is ${(smallest.sz / 100).toFixed(
|
|
28872
|
+
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
28873
|
});
|
|
28234
28874
|
}
|
|
28235
28875
|
let worstPair;
|
|
@@ -28254,6 +28894,110 @@ function lintSlideGeometry(slideXml, slideNumber, canvas = DEFAULT_CANVAS) {
|
|
|
28254
28894
|
}
|
|
28255
28895
|
return findings;
|
|
28256
28896
|
}
|
|
28897
|
+
var BACKDROP_COVER = 0.98;
|
|
28898
|
+
var BACKDROP_FLOOR = 2;
|
|
28899
|
+
var BACKDROP_SAMPLES = 2500;
|
|
28900
|
+
var LIN = Array.from({ length: 256 }, (_, i) => {
|
|
28901
|
+
const c = i / 255;
|
|
28902
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
28903
|
+
});
|
|
28904
|
+
var lumRgb = (r, g, b) => 0.2126 * LIN[r] + 0.7152 * LIN[g] + 0.0722 * LIN[b];
|
|
28905
|
+
var ratioOf = (a, b) => (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
28906
|
+
var hex2 = (n) => n.toString(16).padStart(2, "0").toUpperCase();
|
|
28907
|
+
var BLOCK_RE = /<p:(?:sp|pic|graphicFrame|cxnSp)\b[^>]*>[\s\S]*?<\/p:(?:sp|pic|graphicFrame|cxnSp)>/g;
|
|
28908
|
+
function backdropPicEmbed(slideXml, canvas = DEFAULT_CANVAS) {
|
|
28909
|
+
const first = (slideXml.match(BLOCK_RE) ?? [])[0];
|
|
28910
|
+
if (!first || !first.startsWith("<p:pic"))
|
|
28911
|
+
return void 0;
|
|
28912
|
+
const box = firstBox(first);
|
|
28913
|
+
if (!box || box.x > canvas.w * (1 - BACKDROP_COVER) || box.y > canvas.h * (1 - BACKDROP_COVER))
|
|
28914
|
+
return void 0;
|
|
28915
|
+
if (box.w < canvas.w * BACKDROP_COVER || box.h < canvas.h * BACKDROP_COVER)
|
|
28916
|
+
return void 0;
|
|
28917
|
+
return /<a:blip\b[^>]*\br:embed="([^"]+)"/.exec(first)?.[1];
|
|
28918
|
+
}
|
|
28919
|
+
function lintBackdropContrast(slideXml, slideNumber, backdrop, canvas = DEFAULT_CANVAS) {
|
|
28920
|
+
const blocks = slideXml.match(BLOCK_RE) ?? [];
|
|
28921
|
+
const sx = backdrop.width / canvas.w;
|
|
28922
|
+
const sy = backdrop.height / canvas.h;
|
|
28923
|
+
const occluders = [];
|
|
28924
|
+
let worst;
|
|
28925
|
+
for (let bi = 1; bi < blocks.length; bi++) {
|
|
28926
|
+
const b = blocks[bi];
|
|
28927
|
+
const box = firstBox(b);
|
|
28928
|
+
if (!box)
|
|
28929
|
+
continue;
|
|
28930
|
+
const txIdx = b.indexOf("<p:txBody>");
|
|
28931
|
+
const spPr = txIdx >= 0 ? b.slice(0, txIdx) : b;
|
|
28932
|
+
const prst = /<a:prstGeom\b[^>]*\bprst="([^"]+)"/.exec(spPr)?.[1];
|
|
28933
|
+
const visual = !b.startsWith("<p:sp") || FILL_RE.test(spPr) || prst !== void 0 && prst !== "rect";
|
|
28934
|
+
const hasText = txIdx >= 0 && /<a:t\b[^>]*>\s*\S/.test(b);
|
|
28935
|
+
if (visual) {
|
|
28936
|
+
occluders.push(box);
|
|
28937
|
+
continue;
|
|
28938
|
+
}
|
|
28939
|
+
if (!hasText)
|
|
28940
|
+
continue;
|
|
28941
|
+
const ink = inkBox(b, box);
|
|
28942
|
+
if (!ink || ink.w <= 0 || ink.h <= 0)
|
|
28943
|
+
continue;
|
|
28944
|
+
if (occluders.some((o2) => overlapArea(o2, ink) > 0))
|
|
28945
|
+
continue;
|
|
28946
|
+
let fg;
|
|
28947
|
+
let alpha = 1;
|
|
28948
|
+
let text = "";
|
|
28949
|
+
for (const run of b.match(/<a:r>[\s\S]*?<\/a:r>/g) ?? []) {
|
|
28950
|
+
const raw = /<a:t\b[^>]*>([\s\S]*?)<\/a:t>/.exec(run)?.[1];
|
|
28951
|
+
if (raw === void 0 || !unescXml(raw).trim())
|
|
28952
|
+
continue;
|
|
28953
|
+
text += unescXml(raw);
|
|
28954
|
+
if (fg)
|
|
28955
|
+
continue;
|
|
28956
|
+
const rPr = run.slice(0, Math.max(0, run.indexOf("</a:rPr>")));
|
|
28957
|
+
fg = firstSrgb(rPr);
|
|
28958
|
+
const a = /<a:srgbClr\b[^>]*>\s*<a:alpha\b[^>]*\bval="(\d+)"/.exec(rPr)?.[1];
|
|
28959
|
+
if (fg && a)
|
|
28960
|
+
alpha = Math.min(1, +a / 1e5);
|
|
28961
|
+
}
|
|
28962
|
+
if (!fg)
|
|
28963
|
+
continue;
|
|
28964
|
+
const x0 = Math.max(0, Math.floor(ink.x * sx));
|
|
28965
|
+
const y0 = Math.max(0, Math.floor(ink.y * sy));
|
|
28966
|
+
const x1 = Math.min(backdrop.width, Math.ceil((ink.x + ink.w) * sx));
|
|
28967
|
+
const y1 = Math.min(backdrop.height, Math.ceil((ink.y + ink.h) * sy));
|
|
28968
|
+
if (x1 <= x0 || y1 <= y0)
|
|
28969
|
+
continue;
|
|
28970
|
+
const step = Math.max(1, Math.floor(Math.sqrt((x1 - x0) * (y1 - y0) / BACKDROP_SAMPLES)));
|
|
28971
|
+
const fr = parseInt(fg.slice(0, 2), 16), fgG = parseInt(fg.slice(2, 4), 16), fb = parseInt(fg.slice(4, 6), 16);
|
|
28972
|
+
const fgLum = lumRgb(fr, fgG, fb);
|
|
28973
|
+
const samples = [];
|
|
28974
|
+
for (let y = y0; y < y1; y += step) {
|
|
28975
|
+
for (let x = x0; x < x1; x += step) {
|
|
28976
|
+
const o2 = (y * backdrop.width + x) * 3;
|
|
28977
|
+
const r = backdrop.rgb[o2], g = backdrop.rgb[o2 + 1], bl = backdrop.rgb[o2 + 2];
|
|
28978
|
+
const l = lumRgb(r, g, bl);
|
|
28979
|
+
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));
|
|
28980
|
+
samples.push([ratioOf(tl, l), o2]);
|
|
28981
|
+
}
|
|
28982
|
+
}
|
|
28983
|
+
if (!samples.length)
|
|
28984
|
+
continue;
|
|
28985
|
+
samples.sort((p, q) => p[0] - q[0]);
|
|
28986
|
+
const [ratio, o] = samples[samples.length >> 1];
|
|
28987
|
+
if (ratio < BACKDROP_FLOOR && (!worst || ratio < worst.ratio)) {
|
|
28988
|
+
const bg = hex2(backdrop.rgb[o]) + hex2(backdrop.rgb[o + 1]) + hex2(backdrop.rgb[o + 2]);
|
|
28989
|
+
worst = { ratio, text: text.trim(), fg, bg };
|
|
28990
|
+
}
|
|
28991
|
+
}
|
|
28992
|
+
if (!worst)
|
|
28993
|
+
return [];
|
|
28994
|
+
const snippet = worst.text.length > 24 ? `${worst.text.slice(0, 24)}\u2026` : worst.text;
|
|
28995
|
+
return [{
|
|
28996
|
+
slide: slideNumber,
|
|
28997
|
+
code: "low-contrast-text",
|
|
28998
|
+
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`
|
|
28999
|
+
}];
|
|
29000
|
+
}
|
|
28257
29001
|
function canvasFromPresentation(xml) {
|
|
28258
29002
|
if (!xml)
|
|
28259
29003
|
return DEFAULT_CANVAS;
|
|
@@ -28261,8 +29005,28 @@ function canvasFromPresentation(xml) {
|
|
|
28261
29005
|
const cy = /<p:sldSz\b[^>]*\bcy="(\d+)"/.exec(xml)?.[1];
|
|
28262
29006
|
return cx && cy ? { w: +cx, h: +cy } : DEFAULT_CANVAS;
|
|
28263
29007
|
}
|
|
29008
|
+
async function backdropRaster(zip, slideXml, relsPath, canvas) {
|
|
29009
|
+
const rId = backdropPicEmbed(slideXml, canvas);
|
|
29010
|
+
const rels = rId ? zip.file(relsPath) : null;
|
|
29011
|
+
if (!rId || !rels)
|
|
29012
|
+
return void 0;
|
|
29013
|
+
const relsXml = await rels.async("string");
|
|
29014
|
+
const rel = [...relsXml.matchAll(/<Relationship\b[^>]*>/g)].map((m) => m[0]).find((r) => new RegExp(`\\bId="${rId}"`).test(r));
|
|
29015
|
+
const target = rel && /\bTarget="([^"]+)"/.exec(rel)?.[1];
|
|
29016
|
+
if (!target || !/\.png$/i.test(target))
|
|
29017
|
+
return void 0;
|
|
29018
|
+
const parts = ["ppt", "slides"];
|
|
29019
|
+
for (const seg of target.split("/")) {
|
|
29020
|
+
if (seg === "..")
|
|
29021
|
+
parts.pop();
|
|
29022
|
+
else if (seg && seg !== ".")
|
|
29023
|
+
parts.push(seg);
|
|
29024
|
+
}
|
|
29025
|
+
const media = zip.file(target.startsWith("/") ? target.slice(1) : parts.join("/"));
|
|
29026
|
+
return media ? decodePng(await media.async("uint8array")) : void 0;
|
|
29027
|
+
}
|
|
28264
29028
|
async function designLintPptx(bytes) {
|
|
28265
|
-
const zip = await
|
|
29029
|
+
const zip = await import_jszip5.default.loadAsync(Buffer.from(bytes));
|
|
28266
29030
|
const findings = [];
|
|
28267
29031
|
let masterBg = { colored: false };
|
|
28268
29032
|
const masterFile = Object.keys(zip.files).find((p) => /^ppt\/slideMasters\/slideMaster\d+\.xml$/.test(p));
|
|
@@ -28271,9 +29035,15 @@ async function designLintPptx(bytes) {
|
|
|
28271
29035
|
const presFile = zip.file("ppt/presentation.xml");
|
|
28272
29036
|
const canvas = canvasFromPresentation(presFile ? await presFile.async("string") : void 0);
|
|
28273
29037
|
const lang = detectDeckLang();
|
|
28274
|
-
for (const { position, path } of await slidePartsInOrder(zip)) {
|
|
29038
|
+
for (const { position, path, relsPath } of await slidePartsInOrder(zip)) {
|
|
28275
29039
|
const xml = await zip.file(path).async("string");
|
|
28276
|
-
|
|
29040
|
+
const own = lintSlideXml(xml, position, masterBg, lang);
|
|
29041
|
+
findings.push(...own);
|
|
29042
|
+
if (!own.some((f) => f.code === "low-contrast-text")) {
|
|
29043
|
+
const backdrop = await backdropRaster(zip, xml, relsPath, canvas);
|
|
29044
|
+
if (backdrop)
|
|
29045
|
+
findings.push(...lintBackdropContrast(xml, position, backdrop, canvas));
|
|
29046
|
+
}
|
|
28277
29047
|
findings.push(...lintSlideGeometry(xml, position, canvas));
|
|
28278
29048
|
}
|
|
28279
29049
|
return { ok: findings.length === 0, findings };
|
|
@@ -28302,7 +29072,7 @@ async function main() {
|
|
|
28302
29072
|
}];
|
|
28303
29073
|
}
|
|
28304
29074
|
if (strict && strictApplies) {
|
|
28305
|
-
const schemaDir = process.env.TALOS_XSD_DIR || (0,
|
|
29075
|
+
const schemaDir = process.env.TALOS_XSD_DIR || (0, import_node_path3.join)((0, import_node_path3.dirname)(process.argv[1]), "..", "schemas");
|
|
28306
29076
|
strictResult = await validateOoxmlStrict(bytes, schemaDir).catch((e) => {
|
|
28307
29077
|
die(`--strict failed to run: ${e.message}`);
|
|
28308
29078
|
});
|