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
@@ -9,7 +9,7 @@ import {
9
9
  getAssetsDir,
10
10
  lastPageMode,
11
11
  parseSize
12
- } from "./index-pnvzgftg.js";
12
+ } from "./index-3nr5rzzp.js";
13
13
  import {
14
14
  INSTALL_HINTS,
15
15
  enhancedPath,
@@ -141,6 +141,11 @@ function resolvePort(raw) {
141
141
  // src/lib/presets.ts
142
142
  var PRESET_IDS = ["dtrpg", "book", "custom"];
143
143
  var DTRPG_PRESET = {
144
+ id: "dtrpg",
145
+ spec: {
146
+ label: "DriveThruRPG — Quick Specifications for Print Books",
147
+ url: "https://help.drivethrupartners.com/hc/en-us/articles/12780800178583-Quick-Specifications-for-Print-Books"
148
+ },
144
149
  defaultTargets: ["dtrpg"],
145
150
  extensions: [],
146
151
  source: {
@@ -159,7 +164,8 @@ var DTRPG_PRESET = {
159
164
  height: 810,
160
165
  tolerance: 0.5,
161
166
  bleed: 9,
162
- safe: 18
167
+ safe: 36,
168
+ artSafe: 18
163
169
  },
164
170
  ink: {
165
171
  maxTac: 240,
@@ -188,6 +194,10 @@ var DTRPG_PRESET = {
188
194
  "pdf.print.embedded-fonts": {
189
195
  enabled: true,
190
196
  severity: "error"
197
+ },
198
+ "pdf.print.safe-margins": {
199
+ enabled: true,
200
+ severity: "error"
191
201
  }
192
202
  },
193
203
  source: {
@@ -220,6 +230,7 @@ var DTRPG_PRESET = {
220
230
  }
221
231
  };
222
232
  var BOOK_PRESET = {
233
+ id: "book",
223
234
  defaultTargets: [],
224
235
  extensions: [],
225
236
  source: {
@@ -238,7 +249,8 @@ var BOOK_PRESET = {
238
249
  height: 648,
239
250
  tolerance: 0.5,
240
251
  bleed: undefined,
241
- safe: 18
252
+ safe: 18,
253
+ artSafe: undefined
242
254
  },
243
255
  ink: {
244
256
  maxTac: 400,
@@ -260,6 +272,10 @@ var BOOK_PRESET = {
260
272
  enabled: true,
261
273
  severity: "error"
262
274
  },
275
+ "pdf.print.safe-margins": {
276
+ enabled: true,
277
+ severity: "warning"
278
+ },
263
279
  "pdf.print.pdfx-markers": false,
264
280
  "pdf.print.pdfx-metadata": false
265
281
  },
@@ -294,6 +310,7 @@ var BOOK_PRESET = {
294
310
  };
295
311
  var CUSTOM_PRESET = {
296
312
  ...BOOK_PRESET,
313
+ id: "custom",
297
314
  page: null,
298
315
  defaultTargets: []
299
316
  };
@@ -339,7 +356,8 @@ var DTRPG_TARGET = {
339
356
  "pdf.structure.qpdf": { enabled: true, severity: "error" },
340
357
  "pdf.print.pdfx-markers": { enabled: true, severity: "error" },
341
358
  "pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
342
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" }
359
+ "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
360
+ "pdf.print.safe-margins": { enabled: true, severity: "error" }
343
361
  },
344
362
  assets: {
345
363
  allowedColorSpaces: [...DTRPG_PRESET.validate.assets.allowedColorSpaces]
@@ -354,7 +372,8 @@ var DTRPG_TARGET = {
354
372
  "pdf.structure.qpdf",
355
373
  "pdf.print.pdfx-markers",
356
374
  "pdf.print.pdfx-metadata",
357
- "pdf.print.embedded-fonts"
375
+ "pdf.print.embedded-fonts",
376
+ "pdf.print.safe-margins"
358
377
  ],
359
378
  requiredTools: ["qpdf", "gs"]
360
379
  };
@@ -369,6 +388,7 @@ var ITCH_TARGET = {
369
388
  "pdf.print.pdfx-markers": false,
370
389
  "pdf.print.pdfx-metadata": false,
371
390
  "pdf.print.ink-coverage": false,
391
+ "pdf.print.safe-margins": false,
372
392
  "asset.image.tac-raster": false
373
393
  },
374
394
  assets: {
@@ -593,7 +613,8 @@ function resolveCustomPage(c, m) {
593
613
  height,
594
614
  tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
595
615
  bleed: c.page?.bleed ?? m.page?.bleed,
596
- safe: c.page?.safe ?? m.page?.safe
616
+ safe: c.page?.safe ?? m.page?.safe,
617
+ artSafe: c.page?.artSafe ?? m.page?.artSafe
597
618
  };
598
619
  }
599
620
  function resolveWithPreset(cliOverrides, manifest, preset) {
@@ -638,6 +659,8 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
638
659
  return {
639
660
  title: c.title ?? m.title ?? "Document",
640
661
  authors: c.authors ?? m.authors ?? [],
662
+ preset: preset.id,
663
+ spec: preset.spec,
641
664
  styles: c.styles ?? m.styles,
642
665
  extensions,
643
666
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
@@ -5994,7 +6017,7 @@ import git from "isomorphic-git";
5994
6017
  // package.json
5995
6018
  var package_default = {
5996
6019
  name: "gutterpress",
5997
- version: "0.11.20-alpha.1",
6020
+ version: "0.11.20-alpha.2",
5998
6021
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5999
6022
  author: "itlackey",
6000
6023
  license: "MPL-2.0",
@@ -6987,6 +7010,79 @@ var check3 = {
6987
7010
  };
6988
7011
  registerCheck(check3);
6989
7012
 
7013
+ // src/checks/pdf/safe-margins.ts
7014
+ var TOLERANCE_PT = 0.5;
7015
+ var ABOVE_BASELINE = 0.8;
7016
+ var BELOW_BASELINE = 0.2;
7017
+ var inches = (pt) => `${+(pt / 72).toFixed(2)}in`;
7018
+ var check4 = {
7019
+ id: "pdf.print.safe-margins",
7020
+ name: "Safe margins",
7021
+ 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",
7022
+ category: "pdf",
7023
+ phase: "post-build",
7024
+ async run(ctx) {
7025
+ const { width, height, bleed = 0, safe = 0 } = ctx.config.page;
7026
+ if (!ctx.pdfPath || !safe)
7027
+ return [];
7028
+ const doc = await loadPdf(ctx.pdfPath);
7029
+ if (!doc) {
7030
+ return [inspectionFailed(check4.id, "Could not parse the PDF's text positions.", { file: ctx.pdfPath })];
7031
+ }
7032
+ const rule = ctx.config.spec?.label ?? "page.safe";
7033
+ const results = [];
7034
+ try {
7035
+ for (let pageNum = 1;pageNum <= doc.numPages; pageNum++) {
7036
+ const recto = pageNum % 2 === 1;
7037
+ const trim = {
7038
+ left: recto ? 0 : bleed,
7039
+ right: width - (recto ? bleed : 0),
7040
+ bottom: bleed,
7041
+ top: height - bleed
7042
+ };
7043
+ const { items } = await (await doc.getPage(pageNum)).getTextContent();
7044
+ for (const item of items) {
7045
+ if (!("str" in item) || !item.str.trim())
7046
+ continue;
7047
+ const [a, b, c, d, e, f] = item.transform;
7048
+ const along = item.width / Math.hypot(a, b);
7049
+ const xs = [];
7050
+ const ys = [];
7051
+ for (const s of [0, 1]) {
7052
+ for (const u of [-BELOW_BASELINE, ABOVE_BASELINE]) {
7053
+ xs.push(e + s * along * a + u * c);
7054
+ ys.push(f + s * along * b + u * d);
7055
+ }
7056
+ }
7057
+ const gaps = [
7058
+ [recto ? "binding" : "outside", Math.min(...xs) - trim.left],
7059
+ [recto ? "outside" : "binding", trim.right - Math.max(...xs)],
7060
+ ["bottom", Math.min(...ys) - trim.bottom],
7061
+ ["top", trim.top - Math.max(...ys)]
7062
+ ];
7063
+ const [edge, gap] = gaps.reduce((worst, g) => g[1] < worst[1] ? g : worst);
7064
+ const shortfall = safe - gap;
7065
+ if (shortfall <= TOLERANCE_PT)
7066
+ continue;
7067
+ const text = item.str.trim();
7068
+ results.push(finding(check4.id, {
7069
+ severity: "error",
7070
+ message: `Page ${pageNum}: "${text.slice(0, 40)}${text.length > 40 ? "…" : ""}" is ${inches(shortfall)} too close to the ${edge} edge.`,
7071
+ file: ctx.pdfPath,
7072
+ detail: `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.`,
7073
+ code: "text-outside-safe-margin",
7074
+ data: { page: pageNum, edge, shortfallPt: +shortfall.toFixed(2) }
7075
+ }));
7076
+ }
7077
+ }
7078
+ } catch {
7079
+ return [inspectionFailed(check4.id, "Could not read the PDF's text positions.", { file: ctx.pdfPath })];
7080
+ }
7081
+ return results;
7082
+ }
7083
+ };
7084
+ registerCheck(check4);
7085
+
6990
7086
  // src/checks/pdf/pdfx-structure.ts
6991
7087
  function isObject(value) {
6992
7088
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -7095,7 +7191,7 @@ function getPdfxMetadataIssues(objects, flavor) {
7095
7191
  }
7096
7192
 
7097
7193
  // src/checks/pdf/pdfx-markers.ts
7098
- var check4 = {
7194
+ var check5 = {
7099
7195
  id: "pdf.print.pdfx-markers",
7100
7196
  name: "PDF/X Markers",
7101
7197
  description: "Checks PDF/X OutputIntent structure via qpdf JSON",
@@ -7114,22 +7210,22 @@ var check4 = {
7114
7210
  const objects = parseQpdfObjectsJson(stdout);
7115
7211
  if (!objects) {
7116
7212
  return [
7117
- inspectionFailed(check4.id, "Unable to parse qpdf JSON output for PDF/X checks.", { file: ctx.pdfPath })
7213
+ inspectionFailed(check5.id, "Unable to parse qpdf JSON output for PDF/X checks.", { file: ctx.pdfPath })
7118
7214
  ];
7119
7215
  }
7120
- return getPdfxOutputIntentIssues(objects).map((message) => finding(check4.id, {
7216
+ return getPdfxOutputIntentIssues(objects).map((message) => finding(check5.id, {
7121
7217
  severity: "error",
7122
7218
  message,
7123
7219
  file: ctx.pdfPath
7124
7220
  }));
7125
7221
  } catch {
7126
7222
  return [
7127
- inspectionFailed(check4.id, "Failed to inspect PDF objects with qpdf for PDF/X markers.", { file: ctx.pdfPath })
7223
+ inspectionFailed(check5.id, "Failed to inspect PDF objects with qpdf for PDF/X markers.", { file: ctx.pdfPath })
7128
7224
  ];
7129
7225
  }
7130
7226
  }
7131
7227
  };
7132
- registerCheck(check4);
7228
+ registerCheck(check5);
7133
7229
 
7134
7230
  // src/lib/pdf-parse.ts
7135
7231
  import { readFile as readFile14 } from "node:fs/promises";
@@ -7182,7 +7278,7 @@ async function getPerPageInkCoverage(pdfPath) {
7182
7278
  }
7183
7279
 
7184
7280
  // src/checks/pdf/color-spaces.ts
7185
- var check5 = {
7281
+ var check6 = {
7186
7282
  id: "pdf.print.color-spaces",
7187
7283
  name: "Color Spaces",
7188
7284
  description: "Checks for forbidden color spaces (DeviceRGB, Lab, Separation, DeviceN)",
@@ -7195,7 +7291,7 @@ var check5 = {
7195
7291
  const results = [];
7196
7292
  if (/\/DeviceRGB/.test(bytes)) {
7197
7293
  results.push({
7198
- checkId: check5.id,
7294
+ checkId: check6.id,
7199
7295
  severity: "error",
7200
7296
  message: "DeviceRGB found (interior must be CMYK or grayscale only).",
7201
7297
  file: ctx.pdfPath
@@ -7203,7 +7299,7 @@ var check5 = {
7203
7299
  }
7204
7300
  if (/\/Lab\b/.test(bytes)) {
7205
7301
  results.push({
7206
- checkId: check5.id,
7302
+ checkId: check6.id,
7207
7303
  severity: "error",
7208
7304
  message: "Lab color space found (not allowed).",
7209
7305
  file: ctx.pdfPath
@@ -7211,7 +7307,7 @@ var check5 = {
7211
7307
  }
7212
7308
  if (/\/Separation/.test(bytes)) {
7213
7309
  results.push({
7214
- checkId: check5.id,
7310
+ checkId: check6.id,
7215
7311
  severity: "error",
7216
7312
  message: "Spot color (Separation) found (not allowed).",
7217
7313
  file: ctx.pdfPath
@@ -7219,7 +7315,7 @@ var check5 = {
7219
7315
  }
7220
7316
  if (/\/DeviceN/.test(bytes)) {
7221
7317
  results.push({
7222
- checkId: check5.id,
7318
+ checkId: check6.id,
7223
7319
  severity: "error",
7224
7320
  message: "Spot color (DeviceN) found (not allowed).",
7225
7321
  file: ctx.pdfPath
@@ -7228,10 +7324,10 @@ var check5 = {
7228
7324
  return results;
7229
7325
  }
7230
7326
  };
7231
- registerCheck(check5);
7327
+ registerCheck(check6);
7232
7328
 
7233
7329
  // src/checks/pdf/embedded-fonts.ts
7234
- var check6 = {
7330
+ var check7 = {
7235
7331
  id: "pdf.print.embedded-fonts",
7236
7332
  name: "Embedded Fonts",
7237
7333
  description: "Verifies all fonts in the PDF are embedded",
@@ -7247,7 +7343,7 @@ var check6 = {
7247
7343
  if (fonts.length === 0) {
7248
7344
  return [
7249
7345
  {
7250
- checkId: check6.id,
7346
+ checkId: check7.id,
7251
7347
  severity: "warning",
7252
7348
  message: "No fonts detected (unexpected).",
7253
7349
  file: ctx.pdfPath,
@@ -7260,7 +7356,7 @@ var check6 = {
7260
7356
  const names = notEmbedded.map((f) => f.name);
7261
7357
  return [
7262
7358
  {
7263
- checkId: check6.id,
7359
+ checkId: check7.id,
7264
7360
  severity: "error",
7265
7361
  message: `Not all fonts are embedded (${names.join(", ")}). Check @font-face and Chromium output.`,
7266
7362
  file: ctx.pdfPath,
@@ -7272,10 +7368,10 @@ var check6 = {
7272
7368
  return [];
7273
7369
  }
7274
7370
  };
7275
- registerCheck(check6);
7371
+ registerCheck(check7);
7276
7372
 
7277
7373
  // src/checks/pdf/ink-coverage.ts
7278
- var check7 = {
7374
+ var check8 = {
7279
7375
  id: "pdf.print.ink-coverage",
7280
7376
  name: "Ink Coverage (TAC)",
7281
7377
  description: "Checks total area coverage (TAC) against maximum ink limits",
@@ -7289,7 +7385,7 @@ var check7 = {
7289
7385
  if (!inkResult.ok) {
7290
7386
  return [
7291
7387
  {
7292
- checkId: check7.id,
7388
+ checkId: check8.id,
7293
7389
  severity: "warning",
7294
7390
  message: "Ink coverage could not be measured",
7295
7391
  file: ctx.pdfPath,
@@ -7314,7 +7410,7 @@ var check7 = {
7314
7410
  }
7315
7411
  return [
7316
7412
  {
7317
- checkId: check7.id,
7413
+ checkId: check8.id,
7318
7414
  severity: "warning",
7319
7415
  message: `Total ink coverage too high on ${offending.length} page(s) (max ${maxTac.toFixed(1)}%, recommended <=${ctx.config.ink.maxTac}%)`,
7320
7416
  file: ctx.pdfPath,
@@ -7338,10 +7434,10 @@ var check7 = {
7338
7434
  ];
7339
7435
  }
7340
7436
  };
7341
- registerCheck(check7);
7437
+ registerCheck(check8);
7342
7438
 
7343
7439
  // src/checks/pdf/rasterized-pages.ts
7344
- var check8 = {
7440
+ var check9 = {
7345
7441
  id: "pdf.print.rasterized-pages",
7346
7442
  name: "Rasterized Pages",
7347
7443
  description: "Detects pages that appear to be fully rasterized (CSS filters, blend modes, transparency)",
@@ -7381,7 +7477,7 @@ var check8 = {
7381
7477
  rasterizedPages.sort((a, b) => a - b);
7382
7478
  return [
7383
7479
  {
7384
- checkId: check8.id,
7480
+ checkId: check9.id,
7385
7481
  severity: "warning",
7386
7482
  message: `Possible rasterized pages detected: ${rasterizedPages.join(", ")}`,
7387
7483
  file: ctx.pdfPath,
@@ -7392,11 +7488,11 @@ var check8 = {
7392
7488
  ];
7393
7489
  }
7394
7490
  };
7395
- registerCheck(check8);
7491
+ registerCheck(check9);
7396
7492
 
7397
7493
  // src/checks/pdf/black-text.ts
7398
7494
  import { readFile as readFile15 } from "node:fs/promises";
7399
- var check9 = {
7495
+ var check10 = {
7400
7496
  id: "pdf.print.black-text",
7401
7497
  name: "Black Text",
7402
7498
  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)",
@@ -7412,7 +7508,7 @@ var check9 = {
7412
7508
  if (found.runs === 0)
7413
7509
  return [];
7414
7510
  return [
7415
- finding(check9.id, {
7511
+ finding(check10.id, {
7416
7512
  severity: "warning",
7417
7513
  message: `Dark four-colour text at ${LARGE_TEXT_PT}pt or smaller on page(s) ${found.pages.join(", ")}.`,
7418
7514
  file: ctx.pdfPath,
@@ -7423,17 +7519,17 @@ var check9 = {
7423
7519
  ];
7424
7520
  } catch {
7425
7521
  return [
7426
- inspectionFailed(check9.id, "Could not inspect the PDF's text colours.", {
7522
+ inspectionFailed(check10.id, "Could not inspect the PDF's text colours.", {
7427
7523
  file: ctx.pdfPath
7428
7524
  })
7429
7525
  ];
7430
7526
  }
7431
7527
  }
7432
7528
  };
7433
- registerCheck(check9);
7529
+ registerCheck(check10);
7434
7530
 
7435
7531
  // src/checks/pdf/signature.ts
7436
- var check10 = {
7532
+ var check11 = {
7437
7533
  id: "pdf.print.signature",
7438
7534
  name: "Signature",
7439
7535
  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)",
@@ -7445,7 +7541,7 @@ var check10 = {
7445
7541
  return [];
7446
7542
  const doc = await loadPdf(ctx.pdfPath);
7447
7543
  if (!doc) {
7448
- return [inspectionFailed(check10.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
7544
+ return [inspectionFailed(check11.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
7449
7545
  }
7450
7546
  const size = ctx.config.print.signature;
7451
7547
  const pages = doc.numPages;
@@ -7454,7 +7550,7 @@ var check10 = {
7454
7550
  const data = { pages, signature: size };
7455
7551
  if (pages % size !== 0) {
7456
7552
  return [
7457
- finding(check10.id, {
7553
+ finding(check11.id, {
7458
7554
  severity: "error",
7459
7555
  message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
7460
7556
  file: ctx.pdfPath,
@@ -7470,10 +7566,10 @@ var check10 = {
7470
7566
  if (!await pageHasMarks(doc, pages))
7471
7567
  return [];
7472
7568
  } catch {
7473
- return [inspectionFailed(check10.id, "Could not read the last page.", { file: ctx.pdfPath })];
7569
+ return [inspectionFailed(check11.id, "Could not read the last page.", { file: ctx.pdfPath })];
7474
7570
  }
7475
7571
  return [
7476
- finding(check10.id, {
7572
+ finding(check11.id, {
7477
7573
  severity: "error",
7478
7574
  message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
7479
7575
  file: ctx.pdfPath,
@@ -7484,10 +7580,10 @@ var check10 = {
7484
7580
  ];
7485
7581
  }
7486
7582
  };
7487
- registerCheck(check10);
7583
+ registerCheck(check11);
7488
7584
 
7489
7585
  // src/checks/pdf/bookmarks.ts
7490
- var check11 = {
7586
+ var check12 = {
7491
7587
  id: "pdf.nav.bookmarks",
7492
7588
  name: "Bookmarks",
7493
7589
  description: "Checks for PDF outline (bookmarks) tree",
@@ -7501,14 +7597,14 @@ var check11 = {
7501
7597
  const doc = await loadPdf(ctx.pdfPath);
7502
7598
  if (!doc) {
7503
7599
  return [
7504
- inspectionFailed(check11.id, "Could not inspect PDF for bookmarks.", {
7600
+ inspectionFailed(check12.id, "Could not inspect PDF for bookmarks.", {
7505
7601
  file: ctx.pdfPath
7506
7602
  })
7507
7603
  ];
7508
7604
  }
7509
7605
  if (await getOutlineCount(doc) === 0) {
7510
7606
  return [
7511
- finding(check11.id, {
7607
+ finding(check12.id, {
7512
7608
  severity: "warning",
7513
7609
  message: "PDF does not contain bookmarks (outline tree).",
7514
7610
  file: ctx.pdfPath
@@ -7518,10 +7614,10 @@ var check11 = {
7518
7614
  return [];
7519
7615
  }
7520
7616
  };
7521
- registerCheck(check11);
7617
+ registerCheck(check12);
7522
7618
 
7523
7619
  // src/checks/pdf/toc-links.ts
7524
- var check12 = {
7620
+ var check13 = {
7525
7621
  id: "pdf.nav.toc-links",
7526
7622
  name: "TOC Links",
7527
7623
  description: "Detects link annotations on TOC pages",
@@ -7536,7 +7632,7 @@ var check12 = {
7536
7632
  if (!doc) {
7537
7633
  return [
7538
7634
  {
7539
- checkId: check12.id,
7635
+ checkId: check13.id,
7540
7636
  severity: "warning",
7541
7637
  message: "Could not inspect PDF for TOC links.",
7542
7638
  file: ctx.pdfPath
@@ -7547,7 +7643,7 @@ var check12 = {
7547
7643
  if (!hasLinks) {
7548
7644
  return [
7549
7645
  {
7550
- checkId: check12.id,
7646
+ checkId: check13.id,
7551
7647
  severity: "warning",
7552
7648
  message: "No link annotations found in PDF (expected for TOC).",
7553
7649
  file: ctx.pdfPath
@@ -7557,10 +7653,10 @@ var check12 = {
7557
7653
  return [];
7558
7654
  }
7559
7655
  };
7560
- registerCheck(check12);
7656
+ registerCheck(check13);
7561
7657
 
7562
7658
  // src/checks/pdf/cross-refs.ts
7563
- var check13 = {
7659
+ var check14 = {
7564
7660
  id: "pdf.nav.cross-refs",
7565
7661
  name: "Cross References",
7566
7662
  description: "Counts and verifies internal link annotations",
@@ -7573,7 +7669,7 @@ var check13 = {
7573
7669
  if (!doc) {
7574
7670
  return [
7575
7671
  {
7576
- checkId: check13.id,
7672
+ checkId: check14.id,
7577
7673
  severity: "info",
7578
7674
  message: "Could not inspect PDF for cross-references.",
7579
7675
  file: ctx.pdfPath
@@ -7583,7 +7679,7 @@ var check13 = {
7583
7679
  const linkCount = await countLinkAnnotations(doc);
7584
7680
  return [
7585
7681
  {
7586
- checkId: check13.id,
7682
+ checkId: check14.id,
7587
7683
  severity: "info",
7588
7684
  message: `PDF contains ${linkCount} internal link annotation${linkCount !== 1 ? "s" : ""}.`,
7589
7685
  file: ctx.pdfPath
@@ -7591,10 +7687,10 @@ var check13 = {
7591
7687
  ];
7592
7688
  }
7593
7689
  };
7594
- registerCheck(check13);
7690
+ registerCheck(check14);
7595
7691
 
7596
7692
  // src/checks/pdf/page-labels.ts
7597
- var check14 = {
7693
+ var check15 = {
7598
7694
  id: "pdf.nav.page-labels",
7599
7695
  name: "Page Labels",
7600
7696
  description: "Verifies PDF page labels / folio numbering",
@@ -7609,7 +7705,7 @@ var check14 = {
7609
7705
  const labels = await getPageLabels(doc);
7610
7706
  return [
7611
7707
  {
7612
- checkId: check14.id,
7708
+ checkId: check15.id,
7613
7709
  severity: "info",
7614
7710
  message: labels && labels.length > 0 ? "PDF contains page labels." : "PDF does not contain explicit page labels.",
7615
7711
  file: ctx.pdfPath
@@ -7617,10 +7713,10 @@ var check14 = {
7617
7713
  ];
7618
7714
  }
7619
7715
  };
7620
- registerCheck(check14);
7716
+ registerCheck(check15);
7621
7717
 
7622
7718
  // src/checks/pdf/image-resolution.ts
7623
- var check15 = {
7719
+ var check16 = {
7624
7720
  id: "pdf.print.image-resolution",
7625
7721
  name: "Image Resolution",
7626
7722
  description: "Checks DPI of images embedded in the PDF",
@@ -7647,7 +7743,7 @@ var check15 = {
7647
7743
  lowResPages.sort((a, b) => a - b);
7648
7744
  return [
7649
7745
  {
7650
- checkId: check15.id,
7746
+ checkId: check16.id,
7651
7747
  severity: "warning",
7652
7748
  message: `Low-resolution images (below ${minDpi} DPI) found on pages: ${lowResPages.join(", ")}`,
7653
7749
  file: ctx.pdfPath
@@ -7657,10 +7753,10 @@ var check15 = {
7657
7753
  return [];
7658
7754
  }
7659
7755
  };
7660
- registerCheck(check15);
7756
+ registerCheck(check16);
7661
7757
 
7662
7758
  // src/checks/pdf/transparency.ts
7663
- var check16 = {
7759
+ var check17 = {
7664
7760
  id: "pdf.print.transparency",
7665
7761
  name: "Transparency",
7666
7762
  description: "Checks for transparency markers (/Transparency, /SMask, /BM) in the PDF",
@@ -7682,7 +7778,7 @@ var check16 = {
7682
7778
  if (found.length > 0) {
7683
7779
  return [
7684
7780
  {
7685
- checkId: check16.id,
7781
+ checkId: check17.id,
7686
7782
  severity: "warning",
7687
7783
  message: `Transparency detected in PDF: ${found.join(", ")}. This may cause issues with some printers.`,
7688
7784
  file: ctx.pdfPath
@@ -7692,7 +7788,7 @@ var check16 = {
7692
7788
  return [];
7693
7789
  }
7694
7790
  };
7695
- registerCheck(check16);
7791
+ registerCheck(check17);
7696
7792
 
7697
7793
  // src/checks/pdf/bleed.ts
7698
7794
  function findBox(bytes, box) {
@@ -7702,7 +7798,7 @@ function findBox(bytes, box) {
7702
7798
  return null;
7703
7799
  return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
7704
7800
  }
7705
- var check17 = {
7801
+ var check18 = {
7706
7802
  id: "pdf.print.bleed",
7707
7803
  name: "Bleed Box",
7708
7804
  description: "Compares MediaBox vs TrimBox/BleedBox to verify bleed area",
@@ -7716,7 +7812,7 @@ var check17 = {
7716
7812
  const bytes = await readPdfBytes(ctx.pdfPath).catch(() => "");
7717
7813
  if (!bytes) {
7718
7814
  return [
7719
- inspectionFailed(check17.id, "Could not inspect PDF for bleed boxes.", {
7815
+ inspectionFailed(check18.id, "Could not inspect PDF for bleed boxes.", {
7720
7816
  file: ctx.pdfPath
7721
7817
  })
7722
7818
  ];
@@ -7726,14 +7822,14 @@ var check17 = {
7726
7822
  const bleed = findBox(bytes, "BleedBox");
7727
7823
  if (!media) {
7728
7824
  return [
7729
- inspectionFailed(check17.id, "Could not parse MediaBox from PDF.", {
7825
+ inspectionFailed(check18.id, "Could not parse MediaBox from PDF.", {
7730
7826
  file: ctx.pdfPath
7731
7827
  })
7732
7828
  ];
7733
7829
  }
7734
7830
  if (!trim && !bleed) {
7735
7831
  return [
7736
- finding(check17.id, {
7832
+ finding(check18.id, {
7737
7833
  severity: "warning",
7738
7834
  message: "No TrimBox or BleedBox found. Bleed area cannot be verified.",
7739
7835
  file: ctx.pdfPath
@@ -7746,7 +7842,7 @@ var check17 = {
7746
7842
  const bleedSize = ctx.config.validate.pdf.bleedSize;
7747
7843
  if (mediaW - trimW < bleedSize * 2 * 0.9) {
7748
7844
  return [
7749
- finding(check17.id, {
7845
+ finding(check18.id, {
7750
7846
  severity: "warning",
7751
7847
  message: `Bleed area appears insufficient. Expected at least ${bleedSize}pt on each side.`,
7752
7848
  file: ctx.pdfPath
@@ -7757,10 +7853,10 @@ var check17 = {
7757
7853
  return [];
7758
7854
  }
7759
7855
  };
7760
- registerCheck(check17);
7856
+ registerCheck(check18);
7761
7857
 
7762
7858
  // src/checks/pdf/pdfx-metadata.ts
7763
- var check18 = {
7859
+ var check19 = {
7764
7860
  id: "pdf.print.pdfx-metadata",
7765
7861
  name: "PDF/X Metadata",
7766
7862
  description: "Verifies PDF/X DOCINFO metadata via qpdf JSON",
@@ -7779,22 +7875,22 @@ var check18 = {
7779
7875
  const objects = parseQpdfObjectsJson(stdout);
7780
7876
  if (!objects) {
7781
7877
  return [
7782
- inspectionFailed(check18.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
7878
+ inspectionFailed(check19.id, "Unable to parse qpdf JSON output for PDF/X metadata checks.", { file: ctx.pdfPath })
7783
7879
  ];
7784
7880
  }
7785
- return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check18.id, {
7881
+ return getPdfxMetadataIssues(objects, ctx.config.pdfx.flavor).map((message) => finding(check19.id, {
7786
7882
  severity: "error",
7787
7883
  message,
7788
7884
  file: ctx.pdfPath
7789
7885
  }));
7790
7886
  } catch {
7791
7887
  return [
7792
- inspectionFailed(check18.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
7888
+ inspectionFailed(check19.id, "Failed to inspect PDF objects with qpdf for PDF/X metadata.", { file: ctx.pdfPath })
7793
7889
  ];
7794
7890
  }
7795
7891
  }
7796
7892
  };
7797
- registerCheck(check18);
7893
+ registerCheck(check19);
7798
7894
 
7799
7895
  // src/checks/source/htmlhint.ts
7800
7896
  import { existsSync as existsSync9 } from "node:fs";
@@ -7802,7 +7898,7 @@ import { readFile as readFile16 } from "node:fs/promises";
7802
7898
  import htmlhint from "htmlhint";
7803
7899
  var { HTMLHint } = htmlhint;
7804
7900
  var CONFIG_NAMES2 = [".htmlhintrc"];
7805
- var check19 = {
7901
+ var check20 = {
7806
7902
  id: "source.htmlhint",
7807
7903
  name: "HTMLHint",
7808
7904
  description: "Runs htmlhint with project config to validate generated HTML",
@@ -7822,7 +7918,7 @@ var check19 = {
7822
7918
  const html = await readFile16(ctx.htmlPath, "utf8");
7823
7919
  const messages = HTMLHint.verify(html, ruleset);
7824
7920
  return messages.map((m) => ({
7825
- checkId: check19.id,
7921
+ checkId: check20.id,
7826
7922
  severity: m.type === "error" ? "error" : "warning",
7827
7923
  message: `${m.rule.id}: ${m.message}`,
7828
7924
  file: ctx.htmlPath,
@@ -7831,7 +7927,7 @@ var check19 = {
7831
7927
  }));
7832
7928
  }
7833
7929
  };
7834
- registerCheck(check19);
7930
+ registerCheck(check20);
7835
7931
 
7836
7932
  // src/checks/source/stylelint.ts
7837
7933
  import { readFile as readFile17 } from "node:fs/promises";
@@ -8224,7 +8320,7 @@ function checkCssRaw(css, from) {
8224
8320
  }
8225
8321
 
8226
8322
  // src/checks/source/stylelint.ts
8227
- var check20 = {
8323
+ var check21 = {
8228
8324
  id: "source.stylelint",
8229
8325
  name: "Print-safety CSS",
8230
8326
  description: "Checks CSS for print-safety issues (remote URLs, rasterizing effects, and page-layer containment)",
@@ -8244,7 +8340,7 @@ var check20 = {
8244
8340
  css = await readFile17(file, "utf8");
8245
8341
  } catch (err) {
8246
8342
  results.push({
8247
- checkId: check20.id,
8343
+ checkId: check21.id,
8248
8344
  severity: "error",
8249
8345
  message: `Cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`,
8250
8346
  file
@@ -8253,7 +8349,7 @@ var check20 = {
8253
8349
  }
8254
8350
  for (const w of checkCss(css, file, { rules: ctx.config.lint.rules })) {
8255
8351
  results.push({
8256
- checkId: check20.id,
8352
+ checkId: check21.id,
8257
8353
  severity: w.severity,
8258
8354
  message: `${w.rule}: ${w.message}`,
8259
8355
  file,
@@ -8265,7 +8361,7 @@ var check20 = {
8265
8361
  return results;
8266
8362
  }
8267
8363
  };
8268
- registerCheck(check20);
8364
+ registerCheck(check21);
8269
8365
 
8270
8366
  // src/checks/source/css-ownership.ts
8271
8367
  import { readFile as readFile18 } from "node:fs/promises";
@@ -8386,7 +8482,7 @@ function checkOwnership(root, file, maps) {
8386
8482
  });
8387
8483
  return out;
8388
8484
  }
8389
- var check21 = {
8485
+ var check22 = {
8390
8486
  id: "source.css-ownership",
8391
8487
  name: "CSS ownership contract",
8392
8488
  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.",
@@ -8409,7 +8505,7 @@ var check21 = {
8409
8505
  } catch (err) {
8410
8506
  return [
8411
8507
  {
8412
- checkId: check21.id,
8508
+ checkId: check22.id,
8413
8509
  severity: "error",
8414
8510
  message: `Failed to parse CSS ownership contract "${configPath}": ${err instanceof Error ? err.message : String(err)}`,
8415
8511
  file: configPath
@@ -8451,7 +8547,7 @@ var check21 = {
8451
8547
  }
8452
8548
  for (const v of violations) {
8453
8549
  results.push({
8454
- checkId: check21.id,
8550
+ checkId: check22.id,
8455
8551
  severity: "warning",
8456
8552
  message: v.message,
8457
8553
  file: absFile,
@@ -8463,7 +8559,7 @@ var check21 = {
8463
8559
  return results;
8464
8560
  }
8465
8561
  };
8466
- registerCheck(check21);
8562
+ registerCheck(check22);
8467
8563
 
8468
8564
  // src/checks/source/local-refs.ts
8469
8565
  import { existsSync as existsSync10 } from "node:fs";
@@ -8550,7 +8646,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
8550
8646
  }
8551
8647
 
8552
8648
  // src/checks/source/local-refs.ts
8553
- var check22 = {
8649
+ var check23 = {
8554
8650
  id: "source.links.local-refs",
8555
8651
  name: "Local Markdown References",
8556
8652
  description: "Checks local markdown links and image refs resolve on disk",
@@ -8558,12 +8654,12 @@ var check22 = {
8558
8654
  phase: "pre-build",
8559
8655
  async run(ctx) {
8560
8656
  const results = [];
8561
- for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check22.id, results)) {
8657
+ for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
8562
8658
  if (isNonFilesystemRef(ref))
8563
8659
  continue;
8564
8660
  const escape = kind === "image" ? proseImageRefError(ref, ctx.inputDir) : null;
8565
8661
  if (escape) {
8566
- results.push(finding(check22.id, {
8662
+ results.push(finding(check23.id, {
8567
8663
  severity: "error",
8568
8664
  message: escape,
8569
8665
  file,
@@ -8573,7 +8669,7 @@ var check22 = {
8573
8669
  }
8574
8670
  if (localRefExists(ref, kind, file, ctx.inputDir))
8575
8671
  continue;
8576
- results.push(finding(check22.id, {
8672
+ results.push(finding(check23.id, {
8577
8673
  severity: kind === "image" ? "warning" : "error",
8578
8674
  code: kind === "image" ? "missing-image-placeholder" : undefined,
8579
8675
  message: kind === "image" ? `Local image not found; build will substitute a magenta placeholder: ${ref}` : `Local reference not found: ${ref}`,
@@ -8598,7 +8694,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
8598
8694
  return existsSync10(fileRelative);
8599
8695
  }
8600
8696
  }
8601
- registerCheck(check22);
8697
+ registerCheck(check23);
8602
8698
 
8603
8699
  // src/lib/build-staging.ts
8604
8700
  import path11 from "node:path";
@@ -8864,7 +8960,7 @@ async function createStageRoot() {
8864
8960
  }
8865
8961
 
8866
8962
  // src/checks/source/dangling-links.ts
8867
- var check23 = {
8963
+ var check24 = {
8868
8964
  id: "source.links.dangling",
8869
8965
  name: "Dangling Links",
8870
8966
  description: "Flags relative links the printed book cannot open (their href is dropped at print time)",
@@ -8872,10 +8968,10 @@ var check23 = {
8872
8968
  phase: "pre-build",
8873
8969
  async run(ctx) {
8874
8970
  const results = [];
8875
- for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check23.id, results)) {
8971
+ for await (const { ref, kind, line, file } of renderedLocalRefs(ctx, check24.id, results)) {
8876
8972
  if (kind !== "link" || isPrintResolvableHref(ref))
8877
8973
  continue;
8878
- results.push(finding(check23.id, {
8974
+ results.push(finding(check24.id, {
8879
8975
  severity: "warning",
8880
8976
  code: "dangling-link",
8881
8977
  data: { ref },
@@ -8887,11 +8983,11 @@ var check23 = {
8887
8983
  return results;
8888
8984
  }
8889
8985
  };
8890
- registerCheck(check23);
8986
+ registerCheck(check24);
8891
8987
 
8892
8988
  // src/checks/source/accessibility-alt-text.ts
8893
8989
  import { readFile as readFile20 } from "node:fs/promises";
8894
- var check24 = {
8990
+ var check25 = {
8895
8991
  id: "source.accessibility.alt-text",
8896
8992
  name: "Image Alt Text",
8897
8993
  description: "Checks markdown images include non-empty alt text",
@@ -8920,7 +9016,7 @@ var check24 = {
8920
9016
  if (alt.trim())
8921
9017
  continue;
8922
9018
  results.push({
8923
- checkId: check24.id,
9019
+ checkId: check25.id,
8924
9020
  severity: "warning",
8925
9021
  message: "Image is missing alt text",
8926
9022
  file,
@@ -8945,11 +9041,11 @@ function extractImageAlts(line) {
8945
9041
  }
8946
9042
  return alts;
8947
9043
  }
8948
- registerCheck(check24);
9044
+ registerCheck(check25);
8949
9045
 
8950
9046
  // src/checks/source/accessibility-heading-order.ts
8951
9047
  import { readFile as readFile21 } from "node:fs/promises";
8952
- var check25 = {
9048
+ var check26 = {
8953
9049
  id: "source.accessibility.heading-order",
8954
9050
  name: "Heading Order",
8955
9051
  description: "Checks markdown heading levels do not jump by more than one",
@@ -8977,7 +9073,7 @@ var check25 = {
8977
9073
  if (inFence)
8978
9074
  continue;
8979
9075
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
8980
- if (comment && parseDisableDirective(comment[1])?.includes(check25.id)) {
9076
+ if (comment && parseDisableDirective(comment[1])?.includes(check26.id)) {
8981
9077
  silencedLine = i + 1;
8982
9078
  continue;
8983
9079
  }
@@ -8987,7 +9083,7 @@ var check25 = {
8987
9083
  const level = match[1].length;
8988
9084
  if (prevLevel != null && level > prevLevel + 1 && i !== silencedLine) {
8989
9085
  results.push({
8990
- checkId: check25.id,
9086
+ checkId: check26.id,
8991
9087
  severity: "warning",
8992
9088
  message: `Heading level jump from h${prevLevel} to h${level}`,
8993
9089
  file,
@@ -9001,7 +9097,7 @@ var check25 = {
9001
9097
  return results;
9002
9098
  }
9003
9099
  };
9004
- registerCheck(check25);
9100
+ registerCheck(check26);
9005
9101
 
9006
9102
  // src/checks/source/layout-markers.ts
9007
9103
  import { readFile as readFile22 } from "node:fs/promises";
@@ -9112,7 +9208,7 @@ async function collectKnownClasses(ctx, plugins) {
9112
9208
  }
9113
9209
  return names;
9114
9210
  }
9115
- var check26 = {
9211
+ var check27 = {
9116
9212
  id: "source.markdown.layout-markers",
9117
9213
  name: "Layout Markers",
9118
9214
  description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
@@ -9124,7 +9220,7 @@ var check26 = {
9124
9220
  return [];
9125
9221
  const results = [];
9126
9222
  const plugins = await loadPlugins(ctx.config.extensions, ctx.inputDir, (ref, error2) => {
9127
- results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
9223
+ results.push(inspectionFailed(check27.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
9128
9224
  });
9129
9225
  const md = createMarkdownRenderer(plugins);
9130
9226
  let known;
@@ -9143,7 +9239,7 @@ var check26 = {
9143
9239
  if (names.has(cls) || warnedLines.has(use.line))
9144
9240
  continue;
9145
9241
  const guess = nearestKnownClass(cls, names);
9146
- results.push(finding(check26.id, {
9242
+ results.push(finding(check27.id, {
9147
9243
  severity: "warning",
9148
9244
  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.` : ""),
9149
9245
  file,
@@ -9153,7 +9249,7 @@ var check26 = {
9153
9249
  }
9154
9250
  }
9155
9251
  for (const w of env.layoutWarnings ?? []) {
9156
- results.push(finding(check26.id, {
9252
+ results.push(finding(check27.id, {
9157
9253
  severity: w.severity ?? "warning",
9158
9254
  message: w.message,
9159
9255
  file,
@@ -9162,13 +9258,13 @@ var check26 = {
9162
9258
  }));
9163
9259
  }
9164
9260
  } catch (error2) {
9165
- results.push(inspectionFailed(check26.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
9261
+ results.push(inspectionFailed(check27.id, `Could not check layout markers in ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`, { file }));
9166
9262
  }
9167
9263
  }
9168
9264
  return results;
9169
9265
  }
9170
9266
  };
9171
- registerCheck(check26);
9267
+ registerCheck(check27);
9172
9268
 
9173
9269
  // src/checks/source/merge-markers.ts
9174
9270
  import { readFile as readFile23 } from "node:fs/promises";
@@ -9201,7 +9297,7 @@ var CLOSE_SENTINEL = ">>>>>>> online version";
9201
9297
  function withoutOnlineTag(relPath) {
9202
9298
  return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
9203
9299
  }
9204
- var check27 = {
9300
+ var check28 = {
9205
9301
  id: "source.sync.merge-markers",
9206
9302
  name: "Combined Versions",
9207
9303
  description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
@@ -9215,7 +9311,7 @@ var check27 = {
9215
9311
  try {
9216
9312
  content = await readFile23(file, "utf8");
9217
9313
  } catch {
9218
- results.push(inspectionFailed(check27.id, `Could not read source file: ${file}`, { file }));
9314
+ results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
9219
9315
  continue;
9220
9316
  }
9221
9317
  if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
@@ -9227,7 +9323,7 @@ var check27 = {
9227
9323
  const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
9228
9324
  if (line === OPEN_SENTINEL) {
9229
9325
  inBlock = true;
9230
- results.push(finding(check27.id, {
9326
+ results.push(finding(check28.id, {
9231
9327
  severity: "error",
9232
9328
  code: "two-versions-passage",
9233
9329
  message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
@@ -9236,7 +9332,7 @@ var check27 = {
9236
9332
  }));
9237
9333
  } else if (line === CLOSE_SENTINEL) {
9238
9334
  if (!inBlock) {
9239
- results.push(finding(check27.id, {
9335
+ results.push(finding(check28.id, {
9240
9336
  severity: "error",
9241
9337
  code: "leftover-version-marker",
9242
9338
  message: "This marker line is left over from combining two versions — delete it.",
@@ -9257,7 +9353,7 @@ var check27 = {
9257
9353
  });
9258
9354
  for (const sibling of siblings.sort()) {
9259
9355
  const rel = path12.relative(ctx.inputDir, sibling).split(path12.sep).join("/");
9260
- results.push(finding(check27.id, {
9356
+ results.push(finding(check28.id, {
9261
9357
  severity: "warning",
9262
9358
  code: "kept-both-versions",
9263
9359
  message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
@@ -9265,12 +9361,12 @@ var check27 = {
9265
9361
  }));
9266
9362
  }
9267
9363
  } catch (error2) {
9268
- results.push(inspectionFailed(check27.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
9364
+ results.push(inspectionFailed(check28.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
9269
9365
  }
9270
9366
  return results;
9271
9367
  }
9272
9368
  };
9273
- registerCheck(check27);
9369
+ registerCheck(check28);
9274
9370
 
9275
9371
  // src/checks/source/characters.ts
9276
9372
  import { readFile as readFile24 } from "node:fs/promises";
@@ -9385,7 +9481,7 @@ function codePointLabel(char) {
9385
9481
 
9386
9482
  // src/checks/source/characters.ts
9387
9483
  var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
9388
- var check28 = {
9484
+ var check29 = {
9389
9485
  id: "source.characters",
9390
9486
  name: "Unusual Characters",
9391
9487
  description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
@@ -9398,7 +9494,7 @@ var check28 = {
9398
9494
  try {
9399
9495
  content = await readFile24(file, "utf8");
9400
9496
  } catch {
9401
- results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
9497
+ results.push(inspectionFailed(check29.id, `Could not read source file: ${file}`, { file }));
9402
9498
  continue;
9403
9499
  }
9404
9500
  if (!PROBE.test(content))
@@ -9409,7 +9505,7 @@ var check28 = {
9409
9505
  for (let i = 0;i < lines.length; i++) {
9410
9506
  const line = lines[i];
9411
9507
  const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
9412
- if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
9508
+ if (comment && parseDisableDirective(comment[1])?.includes(check29.id)) {
9413
9509
  silencedLine = i + 1;
9414
9510
  continue;
9415
9511
  }
@@ -9418,7 +9514,7 @@ var check28 = {
9418
9514
  for (const match of line.matchAll(CHARACTER_PATTERN)) {
9419
9515
  const char = match[0];
9420
9516
  const { name, code, fix } = describeCharacter(char);
9421
- results.push(finding(check28.id, {
9517
+ results.push(finding(check29.id, {
9422
9518
  severity: code === "typographer" ? "info" : "warning",
9423
9519
  code,
9424
9520
  message: `${name} (${codePointLabel(char)}). ${fix}`,
@@ -9433,7 +9529,7 @@ var check28 = {
9433
9529
  return results;
9434
9530
  }
9435
9531
  };
9436
- registerCheck(check28);
9532
+ registerCheck(check29);
9437
9533
 
9438
9534
  // src/checks/asset/image-file-size.ts
9439
9535
  import { stat as stat4 } from "node:fs/promises";
@@ -9659,7 +9755,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
9659
9755
  }
9660
9756
 
9661
9757
  // src/checks/asset/image-file-size.ts
9662
- var check29 = {
9758
+ var check30 = {
9663
9759
  id: "asset.image.file-size",
9664
9760
  name: "Image File Size",
9665
9761
  description: "Checks that image files do not exceed the maximum size limit",
@@ -9680,14 +9776,14 @@ var check29 = {
9680
9776
  if (info3.size > maxSize) {
9681
9777
  const sizeMb = (info3.size / 1e6).toFixed(1);
9682
9778
  const maxMb = (maxSize / 1e6).toFixed(1);
9683
- results.push(finding(check29.id, {
9779
+ results.push(finding(check30.id, {
9684
9780
  severity: "warning",
9685
9781
  message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
9686
9782
  file
9687
9783
  }));
9688
9784
  }
9689
9785
  } catch {
9690
- results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
9786
+ results.push(inspectionFailed(check30.id, `Could not stat image file: ${file}`, {
9691
9787
  file
9692
9788
  }));
9693
9789
  }
@@ -9695,10 +9791,10 @@ var check29 = {
9695
9791
  return results;
9696
9792
  }
9697
9793
  };
9698
- registerCheck(check29);
9794
+ registerCheck(check30);
9699
9795
 
9700
9796
  // src/checks/asset/image-resolution.ts
9701
- var check30 = {
9797
+ var check31 = {
9702
9798
  id: "asset.image.resolution",
9703
9799
  name: "Image Resolution",
9704
9800
  description: "Checks source image DPI from embedded density metadata",
@@ -9720,7 +9816,7 @@ var check30 = {
9720
9816
  const { xDpi, yDpi } = info3;
9721
9817
  if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
9722
9818
  results.push({
9723
- checkId: check30.id,
9819
+ checkId: check31.id,
9724
9820
  severity: "warning",
9725
9821
  message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
9726
9822
  file
@@ -9730,7 +9826,7 @@ var check30 = {
9730
9826
  return results;
9731
9827
  }
9732
9828
  };
9733
- registerCheck(check30);
9829
+ registerCheck(check31);
9734
9830
 
9735
9831
  // src/checks/asset/image-color-space.ts
9736
9832
  var LABEL = {
@@ -9738,7 +9834,7 @@ var LABEL = {
9738
9834
  gray: "Gray",
9739
9835
  cmyk: "CMYK"
9740
9836
  };
9741
- var check31 = {
9837
+ var check32 = {
9742
9838
  id: "asset.image.color-space",
9743
9839
  name: "Image Color Space",
9744
9840
  description: "Validates image color spaces against allowed list",
@@ -9759,7 +9855,7 @@ var check31 = {
9759
9855
  const cs = info3?.colorSpace;
9760
9856
  if (cs && !allowedLower.has(cs)) {
9761
9857
  results.push({
9762
- checkId: check31.id,
9858
+ checkId: check32.id,
9763
9859
  severity: "warning",
9764
9860
  message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
9765
9861
  file
@@ -9769,10 +9865,10 @@ var check31 = {
9769
9865
  return results;
9770
9866
  }
9771
9867
  };
9772
- registerCheck(check31);
9868
+ registerCheck(check32);
9773
9869
 
9774
9870
  // src/checks/asset/image-alpha.ts
9775
- var check32 = {
9871
+ var check33 = {
9776
9872
  id: "asset.image.alpha-channel",
9777
9873
  name: "Image Alpha Channel",
9778
9874
  description: "Checks for alpha channels in PNG/TIFF images",
@@ -9790,7 +9886,7 @@ var check32 = {
9790
9886
  const info3 = await inspectImage(file);
9791
9887
  if (info3?.hasAlpha) {
9792
9888
  results.push({
9793
- checkId: check32.id,
9889
+ checkId: check33.id,
9794
9890
  severity: "warning",
9795
9891
  message: "Image contains alpha channel, which may cause print issues",
9796
9892
  file
@@ -9800,10 +9896,10 @@ var check32 = {
9800
9896
  return results;
9801
9897
  }
9802
9898
  };
9803
- registerCheck(check32);
9899
+ registerCheck(check33);
9804
9900
 
9805
9901
  // src/checks/asset/image-tac.ts
9806
- var check33 = {
9902
+ var check34 = {
9807
9903
  id: "asset.image.tac-raster",
9808
9904
  name: "Image TAC (Raster)",
9809
9905
  description: "Rasterizes and checks TAC per image using Ghostscript",
@@ -9822,7 +9918,7 @@ var check33 = {
9822
9918
  const ghostscript = await resolveGhostscript();
9823
9919
  if (!ghostscript) {
9824
9920
  return [
9825
- inspectionFailed(check33.id, "Could not inspect image ink coverage: Ghostscript executable not found")
9921
+ inspectionFailed(check34.id, "Could not inspect image ink coverage: Ghostscript executable not found")
9826
9922
  ];
9827
9923
  }
9828
9924
  for (const file of files) {
@@ -9840,7 +9936,7 @@ var check33 = {
9840
9936
  if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
9841
9937
  const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
9842
9938
  if (tac > maxTac) {
9843
- results.push(finding(check33.id, {
9939
+ results.push(finding(check34.id, {
9844
9940
  severity: "warning",
9845
9941
  message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
9846
9942
  file,
@@ -9852,16 +9948,16 @@ var check33 = {
9852
9948
  }
9853
9949
  }
9854
9950
  } catch {
9855
- results.push(inspectionFailed(check33.id, `Could not inspect image ink coverage: ${file}`, { file }));
9951
+ results.push(inspectionFailed(check34.id, `Could not inspect image ink coverage: ${file}`, { file }));
9856
9952
  }
9857
9953
  }
9858
9954
  return results;
9859
9955
  }
9860
9956
  };
9861
- registerCheck(check33);
9957
+ registerCheck(check34);
9862
9958
 
9863
9959
  // src/checks/asset/approved-fonts.ts
9864
- var check34 = {
9960
+ var check35 = {
9865
9961
  id: "asset.font.approved-files",
9866
9962
  name: "Approved Font Files",
9867
9963
  description: "Checks font files against the approved file patterns",
@@ -9900,7 +9996,7 @@ var check34 = {
9900
9996
  for (const font of allFonts) {
9901
9997
  if (!approvedFonts.has(font)) {
9902
9998
  results.push({
9903
- checkId: check34.id,
9999
+ checkId: check35.id,
9904
10000
  severity: "warning",
9905
10001
  message: "Font file not in approved list",
9906
10002
  file: font
@@ -9910,7 +10006,7 @@ var check34 = {
9910
10006
  return results;
9911
10007
  }
9912
10008
  };
9913
- registerCheck(check34);
10009
+ registerCheck(check35);
9914
10010
 
9915
10011
  // src/checks/asset/font-license.ts
9916
10012
  import { existsSync as existsSync11 } from "node:fs";
@@ -9925,7 +10021,7 @@ var LICENSE_NAMES = [
9925
10021
  "OFL-1.1.txt",
9926
10022
  "COPYING"
9927
10023
  ];
9928
- var check35 = {
10024
+ var check36 = {
9929
10025
  id: "asset.font.license",
9930
10026
  name: "Font License",
9931
10027
  description: "Checks for license files in font directories",
@@ -9952,7 +10048,7 @@ var check35 = {
9952
10048
  const hasLicense = LICENSE_NAMES.some((name) => existsSync11(resolve7(fontDir, name)));
9953
10049
  if (!hasLicense) {
9954
10050
  results.push({
9955
- checkId: check35.id,
10051
+ checkId: check36.id,
9956
10052
  severity: "warning",
9957
10053
  message: `No font license file found in directory`,
9958
10054
  file: fontDir
@@ -9962,10 +10058,10 @@ var check35 = {
9962
10058
  return results;
9963
10059
  }
9964
10060
  };
9965
- registerCheck(check35);
10061
+ registerCheck(check36);
9966
10062
 
9967
10063
  // src/checks/heuristic/text-density.ts
9968
- var check36 = {
10064
+ var check37 = {
9969
10065
  id: "heuristic.whitespace.text-density",
9970
10066
  name: "Text Density",
9971
10067
  description: "Checks characters-per-page ratio",
@@ -9994,7 +10090,7 @@ var check36 = {
9994
10090
  });
9995
10091
  if (lowPages.length > 0) {
9996
10092
  results.push({
9997
- checkId: check36.id,
10093
+ checkId: check37.id,
9998
10094
  severity: "info",
9999
10095
  message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
10000
10096
  file: ctx.pdfPath
@@ -10002,7 +10098,7 @@ var check36 = {
10002
10098
  }
10003
10099
  if (highPages.length > 0) {
10004
10100
  results.push({
10005
- checkId: check36.id,
10101
+ checkId: check37.id,
10006
10102
  severity: "info",
10007
10103
  message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
10008
10104
  file: ctx.pdfPath
@@ -10011,11 +10107,11 @@ var check36 = {
10011
10107
  return results;
10012
10108
  }
10013
10109
  };
10014
- registerCheck(check36);
10110
+ registerCheck(check37);
10015
10111
 
10016
10112
  // src/checks/heuristic/section-density.ts
10017
10113
  import { readFile as readFile26 } from "node:fs/promises";
10018
- var check37 = {
10114
+ var check38 = {
10019
10115
  id: "heuristic.chunking.section-density",
10020
10116
  name: "Section Density",
10021
10117
  description: "Checks heading/paragraph/callout density from source Markdown",
@@ -10041,7 +10137,7 @@ var check37 = {
10041
10137
  const line = lines[i];
10042
10138
  if (/^#{1,6}\s/.test(line)) {
10043
10139
  if (paragraphCount > maxParas) {
10044
- results.push(finding(check37.id, {
10140
+ results.push(finding(check38.id, {
10045
10141
  severity: "info",
10046
10142
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
10047
10143
  file,
@@ -10061,7 +10157,7 @@ var check37 = {
10061
10157
  }
10062
10158
  }
10063
10159
  if (paragraphCount > maxParas) {
10064
- results.push(finding(check37.id, {
10160
+ results.push(finding(check38.id, {
10065
10161
  severity: "info",
10066
10162
  message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
10067
10163
  file,
@@ -10069,7 +10165,7 @@ var check37 = {
10069
10165
  }));
10070
10166
  }
10071
10167
  } catch {
10072
- results.push(inspectionFailed(check37.id, `Could not read source file: ${file}`, {
10168
+ results.push(inspectionFailed(check38.id, `Could not read source file: ${file}`, {
10073
10169
  file
10074
10170
  }));
10075
10171
  }
@@ -10077,10 +10173,10 @@ var check37 = {
10077
10173
  return results;
10078
10174
  }
10079
10175
  };
10080
- registerCheck(check37);
10176
+ registerCheck(check38);
10081
10177
 
10082
10178
  // src/checks/heuristic/layer-count.ts
10083
- var check38 = {
10179
+ var check39 = {
10084
10180
  id: "heuristic.decoration.layer-count",
10085
10181
  name: "Layer Count",
10086
10182
  description: "Counts image objects per page",
@@ -10105,7 +10201,7 @@ var check38 = {
10105
10201
  heavyPages.sort((a, b) => a - b);
10106
10202
  return [
10107
10203
  {
10108
- checkId: check38.id,
10204
+ checkId: check39.id,
10109
10205
  severity: "info",
10110
10206
  message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
10111
10207
  file: ctx.pdfPath
@@ -10115,10 +10211,10 @@ var check38 = {
10115
10211
  return [];
10116
10212
  }
10117
10213
  };
10118
- registerCheck(check38);
10214
+ registerCheck(check39);
10119
10215
 
10120
10216
  // src/checks/heuristic/placement-variance.ts
10121
- var check39 = {
10217
+ var check40 = {
10122
10218
  id: "heuristic.layout.placement-variance",
10123
10219
  name: "Placement Variance",
10124
10220
  description: "Analyzes text baseline coordinates for layout consistency",
@@ -10136,7 +10232,7 @@ var check39 = {
10136
10232
  const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
10137
10233
  return [
10138
10234
  {
10139
- checkId: check39.id,
10235
+ checkId: check40.id,
10140
10236
  severity: "info",
10141
10237
  message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
10142
10238
  file: ctx.pdfPath
@@ -10144,7 +10240,7 @@ var check39 = {
10144
10240
  ];
10145
10241
  }
10146
10242
  };
10147
- registerCheck(check39);
10243
+ registerCheck(check40);
10148
10244
 
10149
10245
  // src/checks/runner.ts
10150
10246
  function getCheckSeverityOverride(checkId, config) {
@@ -10174,16 +10270,16 @@ async function runChecks(ctx, opts = {}) {
10174
10270
  }
10175
10271
  const release = retainPdfCache();
10176
10272
  try {
10177
- for (const check40 of checks2) {
10273
+ for (const check41 of checks2) {
10178
10274
  try {
10179
- let results = await check40.run(ctx);
10180
- const severityOverride = getCheckSeverityOverride(check40.id, ctx.config);
10275
+ let results = await check41.run(ctx);
10276
+ const severityOverride = getCheckSeverityOverride(check41.id, ctx.config);
10181
10277
  if (severityOverride) {
10182
10278
  for (const r of results) {
10183
10279
  r.severity = severityOverride;
10184
10280
  }
10185
10281
  }
10186
- const lintLevel = ruleLevel(ctx.config.lint.rules, check40.id);
10282
+ const lintLevel = ruleLevel(ctx.config.lint.rules, check41.id);
10187
10283
  if (lintLevel === "off")
10188
10284
  results = [];
10189
10285
  else if (lintLevel) {
@@ -10191,15 +10287,15 @@ async function runChecks(ctx, opts = {}) {
10191
10287
  r.severity = lintLevel === "warn" ? "warning" : "error";
10192
10288
  }
10193
10289
  if (results.length === 0) {
10194
- passed.push(check40.id);
10290
+ passed.push(check41.id);
10195
10291
  } else {
10196
10292
  allResults.push(...results);
10197
10293
  }
10198
10294
  } catch (err) {
10199
10295
  allResults.push({
10200
- checkId: check40.id,
10296
+ checkId: check41.id,
10201
10297
  severity: "error",
10202
- message: `Check "${check40.id}" threw: ${err instanceof Error ? err.message : String(err)}`
10298
+ message: `Check "${check41.id}" threw: ${err instanceof Error ? err.message : String(err)}`
10203
10299
  });
10204
10300
  }
10205
10301
  }
@@ -10239,15 +10335,15 @@ function emptyReport() {
10239
10335
  async function checkToolAvailability(config, opts = {}) {
10240
10336
  const { checks: checks2 } = selectChecks(opts, config);
10241
10337
  const toolToChecks = new Map;
10242
- for (const check40 of checks2) {
10243
- if (!check40.requiredTools?.length)
10338
+ for (const check41 of checks2) {
10339
+ if (!check41.requiredTools?.length)
10244
10340
  continue;
10245
- for (const tool of check40.requiredTools) {
10341
+ for (const tool of check41.requiredTools) {
10246
10342
  const existing = toolToChecks.get(tool);
10247
10343
  if (existing) {
10248
- existing.push(check40.id);
10344
+ existing.push(check41.id);
10249
10345
  } else {
10250
- toolToChecks.set(tool, [check40.id]);
10346
+ toolToChecks.set(tool, [check41.id]);
10251
10347
  }
10252
10348
  }
10253
10349
  }
@@ -10263,11 +10359,11 @@ async function checkToolAvailability(config, opts = {}) {
10263
10359
  const missing = results.filter((r) => !r.found).map((r) => r.tool);
10264
10360
  const missingSet = new Set(missing);
10265
10361
  const skippedChecks = [];
10266
- for (const check40 of checks2) {
10267
- if (!check40.requiredTools?.length)
10362
+ for (const check41 of checks2) {
10363
+ if (!check41.requiredTools?.length)
10268
10364
  continue;
10269
- if (check40.requiredTools.some((t) => missingSet.has(t))) {
10270
- skippedChecks.push(check40.id);
10365
+ if (check41.requiredTools.some((t) => missingSet.has(t))) {
10366
+ skippedChecks.push(check41.id);
10271
10367
  }
10272
10368
  }
10273
10369
  return { available, missing, skippedChecks, toolToChecks };
@@ -10540,7 +10636,7 @@ async function executeValidation(args) {
10540
10636
  };
10541
10637
  if (args.fix) {
10542
10638
  const selected = selectChecks(runnerOptions, config).checks;
10543
- if (selected.some((check40) => check40.id === MARKDOWNLINT_CHECK_ID)) {
10639
+ if (selected.some((check41) => check41.id === MARKDOWNLINT_CHECK_ID)) {
10544
10640
  await applyMarkdownlintFixes(context);
10545
10641
  } else {
10546
10642
  log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
@@ -10913,7 +11009,7 @@ class PdfOutput {
10913
11009
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
10914
11010
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
10915
11011
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
10916
- const { buildNativePdf } = await import("./engine-adxptzf0.js");
11012
+ const { buildNativePdf } = await import("./engine-yax79k9m.js");
10917
11013
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
10918
11014
  title: config.title,
10919
11015
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -11142,8 +11238,16 @@ async function renderPreviewBook(inputPath, config, opts) {
11142
11238
  });
11143
11239
  }
11144
11240
  function injectPreviewScripts(html, page) {
11145
- const area = page ? JSON.stringify({ width: page.width, height: page.height, bleed: page.bleed ?? 0, safe: page.safe ?? 0 }) : "";
11146
- const meta = area ? ` <meta name="gp-print-area" content='${area}'>
11241
+ const area = page ? JSON.stringify({
11242
+ width: page.width,
11243
+ height: page.height,
11244
+ bleed: page.bleed ?? 0,
11245
+ safe: page.safe ?? 0,
11246
+ artSafe: page.artSafe,
11247
+ preset: page.preset,
11248
+ spec: page.spec
11249
+ }) : "";
11250
+ const meta = area ? ` <meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>
11147
11251
  ` : "";
11148
11252
  const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
11149
11253
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
@@ -11179,7 +11283,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
11179
11283
  for (const ref of nextImageRefs)
11180
11284
  imageRefs.add(ref);
11181
11285
  }
11182
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page), "utf-8");
11286
+ await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page && { ...config.page, preset: config.preset, spec: config.spec }), "utf-8");
11183
11287
  }
11184
11288
  function isDotPathUnderRoot(candidatePath, root) {
11185
11289
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
@@ -12428,8 +12532,8 @@ function pageSizeForChoice(choice) {
12428
12532
  }
12429
12533
  var round3 = (n) => Math.round(n * 1000) / 1000;
12430
12534
  function cssLength(pt) {
12431
- const inches = round3(pt / 72);
12432
- return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
12535
+ const inches2 = round3(pt / 72);
12536
+ return Math.abs(inches2 * 72 - pt) < 0.000001 ? `${inches2}in` : `${round3(pt)}pt`;
12433
12537
  }
12434
12538
  function formatPageSize(page) {
12435
12539
  return `${cssLength(page.width)} ${cssLength(page.height)}`;