gutterpress 0.11.22-alpha.1 → 0.11.22-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 (44) hide show
  1. package/dist/api/index.js +6 -6
  2. package/dist/{build-se85j9ec.js → build-84retk4k.js} +9 -9
  3. package/dist/{cli-sth219v1.js → cli-2ppw6q9v.js} +26 -6
  4. package/dist/{cli-gc9av5hs.js → cli-3twpesca.js} +1 -1
  5. package/dist/{cli-9vy4b6ec.js → cli-5et23z83.js} +4 -4
  6. package/dist/{cli-yejbb9f8.js → cli-5qhjwne5.js} +1 -1
  7. package/dist/{cli-5553w972.js → cli-80t93x2r.js} +1 -1
  8. package/dist/{cli-fbwqv7p4.js → cli-885pcze8.js} +173 -61
  9. package/dist/{cli-8hccw62p.js → cli-9pk65fqf.js} +2 -2
  10. package/dist/{cli-gmm3bdeq.js → cli-hndkg7bn.js} +2 -2
  11. package/dist/{cli-pfrg1r9x.js → cli-pn3z0y9p.js} +1 -1
  12. package/dist/cli.js +12 -12
  13. package/dist/{doctor-bv2t82zd.js → doctor-fmzpg7ag.js} +3 -3
  14. package/dist/engine/shared/print-area.d.ts +77 -1
  15. package/dist/{engine-h4ehmrvf.js → engine-5p8tn9b4.js} +2 -2
  16. package/dist/{engine-qwkyhrej.js → engine-9710cd7x.js} +3 -3
  17. package/dist/{ext-a8ndp142.js → ext-hsdnad5c.js} +9 -9
  18. package/dist/{extension-restore-926mv2qk.js → extension-restore-05pae3cp.js} +3 -3
  19. package/dist/{extension-restore-q1f7njec.js → extension-restore-3r7phztr.js} +5 -5
  20. package/dist/{gutterpress-viewer-w7njnkj7.js → gutterpress-viewer-w9jzx6sb.js} +56 -52
  21. package/dist/{index-gmfh6n05.js → index-2d8j0enc.js} +2 -2
  22. package/dist/{index-sth219v1.js → index-2ppw6q9v.js} +26 -6
  23. package/dist/{index-tkjvs1gf.js → index-3wcr69ba.js} +162 -58
  24. package/dist/{index-5h9a38jn.js → index-apc8w9w8.js} +55 -55
  25. package/dist/{index-dm7hx46j.js → index-ew7cyk7c.js} +3 -3
  26. package/dist/{index-dhknsvfz.js → index-jsz0s3dy.js} +2 -2
  27. package/dist/{index-38e18a7j.js → index-kx03e4m4.js} +1 -1
  28. package/dist/index.js +7 -7
  29. package/dist/lib/markdown/assemble.d.ts +3 -2
  30. package/dist/lib/page-size.d.ts +3 -4
  31. package/dist/lib/pdf-inspect.d.ts +12 -0
  32. package/dist/lib/project-scaffold.d.ts +3 -3
  33. package/dist/{manifest.schema-n292sb77.json → manifest.schema-nvawq0xv.json} +2 -2
  34. package/dist/{new-nadp2hvb.js → new-fqtt022k.js} +9 -9
  35. package/dist/{pdf-inspect-zdmy7qca.js → pdf-inspect-wnw4za71.js} +1 -1
  36. package/dist/{plugins-yeh4shd7.js → plugins-gfvsgnxb.js} +1 -1
  37. package/dist/{plugins-gy4q9vaa.js → plugins-nfxr831f.js} +3 -3
  38. package/dist/{preflight-c6q25xqa.js → preflight-gr9mscjw.js} +9 -9
  39. package/dist/{preview-y8chv4cb.js → preview-52w9pdps.js} +9 -9
  40. package/dist/{publish-3qwvxsxa.js → publish-esmcyhpb.js} +9 -9
  41. package/dist/render.js +586 -0
  42. package/dist/schema/manifest.types.d.ts +8 -3
  43. package/dist/{validate-w108kk87.js → validate-vt1d7h6k.js} +9 -9
  44. package/package.json +1 -1
