gutterpress 0.11.19 → 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 (32) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-czbaj4qf.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-cgbzdrwz.js → cli-7z29z4wz.js} +1 -1
  6. package/dist/{cli-dzw28t3s.js → cli-gh0ym80r.js} +2 -1
  7. package/dist/{cli-xx94a1k6.js → cli-j802s31j.js} +5 -5
  8. package/dist/{cli-aw614ztv.js → cli-zacevwcy.js} +299 -184
  9. package/dist/cli.js +12 -12
  10. package/dist/{doctor-6e9perj7.js → doctor-fe7tcs4v.js} +2 -2
  11. package/dist/{engine-fe0n7v9p.js → engine-9xb1ky8d.js} +1 -1
  12. package/dist/{engine-zehw6tkv.js → engine-yax79k9m.js} +2 -2
  13. package/dist/{ext-kavfk1zn.js → ext-ahwxt6gv.js} +4 -4
  14. package/dist/{gutterpress-viewer-k4mpqjh0.js → gutterpress-viewer-retj6ym2.js} +515 -281
  15. package/dist/{index-jt552wtf.js → index-3nr5rzzp.js} +5 -5
  16. package/dist/{index-b866g2ph.js → index-6xwtb7cw.js} +1 -1
  17. package/dist/{index-g7vszjqf.js → index-razpty47.js} +298 -182
  18. package/dist/index.js +3 -3
  19. package/dist/lib/ghostscript.d.ts +7 -0
  20. package/dist/lib/presets.d.ts +3 -1
  21. package/dist/{manifest.schema-5pzhscqh.json → manifest.schema-74s1ykyv.json} +20 -1
  22. package/dist/{new-ckpas1ya.js → new-ftd4fec4.js} +4 -4
  23. package/dist/{preflight-yh57q9nn.js → preflight-8sqarcsr.js} +4 -4
  24. package/dist/preview/file-watcher.d.ts +16 -2
  25. package/dist/{preview-bridge-evecsh4w.js → preview-bridge-8wjcjec3.js} +3 -2
  26. package/dist/{preview-interface-2n1d9pbf.js → preview-interface-nnmrxyqm.js} +37 -41
  27. package/dist/{preview-a4x2ezjp.js → preview-q76xn95h.js} +4 -4
  28. package/dist/{preview-shell-hyp7g158.js → preview-shell-wk9k3gcd.js} +18 -6
  29. package/dist/{publish-sfn5hmnx.js → publish-7gdy6gbf.js} +4 -4
  30. package/dist/schema/manifest.types.d.ts +38 -3
  31. package/dist/{validate-281rsdsv.js → validate-e3ryegce.js} +4 -4
  32. package/package.json +1 -1
@@ -14,12 +14,12 @@ import {
14
14
  stampCreator,
15
15
  stripAnnotations,
16
16
  warn
17
- } from "./cli-dzw28t3s.js";
17
+ } from "./cli-gh0ym80r.js";
18
18
  import {
19
19
  DEBOUNCE,
20
20
  UsageError,
21
21
  resolvePort
22
- } from "./cli-cgbzdrwz.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-xx94a1k6.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: {
@@ -116,13 +121,16 @@ var DTRPG_PRESET = {
116
121
  flavor: "x1a",
117
122
  icc: "profiles/CGATS21_CRPC1.icc",
118
123
  stripAnnotations: true,
119
- blackText: "k-only"
124
+ blackText: "k-only",
125
+ rasterDpi: undefined
120
126
  },
121
127
  page: {
122
128
  width: 621,
123
129
  height: 810,
124
130
  tolerance: 0.5,
125
- bleed: 9
131
+ bleed: 9,
132
+ safe: 36,
133
+ artSafe: 18
126
134
  },
