gutterpress 0.11.20-alpha.1 → 0.11.20-alpha.2

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 (31) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-cmf4nbpm.js → build-qmjg9r2d.js} +4 -4
  3. package/dist/checks/pdf/index.d.ts +1 -0
  4. package/dist/checks/pdf/safe-margins.d.ts +3 -0
  5. package/dist/{cli-7shp2kmj.js → cli-7z29z4wz.js} +1 -1
  6. package/dist/{cli-mmm22cza.js → cli-gh0ym80r.js} +1 -1
  7. package/dist/{cli-rtpm04dv.js → cli-j802s31j.js} +5 -5
  8. package/dist/{cli-x1nfhaf1.js → cli-zacevwcy.js} +284 -180
  9. package/dist/cli.js +12 -12
  10. package/dist/{doctor-0d6esj02.js → doctor-fe7tcs4v.js} +2 -2
  11. package/dist/{engine-1v8kmv3a.js → engine-9xb1ky8d.js} +1 -1
  12. package/dist/{engine-adxptzf0.js → engine-yax79k9m.js} +2 -2
  13. package/dist/{ext-65nfh7st.js → ext-ahwxt6gv.js} +4 -4
  14. package/dist/{gutterpress-viewer-0r4rt4de.js → gutterpress-viewer-retj6ym2.js} +343 -98
  15. package/dist/{index-pnvzgftg.js → index-3nr5rzzp.js} +5 -5
  16. package/dist/{index-cngw658w.js → index-6xwtb7cw.js} +1 -1
  17. package/dist/{index-1y885h1j.js → index-razpty47.js} +282 -178
  18. package/dist/index.js +3 -3
  19. package/dist/lib/presets.d.ts +3 -1
  20. package/dist/{manifest.schema-nrykd2c6.json → manifest.schema-74s1ykyv.json} +8 -2
  21. package/dist/{new-y4a2xz3g.js → new-ftd4fec4.js} +4 -4
  22. package/dist/{preflight-kt7wfgbd.js → preflight-8sqarcsr.js} +4 -4
  23. package/dist/preview/file-watcher.d.ts +7 -1
  24. package/dist/{preview-bridge-evecsh4w.js → preview-bridge-8wjcjec3.js} +3 -2
  25. package/dist/{preview-interface-k2mj5kvq.js → preview-interface-nnmrxyqm.js} +37 -41
  26. package/dist/{preview-5ze728kp.js → preview-q76xn95h.js} +4 -4
  27. package/dist/{preview-shell-7jfe6vsc.js → preview-shell-wk9k3gcd.js} +18 -6
  28. package/dist/{publish-5tpcyysh.js → publish-7gdy6gbf.js} +4 -4
  29. package/dist/schema/manifest.types.d.ts +27 -6
  30. package/dist/{validate-p0hkqkfe.js → validate-e3ryegce.js} +4 -4
  31. package/package.json +1 -1
@@ -14,12 +14,12 @@ import {
14
14
  stampCreator,
15
15
  stripAnnotations,
16
16
  warn
17
- } from "./cli-mmm22cza.js";
17
+ } from "./cli-gh0ym80r.js";
18
18
  import {
19
19
  DEBOUNCE,
20
20
  UsageError,
21
21
  resolvePort
22
- } from "./cli-7shp2kmj.js";
22
+ } from "./cli-7z29z4wz.js";
23
23
  import {
24
24
  blackTextToGray
25
25
  } from "./cli-ht1bcqdd.js";