package/dist/api/index.js CHANGED
@@ -101,7 +101,7 @@ import {
101
101
  substituteVariables,
102
102
  syncProject,
103
103
  testRemoteAccess
104
- } from "../index-tkjvs1gf.js";
104
+ } from "../index-3wcr69ba.js";
105
105
  import {
106
106
  BUILT_IN_STYLE_SET_IDS,
107
107
  BUNDLED_EXTENSIONS,
@@ -138,7 +138,7 @@ import {
138
138
  setExtensionEnabled,
139
139
  updateExtensions,
140
140
  validateProjectExtensions
141
- } from "../index-dm7hx46j.js";
141
+ } from "../index-ew7cyk7c.js";
142
142
  import {
143
143
  EXTENSION_MANIFEST_FILENAME,
144
144
  assertExtensionContained,
@@ -148,13 +148,13 @@ import {
148
148
  pathEscapesFolder,
149
149
  readExtensionMeta,
150
150
  resolveExtension
151
- } from "../index-38e18a7j.js";
151
+ } from "../index-kx03e4m4.js";
152
152
  import"../index-ht1bcqdd.js";
153
- import"../index-5h9a38jn.js";
153
+ import"../index-apc8w9w8.js";
154
154
  import {
155
155
  BuildError
156
- } from "../index-dhknsvfz.js";
157
- import"../index-sth219v1.js";
156
+ } from "../index-jsz0s3dy.js";
157
+ import"../index-2ppw6q9v.js";
158
158
  import {
159
159
  DEFAULT_PRINT_OPTS,
160
160
  assertMilestone,
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  runBuild,
3
3
  splitOutPath
4
- } from "./cli-fbwqv7p4.js";
5
- import"./cli-yejbb9f8.js";
4
+ } from "./cli-885pcze8.js";
5
+ import"./cli-5qhjwne5.js";
6
6
  import"./cli-ht1bcqdd.js";
7
- import"./cli-8hccw62p.js";
8
- import"./cli-sth219v1.js";
7
+ import"./cli-9pk65fqf.js";
8
+ import"./cli-2ppw6q9v.js";
9
9
  import"./cli-eyyggect.js";
10
10
  import"./cli-asap4t1z.js";
11
11
  import"./cli-habkc5ng.js";
12
12
  import"./cli-v5mp7a6q.js";
