gutterpress 0.11.23-beta.1 → 0.11.23-beta.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/README.md +1 -1
  2. package/dist/api/index.js +5 -5
  3. package/dist/{build-dg8a566j.js → build-3e18wnqj.js} +8 -8
  4. package/dist/{cli-vhav8g82.js → cli-0ep11b0q.js} +1 -1
  5. package/dist/{cli-e31jshz6.js → cli-88arpdke.js} +2 -2
  6. package/dist/{cli-1fy1pm9d.js → cli-9n9kg58f.js} +1 -1
  7. package/dist/{cli-k087gj0p.js → cli-pd2cje5b.js} +122 -11
  8. package/dist/{cli-989eg8jz.js → cli-pnrararp.js} +11 -10
  9. package/dist/{cli-ttj95aex.js → cli-tgnyvra3.js} +13 -3
  10. package/dist/{cli-31anr0r9.js → cli-v0evdpdf.js} +68 -28
  11. package/dist/{cli-s1zt779w.js → cli-x7gcdg6w.js} +1 -1
  12. package/dist/cli.js +12 -12
  13. package/dist/{doctor-g4a42tcz.js → doctor-2f4s9dyn.js} +3 -3
  14. package/dist/engine/compiler/postprocess.d.ts +3 -0
  15. package/dist/engine/shared/pdf-inspect.d.ts +33 -0
  16. package/dist/{engine-jvkcahr1.js → engine-c4pxz79g.js} +1 -1
  17. package/dist/{engine-wgkaqjsc.js → engine-cs5g2ez0.js} +2 -2
  18. package/dist/{ext-w9d5ftcg.js → ext-70gv86v5.js} +8 -8
  19. package/dist/{extension-restore-5x4mj79d.js → extension-restore-chj5nx3t.js} +2 -2
  20. package/dist/{extension-restore-as0g38p1.js → extension-restore-pd87p8r2.js} +4 -4
  21. package/dist/{gutterpress-agent-d29ry3ak.js → gutterpress-agent-wyryqehx.js} +13 -4
  22. package/dist/{gutterpress-viewer-ze691rrd.js → gutterpress-viewer-ywkf49q5.js} +38 -153
  23. package/dist/{index-c6y0p7jy.js → index-0k43evt0.js} +66 -22
  24. package/dist/{index-fk2kpf2t.js → index-2t1gdma7.js} +121 -10
  25. package/dist/{index-cw03e9p4.js → index-7j3hkwya.js} +68 -28
  26. package/dist/{index-a4j3xcfp.js → index-80vg3ed6.js} +13 -3
  27. package/dist/{index-798jt0qc.js → index-swfdgn2n.js} +2 -2
  28. package/dist/{index-rj5vrhz6.js → index-ytjkvwy5.js} +1 -1
  29. package/dist/index.js +6 -6
  30. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  31. package/dist/lib/source-provider.d.ts +27 -0
  32. package/dist/lib/version-changes.d.ts +14 -4
  33. package/dist/{new-jtk028rf.js → new-d61n5cn0.js} +8 -8
  34. package/dist/{plugins-bcbe7xs8.js → plugins-0hhw6s17.js} +1 -1
  35. package/dist/{plugins-zsm6wv79.js → plugins-e0n46s6m.js} +3 -3
  36. package/dist/{preflight-cs6mj1d6.js → preflight-fjayhjr5.js} +8 -8
  37. package/dist/{preview-d1j1g4qf.js → preview-h815jrfb.js} +8 -8
  38. package/dist/{publish-7yw4jyrt.js → publish-ft39zgma.js} +8 -8
  39. package/dist/render.js +12 -2
  40. package/dist/{source-provider-n7vehh6t.js → source-provider-j1w7byeq.js} +5 -1
  41. package/dist/{source-provider-tgzet3j5.js → source-provider-tjaf303m.js} +5 -1
  42. package/dist/test-helpers/vocabulary.d.ts +12 -0
  43. package/dist/{validate-rkcdnzhw.js → validate-b201dbrb.js} +8 -8
  44. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "./cli-d8xjns66.js";
9
9
  import {
10
10
  package_default
11
- } from "./cli-s1zt779w.js";
11
+ } from "./cli-x7gcdg6w.js";
12
12
  import {
13
13
  EXIT_CODES
14
14
  } from "./cli-46ycxe6r.js";
