postext 0.3.38 → 0.3.40

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 (135) hide show
  1. package/dist/__bench__/chapter.bench.test.d.ts +2 -0
  2. package/dist/__bench__/chapter.bench.test.d.ts.map +1 -0
  3. package/dist/__bench__/chapter.bench.test.js +80 -0
  4. package/dist/__bench__/chapter.bench.test.js.map +1 -0
  5. package/dist/__tests__/buildAsync.test.d.ts +2 -0
  6. package/dist/__tests__/buildAsync.test.d.ts.map +1 -0
  7. package/dist/__tests__/buildAsync.test.js +50 -0
  8. package/dist/__tests__/buildAsync.test.js.map +1 -0
  9. package/dist/__tests__/exports.test.js +5 -0
  10. package/dist/__tests__/exports.test.js.map +1 -1
  11. package/dist/__tests__/hyphenateMemo.test.d.ts +2 -0
  12. package/dist/__tests__/hyphenateMemo.test.d.ts.map +1 -0
  13. package/dist/__tests__/hyphenateMemo.test.js +21 -0
  14. package/dist/__tests__/hyphenateMemo.test.js.map +1 -0
  15. package/dist/__tests__/knuthPlassMerge.test.d.ts +2 -0
  16. package/dist/__tests__/knuthPlassMerge.test.d.ts.map +1 -0
  17. package/dist/__tests__/knuthPlassMerge.test.js +55 -0
  18. package/dist/__tests__/knuthPlassMerge.test.js.map +1 -0
  19. package/dist/__tests__/memos.test.d.ts +2 -0
  20. package/dist/__tests__/memos.test.d.ts.map +1 -0
  21. package/dist/__tests__/memos.test.js +37 -0
  22. package/dist/__tests__/memos.test.js.map +1 -0
  23. package/dist/__tests__/pipeline/rotatedResource.test.d.ts +2 -0
  24. package/dist/__tests__/pipeline/rotatedResource.test.d.ts.map +1 -0
  25. package/dist/__tests__/pipeline/rotatedResource.test.js +242 -0
  26. package/dist/__tests__/pipeline/rotatedResource.test.js.map +1 -0
  27. package/dist/canvas-backend/blockRender.d.ts.map +1 -1
  28. package/dist/canvas-backend/blockRender.js +7 -1
  29. package/dist/canvas-backend/blockRender.js.map +1 -1
  30. package/dist/canvas-backend/index.d.ts +5 -0
  31. package/dist/canvas-backend/index.d.ts.map +1 -1
  32. package/dist/canvas-backend/index.js +7 -4
  33. package/dist/canvas-backend/index.js.map +1 -1
  34. package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
  35. package/dist/canvas-backend/renderResourceBlock.js +21 -4
  36. package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
  37. package/dist/canvas-backend/swatch.d.ts +8 -0
  38. package/dist/canvas-backend/swatch.d.ts.map +1 -0
  39. package/dist/canvas-backend/swatch.js +20 -0
  40. package/dist/canvas-backend/swatch.js.map +1 -0
  41. package/dist/html-backend.d.ts.map +1 -1
  42. package/dist/html-backend.js +38 -10
  43. package/dist/html-backend.js.map +1 -1
  44. package/dist/hyphenate.d.ts.map +1 -1
  45. package/dist/hyphenate.js +50 -1
  46. package/dist/hyphenate.js.map +1 -1
  47. package/dist/index.d.ts +5 -4
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +3 -2
  50. package/dist/index.js.map +1 -1
  51. package/dist/knuthPlass/breakpoints.d.ts.map +1 -1
  52. package/dist/knuthPlass/breakpoints.js +7 -1
  53. package/dist/knuthPlass/breakpoints.js.map +1 -1
  54. package/dist/knuthPlass/richAdapter.d.ts +4 -0
  55. package/dist/knuthPlass/richAdapter.d.ts.map +1 -1
  56. package/dist/knuthPlass/richAdapter.js +2 -1
  57. package/dist/knuthPlass/richAdapter.js.map +1 -1
  58. package/dist/knuthPlass/types.d.ts +8 -0
  59. package/dist/knuthPlass/types.d.ts.map +1 -1
  60. package/dist/measure/plain.d.ts.map +1 -1
  61. package/dist/measure/plain.js +1 -0
  62. package/dist/measure/plain.js.map +1 -1
  63. package/dist/measure/rich.d.ts +8 -0
  64. package/dist/measure/rich.d.ts.map +1 -1
  65. package/dist/measure/rich.js +25 -2
  66. package/dist/measure/rich.js.map +1 -1
  67. package/dist/parse/blockParser.d.ts +5 -3
  68. package/dist/parse/blockParser.d.ts.map +1 -1
  69. package/dist/parse/blockParser.js +45 -30
  70. package/dist/parse/blockParser.js.map +1 -1
  71. package/dist/parse/index.d.ts +2 -0
  72. package/dist/parse/index.d.ts.map +1 -1
  73. package/dist/parse/index.js +1 -0
  74. package/dist/parse/index.js.map +1 -1
  75. package/dist/parse/inlineFormatting.d.ts +26 -0
  76. package/dist/parse/inlineFormatting.d.ts.map +1 -1
  77. package/dist/parse/inlineFormatting.js +45 -0
  78. package/dist/parse/inlineFormatting.js.map +1 -1
  79. package/dist/parse/inlineSnippet.d.ts +1 -1
  80. package/dist/parse/inlineSnippet.d.ts.map +1 -1
  81. package/dist/parse/inlineSnippet.js +4 -3
  82. package/dist/parse/inlineSnippet.js.map +1 -1
  83. package/dist/parse/sourceMapping.d.ts.map +1 -1
  84. package/dist/parse/sourceMapping.js +19 -1
  85. package/dist/parse/sourceMapping.js.map +1 -1
  86. package/dist/parse/types.d.ts +10 -0
  87. package/dist/parse/types.d.ts.map +1 -1
  88. package/dist/pipeline/bandCaps.d.ts +16 -0
  89. package/dist/pipeline/bandCaps.d.ts.map +1 -1
  90. package/dist/pipeline/bandCaps.js +22 -0
  91. package/dist/pipeline/bandCaps.js.map +1 -1
  92. package/dist/pipeline/build.d.ts +33 -0
  93. package/dist/pipeline/build.d.ts.map +1 -1
  94. package/dist/pipeline/build.js +140 -38
  95. package/dist/pipeline/build.js.map +1 -1
  96. package/dist/pipeline/config.d.ts.map +1 -1
  97. package/dist/pipeline/config.js +18 -0
  98. package/dist/pipeline/config.js.map +1 -1
  99. package/dist/pipeline/floatPlacement.d.ts +10 -3
  100. package/dist/pipeline/floatPlacement.d.ts.map +1 -1
  101. package/dist/pipeline/floatPlacement.js +10 -5
  102. package/dist/pipeline/floatPlacement.js.map +1 -1
  103. package/dist/pipeline/floatSlots.d.ts +3 -0
  104. package/dist/pipeline/floatSlots.d.ts.map +1 -1
  105. package/dist/pipeline/floatSlots.js.map +1 -1
  106. package/dist/pipeline/index.d.ts +2 -2
  107. package/dist/pipeline/index.d.ts.map +1 -1
  108. package/dist/pipeline/index.js +1 -1
  109. package/dist/pipeline/index.js.map +1 -1
  110. package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
  111. package/dist/pipeline/measureContentBlock.js +6 -2
  112. package/dist/pipeline/measureContentBlock.js.map +1 -1
  113. package/dist/pipeline/resourceLayout.d.ts +16 -2
  114. package/dist/pipeline/resourceLayout.d.ts.map +1 -1
  115. package/dist/pipeline/resourceLayout.js +51 -4
  116. package/dist/pipeline/resourceLayout.js.map +1 -1
  117. package/dist/table/model.d.ts +7 -1
  118. package/dist/table/model.d.ts.map +1 -1
  119. package/dist/table/model.js +19 -0
  120. package/dist/table/model.js.map +1 -1
  121. package/dist/types.d.ts +18 -0
  122. package/dist/types.d.ts.map +1 -1
  123. package/dist/vdt.d.ts +50 -1
  124. package/dist/vdt.d.ts.map +1 -1
  125. package/dist/vdt.js +29 -0
  126. package/dist/vdt.js.map +1 -1
  127. package/dist/worker/client.d.ts +18 -2
  128. package/dist/worker/client.d.ts.map +1 -1
  129. package/dist/worker/client.js +28 -3
  130. package/dist/worker/client.js.map +1 -1
  131. package/dist/worker/layout.worker.js +135 -56
  132. package/dist/worker/layout.worker.js.map +1 -1
  133. package/dist/worker/protocol.d.ts +27 -2
  134. package/dist/worker/protocol.d.ts.map +1 -1
  135. package/package.json +3 -2
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=chapter.bench.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chapter.bench.test.d.ts","sourceRoot":"","sources":["../../src/__bench__/chapter.bench.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Layout cost of one textbook-sized chapter, cold (empty measurement cache)
3
+ * and warm (second build against the same cache), with the pass count.
4
+ * Skipped by `pnpm test`; run with `pnpm bench` (BENCH=1).
5
+ *
6
+ * Text measurement is stubbed (no canvas in node), so absolute numbers are
7
+ * not the browser's — but the cold/warm ratio and the pass count are, and
8
+ * a regression in either shows up here before it shows up in the sandbox.
9
+ */
10
+ import { describe, it } from 'vitest';
11
+ import { buildDocument } from '../pipeline/build';
12
+ import { createMeasurementCache } from '../measure';
13
+ class StubCtx {
14
+ font = '';
15
+ measureText(s) {
16
+ let w = 0;
17
+ for (let i = 0; i < s.length; i++)
18
+ w += s.charCodeAt(i) % 5 + 5;
19
+ return { width: w };
20
+ }
21
+ }
22
+ globalThis.OffscreenCanvas = class {
23
+ getContext() {
24
+ return new StubCtx();
25
+ }
26
+ };
27
+ const SENTENCES = [
28
+ 'La terapia ocupacional interviene sobre las actividades de la vida diaria, el juego y la participación social de la persona.',
29
+ 'El **razonamiento clínico** combina la evaluación estandarizada con la observación directa del desempeño en el entorno real.',
30
+ 'Los marcos de referencia *biomecánico*, *rehabilitador* y *neurodesarrollo* orientan la elección de productos de apoyo.',
31
+ 'Una férula de reposo mantiene la articulación en posición funcional y reduce el dolor durante los brotes inflamatorios.',
32
+ 'La prevención primaria actúa antes de la aparición de la enfermedad; la secundaria busca el diagnóstico precoz.',
33
+ ];
34
+ function paragraph(seed, sentences) {
35
+ const out = [];
36
+ for (let i = 0; i < sentences; i++)
37
+ out.push(SENTENCES[(seed + i) % SENTENCES.length]);
38
+ return out.join(' ');
39
+ }
40
+ /** A long chapter in the default two-column A4: headings, mixed paragraphs, lists. */
41
+ function chapterMarkdown() {
42
+ const parts = ['# Programas de intervención en geriatría', '', paragraph(0, 5), ''];
43
+ for (let i = 1; i <= 40; i++) {
44
+ parts.push(`## Sección ${i}`, '', paragraph(i, 6), '', paragraph(i + 2, 4), '');
45
+ parts.push(`### Detalle ${i}`, '', paragraph(i + 1, 5), '');
46
+ parts.push('- Valoración inicial de la persona y de su entorno.', '- Objetivos consensuados con la persona y la familia.', '- Reevaluación periódica del plan de intervención.', '');
47
+ parts.push(paragraph(i + 3, 3), '');
48
+ }
49
+ return parts.join('\n');
50
+ }
51
+ const markdown = chapterMarkdown();
52
+ const config = { bodyText: { hyphenation: { locale: 'es' } } };
53
+ // No node typings in this package: reach the environment through globalThis.
54
+ const nodeProcess = globalThis.process;
55
+ describe.skipIf(!nodeProcess?.env?.BENCH)('chapter layout bench', () => {
56
+ const ROUNDS = 5;
57
+ const median = (xs) => xs.slice().sort((a, b) => a - b)[Math.floor(xs.length / 2)];
58
+ const time = (fn) => {
59
+ const t0 = performance.now();
60
+ fn();
61
+ return performance.now() - t0;
62
+ };
63
+ it('reports cold / warm build time and passes', { timeout: 300_000 }, () => {
64
+ const cold = [];
65
+ for (let i = 0; i < ROUNDS; i++)
66
+ cold.push(time(() => buildDocument({ markdown }, config, createMeasurementCache())));
67
+ const warmCache = createMeasurementCache();
68
+ buildDocument({ markdown }, config, warmCache);
69
+ const warm = [];
70
+ for (let i = 0; i < ROUNDS; i++)
71
+ warm.push(time(() => buildDocument({ markdown }, config, warmCache)));
72
+ const passes = [];
73
+ const doc = buildDocument({ markdown }, config, createMeasurementCache(), { onPass: (p) => passes.push(p) });
74
+ const passMs = passes.map((p) => Math.round(p.ms)).join('/');
75
+ // Written to the process stream: vitest hides console output of passing tests.
76
+ nodeProcess?.stdout.write(`[bench] chapter: ${doc.pages.length} pages, ${passes.length} passes (${passMs} ms), iterationCount=${doc.iterationCount}; `
77
+ + `cold median ${Math.round(median(cold))} ms, warm median ${Math.round(median(warm))} ms (${ROUNDS} rounds)\n`);
78
+ });
79
+ });
80
+ //# sourceMappingURL=chapter.bench.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chapter.bench.test.js","sourceRoot":"","sources":["../../src/__bench__/chapter.bench.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=buildAsync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildAsync.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/buildAsync.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildDocument, buildDocumentAsync, BuildCancelledError } from '../pipeline/build';
3
+ import { createMeasurementCache } from '../measure';
4
+ class StubCtx {
5
+ font = '';
6
+ measureText(s) {
7
+ return { width: s.length * 7 };
8
+ }
9
+ }
10
+ globalThis.OffscreenCanvas = class {
11
+ getContext() {
12
+ return new StubCtx();
13
+ }
14
+ };
15
+ const SENTENCE = 'La composición tipográfica editorial exige columnas alineadas, rejilla base estable y márgenes consistentes. ';
16
+ function markdown() {
17
+ const parts = ['# Documento', '', SENTENCE.repeat(6)];
18
+ for (let i = 1; i <= 6; i++)
19
+ parts.push('', `## Sección ${i}`, '', SENTENCE.repeat(5), '', SENTENCE.repeat(4));
20
+ return parts.join('\n');
21
+ }
22
+ const config = { headings: { balancing: { enabled: true } } };
23
+ describe('buildDocumentAsync', () => {
24
+ it('produces the same document as the synchronous build', async () => {
25
+ const sync = buildDocument({ markdown: markdown() }, config, createMeasurementCache());
26
+ const passes = [];
27
+ let yields = 0;
28
+ const async = await buildDocumentAsync({ markdown: markdown() }, config, createMeasurementCache(), {
29
+ onPass: (p) => passes.push(p),
30
+ yieldBetweenPasses: async () => { yields++; },
31
+ });
32
+ expect(async.pages.length).toBe(sync.pages.length);
33
+ expect(async.iterationCount).toBe(sync.iterationCount);
34
+ expect(passes.length).toBe(sync.iterationCount);
35
+ // One turn of the event loop after every pass.
36
+ expect(yields).toBe(passes.length);
37
+ });
38
+ it('stops at the next pass boundary once cancelled', async () => {
39
+ const passes = [];
40
+ let cancelled = false;
41
+ await expect(buildDocumentAsync({ markdown: markdown() }, config, createMeasurementCache(), {
42
+ onPass: (p) => { passes.push(p); if (passes.length === 2)
43
+ cancelled = true; },
44
+ shouldCancel: () => cancelled,
45
+ yieldBetweenPasses: async () => { },
46
+ })).rejects.toBeInstanceOf(BuildCancelledError);
47
+ expect(passes.length).toBe(2);
48
+ });
49
+ });
50
+ //# sourceMappingURL=buildAsync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildAsync.test.js","sourceRoot":"","sources":["../../src/__tests__/buildAsync.test.ts"],"names":[],"mappings":"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"}
@@ -79,6 +79,7 @@ describe("package exports", () => {
79
79
  "unmergeCell",
80
80
  "setCellContent",
81
81
  "setCellImage",
82
+ "setCellBackground",
82
83
  "setAlignment",
83
84
  "parseTSV",
84
85
  "extractFrontmatter",
@@ -184,9 +185,13 @@ describe("package exports", () => {
184
185
  "resolveDesignSlot",
185
186
  "stripDesignSlotDefaults",
186
187
  "DEFAULT_BOX_ELEMENT",
188
+ "resourceBlockToPage",
189
+ "resourceBlockToLocal",
190
+ "resourceBlockRectToPage",
187
191
  "computeColumnEdges",
188
192
  "parseMarkdownWithIssues",
189
193
  "MATH_PLACEHOLDER",
194
+ "SWATCH_PLACEHOLDER",
190
195
  "KNOWN_DIRECTIVES",
191
196
  "KNOWN_CONTAINERS",
192
197
  "computeSourceMap",
@@ -1 +1 @@
1
- {"version":3,"file":"exports.test.js","sourceRoot":"","sources":["../../src/__tests__/exports.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,UAAU,CAAC;AASpC,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,qDAAqD;AACrD,MAAM,mBAAmB,GAAG,GAAG,EAAE;IAC/B,MAAM,IAAI,GAAiB,OAAO,CAAC;IACnC,MAAM,IAAI,GAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IACzC,MAAM,KAAK,GAAe,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAiB;QACzB,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,MAAM;QAClB,iBAAiB,EAAE,KAAK;QACxB,OAAO,EAAE,OAAO;QAChB,aAAa,EAAE,SAAS;QACxB,aAAa,EAAE,QAAQ;KACxB,CAAC;IACF,MAAM,QAAQ,GAAa;QACzB,EAAE,EAAE,GAAG;QACP,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,IAAI;QACJ,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,KAAK,EAAE,EAAE,KAAK,EAAE;KACjB,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AACF,KAAK,mBAAmB,CAAC;AAEzB,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,CAAC,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAC3B,cAAc;YACd,eAAe;YACf,qBAAqB;YACrB,mBAAmB;YACnB,gBAAgB;YAChB,gBAAgB;YAChB,mBAAmB;YACnB,gBAAgB;YAChB,YAAY;YACZ,aAAa;YACb,iBAAiB;YACjB,gBAAgB;YAChB,YAAY;YACZ,oBAAoB;YACpB,uBAAuB;YACvB,yBAAyB;YACzB,qBAAqB;YACrB,kBAAkB;YAClB,cAAc;YACd,qBAAqB;YACrB,eAAe;YACf,uBAAuB;YACvB,oBAAoB;YACpB,iBAAiB;YACjB,cAAc;YACd,kBAAkB;YAClB,mBAAmB;YACnB,gBAAgB;YAChB,uBAAuB;YACvB,wBAAwB;YACxB,oBAAoB;YACpB,wBAAwB;YACxB,eAAe;YACf,sBAAsB;YACtB,eAAe;YACf,QAAQ;YACR,WAAW;YACX,WAAW;YACX,cAAc;YACd,YAAY;YACZ,aAAa;YACb,gBAAgB;YAChB,cAAc;YACd,cAAc;YACd,UAAU;YACV,oBAAoB;YACpB,qBAAqB;YACrB,mBAAmB;YACnB,wBAAwB;YACxB,mBAAmB;YACnB,mBAAmB;YACnB,uBAAuB;YACvB,qBAAqB;YACrB,0BAA0B;YAC1B,qBAAqB;YACrB,qBAAqB;YACrB,0BAA0B;YAC1B,4BAA4B;YAC5B,uBAAuB;YACvB,uBAAuB;YACvB,kBAAkB;YAClB,yBAAyB;YACzB,uBAAuB;YACvB,uBAAuB;YACvB,yBAAyB;YACzB,yBAAyB;YACzB,iCAAiC;YACjC,2BAA2B;YAC3B,2BAA2B;YAC3B,mBAAmB;YACnB,8BAA8B;YAC9B,2BAA2B;YAC3B,2BAA2B;YAC3B,0BAA0B;YAC1B,8BAA8B;YAC9B,8BAA8B;YAC9B,wBAAwB;YACxB,8BAA8B;YAC9B,4BAA4B;YAC5B,4BAA4B;YAC5B,gCAAgC;YAChC,6BAA6B;YAC7B,6BAA6B;YAC7B,8BAA8B;YAC9B,2BAA2B;YAC3B,2BAA2B;YAC3B,qBAAqB;YACrB,mBAAmB;YACnB,mBAAmB;YACnB,iBAAiB;YACjB,aAAa;YACb,mBAAmB;YACnB,sBAAsB;YACtB,8BAA8B;YAC9B,uBAAuB;YACvB,oBAAoB;YACpB,uBAAuB;YACvB,yBAAyB;YACzB,wBAAwB;YACxB,0BAA0B;YAC1B,uBAAuB;YACvB,mBAAmB;YACnB,qBAAqB;YACrB,sBAAsB;YACtB,oBAAoB;YACpB,oBAAoB;YACpB,4BAA4B;YAC5B,yBAAyB;YACzB,yBAAyB;YACzB,sBAAsB;YACtB,0BAA0B;YAC1B,+BAA+B;YAC/B,4BAA4B;YAC5B,4BAA4B;YAC5B,4BAA4B;YAC5B,qBAAqB;YACrB,qBAAqB;YACrB,sBAAsB;YACtB,sBAAsB;YACtB,2BAA2B;YAC3B,2BAA2B;YAC3B,sBAAsB;YACtB,sBAAsB;YACtB,oBAAoB;YACpB,oBAAoB;YACpB,wBAAwB;YACxB,4BAA4B;YAC5B,4BAA4B;YAC5B,oBAAoB;YACpB,kBAAkB;YAClB,kBAAkB;YAClB,qBAAqB;YACrB,sBAAsB;YACtB,yBAAyB;YACzB,oBAAoB;YACpB,uBAAuB;YACvB,qBAAqB;YACrB,mBAAmB;YACnB,2BAA2B;YAC3B,wBAAwB;YACxB,sBAAsB;YACtB,kBAAkB;YAClB,eAAe;YACf,iCAAiC;YACjC,0BAA0B;YAC1B,mBAAmB;YACnB,yBAAyB;YACzB,qBAAqB;YACrB,oBAAoB;YACpB,yBAAyB;YACzB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,kBAAkB;YAClB,yBAAyB;YACzB,kBAAkB;YAClB,iBAAiB;YACjB,sBAAsB;YACtB,eAAe;YACf,gBAAgB;YAChB,aAAa;YACb,aAAa;YACb,YAAY;YACZ,mBAAmB;YACnB,gBAAgB;YAChB,qBAAqB;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"exports.test.js","sourceRoot":"","sources":["../../src/__tests__/exports.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hyphenateMemo.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyphenateMemo.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hyphenateMemo.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import Hypher from 'hypher';
3
+ import es from 'hyphenation.es';
4
+ import { hyphenateText } from '../hyphenate';
5
+ describe('hyphenateText', () => {
6
+ it('matches the dictionary applied to the whole text', () => {
7
+ const reference = new Hypher(es);
8
+ const texts = [
9
+ 'La composición tipográfica editorial exige columnas alineadas y márgenes consistentes.',
10
+ 'Reevaluación periódica\tdel plan; intervención (geriátrica) — enseñanza-aprendizaje.',
11
+ 'Visita https://ejemplo.org/ruta/larga y a/b y termina/',
12
+ ' espacios al principio y al final ',
13
+ ];
14
+ for (const text of texts) {
15
+ expect(hyphenateText(text, 'es')).toBe(reference.hyphenateText(text));
16
+ // Second call: from the memo.
17
+ expect(hyphenateText(text, 'es')).toBe(reference.hyphenateText(text));
18
+ }
19
+ });
20
+ });
21
+ //# sourceMappingURL=hyphenateMemo.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyphenateMemo.test.js","sourceRoot":"","sources":["../../src/__tests__/hyphenateMemo.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG;YACZ,wFAAwF;YACxF,wFAAwF;YACxF,wDAAwD;YACxD,sCAAsC;SACvC,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;YACtE,8BAA8B;YAC9B,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=knuthPlassMerge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knuthPlassMerge.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/knuthPlassMerge.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { computeBreakpoints } from '../knuthPlass/breakpoints';
3
+ import { KP_INFINITY, MAX_STRETCH, HYPHEN_PENALTY } from '../knuthPlass/constants';
4
+ /** Deterministic pseudo-random paragraphs: words of varying width, some
5
+ * with a discretionary hyphen, glue between them, the closing glue and
6
+ * forced break. */
7
+ function paragraph(seed, words) {
8
+ let x = seed;
9
+ const rnd = () => { x = (x * 1103515245 + 12345) & 0x7fffffff; return x / 0x7fffffff; };
10
+ const items = [];
11
+ for (let w = 0; w < words; w++) {
12
+ const width = 20 + Math.floor(rnd() * 60);
13
+ if (rnd() < 0.3) {
14
+ const head = Math.floor(width * (0.3 + rnd() * 0.4));
15
+ items.push({ type: 'box', width: head, sourceIndex: w });
16
+ items.push({ type: 'penalty', width: 6, penalty: HYPHEN_PENALTY, flagged: true, sourceIndex: w });
17
+ items.push({ type: 'box', width: width - head, sourceIndex: w });
18
+ }
19
+ else {
20
+ items.push({ type: 'box', width, sourceIndex: w });
21
+ }
22
+ if (w < words - 1)
23
+ items.push({ type: 'glue', width: 8, stretch: 4, shrink: 2, sourceIndex: w });
24
+ }
25
+ items.push({ type: 'glue', width: 0, stretch: MAX_STRETCH, shrink: 0, sourceIndex: -1 });
26
+ items.push({ type: 'penalty', width: 0, penalty: -KP_INFINITY, flagged: false, sourceIndex: -1 });
27
+ return items;
28
+ }
29
+ const baseOptions = (indent, runt) => ({
30
+ lineWidth: (line) => (line === 0 ? 300 - indent : 300),
31
+ normalSpaceWidth: 8,
32
+ maxStretchRatio: 1.5,
33
+ minShrinkRatio: 0.75,
34
+ runtPenalty: runt ? 300 : 0,
35
+ runtMinWidth: runt ? 40 : 0,
36
+ });
37
+ describe('computeBreakpoints with lineWidthUniformFrom', () => {
38
+ it('chooses the same breaks as the unmerged search', () => {
39
+ for (let seed = 1; seed <= 120; seed++) {
40
+ const items = paragraph(seed, 12 + (seed % 90));
41
+ const options = baseOptions(seed % 3 === 0 ? 0 : 24, seed % 2 === 0);
42
+ const plain = computeBreakpoints(items, options);
43
+ const merged = computeBreakpoints(items, { ...options, lineWidthUniformFrom: 1 });
44
+ expect(merged).toEqual(plain);
45
+ }
46
+ });
47
+ it('keeps every line count apart while a looseness target is set', () => {
48
+ for (let seed = 1; seed <= 40; seed++) {
49
+ const items = paragraph(seed, 30 + seed);
50
+ const options = { ...baseOptions(24, false), looseness: 1 };
51
+ expect(computeBreakpoints(items, { ...options, lineWidthUniformFrom: 1 })).toEqual(computeBreakpoints(items, options));
52
+ }
53
+ });
54
+ });
55
+ //# sourceMappingURL=knuthPlassMerge.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knuthPlassMerge.test.js","sourceRoot":"","sources":["../../src/__tests__/knuthPlassMerge.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=memos.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memos.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/memos.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseMarkdownMemo, parseMarkdownWithIssuesMemo, PARSE_MEMO_CAPACITY } from '../parse/blockParser';
3
+ import { resolveAllConfig } from '../pipeline/config';
4
+ describe('parseMarkdownMemo', () => {
5
+ it('returns the same blocks for the same input', () => {
6
+ const md = '# Title\n\nA paragraph.\n';
7
+ expect(parseMarkdownMemo(md)).toBe(parseMarkdownMemo(md));
8
+ expect(parseMarkdownWithIssuesMemo(md).blocks).toBe(parseMarkdownMemo(md));
9
+ });
10
+ it('keeps the last few inputs, so interleaved chapters do not re-parse', () => {
11
+ const a = parseMarkdownMemo('# A\n\nalpha\n');
12
+ const b = parseMarkdownMemo('# B\n\nbeta\n');
13
+ expect(parseMarkdownMemo('# A\n\nalpha\n')).toBe(a);
14
+ expect(parseMarkdownMemo('# B\n\nbeta\n')).toBe(b);
15
+ });
16
+ it('evicts the least recently used input past its capacity', () => {
17
+ const first = parseMarkdownMemo('# first\n');
18
+ for (let i = 0; i < PARSE_MEMO_CAPACITY; i++)
19
+ parseMarkdownMemo(`# filler ${i}\n`);
20
+ expect(parseMarkdownMemo('# first\n')).not.toBe(first);
21
+ });
22
+ });
23
+ describe('resolveAllConfig', () => {
24
+ it('resolves a config object once', () => {
25
+ const config = { bodyText: { textAlign: 'left' } };
26
+ expect(resolveAllConfig(config)).toBe(resolveAllConfig(config));
27
+ expect(resolveAllConfig(undefined)).toBe(resolveAllConfig(undefined));
28
+ });
29
+ it('resolves distinct objects separately', () => {
30
+ const a = resolveAllConfig({ bodyText: { textAlign: 'left' } });
31
+ const b = resolveAllConfig({ bodyText: { textAlign: 'justify' } });
32
+ expect(a).not.toBe(b);
33
+ expect(a.bodyText.textAlign).toBe('left');
34
+ expect(b.bodyText.textAlign).toBe('justify');
35
+ });
36
+ });
37
+ //# sourceMappingURL=memos.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memos.test.js","sourceRoot":"","sources":["../../src/__tests__/memos.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,EAAE,GAAG,2BAA2B,CAAC;QACvC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,CAAC,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,KAAK,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,mBAAmB,EAAE,CAAC,EAAE;YAAE,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnF,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAkB,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;QAClE,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,GAAG,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,GAAG,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rotatedResource.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotatedResource.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/pipeline/rotatedResource.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,242 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildDocument } from '../../pipeline';
3
+ import { layoutResourceBlock } from '../../pipeline/resourceLayout';
4
+ import { resolveAllConfig } from '../../pipeline/config';
5
+ import { defaultResourceTypes } from '../../defaults/resourceTypes';
6
+ import { resourceBlockRectToPage, resourceBlockToLocal, resourceBlockToPage } from '../../vdt';
7
+ import { parseInlineSnippetSpans } from '../../parse/inlineSnippet';
8
+ import { SWATCH_PLACEHOLDER } from '../../parse/inlineFormatting';
9
+ // Deterministic text measurement stub (no DOM in the node test env).
10
+ class StubCtx {
11
+ font = '';
12
+ measureText(s) {
13
+ return { width: s.length * 7 };
14
+ }
15
+ }
16
+ globalThis.OffscreenCanvas = class {
17
+ getContext() {
18
+ return new StubCtx();
19
+ }
20
+ };
21
+ const cell = (content, extra = {}) => ({ content, ...extra });
22
+ /** A header row plus `n` body rows. */
23
+ const table = (id, n, extra = {}) => ({
24
+ id,
25
+ typeId: 'table',
26
+ kind: 'table',
27
+ caption: 'Services and benefits.',
28
+ note: 'Source: the reference book.',
29
+ createdAt: 0,
30
+ updatedAt: 0,
31
+ table: {
32
+ model: {
33
+ headerRowCount: 1,
34
+ rows: [
35
+ [cell('Service', { isHeader: true }), cell('Grade I', { isHeader: true }), cell('Grade II', { isHeader: true })],
36
+ ...Array.from({ length: n }, (_, i) => [cell(`Service ${i + 1}`), cell('Yes'), cell('Yes')]),
37
+ ],
38
+ },
39
+ },
40
+ placement: { rotate: 'ccw' },
41
+ ...extra,
42
+ });
43
+ const pt = (value) => ({ value, unit: 'pt' });
44
+ /** Two-column page with balancing off (single pass, deterministic). */
45
+ const PAGE = {
46
+ page: { width: pt(400), height: pt(500), margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
47
+ headings: { balancing: { enabled: false }, levels: [{ level: 1, breakBefore: { enabled: false } }] },
48
+ };
49
+ const para = (i) => `Paragraph ${i} carries enough words to take a few lines of the narrow column so the flow advances steadily.`;
50
+ const filler = (n, from = 0) => Array.from({ length: n }, (_, i) => para(from + i)).join('\n\n');
51
+ const build = (markdown, resources, config = PAGE) => buildDocument({ markdown, resources }, config);
52
+ const floatsOf = (doc, id) => doc.pages.flatMap((p) => (p.floats ?? []).filter((b) => b.resourceBlock.resource.id === id).map((b) => ({ page: p, block: b })));
53
+ const rbOf = (b) => b.resourceBlock;
54
+ function layout(resource, config, extra = {}) {
55
+ const resourceTypes = config?.resourceTypes ?? defaultResourceTypes();
56
+ const resolved = resolveAllConfig(config);
57
+ return layoutResourceBlock({
58
+ resource,
59
+ resourceType: resourceTypes.find((t) => t.id === resource.typeId),
60
+ number: '46-5',
61
+ resolved,
62
+ columnWidth: extra.columnWidth ?? 300,
63
+ resourceNumbering: {},
64
+ resourceTypes,
65
+ resources: [resource],
66
+ ...(extra.rotate ? { rotate: extra.rotate, rotatedLength: extra.rotatedLength } : {}),
67
+ });
68
+ }
69
+ describe('rotated resource layout', () => {
70
+ it('lays the block out upright at the rotated length and reports that length as its height', () => {
71
+ const { block, totalHeight } = layout(table('tab', 4), undefined, { rotate: 'ccw', rotatedLength: 500, columnWidth: 300 });
72
+ expect(totalHeight).toBe(500);
73
+ expect(block.rotation).toBeDefined();
74
+ expect(block.rotation.direction).toBe('ccw');
75
+ expect(block.rotation.width).toBe(500);
76
+ expect(block.bodyRect.width).toBe(500);
77
+ // Upright height: body plus caption and note.
78
+ expect(block.rotation.height).toBeCloseTo(block.bodyRect.height + (block.rotation.height - block.bodyRect.height), 6);
79
+ expect(block.rotation.height).toBeGreaterThan(block.bodyRect.height);
80
+ // The frame's origin is left to the placer.
81
+ expect(block.rotation.originX).toBe(0);
82
+ expect(block.rotation.originY).toBe(0);
83
+ // Cells span the upright width.
84
+ const cells = block.table.cells;
85
+ expect(Math.max(...cells.map((c) => c.rect.x + c.rect.width))).toBeCloseTo(500, 6);
86
+ });
87
+ it('an upright layout of the same table has no rotation', () => {
88
+ const { block, totalHeight } = layout(table('tab', 4));
89
+ expect(block.rotation).toBeUndefined();
90
+ expect(totalHeight).toBeGreaterThan(0);
91
+ expect(block.bodyRect.width).toBe(300);
92
+ });
93
+ it('maps the upright frame onto the page and back', () => {
94
+ const rb = { rotation: { direction: 'ccw', originX: 20, originY: 400, width: 500, height: 120 } };
95
+ expect(resourceBlockToPage(rb, 0, 0)).toEqual({ x: 20, y: 400 });
96
+ // Upright x runs up the page, upright y runs right.
97
+ expect(resourceBlockToPage(rb, 10, 5)).toEqual({ x: 25, y: 390 });
98
+ expect(resourceBlockToLocal(rb, 25, 390)).toEqual({ x: 10, y: 5 });
99
+ expect(resourceBlockRectToPage(rb, { x: 0, y: 0, width: 500, height: 120 })).toEqual({ x: 20, y: -100, width: 120, height: 500 });
100
+ const cw = { rotation: { direction: 'cw', originX: 140, originY: 30, width: 500, height: 120 } };
101
+ expect(resourceBlockToPage(cw, 10, 5)).toEqual({ x: 135, y: 40 });
102
+ expect(resourceBlockToLocal(cw, 135, 40)).toEqual({ x: 10, y: 5 });
103
+ expect(resourceBlockRectToPage(cw, { x: 0, y: 0, width: 500, height: 120 })).toEqual({ x: 20, y: 30, width: 120, height: 500 });
104
+ // An upright block: the identity.
105
+ expect(resourceBlockToPage({}, 3, 4)).toEqual({ x: 3, y: 4 });
106
+ expect(resourceBlockRectToPage({}, { x: 1, y: 2, width: 3, height: 4 })).toEqual({ x: 1, y: 2, width: 3, height: 4 });
107
+ });
108
+ });
109
+ describe('rotated floats', () => {
110
+ const markdown = `Intro :ref{id="tab"} text.\n\n${filler(30)}`;
111
+ it('takes a whole page as a page-span float, flush left, turned counter-clockwise', () => {
112
+ const doc = build(markdown, [table('tab', 6)]);
113
+ const placed = floatsOf(doc, 'tab');
114
+ expect(placed).toHaveLength(1);
115
+ const { page, block } = placed[0];
116
+ const rb = rbOf(block);
117
+ expect(rb.rotation).toBeDefined();
118
+ // The band spans the content width and (almost) the full content height.
119
+ expect(block.bbox.x).toBeCloseTo(page.contentArea.x, 6);
120
+ expect(block.bbox.width).toBeCloseTo(page.contentArea.width, 6);
121
+ expect(block.bbox.y).toBeCloseTo(page.contentArea.y, 6);
122
+ expect(block.bbox.height).toBeGreaterThan(page.contentArea.height * 0.85);
123
+ expect(block.bbox.height).toBeLessThanOrEqual(page.contentArea.height + 1e-6);
124
+ // Upright frame as long as the band is tall; its origin at the band's
125
+ // bottom-left (the frame's top faces the left edge of the page).
126
+ expect(rb.rotation.width).toBeCloseTo(block.bbox.height, 6);
127
+ expect(rb.rotation.originX).toBeCloseTo(page.contentArea.x, 6);
128
+ expect(rb.rotation.originY).toBeCloseTo(block.bbox.y + block.bbox.height, 6);
129
+ // Every cell, mapped onto the page, lies inside the band.
130
+ for (const c of rb.table.cells) {
131
+ const r = resourceBlockRectToPage(rb, c.rect);
132
+ expect(r.x).toBeGreaterThanOrEqual(block.bbox.x - 1e-6);
133
+ expect(r.x + r.width).toBeLessThanOrEqual(block.bbox.x + block.bbox.width + 1e-6);
134
+ expect(r.y).toBeGreaterThanOrEqual(block.bbox.y - 1e-6);
135
+ expect(r.y + r.height).toBeLessThanOrEqual(block.bbox.y + block.bbox.height + 1e-6);
136
+ }
137
+ // No body text shares the page with it.
138
+ const textOnPage = doc.blocks.filter((b) => b.pageIndex === page.index && !b.hidden && b.lines.length > 0);
139
+ expect(textOnPage).toHaveLength(0);
140
+ });
141
+ it('sits flush to the spine on a verso page when the margins are mirrored', () => {
142
+ const config = {
143
+ ...PAGE,
144
+ page: { ...PAGE.page, margins: { ...PAGE.page.margins, mirror: true } },
145
+ };
146
+ const doc = build(markdown, [table('tab', 6)], config);
147
+ const placed = floatsOf(doc, 'tab');
148
+ expect(placed).toHaveLength(1);
149
+ const { page, block } = placed[0];
150
+ const rb = rbOf(block);
151
+ const verso = page.index % 2 === 1;
152
+ const expectedLeft = verso ? block.bbox.x + block.bbox.width - rb.rotation.height : block.bbox.x;
153
+ expect(rb.rotation.originX).toBeCloseTo(expectedLeft, 6);
154
+ });
155
+ it('a table too wide for the page is cut between rows and continues, turned, on the next pages', () => {
156
+ const doc = build(markdown, [table('tab', 90)]);
157
+ const slices = floatsOf(doc, 'tab');
158
+ expect(slices.length).toBeGreaterThan(1);
159
+ let next = 0;
160
+ for (let i = 0; i < slices.length; i++) {
161
+ const { page, block } = slices[i];
162
+ const rb = rbOf(block);
163
+ expect(rb.rotation).toBeDefined();
164
+ // Each slice fits the band's width upright…
165
+ expect(rb.rotation.height).toBeLessThanOrEqual(page.contentArea.width + 1e-6);
166
+ // …on consecutive pages, one per page.
167
+ if (i > 0)
168
+ expect(page.index).toBe(slices[i - 1].page.index + 1);
169
+ const s = rb.slice;
170
+ if (s) {
171
+ if (i === 0)
172
+ expect(s.startRow).toBe(0);
173
+ else
174
+ expect(s.startRow).toBe(next);
175
+ next = s.endRow;
176
+ expect(s.continued).toBe(i > 0);
177
+ expect(s.continues).toBe(i < slices.length - 1);
178
+ }
179
+ }
180
+ expect(next).toBe(91);
181
+ });
182
+ });
183
+ describe('table cell fills', () => {
184
+ it('a cell background is resolved through the palette and carried onto the cell', () => {
185
+ const config = {
186
+ colorPalette: [{ id: 'ok', name: 'Compatible', value: { hex: '#c1dfd6', model: 'hex' } }],
187
+ };
188
+ const res = table('tab', 1, {
189
+ placement: {},
190
+ table: {
191
+ model: {
192
+ headerRowCount: 1,
193
+ rows: [
194
+ [cell('A', { isHeader: true }), cell('B', { isHeader: true })],
195
+ [cell('x', { background: { hex: '#000000', model: 'hex', paletteId: 'ok' } }), cell('y', { background: { hex: '#ff0000', model: 'hex' } })],
196
+ [cell('p'), cell('q', { background: { hex: '#123456', model: 'hex', paletteId: 'missing' } })],
197
+ ],
198
+ },
199
+ },
200
+ });
201
+ const { block } = layout(res, config);
202
+ const at = (row, col) => block.table.cells.find((c) => c.row === row && c.col === col);
203
+ expect(at(1, 0).background).toBe('#c1dfd6');
204
+ expect(at(1, 1).background).toBe('#ff0000');
205
+ expect(at(2, 0).background).toBeUndefined();
206
+ // An unknown palette id falls back to the stored hex.
207
+ expect(at(2, 1).background).toBe('#123456');
208
+ expect(at(0, 0).background).toBeUndefined();
209
+ });
210
+ });
211
+ describe('inline colour swatches', () => {
212
+ it('parses `:swatch{color=…}` into an atomic swatch span', () => {
213
+ const spans = parseInlineSnippetSpans('Key: :swatch{color="#abc"} compatible; :swatch{color="ok"} **bold**');
214
+ const swatches = spans.filter((s) => s.swatch);
215
+ expect(swatches).toHaveLength(2);
216
+ expect(swatches[0].text).toBe(SWATCH_PLACEHOLDER);
217
+ expect(swatches[0].swatch.color).toBe('#abc');
218
+ expect(swatches[1].swatch.color).toBe('ok');
219
+ // A swatch without a colour is left as text.
220
+ expect(parseInlineSnippetSpans('no :swatch{} here').some((s) => s.swatch)).toBe(false);
221
+ });
222
+ it('measures a swatch as a square on the baseline, filled from a hex or a palette entry', () => {
223
+ const config = {
224
+ colorPalette: [{ id: 'ok', name: 'Compatible', value: { hex: '#c1dfd6', model: 'hex' } }],
225
+ };
226
+ const res = table('tab', 1, {
227
+ placement: {},
228
+ note: ':swatch{color="#abc"} compatible; :swatch{color="ok"} conditional; :swatch{color="nope"} unknown',
229
+ });
230
+ const { block } = layout(res, config);
231
+ const segs = block.noteLines.flatMap((l) => l.segments ?? []).filter((s) => s.kind === 'swatch');
232
+ expect(segs).toHaveLength(3);
233
+ expect(segs[0].swatch?.color).toBe('#aabbcc');
234
+ expect(segs[1].swatch?.color).toBe('#c1dfd6');
235
+ expect(segs[2].swatch?.color).toBeUndefined();
236
+ // Three quarters of the note's font size, square.
237
+ const sizePx = Number(/(\d*\.?\d+)px/.exec(block.noteFontString)[1]);
238
+ for (const s of segs)
239
+ expect(s.width).toBeCloseTo(sizePx * 0.75, 6);
240
+ });
241
+ });
242
+ //# sourceMappingURL=rotatedResource.test.js.map