13
- import"./cli-9vy4b6ec.js";
14
- import"./cli-pfrg1r9x.js";
13
+ import"./cli-5et23z83.js";
14
+ import"./cli-pn3z0y9p.js";
15
15
  import {
16
16
  log
17
- } from "./cli-5553w972.js";
17
+ } from "./cli-80t93x2r.js";
18
18
  import {
19
19
  UsageError,
20
20
  parseFormat,
@@ -22,8 +22,8 @@ import {
22
22
  rejectExtraPositionals,
23
23
  rejectUnknownFlags
24
24
  } from "./cli-d8xjns66.js";
25
- import"./cli-gc9av5hs.js";
26
- import"./cli-gmm3bdeq.js";
25
+ import"./cli-3twpesca.js";
26
+ import"./cli-hndkg7bn.js";
27
27
  import {
28
28
  BuildError
29
29
  } from "./cli-46ycxe6r.js";
@@ -168,6 +168,14 @@ function getTextPass(doc) {
168
168
  return p;
169
169
  }
170
170
  var opCache = new WeakMap;
171
+ var opSet = (names) => new Set(names.map((name) => OPS[name]).filter((op) => op !== undefined));
172
+ var IMAGE_OPS = opSet([
173
+ "paintImageXObject",
174
+ "paintImageXObjectRepeat",
175
+ "paintInlineImageXObject",
176
+ "paintImageMaskXObject",
177
+ "paintJpegXObject"
178
+ ]);
171
179
  function multiply(m1, m2) {
172
180
  return [
173
181
  m1[0] * m2[0] + m1[2] * m2[1],
@@ -219,14 +227,27 @@ function getOpPass(doc) {
219
227
  ctm = stack.pop() ?? ctm;
220
228
  } else if (fn === OPS.transform) {
221
229
  ctm = multiply(ctm, args);
222
- } else if (fn === OPS.paintImageXObject || fn === OPS.paintImageXObjectRepeat || fn === OPS.paintInlineImageXObject) {
230
+ } else if (fn === OPS.paintFormXObjectBegin) {
231
+ stack.push(ctm.slice());
232
+ const matrix = args[0];
233
+ if (Array.isArray(matrix))
234
+ ctm = multiply(ctm, matrix);
235
+ } else if (fn === OPS.paintFormXObjectEnd) {
236
+ ctm = stack.pop() ?? ctm;
237
+ } else if (IMAGE_OPS.has(fn)) {
223
238
  const name = typeof args[0] === "string" ? args[0] : "(inline)";
239
+ const [a, b, c, d, e, f] = ctm;
240
+ const xs = [e, a + e, c + e, a + c + e];
241
+ const ys = [f, b + f, d + f, b + d + f];
242
+ const x0 = Math.min(...xs);
243
+ const y0 = Math.min(...ys);
224
244
  pageImages.push({
225
245
  name,
226
- placedW: Math.hypot(ctm[0], ctm[1]),
227
- placedH: Math.hypot(ctm[2], ctm[3]),
228
- x: ctm[4],
229
- y: ctm[5],
246
+ placedW: Math.hypot(a, b),
247
+ placedH: Math.hypot(c, d),
248
+ x: e,
249
+ y: f,
250
+ bounds: { x: x0, y: y0, w: Math.max(...xs) - x0, h: Math.max(...ys) - y0 },
230
251
  page: i
231
252
  });
232
253
  } else if (fn === OPS.setFont) {
@@ -254,7 +275,6 @@ function getOpPass(doc) {
254
275
  }
255
276
  return p;
256
277
  }
257
- var opSet = (names) => new Set(names.map((name) => OPS[name]).filter((op) => op !== undefined));
258
278
  var FILL_COLOR_OPS = opSet(["setFillGray", "setFillRGBColor", "setFillCMYKColor", "setFillColor"]);
259
279
  var STROKE_COLOR_OPS = opSet(["setStrokeGray", "setStrokeRGBColor", "setStrokeCMYKColor", "setStrokeColor"]);
260
280
  var SHOW_TEXT_OPS = opSet(["showText", "showSpacedText", "nextLineShowText", "nextLineSetSpacingShowText"]);
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "gutterpress",
4
- version: "0.11.22-alpha.1",
4
+ version: "0.11.22-alpha.2",
5
5
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
6
6
  author: "itlackey",
7
7
  license: "MPL-2.0",
@@ -7,16 +7,16 @@ import {
7
7
  prettify,
8
8
  readExtensionMeta,
9
9
  slugify
10
- } from "./cli-pfrg1r9x.js";
10
+ } from "./cli-pn3z0y9p.js";
11
11
  import {
12
12
  log
13
- } from "./cli-5553w972.js";
13
+ } from "./cli-80t93x2r.js";
14
14
  import {
15
15
  UsageError
16
16
  } from "./cli-d8xjns66.js";
17
17
  import {
18
18
  getAssetsDir
19
- } from "./cli-gmm3bdeq.js";
19
+ } from "./cli-hndkg7bn.js";
20
20
  import {
21
21
  BuildError,
22
22
  EXIT_CODES
@@ -159,7 +159,7 @@ var DTRPG_PRESET = {
159
159
  bleed: 9,
160
160
  safe: 36,
161
161
  artSafe: 18,
162
- gutter: undefined
162
+ gutter: 36
163
163
  },
164
164
  ink: {
165
165
  maxTac: 240,
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  PACKAGE_VERSION,
13
13
  log
14
- } from "./cli-5553w972.js";
14
+ } from "./cli-80t93x2r.js";
15
15
  import {
16
16
  __require
17
17
  } from "./cli-37x76zdn.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "./cli-gc9av5hs.js";
3
+ } from "./cli-3twpesca.js";
4
4
 
5
5
  // src/utils/logger.ts
6
6
  var currentLevel = "INFO";
@@ -8,14 +8,16 @@ import {
8
8
  stampCreator,
9
9
  stripAnnotations,
10
10
  transparentPages
11
- } from "./cli-yejbb9f8.js";
11
+ } from "./cli-5qhjwne5.js";
12
12
  import {
13
13
  blackTextToGray
14
14
  } from "./cli-ht1bcqdd.js";