@@ -20,14 +20,14 @@ import {
20
20
  import { defineCommand, parseArgs, runMain } from "citty";
21
21
  import { statSync } from "node:fs";
22
22
  var SUBCOMMANDS = {
23
- new: () => import("./new-jtk028rf.js").then((m) => m.default),
24
- preview: () => import("./preview-d1j1g4qf.js").then((m) => m.default),
25
- build: () => import("./build-dg8a566j.js").then((m) => m.default),
26
- publish: () => import("./publish-7yw4jyrt.js").then((m) => m.default),
27
- validate: () => import("./validate-rkcdnzhw.js").then((m) => m.default),
28
- preflight: () => import("./preflight-cs6mj1d6.js").then((m) => m.default),
29
- doctor: () => import("./doctor-g4a42tcz.js").then((m) => m.default),
30
- ext: () => import("./ext-w9d5ftcg.js").then((m) => m.default)
23
+ new: () => import("./new-d61n5cn0.js").then((m) => m.default),
24
+ preview: () => import("./preview-h815jrfb.js").then((m) => m.default),
25
+ build: () => import("./build-3e18wnqj.js").then((m) => m.default),
26
+ publish: () => import("./publish-ft39zgma.js").then((m) => m.default),
27
+ validate: () => import("./validate-b201dbrb.js").then((m) => m.default),
28
+ preflight: () => import("./preflight-fjayhjr5.js").then((m) => m.default),
29
+ doctor: () => import("./doctor-2f4s9dyn.js").then((m) => m.default),
30
+ ext: () => import("./ext-70gv86v5.js").then((m) => m.default)
31
31
  };
32
32
  var VERSION = package_default.version;
33
33
  var main = defineCommand({
@@ -52,7 +52,7 @@ async function preflightRequiredInvocations(rawArgs) {
52
52
  const [command, ...commandArgs] = rawArgs;
53
53
  try {
54
54
  if (command === "new") {
55
- const { newArgs } = await import("./new-jtk028rf.js");
55
+ const { newArgs } = await import("./new-d61n5cn0.js");
56
56
  rejectUnknownFlags(commandArgs, newArgs, "new");
57
57
  const parsed = parseArgs(commandArgs, {
58
58
  ...newArgs,
@@ -64,7 +64,7 @@ async function preflightRequiredInvocations(rawArgs) {
64
64
  return;
65
65
  }
66
66
  if (command === "preflight") {
67
- const { preflightArgs } = await import("./preflight-cs6mj1d6.js");
67
+ const { preflightArgs } = await import("./preflight-fjayhjr5.js");
68
68
  rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
69
69
  const parsed = parseArgs(commandArgs, {
70
70
  ...preflightArgs,
@@ -84,7 +84,7 @@ async function preflightRequiredInvocations(rawArgs) {
84
84
  }
85
85
  if (subcommand.startsWith("-"))
86
86
  return;
87
- const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-w9d5ftcg.js");
87
+ const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-70gv86v5.js");
88
88
  if (!EXT_SUBCOMMANDS.includes(subcommand)) {
89
89
  throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
90
90
  }
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  getSystemDiagnostics
3
- } from "./cli-vhav8g82.js";
3
+ } from "./cli-0ep11b0q.js";
4
4
  import"./cli-5crpmz42.js";
5
5
  import"./cli-asap4t1z.js";
6
6
  import {
7
7
  log
8
- } from "./cli-1fy1pm9d.js";
8
+ } from "./cli-9n9kg58f.js";
9
9
  import {
10
10
  UsageError,
11
11
  rejectExtraPositionals,
12
12
  rejectUnknownFlags
13
13
  } from "./cli-d8xjns66.js";
14
- import"./cli-s1zt779w.js";
14
+ import"./cli-x7gcdg6w.js";
15
15
  import"./cli-46ycxe6r.js";
16
16
  import"./cli-37x76zdn.js";
17
17
 
@@ -27,5 +27,8 @@ export interface PostprocessResult {
27
27
  bleed: number[];
28
28
  trim: number[];
29
29
  };
30
+ /** The scale Chromium printed at, read before anything here draws:
31
+ * `printScale` in shared/pdf-inspect.ts (1 = not shrunk). */
32
+ scale: number | null;
30
33
  }
31
34
  export declare function postprocess(input: Uint8Array, opts: PostprocessOptions): Promise<PostprocessResult>;
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Read-side PDF utilities (pdf-lib, pure JS).
3
+ *
4
+ * Three jobs:
5
+ * 1. Verification: page count, page boxes, embedded fonts — the evidence the
6
+ * tests assert on.
7
+ * 2. Tier 3 measurement channel (§8): harvest Chromium's own link annotations
8
+ * and document outline to learn "which page is element X on", with no text
9
+ * heuristics.
10
+ * 3. The print scale: whether Chromium's shrink-to-fit scaled the page area
11
+ * ({@link printScale}).
12
+ */
13
+ import { PDFDocument } from "pdf-lib";
1
14
  export interface PageBoxes {
2
15
  media: number[];
3
16
  crop?: number[];
@@ -18,3 +31,23 @@ export interface PdfFacts {
18
31
  }>;
19
32
  }
20
33
  export declare function inspectPdf(bytes: Uint8Array): Promise<PdfFacts>;
34
+ /**
35
+ * The scale Chromium printed the page area at: 1 when it laid the document
36
+ * out at the page content box's own width, below 1 when print shrink-to-fit
37
+ * scaled the whole document down because something reached past that box
38
+ * (ENGINE.md §9). `null` when no page shows the transform — a reader that
39
+ * cannot find it makes no claim. Pass Chromium's own print, before anything
40
+ * redraws its pages.
41
+ *
42
+ * Read off the content streams rather than predicted from element widths.
43
+ * Each page draws its page box (background, margin boxes) under an unscaled
44
+ * `3.125 0 0 3.125 0 0 cm` — CSS px to 300-per-inch device units — then clips
45
+ * to the page area, its largest clip, and draws the page area's content
46
+ * under `s 0 0 s x y cm`, where x is the clip's own left edge and s is 3.125
47
+ * times the shrink. An element with a CSS transform or its own clip draws
48
+ * under other matrices, so one page can read wrong; Chromium applies one
49
+ * factor to the whole document, so the first reading three pages agree on
50
+ * (or the most common, in a shorter document) is the answer. The prologue
51
+ * fixes the device resolution: the scale is s × prologue × 96/72.
52
+ */
53
+ export declare function printScale(doc: PDFDocument): number | null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  build
3
- } from "./cli-k087gj0p.js";
3
+ } from "./cli-pd2cje5b.js";
4
4
  import"./cli-eyyggect.js";
5
5
  import"./cli-asap4t1z.js";
6
6
  import"./cli-65gy8ask.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  build
3
- } from "./index-fk2kpf2t.js";
4
- import"./index-798jt0qc.js";
3
+ } from "./index-2t1gdma7.js";
4
+ import"./index-swfdgn2n.js";
5
5
  import {
6
6
  BuildError
7
7
  } from "./index-tayzfr9c.js";
@@ -2,14 +2,14 @@ import {
2
2
  importExtensionFromFile,
3
3
  importExtensionFromUrl,
4
4
  searchNpmExtensions
5
- } from "./cli-989eg8jz.js";
6
- import"./cli-vhav8g82.js";
5
+ } from "./cli-pnrararp.js";
6
+ import"./cli-0ep11b0q.js";
7
7
  import"./cli-ht1bcqdd.js";
8
- import"./cli-k087gj0p.js";
8
+ import"./cli-pd2cje5b.js";
9
9
  import"./cli-5crpmz42.js";
10
10
  import"./cli-eyyggect.js";
11
11
  import"./cli-asap4t1z.js";
12
- import"./cli-31anr0r9.js";
12
+ import"./cli-v0evdpdf.js";
13
13
  import"./cli-v5mp7a6q.js";
14
14
  import {
15
15
  addExtension,
@@ -18,12 +18,12 @@ import {
18
18
  removeExtension,
19
19
  setExtensionEnabled,
20
20
  updateExtensions
21
- } from "./cli-e31jshz6.js";
21
+ } from "./cli-88arpdke.js";
22
22
  import {
23
23
  isPathSpecifier,
24
24
  parseExtensionSpecifier
25
- } from "./cli-ttj95aex.js";
26
- import"./cli-1fy1pm9d.js";
25
+ } from "./cli-tgnyvra3.js";
26
+ import"./cli-9n9kg58f.js";
27
27
  import {
28
28
  UsageError,
29
29
  exitForUsage,
@@ -31,7 +31,7 @@ import {
31
31
  rejectUnknownFlags,
32
32
  resolveProjectDir
33
33
  } from "./cli-d8xjns66.js";
34
- import"./cli-s1zt779w.js";
34
+ import"./cli-x7gcdg6w.js";
35
35
  import"./cli-65gy8ask.js";
36
36
  import {
37
37
  EXIT_CODES
@@ -3,8 +3,8 @@ import {
3
3
  restoreForCommand,
4
4
  restoreOfflineMessage,
5
5
  restorePinnedExtensions
6
- } from "./index-rj5vrhz6.js";
7
- import"./index-a4j3xcfp.js";
6
+ } from "./index-ytjkvwy5.js";
7
+ import"./index-80vg3ed6.js";
8
8
  import"./index-tayzfr9c.js";
9
9
  import"./index-5x42amh8.js";
10
10
  import"./index-37x76zdn.js";
@@ -3,11 +3,11 @@ import {
3
3
  restoreForCommand,
4
4
  restoreOfflineMessage,
5
5
  restorePinnedExtensions
6
- } from "./cli-e31jshz6.js";
7
- import"./cli-ttj95aex.js";
8
- import"./cli-1fy1pm9d.js";
6
+ } from "./cli-88arpdke.js";
7
+ import"./cli-tgnyvra3.js";
8
+ import"./cli-9n9kg58f.js";
9
9
  import"./cli-d8xjns66.js";
10
- import"./cli-s1zt779w.js";
10
+ import"./cli-x7gcdg6w.js";
11
11
  import"./cli-65gy8ask.js";
12
12
  import"./cli-46ycxe6r.js";
13
13
  import"./cli-y28j019d.js";
@@ -59,6 +59,7 @@
59
59
  });