@@ -28,7 +28,7 @@ import {
28
28
  getAssetPath,
29
29
  getAssetsDir,
30
30
  lastPageMode
31
- } from "./cli-rtpm04dv.js";
31
+ } from "./cli-j802s31j.js";
32
32
  import {
33
33
  BuildError,
34
34
  EXIT_CODES
@@ -106,6 +106,11 @@ function pinnedNpmSpecifier(name, version) {
106
106
  // src/lib/presets.ts
107
107
  var PRESET_IDS = ["dtrpg", "book", "custom"];
108
108
  var DTRPG_PRESET = {
109
+ id: "dtrpg",
110
+ spec: {
111
+ label: "DriveThruRPG — Quick Specifications for Print Books",
112
+ url: "https://help.drivethrupartners.com/hc/en-us/articles/12780800178583-Quick-Specifications-for-Print-Books"
113
+ },
109
114
  defaultTargets: ["dtrpg"],
110
115
  extensions: [],
111
116
  source: {
@@ -124,7 +129,8 @@ var DTRPG_PRESET = {
124
129
  height: 810,
125
130
  tolerance: 0.5,
126
131
  bleed: 9,
127
- safe: 18
132
+ safe: 36,
133
+ artSafe: 18
128
134
  },
129
135
  ink: {
130
136
  maxTac: 240,
@@ -153,6 +159,10 @@ var DTRPG_PRESET = {
153
159
  "pdf.print.embedded-fonts": {
154
160
  enabled: true,
155
161
  severity: "error"
162
+ },
163
+ "pdf.print.safe-margins": {
164
+ enabled: true,
165
+ severity: "error"
156
166
  }
157
167
  },
158
168
  source: {
@@ -185,6 +195,7 @@ var DTRPG_PRESET = {
185
195
  }
186
196
  };
187
197
  var BOOK_PRESET = {
198
+ id: "book",
188
199
  defaultTargets: [],
189
200
  extensions: [],
190
201
  source: {
@@ -203,7 +214,8 @@ var BOOK_PRESET = {
203
214
  height: 648,
204
215
  tolerance: 0.5,
205
216
  bleed: undefined,
206
- safe: 18
217
+ safe: 18,
218
+ artSafe: undefined
207
219
  },
208
220
  ink: {
209
221
  maxTac: 400,
@@ -225,6 +237,10 @@ var BOOK_PRESET = {
225
237
  enabled: true,
226
238
  severity: "error"
227
239
  },
240
+ "pdf.print.safe-margins": {
241
+ enabled: true,
242
+ severity: "warning"
243
+ },
228
244
  "pdf.print.pdfx-markers": false,
229
245
  "pdf.print.pdfx-metadata": false
230
246
  },
@@ -259,6 +275,7 @@ var BOOK_PRESET = {
259
275
  };
260
276
  var CUSTOM_PRESET = {
261
277
  ...BOOK_PRESET,
278
+ id: "custom",
262
279
  page: null,
263
280
  defaultTargets: []
264
281
  };
@@ -304,7 +321,8 @@ var DTRPG_TARGET = {
304
321
  "pdf.structure.qpdf": { enabled: true, severity: "error" },
305
322
  "pdf.print.pdfx-markers": { enabled: true, severity: "error" },
306
323
  "pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
307
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" }
324
+ "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
325
+ "pdf.print.safe-margins": { enabled: true, severity: "error" }
308
326
  },
309
327
  assets: {
310
328
  allowedColorSpaces: [...DTRPG_PRESET.validate.assets.allowedColorSpaces]
@@ -319,7 +337,8 @@ var DTRPG_TARGET = {
319
337
  "pdf.structure.qpdf",
320
338
  "pdf.print.pdfx-markers",
321
339
  "pdf.print.pdfx-metadata",
322
- "pdf.print.embedded-fonts"
340
+ "pdf.print.embedded-fonts",
341
+ "pdf.print.safe-margins"
323
342
  ],
324
343
  requiredTools: ["qpdf", "gs"]
325
344
  };
@@ -334,6 +353,7 @@ var ITCH_TARGET = {
334
353
  "pdf.print.pdfx-markers": false,
335
354
  "pdf.print.pdfx-metadata": false,
336
355
  "pdf.print.ink-coverage": false,
356
+ "pdf.print.safe-margins": false,
337
357
  "asset.image.tac-raster": false
338
358
  },
339
359
  assets: {
@@ -552,7 +572,8 @@ function resolveCustomPage(c, m) {
552
572
  height,
553
573
  tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
554
574
  bleed: c.page?.bleed ?? m.page?.bleed,
555
- safe: c.page?.safe ?? m.page?.safe
575
+ safe: c.page?.safe ?? m.page?.safe,
576
+ artSafe: c.page?.artSafe ?? m.page?.artSafe
556
577
  };
557
578
  }
558
579
  function resolveWithPreset(cliOverrides, manifest, preset) {
@@ -597,6 +618,8 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
597
618
  return {
598
619
  title: c.title ?? m.title ?? "Document",
599
620
  authors: c.authors ?? m.authors ?? [],
621
+ preset: preset.id,
622
+ spec: preset.spec,
600
623
  styles: c.styles ?? m.styles,
601
624
  extensions,
602
625
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
@@ -5272,6 +5295,79 @@ var check2 = {
5272
5295
  };
5273
5296
  registerCheck(check2);
5274
5297
 
5298
+ // src/checks/pdf/safe-margins.ts
5299
+ var TOLERANCE_PT = 0.5;
5300
+ var ABOVE_BASELINE = 0.8;
5301
+ var BELOW_BASELINE = 0.2;
5302
+ var inches = (pt) => `${+(pt / 72).toFixed(2)}in`;
5303
+ var check3 = {
5304
+ id: "pdf.print.safe-margins",
5305
+ name: "Safe margins",
5306
+ description: "Fails when text sits closer to the trim than `page.safe` (DriveThruRPG: 0.5in on every edge). Text only: art is judged in the preview's safe-area overlay, not here. Skipped when `page.safe` is not set",
5307
+ category: "pdf",
5308
+ phase: "post-build",
5309
+ async run(ctx) {
5310
+ const { width, height, bleed = 0, safe = 0 } = ctx.config.page;
5311
+ if (!ctx.pdfPath || !safe)
5312
+ return [];
5313
+ const doc = await loadPdf(ctx.pdfPath);
5314
+ if (!doc) {
5315
+ return [inspectionFailed(check3.id, "Could not parse the PDF's text positions.", { file: ctx.pdfPath })];
5316
+ }
5317
+ const rule = ctx.config.spec?.label ?? "page.safe";
5318
+ const results = [];
5319
+ try {
5320
+ for (let pageNum = 1;pageNum <= doc.numPages; pageNum++) {
5321
+ const recto = pageNum % 2 === 1;
5322
+ const trim = {
5323
+ left: recto ? 0 : bleed,
5324
+ right: width - (recto ? bleed : 0),
5325
+ bottom: bleed,
5326
+ top: height - bleed
5327
+ };
5328
+ const { items } = await (await doc.getPage(pageNum)).getTextContent();
5329
+ for (const item of items) {
5330
+ if (!("str" in item) || !item.str.trim())
5331
+ continue;
5332
+ const [a, b, c, d, e, f] = item.transform;
5333
+ const along = item.width / Math.hypot(a, b);
5334
+ const xs = [];
5335
+ const ys = [];
5336
+ for (const s of [0, 1]) {
5337
+ for (const u of [-BELOW_BASELINE, ABOVE_BASELINE]) {
5338
+ xs.push(e + s * along * a + u * c);
5339
+ ys.push(f + s * along * b + u * d);
5340
+ }
5341
+ }
5342
+ const gaps = [
5343
+ [recto ? "binding" : "outside", Math.min(...xs) - trim.left],
5344
+ [recto ? "outside" : "binding", trim.right - Math.max(...xs)],
5345
+ ["bottom", Math.min(...ys) - trim.bottom],
5346
+ ["top", trim.top - Math.max(...ys)]
5347
+ ];
5348
+ const [edge, gap] = gaps.reduce((worst, g) => g[1] < worst[1] ? g : worst);
5349
+ const shortfall = safe - gap;
5350
+ if (shortfall <= TOLERANCE_PT)
5351
+ continue;
5352
+ const text = item.str.trim();
5353
+ results.push(finding(check3.id, {
5354
+ severity: "error",
5355
+ message: `Page ${pageNum}: "${text.slice(0, 40)}${text.length > 40 ? "…" : ""}" is ${inches(shortfall)} too close to the ${edge} edge.`,
5356
+ file: ctx.pdfPath,
5357
+ detail: `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.`,
5358
+ code: "text-outside-safe-margin",
5359
+ data: { page: pageNum, edge, shortfallPt: +shortfall.toFixed(2) }
5360
+ }));
5361
+ }
5362
+ }
5363
+ } catch {
5364
+ return [inspectionFailed(check3.id, "Could not read the PDF's text positions.", { file: ctx.pdfPath })];
5365
+ }
5366
+ return results;
5367
+ }
5368
+ };
5369
+ registerCheck(check3);
5370
+
5275
5371
  // src/checks/pdf/pdfx-structure.ts
5276
5372
  function isObject(value) {
5277
5373
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5380,7 +5476,7 @@ function getPdfxMetadataIssues(objects, flavor) {
5380
5476
  }
5381
5477
 
5382
5478
  // src/checks/pdf/pdfx-markers.ts
5383
- var check3 = {
5479
+ var check4 = {
5384
5480
  id: "pdf.print.pdfx-markers",
5385
5481
  name: "PDF/X Markers",
5386
5482
  description: "Checks PDF/X OutputIntent structure via qpdf JSON",
@@ -5399,22 +5495,22 @@ var check3 = {
5399
5495
  const objects = parseQpdfObjectsJson(stdout);
5400
5496
  if (!objects) {
5401
5497
  return [
5402
- inspectionFailed(check3.id, "Unable to parse qpdf JSON output for PDF/X checks.", { file: ctx.pdfPath })
5498
+ inspectionFailed(check4.id, "Unable to parse qpdf JSON output for PDF/X checks.", { file: ctx.pdfPath })
5403
5499
  ];
5404
5500
  }
5405
- return getPdfxOutputIntentIssues(objects).map((message) => finding(check3.id, {
5501
+ return getPdfxOutputIntentIssues(objects).map((message) => finding(check4.id, {
5406
5502
  severity: "error",
5407
5503
  message,
5408
5504
  file: ctx.pdfPath
5409
5505
  }));
5410
5506
  } catch {
5411
5507
  return [
5412
- inspectionFailed(check3.id, "Failed to inspect PDF objects with qpdf for PDF/X markers.", { file: ctx.pdfPath })
5508
+ inspectionFailed(check4.id, "Failed to inspect PDF objects with qpdf for PDF/X markers.", { file: ctx.pdfPath })
5413
5509
  ];
5414
5510
  }
5415
5511
  }
5416
5512
  };
5417
- registerCheck(check3);
5513
+ registerCheck(check4);
5418
5514
 
5419
5515
  // src/lib/pdf-parse.ts
5420
5516
  import { readFile as readFile8 } from "node:fs/promises";
@@ -5467,7 +5563,7 @@ async function getPerPageInkCoverage(pdfPath) {
5467
5563
  }
5468
5564
 
5469
5565
  // src/checks/pdf/color-spaces.ts
5470
- var check4 = {
5566
+ var check5 = {
5471
5567
  id: "pdf.print.color-spaces",
5472
5568
  name: "Color Spaces",
5473
5569
  description: "Checks for forbidden color spaces (DeviceRGB, Lab, Separation, DeviceN)",
@@ -5480,7 +5576,7 @@ var check4 = {
5480
5576
  const results = [];
5481
5577
  if (/\/DeviceRGB/.test(bytes)) {
5482
5578
  results.push({
5483
- checkId: check4.id,
5579
+ checkId: check5.id,
5484
5580
  severity: "error",
5485
5581
  message: "DeviceRGB found (interior must be CMYK or grayscale only).",
5486
5582
  file: ctx.pdfPath
@@ -5488,7 +5584,7 @@ var check4 = {
5488
5584
  }
5489
5585
  if (/\/Lab\b/.test(bytes)) {
5490
5586
  results.push({
5491
- checkId: check4.id,
5587
+ checkId: check5.id,
5492
5588
  severity: "error",
5493
5589
  message: "Lab color space found (not allowed).",
5494
5590
  file: ctx.pdfPath
@@ -5496,7 +5592,7 @@ var check4 = {
5496
5592
  }
5497
5593
  if (/\/Separation/.test(bytes)) {
5498
5594
  results.push({
5499
- checkId: check4.id,
5595
+ checkId: check5.id,
5500
5596
  severity: "error",
5501
5597
  message: "Spot color (Separation) found (not allowed).",
5502
5598
  file: ctx.pdfPath
@@ -5504,7 +5600,7 @@ var check4 = {
5504
5600
  }
5505
5601
  if (/\/DeviceN/.test(bytes)) {
5506
5602
  results.push({
5507
- checkId: check4.id,
5603
+ checkId: check5.id,
5508
5604
  severity: "error",
5509
5605
  message: "Spot color (DeviceN) found (not allowed).",
5510
5606
  file: ctx.pdfPath
@@ -5513,10 +5609,10 @@ var check4 = {
5513
5609
  return results;
5514
5610
  }
5515
5611
  };
5516
- registerCheck(check4);
5612
+ registerCheck(check5);
5517
5613
 
5518
5614
  // src/checks/pdf/embedded-fonts.ts
5519
- var check5 = {
5615
+ var check6 = {
5520
5616
  id: "pdf.print.embedded-fonts",
5521
5617
  name: "Embedded Fonts",
5522
5618
  description: "Verifies all fonts in the PDF are embedded",
@@ -5532,7 +5628,7 @@ var check5 = {
5532
5628
  if (fonts.length === 0) {
5533
5629
  return [
5534
5630
  {
5535
- checkId: check5.id,
5631
+ checkId: check6.id,
5536
5632
  severity: "warning",
5537
5633
  message: "No fonts detected (unexpected).",
5538
5634
  file: ctx.pdfPath,
@@ -5545,7 +5641,7 @@ var check5 = {
5545
5641
  const names = notEmbedded.map((f) => f.name);
5546
5642
  return [
5547
5643
  {
5548
- checkId: check5.id,
5644
+ checkId: check6.id,
5549
5645
  severity: "error",
5550
5646
  message: `Not all fonts are embedded (${names.join(", ")}). Check @font-face and Chromium output.`,
5551
5647
  file: ctx.pdfPath,
@@ -5557,10 +5653,10 @@ var check5 = {
5557
5653
  return [];
5558
5654
  }
5559
5655
  };
5560
- registerCheck(check5);
5656
+ registerCheck(check6);
5561
5657
 
5562
5658
  // src/checks/pdf/ink-coverage.ts
5563
- var check6 = {
5659
+ var check7 = {
5564
5660
  id: "pdf.print.ink-coverage",
5565
5661
  name: "Ink Coverage (TAC)",
5566
5662
  description: "Checks total area coverage (TAC) against maximum ink limits",
@@ -5574,7 +5670,7 @@ var check6 = {
5574
5670
  if (!inkResult.ok) {
5575
5671
  return [
5576
5672
  {
5577
- checkId: check6.id,
5673
+ checkId: check7.id,
5578
5674
  severity: "warning",
5579
5675
  message: "Ink coverage could not be measured",
5580
5676
  file: ctx.pdfPath,
@@ -5599,7 +5695,7 @@ var check6 = {
5599
5695
  }
5600
5696
  return [
5601
5697
  {
5602
- checkId: check6.id,
5698
+ checkId: check7.id,
5603
5699
  severity: "warning",
5604
5700
  message: `Total ink coverage too high on ${offending.length} page(s) (max ${maxTac.toFixed(1)}%, recommended <=${ctx.config.ink.maxTac}%)`,
5605
5701
  file: ctx.pdfPath,
@@ -5623,10 +5719,10 @@ var check6 = {
5623
5719
  ];
5624
5720
  }
5625
5721
  };
5626
- registerCheck(check6);
5722
+ registerCheck(check7);
5627
5723
 
5628
5724
  // src/checks/pdf/rasterized-pages.ts
5629
- var check7 = {
5725
+ var check8 = {
5630
5726
  id: "pdf.print.rasterized-pages",
5631
5727
  name: "Rasterized Pages",
5632
5728
  description: "Detects pages that appear to be fully rasterized (CSS filters, blend modes, transparency)",
@@ -5666,7 +5762,7 @@ var check7 = {
5666
5762
  rasterizedPages.sort((a, b) => a - b);
5667
5763
  return [
5668
5764
  {
5669
- checkId: check7.id,
5765
+ checkId: check8.id,
5670
5766
  severity: "warning",
5671
5767
  message: `Possible rasterized pages detected: ${rasterizedPages.join(", ")}`,
5672
5768
  file: ctx.pdfPath,
@@ -5677,11 +5773,11 @@ var check7 = {
5677
5773
  ];
5678
5774
  }
5679
5775
  };
5680
- registerCheck(check7);
5776
+ registerCheck(check8);
5681
5777
 
5682
5778
  // src/checks/pdf/black-text.ts
5683
5779
  import { readFile as readFile9 } from "node:fs/promises";
5684
- var check8 = {
5780
+ var check9 = {
5685
5781
  id: "pdf.print.black-text",
5686
5782
  name: "Black Text",
5687
5783
  description: "Warns when text at or below 24pt is painted in a dark four-colour mix (black, or a near-black colour such as a dark brown)",
@@ -5697,7 +5793,7 @@ var check8 = {
5697
5793
  if (found.runs === 0)
5698
5794
  return [];
5699
5795
  return [
5700
- finding(check8.id, {
5796
+ finding(check9.id, {
5701
5797
  severity: "warning",
5702
5798
  message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
5703
5799
  file: ctx.pdfPath,
@@ -5708,17 +5804,17 @@ var check8 = {
5708
5804
  ];
5709
5805
  } catch {
5710
5806
  return [
5711
- inspectionFailed(check8.id, "Could not inspect the PDF's text colours.", {
5807
+ inspectionFailed(check9.id, "Could not inspect the PDF's text colours.", {
5712
5808
  file: ctx.pdfPath
5713
5809
  })
5714
5810
  ];
5715
5811
  }
5716
5812
  }
5717
5813
  };
5718
- registerCheck(check8);
5814
+ registerCheck(check9);
5719
5815
 
5720
5816
  // src/checks/pdf/signature.ts
5721
- var check9 = {
5817
+ var check10 = {
5722
5818
  id: "pdf.print.signature",
5723
5819
  name: "Signature",
5724
5820
  description: "Fails when the interior's page count does not fit the print signature (a multiple of it ending on a blank page, or one short of a multiple)",
@@ -5730,7 +5826,7 @@ var check9 = {
5730
5826
  return [];
5731
5827
  const doc = await loadPdf(ctx.pdfPath);
5732
5828
  if (!doc) {
5733
- return [inspectionFailed(check9.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
5829
+ return [inspectionFailed(check10.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
5734
5830
  }
5735
5831
  const size = ctx.config.print.signature;
5736
5832
  const pages = doc.numPages;
@@ -5739,7 +5835,7 @@ var check9 = {
5739
5835
  const data = { pages, signature: size };
5740
5836
  if (pages % size !== 0) {
5741
5837
  return [
5742
- finding(check9.id, {
5838
+ finding(check10.id, {
5743
5839
  severity: "error",
5744
5840
  message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
5745
5841
  file: ctx.pdfPath,
@@ -5755,10 +5851,10 @@ var check9 = {
5755
5851
  if (!await pageHasMarks(doc, pages))
5756
5852
  return [];
5757
5853
  } catch {
5758
- return [inspectionFailed(check9.id, "Could not read the last page.", { file: ctx.pdfPath })];
5854
+ return [inspectionFailed(check10.id, "Could not read the last page.", { file: ctx.pdfPath })];
5759
5855
  }
5760
5856
  return [
5761
- finding(check9.id, {
5857
+ finding(check10.id, {
5762
5858
  severity: "error",
5763
5859
  message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
5764
5860
  file: ctx.pdfPath,
@@ -5769,10 +5865,10 @@ var check9 = {
5769
5865
  ];
5770
5866
  }
5771
5867
  };
5772
- registerCheck(check9);
5868
+ registerCheck(check10);
5773
5869
 
5774
5870
  // src/checks/pdf/bookmarks.ts
5775
- var check10 = {
5871
+ var check11 = {
5776
5872
  id: "pdf.nav.bookmarks",
5777
5873
  name: "Bookmarks",
5778
5874
  description: "Checks for PDF outline (bookmarks) tree",
@@ -5786,14 +5882,14 @@ var check10 = {
5786
5882
  const doc = await loadPdf(ctx.pdfPath);
5787
5883
  if (!doc) {
5788
5884
  return [
5789
- inspectionFailed(check10.id, "Could not inspect PDF for bookmarks.", {
5885
+ inspectionFailed(check11.id, "Could not inspect PDF for bookmarks.", {
5790
5886
  file: ctx.pdfPath
5791
5887
  })
5792
5888
  ];
5793
5889
  }
5794
5890
  if (await getOutlineCount(doc) === 0) {
5795
5891
  return [
5796
- finding(check10.id, {
5892
+ finding(check11.id, {
5797
5893
  severity: "warning",
5798
5894
  message: "PDF does not contain bookmarks (outline tree).",
5799
5895
  file: ctx.pdfPath
@@ -5803,10 +5899,10 @@ var check10 = {
5803
5899
  return [];
5804
5900
  }
5805
5901
  };
5806
- registerCheck(check10);
5902
+ registerCheck(check11);
5807
5903
 
5808
5904
  // src/checks/pdf/toc-links.ts
5809
- var check11 = {
5905
+ var check12 = {
5810
5906
  id: "pdf.nav.toc-links",
5811
5907
  name: "TOC Links",
5812
5908
  description: "Detects link annotations on TOC pages",
@@ -5821,7 +5917,7 @@ var check11 = {
5821
5917
  if (!doc) {
5822
5918
  return [
5823
5919
  {
5824
- checkId: check11.id,
5920
+ checkId: check12.id,
5825
5921
  severity: "warning",
5826
5922
  message: "Could not inspect PDF for TOC links.",
5827
5923
  file: ctx.pdfPath
@@ -5832,7 +5928,7 @@ var check11 = {
5832
5928
  if (!hasLinks) {
5833
5929
  return [
5834
5930
  {
5835
- checkId: check11.id,
5931
+ checkId: check12.id,
5836
5932
  severity: "warning",
5837
5933
  message: "No link annotations found in PDF (expected for TOC).",
5838
5934
  file: ctx.pdfPath
@@ -5842,10 +5938,10 @@ var check11 = {
5842
5938
  return [];
5843
5939
  }
5844
5940
  };
5845
- registerCheck(check11);
5941
+ registerCheck(check12);
5846
5942
 
5847
5943
  // src/checks/pdf/cross-refs.ts
5848
- var check12 = {
5944
+ var check13 = {
5849
5945
  id: "pdf.nav.cross-refs",
5850
5946
  name: "Cross References",
5851
5947
  description: "Counts and verifies internal link annotations",
@@ -5858,7 +5954,7 @@ var check12 = {
5858
5954
  if (!doc) {
5859
5955
  return [
5860
5956
  {
5861
- checkId: check12.id,
5957
+ checkId: check13.id,
5862
5958
  severity: "info",
5863
5959
  message: "Could not inspect PDF for cross-references.",
5864
5960
  file: ctx.pdfPath
@@ -5868,7 +5964,7 @@ var check12 = {
5868
5964
  const linkCount = await countLinkAnnotations(doc);
5869
5965
  return [
5870
5966
  {
5871
- checkId: check12.id,
5967
+ checkId: check13.id,
5872
5968
  severity: "info",
5873
5969
  message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
5874
5970
  file: ctx.pdfPath
@@ -5876,10 +5972,10 @@ var check12 = {
5876
5972
  ];
5877
5973
  }
5878
5974
  };
5879
- registerCheck(check12);
5975
+ registerCheck(check13);
5880
5976
 
5881
5977
  // src/checks/pdf/page-labels.ts
5882
- var check13 = {
5978
+ var check14 = {
5883
5979
  id: "pdf.nav.page-labels",
5884
5980
  name: "Page Labels",
5885
5981
  description: "Verifies PDF page labels / folio numbering",
@@ -5894,7 +5990,7 @@ var check13 = {
5894
5990
  const labels = await getPageLabels(doc);
5895
5991
  return [
5896
5992
  {
5897
- checkId: check13.id,
5993
+ checkId: check14.id,
5898
5994
  severity: "info",
5899
5995
  message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
5900
5996
  file: ctx.pdfPath
@@ -5902,10 +5998,10 @@ var check13 = {
5902
5998
  ];
5903
5999
  }
5904
6000
  };
5905
- registerCheck(check13);
6001
+ registerCheck(check14);
5906
6002
 
5907
6003
  // src/checks/pdf/image-resolution.ts
5908
- var check14 = {
6004
+ var check15 = {
5909
6005
  id: "pdf.print.image-resolution",
5910
6006
  name: "Image Resolution",
5911
6007
  description: "Checks DPI of images embedded in the PDF",
@@ -5932,7 +6028,7 @@ var check14 = {
5932
6028
  lowResPages.sort((a, b) => a - b);
5933
6029
  return [
5934
6030
  {
5935
- checkId: check14.id,
6031
+ checkId: check15.id,
5936
6032
  severity: "warning",
5937
6033
  message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
5938
6034
  file: ctx.pdfPath
@@ -5942,10 +6038,10 @@ var check14 = {
5942
6038
  return [];
5943
6039
  }
5944
6040
  };
5945
- registerCheck(check14);
6041
+ registerCheck(check15);
5946
6042
 
5947
6043
  // src/checks/pdf/transparency.ts
5948
- var check15 = {
6044
+ var check16 = {
5949
6045
  id: "pdf.print.transparency",
5950
6046
  name: "Transparency",
5951
6047
  description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
@@ -5967,7 +6063,7 @@ var check15 = {
5967
6063
  if (found.length > 0) {
5968
6064
  return [
5969
6065
  {
5970
- checkId: check15.id,
6066
+ checkId: check16.id,
5971
6067
  severity: "warning",
5972
6068
  message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
5973
6069
  file: ctx.pdfPath
@@ -5977,7 +6073,7 @@ var check15 = {
5977
6073
  return [];
5978
6074
  }
5979
6075
  };
5980
- registerCheck(check15);
6076
+ registerCheck(check16);
5981
6077
 
5982
6078
  // src/checks/pdf/bleed.ts
5983
6079
  function findBox(bytes, box) {
@@ -5987,7 +6083,7 @@ function findBox(bytes, box) {
5987
6083
  return null;
5988
6084
  return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
5989
6085
  }
5990
- var check16 = {
6086
+ var check17 = {
5991
6087
  id: "pdf.print.bleed",
5992
6088
  name: "Bleed Box",
5993
6089
  description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
@@ -6001,7 +6097,7 @@ var check16 = {
6001
6097
  const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
6002
6098
  if (!bytes) {
6003
6099
  return [
6004
- inspectionFailed(check16.id, "Could not inspect PDF for bleed boxes.", {
6100
+ inspectionFailed(check17.id, "Could not inspect PDF for bleed boxes.", {
6005
6101
  file: ctx.pdfPath
6006
6102
  })
6007
6103
  ];
@@ -6011,14 +6107,14 @@ var check16 = {
6011
6107
  const bleed = findBox(bytes, "BleedBox");
6012
6108
  if (!media) {
6013
6109
  return [
6014
- inspectionFailed(check16.id, "Could not parse MediaBox from PDF.", {
6110
+ inspectionFailed(check17.id, "Could not parse MediaBox from PDF.", {
6015
6111
  file: ctx.pdfPath
6016
6112
  })
6017
6113
  ];
6018
6114
  }
6019
6115
  if (!trim && !bleed) {
6020
6116
  return [
6021
- finding(check16.id, {
6117
+ finding(check17.id, {
6022
6118
  severity: "warning",
6023
6119
  message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
6024
6120
  file: ctx.pdfPath
@@ -6031,7 +6127,7 @@ var check16 = {
6031
6127
  const bleedSize = ctx.config.validate.pdf.bleedSize;
6032
6128
  if (mediaW - trimW < bleedSize * 2 * 0.9) {
6033
6129
  return [
6034
- finding(check16.id, {
6130
+ finding(check17.id, {
6035
6131
  severity: "warning",
6036
6132
  message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
6037
6133
  file: ctx.pdfPath
@@ -6042,10 +6138,10 @@ var check16 = {
6042
6138
  return [];
6043
6139
  }
6044
6140
  };
6045
- registerCheck(check16);
6141
+ registerCheck(check17);
6046
6142
 
6047
6143
  // src/checks/pdf/pdfx-metadata.ts
6048
- var check17 = {
6144
+ var check18 = {
6049
6145
  id: "pdf.print.pdfx-metadata",
6050
6146
  name: "PDF/X Metadata",
6051
6147
  description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
@@ -6064,22 +6160,22 @@ var check17 = {
6064
6160
  const objects = parseQpdfObjectsJson(stdout);
6065
6161
  if (!objects) {
6066
6162
  return [
6067
- inspectionFailed(check17.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
6163
+ inspectionFailed(check18.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
6068
6164
  ];
6069
6165
  }
6070
- return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check17.id, {
6166
+ return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check18.id, {
6071
6167
  severity: "error",
6072
6168
  message,
6073
6169
  file: ctx.pdfPath
6074
6170
  }));
6075
6171
  } catch {
6076
6172
  return [
6077
- inspectionFailed(check17.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
6173
+ inspectionFailed(check18.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
6078
6174
  ];
6079
6175
  }
6080
6176
  }
6081
6177
  };
6082
- registerCheck(check17);
6178
+ registerCheck(check18);
6083
6179
 
6084
6180
  // src/checks/source/markdownlint.ts
6085
6181
  import { readFile as readFile10 } from "node:fs/promises";
@@ -6134,7 +6230,7 @@ async function loadConfig(configPath) {
6134
6230
  return {};
6135
6231
  }
6136
6232
  var MARKDOWNLINT_CHECK_ID = "source.markdownlint";
6137
- var check18 = {
6233
+ var check19 = {
6138
6234
  id: MARKDOWNLINT_CHECK_ID,
6139
6235
  name: "Markdownlint",
6140
6236
  description: "Runs markdownlint with project config to validate Markdown files",
@@ -6159,7 +6255,7 @@ var check18 = {
6159
6255
  continue;
6160
6256
  for (const v of violations) {
6161
6257
  out.push({
6162
- checkId: check18.id,
6258
+ checkId: check19.id,
6163
6259
  severity: "warning",
6164
6260
  message: `${v.ruleDescription} (${v.ruleNames.join("/")})`,
6165
6261
  file,
@@ -6172,7 +6268,7 @@ var check18 = {
6172
6268
  return out;
6173
6269
  }
6174
6270
  };
6175
- registerCheck(check18);
6271
+ registerCheck(check19);
6176
6272
 
6177
6273
  // src/checks/source/htmlhint.ts
6178
6274
  import { existsSync as existsSync7 } from "node:fs";
@@ -6180,7 +6276,7 @@ import { readFile as readFile11 } from "node:fs/promises";
6180
6276
  import htmlhint from "htmlhint";
6181
6277
  var { HTMLHint } = htmlhint;
6182
6278
  var CONFIG_NAMES2 = [".htmlhintrc"];
6183
- var check19 = {
6279
+ var check20 = {
6184
6280
  id: "source.htmlhint",
6185
6281
  name: "HTMLHint",
6186
6282
  description: "Runs htmlhint with project config to validate generated HTML",
@@ -6200,7 +6296,7 @@ var check19 = {
6200
6296
  const html = await readFile11(ctx.htmlPath, "utf8");
6201
6297
  const messages = HTMLHint.verify(html, ruleset);
6202
6298
  return messages.map((m) => ({
6203
- checkId: check19.id,
6299
+ checkId: check20.id,
6204
6300
  severity: m.type === "error" ? "error" : "warning",
6205
6301
  message: `${m.rule.id}: ${m.message}`,
6206
6302
  file: ctx.htmlPath,
@@ -6209,7 +6305,7 @@ var check19 = {
6209
6305
  }));
6210
6306
  }
6211
6307
  };
6212
- registerCheck(check19);
6308
+ registerCheck(check20);
6213
6309
 
6214
6310
  // src/checks/source/stylelint.ts
6215
6311
  import { readFile as readFile12 } from "node:fs/promises";
@@ -6602,7 +6698,7 @@ function checkCssRaw(css, from) {
6602
6698
  }
6603
6699
 
6604
6700
  // src/checks/source/stylelint.ts
6605
- var check20 = {
6701
+ var check21 = {
6606
6702
  id: "source.stylelint",
6607
6703
  name: "Print-safety CSS",
6608
6704
  description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
@@ -6622,7 +6718,7 @@ var check20 = {
6622
6718
  css = await readFile12(file, "utf8");
6623
6719
  } catch (err) {
6624
6720
  results.push({
6625
- checkId: check20.id,
6721
+ checkId: check21.id,
6626
6722
  severity: "error",
6627
6723
  message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
6628
6724
  file
@@ -6631,7 +6727,7 @@ var check20 = {
6631
6727
  }
6632
6728
  for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
6633
6729
  results.push({
6634
- checkId: check20.id,
6730
+ checkId: check21.id,
6635
6731
  severity: w.severity,
6636
6732
  message: `${w.rule}: ${w.message}`,
6637
6733
  file,
@@ -6643,7 +6739,7 @@ var check20 = {
6643
6739
  return results;
6644
6740
  }
6645
6741
  };
6646
- registerCheck(check20);
6742
+ registerCheck(check21);
6647
6743
 
6648
6744
  // src/checks/source/css-ownership.ts
6649
6745
  import { readFile as readFile13 } from "node:fs/promises";
@@ -6827,7 +6923,7 @@ function checkOwnership(root, file, maps) {
6827
6923
  });
6828
6924
  return out;
6829
6925
  }
6830
- var check21 = {
6926
+ var check22 = {
6831
6927
  id: "source.css-ownership",
6832
6928
  name: "CSS ownership contract",
6833
6929
  description: "Checks an optional per-project CSS ownership contract (.gutterpress/css-contract.yaml): which file may declare which properties/at-rules, and which selectors must never appear unscoped.",
@@ -6850,7 +6946,7 @@ var check21 = {
6850
6946
  } catch (err) {
6851
6947
  return [
6852
6948
  {
6853
- checkId: check21.id,
6949
+ checkId: check22.id,
6854
6950
  severity: "error",
6855
6951
  message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
6856
6952
  file: configPath
@@ -6892,7 +6988,7 @@ var check21 = {
6892
6988
  }
6893
6989
  for (const v of violations) {
6894
6990
  results.push({
6895
- checkId: check21.id,
6991
+ checkId: check22.id,
6896
6992
  severity: "warning",
6897
6993
  message: v.message,
6898
6994
  file: absFile,
@@ -6904,7 +7000,7 @@ var check21 = {
6904
7000
  return results;
6905
7001
  }
6906
7002
  };
6907
- registerCheck(check21);
7003
+ registerCheck(check22);
6908
7004
 
6909
7005
  // src/checks/source/local-refs.ts
6910
7006
  import { existsSync as existsSync9 } from "node:fs";
@@ -7295,7 +7391,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
7295
7391
  }
7296
7392
 
7297
7393
  // src/checks/source/local-refs.ts
7298
- var check22 = {
7394
+ var check23 = {
7299
7395
  id: "source.links.local-refs",
7300
7396
  name: "Local Markdown References",
7301
7397
  description: "Checks local markdown links and image refs resolve on disk",
@@ -7303,12 +7399,12 @@ var check22 = {
7303
7399
  phase: "pre-build",
7304
7400
  async run(ctx) {
7305
7401
  const results = [];
7306
- for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
7402
+ for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
7307
7403
  if (isNonFilesystemRef(ref))
7308
7404
  continue;
7309
7405
  const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
7310
7406
  if (escape) {
7311
- results.push(finding(check22.id, {
7407
+ results.push(finding(check23.id, {
7312
7408
  severity: "error",
7313
7409
  message: escape,
7314
7410
  file,
@@ -7318,7 +7414,7 @@ var check22 = {
7318
7414
  }
7319
7415
  if (localRefExists(ref, kind, file, ctx.inputDir))
7320
7416
  continue;
7321
- results.push(finding(check22.id, {
7417
+ results.push(finding(check23.id, {
7322
7418
  severity: kind === "image" ? "warning" : "error",
7323
7419
  code: kind === "image" ? "missing-image-placeholder" : undefined,
7324
7420
  message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
@@ -7343,7 +7439,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
7343
7439
  return existsSync9(fileRelative);
7344
7440
  }
7345
7441
  }
7346
- registerCheck(check22);
7442
+ registerCheck(check23);
7347
7443
 
7348
7444
  // src/lib/build-staging.ts
7349
7445
  import path8 from "node:path";
@@ -7609,7 +7705,7 @@ async function createStageRoot() {
7609
7705
  }
7610
7706
 
7611
7707
  // src/checks/source/dangling-links.ts
7612
- var check23 = {
7708
+ var check24 = {
7613
7709
  id: "source.links.dangling",
7614
7710
  name: "Dangling Links",
7615
7711
  description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
@@ -7617,10 +7713,10 @@ var check23 = {
7617
7713
  phase: "pre-build",
7618
7714
  async run(ctx) {
7619
7715
  const results = [];
7620
- for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
7716
+ for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check24.id, results)) {
7621
7717
  if (kind !== "link" || isPrintResolvableHref(ref))
7622
7718
  continue;
7623
- results.push(finding(check23.id, {
7719
+ results.push(finding(check24.id, {
7624
7720
  severity: "warning",
7625
7721
  code: "dangling-link",
7626
7722
  data: { ref },
@@ -7632,11 +7728,11 @@ var check23 = {
7632
7728
  return results;
7633
7729
  }
7634
7730
  };
7635
- registerCheck(check23);
7731
+ registerCheck(check24);
7636
7732
 
7637
7733
  // src/checks/source/accessibility-alt-text.ts
7638
7734
  import { readFile as readFile16 } from "node:fs/promises";
7639
- var check24 = {
7735
+ var check25 = {
7640
7736
  id: "source.accessibility.alt-text",
7641
7737
  name: "Image Alt Text",
7642
7738
  description: "Checks markdown images include non-empty alt text",
@@ -7665,7 +7761,7 @@ var check24 = {
7665
7761
  if (alt.trim())
7666
7762
  continue;
7667
7763
  results.push({
7668
- checkId: check24.id,
7764
+ checkId: check25.id,
7669
7765
  severity: "warning",
7670
7766
  message: "Image is missing alt text",
7671
7767
  file,
@@ -7690,11 +7786,11 @@ function extractImageAlts(line) {
7690
7786
  }
7691
7787
  return alts;
7692
7788
  }
7693
- registerCheck(check24);
7789
+ registerCheck(check25);
7694
7790
 
7695
7791
  // src/checks/source/accessibility-heading-order.ts
7696
7792
  import { readFile as readFile17 } from "node:fs/promises";
7697
- var check25 = {
7793
+ var check26 = {
7698
7794
  id: "source.accessibility.heading-order",
7699
7795
  name: "Heading Order",
7700
7796
  description: "Checks markdown heading levels do not jump by more than one",
@@ -7722,7 +7818,7 @@ var check25 = {
7722
7818
  if (inFence)
7723
7819
  continue;
7724
7820
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
7725
- if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
7821
+ if (comment && parseDisableDirective(comment[1])?.includes(check26.id)) {
7726
7822
  silencedLine = i + 1;
7727
7823
  continue;
7728
7824
  }
@@ -7732,7 +7828,7 @@ var check25 = {
7732
7828
  const level = match[1].length;
7733
7829
  if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
7734
7830
  results.push({
7735
- checkId: check25.id,
7831
+ checkId: check26.id,
7736
7832
  severity: "warning",
7737
7833
  message: `Heading level jump from h${prevLevel} to h${level}`,
7738
7834
  file,
@@ -7746,7 +7842,7 @@ var check25 = {
7746
7842
  return results;
7747
7843
  }
7748
7844
  };
7749
- registerCheck(check25);
7845
+ registerCheck(check26);
7750
7846
 
7751
7847
  // src/checks/source/layout-markers.ts
7752
7848
  import { readFile as readFile18 } from "node:fs/promises";
@@ -7857,7 +7953,7 @@ async function collectKnownClasses(ctx, plugins) {
7857
7953
  }
7858
7954
  return names;
7859
7955
  }
7860
- var check26 = {
7956
+ var check27 = {
7861
7957
  id: "source.markdown.layout-markers",
7862
7958
  name: "Layout Markers",
7863
7959
  description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
@@ -7869,7 +7965,7 @@ var check26 = {
7869
7965
  return [];
7870
7966
  const results = [];
7871
7967
  const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
7872
- results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
7968
+ results.push(inspectionFailed(check27.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
7873
7969
  });
7874
7970
  const md = createMarkdownRenderer(plugins);
7875
7971
  let known;
@@ -7888,7 +7984,7 @@ var check26 = {
7888
7984
  if (names.has(cls) || warnedLines.has(use.line))
7889
7985
  continue;
7890
7986
  const guess = nearestKnownClass(cls, names);
7891
- results.push(finding(check26.id, {
7987
+ results.push(finding(check27.id, {
7892
7988
  severity: "warning",
7893
7989
  message: `Class ".${cls}" is not in any stylesheet (Gutterpress's own, the plugins', or this book's), so it styles nothing.` + (guess ? ` Did you mean ".${guess}"?` : "") + (use.count > 1 ? ` Used ${use.count} times in this file; the first is reported.` : ""),
7894
7990
  file,
@@ -7898,7 +7994,7 @@ var check26 = {
7898
7994
  }
7899
7995
  }
7900
7996
  for (const w of env.layoutWarnings ?? []) {
7901
- results.push(finding(check26.id, {
7997
+ results.push(finding(check27.id, {
7902
7998
  severity: w.severity ?? "warning",
7903
7999
  message: w.message,
7904
8000
  file,
@@ -7907,13 +8003,13 @@ var check26 = {
7907
8003
  }));
7908
8004
  }
7909
8005
  } catch (error2) {
7910
- results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
8006
+ results.push(inspectionFailed(check27.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
7911
8007
  }
7912
8008
  }
7913
8009
  return results;
7914
8010
  }
7915
8011
  };
7916
- registerCheck(check26);
8012
+ registerCheck(check27);
7917
8013
 
7918
8014
  // src/checks/source/merge-markers.ts
7919
8015
  import { readFile as readFile19 } from "node:fs/promises";
@@ -7946,7 +8042,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
7946
8042
  function withoutOnlineTag(relPath) {
7947
8043
  return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
7948
8044
  }
7949
- var check27 = {
8045
+ var check28 = {
7950
8046
  id: "source.sync.merge-markers",
7951
8047
  name: "Combined Versions",
7952
8048
  description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
@@ -7960,7 +8056,7 @@ var check27 = {
7960
8056
  try {
7961
8057
  content = await readFile19(file, "utf8");
7962
8058
  } catch {
7963
- results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
8059
+ results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
7964
8060
  continue;
7965
8061
  }
7966
8062
  if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
@@ -7972,7 +8068,7 @@ var check27 = {
7972
8068
  const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
7973
8069
  if (line === OPEN_SENTINEL) {
7974
8070
  inBlock = true;
7975
- results.push(finding(check27.id, {
8071
+ results.push(finding(check28.id, {
7976
8072
  severity: "error",
7977
8073
  code: "two-versions-passage",
7978
8074
  message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
@@ -7981,7 +8077,7 @@ var check27 = {
7981
8077
  }));
7982
8078
  } else if (line === CLOSE_SENTINEL) {
7983
8079
  if (!inBlock) {
7984
- results.push(finding(check27.id, {
8080
+ results.push(finding(check28.id, {
7985
8081
  severity: "error",
7986
8082
  code: "leftover-version-marker",
7987
8083
  message: "This marker line is left over from combining two versions — delete it.",
@@ -8002,7 +8098,7 @@ var check27 = {
8002
8098
  });
8003
8099
  for (const sibling of siblings.sort()) {
8004
8100
  const rel = path9.relative(ctx.inputDir, sibling).split(path9.sep).join("/");
8005
- results.push(finding(check27.id, {
8101
+ results.push(finding(check28.id, {
8006
8102
  severity: "warning",
8007
8103
  code: "kept-both-versions",
8008
8104
  message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
@@ -8010,12 +8106,12 @@ var check27 = {
8010
8106
  }));
8011
8107
  }
8012
8108
  } catch (error2) {
8013
- results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
8109
+ results.push(inspectionFailed(check28.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
8014
8110
  }
8015
8111
  return results;
8016
8112
  }
8017
8113
  };
8018
- registerCheck(check27);
8114
+ registerCheck(check28);
8019
8115
 
8020
8116
  // src/checks/source/characters.ts
8021
8117
  import { readFile as readFile20 } from "node:fs/promises";
@@ -8130,7 +8226,7 @@ function codePointLabel(char) {
8130
8226
 
8131
8227
  // src/checks/source/characters.ts
8132
8228
  var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
8133
- var check28 = {
8229
+ var check29 = {
8134
8230
  id: "source.characters",
8135
8231
  name: "Unusual Characters",
8136
8232
  description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
@@ -8143,7 +8239,7 @@ var check28 = {
8143
8239
  try {
8144
8240
  content = await readFile20(file, "utf8");
8145
8241
  } catch {
8146
- results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
8242
+ results.push(inspectionFailed(check29.id, `Could not read source file: ${file}`, { file }));
8147
8243
  continue;
8148
8244
  }
8149
8245
  if (!PROBE.test(content))
@@ -8154,7 +8250,7 @@ var check28 = {
8154
8250
  for (let i = 0;i < lines.length; i++) {
8155
8251
  const line = lines[i];
8156
8252
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
8157
- if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
8253
+ if (comment && parseDisableDirective(comment[1])?.includes(check29.id)) {
8158
8254
  silencedLine = i + 1;
8159
8255
  continue;
8160
8256
  }
@@ -8163,7 +8259,7 @@ var check28 = {
8163
8259
  for (const match of line.matchAll(CHARACTER_PATTERN)) {
8164
8260
  const char = match[0];
8165
8261
  const { name, code, fix } = describeCharacter(char);
8166
- results.push(finding(check28.id, {
8262
+ results.push(finding(check29.id, {
8167
8263
  severity: code === "typographer" ? "info" : "warning",
8168
8264
  code,
8169
8265
  message: `${name} (${codePointLabel(char)}). ${fix}`,
@@ -8178,7 +8274,7 @@ var check28 = {
8178
8274
  return results;
8179
8275
  }
8180
8276
  };
8181
- registerCheck(check28);
8277
+ registerCheck(check29);
8182
8278
 
8183
8279
  // src/checks/asset/image-file-size.ts
8184
8280
  import { stat as stat4 } from "node:fs/promises";
@@ -8404,7 +8500,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
8404
8500
  }
8405
8501
 
8406
8502
  // src/checks/asset/image-file-size.ts
8407
- var check29 = {
8503
+ var check30 = {
8408
8504
  id: "asset.image.file-size",
8409
8505
  name: "Image File Size",
8410
8506
  description: "Checks that image files do not exceed the maximum size limit",
@@ -8425,14 +8521,14 @@ var check29 = {
8425
8521
  if (info3.size > maxSize) {
8426
8522
  const sizeMb = (info3.size / 1e6).toFixed(1);
8427
8523
  const maxMb = (maxSize / 1e6).toFixed(1);
8428
- results.push(finding(check29.id, {
8524
+ results.push(finding(check30.id, {
8429
8525
  severity: "warning",
8430
8526
  message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
8431
8527
  file
8432
8528
  }));
8433
8529
  }
8434
8530
  } catch {
8435
- results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
8531
+ results.push(inspectionFailed(check30.id, `Could not stat image file: ${file}`, {
8436
8532
  file
8437
8533
  }));
8438
8534
  }
@@ -8440,10 +8536,10 @@ var check29 = {
8440
8536
  return results;
8441
8537
  }
8442
8538
  };
8443
- registerCheck(check29);
8539
+ registerCheck(check30);
8444
8540
 
8445
8541
  // src/checks/asset/image-resolution.ts
8446
- var check30 = {
8542
+ var check31 = {
8447
8543
  id: "asset.image.resolution",
8448
8544
  name: "Image Resolution",
8449
8545
  description: "Checks source image DPI from embedded density metadata",
@@ -8465,7 +8561,7 @@ var check30 = {
8465
8561
  const { xDpi, yDpi } = info3;
8466
8562
  if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
8467
8563
  results.push({
8468
- checkId: check30.id,
8564
+ checkId: check31.id,
8469
8565
  severity: "warning",
8470
8566
  message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
8471
8567
  file
@@ -8475,7 +8571,7 @@ var check30 = {
8475
8571
  return results;
8476
8572
  }
8477
8573
  };
8478
- registerCheck(check30);
8574
+ registerCheck(check31);
8479
8575
 
8480
8576
  // src/checks/asset/image-color-space.ts
8481
8577
  var LABEL = {
@@ -8483,7 +8579,7 @@ var LABEL = {
8483
8579
  gray: "Gray",
8484
8580
  cmyk: "CMYK"
8485
8581
  };
8486
- var check31 = {
8582
+ var check32 = {
8487
8583
  id: "asset.image.color-space",
8488
8584
  name: "Image Color Space",
8489
8585
  description: "Validates image color spaces against allowed list",
@@ -8504,7 +8600,7 @@ var check31 = {
8504
8600
  const cs = info3?.colorSpace;
8505
8601
  if (cs && !allowedLower.has(cs)) {
8506
8602
  results.push({
8507
- checkId: check31.id,
8603
+ checkId: check32.id,
8508
8604
  severity: "warning",
8509
8605
  message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
8510
8606
  file
@@ -8514,10 +8610,10 @@ var check31 = {
8514
8610
  return results;
8515
8611
  }
8516
8612
  };
8517
- registerCheck(check31);
8613
+ registerCheck(check32);
8518
8614
 
8519
8615
  // src/checks/asset/image-alpha.ts
8520
- var check32 = {
8616
+ var check33 = {
8521
8617
  id: "asset.image.alpha-channel",
8522
8618
  name: "Image Alpha Channel",
8523
8619
  description: "Checks for alpha channels in PNG/TIFF images",
@@ -8535,7 +8631,7 @@ var check32 = {
8535
8631
  const info3 = await inspectImage(file);
8536
8632
  if (info3?.hasAlpha) {
8537
8633
  results.push({
8538
- checkId: check32.id,
8634
+ checkId: check33.id,
8539
8635
  severity: "warning",
8540
8636
  message: "Image contains alpha channel, which may cause print issues",
8541
8637
  file
@@ -8545,10 +8641,10 @@ var check32 = {
8545
8641
  return results;
8546
8642
  }
8547
8643
  };
8548
- registerCheck(check32);
8644
+ registerCheck(check33);
8549
8645
 
8550
8646
  // src/checks/asset/image-tac.ts
8551
- var check33 = {
8647
+ var check34 = {
8552
8648
  id: "asset.image.tac-raster",
8553
8649
  name: "Image TAC (Raster)",
8554
8650
  description: "Rasterizes and checks TAC per image using Ghostscript",
@@ -8567,7 +8663,7 @@ var check33 = {
8567
8663
  const ghostscript = await resolveGhostscript();
8568
8664
  if (!ghostscript) {
8569
8665
  return [
8570
- inspectionFailed(check33.id, "Could not inspect image ink coverage: Ghostscript executable not found")
8666
+ inspectionFailed(check34.id, "Could not inspect image ink coverage: Ghostscript executable not found")
8571
8667
  ];
8572
8668
  }
8573
8669
  for (const file of files) {
@@ -8585,7 +8681,7 @@ var check33 = {
8585
8681
  if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
8586
8682
  const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
8587
8683
  if (tac > maxTac) {
8588
- results.push(finding(check33.id, {
8684
+ results.push(finding(check34.id, {
8589
8685
  severity: "warning",
8590
8686
  message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
8591
8687
  file,
@@ -8597,16 +8693,16 @@ var check33 = {
8597
8693
  }
8598
8694
  }
8599
8695
  } catch {
8600
- results.push(inspectionFailed(check33.id, `Could not inspect image ink coverage: ${file}`, { file }));
8696
+ results.push(inspectionFailed(check34.id, `Could not inspect image ink coverage: ${file}`, { file }));
8601
8697
  }
8602
8698
  }
8603
8699
  return results;
8604
8700
  }
8605
8701
  };
8606
- registerCheck(check33);
8702
+ registerCheck(check34);
8607
8703
 
8608
8704
  // src/checks/asset/approved-fonts.ts
8609
- var check34 = {
8705
+ var check35 = {
8610
8706
  id: "asset.font.approved-files",
8611
8707
  name: "Approved Font Files",
8612
8708
  description: "Checks font files against the approved file patterns",
@@ -8645,7 +8741,7 @@ var check34 = {
8645
8741
  for (const font of allFonts) {
8646
8742
  if (!approvedFonts.has(font)) {
8647
8743
  results.push({
8648
- checkId: check34.id,
8744
+ checkId: check35.id,
8649
8745
  severity: "warning",
8650
8746
  message: "Font file not in approved list",
8651
8747
  file: font
@@ -8655,7 +8751,7 @@ var check34 = {
8655
8751
  return results;
8656
8752
  }
8657
8753
  };
8658
- registerCheck(check34);
8754
+ registerCheck(check35);
8659
8755
 
8660
8756
  // src/checks/asset/font-license.ts
8661
8757
  import { existsSync as existsSync10 } from "node:fs";
@@ -8670,7 +8766,7 @@ var LICENSE_NAMES = [
8670
8766
  "OFL-1.1.txt",
8671
8767
  "COPYING"
8672
8768
  ];
8673
- var check35 = {
8769
+ var check36 = {
8674
8770
  id: "asset.font.license",
8675
8771
  name: "Font License",
8676
8772
  description: "Checks for license files in font directories",
@@ -8697,7 +8793,7 @@ var check35 = {
8697
8793
  const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
8698
8794
  if (!hasLicense) {
8699
8795
  results.push({
8700
- checkId: check35.id,
8796
+ checkId: check36.id,
8701
8797
  severity: "warning",
8702
8798
  message: `No font license file found in directory`,
8703
8799
  file: fontDir
@@ -8707,10 +8803,10 @@ var check35 = {
8707
8803
  return results;
8708
8804
  }
8709
8805
  };
8710
- registerCheck(check35);
8806
+ registerCheck(check36);
8711
8807
 
8712
8808
  // src/checks/heuristic/text-density.ts
8713
- var check36 = {
8809
+ var check37 = {
8714
8810
  id: "heuristic.whitespace.text-density",
8715
8811
  name: "Text Density",
8716
8812
  description: "Checks characters-per-page ratio",
@@ -8739,7 +8835,7 @@ var check36 = {
8739
8835
  });
8740
8836
  if (lowPages.length > 0) {
8741
8837
  results.push({
8742
- checkId: check36.id,
8838
+ checkId: check37.id,
8743
8839
  severity: "info",
8744
8840
  message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
8745
8841
  file: ctx.pdfPath
@@ -8747,7 +8843,7 @@ var check36 = {
8747
8843
  }
8748
8844
  if (highPages.length > 0) {
8749
8845
  results.push({
8750
- checkId: check36.id,
8846
+ checkId: check37.id,
8751
8847
  severity: "info",
8752
8848
  message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
8753
8849
  file: ctx.pdfPath
@@ -8756,11 +8852,11 @@ var check36 = {
8756
8852
  return results;
8757
8853
  }
8758
8854
  };
8759
- registerCheck(check36);
8855
+ registerCheck(check37);
8760
8856
 
8761
8857
  // src/checks/heuristic/section-density.ts
8762
8858
  import { readFile as readFile22 } from "node:fs/promises";
8763
- var check37 = {
8859
+ var check38 = {
8764
8860
  id: "heuristic.chunking.section-density",
8765
8861
  name: "Section Density",
8766
8862
  description: "Checks heading/paragraph/callout density from source Markdown",
@@ -8786,7 +8882,7 @@ var check37 = {
8786
8882
  const line = lines[i];
8787
8883
  if (/^#{1,6}\s/.test(line)) {
8788
8884
  if (paragraphCount > maxParas) {
8789
- results.push(finding(check37.id, {
8885
+ results.push(finding(check38.id, {
8790
8886
  severity: "info",
8791
8887
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
8792
8888
  file,
@@ -8806,7 +8902,7 @@ var check37 = {
8806
8902
  }
8807
8903
  }
8808
8904
  if (paragraphCount > maxParas) {
8809
- results.push(finding(check37.id, {
8905
+ results.push(finding(check38.id, {
8810
8906
  severity: "info",
8811
8907
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
8812
8908
  file,
@@ -8814,7 +8910,7 @@ var check37 = {
8814
8910
  }));
8815
8911
  }
8816
8912
  } catch {
8817
- results.push(inspectionFailed(check37.id, `Could not read source file: ${file}`, {
8913
+ results.push(inspectionFailed(check38.id, `Could not read source file: ${file}`, {
8818
8914
  file
8819
8915
  }));
8820
8916
  }
@@ -8822,10 +8918,10 @@ var check37 = {
8822
8918
  return results;
8823
8919
  }
8824
8920
  };
8825
- registerCheck(check37);
8921
+ registerCheck(check38);
8826
8922
 
8827
8923
  // src/checks/heuristic/layer-count.ts
8828
- var check38 = {
8924
+ var check39 = {
8829
8925
  id: "heuristic.decoration.layer-count",
8830
8926
  name: "Layer Count",
8831
8927
  description: "Counts image objects per page",
@@ -8850,7 +8946,7 @@ var check38 = {
8850
8946
  heavyPages.sort((a, b) => a - b);
8851
8947
  return [
8852
8948
  {
8853
- checkId: check38.id,
8949
+ checkId: check39.id,
8854
8950
  severity: "info",
8855
8951
  message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
8856
8952
  file: ctx.pdfPath
@@ -8860,10 +8956,10 @@ var check38 = {
8860
8956
  return [];
8861
8957
  }
8862
8958
  };
8863
- registerCheck(check38);
8959
+ registerCheck(check39);
8864
8960
 
8865
8961
  // src/checks/heuristic/placement-variance.ts
8866
- var check39 = {
8962
+ var check40 = {
8867
8963
  id: "heuristic.layout.placement-variance",
8868
8964
  name: "Placement Variance",
8869
8965
  description: "Analyzes text baseline coordinates for layout consistency",
@@ -8881,7 +8977,7 @@ var check39 = {
8881
8977
  const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
8882
8978
  return [
8883
8979
  {
8884
- checkId: check39.id,
8980
+ checkId: check40.id,
8885
8981
  severity: "info",
8886
8982
  message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
8887
8983
  file: ctx.pdfPath
@@ -8889,21 +8985,21 @@ var check39 = {
8889
8985
  ];
8890
8986
  }
8891
8987
  };
8892
- registerCheck(check39);
8988
+ registerCheck(check40);
8893
8989
 
8894
8990
  // src/checks/tool-check.ts
8895
8991
  async function checkToolAvailability(config, opts = {}) {
8896
8992
  const { checks: checks2 } = selectChecks(opts, config);
8897
8993
  const toolToChecks = new Map;
8898
- for (const check40 of checks2) {
8899
- if (!check40.requiredTools?.length)
8994
+ for (const check41 of checks2) {
8995
+ if (!check41.requiredTools?.length)
8900
8996
  continue;
8901
- for (const tool of check40.requiredTools) {
8997
+ for (const tool of check41.requiredTools) {
8902
8998
  const existing = toolToChecks.get(tool);
8903
8999
  if (existing) {
8904
- existing.push(check40.id);
9000
+ existing.push(check41.id);
8905
9001
  } else {
8906
- toolToChecks.set(tool, [check40.id]);
9002
+ toolToChecks.set(tool, [check41.id]);
8907
9003
  }
8908
9004
  }
8909
9005
  }
@@ -8919,11 +9015,11 @@ async function checkToolAvailability(config, opts = {}) {
8919
9015
  const missing = results.filter((r) => !r.found).map((r) => r.tool);
8920
9016
  const missingSet = new Set(missing);
8921
9017
  const skippedChecks = [];
8922
- for (const check40 of checks2) {
8923
- if (!check40.requiredTools?.length)
9018
+ for (const check41 of checks2) {
9019
+ if (!check41.requiredTools?.length)
8924
9020
  continue;
8925
- if (check40.requiredTools.some((t) => missingSet.has(t))) {
8926
- skippedChecks.push(check40.id);
9021
+ if (check41.requiredTools.some((t) => missingSet.has(t))) {
9022
+ skippedChecks.push(check41.id);
8927
9023
  }
8928
9024
  }
8929
9025
  return { available, missing, skippedChecks, toolToChecks };
@@ -9403,16 +9499,16 @@ async function runChecks(ctx, opts = {}) {
9403
9499
  }
9404
9500
  const release = retainPdfCache();
9405
9501
  try {
9406
- for (const check40 of checks2) {
9502
+ for (const check41 of checks2) {
9407
9503
  try {
9408
- let results = await check40.run(ctx);
9409
- const severityOverride = getCheckSeverityOverride(check40.id, ctx.config);
9504
+ let results = await check41.run(ctx);
9505
+ const severityOverride = getCheckSeverityOverride(check41.id, ctx.config);
9410
9506
  if (severityOverride) {
9411
9507
  for (const r of results) {
9412
9508
  r.severity = severityOverride;
9413
9509
  }
9414
9510
  }
9415
- const lintLevel = ruleLevel(ctx.config.lint.rules, check40.id);
9511
+ const lintLevel = ruleLevel(ctx.config.lint.rules, check41.id);
9416
9512
  if (lintLevel === "off")
9417
9513
  results = [];
9418
9514
  else if (lintLevel) {
@@ -9420,15 +9516,15 @@ async function runChecks(ctx, opts = {}) {
9420
9516
  r.severity = lintLevel === "warn" ? "warning" : "error";
9421
9517
  }
9422
9518
  if (results.length === 0) {
9423
- passed.push(check40.id);
9519
+ passed.push(check41.id);
9424
9520
  } else {
9425
9521
  allResults.push(...results);
9426
9522
  }
9427
9523
  } catch (err) {
9428
9524
  allResults.push({
9429
- checkId: check40.id,
9525
+ checkId: check41.id,
9430
9526
  severity: "error",
9431
- message: `Check "${check40.id}" threw: ${err instanceof Error ? err.message : String(err)}`
9527
+ message: `Check "${check41.id}" threw: ${err instanceof Error ? err.message : String(err)}`
9432
9528
  });
9433
9529
  }
9434
9530
  }
@@ -9720,7 +9816,7 @@ async function executeValidation(args) {
9720
9816
  };
9721
9817
  if (args.fix) {
9722
9818
  const selected = selectChecks(runnerOptions, config).checks;
9723
- if (selected.some((check40) => check40.id === MARKDOWNLINT_CHECK_ID)) {
9819
+ if (selected.some((check41) => check41.id === MARKDOWNLINT_CHECK_ID)) {
9724
9820
  await applyMarkdownlintFixes(context);
9725
9821
  } else {
9726
9822
  log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
@@ -10265,7 +10361,7 @@ class PdfOutput {
10265
10361
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
10266
10362
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
10267
10363
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
10268
- const { buildNativePdf } = await import("./engine-1v8kmv3a.js");
10364
+ const { buildNativePdf } = await import("./engine-9xb1ky8d.js");
10269
10365
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
10270
10366
  title: config.title,
10271
10367
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -10523,8 +10619,16 @@ async function renderPreviewBook(inputPath, config, opts) {
10523
10619
  });
10524
10620
  }
10525
10621
  function injectPreviewScripts(html, page) {
10526
- const area = page ? JSON.stringify({ width: page.width, height: page.height, bleed: page.bleed ?? 0, safe: page.safe ?? 0 }) : "";
10527
- const meta = area ? ` <meta name="gp-print-area" content='${area}'>
10622
+ const area = page ? JSON.stringify({
10623
+ width: page.width,
10624
+ height: page.height,
10625
+ bleed: page.bleed ?? 0,
10626
+ safe: page.safe ?? 0,
10627
+ artSafe: page.artSafe,
10628
+ preset: page.preset,
10629
+ spec: page.spec
10630
+ }) : "";
10631
+ const meta = area ? ` <meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>
10528
10632
  ` : "";
10529
10633
  const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
10530
10634
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
@@ -10560,7 +10664,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
10560
10664
  for (const ref of nextImageRefs)
10561
10665
  imageRefs.add(ref);
10562
10666
  }
10563
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page), "utf-8");
10667
+ await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page && { ...config.page, preset: config.preset, spec: config.spec }), "utf-8");
10564
10668
  }
10565
10669
  function isDotPathUnderRoot(candidatePath, root) {
10566
10670
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");