15
15
  import {
16
16
  MARGIN_BOX_IGNORED_PROPERTIES,
17
- lastPageMode
18
- } from "./cli-8hccw62p.js";
17
+ extract,
18
+ lastPageMode,
19
+ resolvePage
20
+ } from "./cli-9pk65fqf.js";
19
21
  import {
20
22
  countLinkAnnotations,
21
23
  getImageResolutions,
@@ -28,7 +30,7 @@ import {
28
30
  loadPdf,
29
31
  pageHasMarks,
30
32
  retainPdfCache
31
- } from "./cli-sth219v1.js";
33
+ } from "./cli-2ppw6q9v.js";
32
34
  import {
33
35
  INSTALL_HINTS,
34
36
  execCapture,
@@ -68,7 +70,7 @@ import {
68
70
  uniqueExtensionId,
69
71
  withFetchTimeout,
70
72
  writeManifestDoc
71
- } from "./cli-9vy4b6ec.js";
73
+ } from "./cli-5et23z83.js";
72
74
  import {
73
75
  EXTENSION_MANIFEST_FILENAME,
74
76
  GUTTERPRESS_CSS,
@@ -89,7 +91,7 @@ import {
89
91
  readExtensionMeta,
90
92
  resolveDeclaredStyles,
91
93
  slugify
92
- } from "./cli-pfrg1r9x.js";
94
+ } from "./cli-pn3z0y9p.js";
93
95
  import {
94
96
  PACKAGE_META,
95
97
  PACKAGE_VERSION,
@@ -99,7 +101,7 @@ import {
99
101
  log,
100
102
  setLogLevel,
101
103
  warn
102
- } from "./cli-5553w972.js";
104
+ } from "./cli-80t93x2r.js";
103
105
  import {
104
106
  DEBOUNCE,
105
107
  UsageError,
@@ -107,7 +109,7 @@ import {
107
109
  } from "./cli-d8xjns66.js";
108
110
  import {
109
111
  getAssetPath
110
- } from "./cli-gmm3bdeq.js";
112
+ } from "./cli-hndkg7bn.js";
111
113
  import {
112
114
  BuildError
113
115
  } from "./cli-46ycxe6r.js";
@@ -281,36 +283,122 @@ var check2 = {
281
283
  };
282
284
  registerCheck(check2);
283
285
 
286
+ // src/engine/shared/print-area.ts
287
+ function isPositivePoints(value) {
288
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
289
+ }
290
+ var round3 = (n) => Math.round(n * 1000) / 1000;
291
+ function cssLength(pt) {
292
+ const inches = round3(pt / 72);
293
+ return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
294
+ }
295
+ function formatPageSize(page) {
296
+ return `${cssLength(page.width)} ${cssLength(page.height)}`;
297
+ }
298
+ function printAreaMeta(page) {
299
+ const area = JSON.stringify({
300
+ width: page.width,
301
+ height: page.height,
302
+ bleed: page.bleed ?? 0,
303
+ safe: page.safe ?? 0,
304
+ artSafe: page.artSafe,
305
+ gutter: page.gutter,
306
+ preset: page.preset,
307
+ spec: page.spec
308
+ });
309
+ return `<meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>`;
310
+ }
311
+ function pageBoxes(area, side, scale = 1) {
312
+ const w = area.width * scale;
313
+ const h = area.height * scale;
314
+ const b = Math.max(0, area.bleed) * scale;
315
+ const s = Math.max(0, area.safe) * scale;
316
+ const a = Math.max(0, area.artSafe) * scale;
317
+ const g = Math.max(0, area.gutter) * scale;
318
+ const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
319
+ const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
320
+ const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
321
+ return {
322
+ sheet: { x: 0, y: 0, w, h },
323
+ trim,
324
+ safe: {
325
+ x: trim.x + textLeft,
326
+ y: trim.y + s,
327
+ w: Math.max(0, trim.w - textLeft - textRight),
328
+ h: Math.max(0, trim.h - 2 * s)
329
+ },
330
+ safeArt: {
331
+ x: trim.x + artLeft,
332
+ y: trim.y + a,
333
+ w: Math.max(0, trim.w - artLeft - artRight),
334
+ h: Math.max(0, trim.h - 2 * a)
335
+ },
336
+ bleed: b
337
+ };
338
+ }
339
+ var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
340
+ function judgeRect(r, boxes, rule, tol = 0) {
341
+ const { sheet, trim } = boxes;
342
+ const safe = rule === "art" ? boxes.safeArt : boxes.safe;
343
+ const sides = [
344
+ ["left", -r.x, -safe.x, -trim.x, -sheet.x],
345
+ ["right", r.x + r.w, safe.x + safe.w, trim.x + trim.w, sheet.x + sheet.w],
346
+ ["top", -r.y, -safe.y, -trim.y, -sheet.y],
347
+ ["bottom", r.y + r.h, safe.y + safe.h, trim.y + trim.h, sheet.y + sheet.h]
348
+ ];
349
+ let worst = { kind: "inside", gap: 0, need: 0, edge: null };
350
+ let beyond = 0;
351
+ for (const [edge, ink, limit, trimEdge, sheetEdge] of sides) {
352
+ const past = ink - limit;
353
+ if (past <= tol || rule === "art" && ink >= sheetEdge - tol)
354
+ continue;
355
+ const kind = ink > sheetEdge + tol ? "off-page" : ink > trimEdge + tol ? "bleed" : "unsafe";
356
+ if (SEVERITY[kind] > SEVERITY[worst.kind] || kind === worst.kind && past > beyond) {
357
+ worst = { kind, gap: trimEdge - ink, need: trimEdge - limit, edge };
358
+ beyond = past;
359
+ }
360
+ }
361
+ return worst;
362
+ }
363
+
284
364
  // src/checks/pdf/safe-margins.ts
