@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.
Files changed (159) hide show
  1. package/package.json +3 -3
  2. package/skills/document/SKILL.md +135 -219
  3. package/skills/document/art-directions/base.css +27 -2
  4. package/skills/document/art-directions/cinematic-depth.css +3 -1
  5. package/skills/document/art-directions/collage-paper.css +5 -1
  6. package/skills/document/art-directions/editorial-cut.css +5 -1
  7. package/skills/document/art-directions/geometric-pop.css +6 -0
  8. package/skills/document/art-directions/gradient-mesh.css +5 -1
  9. package/skills/document/art-directions/illustrated-doodle.css +5 -0
  10. package/skills/document/art-directions/luxury-ornament.css +4 -1
  11. package/skills/document/art-directions/maximalist-blocks.css +6 -0
  12. package/skills/document/art-directions/technical-grid.css +4 -0
  13. package/skills/document/assets/fonts/fonts.json +2 -2
  14. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
  15. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
  16. package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
  17. package/skills/document/assets/pdf-fonts/README.md +35 -0
  18. package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
  19. package/skills/document/assets/tessdata/README.md +21 -0
  20. package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
  21. package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
  22. package/skills/document/bin/deck-build.cjs +12563 -83
  23. package/skills/document/bin/deck-chart.cjs +771 -0
  24. package/skills/document/bin/deck-plan.cjs +12414 -0
  25. package/skills/document/bin/deck-qa.cjs +1075 -266
  26. package/skills/document/bin/deck-scenario.cjs +1363 -0
  27. package/skills/document/bin/deck-styles.cjs +1604 -0
  28. package/skills/document/bin/doc-convert.cjs +308 -292
  29. package/skills/document/bin/doc-fill.cjs +73264 -192
  30. package/skills/document/bin/doc-gen.cjs +2491 -312
  31. package/skills/document/bin/doc-run.cjs +162 -24
  32. package/skills/document/bin/doc-scan.cjs +13 -0
  33. package/skills/document/bin/doc-text.cjs +13 -0
  34. package/skills/document/bin/doc-validate.cjs +838 -68
  35. package/skills/document/bin/doc-xml.cjs +9 -5
  36. package/skills/document/bin/docx-annotate.cjs +95 -19
  37. package/skills/document/bin/docx-defaults.cjs +9907 -0
  38. package/skills/document/bin/fontkit.cjs +1 -0
  39. package/skills/document/bin/html2pptx.cjs +36622 -31394
  40. package/skills/document/bin/ocr-worker.cjs +1 -0
  41. package/skills/document/bin/ocr.cjs +131 -40
  42. package/skills/document/bin/pdf-form.cjs +682 -177
  43. package/skills/document/bin/pdf-images.cjs +13 -0
  44. package/skills/document/bin/pdf-ops.cjs +642 -199
  45. package/skills/document/bin/pdf-render.cjs +19 -3
  46. package/skills/document/bin/pdf-tables.cjs +13 -0
  47. package/skills/document/bin/pptx-clone.cjs +3 -3
  48. package/skills/document/bin/pptx-notes.cjs +10038 -0
  49. package/skills/document/bin/pptx-slide.cjs +3 -3
  50. package/skills/document/bin/pptx-tokenize.cjs +19 -4
  51. package/skills/document/bin/template-skin.cjs +10910 -0
  52. package/skills/document/bin/xlsx-query.cjs +192 -61
  53. package/skills/document/bin-reference.md +61 -33
  54. package/skills/document/catalog.json +33 -32
  55. package/skills/document/content-pipeline.md +179 -257
  56. package/skills/document/deck-design.md +378 -638
  57. package/skills/document/generated-visuals.md +99 -0
  58. package/skills/document/layouts/MAINTAINERS.md +33 -0
  59. package/skills/document/layouts/README.md +49 -36
  60. package/skills/document/layouts/big-stat.tpl +1 -1
  61. package/skills/document/layouts/capacity.json +1 -0
  62. package/skills/document/layouts/cards-3.tpl +1 -1
  63. package/skills/document/layouts/chart-full.tpl +1 -1
  64. package/skills/document/layouts/chart-kpi.tpl +1 -1
  65. package/skills/document/layouts/comparison.tpl +1 -1
  66. package/skills/document/layouts/data-table.tpl +1 -1
  67. package/skills/document/layouts/funnel.tpl +1 -1
  68. package/skills/document/layouts/gantt.tpl +1 -1
  69. package/skills/document/layouts/hub-flow.tpl +4 -0
  70. package/skills/document/layouts/icon-row.tpl +1 -1
  71. package/skills/document/layouts/image-grid.tpl +1 -1
  72. package/skills/document/layouts/matrix-2x2.tpl +1 -1
  73. package/skills/document/layouts/mock-qa.tpl +4 -0
  74. package/skills/document/layouts/profiles.json +2 -2
  75. package/skills/document/layouts/pyramid.tpl +1 -1
  76. package/skills/document/layouts/radar.tpl +1 -1
  77. package/skills/document/layouts/ring-progress.tpl +1 -1
  78. package/skills/document/layouts/slots.json +1 -1
  79. package/skills/document/layouts/steps-vertical.tpl +1 -1
  80. package/skills/document/layouts/steps.tpl +1 -1
  81. package/skills/document/layouts/three-kpi.tpl +1 -1
  82. package/skills/document/layouts/timeline.tpl +1 -1
  83. package/skills/document/layouts/toc-panel.tpl +2 -2
  84. package/skills/document/layouts/two-kpi.tpl +2 -2
  85. package/skills/document/layouts/versus.tpl +1 -1
  86. package/skills/document/motion/README.md +3 -2
  87. package/skills/document/office.md +3 -3
  88. package/skills/document/packs/README.md +1 -1
  89. package/skills/document/packs/board-brief/backdrop.html +2 -0
  90. package/skills/document/packs/board-brief/pack.css +2 -1
  91. package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
  92. package/skills/document/packs/mckinsey-photo/pack.css +2 -1
  93. package/skills/document/packs/press-room/backdrop.html +2 -0
  94. package/skills/document/packs/press-room/pack.css +2 -1
  95. package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
  96. package/skills/document/packs/prismatic-3d/pack.css +4 -1
  97. package/skills/document/pdf.md +60 -43
  98. package/skills/document/rewrite.md +257 -309
  99. package/skills/document/scenarios.json +1307 -0
  100. package/skills/document/scenarios.schema.json +104 -0
  101. package/skills/document/styles/README.md +15 -25
  102. package/skills/document/styles/_backdrops/rimlight.html +3 -2
  103. package/skills/document/styles/_contract-defaults.css +31 -2
  104. package/skills/document/styles/_samples/_groups/biz-premium.webp +0 -0
  105. package/skills/document/styles/_samples/mckinsey.webp +0 -0
  106. package/skills/document/styles/anthropic/skin.css +6 -3
  107. package/skills/document/styles/apple/skin.css +4 -3
  108. package/skills/document/styles/bistro-warm/backdrop.html +2 -0
  109. package/skills/document/styles/bistro-warm/skin.css +4 -1
  110. package/skills/document/styles/black-gold/skin.css +6 -5
  111. package/skills/document/styles/bold-clash/backdrop.html +2 -0
  112. package/skills/document/styles/bold-clash/skin.css +2 -1
  113. package/skills/document/styles/branding/skin.css +4 -4
  114. package/skills/document/styles/campus-teal/backdrop.html +2 -0
  115. package/skills/document/styles/campus-teal/skin.css +2 -1
  116. package/skills/document/styles/cinereel/skin.css +7 -3
  117. package/skills/document/styles/clinic-care/backdrop.html +2 -0
  118. package/skills/document/styles/clinic-care/skin.css +2 -1
  119. package/skills/document/styles/cool/skin.css +6 -5
  120. package/skills/document/styles/dark-glass/backdrop.html +2 -0
  121. package/skills/document/styles/dark-glass/skin.css +2 -1
  122. package/skills/document/styles/gov-brief/backdrop.html +2 -0
  123. package/skills/document/styles/gov-brief/skin.css +2 -1
  124. package/skills/document/styles/grid-tech/backdrop.html +2 -0
  125. package/skills/document/styles/grid-tech/skin.css +2 -1
  126. package/skills/document/styles/hand-drawn/skin.css +3 -3
  127. package/skills/document/styles/ivory-gold/skin.css +7 -6
  128. package/skills/document/styles/legal-slate/backdrop.html +2 -0
  129. package/skills/document/styles/legal-slate/skin.css +2 -1
  130. package/skills/document/styles/magazine-ink/skin.css +4 -3
  131. package/skills/document/styles/mckinsey/backdrop.html +7 -5
  132. package/skills/document/styles/mckinsey/skin.css +2 -1
  133. package/skills/document/styles/memphis/backdrop.html +2 -0
  134. package/skills/document/styles/memphis/skin.css +2 -1
  135. package/skills/document/styles/mono/skin.css +6 -2
  136. package/skills/document/styles/neon-cyber/backdrop.html +2 -0
  137. package/skills/document/styles/neon-cyber/skin.css +2 -1
  138. package/skills/document/styles/olive-gold/skin.css +7 -6
  139. package/skills/document/styles/organic-earth/backdrop.html +2 -0
  140. package/skills/document/styles/organic-earth/skin.css +2 -1
  141. package/skills/document/styles/pastel-saas/backdrop.html +2 -0
  142. package/skills/document/styles/pastel-saas/skin.css +2 -1
  143. package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
  144. package/skills/document/styles/portfolio-noir/skin.css +2 -1
  145. package/skills/document/styles/retro-press/backdrop.html +2 -0
  146. package/skills/document/styles/retro-press/skin.css +2 -1
  147. package/skills/document/styles/sport-arena/backdrop.html +2 -0
  148. package/skills/document/styles/sport-arena/skin.css +4 -1
  149. package/skills/document/styles/swiss-grid/skin.css +10 -5
  150. package/skills/document/styles/wedding-blush/backdrop.html +2 -0
  151. package/skills/document/styles/wedding-blush/skin.css +4 -1
  152. package/skills/document/theme-system.md +85 -77
  153. package/skills/document/themes/README.md +7 -5
  154. package/skills/document/vision.md +8 -5
  155. package/skills/document/xlsx.md +15 -25
  156. package/skills/grill-me/SKILL.md +6 -0
  157. package/skills/grill-me/agents/openai.yaml +5 -0
  158. package/skills/grilling/SKILL.md +28 -0
  159. 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 join3(s) {
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 JSZip4() {
9794
- if (!(this instanceof JSZip4)) {
9795
- return new JSZip4();
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 JSZip4();
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
- JSZip4.prototype = require_object();
9814
- JSZip4.prototype.loadAsync = require_load();
9815
- JSZip4.support = require_support();
9816
- JSZip4.defaults = require_defaults();
9817
- JSZip4.version = "3.10.1";
9818
- JSZip4.loadAsync = function(content, options) {
9819
- return new JSZip4().loadAsync(content, options);
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
- JSZip4.external = require_external();
9822
- module2.exports = JSZip4;
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, params) {
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 (params instanceof PDFDict_1.default) {
18364
- var EarlyChange = params.lookup(PDFName_1.default.of("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 params = 0;
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
- params = parameters.get(c);
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 === params) {
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 === params) {
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 import_node_path2 = require("node:path");
27981
+ var import_node_path3 = require("node:path");
27587
27982
 
27588
27983
  // packages/core/dist/document/doc-validate.js
27589
- var import_jszip = __toESM(require_lib3(), 1);
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 import_jszip.default.loadAsync(Buffer.from(bytes));
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 import_node_path = require("node:path");
27832
- var import_node_url = require("node:url");
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 import_jszip2 = __toESM(require_lib3(), 1);
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, import_node_url.pathToFileURL)((0, import_node_path.join)(deps, "xmllint-wasm", "index-node.js")).href : "xmllint-wasm";
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, import_node_path.join)(schemaDir, f), "utf8"))));
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 import_jszip2.default.loadAsync(Buffer.from(bytes));
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 import_jszip3 = __toESM(require_lib3(), 1);
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/pptx-parts.js
27958
- var SLIDE_PART = /^ppt\/slides\/(slide\d+\.xml)$/;
27959
- function relsPathFor(path) {
27960
- return path.replace(SLIDE_PART, "ppt/slides/_rels/$1.rels");
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
- function attr(el, name) {
27963
- return new RegExp(`\\b${name}="([^"]*)"`).exec(el)?.[1];
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
- async function slidePartsInOrder(zip) {
27966
- 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]));
27967
- const toParts = (paths) => paths.map((path, i) => ({ position: i + 1, path, relsPath: relsPathFor(path) }));
27968
- const pres = await zip.file("ppt/presentation.xml")?.async("string");
27969
- const rels = await zip.file("ppt/_rels/presentation.xml.rels")?.async("string");
27970
- if (!pres || !rels)
27971
- return toParts(byNumber);
27972
- const targetById = /* @__PURE__ */ new Map();
27973
- for (const [el] of rels.matchAll(/<Relationship\b[^>]*>/g)) {
27974
- const id = attr(el, "Id");
27975
- const target = attr(el, "Target");
27976
- if (!id || !target)
27977
- continue;
27978
- targetById.set(id, target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.\//, "")}`);
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 = 1e3;
28166
- var OVERLAP_RATIO = 0.5;
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
- textBoxes.push(box);
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
- if (sz && +sz < MIN_FONT_HUNDREDTHS && (!smallest || +sz < smallest.sz)) {
28216
- smallest = { sz: +sz, text: unescXml(raw).trim() };
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(0)}pt \u2014 below the ~10pt readable floor for a projected slide; enlarge it or cut the text`
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 import_jszip3.default.loadAsync(Buffer.from(bytes));
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
- findings.push(...lintSlideXml(xml, position, masterBg, lang));
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, import_node_path2.join)((0, import_node_path2.dirname)(process.argv[1]), "..", "schemas");
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
  });