@json-to-office/jto-ops 6.2.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1061,6 +1061,7 @@ var DocxFormatAdapter = class {
1061
1061
  options.services
1062
1062
  );
1063
1063
  const prepared = !hasPlugins && options.prepared?.format === "docx" ? options.prepared : void 0;
1064
+ const pluginPrepared = hasPlugins && options.prepared?.format === "docx" ? options.prepared : void 0;
1064
1065
  const resolved = prepared ? void 0 : await this.resolveThemes(options);
1065
1066
  const requestedTheme = resolved?.requested;
1066
1067
  const customThemes = resolved?.customThemes;
@@ -1136,6 +1137,7 @@ var DocxFormatAdapter = class {
1136
1137
  requestedTheme,
1137
1138
  customThemes
1138
1139
  );
1140
+ const usable = preparedFor(pluginPrepared, parsed);
1139
1141
  const result = await generator.generateBuffer(docDefinition, {
1140
1142
  validation: {
1141
1143
  allowUnknownFields: options.validation?.allowUnknownFields
@@ -1144,10 +1146,12 @@ var DocxFormatAdapter = class {
1144
1146
  generatedAt: options.generatedAt,
1145
1147
  baseDir: options.baseDir,
1146
1148
  renderer: options.renderer,
1147
- svgRasterFallback: options.svgRasterFallback
1149
+ svgRasterFallback: options.svgRasterFallback,
1150
+ ...usable && { prepared: usable }
1148
1151
  });
1149
- emitGenerationWarnings(result.warnings ?? []);
1150
- options.warnings?.push(...toGenerationWarnings(result.warnings));
1152
+ const emitted = withoutPreparedWarnings(result.warnings ?? [], usable);
1153
+ emitGenerationWarnings(emitted);
1154
+ options.warnings?.push(...toGenerationWarnings(emitted));
1151
1155
  return result.buffer;
1152
1156
  },
1153
1157
  getStandardDefinition: generator.expandStandardDefinition ? async (config) => {
@@ -1230,15 +1234,35 @@ var DocxFormatAdapter = class {
1230
1234
  resolved.requested,
1231
1235
  resolved.customThemes
1232
1236
  );
1233
- const prepared = core.prepareDocxQualityDocument(
1234
- normalized.document,
1235
- {
1236
- customThemes: normalized.customThemes,
1237
+ const prepared = options.plugins && options.plugins.length > 0 ? await (async () => {
1238
+ let generator = core.createDocumentGenerator({
1239
+ theme: resolved.requested,
1240
+ customThemes: resolved.requested ? {
1241
+ ...resolved.customThemes,
1242
+ [CLI_THEME_KEY]: resolved.requested
1243
+ } : resolved.customThemes,
1244
+ services: withRequestedServices(
1245
+ buildDocxServices(),
1246
+ options.services
1247
+ ),
1248
+ baseDir: options.baseDir,
1237
1249
  fonts: options.fonts,
1238
- renderer: options.renderer ?? documentRenderer(parsed),
1239
- warnings
1240
- }
1241
- );
1250
+ validation: {
1251
+ allowUnknownFields: options.validation?.allowUnknownFields
1252
+ },
1253
+ renderer: options.renderer
1254
+ });
1255
+ for (const plugin of options.plugins)
1256
+ generator = generator.addComponent(plugin);
1257
+ const result = await generator.prepareQuality(normalized.document);
1258
+ warnings.push(...result.warnings ?? []);
1259
+ return result.prepared;
1260
+ })() : core.prepareDocxQualityDocument(normalized.document, {
1261
+ customThemes: normalized.customThemes,
1262
+ fonts: options.fonts,
1263
+ renderer: options.renderer ?? documentRenderer(parsed),
1264
+ warnings
1265
+ });
1242
1266
  return stampPrepared(
1243
1267
  {
1244
1268
  ...prepared,
@@ -1269,7 +1293,7 @@ var DocxFormatAdapter = class {
1269
1293
  async resolveTheme(options) {
1270
1294
  const core = await import("@json-to-office/core-docx");
1271
1295
  const { requested } = await this.resolveThemes(options);
1272
- return requested ?? core.themes?.minimal ?? {};
1296
+ return requested ?? core.themes?.consulting ?? {};
1273
1297
  }
1274
1298
  /**
1275
1299
  * Resolve `theme`/`themePath` once for a whole run: `themePath` is read a
@@ -1430,6 +1454,7 @@ var PptxFormatAdapter = class {
1430
1454
  options.services
1431
1455
  );
1432
1456
  const prepared = !hasPlugins && options.prepared?.format === "pptx" ? options.prepared : void 0;
1457
+ const pluginPrepared = hasPlugins && options.prepared?.format === "pptx" ? options.prepared : void 0;
1433
1458
  const resolved = prepared ? void 0 : await this.resolveThemes(options);
1434
1459
  const requestedTheme = resolved?.requested;
1435
1460
  const customThemes = resolved?.customThemes;
@@ -1504,6 +1529,7 @@ var PptxFormatAdapter = class {
1504
1529
  requestedTheme,
1505
1530
  customThemes
1506
1531
  );
1532
+ const usable = preparedFor(pluginPrepared, parsed);
1507
1533
  const result = await generator.generateBuffer(docDefinition, {
1508
1534
  validation: {
1509
1535
  allowUnknownFields: options.validation?.allowUnknownFields
@@ -1511,9 +1537,13 @@ var PptxFormatAdapter = class {
1511
1537
  deterministic: options.deterministic,
1512
1538
  generatedAt: options.generatedAt,
1513
1539
  baseDir: options.baseDir,
1514
- renderer: options.renderer
1540
+ renderer: options.renderer,
1541
+ ...usable && { prepared: usable }
1515
1542
  });
1516
- const normalized = toGenerationWarnings(result.warnings);
1543
+ const normalized = withoutPreparedWarnings(
1544
+ toGenerationWarnings(result.warnings),
1545
+ usable
1546
+ );
1517
1547
  emitGenerationWarnings(normalized);
1518
1548
  options.warnings?.push(...normalized);
1519
1549
  return result.buffer;
@@ -1580,19 +1610,37 @@ var PptxFormatAdapter = class {
1580
1610
  resolved.requested,
1581
1611
  resolved.customThemes
1582
1612
  );
1583
- const prepared = core.preparePptxQualityDocument(
1584
- normalized.document,
1585
- {
1586
- customThemes: normalized.customThemes,
1587
- fonts: options.fonts,
1588
- services: withRequestedServices(
1589
- buildServicesFromEnv(),
1590
- options.services
1591
- ),
1592
- renderer: options.renderer ?? documentRenderer(parsed),
1593
- warnings
1594
- }
1613
+ const services = withRequestedServices(
1614
+ buildServicesFromEnv(),
1615
+ options.services
1595
1616
  );
1617
+ const prepared = options.plugins && options.plugins.length > 0 ? await (async () => {
1618
+ let generator = core.createPresentationGenerator({
1619
+ theme: resolved.requested,
1620
+ customThemes: resolved.requested ? {
1621
+ ...resolved.customThemes,
1622
+ [CLI_THEME_KEY]: resolved.requested
1623
+ } : resolved.customThemes,
1624
+ services,
1625
+ baseDir: options.baseDir,
1626
+ fonts: options.fonts,
1627
+ validation: {
1628
+ allowUnknownFields: options.validation?.allowUnknownFields
1629
+ },
1630
+ renderer: options.renderer
1631
+ });
1632
+ for (const plugin of options.plugins)
1633
+ generator = generator.addComponent(plugin);
1634
+ const result = await generator.prepareQuality(normalized.document);
1635
+ warnings.push(...result.warnings ?? []);
1636
+ return result.prepared;
1637
+ })() : core.preparePptxQualityDocument(normalized.document, {
1638
+ customThemes: normalized.customThemes,
1639
+ fonts: options.fonts,
1640
+ services,
1641
+ renderer: options.renderer ?? documentRenderer(parsed),
1642
+ warnings
1643
+ });
1596
1644
  return stampPrepared(
1597
1645
  {
1598
1646
  ...prepared,
@@ -1624,7 +1672,7 @@ var PptxFormatAdapter = class {
1624
1672
  const core = await import("@json-to-office/core-pptx");
1625
1673
  const themes = core.pptxThemes || {};
1626
1674
  const { requested } = await this.resolveThemes(options);
1627
- return requested ?? themes.minimal ?? {};
1675
+ return requested ?? themes.consulting ?? {};
1628
1676
  }
1629
1677
  /**
1630
1678
  * Resolve `theme`/`themePath` once for a whole run: `themePath` is read a
@@ -2179,48 +2227,92 @@ function authoredTextForMatch(text) {
2179
2227
  function needleSegments(text) {
2180
2228
  return authoredTextForMatch(text).split(FIELD).map(normalizeForMatch).filter((segment) => segment !== "");
2181
2229
  }
2182
- function indexDocument(pages, exclude = /* @__PURE__ */ new Set()) {
2230
+ function indexDocument(pages, exclude = /* @__PURE__ */ new Set(), order = pageOrder) {
2183
2231
  const refs = [];
2184
2232
  const fragments = [];
2185
2233
  const positions = [];
2186
2234
  let cursor = 0;
2187
- pages.forEach((page, pageIndex) => {
2188
- for (const index of readingOrder(page.words)) {
2189
- const word = page.words[index];
2190
- const key = `${pageIndex}:${index}`;
2191
- const fragment = exclude.has(key) ? "" : normalizeForMatch(word.text);
2192
- refs.push({ pageIndex, word: index });
2193
- fragments.push(fragment);
2194
- positions.push(cursor);
2195
- cursor += fragment.length;
2196
- }
2235
+ const push = (pageIndex, index) => {
2236
+ const word = pages[pageIndex].words[index];
2237
+ const key = `${pageIndex}:${index}`;
2238
+ const fragment = exclude.has(key) ? "" : normalizeForMatch(word.text);
2239
+ refs.push({ pageIndex, word: index });
2240
+ fragments.push(fragment);
2241
+ positions.push(cursor);
2242
+ cursor += fragment.length;
2243
+ };
2244
+ const orders = pages.map((page) => order(page.words, page.lines));
2245
+ orders.forEach((order2, pageIndex) => {
2246
+ for (const index of order2.upright) push(pageIndex, index);
2197
2247
  });
2198
- return { pages, refs, fragments, positions, stream: fragments.join("") };
2248
+ const rotatedFrom = cursor;
2249
+ orders.forEach((order2, pageIndex) => {
2250
+ for (const index of order2.rotated) push(pageIndex, index);
2251
+ });
2252
+ return {
2253
+ pages,
2254
+ refs,
2255
+ fragments,
2256
+ positions,
2257
+ stream: fragments.join(""),
2258
+ rotatedFrom
2259
+ };
2260
+ }
2261
+ function readingOrder(words2, lines = []) {
2262
+ const { upright, rotated } = pageOrder(words2, lines);
2263
+ return [...upright, ...rotated];
2264
+ }
2265
+ function pageOrder(words2, lines = []) {
2266
+ const rotated = rotatedWords(words2, lines);
2267
+ return {
2268
+ upright: uprightOrder(
2269
+ words2,
2270
+ words2.map((_, i) => i).filter((i) => !rotated.has(i))
2271
+ ),
2272
+ rotated: [...rotated].sort((a, b) => a - b)
2273
+ };
2274
+ }
2275
+ function linedOrder(words2, lines = []) {
2276
+ const rotated = rotatedWords(words2, lines);
2277
+ const upright = words2.map((_, i) => i).filter((i) => !rotated.has(i));
2278
+ return {
2279
+ upright: groupRows(words2, upright).flatMap(
2280
+ (row) => [...row.words].sort((a, b) => words2[a].xMin - words2[b].xMin)
2281
+ ),
2282
+ rotated: [...rotated].sort((a, b) => a - b)
2283
+ };
2199
2284
  }
2200
- function readingOrder(words2) {
2285
+ function rotatedWords(words2, lines) {
2201
2286
  const rotated = new Set(
2202
2287
  words2.map((_, i) => i).filter((i) => {
2203
2288
  const w = words2[i];
2204
2289
  return w.text.length > 2 && w.yMax - w.yMin > 1.5 * (w.xMax - w.xMin);
2205
2290
  })
2206
2291
  );
2207
- const upright = uprightOrder(
2208
- words2,
2209
- words2.map((_, i) => i).filter((i) => !rotated.has(i))
2210
- );
2211
- const order = [];
2212
- let next = 0;
2213
- for (let i = 0; i < words2.length; i++) {
2214
- if (rotated.has(i)) order.push(i);
2215
- else order.push(upright[next++]);
2292
+ for (const line of lines) {
2293
+ if (line.words.length < 2) continue;
2294
+ const stacked = line.words.slice(1).every((index, n) => {
2295
+ const a = words2[line.words[n]];
2296
+ const b = words2[index];
2297
+ const across = Math.min(a.xMax, b.xMax) - Math.max(a.xMin, b.xMin);
2298
+ const along = Math.min(a.yMax, b.yMax) - Math.max(a.yMin, b.yMin);
2299
+ return across > 0 && along <= 0;
2300
+ });
2301
+ if (stacked) for (const index of line.words) rotated.add(index);
2216
2302
  }
2217
- return order;
2303
+ return rotated;
2218
2304
  }
2219
2305
  function median(values) {
2220
2306
  const sorted = [...values].sort((a, b) => a - b);
2221
2307
  const mid = sorted.length >> 1;
2222
2308
  return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
2223
2309
  }
2310
+ var ROW_SIZE_RATIO = 2;
2311
+ function comparableSize(a, b) {
2312
+ const ha = a.yMax - a.yMin;
2313
+ const hb = b.yMax - b.yMin;
2314
+ return Math.max(ha, hb) <= ROW_SIZE_RATIO * Math.min(ha, hb);
2315
+ }
2224
2316
  function groupRows(words2, indices) {
2225
2317
  const rows = [];
2226
2318
  const byY = [...indices].sort(
@@ -2229,7 +2321,8 @@ function groupRows(words2, indices) {
2229
2321
  for (const i of byY) {
2230
2322
  const word = words2[i];
2231
2323
  const row = rows[rows.length - 1];
2232
- if (row && sameRow(words2[row.words[0]], word)) {
2324
+ const peer = row?.words.find((w) => comparableSize(words2[w], word));
2325
+ if (row && peer !== void 0 && sameRow(words2[peer], word)) {
2233
2326
  row.words.push(i);
2234
2327
  row.yMin = Math.min(row.yMin, word.yMin);
2235
2328
  row.yMax = Math.max(row.yMax, word.yMax);
@@ -2267,20 +2360,25 @@ function uprightOrder(words2, indices) {
2267
2360
  const fragments = rows.map(fragmentsOf);
2268
2361
  const boxes = grouped;
2269
2362
  const order = [];
2363
+ const read = /* @__PURE__ */ new Set();
2364
+ const readColumn = (column) => {
2365
+ column.fragments.sort(
2366
+ (a, b) => words2[a.words[0]].yMin - words2[b.words[0]].yMin || a.xMin - b.xMin
2367
+ );
2368
+ for (const f of column.fragments) {
2369
+ order.push(...f.words);
2370
+ read.add(f);
2371
+ }
2372
+ };
2270
2373
  let cluster = null;
2271
2374
  const flush = () => {
2272
2375
  if (!cluster) return;
2273
2376
  if (cluster.columns.length < 2) {
2274
2377
  for (const r of cluster.rows)
2275
- for (const f of fragments[r]) order.push(...f.words);
2378
+ for (const f of fragments[r]) if (!read.has(f)) order.push(...f.words);
2276
2379
  } else {
2277
2380
  cluster.columns.sort((a, b) => a.xMin - b.xMin);
2278
- for (const column of cluster.columns) {
2279
- column.fragments.sort(
2280
- (a, b) => words2[a.words[0]].yMin - words2[b.words[0]].yMin || a.xMin - b.xMin
2281
- );
2282
- for (const f of column.fragments) order.push(...f.words);
2283
- }
2381
+ for (const column of cluster.columns) readColumn(column);
2284
2382
  }
2285
2383
  cluster = null;
2286
2384
  };
@@ -2294,6 +2392,16 @@ function uprightOrder(words2, indices) {
2294
2392
  if (!adjacent || wide) flush();
2295
2393
  if (!cluster) cluster = { rows: [], columns: [] };
2296
2394
  cluster.rows.push(r);
2395
+ const landing = /* @__PURE__ */ new Map();
2396
+ for (const f of fragments[r]) {
2397
+ const hit = overlapping(cluster.columns, f)[0];
2398
+ if (hit) landing.set(hit, (landing.get(hit) ?? 0) + 1);
2399
+ }
2400
+ for (const [column, count] of landing) {
2401
+ if (count < 2) continue;
2402
+ readColumn(column);
2403
+ cluster.columns.splice(cluster.columns.indexOf(column), 1);
2404
+ }
2297
2405
  for (const f of fragments[r]) {
2298
2406
  const hit = overlapping(cluster.columns, f)[0];
2299
2407
  if (hit) {
@@ -2361,6 +2469,7 @@ function occurrence(index, at, end) {
2361
2469
  const parts = partsOf(index, at, end);
2362
2470
  return {
2363
2471
  at,
2472
+ end,
2364
2473
  pageIndex: parts[0].pageIndex,
2365
2474
  endPageIndex: parts[parts.length - 1].pageIndex,
2366
2475
  parts
@@ -2386,7 +2495,11 @@ function findOccurrences(index, segments) {
2386
2495
  end = next + segments[s].length;
2387
2496
  }
2388
2497
  if (!complete || !alignedToFragments(index, at, end)) continue;
2389
- hits.push(occurrence(index, at, end));
2498
+ const hit = occurrence(index, at, end);
2499
+ if (at < index.rotatedFrom ? end > index.rotatedFrom : hit.pageIndex !== hit.endPageIndex) {
2500
+ continue;
2501
+ }
2502
+ hits.push(hit);
2390
2503
  searchFrom = end;
2391
2504
  }
2392
2505
  return hits;
@@ -2394,7 +2507,7 @@ function findOccurrences(index, segments) {
2394
2507
  var MIN_NEEDLE_LENGTH = 3;
2395
2508
  var MIN_PARTIAL_PREFIX = 16;
2396
2509
  var CHROME_BAND = 0.2;
2397
- function longestRenderedPrefix(index, needle) {
2510
+ function longestRenderedPrefix(index, needle, page, region) {
2398
2511
  if (needle.length < MIN_PARTIAL_PREFIX) return void 0;
2399
2512
  const anchor = needle.slice(0, MIN_PARTIAL_PREFIX);
2400
2513
  let best;
@@ -2407,14 +2520,36 @@ function longestRenderedPrefix(index, needle) {
2407
2520
  if (first >= index.fragments.length || index.positions[first] !== at) {
2408
2521
  continue;
2409
2522
  }
2523
+ if (page !== void 0 && index.refs[first].pageIndex !== page) continue;
2524
+ const rotated = at >= index.rotatedFrom;
2525
+ const limit = Math.min(
2526
+ page !== void 0 || rotated ? pageEnd(index, first) : index.stream.length,
2527
+ rotated ? index.stream.length : index.rotatedFrom
2528
+ );
2529
+ if (at + MIN_PARTIAL_PREFIX > limit) continue;
2410
2530
  let length = MIN_PARTIAL_PREFIX;
2411
- while (length < needle.length && index.stream.charCodeAt(at + length) === needle.charCodeAt(length)) {
2531
+ while (length < needle.length && at + length < limit && index.stream.charCodeAt(at + length) === needle.charCodeAt(length)) {
2412
2532
  length++;
2413
2533
  }
2534
+ if (region && !touchesRegion(partsOf(index, at, at + length), region)) {
2535
+ continue;
2536
+ }
2414
2537
  if (!best || length > best.length) best = { length, at };
2415
2538
  }
2416
2539
  return best;
2417
2540
  }
2541
+ function pageEnd(index, from) {
2542
+ const page = index.refs[from].pageIndex;
2543
+ let i = from;
2544
+ while (i < index.refs.length && index.refs[i].pageIndex === page) i++;
2545
+ return i < index.positions.length ? index.positions[i] : index.stream.length;
2546
+ }
2547
+ var REGION_TOLERANCE_PT = 2;
2548
+ function touchesRegion(parts, region) {
2549
+ return parts.some(
2550
+ (part) => part.xMax >= region.xMin - REGION_TOLERANCE_PT && part.xMin <= region.xMax + REGION_TOLERANCE_PT && part.yMax >= region.yMin - REGION_TOLERANCE_PT && part.yMin <= region.yMax + REGION_TOLERANCE_PT
2551
+ );
2552
+ }
2418
2553
  function sameRow(a, b) {
2419
2554
  const overlap = Math.min(a.yMax, b.yMax) - Math.max(a.yMin, b.yMin);
2420
2555
  const shorter = Math.min(a.yMax - a.yMin, b.yMax - b.yMin);
@@ -2470,6 +2605,120 @@ function pageNumberRows(pages, claimed) {
2470
2605
  )
2471
2606
  );
2472
2607
  }
2608
+ function insideRegion(occurrences, region) {
2609
+ let best;
2610
+ for (const o of occurrences) {
2611
+ let inside = 0;
2612
+ let total = 0;
2613
+ for (const part of o.parts) {
2614
+ const w = Math.min(part.xMax, region.xMax) - Math.max(part.xMin, region.xMin);
2615
+ const h = Math.min(part.yMax, region.yMax) - Math.max(part.yMin, region.yMin);
2616
+ if (w > 0 && h > 0) inside += w * h;
2617
+ total += (part.xMax - part.xMin) * (part.yMax - part.yMin);
2618
+ }
2619
+ const share = total > 0 ? inside / total : 0;
2620
+ if (share > 0 && (!best || share > best.share)) {
2621
+ best = { occurrence: o, share };
2622
+ }
2623
+ }
2624
+ return best?.occurrence;
2625
+ }
2626
+ function overprintedOccurrence(index, page, region, needle, claimed) {
2627
+ const words2 = index.pages[page]?.words;
2628
+ if (!words2) return void 0;
2629
+ const fragmentOf = /* @__PURE__ */ new Map();
2630
+ index.refs.forEach((ref, i) => {
2631
+ if (ref.pageIndex !== page || index.fragments[i] === "") return;
2632
+ const w = words2[ref.word];
2633
+ const centre = (w.xMin + w.xMax) / 2;
2634
+ if (centre < region.xMin || centre > region.xMax) return;
2635
+ if (w.yMax < region.yMin) return;
2636
+ fragmentOf.set(ref.word, i);
2637
+ });
2638
+ const sequence = groupRows(words2, [...fragmentOf.keys()]).flatMap(
2639
+ (row) => [...row.words].sort((a, b) => words2[a].xMin - words2[b].xMin)
2640
+ );
2641
+ let reach = /* @__PURE__ */ new Map();
2642
+ let best;
2643
+ for (const word of sequence) {
2644
+ const i = fragmentOf.get(word);
2645
+ const text = index.fragments[i];
2646
+ const usable = !claimed(index.positions[i]);
2647
+ const next = /* @__PURE__ */ new Map();
2648
+ const offer = (consumed, path9) => {
2649
+ const held = next.get(consumed);
2650
+ if (!held || path9.skips < held.skips) next.set(consumed, path9);
2651
+ };
2652
+ const inside = words2[word].yMin + words2[word].yMax <= 2 * region.yMax && words2[word].yMin + words2[word].yMax >= 2 * region.yMin;
2653
+ if (usable && inside && needle.startsWith(text)) {
2654
+ offer(text.length, { skips: 0, words: [word] });
2655
+ }
2656
+ for (const [consumed, path9] of reach) {
2657
+ if (usable && needle.startsWith(text, consumed)) {
2658
+ offer(consumed + text.length, {
2659
+ skips: path9.skips,
2660
+ words: [...path9.words, word]
2661
+ });
2662
+ }
2663
+ offer(consumed, { skips: path9.skips + 1, words: path9.words });
2664
+ }
2665
+ const done = next.get(needle.length);
2666
+ if (done && (!best || done.skips < best.skips)) best = done;
2667
+ next.delete(needle.length);
2668
+ reach = next;
2669
+ }
2670
+ if (!best || best.skips > best.words.length) return void 0;
2671
+ const first = fragmentOf.get(best.words[0]);
2672
+ const part = {
2673
+ pageIndex: page,
2674
+ words: best.words,
2675
+ xMin: Math.min(...best.words.map((w) => words2[w].xMin)),
2676
+ yMin: Math.min(...best.words.map((w) => words2[w].yMin)),
2677
+ xMax: Math.max(...best.words.map((w) => words2[w].xMax)),
2678
+ yMax: Math.max(...best.words.map((w) => words2[w].yMax))
2679
+ };
2680
+ const at = index.positions[first];
2681
+ return {
2682
+ occurrence: {
2683
+ at,
2684
+ end: at,
2685
+ pageIndex: page,
2686
+ endPageIndex: page,
2687
+ parts: [part]
2688
+ },
2689
+ claims: best.words.map((w) => {
2690
+ const i = fragmentOf.get(w);
2691
+ return {
2692
+ at: index.positions[i],
2693
+ end: index.positions[i] + index.fragments[i].length
2694
+ };
2695
+ })
2696
+ };
2697
+ }
2698
+ function linedOccurrence(lined, body, segments, claimed) {
2699
+ const bodyFragment = /* @__PURE__ */ new Map();
2700
+ body.refs.forEach((ref, i) => {
2701
+ if (body.fragments[i] !== "")
2702
+ bodyFragment.set(`${ref.pageIndex}:${ref.word}`, i);
2703
+ });
2704
+ for (const hit of findOccurrences(lined, segments)) {
2705
+ const claims = [];
2706
+ for (const part of hit.parts)
2707
+ for (const word of part.words) {
2708
+ const i = bodyFragment.get(`${part.pageIndex}:${word}`);
2709
+ if (i === void 0) continue;
2710
+ claims.push({
2711
+ at: body.positions[i],
2712
+ end: body.positions[i] + body.fragments[i].length
2713
+ });
2714
+ }
2715
+ if (claims.length === 0 || claims.some((claim) => claimed(claim.at)))
2716
+ continue;
2717
+ const at = Math.min(...claims.map((claim) => claim.at));
2718
+ return { occurrence: { ...hit, at, end: at }, claims };
2719
+ }
2720
+ return void 0;
2721
+ }
2473
2722
  function assignInventory(pages, inventory) {
2474
2723
  const results = /* @__PURE__ */ new Map();
2475
2724
  const chromeWords = /* @__PURE__ */ new Set();
@@ -2509,7 +2758,9 @@ function assignInventory(pages, inventory) {
2509
2758
  for (const row of pageNumberRows(pages, chromeWords))
2510
2759
  claimRow(row.pageIndex, row.box);
2511
2760
  const body = indexDocument(pages, chromeWords);
2512
- const claimed = /* @__PURE__ */ new Map();
2761
+ let lined;
2762
+ const claims = [];
2763
+ const claimsOver = (o) => claims.filter((claim) => o.at < claim.end && claim.at < o.end);
2513
2764
  let cursor = 0;
2514
2765
  for (const entry of inventory) {
2515
2766
  if (entry.repeats) continue;
@@ -2519,10 +2770,44 @@ function assignInventory(pages, inventory) {
2519
2770
  results.set(entry, { entry, needle, status: "skipped", occurrences: [] });
2520
2771
  continue;
2521
2772
  }
2522
- let all = findOccurrences(body, segments);
2773
+ let all = findOccurrences(body, segments).filter(
2774
+ (o) => (entry.page === void 0 || o.pageIndex === entry.page && o.endPageIndex === entry.page) && (!entry.region || touchesRegion(o.parts, entry.region))
2775
+ );
2523
2776
  let partial;
2777
+ let sparse;
2778
+ if (all.length === 0 && segments.length === 1 && entry.page !== void 0 && entry.region) {
2779
+ const overprinted = overprintedOccurrence(
2780
+ body,
2781
+ entry.page,
2782
+ entry.region,
2783
+ needle,
2784
+ (offset) => claims.some((claim) => claim.at <= offset && offset < claim.end)
2785
+ );
2786
+ if (overprinted) {
2787
+ all = [overprinted.occurrence];
2788
+ sparse = overprinted.claims;
2789
+ }
2790
+ }
2791
+ if (all.length === 0 && entry.page === void 0 && !entry.optional) {
2792
+ lined ??= indexDocument(pages, chromeWords, linedOrder);
2793
+ const found = linedOccurrence(
2794
+ lined,
2795
+ body,
2796
+ segments,
2797
+ (offset) => claims.some((claim) => claim.at <= offset && offset < claim.end)
2798
+ );
2799
+ if (found) {
2800
+ all = [found.occurrence];
2801
+ sparse = found.claims;
2802
+ }
2803
+ }
2524
2804
  if (all.length === 0 && segments.length === 1 && !entry.optional) {
2525
- const prefix = longestRenderedPrefix(body, needle);
2805
+ const prefix = longestRenderedPrefix(
2806
+ body,
2807
+ needle,
2808
+ entry.page,
2809
+ entry.region
2810
+ );
2526
2811
  if (prefix) {
2527
2812
  all = [occurrence(body, prefix.at, prefix.at + prefix.length)];
2528
2813
  partial = { matchedChars: prefix.length, totalChars: needle.length };
@@ -2537,18 +2822,22 @@ function assignInventory(pages, inventory) {
2537
2822
  });
2538
2823
  continue;
2539
2824
  }
2540
- let free = all.filter((o) => !claimed.has(o.at));
2825
+ let free = all.filter((o) => claimsOver(o).length === 0);
2541
2826
  if (free.length === 0 && !entry.optional) {
2542
- const held = all.find((o) => claimed.get(o.at)?.optional);
2827
+ const held = all.find((o) => {
2828
+ const over = claimsOver(o);
2829
+ return over.length > 0 && over.every((claim) => claim.entry.optional);
2830
+ });
2543
2831
  if (held) {
2544
- const holder = claimed.get(held.at);
2545
- claimed.delete(held.at);
2546
- results.set(holder, {
2547
- entry: holder,
2548
- needle: results.get(holder)?.needle ?? "",
2549
- status: "skipped",
2550
- occurrences: []
2551
- });
2832
+ for (const claim of claimsOver(held)) {
2833
+ claims.splice(claims.indexOf(claim), 1);
2834
+ results.set(claim.entry, {
2835
+ entry: claim.entry,
2836
+ needle: results.get(claim.entry)?.needle ?? "",
2837
+ status: "skipped",
2838
+ occurrences: []
2839
+ });
2840
+ }
2552
2841
  free = [held];
2553
2842
  }
2554
2843
  }
@@ -2561,9 +2850,10 @@ function assignInventory(pages, inventory) {
2561
2850
  });
2562
2851
  continue;
2563
2852
  }
2564
- const chosen = free.find((o) => o.at >= cursor) ?? free[0];
2565
- claimed.set(chosen.at, entry);
2566
- cursor = chosen.at;
2853
+ const chosen = (entry.region && insideRegion(free, entry.region)) ?? free.find((o) => o.at >= cursor) ?? free[0];
2854
+ for (const range of sparse ?? [chosen])
2855
+ claims.push({ at: range.at, end: range.end, entry });
2856
+ if (chosen.at < body.rotatedFrom) cursor = chosen.at;
2567
2857
  results.set(entry, {
2568
2858
  entry,
2569
2859
  needle,
@@ -2595,6 +2885,11 @@ function renderedDraftFor(fact) {
2595
2885
  }
2596
2886
  var VISIBLE_SPILL_PT = 2;
2597
2887
  var OVERLAP_FRACTION = 0.3;
2888
+ var WORD_BOX_SLACK = 0.2;
2889
+ function inkBand(word) {
2890
+ const slack = WORD_BOX_SLACK * Math.max(0, word.yMax - word.yMin);
2891
+ return { yMin: word.yMin + slack, yMax: word.yMax - slack };
2892
+ }
2598
2893
  function finding(draft) {
2599
2894
  const { mapping, page, context, ...rest } = draft;
2600
2895
  return {
@@ -2610,12 +2905,15 @@ function round(value) {
2610
2905
  return Math.round(value * 10) / 10;
2611
2906
  }
2612
2907
  function intersects(a, b) {
2908
+ const inkA = inkBand(a);
2909
+ const inkB = inkBand(b);
2613
2910
  const w = Math.min(a.xMax, b.xMax) - Math.max(a.xMin, b.xMin);
2614
- const h = Math.min(a.yMax, b.yMax) - Math.max(a.yMin, b.yMin);
2911
+ const h = Math.min(inkA.yMax, inkB.yMax) - Math.max(inkA.yMin, inkB.yMin);
2615
2912
  return w > 0 && h > 0 ? w * h : 0;
2616
2913
  }
2617
2914
  function area(w) {
2618
- return Math.max(0, w.xMax - w.xMin) * Math.max(0, w.yMax - w.yMin);
2915
+ const ink = inkBand(w);
2916
+ return Math.max(0, w.xMax - w.xMin) * Math.max(0, ink.yMax - ink.yMin);
2619
2917
  }
2620
2918
  function excerpt(text, max = 60) {
2621
2919
  const flat = text.replace(/\s+/g, " ").trim();
@@ -2731,14 +3029,51 @@ function draftRenderedFindings(input) {
2731
3029
  }
2732
3030
  for (const match of matches) {
2733
3031
  const box = match.entry.box;
3032
+ const region = match.entry.region;
2734
3033
  if (!box || match.status !== "mapped") continue;
2735
3034
  for (const o of match.occurrences) {
3035
+ const boxHeight = box.heightPt;
3036
+ const byEdges = region !== void 0 && boxHeight !== void 0;
3037
+ if (region && boxHeight !== void 0) {
3038
+ const words2 = o.parts.flatMap(
3039
+ (part) => part.words.map((w) => pages[part.pageIndex].words[w])
3040
+ );
3041
+ const inkTop = Math.min(...words2.map((w) => inkBand(w).yMin));
3042
+ const inkBottom = Math.max(...words2.map((w) => inkBand(w).yMax));
3043
+ const edges = [
3044
+ { edge: "bottom", over: inkBottom - region.yMax, at: inkBottom },
3045
+ { edge: "top", over: region.yMin - inkTop, at: inkTop }
3046
+ ].sort((a, b) => b.over - a.over);
3047
+ const [worst2] = edges;
3048
+ if (worst2.over > VISIBLE_SPILL_PT) {
3049
+ findings.push(
3050
+ finding({
3051
+ ruleId: "rendered/spill",
3052
+ mapping: "mapped",
3053
+ page: o.pageIndex + 1,
3054
+ path: match.entry.path,
3055
+ message: `"${excerpt(match.entry.text)}" runs ${round(worst2.over)} pt past the ${worst2.edge} of its ${round(boxHeight)} pt box on page ${o.pageIndex + 1}.`,
3056
+ suggestion: "Shorten the text, reduce its size, or enlarge the box; the renderer let it spill past the edge the author drew.",
3057
+ evidence: {
3058
+ summary: "Rendered ink against the edge of the declared box",
3059
+ actual: round(worst2.at),
3060
+ expected: round(
3061
+ worst2.edge === "bottom" ? region.yMax : region.yMin
3062
+ ),
3063
+ unit: "pt",
3064
+ values: { edge: worst2.edge, overPt: round(worst2.over) }
3065
+ }
3066
+ })
3067
+ );
3068
+ continue;
3069
+ }
3070
+ }
2736
3071
  const extent = unionBox(o.parts);
2737
3072
  const rendered = {
2738
3073
  heightPt: extent.yMax - extent.yMin,
2739
3074
  widthPt: extent.xMax - extent.xMin
2740
3075
  };
2741
- const worst = ["heightPt", "widthPt"].filter((axis) => box[axis] !== void 0).map((axis) => ({
3076
+ const worst = ["heightPt", "widthPt"].filter((axis) => box[axis] !== void 0).filter((axis) => !(byEdges && axis === "heightPt")).map((axis) => ({
2742
3077
  axis,
2743
3078
  declared: box[axis],
2744
3079
  rendered: rendered[axis],
@@ -3152,11 +3487,295 @@ function analyzeRenderedDocument(input, options = {}) {
3152
3487
  };
3153
3488
  }
3154
3489
 
3490
+ // src/rendered-inventory.ts
3491
+ function renderedInventoryFromFacts(format, facts) {
3492
+ const entries = [];
3493
+ for (const fact of facts) {
3494
+ if (format === "docx" && fact.kind === "docx/text") {
3495
+ const frame = fact.frame;
3496
+ entries.push({
3497
+ path: fact.path,
3498
+ text: String(fact.text),
3499
+ role: fact.role,
3500
+ ...typeof fact.level === "number" && { level: fact.level },
3501
+ ...fact.repeats === true && { repeats: true },
3502
+ ...fact.optional === true && { optional: true },
3503
+ ...frame && {
3504
+ box: {
3505
+ ...frame.widthPt !== void 0 && { widthPt: frame.widthPt },
3506
+ ...frame.heightPt !== void 0 && { heightPt: frame.heightPt }
3507
+ }
3508
+ }
3509
+ });
3510
+ } else if (format === "pptx" && fact.kind === "pptx/text") {
3511
+ if (fact.slideHidden === true) continue;
3512
+ const turned = typeof fact.rotationDeg === "number" && (fact.rotationDeg % 360 + 360) % 360 !== 0;
3513
+ if (turned) {
3514
+ entries.push({
3515
+ path: fact.path,
3516
+ text: String(fact.text),
3517
+ role: "slide-text",
3518
+ optional: true,
3519
+ ...typeof fact.page === "number" && { page: fact.page }
3520
+ });
3521
+ continue;
3522
+ }
3523
+ const { boxXPt: x, boxYPt: y } = fact;
3524
+ const widthPt = fact.boxWidthPt;
3525
+ const heightPt = fact.boxHeightPt;
3526
+ entries.push({
3527
+ path: fact.path,
3528
+ text: String(fact.text),
3529
+ role: "slide-text",
3530
+ // A slide is its own page and its boxes are where the deck put
3531
+ // them, so the text is looked for there before anywhere else.
3532
+ ...typeof fact.page === "number" && { page: fact.page },
3533
+ ...typeof x === "number" && typeof y === "number" && typeof widthPt === "number" && typeof heightPt === "number" && {
3534
+ region: {
3535
+ xMin: x,
3536
+ yMin: y,
3537
+ xMax: x + widthPt,
3538
+ yMax: y + heightPt
3539
+ }
3540
+ },
3541
+ ...(typeof widthPt === "number" || typeof heightPt === "number") && {
3542
+ box: {
3543
+ ...typeof widthPt === "number" && { widthPt },
3544
+ ...typeof heightPt === "number" && { heightPt }
3545
+ }
3546
+ }
3547
+ });
3548
+ }
3549
+ }
3550
+ return entries;
3551
+ }
3552
+ function requestedFontsFromFacts(format, facts, resolvedFonts) {
3553
+ const kind = format === "docx" ? "docx/font-family" : "pptx/font-family";
3554
+ const declared = new Set(
3555
+ resolvedFonts.filter((font) => font.declared).map((font) => font.family.trim().toLowerCase())
3556
+ );
3557
+ const seen = /* @__PURE__ */ new Set();
3558
+ const requested = [];
3559
+ const add = (family, path9) => {
3560
+ if (typeof family !== "string" || family.trim() === "") return;
3561
+ const key = family.trim().toLowerCase();
3562
+ if (seen.has(key)) return;
3563
+ seen.add(key);
3564
+ requested.push({
3565
+ family: family.trim(),
3566
+ ...path9 && { path: path9 },
3567
+ ...declared.has(key) && { declared: true }
3568
+ });
3569
+ };
3570
+ for (const fact of facts) {
3571
+ if (fact.kind === kind) add(fact.family, fact.path);
3572
+ }
3573
+ for (const font of resolvedFonts) if (font.declared) add(font.family);
3574
+ return requested;
3575
+ }
3576
+
3155
3577
  // src/block-matrix.ts
3156
3578
  import {
3157
3579
  blockDependencies,
3580
+ blockReferencesFromDocument as blockReferencesFromDocument2,
3581
+ readBlockDefinitions as readBlockDefinitions2
3582
+ } from "@json-to-office/shared";
3583
+
3584
+ // src/matrix-inventory.ts
3585
+ import {
3586
+ blockReferencesFromDocument,
3158
3587
  readBlockDefinitions
3159
3588
  } from "@json-to-office/shared";
3589
+ function matrixUniverse(format, themes) {
3590
+ return {
3591
+ themes: [...themes],
3592
+ canvases: format === "docx" ? ["A4", "LETTER"] : ["wide169", "standard43"],
3593
+ fonts: ["design", "fallback"],
3594
+ edges: ["min", "max"]
3595
+ };
3596
+ }
3597
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3598
+ function templateFormat(name, document) {
3599
+ const root = isRecord(document) ? document.name : void 0;
3600
+ if (root === "docx" || root === "pptx") return root;
3601
+ if (name.endsWith(".docx.json")) return "docx";
3602
+ if (name.endsWith(".pptx.json")) return "pptx";
3603
+ return void 0;
3604
+ }
3605
+ function authoredDeckCanvas(props) {
3606
+ const width = typeof props.slideWidth === "number" ? props.slideWidth : 10;
3607
+ const height = typeof props.slideHeight === "number" ? props.slideHeight : 7.5;
3608
+ return Math.abs(width / height - 16 / 9) < Math.abs(width / height - 4 / 3) ? "wide169" : "standard43";
3609
+ }
3610
+ function requiredRoles(format, profile) {
3611
+ const required = profile?.rules?.[`${format}/required-chrome`]?.parameters?.required;
3612
+ return Array.isArray(required) ? required.filter((role) => typeof role === "string") : [];
3613
+ }
3614
+ function templateConditions(format, props, universe) {
3615
+ const named = typeof props.theme === "string" ? props.theme : void 0;
3616
+ const canvas = format === "pptx" ? authoredDeckCanvas(props) : void 0;
3617
+ const canvasExclusions = (reason) => canvas === void 0 ? [] : universe.canvases.filter((c) => c !== canvas).map((c) => ({ condition: `canvas ${c}`, reason }));
3618
+ if (named !== void 0 && universe.themes.includes(named)) {
3619
+ return {
3620
+ theme: named,
3621
+ conditions: {
3622
+ ...structuredClone(universe),
3623
+ ...canvas !== void 0 && { canvases: [canvas] }
3624
+ },
3625
+ exclusions: canvasExclusions(
3626
+ `slot budgets are measured on the ${canvas} slide the template is drawn on; another shape lays out, but holds fewer words`
3627
+ )
3628
+ };
3629
+ }
3630
+ const theme = named ?? "inline";
3631
+ const why = named === void 0 ? "authored against the template's own theme object" : `authored against "${named}", which is no bundled theme`;
3632
+ const exclusions = [
3633
+ ...universe.themes.map((t) => ({ condition: `theme ${t}`, reason: why })),
3634
+ ...canvasExclusions(
3635
+ "absolute geometry fitted to the template's own slide size"
3636
+ ),
3637
+ ...canvas === void 0 ? universe.canvases.filter((c) => c !== "A4").map((c) => ({
3638
+ condition: `canvas ${c}`,
3639
+ reason: "absolute geometry fitted to the template's own page"
3640
+ })) : [],
3641
+ {
3642
+ condition: "font fallback",
3643
+ reason: "lines fitted to the template's own faces; a wider face is another design, not a boundary"
3644
+ }
3645
+ ];
3646
+ return {
3647
+ theme,
3648
+ conditions: {
3649
+ themes: [theme],
3650
+ canvases: [canvas ?? "A4"],
3651
+ fonts: ["design"],
3652
+ edges: [...universe.edges]
3653
+ },
3654
+ exclusions
3655
+ };
3656
+ }
3657
+ function buildMatrixInventory(templates, options) {
3658
+ const inventory = { templates: [], entries: [] };
3659
+ for (const source of templates) {
3660
+ const format = templateFormat(source.name, source.document);
3661
+ if (!format || !isRecord(source.document)) continue;
3662
+ const props = isRecord(source.document.props) ? source.document.props : {};
3663
+ const universe = matrixUniverse(format, options.themes[format]);
3664
+ const blueprint = options.blueprints?.find(
3665
+ (b) => b.format === format && b.definitions === source.name
3666
+ );
3667
+ const profile = blueprint ? options.profiles?.[blueprint.profile] : void 0;
3668
+ const roles = requiredRoles(format, profile);
3669
+ const { theme, conditions: conditions2, exclusions } = templateConditions(
3670
+ format,
3671
+ props,
3672
+ universe
3673
+ );
3674
+ const definitions = Object.keys(readBlockDefinitions(source.document));
3675
+ const references = blockReferencesFromDocument(source.document, {
3676
+ template: source.name,
3677
+ format
3678
+ });
3679
+ const templateExclusions = [...exclusions];
3680
+ if (definitions.length > 0 && references.length === 0) {
3681
+ templateExclusions.push({
3682
+ condition: "definitions",
3683
+ reason: "the definitions do not validate, so discovery publishes none"
3684
+ });
3685
+ }
3686
+ inventory.templates.push({
3687
+ template: source.name,
3688
+ format,
3689
+ theme,
3690
+ ...blueprint && { profile: blueprint.profile },
3691
+ requiredRoles: roles,
3692
+ definitions,
3693
+ conditions: conditions2,
3694
+ exclusions: templateExclusions
3695
+ });
3696
+ for (const reference of references) {
3697
+ const slotless = Object.keys(reference.definition.slots ?? {}).length === 0;
3698
+ inventory.entries.push({
3699
+ id: `${source.name}#${reference.definitionPointer}`,
3700
+ template: source.name,
3701
+ format,
3702
+ name: reference.name,
3703
+ definitionPointer: reference.definitionPointer,
3704
+ dependencies: reference.dependencies,
3705
+ ...blueprint && { profile: blueprint.profile },
3706
+ requiredRoles: roles,
3707
+ themes: [...conditions2.themes],
3708
+ canvases: [...conditions2.canvases],
3709
+ fonts: [...conditions2.fonts],
3710
+ edges: slotless ? ["max"] : [...conditions2.edges],
3711
+ exclusions: [
3712
+ ...exclusions,
3713
+ ...slotless ? [
3714
+ {
3715
+ condition: "edge min",
3716
+ reason: "no slots, so min and max are the same document"
3717
+ }
3718
+ ] : []
3719
+ ]
3720
+ });
3721
+ }
3722
+ }
3723
+ return inventory;
3724
+ }
3725
+ function inventoryGaps(entry, universe) {
3726
+ const excluded = new Set(entry.exclusions.map((e) => e.condition));
3727
+ const gaps = [];
3728
+ const check = (kind, all, got) => {
3729
+ for (const value of all) {
3730
+ const condition = `${kind} ${value}`;
3731
+ const has = got.includes(value);
3732
+ if (has && excluded.has(condition)) gaps.push(`${condition}: both`);
3733
+ if (!has && !excluded.has(condition)) gaps.push(`${condition}: neither`);
3734
+ }
3735
+ };
3736
+ check("theme", universe.themes, entry.themes);
3737
+ check("canvas", universe.canvases, entry.canvases);
3738
+ check("font", universe.fonts, entry.fonts);
3739
+ check("edge", universe.edges, entry.edges);
3740
+ return gaps;
3741
+ }
3742
+ function describeInventory(inventory) {
3743
+ const lines = [];
3744
+ for (const template of inventory.templates) {
3745
+ lines.push(
3746
+ `${template.template} (${template.format}, theme ${template.theme}${template.profile ? `, profile ${template.profile}` : ""}): ${template.definitions.length} definition(s)`
3747
+ );
3748
+ for (const entry of inventory.entries.filter(
3749
+ (e) => e.template === template.template
3750
+ )) {
3751
+ lines.push(
3752
+ ` ${entry.name}: themes ${entry.themes.join(" ")} \xB7 canvases ${entry.canvases.join(
3753
+ " "
3754
+ )} \xB7 fonts ${entry.fonts.join(" ")} \xB7 edges ${entry.edges.join(" ")}`
3755
+ );
3756
+ const reasons = /* @__PURE__ */ new Map();
3757
+ for (const exclusion of entry.exclusions) {
3758
+ reasons.set(exclusion.reason, [
3759
+ ...reasons.get(exclusion.reason) ?? [],
3760
+ exclusion.condition
3761
+ ]);
3762
+ }
3763
+ for (const [reason, conditions2] of reasons)
3764
+ lines.push(` not ${conditions2.join(", ")}: ${reason}`);
3765
+ }
3766
+ }
3767
+ return lines.join("\n");
3768
+ }
3769
+
3770
+ // src/block-matrix.ts
3771
+ var PPTX_FALLBACK_FONTS = {
3772
+ heading: "DejaVu Sans",
3773
+ body: "DejaVu Sans"
3774
+ };
3775
+ var PPTX_CANVAS_SIZE = {
3776
+ wide169: { slideWidth: 13.333, slideHeight: 7.5 },
3777
+ standard43: { slideWidth: 10, slideHeight: 7.5 }
3778
+ };
3160
3779
  var FALLBACK_FONTS = {
3161
3780
  heading: { family: "DejaVu Sans" },
3162
3781
  body: { family: "DejaVu Sans" }
@@ -3196,7 +3815,7 @@ function widestNumber(length, seed = 0) {
3196
3815
  const integer = group(digits);
3197
3816
  return `\u2212${integer}.${"0".repeat(length - integer.length - 2)}`;
3198
3817
  }
3199
- var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3818
+ var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3200
3819
  var NUMERIC_SLOTS = /* @__PURE__ */ new Set(["value", "delta", "cells"]);
3201
3820
  function stringValue(slot, name, edge, seed) {
3202
3821
  if (slot.enum && slot.enum.length > 0) {
@@ -3238,14 +3857,17 @@ function stringValue(slot, name, edge, seed) {
3238
3857
  return edge === "max" ? words(count, seed, false) : "01";
3239
3858
  return words(count, seed, sentence);
3240
3859
  }
3241
- function componentValue(name, edge, seed) {
3242
- if (name === "chart") {
3860
+ function componentValue(name, edge, seed, format = "docx") {
3861
+ const chart = name === "chart" || format === "pptx" && name === "content";
3862
+ if (chart && (name === "chart" || edge === "max")) {
3243
3863
  const categories = edge === "max" ? 8 : 2;
3244
3864
  const series = edge === "max" ? 3 : 1;
3245
3865
  return {
3246
3866
  name: "chart",
3247
3867
  props: {
3248
- type: "column",
3868
+ // The native column chart: `column` in a report, `bar` standing
3869
+ // upright in a deck.
3870
+ type: format === "pptx" ? "bar" : "column",
3249
3871
  valAxisTitle: edge === "max" ? `${words(3, seed, false)} (\u20ACm)` : "Revenue (\u20ACm)",
3250
3872
  ...edge === "max" && { catAxisTitle: words(3, seed + 3, false) },
3251
3873
  chartColors: ["primary", "secondary", "accent"].slice(0, series),
@@ -3266,6 +3888,12 @@ function componentValue(name, edge, seed) {
3266
3888
  }
3267
3889
  };
3268
3890
  }
3891
+ if (format === "pptx") {
3892
+ return {
3893
+ name: "image",
3894
+ props: { path: MATRIX_IMAGE, alt: words(6, seed, false) }
3895
+ };
3896
+ }
3269
3897
  return {
3270
3898
  name: "image",
3271
3899
  props: {
@@ -3275,7 +3903,7 @@ function componentValue(name, edge, seed) {
3275
3903
  }
3276
3904
  };
3277
3905
  }
3278
- function boundarySlotValue(slot, name, edge, seed = 0) {
3906
+ function boundarySlotValue(slot, name, edge, seed = 0, format = "docx") {
3279
3907
  switch (slot.type) {
3280
3908
  case "string":
3281
3909
  return stringValue(slot, name, edge, seed);
@@ -3285,12 +3913,18 @@ function boundarySlotValue(slot, name, edge, seed = 0) {
3285
3913
  case "boolean":
3286
3914
  return edge === "max" ? true : slot.default ?? true;
3287
3915
  case "component":
3288
- return componentValue(name, edge, seed);
3916
+ return componentValue(name, edge, seed, format);
3289
3917
  case "object": {
3290
3918
  const out = {};
3291
3919
  for (const [key, property] of Object.entries(slot.properties ?? {})) {
3292
3920
  if (edge === "min" && !property.required) continue;
3293
- const value = boundarySlotValue(property, key, edge, seed + key.length);
3921
+ const value = boundarySlotValue(
3922
+ property,
3923
+ key,
3924
+ edge,
3925
+ seed + key.length,
3926
+ format
3927
+ );
3294
3928
  if (value !== void 0) out[key] = value;
3295
3929
  }
3296
3930
  return out;
@@ -3300,21 +3934,21 @@ function boundarySlotValue(slot, name, edge, seed = 0) {
3300
3934
  const item = slot.items ?? { type: "string" };
3301
3935
  return Array.from(
3302
3936
  { length: count },
3303
- (_, i) => boundarySlotValue(item, name, edge, seed + i)
3937
+ (_, i) => boundarySlotValue(item, name, edge, seed + i, format)
3304
3938
  );
3305
3939
  }
3306
3940
  default:
3307
3941
  return void 0;
3308
3942
  }
3309
3943
  }
3310
- function boundaryInvocation(name, definition, edge, seed = 0, requiredRoles = []) {
3944
+ function boundaryInvocation(name, definition, edge, seed = 0, requiredRoles2 = [], format = "docx") {
3311
3945
  const slots = {};
3312
3946
  let offset = seed;
3313
3947
  for (const [slotName, slot] of Object.entries(definition.slots)) {
3314
3948
  offset += slotName.length;
3315
- const required = slot.required === true || slot.role !== void 0 && requiredRoles.includes(slot.role);
3949
+ const required = slot.required === true || slot.role !== void 0 && requiredRoles2.includes(slot.role);
3316
3950
  if (edge === "min" && !required) continue;
3317
- const value = boundarySlotValue(slot, slotName, edge, offset);
3951
+ const value = boundarySlotValue(slot, slotName, edge, offset, format);
3318
3952
  if (value !== void 0) slots[slotName] = value;
3319
3953
  }
3320
3954
  const labels = slots.labels;
@@ -3322,17 +3956,30 @@ function boundaryInvocation(name, definition, edge, seed = 0, requiredRoles = []
3322
3956
  if (Array.isArray(labels) && Array.isArray(columns)) {
3323
3957
  const cells = definition.slots.columns?.items?.properties?.cells;
3324
3958
  for (const column of columns) {
3325
- if (isRecord(column) && Array.isArray(column.cells) && cells?.items) {
3959
+ if (isRecord2(column) && Array.isArray(column.cells) && cells?.items) {
3326
3960
  column.cells = labels.map(
3327
- (_, i) => boundarySlotValue(cells.items, "cells", edge, offset + i)
3961
+ (_, i) => boundarySlotValue(
3962
+ cells.items,
3963
+ "cells",
3964
+ edge,
3965
+ offset + i,
3966
+ format
3967
+ )
3328
3968
  );
3329
3969
  }
3330
3970
  }
3331
3971
  }
3332
3972
  return { name: "block", props: { ref: name, slots } };
3333
3973
  }
3334
- function overBudgetInvocation(name, definition, seed = 0) {
3335
- const invocation = boundaryInvocation(name, definition, "max", seed);
3974
+ function overBudgetInvocation(name, definition, seed = 0, format = "docx") {
3975
+ const invocation = boundaryInvocation(
3976
+ name,
3977
+ definition,
3978
+ "max",
3979
+ seed,
3980
+ [],
3981
+ format
3982
+ );
3336
3983
  const slots = invocation.props.slots;
3337
3984
  for (const [slotName, slot] of Object.entries(definition.slots)) {
3338
3985
  if (slot.type === "string" && slot.maxWords !== void 0) {
@@ -3350,27 +3997,41 @@ function overBudgetInvocation(name, definition, seed = 0) {
3350
3997
  return void 0;
3351
3998
  }
3352
3999
  function enumerateBlockDefinitions(template, templateName) {
3353
- const definitions = readBlockDefinitions(template);
4000
+ const format = templateFormat(templateName, template);
4001
+ if (!format)
4002
+ throw new Error(`${templateName} is neither a report nor a deck.`);
4003
+ const references = blockReferencesFromDocument2(template, {
4004
+ template: templateName,
4005
+ format
4006
+ });
4007
+ const declared = Object.keys(readBlockDefinitions2(template));
4008
+ if (references.length !== declared.length)
4009
+ throw new Error(
4010
+ `${templateName}: discovery publishes ${references.length} of ${declared.length} definitions; the rest do not validate.`
4011
+ );
3354
4012
  const examples = /* @__PURE__ */ new Map();
3355
4013
  const walk = (node) => {
3356
4014
  if (Array.isArray(node)) {
3357
4015
  node.forEach(walk);
3358
4016
  return;
3359
4017
  }
3360
- if (!isRecord(node)) return;
4018
+ if (!isRecord2(node)) return;
3361
4019
  const props = node.props;
3362
- if (node.name === "block" && isRecord(props) && typeof props.ref === "string" && !examples.has(props.ref)) {
3363
- examples.set(props.ref, isRecord(props.slots) ? props.slots : {});
4020
+ if (node.name === "block" && isRecord2(props) && typeof props.ref === "string" && !examples.has(props.ref)) {
4021
+ examples.set(props.ref, isRecord2(props.slots) ? props.slots : {});
3364
4022
  }
3365
4023
  walk(node.children);
3366
4024
  };
3367
- walk(isRecord(template) ? template.children : void 0);
3368
- return Object.entries(definitions).map(([name, definition]) => ({
3369
- name,
4025
+ walk(isRecord2(template) ? template.children : void 0);
4026
+ return references.map((reference) => ({
4027
+ name: reference.name,
4028
+ format,
3370
4029
  template: templateName,
3371
- pointer: `/props/blocks/${name.replace(/~/g, "~0").replace(/\//g, "~1")}`,
3372
- definition,
3373
- ...examples.has(name) && { example: examples.get(name) }
4030
+ pointer: reference.definitionPointer,
4031
+ definition: reference.definition,
4032
+ ...examples.has(reference.name) && {
4033
+ example: examples.get(reference.name)
4034
+ }
3374
4035
  }));
3375
4036
  }
3376
4037
  var CHROME = ["cover", "running-head", "section-opener"];
@@ -3382,19 +4043,27 @@ function nominal(entry, name, definitions) {
3382
4043
  props: { ref: name, slots: structuredClone(entry.example) }
3383
4044
  };
3384
4045
  }
3385
- return boundaryInvocation(name, definitions[name], "min");
4046
+ return boundaryInvocation(
4047
+ name,
4048
+ definitions[name],
4049
+ "min",
4050
+ 0,
4051
+ [],
4052
+ entry.format
4053
+ );
3386
4054
  }
3387
- function inlineImages(value) {
3388
- if (Array.isArray(value)) return value.map(inlineImages);
3389
- if (!isRecord(value)) return value;
3390
- if (value.name === "image" && isRecord(value.props) && typeof value.props.path === "string") {
3391
- const props = { ...value.props, base64: MATRIX_IMAGE };
3392
- delete props.path;
4055
+ function inlineImages(value, format = "docx") {
4056
+ if (Array.isArray(value)) return value.map((v) => inlineImages(v, format));
4057
+ if (!isRecord2(value)) return value;
4058
+ if (value.name === "image" && isRecord2(value.props) && typeof value.props.path === "string" && !value.props.path.startsWith("data:")) {
4059
+ const props = format === "pptx" ? { ...value.props, path: MATRIX_IMAGE } : { ...value.props, base64: MATRIX_IMAGE };
4060
+ if (format === "docx") delete props.path;
3393
4061
  return { ...value, props };
3394
4062
  }
3395
- if (value.name === "highcharts") return componentValue("chart", "min", 0);
4063
+ if (value.name === "highcharts")
4064
+ return componentValue("chart", "min", 0, format);
3396
4065
  return Object.fromEntries(
3397
- Object.entries(value).map(([k, v]) => [k, inlineImages(v)])
4066
+ Object.entries(value).map(([k, v]) => [k, inlineImages(v, format)])
3398
4067
  );
3399
4068
  }
3400
4069
  function withDefinitions(definitions, names) {
@@ -3410,9 +4079,9 @@ function withDefinitions(definitions, names) {
3410
4079
  }
3411
4080
  function conditions(props, children, theme, font, canvas) {
3412
4081
  const sections = children.map((child) => {
3413
- if (!isRecord(child) || child.name !== "section") return child;
3414
- const props2 = isRecord(child.props) ? child.props : {};
3415
- const page = isRecord(props2.page) ? props2.page : {};
4082
+ if (!isRecord2(child) || child.name !== "section") return child;
4083
+ const props2 = isRecord2(child.props) ? child.props : {};
4084
+ const page = isRecord2(props2.page) ? props2.page : {};
3416
4085
  return { ...child, props: { ...props2, page: { ...page, size: canvas } } };
3417
4086
  });
3418
4087
  const chart = JSON.stringify(sections).includes('"name":"chart"');
@@ -3428,12 +4097,12 @@ function conditions(props, children, theme, font, canvas) {
3428
4097
  };
3429
4098
  }
3430
4099
  var BODY_COPY = words(40, 1e3, true);
3431
- function blockCaseDocument(entries, name, edge, theme, { font = "design", canvas = "A4", requiredRoles = [] } = {}) {
4100
+ function blockCaseDocument(entries, name, edge, theme, { font = "design", canvas = "A4", requiredRoles: requiredRoles2 = [] } = {}) {
3432
4101
  const definitions = Object.fromEntries(
3433
4102
  entries.map((e) => [e.name, e.definition])
3434
4103
  );
3435
4104
  const byName = new Map(entries.map((e) => [e.name, e]));
3436
- const under = (chrome) => chrome === name ? boundaryInvocation(name, definitions[name], edge, 0, requiredRoles) : inlineImages(nominal(byName.get(chrome), chrome, definitions));
4105
+ const under = (chrome) => chrome === name ? boundaryInvocation(name, definitions[name], edge, 0, requiredRoles2) : inlineImages(nominal(byName.get(chrome), chrome, definitions));
3437
4106
  const cover = under("cover");
3438
4107
  const runningHead = under("running-head");
3439
4108
  const opener = under("section-opener");
@@ -3445,7 +4114,7 @@ function blockCaseDocument(entries, name, edge, theme, { font = "design", canvas
3445
4114
  );
3446
4115
  }
3447
4116
  body.push(
3448
- boundaryInvocation(name, definitions[name], edge, 0, requiredRoles)
4117
+ boundaryInvocation(name, definitions[name], edge, 0, requiredRoles2)
3449
4118
  );
3450
4119
  }
3451
4120
  body.push({ name: "paragraph", props: { text: BODY_COPY } });
@@ -3476,26 +4145,26 @@ function blockCaseDocument(entries, name, edge, theme, { font = "design", canvas
3476
4145
  canvas
3477
4146
  );
3478
4147
  }
3479
- function reportCaseDocument(template, edge, theme, { font = "design", canvas = "A4", requiredRoles = [] } = {}) {
3480
- if (!isRecord(template) || !isRecord(template.props))
4148
+ function reportCaseDocument(template, edge, theme, { font = "design", canvas = "A4", requiredRoles: requiredRoles2 = [] } = {}) {
4149
+ if (!isRecord2(template) || !isRecord2(template.props))
3481
4150
  throw new Error("A template is a document with props.");
3482
- const definitions = readBlockDefinitions(template);
4151
+ const definitions = readBlockDefinitions2(template);
3483
4152
  let seed = 0;
3484
4153
  const rewrite = (node) => {
3485
4154
  if (Array.isArray(node)) return node.map(rewrite);
3486
- if (!isRecord(node)) return node;
4155
+ if (!isRecord2(node)) return node;
3487
4156
  const props2 = node.props;
3488
- if (node.name === "block" && isRecord(props2) && typeof props2.ref === "string" && definitions[props2.ref]) {
4157
+ if (node.name === "block" && isRecord2(props2) && typeof props2.ref === "string" && definitions[props2.ref]) {
3489
4158
  seed += 7;
3490
4159
  return boundaryInvocation(
3491
4160
  props2.ref,
3492
4161
  definitions[props2.ref],
3493
4162
  edge,
3494
4163
  seed,
3495
- requiredRoles
4164
+ requiredRoles2
3496
4165
  );
3497
4166
  }
3498
- if (node.name === "paragraph" && isRecord(props2) && typeof props2.text === "string") {
4167
+ if (node.name === "paragraph" && isRecord2(props2) && typeof props2.text === "string") {
3499
4168
  return {
3500
4169
  ...node,
3501
4170
  props: { ...props2, text: edge === "max" ? BODY_COPY : props2.text }
@@ -3556,6 +4225,200 @@ function generateBlockMatrix(template, templateName, options) {
3556
4225
  }
3557
4226
  return cases;
3558
4227
  }
4228
+ function deckConditions(props, children, theme, font, canvas, themeObject) {
4229
+ const out = structuredClone(props);
4230
+ delete out.qualityProfile;
4231
+ const inline = typeof props.theme !== "string";
4232
+ if (!inline || theme !== "inline") {
4233
+ if (font === "fallback") {
4234
+ const base = themeObject?.(theme);
4235
+ if (!base)
4236
+ throw new Error(
4237
+ `The fallback case on "${theme}" needs the theme's object.`
4238
+ );
4239
+ const fonts = isRecord2(base.fonts) ? base.fonts : {};
4240
+ out.theme = {
4241
+ ...structuredClone(base),
4242
+ fonts: { ...fonts, ...PPTX_FALLBACK_FONTS }
4243
+ };
4244
+ } else {
4245
+ out.theme = theme;
4246
+ }
4247
+ }
4248
+ if ((canvas === "wide169" || canvas === "standard43") && authoredDeckCanvas(props) !== canvas)
4249
+ Object.assign(out, PPTX_CANVAS_SIZE[canvas]);
4250
+ return { name: "pptx", props: out, children };
4251
+ }
4252
+ function templateProps(template) {
4253
+ if (!isRecord2(template) || !isRecord2(template.props))
4254
+ throw new Error("A template is a document with props.");
4255
+ return template.props;
4256
+ }
4257
+ function deckBlockCaseDocument(template, entries, name, edge, theme, {
4258
+ font = "design",
4259
+ canvas = "wide169",
4260
+ requiredRoles: requiredRoles2 = [],
4261
+ themeObject
4262
+ } = {}) {
4263
+ const props = templateProps(template);
4264
+ const definitions = Object.fromEntries(
4265
+ entries.map((e) => [e.name, e.definition])
4266
+ );
4267
+ const byName = new Map(entries.map((e) => [e.name, e]));
4268
+ const used = /* @__PURE__ */ new Set([name]);
4269
+ const slides = [];
4270
+ if (name !== "cover" && byName.has("cover")) {
4271
+ used.add("cover");
4272
+ slides.push({
4273
+ name: "slide",
4274
+ children: [
4275
+ inlineImages(
4276
+ nominal(byName.get("cover"), "cover", definitions),
4277
+ "pptx"
4278
+ )
4279
+ ]
4280
+ });
4281
+ }
4282
+ slides.push({
4283
+ name: "slide",
4284
+ children: [
4285
+ boundaryInvocation(
4286
+ name,
4287
+ definitions[name],
4288
+ edge,
4289
+ 0,
4290
+ requiredRoles2,
4291
+ "pptx"
4292
+ )
4293
+ ]
4294
+ });
4295
+ return deckConditions(
4296
+ { ...props, blocks: withDefinitions(definitions, used) },
4297
+ slides,
4298
+ theme,
4299
+ font,
4300
+ canvas,
4301
+ themeObject
4302
+ );
4303
+ }
4304
+ function deckReportCaseDocument(template, edge, theme, {
4305
+ font = "design",
4306
+ canvas = "wide169",
4307
+ requiredRoles: requiredRoles2 = [],
4308
+ themeObject
4309
+ } = {}) {
4310
+ const props = templateProps(template);
4311
+ const definitions = readBlockDefinitions2(template);
4312
+ let seed = 0;
4313
+ const rewrite = (node) => {
4314
+ if (Array.isArray(node)) return node.map(rewrite);
4315
+ if (!isRecord2(node)) return node;
4316
+ const nodeProps = node.props;
4317
+ if (node.name === "block" && isRecord2(nodeProps) && typeof nodeProps.ref === "string" && definitions[nodeProps.ref]) {
4318
+ seed += 7;
4319
+ return boundaryInvocation(
4320
+ nodeProps.ref,
4321
+ definitions[nodeProps.ref],
4322
+ edge,
4323
+ seed,
4324
+ requiredRoles2,
4325
+ "pptx"
4326
+ );
4327
+ }
4328
+ return Array.isArray(node.children) ? { ...node, children: rewrite(node.children) } : node;
4329
+ };
4330
+ return deckConditions(
4331
+ props,
4332
+ rewrite(template.children),
4333
+ theme,
4334
+ font,
4335
+ canvas,
4336
+ themeObject
4337
+ );
4338
+ }
4339
+ var stem = (template) => template.replace(/\.(docx|pptx)\.json$/, "");
4340
+ function generateMatrixCases(inventory, templates, selection = {}, { themeObject } = {}) {
4341
+ const pick = (supported, wanted) => supported.filter((value) => !wanted || wanted.includes(value));
4342
+ const cases = [];
4343
+ for (const summary of inventory.templates) {
4344
+ if (selection.templates && !selection.templates.includes(summary.template))
4345
+ continue;
4346
+ const source = templates.find((t) => t.name === summary.template);
4347
+ const entries = inventory.entries.filter(
4348
+ (e) => e.template === summary.template
4349
+ );
4350
+ if (!source || entries.length === 0) continue;
4351
+ const definitions = enumerateBlockDefinitions(
4352
+ source.document,
4353
+ summary.template
4354
+ );
4355
+ const withSlots = entries.some((e) => e.edges.includes("min"));
4356
+ for (const theme of pick(summary.conditions.themes, selection.themes))
4357
+ for (const font of pick(summary.conditions.fonts, selection.fonts))
4358
+ for (const canvas of pick(
4359
+ summary.conditions.canvases,
4360
+ selection.canvases
4361
+ ))
4362
+ for (const edge of pick(summary.conditions.edges, selection.edges)) {
4363
+ const shared = {
4364
+ format: summary.format,
4365
+ template: summary.template,
4366
+ ...summary.profile && { profile: summary.profile },
4367
+ edge,
4368
+ theme,
4369
+ font,
4370
+ canvas
4371
+ };
4372
+ const conditions2 = {
4373
+ font,
4374
+ canvas,
4375
+ requiredRoles: summary.requiredRoles,
4376
+ themeObject
4377
+ };
4378
+ if (selection.blocks !== false)
4379
+ for (const entry of entries) {
4380
+ if (!entry.edges.includes(edge)) continue;
4381
+ cases.push({
4382
+ ...shared,
4383
+ id: `${stem(summary.template)}/${entry.name}@${edge}/${theme}/${font}/${canvas}`,
4384
+ block: entry.name,
4385
+ document: summary.format === "docx" ? blockCaseDocument(
4386
+ definitions,
4387
+ entry.name,
4388
+ edge,
4389
+ theme,
4390
+ conditions2
4391
+ ) : deckBlockCaseDocument(
4392
+ source.document,
4393
+ definitions,
4394
+ entry.name,
4395
+ edge,
4396
+ theme,
4397
+ conditions2
4398
+ )
4399
+ });
4400
+ }
4401
+ if (selection.report !== false && withSlots)
4402
+ cases.push({
4403
+ ...shared,
4404
+ id: `${stem(summary.template)}/report@${edge}/${theme}/${font}/${canvas}`,
4405
+ block: "report",
4406
+ document: summary.format === "docx" ? reportCaseDocument(
4407
+ source.document,
4408
+ edge,
4409
+ theme,
4410
+ conditions2
4411
+ ) : deckReportCaseDocument(
4412
+ source.document,
4413
+ edge,
4414
+ theme,
4415
+ conditions2
4416
+ )
4417
+ });
4418
+ }
4419
+ }
4420
+ return cases;
4421
+ }
3559
4422
  export {
3560
4423
  DocxFormatAdapter,
3561
4424
  FALLBACK_FONTS,
@@ -3565,6 +4428,8 @@ export {
3565
4428
  MATRIX_IMAGE,
3566
4429
  MacOSCoreTextStager,
3567
4430
  NoopFontStager,
4431
+ PPTX_CANVAS_SIZE,
4432
+ PPTX_FALLBACK_FONTS,
3568
4433
  PptxFormatAdapter,
3569
4434
  RENDERED_QUALITY_RULES,
3570
4435
  VISIBLE_SPILL_PT,
@@ -3575,10 +4440,14 @@ export {
3575
4440
  blockCaseDocument,
3576
4441
  boundaryInvocation,
3577
4442
  boundarySlotValue,
4443
+ buildMatrixInventory,
3578
4444
  clearRasterizerCache,
3579
4445
  createAdapter,
3580
4446
  createLibreOfficePptxBatchRasterizer,
3581
4447
  createLibreOfficePptxRasterizer,
4448
+ deckBlockCaseDocument,
4449
+ deckReportCaseDocument,
4450
+ describeInventory,
3582
4451
  emitDiagnostic,
3583
4452
  enumerateBlockDefinitions,
3584
4453
  extractPdfFonts,
@@ -3586,9 +4455,12 @@ export {
3586
4455
  extractPdfTextGeometry,
3587
4456
  familyRendered,
3588
4457
  generateBlockMatrix,
4458
+ generateMatrixCases,
3589
4459
  getFontStager,
3590
4460
  getRasterizerCacheStats,
3591
4461
  inkedRows,
4462
+ inventoryGaps,
4463
+ matrixUniverse,
3592
4464
  normalizeForMatch,
3593
4465
  overBudgetInvocation,
3594
4466
  pageInkFromPgm,
@@ -3598,9 +4470,12 @@ export {
3598
4470
  pdffontsAvailable,
3599
4471
  pdftotextAvailable,
3600
4472
  readingOrder,
4473
+ renderedInventoryFromFacts,
3601
4474
  reportCaseDocument,
4475
+ requestedFontsFromFacts,
3602
4476
  runWithDiagnosticSink,
3603
4477
  stderrDiagnosticSink,
4478
+ templateFormat,
3604
4479
  widestNumber
3605
4480
  };
3606
4481
  //# sourceMappingURL=index.js.map