285
365
  var TOLERANCE_PT = 0.5;
286
366
  var ABOVE_BASELINE = 0.8;
287
367
  var BELOW_BASELINE = 0.2;
288
368
  var inches = (pt) => `${+(pt / 72).toFixed(2)}in`;
369
+ var size = (r) => `${(r.w / 72).toFixed(1)} x ${(r.h / 72).toFixed(1)}in`;
370
+ var fromPdf = (x, y, w, h, sheetHeight) => ({
371
+ x,
372
+ y: sheetHeight - y - h,
373
+ w,
374
+ h
375
+ });
289
376
  var check3 = {
290
377
  id: "pdf.print.safe-margins",
291
378
  name: "Safe margins",
292
- description: "Fails when text sits closer to the trim than `page.safe` (DriveThruRPG: 0.5in on every edge), or than `page.gutter` at the binding edge where one is set. Text only: art is judged in the preview's safe-area overlay, not here. Skipped when `page.safe` is not set",
379
+ description: "Fails when text sits closer to the trim than `page.safe` (DriveThruRPG: 0.5in on every edge) or an image that does not bleed closer than `page.artSafe` (DriveThruRPG: 0.25in on the top, bottom and outside edges), both held to `page.gutter` at the binding edge where one is set; and when an image crosses the trim without reaching the sheet's edge (it ends in the bleed band). Text and images: filled backgrounds and borders are judged in the preview's safe-area overlay, not here. Skipped when `page.safe` is not set",
293
380
  category: "pdf",
294
381
  phase: "post-build",
295
382
  async run(ctx) {
296
- const { width, height, bleed = 0, safe = 0, gutter = safe } = ctx.config.page;
383
+ const { width, height, bleed = 0, safe = 0, artSafe = safe, gutter = safe } = ctx.config.page;
297
384
  if (!ctx.pdfPath || !safe)
298
385
  return [];
299
386
  const doc = await loadPdf(ctx.pdfPath);
300
387
  if (!doc) {
301
- return [inspectionFailed(check3.id, "Could not parse the PDF's text positions.", { file: ctx.pdfPath })];
388
+ return [inspectionFailed(check3.id, "Could not parse the PDF's text and image positions.", { file: ctx.pdfPath })];
302
389
  }
390
+ const area = { width, height, bleed, safe, artSafe, gutter };
303
391
  const rule = ctx.config.spec?.label ?? "page.safe";
392
+ const textRule = gutter === safe ? `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.` : `${rule}: text must stay at least ${inches(safe)} inside the trim, ${inches(gutter)} at the binding.`;
393
+ const artRule = gutter === artSafe ? `${rule}: art that does not bleed must stay at least ${inches(artSafe)} inside the trim on every edge.` : `${rule}: art that does not bleed must stay at least ${inches(artSafe)} inside the trim, ${inches(gutter)} at the binding.`;
304
394
  const results = [];
305
395
  try {
396
+ const { imagesByPage } = await getOpPass(doc);
306
397
  for (let pageNum = 1;pageNum <= doc.numPages; pageNum++) {
307
- const recto = pageNum % 2 === 1;
308
- const trim = {
309
- left: recto ? 0 : bleed,
310
- right: width - (recto ? bleed : 0),
311
- bottom: bleed,
312
- top: height - bleed
313
- };
398
+ const side = pageNum % 2 === 1 ? "recto" : "verso";
399
+ const boxes = pageBoxes(area, side);
400
+ const binding = side === "recto" ? "left" : "right";
401
+ const edgeName = (edge) => edge === "top" || edge === "bottom" ? edge : edge === binding ? "binding" : "outside";
314
402
  const { items } = await (await doc.getPage(pageNum)).getTextContent();
315
403
  for (const item of items) {
316
404
  if (!("str" in item) || !item.str.trim())
@@ -325,28 +413,55 @@ var check3 = {
325
413
  ys.push(f + s * along * b + u * d);
326
414
  }
327
415
  }
328
- const gaps = [
329
- [recto ? "binding" : "outside", Math.min(...xs) - trim.left],
330
- [recto ? "outside" : "binding", trim.right - Math.max(...xs)],
331
- ["bottom", Math.min(...ys) - trim.bottom],
332
- ["top", trim.top - Math.max(...ys)]
333
- ];
334
- const [edge, shortfall] = gaps.map(([name, gap]) => [name, (name === "binding" ? gutter : safe) - gap]).reduce((worst, g) => g[1] > worst[1] ? g : worst);
335
- if (shortfall <= TOLERANCE_PT)
416
+ const x0 = Math.min(...xs);
417
+ const y0 = Math.min(...ys);
418
+ const box = fromPdf(x0, y0, Math.max(...xs) - x0, Math.max(...ys) - y0, height);
419
+ const { kind, edge, gap, need } = judgeRect(box, boxes, "text", TOLERANCE_PT);
420
+ if (kind === "inside")
336
421
  continue;
422
+ const shortfall = need - gap;
337
423
  const text = item.str.trim();
338
424
  results.push(finding(check3.id, {
339
425
  severity: "error",
340
- message: `Page ${pageNum}: "${text.slice(0, 40)}${text.length > 40 ? "…" : ""}" is ${inches(shortfall)} too close to the ${edge} edge.`,
426
+ message: `Page ${pageNum}: "${text.slice(0, 40)}${text.length > 40 ? "…" : ""}" is ${inches(shortfall)} too close to the ${edgeName(edge)} edge.`,
341
427
  file: ctx.pdfPath,
342
- detail: gutter === safe ? `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.` : `${rule}: text must stay at least ${inches(safe)} inside the trim, ${inches(gutter)} at the binding.`,
428
+ detail: textRule,
343
429
  code: "text-outside-safe-margin",
344
- data: { page: pageNum, edge, shortfallPt: +shortfall.toFixed(2) }
430
+ data: { page: pageNum, edge: edgeName(edge), shortfallPt: +shortfall.toFixed(2) }
431
+ }));
432
+ }
433
+ for (const image of imagesByPage.get(pageNum) ?? []) {
434
+ const { x, y, w, h } = image.bounds;
435
+ const box = fromPdf(x, y, w, h, height);
436
+ const { kind, edge, gap, need } = judgeRect(box, boxes, "art", TOLERANCE_PT);
437
+ if (kind === "inside")
438
+ continue;
439
+ const name = edgeName(edge);
440
+ if (kind === "bleed") {
441
+ const short = boxes.bleed + gap;
442
+ results.push(finding(check3.id, {
443
+ severity: "error",
444
+ message: `Page ${pageNum}: a ${size(box)} image ends in the bleed band on the ${name} edge, ${inches(short)} short of the sheet's edge.`,
445
+ file: ctx.pdfPath,
446
+ detail: `${rule}: art either stays ${inches(artSafe)} inside the trim or bleeds, running past it to the sheet's edge; art that stops between the two can leave a white sliver when the page is cut.`,
447
+ code: "art-ends-in-bleed-band",
448
+ data: { page: pageNum, edge: name, shortfallPt: +short.toFixed(2) }
449
+ }));
450
+ continue;
451
+ }
452
+ const shortfall = need - gap;
453
+ results.push(finding(check3.id, {
454
+ severity: "error",
455
+ message: `Page ${pageNum}: a ${size(box)} image is ${inches(shortfall)} too close to the ${name} edge.`,
456
+ file: ctx.pdfPath,
457
+ detail: artRule,
458
+ code: "art-outside-safe-margin",
459
+ data: { page: pageNum, edge: name, shortfallPt: +shortfall.toFixed(2) }
345
460
  }));
346
461
  }
347
462
  }
348
463
  } catch {
349
- return [inspectionFailed(check3.id, "Could not read the PDF's text positions.", { file: ctx.pdfPath })];
464
+ return [inspectionFailed(check3.id, "Could not read the PDF's text and image positions.", { file: ctx.pdfPath })];
350
465
  }
351
466
  return results;
352
467
  }