60
60
 
61
61
  // src/engine/shared/synthesis.ts
62
+ var RECTO_VERSO_VALUES = /^(right|recto|left|verso)$/;
62
63
  var WHICH_VALUES = new Set(["first", "start", "last", "first-except"]);
63
64
  var LEADER_RE = /\uE000([^\uE001]*)\uE001/;
64
65
  function leaderFillCount(gapPx, gluePx) {
@@ -422,7 +423,8 @@
422
423
  return out;
423
424
  }
424
425
  function forcedBreakSites(decls) {
425
- const out = [];
426
+ const found = [];
427
+ const seen = new Set;
426
428
  for (const d of decls) {
427
429
  let els = [];
428
430
  try {
@@ -430,10 +432,17 @@
430
432
  } catch {
431
433
  continue;
432
434
  }
433
- for (const el of els)
434
- out.push({ id: ensureAnchor(el), prop: d.prop, value: d.value, selector: d.selector });
435
+ for (const el of els) {
436
+ const id = ensureAnchor(el);
437
+ const value = getComputedStyle(el).breakBefore;
438
+ if (seen.has(el) || !RECTO_VERSO_VALUES.test(value))
439
+ continue;
440
+ seen.add(el);
441
+ found.push({ el, id, prop: d.prop, value, selector: d.selector });
442
+ }
435
443
  }
436
- return out;
444
+ found.sort((a, b) => a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
445
+ return found.map(({ el: _el, ...site }) => site);
437
446
  }
438
447
  function counterResetSites(resets) {
439
448
  const out = [];
@@ -40,7 +40,6 @@
40
40
  fragmentDocument: () => fragmentDocument,
41
41
  cssZoomOf: () => cssZoomOf,
42
42
  contentEdgeRect: () => contentEdgeRect,
43
- compensateRepeatedHeaders: () => compensateRepeatedHeaders,
44
43
  compensateRectoBreaks: () => compensateRectoBreaks,
45
44
  columnReserve: () => columnReserve,
46
45
  collectCssText: () => collectCssText,
@@ -193,6 +192,29 @@
193
192
  no extractable body text; \`rabbit-walking.png\` (1379x2530, unique) sits on
194
193
  PDF p134 vs viewer folio 135. */
195
194
 
195
+ /* Repeated table headers and footers. Print draws a table's header group
196
+ (\`thead\`) at the top of every page the table spans, and its footer group
197
+ (\`tfoot\`) at the foot of each, and never leaves a header alone at the foot
198
+ of a page. Chromium does the same in a multicol column, through the same
199
+ table fragmentation, when the group is unbreakable; print media treats
200
+ every header and footer group so, the screen only one that is
201
+ \`break-inside: avoid\` (measured: on screen, a plain \`thead\` printed once and
202
+ a \`break-inside: avoid\` one on every column, exactly as print media does).
203
+ Setting it here makes the columns repeat the groups print repeats, where
204
+ print repeats them, and break around them as print does. A group the author
205
+ set to another \`display\` is not a header group and repeats in neither.
206
+
207
+ The viewer used to imitate this in script, cloning the header into each
208
+ continuation column and forcing a break before a table whose header would
209
+ be left alone. It moved a lead-in paragraph whole where print splits it,
210
+ left a clone at a column's foot, drew a second header wherever Chromium
211
+ already drew one, and shifted pages after the forced-break spacers had been
212
+ placed, losing a chapter's break (the gp-dimm-city design guide's reference
213
+ tables: #454). docs/fixtures/repeated-table-header pins it. */
214
+ .gp-strip :is(thead, tfoot) {
215
+ break-inside: avoid;
216
+ }
217
+
196
218
  /* View mode (\`applySpreadMode\` in fragment.ts): wraps this run's multicol
197
219
  columns into \`--gp-wrap-cols\` ROWS instead of one long row — 2 for
198
220
  two-up/spread, 1 for single (a plain vertical stack of pages), using
@@ -1482,10 +1504,13 @@
1482
1504
  function clearLeadingForcedBreaks(strip) {
1483
1505
  for (let el = strip.firstElementChild;el; el = el.firstElementChild) {
1484
1506
  const cs = getComputedStyle(el);
1485
- if (FORCED_BREAK.test(cs.breakBefore))
1486
- el.style.breakBefore = "auto";
1507
+ if (!FORCED_BREAK.test(cs.breakBefore))
1508
+ continue;
1509
+ clearedLeadingBreaks.set(el, cs.breakBefore);
1510
+ el.style.breakBefore = "auto";
1487
1511
  }
1488
1512
  }
1513
+ var clearedLeadingBreaks = new WeakMap;
1489
1514
  function stabilizeFullHeightPageRoots(model, strips) {
1490
1515
  let stabilized = 0;
1491
1516
  for (const strip of strips) {
@@ -1688,148 +1713,6 @@
1688
1713
  clearLeadingForcedBreaks(s.el);
1689
1714
  return strips;
1690
1715
  }
1691
- var AVOID_BREAK = /^avoid(-page|-column)?$/;
1692
- function keepChainHead(el) {
1693
- let head = el;
1694
- for (let prev = head.previousElementSibling;prev instanceof HTMLElement; prev = head.previousElementSibling) {
1695
- const linked = AVOID_BREAK.test(getComputedStyle(prev).breakAfter) || AVOID_BREAK.test(getComputedStyle(head).breakBefore);
1696
- if (!linked)
1697
- break;
1698
- head = prev;
1699
- }
1700
- return head;
1701
- }
1702
- var pushTargets = new WeakMap;
1703
- function repeatingHead(table) {
1704
- const head = table.tHead;
1705
- return head && getComputedStyle(head).display === "table-header-group" ? head : null;
1706
- }
1707
- function repeatingFoot(table) {
1708
- const foot = table.tFoot;
1709
- return foot && getComputedStyle(foot).display === "table-footer-group" ? foot : null;
1710
- }
1711
- function compensateRepeatedHeaders(strips, maxPasses = 24) {
1712
- const warnings = [];
1713
- let passes = 0;
1714
- let touched = 0;
1715
- for (const strip of strips) {
1716
- const tables = Array.from(strip.el.querySelectorAll("table")).filter((t) => repeatingHead(t) || repeatingFoot(t));
1717
- if (!tables.length)
1718
- continue;
1719
- for (const table of tables) {
1720
- for (const shim of Array.from(table.querySelectorAll("tr.gp-thead-shim, tr.gp-tfoot-shim")))
1721
- shim.remove();
1722
- table.style.breakBefore = "";
1723
- const target = pushTargets.get(table);
1724
- if (target)
1725
- target.style.breakBefore = "";
1726
- pushTargets.delete(table);
1727
- }
1728
- const pushed = new Set;
1729
- const footClaims = new Map;
1730
- let previous = "";
1731
- for (let pass = 0;pass < maxPasses; pass++) {
1732
- passes = Math.max(passes, pass + 1);
1733
- const { stride, height: colBottom } = stripMetrics(strip.el);
1734
- const stripLeft = strip.el.getBoundingClientRect().left - strip.el.scrollLeft;
1735
- const zoom = cssZoomOf(strip.el);
1736
- const colOf = (r) => Math.floor(((r.left - stripLeft) / zoom + 1) / stride);
1737
- const stripTop = strip.el.getBoundingClientRect().top;
1738
- const plans = [];
1739
- for (const table of tables) {
1740
- const head = repeatingHead(table);
1741
- const headRect = head?.getClientRects()[0];
1742
- const footHeight = (repeatingFoot(table)?.getBoundingClientRect().height ?? 0) / zoom;
1743
- const rows = Array.from(table.querySelectorAll("tbody > tr")).filter((r) => !r.classList.contains("gp-thead-shim") && !r.classList.contains("gp-tfoot-shim"));
1744
- if (!rows.length)
1745
- continue;
1746
- const rects = rows.map((r) => r.getClientRects()[0] ?? r.getBoundingClientRect());
1747
- const cols = rects.map(colOf);
1748
- const claims = footClaims.get(table) ?? new Map;
1749
- footClaims.set(table, claims);
1750
- let newClaim;
1751
- if (footHeight > 0) {
1752
- const lastCol = cols[cols.length - 1];
1753
- for (let i = 0;i < rows.length; i++) {
1754
- const row = rows[i];
1755
- if (cols[i] === lastCol || claims.has(row))
1756
- continue;
1757
- const bottom = (rects[i].bottom - stripTop) / zoom;
1758
- if (bottom > colBottom - footHeight + 0.5) {
1759
- newClaim = row;
1760
- claims.set(row, colBottom - (rects[i].top - stripTop) / zoom);
1761
- break;
1762
- }
1763
- }
1764
- }
1765
- plans.push({
1766
- table,
1767
- push: headRect ? colOf(headRect) < cols[0] : false,
1768
- headHeight: (headRect?.height ?? 0) / zoom,
1769
- footHeight,
1770
- breakRows: headRect ? rows.filter((_, i) => i > 0 && cols[i] > cols[i - 1]) : [],
1771
- footRows: [...claims.entries()],
1772
- grew: newClaim !== undefined,
1773
- cells: Math.max(1, ...rows.map((r) => r.cells.length))
1774
- });
1775
- }
1776
- const signature = plans.map((p) => `${p.push || pushed.has(p.table) ? "P" : ""}${p.breakRows.map((r) => r.rowIndex).join(".")}~${p.footRows.map(([r]) => r.rowIndex).join(".")}`).join("|");
1777
- const anyGrowth = plans.some((p) => p.grew);
1778
- if (!anyGrowth && signature === previous)
1779
- break;
1780
- previous = signature;
1781
- for (const plan of plans) {
1782
- for (const shim of Array.from(plan.table.querySelectorAll("tr.gp-thead-shim, tr.gp-tfoot-shim")))
1783
- shim.remove();
1784
- if (plan.push && !pushed.has(plan.table)) {
1785
- pushed.add(plan.table);
1786
- const target = keepChainHead(plan.table);
1787
- target.style.breakBefore = "column";
1788
- pushTargets.set(plan.table, target);
1789
- touched++;
1790
- continue;
1791
- }
1792
- for (const row of plan.breakRows) {
1793
- row.before(headerShim(plan.table.tHead, plan.headHeight, plan.cells));
1794
- touched++;
1795
- }
1796
- for (const [row, height] of plan.footRows) {
1797
- row.before(sectionShim(plan.table.tFoot, height, plan.cells, "gp-tfoot-shim"));
1798
- touched++;
1799
- }
1800
- }
1801
- }
1802
- }
1803
- return { tables: touched, passes, warnings };
1804
- }
1805
- function headerShim(head, height, cells) {
1806
- return sectionShim(head, height, cells, "gp-thead-shim");
1807
- }
1808
- function sectionShim(section, height, cells, className) {
1809
- const shim = document.createElement("tr");
1810
- shim.className = className;
1811
- shim.setAttribute("aria-hidden", "true");
1812
- shim.style.height = `${height}px`;
1813
- if (className === "gp-tfoot-shim")
1814
- shim.style.verticalAlign = "bottom";
1815
- const source = section.rows[0];
1816
- if (source) {
1817
- for (const cell of Array.from(source.cells)) {
1818
- const td = document.createElement("td");
1819
- td.colSpan = cell.colSpan;
1820
- td.innerHTML = cell.innerHTML;
1821
- const cs = getComputedStyle(cell);
1822
- td.style.cssText = `font:${cs.font};color:${cs.color};letter-spacing:${cs.letterSpacing};text-transform:${cs.textTransform};vertical-align:${cs.verticalAlign};text-align:${cs.textAlign};padding:${cs.padding};border:${cs.border};background:${cs.backgroundColor};`;
1823
- shim.appendChild(td);
1824
- }
1825
- } else {
1826
- const td = document.createElement("td");
1827
- td.colSpan = cells;
1828
- td.style.cssText = `height:${height}px;padding:0;border:0;`;
1829
- shim.appendChild(td);
1830
- }
1831
- return shim;
1832
- }
1833
1716
  function compensateRectoBreaks(model, strips) {
1834
1717
  const decls2 = model.breaks.filter(isRectoVersoBreak);
1835
1718
  for (const spacer of Array.from(document.querySelectorAll(".gp-recto-spacer")))
@@ -1837,6 +1720,7 @@
1837
1720
  if (!decls2.length)
1838
1721
  return 0;
1839
1722
  const sites = [];
1723
+ const seen = new Set;
1840
1724
  for (const d of decls2) {
1841
1725
  let els = [];
1842
1726
  try {
@@ -1844,8 +1728,13 @@
1844
1728
  } catch {
1845
1729
  continue;
1846
1730
  }
1847
- for (const el of els)
1848
- sites.push({ el, wantsRecto: wantsRecto(d.value) });
1731
+ for (const el of els) {
1732
+ const value = clearedLeadingBreaks.get(el) ?? getComputedStyle(el).breakBefore;
1733
+ if (seen.has(el) || !RECTO_VERSO_VALUES.test(value))
1734
+ continue;
1735
+ seen.add(el);
1736
+ sites.push({ el, wantsRecto: wantsRecto(value) });
1737
+ }
1849
1738
  }
1850
1739
  sites.sort((a, b) => a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
1851
1740
  const plan = planRectoBlanks(sites.map((site) => ({ page: pageOf(site.el, strips) + 1, wantsRecto: site.wantsRecto })));
@@ -2043,7 +1932,6 @@
2043
1932
  const blanks = compensateRectoBreaks(model, strips);
2044
1933
  if (blanks)
2045
1934
  measure(strips);
2046
- const headers = opts.compensateHeaders === false ? { tables: 0, passes: 0, warnings: [] } : compensateRepeatedHeaders(strips);
2047
1935
  const contentPages = measure(strips).totalPages;
2048
1936
  const totalPages = padToSignature(strips, contentPages);
2049
1937
  fitVersoPinRoots(model, strips);
@@ -2051,7 +1939,7 @@
2051
1939
  model,
2052
1940
  strips,
2053
1941
  totalPages,
2054
- warnings: [...new Set([...authoring, ...headers.warnings])],
1942
+ warnings: [...new Set(authoring)],
2055
1943
  blankPages: blanks,
2056
1944
  blankPageIndices: blankPageIndices(strips),
2057
1945
  paddedPageIndices: pagesAfter(contentPages, totalPages),
@@ -2072,10 +1960,7 @@
2072
1960
  synthesizeColumnBreaks(model);
2073
1961
  measure(strips);
2074
1962
  api.blankPages = compensateRectoBreaks(model, strips);
2075
- if (opts.compensateHeaders !== false)
2076
- api.warnings = [
2077
- ...new Set([...authoring, ...compensateRepeatedHeaders(strips).warnings])
2078
- ];
1963
+ api.warnings = [...new Set(authoring)];
2079
1964
  const content = measure(strips).totalPages;
2080
1965
  padToSignature(strips, content);
2081
1966
  const r = measure(strips);