127
135
  ink: {
128
136
  maxTac: 240,
@@ -151,6 +159,10 @@ var DTRPG_PRESET = {
151
159
  "pdf.print.embedded-fonts": {
152
160
  enabled: true,
153
161
  severity: "error"
162
+ },
163
+ "pdf.print.safe-margins": {
164
+ enabled: true,
165
+ severity: "error"
154
166
  }
155
167
  },
156
168
  source: {
@@ -183,6 +195,7 @@ var DTRPG_PRESET = {
183
195
  }
184
196
  };
185
197
  var BOOK_PRESET = {
198
+ id: "book",
186
199
  defaultTargets: [],
187
200
  extensions: [],
188
201
  source: {
@@ -193,13 +206,16 @@ var BOOK_PRESET = {
193
206
  flavor: "x1a",
194
207
  icc: "profiles/CGATS21_CRPC1.icc",
195
208
  stripAnnotations: true,
196
- blackText: "icc"
209
+ blackText: "icc",
210
+ rasterDpi: undefined
197
211
  },
198
212
  page: {
199
213
  width: 432,
200
214
  height: 648,
201
215
  tolerance: 0.5,
202
- bleed: undefined
216
+ bleed: undefined,
217
+ safe: 18,
218
+ artSafe: undefined
203
219
  },
204
220
  ink: {
205
221
  maxTac: 400,
@@ -221,6 +237,10 @@ var BOOK_PRESET = {
221
237
  enabled: true,
222
238
  severity: "error"
223
239
  },
240
+ "pdf.print.safe-margins": {
241
+ enabled: true,
242
+ severity: "warning"
243
+ },
224
244
  "pdf.print.pdfx-markers": false,
225
245
  "pdf.print.pdfx-metadata": false
226
246
  },
@@ -255,6 +275,7 @@ var BOOK_PRESET = {
255
275
  };
256
276
  var CUSTOM_PRESET = {
257
277
  ...BOOK_PRESET,
278
+ id: "custom",
258
279
  page: null,
259
280
  defaultTargets: []
260
281
  };
@@ -300,7 +321,8 @@ var DTRPG_TARGET = {
300
321
  "pdf.structure.qpdf": { enabled: true, severity: "error" },
301
322
  "pdf.print.pdfx-markers": { enabled: true, severity: "error" },
302
323
  "pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
303
- "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" }
304
326
  },
305
327
  assets: {
306
328
  allowedColorSpaces: [...DTRPG_PRESET.validate.assets.allowedColorSpaces]
@@ -315,7 +337,8 @@ var DTRPG_TARGET = {
315
337
  "pdf.structure.qpdf",
316
338
  "pdf.print.pdfx-markers",
317
339
  "pdf.print.pdfx-metadata",
318
- "pdf.print.embedded-fonts"
340
+ "pdf.print.embedded-fonts",
341
+ "pdf.print.safe-margins"
319
342
  ],
320
343
  requiredTools: ["qpdf", "gs"]
321
344
  };
@@ -330,6 +353,7 @@ var ITCH_TARGET = {
330
353
  "pdf.print.pdfx-markers": false,
331
354
  "pdf.print.pdfx-metadata": false,
332
355
  "pdf.print.ink-coverage": false,
356
+ "pdf.print.safe-margins": false,
333
357
  "asset.image.tac-raster": false
334
358
  },
335
359
  assets: {
@@ -547,7 +571,9 @@ function resolveCustomPage(c, m) {
547
571
  width,
548
572
  height,
549
573
  tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
550
- bleed: c.page?.bleed ?? m.page?.bleed
574
+ bleed: c.page?.bleed ?? m.page?.bleed,
575
+ safe: c.page?.safe ?? m.page?.safe,
576
+ artSafe: c.page?.artSafe ?? m.page?.artSafe
551
577
  };
552
578
  }
553
579
  function resolveWithPreset(cliOverrides, manifest, preset) {
@@ -580,6 +606,10 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
580
606
  if (lastPage !== undefined && lastPage !== "blank" && lastPage !== "short-by-one") {
581
607
  throw new UsageError(`Manifest field \`print.lastPage\` must be "blank" or "short-by-one", got ${JSON.stringify(lastPage)}.`);
582
608
  }
609
+ const rasterDpi = c.pdfx?.rasterDpi ?? m.pdfx?.rasterDpi;
610
+ if (rasterDpi !== undefined && !(Number.isInteger(rasterDpi) && rasterDpi >= 72 && rasterDpi <= 1200)) {
611
+ throw new UsageError(`Manifest field \`pdfx.rasterDpi\` must be a whole number from 72 to 1200, got ${JSON.stringify(rasterDpi)}.`);
612
+ }
583
613
  const rawExtensions = c.extensions ?? m.extensions ?? [];
584
614
  if (!Array.isArray(rawExtensions)) {
585
615
  throw new UsageError("Manifest field `extensions` must be a list of specifiers.");
@@ -588,6 +618,8 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
588
618
  return {
589
619
  title: c.title ?? m.title ?? "Document",
590
620
  authors: c.authors ?? m.authors ?? [],
621
+ preset: preset.id,
622
+ spec: preset.spec,
591
623
  styles: c.styles ?? m.styles,
592
624
  extensions,
593
625
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
@@ -5263,6 +5295,79 @@ var check2 = {
5263
5295
  };
5264
5296
  registerCheck(check2);
5265
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
+
5266
5371
  // src/checks/pdf/pdfx-structure.ts
5267
5372
  function isObject(value) {
5268
5373
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5371,7 +5476,7 @@ function getPdfxMetadataIssues(objects, flavor) {
5371
5476
  }
5372
5477
 
5373
5478
  // src/checks/pdf/pdfx-markers.ts
5374
- var check3 = {
5479
+ var check4 = {
5375
5480
  id: "pdf.print.pdfx-markers",
5376
5481
  name: "PDF/X Markers",
5377
5482
  description: "Checks PDF/X OutputIntent structure via qpdf JSON",
@@ -5390,22 +5495,22 @@ var check3 = {
5390
5495
  const objects = parseQpdfObjectsJson(stdout);
5391
5496
  if (!objects) {
5392
5497
  return [
5393
- 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 })
5394
5499
  ];
5395
5500
  }
5396
- return getPdfxOutputIntentIssues(objects).map((message) => finding(check3.id, {
5501
+ return getPdfxOutputIntentIssues(objects).map((message) => finding(check4.id, {
5397
5502
  severity: "error",
5398
5503
  message,
5399
5504
  file: ctx.pdfPath
5400
5505
  }));
5401
5506
  } catch {
5402
5507
  return [
5403
- 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 })
5404
5509
  ];
5405
5510
  }
5406
5511
  }
5407
5512
  };
5408
- registerCheck(check3);
5513
+ registerCheck(check4);
5409
5514
 
5410
5515
  // src/lib/pdf-parse.ts
5411
5516
  import { readFile as readFile8 } from "node:fs/promises";
@@ -5458,7 +5563,7 @@ async function getPerPageInkCoverage(pdfPath) {
5458
5563
  }
5459
5564
 
5460
5565
  // src/checks/pdf/color-spaces.ts
5461
- var check4 = {
5566
+ var check5 = {
5462
5567
  id: "pdf.print.color-spaces",
5463
5568
  name: "Color Spaces",
5464
5569
  description: "Checks for forbidden color spaces (DeviceRGB, Lab, Separation, DeviceN)",
@@ -5471,7 +5576,7 @@ var check4 = {
5471
5576
  const results = [];
5472
5577
  if (/\/DeviceRGB/.test(bytes)) {
5473
5578
  results.push({
5474
- checkId: check4.id,
5579
+ checkId: check5.id,
5475
5580
  severity: "error",
5476
5581
  message: "DeviceRGB found (interior must be CMYK or grayscale only).",
5477
5582
  file: ctx.pdfPath
@@ -5479,7 +5584,7 @@ var check4 = {
5479
5584
  }
5480
5585
  if (/\/Lab\b/.test(bytes)) {
5481
5586
  results.push({
5482
- checkId: check4.id,
5587
+ checkId: check5.id,
5483
5588
  severity: "error",
5484
5589
  message: "Lab color space found (not allowed).",
5485
5590
  file: ctx.pdfPath
@@ -5487,7 +5592,7 @@ var check4 = {
5487
5592
  }
5488
5593
  if (/\/Separation/.test(bytes)) {
5489
5594
  results.push({
5490
- checkId: check4.id,
5595
+ checkId: check5.id,
5491
5596
  severity: "error",
5492
5597
  message: "Spot color (Separation) found (not allowed).",
5493
5598
  file: ctx.pdfPath
@@ -5495,7 +5600,7 @@ var check4 = {
5495
5600
  }
5496
5601
  if (/\/DeviceN/.test(bytes)) {
5497
5602
  results.push({
5498
- checkId: check4.id,
5603
+ checkId: check5.id,
5499
5604
  severity: "error",
5500
5605
  message: "Spot color (DeviceN) found (not allowed).",
5501
5606
  file: ctx.pdfPath
@@ -5504,10 +5609,10 @@ var check4 = {
5504
5609
  return results;
5505
5610
  }
5506
5611
  };
5507
- registerCheck(check4);
5612
+ registerCheck(check5);
5508
5613
 
5509
5614
  // src/checks/pdf/embedded-fonts.ts
5510
- var check5 = {
5615
+ var check6 = {
5511
5616
  id: "pdf.print.embedded-fonts",
5512
5617
  name: "Embedded Fonts",
5513
5618
  description: "Verifies all fonts in the PDF are embedded",
@@ -5523,7 +5628,7 @@ var check5 = {
5523
5628
  if (fonts.length === 0) {
5524
5629
  return [
5525
5630
  {
5526
- checkId: check5.id,
5631
+ checkId: check6.id,
5527
5632
  severity: "warning",
5528
5633
  message: "No fonts detected (unexpected).",
5529
5634
  file: ctx.pdfPath,
@@ -5536,7 +5641,7 @@ var check5 = {
5536
5641
  const names = notEmbedded.map((f) => f.name);
5537
5642
  return [
5538
5643
  {
5539
- checkId: check5.id,
5644
+ checkId: check6.id,
5540
5645
  severity: "error",
5541
5646
  message: `Not all fonts are embedded (${names.join(", ")}). Check @font-face and Chromium output.`,
5542
5647
  file: ctx.pdfPath,
@@ -5548,10 +5653,10 @@ var check5 = {
5548
5653
  return [];
5549
5654
  }
5550
5655
  };
5551
- registerCheck(check5);
5656
+ registerCheck(check6);
5552
5657
 
5553
5658
  // src/checks/pdf/ink-coverage.ts
5554
- var check6 = {
5659
+ var check7 = {
5555
5660
  id: "pdf.print.ink-coverage",
5556
5661
  name: "Ink Coverage (TAC)",
5557
5662
  description: "Checks total area coverage (TAC) against maximum ink limits",
@@ -5565,7 +5670,7 @@ var check6 = {
5565
5670
  if (!inkResult.ok) {
5566
5671
  return [
5567
5672
  {
5568
- checkId: check6.id,
5673
+ checkId: check7.id,
5569
5674
  severity: "warning",
5570
5675
  message: "Ink coverage could not be measured",
5571
5676
  file: ctx.pdfPath,
@@ -5590,7 +5695,7 @@ var check6 = {
5590
5695
  }
5591
5696
  return [
5592
5697
  {
5593
- checkId: check6.id,
5698
+ checkId: check7.id,
5594
5699
  severity: "warning",
5595
5700
  message: `Total ink coverage too high on ${offending.length} page(s) (max ${maxTac.toFixed(1)}%, recommended <=${ctx.config.ink.maxTac}%)`,
5596
5701
  file: ctx.pdfPath,
@@ -5614,10 +5719,10 @@ var check6 = {
5614
5719
  ];
5615
5720
  }
5616
5721
  };
5617
- registerCheck(check6);
5722
+ registerCheck(check7);
5618
5723
 
5619
5724
  // src/checks/pdf/rasterized-pages.ts
5620
- var check7 = {
5725
+ var check8 = {
5621
5726
  id: "pdf.print.rasterized-pages",
5622
5727
  name: "Rasterized Pages",
5623
5728
  description: "Detects pages that appear to be fully rasterized (CSS filters, blend modes, transparency)",
@@ -5657,7 +5762,7 @@ var check7 = {
5657
5762
  rasterizedPages.sort((a, b) => a - b);
5658
5763
  return [
5659
5764
  {
5660
- checkId: check7.id,
5765
+ checkId: check8.id,
5661
5766
  severity: "warning",
5662
5767
  message: `Possible rasterized pages detected: ${rasterizedPages.join(", ")}`,
5663
5768
  file: ctx.pdfPath,
@@ -5668,11 +5773,11 @@ var check7 = {
5668
5773
  ];
5669
5774
  }
5670
5775
  };
5671
- registerCheck(check7);
5776
+ registerCheck(check8);
5672
5777
 
5673
5778
  // src/checks/pdf/black-text.ts
5674
5779
  import { readFile as readFile9 } from "node:fs/promises";
5675
- var check8 = {
5780
+ var check9 = {
5676
5781
  id: "pdf.print.black-text",
5677
5782
  name: "Black Text",
5678
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)",
@@ -5688,7 +5793,7 @@ var check8 = {
5688
5793
  if (found.runs === 0)
5689
5794
  return [];
5690
5795
  return [
5691
- finding(check8.id, {
5796
+ finding(check9.id, {
5692
5797
  severity: "warning",
5693
5798
  message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
5694
5799
  file: ctx.pdfPath,
@@ -5699,17 +5804,17 @@ var check8 = {
5699
5804
  ];
5700
5805
  } catch {
5701
5806
  return [
5702
- inspectionFailed(check8.id, "Could not inspect the PDF's text colours.", {
5807
+ inspectionFailed(check9.id, "Could not inspect the PDF's text colours.", {
5703
5808
  file: ctx.pdfPath
5704
5809
  })
5705
5810
  ];
5706
5811
  }
5707
5812
  }
5708
5813
  };
5709
- registerCheck(check8);
5814
+ registerCheck(check9);
5710
5815
 
5711
5816
  // src/checks/pdf/signature.ts
5712
- var check9 = {
5817
+ var check10 = {
5713
5818
  id: "pdf.print.signature",
5714
5819
  name: "Signature",
5715
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)",
@@ -5721,7 +5826,7 @@ var check9 = {
5721
5826
  return [];
5722
5827
  const doc = await loadPdf(ctx.pdfPath);
5723
5828
  if (!doc) {
5724
- 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 })];
5725
5830
  }
5726
5831
  const size = ctx.config.print.signature;
5727
5832
  const pages = doc.numPages;
@@ -5730,7 +5835,7 @@ var check9 = {
5730
5835
  const data = { pages, signature: size };
5731
5836
  if (pages % size !== 0) {
5732
5837
  return [
5733
- finding(check9.id, {
5838
+ finding(check10.id, {
5734
5839
  severity: "error",
5735
5840
  message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
5736
5841
  file: ctx.pdfPath,
@@ -5746,10 +5851,10 @@ var check9 = {
5746
5851
  if (!await pageHasMarks(doc, pages))
5747
5852
  return [];
5748
5853
  } catch {
5749
- 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 })];
5750
5855
  }
5751
5856
  return [
5752
- finding(check9.id, {
5857
+ finding(check10.id, {
5753
5858
  severity: "error",
5754
5859
  message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
5755
5860
  file: ctx.pdfPath,
@@ -5760,10 +5865,10 @@ var check9 = {
5760
5865
  ];
5761
5866
  }
5762
5867
  };
5763
- registerCheck(check9);
5868
+ registerCheck(check10);
5764
5869
 
5765
5870
  // src/checks/pdf/bookmarks.ts
5766
- var check10 = {
5871
+ var check11 = {
5767
5872
  id: "pdf.nav.bookmarks",
5768
5873
  name: "Bookmarks",
5769
5874
  description: "Checks for PDF outline (bookmarks) tree",
@@ -5777,14 +5882,14 @@ var check10 = {
5777
5882
  const doc = await loadPdf(ctx.pdfPath);
5778
5883
  if (!doc) {
5779
5884
  return [
5780
- inspectionFailed(check10.id, "Could not inspect PDF for bookmarks.", {
5885
+ inspectionFailed(check11.id, "Could not inspect PDF for bookmarks.", {
5781
5886
  file: ctx.pdfPath
5782
5887
  })
5783
5888
  ];
5784
5889
  }
5785
5890
  if (await getOutlineCount(doc) === 0) {
5786
5891
  return [
5787
- finding(check10.id, {
5892
+ finding(check11.id, {
5788
5893
  severity: "warning",
5789
5894
  message: "PDF does not contain bookmarks (outline tree).",
5790
5895
  file: ctx.pdfPath
@@ -5794,10 +5899,10 @@ var check10 = {
5794
5899
  return [];
5795
5900
  }
5796
5901
  };
5797
- registerCheck(check10);
5902
+ registerCheck(check11);
5798
5903
 
5799
5904
  // src/checks/pdf/toc-links.ts
5800
- var check11 = {
5905
+ var check12 = {
5801
5906
  id: "pdf.nav.toc-links",
5802
5907
  name: "TOC Links",
5803
5908
  description: "Detects link annotations on TOC pages",
@@ -5812,7 +5917,7 @@ var check11 = {
5812
5917
  if (!doc) {
5813
5918
  return [
5814
5919
  {
5815
- checkId: check11.id,
5920
+ checkId: check12.id,
5816
5921
  severity: "warning",
5817
5922
  message: "Could not inspect PDF for TOC links.",
5818
5923
  file: ctx.pdfPath
@@ -5823,7 +5928,7 @@ var check11 = {
5823
5928
  if (!hasLinks) {
5824
5929
  return [
5825
5930
  {
5826
- checkId: check11.id,
5931
+ checkId: check12.id,
5827
5932
  severity: "warning",
5828
5933
  message: "No link annotations found in PDF (expected for TOC).",
5829
5934
  file: ctx.pdfPath
@@ -5833,10 +5938,10 @@ var check11 = {
5833
5938
  return [];
5834
5939
  }
5835
5940
  };
5836
- registerCheck(check11);
5941
+ registerCheck(check12);
5837
5942
 
5838
5943
  // src/checks/pdf/cross-refs.ts
5839
- var check12 = {
5944
+ var check13 = {
5840
5945
  id: "pdf.nav.cross-refs",
5841
5946
  name: "Cross References",
5842
5947
  description: "Counts and verifies internal link annotations",
@@ -5849,7 +5954,7 @@ var check12 = {
5849
5954
  if (!doc) {
5850
5955
  return [
5851
5956
  {
5852
- checkId: check12.id,
5957
+ checkId: check13.id,
5853
5958
  severity: "info",
5854
5959
  message: "Could not inspect PDF for cross-references.",
5855
5960
  file: ctx.pdfPath
@@ -5859,7 +5964,7 @@ var check12 = {
5859
5964
  const linkCount = await countLinkAnnotations(doc);
5860
5965
  return [
5861
5966
  {
5862
- checkId: check12.id,
5967
+ checkId: check13.id,
5863
5968
  severity: "info",
5864
5969
  message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
5865
5970
  file: ctx.pdfPath
@@ -5867,10 +5972,10 @@ var check12 = {
5867
5972
  ];
5868
5973
  }
5869
5974
  };
5870
- registerCheck(check12);
5975
+ registerCheck(check13);
5871
5976
 
5872
5977
  // src/checks/pdf/page-labels.ts
5873
- var check13 = {
5978
+ var check14 = {
5874
5979
  id: "pdf.nav.page-labels",
5875
5980
  name: "Page Labels",
5876
5981
  description: "Verifies PDF page labels / folio numbering",
@@ -5885,7 +5990,7 @@ var check13 = {
5885
5990
  const labels = await getPageLabels(doc);
5886
5991
  return [
5887
5992
  {
5888
- checkId: check13.id,
5993
+ checkId: check14.id,
5889
5994
  severity: "info",
5890
5995
  message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
5891
5996
  file: ctx.pdfPath
@@ -5893,10 +5998,10 @@ var check13 = {
5893
5998
  ];
5894
5999
  }
5895
6000
  };
5896
- registerCheck(check13);
6001
+ registerCheck(check14);
5897
6002
 
5898
6003
  // src/checks/pdf/image-resolution.ts
5899
- var check14 = {
6004
+ var check15 = {
5900
6005
  id: "pdf.print.image-resolution",
5901
6006
  name: "Image Resolution",
5902
6007
  description: "Checks DPI of images embedded in the PDF",
@@ -5923,7 +6028,7 @@ var check14 = {
5923
6028
  lowResPages.sort((a, b) => a - b);
5924
6029
  return [
5925
6030
  {
5926
- checkId: check14.id,
6031
+ checkId: check15.id,
5927
6032
  severity: "warning",
5928
6033
  message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
5929
6034
  file: ctx.pdfPath
@@ -5933,10 +6038,10 @@ var check14 = {
5933
6038
  return [];
5934
6039
  }
5935
6040
  };
5936
- registerCheck(check14);
6041
+ registerCheck(check15);
5937
6042
 
5938
6043
  // src/checks/pdf/transparency.ts
5939
- var check15 = {
6044
+ var check16 = {
5940
6045
  id: "pdf.print.transparency",
5941
6046
  name: "Transparency",
5942
6047
  description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
@@ -5958,7 +6063,7 @@ var check15 = {
5958
6063
  if (found.length > 0) {
5959
6064
  return [
5960
6065
  {
5961
- checkId: check15.id,
6066
+ checkId: check16.id,
5962
6067
  severity: "warning",
5963
6068
  message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
5964
6069
  file: ctx.pdfPath
@@ -5968,7 +6073,7 @@ var check15 = {
5968
6073
  return [];
5969
6074
  }
5970
6075
  };
5971
- registerCheck(check15);
6076
+ registerCheck(check16);
5972
6077
 
5973
6078
  // src/checks/pdf/bleed.ts
5974
6079
  function findBox(bytes, box) {
@@ -5978,7 +6083,7 @@ function findBox(bytes, box) {
5978
6083
  return null;
5979
6084
  return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
5980
6085
  }
5981
- var check16 = {
6086
+ var check17 = {
5982
6087
  id: "pdf.print.bleed",
5983
6088
  name: "Bleed Box",
5984
6089
  description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
@@ -5992,7 +6097,7 @@ var check16 = {
5992
6097
  const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
5993
6098
  if (!bytes) {
5994
6099
  return [
5995
- inspectionFailed(check16.id, "Could not inspect PDF for bleed boxes.", {
6100
+ inspectionFailed(check17.id, "Could not inspect PDF for bleed boxes.", {
5996
6101
  file: ctx.pdfPath
5997
6102
  })
5998
6103
  ];
@@ -6002,14 +6107,14 @@ var check16 = {
6002
6107
  const bleed = findBox(bytes, "BleedBox");
6003
6108
  if (!media) {
6004
6109
  return [
6005
- inspectionFailed(check16.id, "Could not parse MediaBox from PDF.", {
6110
+ inspectionFailed(check17.id, "Could not parse MediaBox from PDF.", {
6006
6111
  file: ctx.pdfPath
6007
6112
  })
6008
6113
  ];
6009
6114
  }
6010
6115
  if (!trim && !bleed) {
6011
6116
  return [
6012
- finding(check16.id, {
6117
+ finding(check17.id, {
6013
6118
  severity: "warning",
6014
6119
  message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
6015
6120
  file: ctx.pdfPath
@@ -6022,7 +6127,7 @@ var check16 = {
6022
6127
  const bleedSize = ctx.config.validate.pdf.bleedSize;
6023
6128
  if (mediaW - trimW < bleedSize * 2 * 0.9) {
6024
6129
  return [
6025
- finding(check16.id, {
6130
+ finding(check17.id, {
6026
6131
  severity: "warning",
6027
6132
  message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
6028
6133
  file: ctx.pdfPath
@@ -6033,10 +6138,10 @@ var check16 = {
6033
6138
  return [];
6034
6139
  }
6035
6140
  };
6036
- registerCheck(check16);
6141
+ registerCheck(check17);
6037
6142
 
6038
6143
  // src/checks/pdf/pdfx-metadata.ts
6039
- var check17 = {
6144
+ var check18 = {
6040
6145
  id: "pdf.print.pdfx-metadata",
6041
6146
  name: "PDF/X Metadata",
6042
6147
  description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
@@ -6055,22 +6160,22 @@ var check17 = {
6055
6160
  const objects = parseQpdfObjectsJson(stdout);
6056
6161
  if (!objects) {
6057
6162
  return [
6058
- 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 })
6059
6164
  ];
6060
6165
  }
6061
- 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, {
6062
6167
  severity: "error",
6063
6168
  message,
6064
6169
  file: ctx.pdfPath
6065
6170
  }));
6066
6171
  } catch {
6067
6172
  return [
6068
- 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 })
6069
6174
  ];
6070
6175
  }
6071
6176
  }
6072
6177
  };
6073
- registerCheck(check17);
6178
+ registerCheck(check18);
6074
6179
 
6075
6180
  // src/checks/source/markdownlint.ts
6076
6181
  import { readFile as readFile10 } from "node:fs/promises";
@@ -6125,7 +6230,7 @@ async function loadConfig(configPath) {
6125
6230
  return {};
6126
6231
  }
6127
6232
  var MARKDOWNLINT_CHECK_ID = "source.markdownlint";
6128
- var check18 = {
6233
+ var check19 = {
6129
6234
  id: MARKDOWNLINT_CHECK_ID,
6130
6235
  name: "Markdownlint",
6131
6236
  description: "Runs markdownlint with project config to validate Markdown files",
@@ -6150,7 +6255,7 @@ var check18 = {
6150
6255
  continue;
6151
6256
  for (const v of violations) {
6152
6257
  out.push({
6153
- checkId: check18.id,
6258
+ checkId: check19.id,
6154
6259
  severity: "warning",
6155
6260
  message: `${v.ruleDescription} (${v.ruleNames.join("/")})`,
6156
6261
  file,
@@ -6163,7 +6268,7 @@ var check18 = {
6163
6268
  return out;
6164
6269
  }
6165
6270
  };
6166
- registerCheck(check18);
6271
+ registerCheck(check19);
6167
6272
 
6168
6273
  // src/checks/source/htmlhint.ts
6169
6274
  import { existsSync as existsSync7 } from "node:fs";
@@ -6171,7 +6276,7 @@ import { readFile as readFile11 } from "node:fs/promises";
6171
6276
  import htmlhint from "htmlhint";
6172
6277
  var { HTMLHint } = htmlhint;
6173
6278
  var CONFIG_NAMES2 = [".htmlhintrc"];
6174
- var check19 = {
6279
+ var check20 = {
6175
6280
  id: "source.htmlhint",
6176
6281
  name: "HTMLHint",
6177
6282
  description: "Runs htmlhint with project config to validate generated HTML",
@@ -6191,7 +6296,7 @@ var check19 = {
6191
6296
  const html = await readFile11(ctx.htmlPath, "utf8");
6192
6297
  const messages = HTMLHint.verify(html, ruleset);
6193
6298
  return messages.map((m) => ({
6194
- checkId: check19.id,
6299
+ checkId: check20.id,
6195
6300
  severity: m.type === "error" ? "error" : "warning",
6196
6301
  message: `${m.rule.id}: ${m.message}`,
6197
6302
  file: ctx.htmlPath,
@@ -6200,7 +6305,7 @@ var check19 = {
6200
6305
  }));
6201
6306
  }
6202
6307
  };
6203
- registerCheck(check19);
6308
+ registerCheck(check20);
6204
6309
 
6205
6310
  // src/checks/source/stylelint.ts
6206
6311
  import { readFile as readFile12 } from "node:fs/promises";
@@ -6593,7 +6698,7 @@ function checkCssRaw(css, from) {
6593
6698
  }
6594
6699
 
6595
6700
  // src/checks/source/stylelint.ts
6596
- var check20 = {
6701
+ var check21 = {
6597
6702
  id: "source.stylelint",
6598
6703
  name: "Print-safety CSS",
6599
6704
  description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
@@ -6613,7 +6718,7 @@ var check20 = {
6613
6718
  css = await readFile12(file, "utf8");
6614
6719
  } catch (err) {
6615
6720
  results.push({
6616
- checkId: check20.id,
6721
+ checkId: check21.id,
6617
6722
  severity: "error",
6618
6723
  message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
6619
6724
  file
@@ -6622,7 +6727,7 @@ var check20 = {
6622
6727
  }
6623
6728
  for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
6624
6729
  results.push({
6625
- checkId: check20.id,
6730
+ checkId: check21.id,
6626
6731
  severity: w.severity,
6627
6732
  message: `${w.rule}: ${w.message}`,
6628
6733
  file,
@@ -6634,7 +6739,7 @@ var check20 = {
6634
6739
  return results;
6635
6740
  }
6636
6741
  };
6637
- registerCheck(check20);
6742
+ registerCheck(check21);
6638
6743
 
6639
6744
  // src/checks/source/css-ownership.ts
6640
6745
  import { readFile as readFile13 } from "node:fs/promises";
@@ -6818,7 +6923,7 @@ function checkOwnership(root, file, maps) {
6818
6923
  });
6819
6924
  return out;
6820
6925
  }
6821
- var check21 = {
6926
+ var check22 = {
6822
6927
  id: "source.css-ownership",
6823
6928
  name: "CSS ownership contract",
6824
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.",
@@ -6841,7 +6946,7 @@ var check21 = {
6841
6946
  } catch (err) {
6842
6947
  return [
6843
6948
  {
6844
- checkId: check21.id,
6949
+ checkId: check22.id,
6845
6950
  severity: "error",
6846
6951
  message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
6847
6952
  file: configPath
@@ -6883,7 +6988,7 @@ var check21 = {
6883
6988
  }
6884
6989
  for (const v of violations) {
6885
6990
  results.push({
6886
- checkId: check21.id,
6991
+ checkId: check22.id,
6887
6992
  severity: "warning",
6888
6993
  message: v.message,
6889
6994
  file: absFile,
@@ -6895,7 +7000,7 @@ var check21 = {
6895
7000
  return results;
6896
7001
  }
6897
7002
  };
6898
- registerCheck(check21);
7003
+ registerCheck(check22);
6899
7004
 
6900
7005
  // src/checks/source/local-refs.ts
6901
7006
  import { existsSync as existsSync9 } from "node:fs";
@@ -7286,7 +7391,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
7286
7391
  }
7287
7392
 
7288
7393
  // src/checks/source/local-refs.ts
7289
- var check22 = {
7394
+ var check23 = {
7290
7395
  id: "source.links.local-refs",
7291
7396
  name: "Local Markdown References",
7292
7397
  description: "Checks local markdown links and image refs resolve on disk",
@@ -7294,12 +7399,12 @@ var check22 = {
7294
7399
  phase: "pre-build",
7295
7400
  async run(ctx) {
7296
7401
  const results = [];
7297
- 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)) {
7298
7403
  if (isNonFilesystemRef(ref))
7299
7404
  continue;
7300
7405
  const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
7301
7406
  if (escape) {
7302
- results.push(finding(check22.id, {
7407
+ results.push(finding(check23.id, {
7303
7408
  severity: "error",
7304
7409
  message: escape,
7305
7410
  file,
@@ -7309,7 +7414,7 @@ var check22 = {
7309
7414
  }
7310
7415
  if (localRefExists(ref, kind, file, ctx.inputDir))
7311
7416
  continue;
7312
- results.push(finding(check22.id, {
7417
+ results.push(finding(check23.id, {
7313
7418
  severity: kind === "image" ? "warning" : "error",
7314
7419
  code: kind === "image" ? "missing-image-placeholder" : undefined,
7315
7420
  message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
@@ -7334,7 +7439,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
7334
7439
  return existsSync9(fileRelative);
7335
7440
  }
7336
7441
  }
7337
- registerCheck(check22);
7442
+ registerCheck(check23);
7338
7443
 
7339
7444
  // src/lib/build-staging.ts
7340
7445
  import path8 from "node:path";
@@ -7600,7 +7705,7 @@ async function createStageRoot() {
7600
7705
  }
7601
7706
 
7602
7707
  // src/checks/source/dangling-links.ts
7603
- var check23 = {
7708
+ var check24 = {
7604
7709
  id: "source.links.dangling",
7605
7710
  name: "Dangling Links",
7606
7711
  description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
@@ -7608,10 +7713,10 @@ var check23 = {
7608
7713
  phase: "pre-build",
7609
7714
  async run(ctx) {
7610
7715
  const results = [];
7611
- 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)) {
7612
7717
  if (kind !== "link" || isPrintResolvableHref(ref))
7613
7718
  continue;
7614
- results.push(finding(check23.id, {
7719
+ results.push(finding(check24.id, {
7615
7720
  severity: "warning",
7616
7721
  code: "dangling-link",
7617
7722
  data: { ref },
@@ -7623,11 +7728,11 @@ var check23 = {
7623
7728
  return results;
7624
7729
  }
7625
7730
  };
7626
- registerCheck(check23);
7731
+ registerCheck(check24);
7627
7732
 
7628
7733
  // src/checks/source/accessibility-alt-text.ts
7629
7734
  import { readFile as readFile16 } from "node:fs/promises";
7630
- var check24 = {
7735
+ var check25 = {
7631
7736
  id: "source.accessibility.alt-text",
7632
7737
  name: "Image Alt Text",
7633
7738
  description: "Checks markdown images include non-empty alt text",
@@ -7656,7 +7761,7 @@ var check24 = {
7656
7761
  if (alt.trim())
7657
7762
  continue;
7658
7763
  results.push({
7659
- checkId: check24.id,
7764
+ checkId: check25.id,
7660
7765
  severity: "warning",
7661
7766
  message: "Image is missing alt text",
7662
7767
  file,
@@ -7681,11 +7786,11 @@ function extractImageAlts(line) {
7681
7786
  }
7682
7787
  return alts;
7683
7788
  }
7684
- registerCheck(check24);
7789
+ registerCheck(check25);
7685
7790
 
7686
7791
  // src/checks/source/accessibility-heading-order.ts
7687
7792
  import { readFile as readFile17 } from "node:fs/promises";
7688
- var check25 = {
7793
+ var check26 = {
7689
7794
  id: "source.accessibility.heading-order",
7690
7795
  name: "Heading Order",
7691
7796
  description: "Checks markdown heading levels do not jump by more than one",
@@ -7713,7 +7818,7 @@ var check25 = {
7713
7818
  if (inFence)
7714
7819
  continue;
7715
7820
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
7716
- if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
7821
+ if (comment && parseDisableDirective(comment[1])?.includes(check26.id)) {
7717
7822
  silencedLine = i + 1;
7718
7823
  continue;
7719
7824
  }
@@ -7723,7 +7828,7 @@ var check25 = {
7723
7828
  const level = match[1].length;
7724
7829
  if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
7725
7830
  results.push({
7726
- checkId: check25.id,
7831
+ checkId: check26.id,
7727
7832
  severity: "warning",
7728
7833
  message: `Heading level jump from h${prevLevel} to h${level}`,
7729
7834
  file,
@@ -7737,7 +7842,7 @@ var check25 = {
7737
7842
  return results;
7738
7843
  }
7739
7844
  };
7740
- registerCheck(check25);
7845
+ registerCheck(check26);
7741
7846
 
7742
7847
  // src/checks/source/layout-markers.ts
7743
7848
  import { readFile as readFile18 } from "node:fs/promises";
@@ -7848,7 +7953,7 @@ async function collectKnownClasses(ctx, plugins) {
7848
7953
  }
7849
7954
  return names;
7850
7955
  }
7851
- var check26 = {
7956
+ var check27 = {
7852
7957
  id: "source.markdown.layout-markers",
7853
7958
  name: "Layout Markers",
7854
7959
  description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
@@ -7860,7 +7965,7 @@ var check26 = {
7860
7965
  return [];
7861
7966
  const results = [];
7862
7967
  const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
7863
- 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}`));
7864
7969
  });
7865
7970
  const md = createMarkdownRenderer(plugins);
7866
7971
  let known;
@@ -7879,7 +7984,7 @@ var check26 = {
7879
7984
  if (names.has(cls) || warnedLines.has(use.line))
7880
7985
  continue;
7881
7986
  const guess = nearestKnownClass(cls, names);
7882
- results.push(finding(check26.id, {
7987
+ results.push(finding(check27.id, {
7883
7988
  severity: "warning",
7884
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.` : ""),
7885
7990
  file,
@@ -7889,7 +7994,7 @@ var check26 = {
7889
7994
  }
7890
7995
  }
7891
7996
  for (const w of env.layoutWarnings ?? []) {
7892
- results.push(finding(check26.id, {
7997
+ results.push(finding(check27.id, {
7893
7998
  severity: w.severity ?? "warning",
7894
7999
  message: w.message,
7895
8000
  file,
@@ -7898,13 +8003,13 @@ var check26 = {
7898
8003
  }));
7899
8004
  }
7900
8005
  } catch (error2) {
7901
- 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 }));
7902
8007
  }
7903
8008
  }
7904
8009
  return results;
7905
8010
  }
7906
8011
  };
7907
- registerCheck(check26);
8012
+ registerCheck(check27);
7908
8013
 
7909
8014
  // src/checks/source/merge-markers.ts
7910
8015
  import { readFile as readFile19 } from "node:fs/promises";
@@ -7937,7 +8042,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
7937
8042
  function withoutOnlineTag(relPath) {
7938
8043
  return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
7939
8044
  }
7940
- var check27 = {
8045
+ var check28 = {
7941
8046
  id: "source.sync.merge-markers",
7942
8047
  name: "Combined Versions",
7943
8048
  description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
@@ -7951,7 +8056,7 @@ var check27 = {
7951
8056
  try {
7952
8057
  content = await readFile19(file, "utf8");
7953
8058
  } catch {
7954
- 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 }));
7955
8060
  continue;
7956
8061
  }
7957
8062
  if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
@@ -7963,7 +8068,7 @@ var check27 = {
7963
8068
  const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
7964
8069
  if (line === OPEN_SENTINEL) {
7965
8070
  inBlock = true;
7966
- results.push(finding(check27.id, {
8071
+ results.push(finding(check28.id, {
7967
8072
  severity: "error",
7968
8073
  code: "two-versions-passage",
7969
8074
  message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
@@ -7972,7 +8077,7 @@ var check27 = {
7972
8077
  }));
7973
8078
  } else if (line === CLOSE_SENTINEL) {
7974
8079
  if (!inBlock) {
7975
- results.push(finding(check27.id, {
8080
+ results.push(finding(check28.id, {
7976
8081
  severity: "error",
7977
8082
  code: "leftover-version-marker",
7978
8083
  message: "This marker line is left over from combining two versions — delete it.",
@@ -7993,7 +8098,7 @@ var check27 = {
7993
8098
  });
7994
8099
  for (const sibling of siblings.sort()) {
7995
8100
  const rel = path9.relative(ctx.inputDir, sibling).split(path9.sep).join("/");
7996
- results.push(finding(check27.id, {
8101
+ results.push(finding(check28.id, {
7997
8102
  severity: "warning",
7998
8103
  code: "kept-both-versions",
7999
8104
  message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
@@ -8001,12 +8106,12 @@ var check27 = {
8001
8106
  }));
8002
8107
  }
8003
8108
  } catch (error2) {
8004
- 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)}`));
8005
8110
  }
8006
8111
  return results;
8007
8112
  }
8008
8113
  };
8009
- registerCheck(check27);
8114
+ registerCheck(check28);
8010
8115
 
8011
8116
  // src/checks/source/characters.ts
8012
8117
  import { readFile as readFile20 } from "node:fs/promises";
@@ -8121,7 +8226,7 @@ function codePointLabel(char) {
8121
8226
 
8122
8227
  // src/checks/source/characters.ts
8123
8228
  var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
8124
- var check28 = {
8229
+ var check29 = {
8125
8230
  id: "source.characters",
8126
8231
  name: "Unusual Characters",
8127
8232
  description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
@@ -8134,7 +8239,7 @@ var check28 = {
8134
8239
  try {
8135
8240
  content = await readFile20(file, "utf8");
8136
8241
  } catch {
8137
- 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 }));
8138
8243
  continue;
8139
8244
  }
8140
8245
  if (!PROBE.test(content))
@@ -8145,7 +8250,7 @@ var check28 = {
8145
8250
  for (let i = 0;i < lines.length; i++) {
8146
8251
  const line = lines[i];
8147
8252
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
8148
- if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
8253
+ if (comment && parseDisableDirective(comment[1])?.includes(check29.id)) {
8149
8254
  silencedLine = i + 1;
8150
8255
  continue;
8151
8256
  }
@@ -8154,7 +8259,7 @@ var check28 = {
8154
8259
  for (const match of line.matchAll(CHARACTER_PATTERN)) {
8155
8260
  const char = match[0];
8156
8261
  const { name, code, fix } = describeCharacter(char);
8157
- results.push(finding(check28.id, {
8262
+ results.push(finding(check29.id, {
8158
8263
  severity: code === "typographer" ? "info" : "warning",
8159
8264
  code,
8160
8265
  message: `${name} (${codePointLabel(char)}). ${fix}`,
@@ -8169,7 +8274,7 @@ var check28 = {
8169
8274
  return results;
8170
8275
  }
8171
8276
  };
8172
- registerCheck(check28);
8277
+ registerCheck(check29);
8173
8278
 
8174
8279
  // src/checks/asset/image-file-size.ts
8175
8280
  import { stat as stat4 } from "node:fs/promises";
@@ -8395,7 +8500,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
8395
8500
  }
8396
8501
 
8397
8502
  // src/checks/asset/image-file-size.ts
8398
- var check29 = {
8503
+ var check30 = {
8399
8504
  id: "asset.image.file-size",
8400
8505
  name: "Image File Size",
8401
8506
  description: "Checks that image files do not exceed the maximum size limit",
@@ -8416,14 +8521,14 @@ var check29 = {
8416
8521
  if (info3.size > maxSize) {
8417
8522
  const sizeMb = (info3.size / 1e6).toFixed(1);
8418
8523
  const maxMb = (maxSize / 1e6).toFixed(1);
8419
- results.push(finding(check29.id, {
8524
+ results.push(finding(check30.id, {
8420
8525
  severity: "warning",
8421
8526
  message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
8422
8527
  file
8423
8528
  }));
8424
8529
  }
8425
8530
  } catch {
8426
- results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
8531
+ results.push(inspectionFailed(check30.id, `Could not stat image file: ${file}`, {
8427
8532
  file
8428
8533
  }));
8429
8534
  }
@@ -8431,10 +8536,10 @@ var check29 = {
8431
8536
  return results;
8432
8537
  }
8433
8538
  };
8434
- registerCheck(check29);
8539
+ registerCheck(check30);
8435
8540
 
8436
8541
  // src/checks/asset/image-resolution.ts
8437
- var check30 = {
8542
+ var check31 = {
8438
8543
  id: "asset.image.resolution",
8439
8544
  name: "Image Resolution",
8440
8545
  description: "Checks source image DPI from embedded density metadata",
@@ -8456,7 +8561,7 @@ var check30 = {
8456
8561
  const { xDpi, yDpi } = info3;
8457
8562
  if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
8458
8563
  results.push({
8459
- checkId: check30.id,
8564
+ checkId: check31.id,
8460
8565
  severity: "warning",
8461
8566
  message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
8462
8567
  file
@@ -8466,7 +8571,7 @@ var check30 = {
8466
8571
  return results;
8467
8572
  }
8468
8573
  };
8469
- registerCheck(check30);
8574
+ registerCheck(check31);
8470
8575
 
8471
8576
  // src/checks/asset/image-color-space.ts
8472
8577
  var LABEL = {
@@ -8474,7 +8579,7 @@ var LABEL = {
8474
8579
  gray: "Gray",
8475
8580
  cmyk: "CMYK"
8476
8581
  };
8477
- var check31 = {
8582
+ var check32 = {
8478
8583
  id: "asset.image.color-space",
8479
8584
  name: "Image Color Space",
8480
8585
  description: "Validates image color spaces against allowed list",
@@ -8495,7 +8600,7 @@ var check31 = {
8495
8600
  const cs = info3?.colorSpace;
8496
8601
  if (cs && !allowedLower.has(cs)) {
8497
8602
  results.push({
8498
- checkId: check31.id,
8603
+ checkId: check32.id,
8499
8604
  severity: "warning",
8500
8605
  message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
8501
8606
  file
@@ -8505,10 +8610,10 @@ var check31 = {
8505
8610
  return results;
8506
8611
  }
8507
8612
  };
8508
- registerCheck(check31);
8613
+ registerCheck(check32);
8509
8614
 
8510
8615
  // src/checks/asset/image-alpha.ts
8511
- var check32 = {
8616
+ var check33 = {
8512
8617
  id: "asset.image.alpha-channel",
8513
8618
  name: "Image Alpha Channel",
8514
8619
  description: "Checks for alpha channels in PNG/TIFF images",
@@ -8526,7 +8631,7 @@ var check32 = {
8526
8631
  const info3 = await inspectImage(file);
8527
8632
  if (info3?.hasAlpha) {
8528
8633
  results.push({
8529
- checkId: check32.id,
8634
+ checkId: check33.id,
8530
8635
  severity: "warning",
8531
8636
  message: "Image contains alpha channel, which may cause print issues",
8532
8637
  file
@@ -8536,10 +8641,10 @@ var check32 = {
8536
8641
  return results;
8537
8642
  }
8538
8643
  };
8539
- registerCheck(check32);
8644
+ registerCheck(check33);
8540
8645
 
8541
8646
  // src/checks/asset/image-tac.ts
8542
- var check33 = {
8647
+ var check34 = {
8543
8648
  id: "asset.image.tac-raster",
8544
8649
  name: "Image TAC (Raster)",
8545
8650
  description: "Rasterizes and checks TAC per image using Ghostscript",
@@ -8558,7 +8663,7 @@ var check33 = {
8558
8663
  const ghostscript = await resolveGhostscript();
8559
8664
  if (!ghostscript) {
8560
8665
  return [
8561
- 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")
8562
8667
  ];
8563
8668
  }
8564
8669
  for (const file of files) {
@@ -8576,7 +8681,7 @@ var check33 = {
8576
8681
  if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
8577
8682
  const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
8578
8683
  if (tac > maxTac) {
8579
- results.push(finding(check33.id, {
8684
+ results.push(finding(check34.id, {
8580
8685
  severity: "warning",
8581
8686
  message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
8582
8687
  file,
@@ -8588,16 +8693,16 @@ var check33 = {
8588
8693
  }
8589
8694
  }
8590
8695
  } catch {
8591
- 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 }));
8592
8697
  }
8593
8698
  }
8594
8699
  return results;
8595
8700
  }
8596
8701
  };
8597
- registerCheck(check33);
8702
+ registerCheck(check34);
8598
8703
 
8599
8704
  // src/checks/asset/approved-fonts.ts
8600
- var check34 = {
8705
+ var check35 = {
8601
8706
  id: "asset.font.approved-files",
8602
8707
  name: "Approved Font Files",
8603
8708
  description: "Checks font files against the approved file patterns",
@@ -8636,7 +8741,7 @@ var check34 = {
8636
8741
  for (const font of allFonts) {
8637
8742
  if (!approvedFonts.has(font)) {
8638
8743
  results.push({
8639
- checkId: check34.id,
8744
+ checkId: check35.id,
8640
8745
  severity: "warning",
8641
8746
  message: "Font file not in approved list",
8642
8747
  file: font
@@ -8646,7 +8751,7 @@ var check34 = {
8646
8751
  return results;
8647
8752
  }
8648
8753
  };
8649
- registerCheck(check34);
8754
+ registerCheck(check35);
8650
8755
 
8651
8756
  // src/checks/asset/font-license.ts
8652
8757
  import { existsSync as existsSync10 } from "node:fs";
@@ -8661,7 +8766,7 @@ var LICENSE_NAMES = [
8661
8766
  "OFL-1.1.txt",
8662
8767
  "COPYING"
8663
8768
  ];
8664
- var check35 = {
8769
+ var check36 = {
8665
8770
  id: "asset.font.license",
8666
8771
  name: "Font License",
8667
8772
  description: "Checks for license files in font directories",
@@ -8688,7 +8793,7 @@ var check35 = {
8688
8793
  const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
8689
8794
  if (!hasLicense) {
8690
8795
  results.push({
8691
- checkId: check35.id,
8796
+ checkId: check36.id,
8692
8797
  severity: "warning",
8693
8798
  message: `No font license file found in directory`,
8694
8799
  file: fontDir
@@ -8698,10 +8803,10 @@ var check35 = {
8698
8803
  return results;
8699
8804
  }
8700
8805
  };
8701
- registerCheck(check35);
8806
+ registerCheck(check36);
8702
8807
 
8703
8808
  // src/checks/heuristic/text-density.ts
8704
- var check36 = {
8809
+ var check37 = {
8705
8810
  id: "heuristic.whitespace.text-density",
8706
8811
  name: "Text Density",
8707
8812
  description: "Checks characters-per-page ratio",
@@ -8730,7 +8835,7 @@ var check36 = {
8730
8835
  });
8731
8836
  if (lowPages.length > 0) {
8732
8837
  results.push({
8733
- checkId: check36.id,
8838
+ checkId: check37.id,
8734
8839
  severity: "info",
8735
8840
  message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
8736
8841
  file: ctx.pdfPath
@@ -8738,7 +8843,7 @@ var check36 = {
8738
8843
  }
8739
8844
  if (highPages.length > 0) {
8740
8845
  results.push({
8741
- checkId: check36.id,
8846
+ checkId: check37.id,
8742
8847
  severity: "info",
8743
8848
  message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
8744
8849
  file: ctx.pdfPath
@@ -8747,11 +8852,11 @@ var check36 = {
8747
8852
  return results;
8748
8853
  }
8749
8854
  };
8750
- registerCheck(check36);
8855
+ registerCheck(check37);
8751
8856
 
8752
8857
  // src/checks/heuristic/section-density.ts
8753
8858
  import { readFile as readFile22 } from "node:fs/promises";
8754
- var check37 = {
8859
+ var check38 = {
8755
8860
  id: "heuristic.chunking.section-density",
8756
8861
  name: "Section Density",
8757
8862
  description: "Checks heading/paragraph/callout density from source Markdown",
@@ -8777,7 +8882,7 @@ var check37 = {
8777
8882
  const line = lines[i];
8778
8883
  if (/^#{1,6}\s/.test(line)) {
8779
8884
  if (paragraphCount > maxParas) {
8780
- results.push(finding(check37.id, {
8885
+ results.push(finding(check38.id, {
8781
8886
  severity: "info",
8782
8887
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
8783
8888
  file,
@@ -8797,7 +8902,7 @@ var check37 = {
8797
8902
  }
8798
8903
  }
8799
8904
  if (paragraphCount > maxParas) {
8800
- results.push(finding(check37.id, {
8905
+ results.push(finding(check38.id, {
8801
8906
  severity: "info",
8802
8907
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
8803
8908
  file,
@@ -8805,7 +8910,7 @@ var check37 = {
8805
8910
  }));
8806
8911
  }
8807
8912
  } catch {
8808
- results.push(inspectionFailed(check37.id, `Could not read source file: ${file}`, {
8913
+ results.push(inspectionFailed(check38.id, `Could not read source file: ${file}`, {
8809
8914
  file
8810
8915
  }));
8811
8916
  }
@@ -8813,10 +8918,10 @@ var check37 = {
8813
8918
  return results;
8814
8919
  }
8815
8920
  };
8816
- registerCheck(check37);
8921
+ registerCheck(check38);
8817
8922
 
8818
8923
  // src/checks/heuristic/layer-count.ts
8819
- var check38 = {
8924
+ var check39 = {
8820
8925
  id: "heuristic.decoration.layer-count",
8821
8926
  name: "Layer Count",
8822
8927
  description: "Counts image objects per page",
@@ -8841,7 +8946,7 @@ var check38 = {
8841
8946
  heavyPages.sort((a, b) => a - b);
8842
8947
  return [
8843
8948
  {
8844
- checkId: check38.id,
8949
+ checkId: check39.id,
8845
8950
  severity: "info",
8846
8951
  message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
8847
8952
  file: ctx.pdfPath
@@ -8851,10 +8956,10 @@ var check38 = {
8851
8956
  return [];
8852
8957
  }
8853
8958
  };
8854
- registerCheck(check38);
8959
+ registerCheck(check39);
8855
8960
 
8856
8961
  // src/checks/heuristic/placement-variance.ts
8857
- var check39 = {
8962
+ var check40 = {
8858
8963
  id: "heuristic.layout.placement-variance",
8859
8964
  name: "Placement Variance",
8860
8965
  description: "Analyzes text baseline coordinates for layout consistency",
@@ -8872,7 +8977,7 @@ var check39 = {
8872
8977
  const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
8873
8978
  return [
8874
8979
  {
8875
- checkId: check39.id,
8980
+ checkId: check40.id,
8876
8981
  severity: "info",
8877
8982
  message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
8878
8983
  file: ctx.pdfPath
@@ -8880,21 +8985,21 @@ var check39 = {
8880
8985
  ];
8881
8986
  }
8882
8987
  };
8883
- registerCheck(check39);
8988
+ registerCheck(check40);
8884
8989
 
8885
8990
  // src/checks/tool-check.ts
8886
8991
  async function checkToolAvailability(config, opts = {}) {
8887
8992
  const { checks: checks2 } = selectChecks(opts, config);
8888
8993
  const toolToChecks = new Map;
8889
- for (const check40 of checks2) {
8890
- if (!check40.requiredTools?.length)
8994
+ for (const check41 of checks2) {
8995
+ if (!check41.requiredTools?.length)
8891
8996
  continue;
8892
- for (const tool of check40.requiredTools) {
8997
+ for (const tool of check41.requiredTools) {
8893
8998
  const existing = toolToChecks.get(tool);
8894
8999
  if (existing) {
8895
- existing.push(check40.id);
9000
+ existing.push(check41.id);
8896
9001
  } else {
8897
- toolToChecks.set(tool, [check40.id]);
9002
+ toolToChecks.set(tool, [check41.id]);
8898
9003
  }
8899
9004
  }
8900
9005
  }
@@ -8910,11 +9015,11 @@ async function checkToolAvailability(config, opts = {}) {
8910
9015
  const missing = results.filter((r) => !r.found).map((r) => r.tool);
8911
9016
  const missingSet = new Set(missing);
8912
9017
  const skippedChecks = [];
8913
- for (const check40 of checks2) {
8914
- if (!check40.requiredTools?.length)
9018
+ for (const check41 of checks2) {
9019
+ if (!check41.requiredTools?.length)
8915
9020
  continue;
8916
- if (check40.requiredTools.some((t) => missingSet.has(t))) {
8917
- skippedChecks.push(check40.id);
9021
+ if (check41.requiredTools.some((t) => missingSet.has(t))) {
9022
+ skippedChecks.push(check41.id);
8918
9023
  }
8919
9024
  }
8920
9025
  return { available, missing, skippedChecks, toolToChecks };
@@ -9394,16 +9499,16 @@ async function runChecks(ctx, opts = {}) {
9394
9499
  }
9395
9500
  const release = retainPdfCache();
9396
9501
  try {
9397
- for (const check40 of checks2) {
9502
+ for (const check41 of checks2) {
9398
9503
  try {
9399
- let results = await check40.run(ctx);
9400
- const severityOverride = getCheckSeverityOverride(check40.id, ctx.config);
9504
+ let results = await check41.run(ctx);
9505
+ const severityOverride = getCheckSeverityOverride(check41.id, ctx.config);
9401
9506
  if (severityOverride) {
9402
9507
  for (const r of results) {
9403
9508
  r.severity = severityOverride;
9404
9509
  }
9405
9510
  }
9406
- const lintLevel = ruleLevel(ctx.config.lint.rules, check40.id);
9511
+ const lintLevel = ruleLevel(ctx.config.lint.rules, check41.id);
9407
9512
  if (lintLevel === "off")
9408
9513
  results = [];
9409
9514
  else if (lintLevel) {
@@ -9411,15 +9516,15 @@ async function runChecks(ctx, opts = {}) {
9411
9516
  r.severity = lintLevel === "warn" ? "warning" : "error";
9412
9517
  }
9413
9518
  if (results.length === 0) {
9414
- passed.push(check40.id);
9519
+ passed.push(check41.id);
9415
9520
  } else {
9416
9521
  allResults.push(...results);
9417
9522
  }
9418
9523
  } catch (err) {
9419
9524
  allResults.push({
9420
- checkId: check40.id,
9525
+ checkId: check41.id,
9421
9526
  severity: "error",
9422
- 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)}`
9423
9528
  });
9424
9529
  }
9425
9530
  }
@@ -9711,7 +9816,7 @@ async function executeValidation(args) {
9711
9816
  };
9712
9817
  if (args.fix) {
9713
9818
  const selected = selectChecks(runnerOptions, config).checks;
9714
- if (selected.some((check40) => check40.id === MARKDOWNLINT_CHECK_ID)) {
9819
+ if (selected.some((check41) => check41.id === MARKDOWNLINT_CHECK_ID)) {
9715
9820
  await applyMarkdownlintFixes(context);
9716
9821
  } else {
9717
9822
  log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
@@ -10256,7 +10361,7 @@ class PdfOutput {
10256
10361
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
10257
10362
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
10258
10363
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
10259
- const { buildNativePdf } = await import("./engine-fe0n7v9p.js");
10364
+ const { buildNativePdf } = await import("./engine-9xb1ky8d.js");
10260
10365
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
10261
10366
  title: config.title,
10262
10367
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -10279,7 +10384,7 @@ class PdfOutput {
10279
10384
  const icc = opts.iccPath ?? config.pdfx.icc;
10280
10385
  effectiveIccPath = await resolveIccProfile(icc, manifestDir, opts.iccPath);
10281
10386
  if (await hasLiveTransparency(await fsp2.readFile(rawPdf))) {
10282
- log.warn("This book uses transparency — an image with an alpha channel, or a CSS " + `rule like \`opacity\` or \`mix-blend-mode\`. PDF/${pdfxMode === "x1a" ? "X-1a" : "X-3"} has no way to represent that (both are based on PDF 1.3, which predates PDF transparency), so Ghostscript will flatten every page that uses it into a single raster ` + "image — that page loses its embedded fonts and searchable text in the " + "PDF/X output. To keep vector text, remove the `opacity`/blend rule and flatten any alpha-channel image against its intended background before including it (export it with no alpha channel), or build --format pdf instead of pdfx if this book doesn't need print-ready CMYK.");
10387
+ log.warn("This book uses transparency — an image with an alpha channel, or a CSS " + `rule like \`opacity\` or \`mix-blend-mode\`. PDF/${pdfxMode === "x1a" ? "X-1a" : "X-3"} has no way to represent that (both are based on PDF 1.3, which predates PDF transparency), so Ghostscript will flatten every page that uses it into a single raster ` + "image — that page loses its embedded fonts and searchable text in the " + "PDF/X output. To keep vector text, remove the `opacity`/blend rule and flatten any alpha-channel image against its intended background before including it (export it with no alpha channel), or build --format pdf instead of pdfx if this book doesn't need print-ready CMYK. `pdfx.rasterDpi` sets the resolution of those page images (default 720).");
10283
10388
  }
10284
10389
  const blankGradientPages = await findPostScriptShadingPages(await fsp2.readFile(rawPdf));
10285
10390
  if (blankGradientPages.length > 0) {
@@ -10301,7 +10406,8 @@ class PdfOutput {
10301
10406
  pdfx: pdfxMode,
10302
10407
  title: config.title,
10303
10408
  maxTac: config.ink.maxTac,
10304
- stagingDir: stage
10409
+ stagingDir: stage,
10410
+ rasterDpi: config.pdfx.rasterDpi
10305
10411
  });
10306
10412
  }
10307
10413
  if (gates.postValidate) {
@@ -10512,8 +10618,17 @@ async function renderPreviewBook(inputPath, config, opts) {
10512
10618
  }
10513
10619
  });
10514
10620
  }
10515
- function injectPreviewScripts(html, insetBleedPt = 0) {
10516
- const meta = insetBleedPt > 0 ? ` <meta name="gp-inset-bleed" content="${insetBleedPt}">
10621
+ function injectPreviewScripts(html, page) {
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;")}'>
10517
10632
  ` : "";
10518
10633
  const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
10519
10634
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
@@ -10549,7 +10664,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
10549
10664
  for (const ref of nextImageRefs)
10550
10665
  imageRefs.add(ref);
10551
10666
  }
10552
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page?.bleed), "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");
10553
10668
  }
10554
10669
  function isDotPathUnderRoot(candidatePath, root) {
10555
10670
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");