@@ -759,16 +874,16 @@ var check9 = {
759
874
  if (!doc) {
760
875
  return [inspectionFailed(check9.id, "Could not parse the PDF's page count.", { file: ctx.pdfPath })];
761
876
  }
762
- const size = ctx.config.print.signature;
877
+ const size2 = ctx.config.print.signature;
763
878
  const pages = doc.numPages;
764
- if ((pages + 1) % size === 0)
879
+ if ((pages + 1) % size2 === 0)
765
880
  return [];
766
- const data = { pages, signature: size };
767
- if (pages % size !== 0) {
881
+ const data = { pages, signature: size2 };
882
+ if (pages % size2 !== 0) {
768
883
  return [
769
884
  finding(check9.id, {
770
885
  severity: "error",
771
- message: `Page count ${pages} does not fit a signature of ${size}: it must be a multiple of ${size}, or one short of one.`,
886
+ message: `Page count ${pages} does not fit a signature of ${size2}: it must be a multiple of ${size2}, or one short of one.`,
772
887
  file: ctx.pdfPath,
773
888
  detail: "Set `print.signature` in the manifest and rebuild; the build pads the interior for you.",
774
889
  code: "signature-page-count",
@@ -789,7 +904,7 @@ var check9 = {
789
904
  severity: "error",
790
905
  message: `The last page (${pages}) has content, but the printer needs it blank for its own information.`,
791
906
  file: ctx.pdfPath,
792
- detail: "The build adds that page for you (`print.lastPage: blank`, or `short-by-one` to stop a page short of a multiple " + `of ${size} and let the printer add it). Check that the interior was built with this manifest, and that nothing in it ` + "forces content onto the final page.",
907
+ detail: "The build adds that page for you (`print.lastPage: blank`, or `short-by-one` to stop a page short of a multiple " + `of ${size2} and let the printer add it). Check that the interior was built with this manifest, and that nothing in it ` + "forces content onto the final page.",
793
908
  code: "signature-last-page",
794
909
  data
795
910
  })
@@ -3884,22 +3999,18 @@ function canonicalChapterId(p) {
3884
3999
  return s;
3885
4000
  }
3886
4001
 
3887
- // src/engine/shared/print-area.ts
3888
- function printAreaMeta(page) {
3889
- const area = JSON.stringify({
3890
- width: page.width,
3891
- height: page.height,
3892
- bleed: page.bleed ?? 0,
3893
- safe: page.safe ?? 0,
3894
- artSafe: page.artSafe,
3895
- gutter: page.gutter,
3896
- preset: page.preset,
3897
- spec: page.spec
3898
- });
3899
- return `<meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>`;
3900
- }
3901
-
3902
4002
  // src/lib/markdown/assemble.ts
4003
+ function defaultPageSizeCss(css, page) {
4004
+ if (!page || !isPositivePoints(page.width) || !isPositivePoints(page.height))
4005
+ return null;
4006
+ try {
4007
+ if (resolvePage(extract(css)).decls.size !== undefined)
4008
+ return null;
4009
+ } catch {
4010
+ return null;
4011
+ }
4012
+ return `@page { size: ${formatPageSize(page)}; }`;
4013
+ }
3903
4014
  async function assembleBookHtml(opts) {
3904
4015
  const title = opts.title ?? "Document";
3905
4016
  const projectCss = opts.projectCss ?? "";
@@ -3937,6 +4048,8 @@ async function assembleBookHtml(opts) {
3937
4048
  imageRefs.add(ref);
3938
4049
  opts.onImageRefs([...imageRefs]);
3939
4050
  }
4051
+ const pageSize = defaultPageSizeCss(`${pluginCss}
4052
+ ${projectCss}`, opts.printArea);
3940
4053
  const inlineCss = [
3941
4054
  "@layer gp.marker, gp.vocab;",
3942
4055
  `/* gutterpress markers */
@@ -3948,6 +4061,8 @@ ${TWO_VERSIONS_CSS.trim()}
3948
4061
  @layer gp.vocab {
3949
4062
  ${GUTTERPRESS_CSS.trim()}
3950
4063
  }`,
4064
+ pageSize ? `/* page size (manifest) */
4065
+ ${pageSize}` : null,
3951
4066
  pluginCss ? `/* extension css */
3952
4067
  ${pluginCss.trim()}` : null,
3953
4068
  `/* column alignment */
@@ -5092,7 +5207,7 @@ class PdfOutput {
5092
5207
  const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
5093
5208
  await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
5094
5209
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
5095
- const { buildNativePdf } = await import("./engine-h4ehmrvf.js");
5210
+ const { buildNativePdf } = await import("./engine-5p8tn9b4.js");
5096
5211
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
5097
5212
  title: config.title,
5098
5213
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -5129,7 +5244,7 @@ class PdfOutput {
5129
5244
  }
5130
5245
  const flattenPages = config.pdfx.flattenPages;
5131
5246
  if (transparent.length > 0 && config.pdfx.transparency === "preserve-text") {
5132
- const { unsplittableTextReasons } = await import("./pdf-inspect-zdmy7qca.js");
5247
+ const { unsplittableTextReasons } = await import("./pdf-inspect-wnw4za71.js");
5133
5248
  const reasons = await unsplittableTextReasons(rawBytes, transparent).catch((err) => {
5134
5249
  log.warn(`pdfx.transparency: preserve-text could not inspect this PDF's text (${err instanceof Error ? err.message : String(err)}), so every page that uses transparency is flattened whole, as with flatten-page.`);
5135
5250
  return null;
@@ -6388,9 +6503,6 @@ import path12 from "node:path";
6388
6503
  // src/lib/page-size.ts
6389
6504
  import postcss5 from "postcss";
6390
6505
  import { isMap, Scalar } from "yaml";
6391
- function isPositivePoints(value) {
6392
- return typeof value === "number" && Number.isFinite(value) && value > 0;
6393
- }
6394
6506
  function writePageBounds(doc, page) {
6395
6507
  const tolerance = page.tolerance ?? numberAt(doc, ["page", "tolerance"]);
6396
6508
  if (!isMap(doc.get("page", true)))
@@ -8906,7 +9018,7 @@ async function resolvePublishProvider(id, book) {
8906
9018
  const ext = await bookExtensions(book);
8907
9019
  const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
8908
9020
  if (npmEntry) {
8909
- const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-q1f7njec.js");
9021
+ const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-3r7phztr.js");
8910
9022
  await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
8911
9023
  }
8912
9024
  const publishers = await discover(ext);
@@ -8944,7 +9056,7 @@ function infoFor(p) {
8944
9056
  }
8945
9057
  function pluginProvider(p) {
8946
9058
  let loading;
8947
- const load = () => loading ??= import("./plugins-gy4q9vaa.js").then((m) => m.loadPublishFunction(p));
9059
+ const load = () => loading ??= import("./plugins-nfxr831f.js").then((m) => m.loadPublishFunction(p));
8948
9060
  return {
8949
9061
  info: infoFor(p),
8950
9062
  async authenticate() {
@@ -9236,10 +9348,10 @@ function isSmallBody(body) {
9236
9348
  return false;
9237
9349
  let total = 0;
9238
9350
  for (const c of body) {
9239
- const size = c?.byteLength;
9240
- if (typeof size !== "number")
9351
+ const size2 = c?.byteLength;
9352
+ if (typeof size2 !== "number")
9241
9353
  return false;
9242
- total += size;
9354
+ total += size2;
9243
9355
  }
9244
9356
  return total <= SMALL_BODY_MAX_BYTES;
9245
9357
  }
@@ -3,7 +3,7 @@ import {
3
3
  } from "./cli-eyyggect.js";
4
4
  import {
5
5
  getAssetPath
6
- } from "./cli-gmm3bdeq.js";
6
+ } from "./cli-hndkg7bn.js";
7
7
 
8
8
  // src/engine/compiler/build.ts
9
9
  import { readFile } from "node:fs/promises";
@@ -2503,4 +2503,4 @@ ${lines.length ? lines.join(`
2503
2503
  `);
2504
2504
  }
2505
2505
 
2506
- export { lastPageMode, MARGIN_BOX_IGNORED_PROPERTIES, build };
2506
+ export { lastPageMode, extract, resolvePage, MARGIN_BOX_IGNORED_PROPERTIES, build };
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
9
9
  var favicon_default = "./favicon-272ga6y0.ico";
10
10
 
11
11
  // src/assets/manifest.schema.json
12
- var manifest_schema_default = "./manifest.schema-n292sb77.json";
12
+ var manifest_schema_default = "./manifest.schema-nvawq0xv.json";
13
13
 
14
14
  // src/assets/preview/scripts/preview-interface.js
15
15
  var preview_interface_default = "./preview-interface-nnmrxyqm.js";
@@ -24,7 +24,7 @@ var preview_shell_default = "./preview-shell-wk9k3gcd.js";
24
24
  var CGATS21_CRPC1_default = "./CGATS21_CRPC1-g0e3k7kr.icc";
25
25
 
26
26
  // src/assets/engine/gutterpress-viewer.js
27
- var gutterpress_viewer_default = "./gutterpress-viewer-w7njnkj7.js";
27
+ var gutterpress_viewer_default = "./gutterpress-viewer-w9jzx6sb.js";
28
28
 
29
29
  // src/assets/engine/gutterpress-agent.js
30
30
  var gutterpress_agent_default = "./gutterpress-agent-d29ry3ak.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PACKAGE_VERSION,
3
3
  debug
4
- } from "./cli-5553w972.js";
4
+ } from "./cli-80t93x2r.js";
5
5
  import {
6
6
  isExactNpmVersion,
7
7
  isValidNpmPackageName,