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