postext 0.3.16 → 0.3.18

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 (228) hide show
  1. package/dist/__tests__/columnBalancing.test.d.ts +2 -0
  2. package/dist/__tests__/columnBalancing.test.d.ts.map +1 -0
  3. package/dist/__tests__/columnBalancing.test.js +194 -0
  4. package/dist/__tests__/columnBalancing.test.js.map +1 -0
  5. package/dist/__tests__/createLayout.test.js +12 -13
  6. package/dist/__tests__/createLayout.test.js.map +1 -1
  7. package/dist/__tests__/defaults/resourceTypes.test.d.ts +2 -0
  8. package/dist/__tests__/defaults/resourceTypes.test.d.ts.map +1 -0
  9. package/dist/__tests__/defaults/resourceTypes.test.js +69 -0
  10. package/dist/__tests__/defaults/resourceTypes.test.js.map +1 -0
  11. package/dist/__tests__/exports.test.js +60 -0
  12. package/dist/__tests__/exports.test.js.map +1 -1
  13. package/dist/__tests__/parse/inlineRef.test.d.ts +2 -0
  14. package/dist/__tests__/parse/inlineRef.test.d.ts.map +1 -0
  15. package/dist/__tests__/parse/inlineRef.test.js +83 -0
  16. package/dist/__tests__/parse/inlineRef.test.js.map +1 -0
  17. package/dist/__tests__/parse/resourceDirective.test.d.ts +2 -0
  18. package/dist/__tests__/parse/resourceDirective.test.d.ts.map +1 -0
  19. package/dist/__tests__/parse/resourceDirective.test.js +55 -0
  20. package/dist/__tests__/parse/resourceDirective.test.js.map +1 -0
  21. package/dist/__tests__/pipeline/floatPlacement.test.d.ts +2 -0
  22. package/dist/__tests__/pipeline/floatPlacement.test.d.ts.map +1 -0
  23. package/dist/__tests__/pipeline/floatPlacement.test.js +262 -0
  24. package/dist/__tests__/pipeline/floatPlacement.test.js.map +1 -0
  25. package/dist/__tests__/pipeline/inlineRefRender.test.d.ts +2 -0
  26. package/dist/__tests__/pipeline/inlineRefRender.test.d.ts.map +1 -0
  27. package/dist/__tests__/pipeline/inlineRefRender.test.js +107 -0
  28. package/dist/__tests__/pipeline/inlineRefRender.test.js.map +1 -0
  29. package/dist/__tests__/pipeline/resourceNumbering.test.d.ts +2 -0
  30. package/dist/__tests__/pipeline/resourceNumbering.test.d.ts.map +1 -0
  31. package/dist/__tests__/pipeline/resourceNumbering.test.js +186 -0
  32. package/dist/__tests__/pipeline/resourceNumbering.test.js.map +1 -0
  33. package/dist/__tests__/singleInk.test.d.ts +2 -0
  34. package/dist/__tests__/singleInk.test.d.ts.map +1 -0
  35. package/dist/__tests__/singleInk.test.js +42 -0
  36. package/dist/__tests__/singleInk.test.js.map +1 -0
  37. package/dist/__tests__/table/model.test.d.ts +2 -0
  38. package/dist/__tests__/table/model.test.d.ts.map +1 -0
  39. package/dist/__tests__/table/model.test.js +187 -0
  40. package/dist/__tests__/table/model.test.js.map +1 -0
  41. package/dist/canvas-backend/blockRender.d.ts.map +1 -1
  42. package/dist/canvas-backend/blockRender.js +103 -66
  43. package/dist/canvas-backend/blockRender.js.map +1 -1
  44. package/dist/canvas-backend/decorations.d.ts +4 -1
  45. package/dist/canvas-backend/decorations.d.ts.map +1 -1
  46. package/dist/canvas-backend/decorations.js +14 -6
  47. package/dist/canvas-backend/decorations.js.map +1 -1
  48. package/dist/canvas-backend/headerFooter.d.ts +2 -2
  49. package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
  50. package/dist/canvas-backend/headerFooter.js +62 -2
  51. package/dist/canvas-backend/headerFooter.js.map +1 -1
  52. package/dist/canvas-backend/index.d.ts +2 -0
  53. package/dist/canvas-backend/index.d.ts.map +1 -1
  54. package/dist/canvas-backend/index.js +20 -10
  55. package/dist/canvas-backend/index.js.map +1 -1
  56. package/dist/canvas-backend/renderResourceBlock.d.ts +28 -0
  57. package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -0
  58. package/dist/canvas-backend/renderResourceBlock.js +146 -0
  59. package/dist/canvas-backend/renderResourceBlock.js.map +1 -0
  60. package/dist/defaults/bodyText.d.ts.map +1 -1
  61. package/dist/defaults/bodyText.js +30 -0
  62. package/dist/defaults/bodyText.js.map +1 -1
  63. package/dist/defaults/captionStyle.d.ts +9 -0
  64. package/dist/defaults/captionStyle.d.ts.map +1 -0
  65. package/dist/defaults/captionStyle.js +74 -0
  66. package/dist/defaults/captionStyle.js.map +1 -0
  67. package/dist/defaults/debug.d.ts.map +1 -1
  68. package/dist/defaults/debug.js +6 -0
  69. package/dist/defaults/debug.js.map +1 -1
  70. package/dist/defaults/diagramStyle.d.ts +5 -0
  71. package/dist/defaults/diagramStyle.d.ts.map +1 -0
  72. package/dist/defaults/diagramStyle.js +29 -0
  73. package/dist/defaults/diagramStyle.js.map +1 -0
  74. package/dist/defaults/headerFooter.d.ts +20 -18
  75. package/dist/defaults/headerFooter.d.ts.map +1 -1
  76. package/dist/defaults/headerFooter.js +269 -165
  77. package/dist/defaults/headerFooter.js.map +1 -1
  78. package/dist/defaults/headings.d.ts +4 -0
  79. package/dist/defaults/headings.d.ts.map +1 -1
  80. package/dist/defaults/headings.js +65 -9
  81. package/dist/defaults/headings.js.map +1 -1
  82. package/dist/defaults/index.d.ts +5 -1
  83. package/dist/defaults/index.d.ts.map +1 -1
  84. package/dist/defaults/index.js +29 -1
  85. package/dist/defaults/index.js.map +1 -1
  86. package/dist/defaults/resourceTypes.d.ts +8 -0
  87. package/dist/defaults/resourceTypes.d.ts.map +1 -0
  88. package/dist/defaults/resourceTypes.js +43 -0
  89. package/dist/defaults/resourceTypes.js.map +1 -0
  90. package/dist/defaults/shared.d.ts.map +1 -1
  91. package/dist/defaults/shared.js +40 -0
  92. package/dist/defaults/shared.js.map +1 -1
  93. package/dist/defaults/tableStyle.d.ts +11 -0
  94. package/dist/defaults/tableStyle.d.ts.map +1 -0
  95. package/dist/defaults/tableStyle.js +114 -0
  96. package/dist/defaults/tableStyle.js.map +1 -0
  97. package/dist/design/layout.d.ts +94 -0
  98. package/dist/design/layout.d.ts.map +1 -0
  99. package/dist/design/layout.js +642 -0
  100. package/dist/design/layout.js.map +1 -0
  101. package/dist/design/placeholders.d.ts +29 -0
  102. package/dist/design/placeholders.d.ts.map +1 -0
  103. package/dist/design/placeholders.js +126 -0
  104. package/dist/design/placeholders.js.map +1 -0
  105. package/dist/html-backend.d.ts +6 -0
  106. package/dist/html-backend.d.ts.map +1 -1
  107. package/dist/html-backend.js +274 -10
  108. package/dist/html-backend.js.map +1 -1
  109. package/dist/index.d.ts +14 -5
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +8 -2
  112. package/dist/index.js.map +1 -1
  113. package/dist/knuthPlass/breakpoints.d.ts.map +1 -1
  114. package/dist/knuthPlass/breakpoints.js +41 -98
  115. package/dist/knuthPlass/breakpoints.js.map +1 -1
  116. package/dist/knuthPlass/richAdapter.d.ts +1 -0
  117. package/dist/knuthPlass/richAdapter.d.ts.map +1 -1
  118. package/dist/knuthPlass/richAdapter.js +2 -1
  119. package/dist/knuthPlass/richAdapter.js.map +1 -1
  120. package/dist/measure/cache.js +1 -1
  121. package/dist/measure/cache.js.map +1 -1
  122. package/dist/measure/canvas.d.ts +3 -0
  123. package/dist/measure/canvas.d.ts.map +1 -1
  124. package/dist/measure/canvas.js +40 -2
  125. package/dist/measure/canvas.js.map +1 -1
  126. package/dist/measure/font.d.ts +3 -2
  127. package/dist/measure/font.d.ts.map +1 -1
  128. package/dist/measure/font.js +5 -2
  129. package/dist/measure/font.js.map +1 -1
  130. package/dist/measure/plain.d.ts.map +1 -1
  131. package/dist/measure/plain.js +14 -7
  132. package/dist/measure/plain.js.map +1 -1
  133. package/dist/measure/rich.d.ts +7 -0
  134. package/dist/measure/rich.d.ts.map +1 -1
  135. package/dist/measure/rich.js +34 -7
  136. package/dist/measure/rich.js.map +1 -1
  137. package/dist/numbering.d.ts +16 -0
  138. package/dist/numbering.d.ts.map +1 -1
  139. package/dist/numbering.js +28 -18
  140. package/dist/numbering.js.map +1 -1
  141. package/dist/parse/blockParser.d.ts.map +1 -1
  142. package/dist/parse/blockParser.js +37 -9
  143. package/dist/parse/blockParser.js.map +1 -1
  144. package/dist/parse/injectSpans.d.ts +9 -0
  145. package/dist/parse/injectSpans.d.ts.map +1 -0
  146. package/dist/parse/injectSpans.js +35 -0
  147. package/dist/parse/injectSpans.js.map +1 -0
  148. package/dist/parse/inlineFormatting.d.ts +38 -0
  149. package/dist/parse/inlineFormatting.d.ts.map +1 -1
  150. package/dist/parse/inlineFormatting.js +58 -0
  151. package/dist/parse/inlineFormatting.js.map +1 -1
  152. package/dist/parse/inlineMath.d.ts.map +1 -1
  153. package/dist/parse/inlineMath.js +26 -43
  154. package/dist/parse/inlineMath.js.map +1 -1
  155. package/dist/parse/sourceMapping.d.ts.map +1 -1
  156. package/dist/parse/sourceMapping.js +34 -7
  157. package/dist/parse/sourceMapping.js.map +1 -1
  158. package/dist/parse/types.d.ts +20 -1
  159. package/dist/parse/types.d.ts.map +1 -1
  160. package/dist/pipeline/build.d.ts.map +1 -1
  161. package/dist/pipeline/build.js +521 -28
  162. package/dist/pipeline/build.js.map +1 -1
  163. package/dist/pipeline/buildBlockKind.d.ts +14 -0
  164. package/dist/pipeline/buildBlockKind.d.ts.map +1 -1
  165. package/dist/pipeline/buildBlockKind.js +16 -1
  166. package/dist/pipeline/buildBlockKind.js.map +1 -1
  167. package/dist/pipeline/buildHelpers.d.ts.map +1 -1
  168. package/dist/pipeline/buildHelpers.js +7 -1
  169. package/dist/pipeline/buildHelpers.js.map +1 -1
  170. package/dist/pipeline/buildMeasurement.d.ts +17 -1
  171. package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
  172. package/dist/pipeline/buildMeasurement.js +32 -0
  173. package/dist/pipeline/buildMeasurement.js.map +1 -1
  174. package/dist/pipeline/columnBalancing.d.ts +75 -0
  175. package/dist/pipeline/columnBalancing.d.ts.map +1 -0
  176. package/dist/pipeline/columnBalancing.js +125 -0
  177. package/dist/pipeline/columnBalancing.js.map +1 -0
  178. package/dist/pipeline/config.d.ts +4 -1
  179. package/dist/pipeline/config.d.ts.map +1 -1
  180. package/dist/pipeline/config.js +12 -1
  181. package/dist/pipeline/config.js.map +1 -1
  182. package/dist/pipeline/floatPlacement.d.ts +45 -0
  183. package/dist/pipeline/floatPlacement.d.ts.map +1 -0
  184. package/dist/pipeline/floatPlacement.js +68 -0
  185. package/dist/pipeline/floatPlacement.js.map +1 -0
  186. package/dist/pipeline/headerFooter.d.ts +23 -7
  187. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  188. package/dist/pipeline/headerFooter.js +260 -100
  189. package/dist/pipeline/headerFooter.js.map +1 -1
  190. package/dist/pipeline/lists.d.ts +4 -9
  191. package/dist/pipeline/lists.d.ts.map +1 -1
  192. package/dist/pipeline/lists.js +24 -42
  193. package/dist/pipeline/lists.js.map +1 -1
  194. package/dist/pipeline/placeholders.d.ts +6 -0
  195. package/dist/pipeline/placeholders.d.ts.map +1 -1
  196. package/dist/pipeline/placeholders.js +18 -5
  197. package/dist/pipeline/placeholders.js.map +1 -1
  198. package/dist/pipeline/placement.d.ts +15 -2
  199. package/dist/pipeline/placement.d.ts.map +1 -1
  200. package/dist/pipeline/placement.js +38 -3
  201. package/dist/pipeline/placement.js.map +1 -1
  202. package/dist/pipeline/resourceLayout.d.ts +58 -0
  203. package/dist/pipeline/resourceLayout.d.ts.map +1 -0
  204. package/dist/pipeline/resourceLayout.js +338 -0
  205. package/dist/pipeline/resourceLayout.js.map +1 -0
  206. package/dist/pipeline/resourceNumbering.d.ts +54 -0
  207. package/dist/pipeline/resourceNumbering.d.ts.map +1 -0
  208. package/dist/pipeline/resourceNumbering.js +218 -0
  209. package/dist/pipeline/resourceNumbering.js.map +1 -0
  210. package/dist/pipeline/styles.d.ts +6 -0
  211. package/dist/pipeline/styles.d.ts.map +1 -1
  212. package/dist/pipeline/styles.js +1 -1
  213. package/dist/pipeline/styles.js.map +1 -1
  214. package/dist/svg/singleInk.d.ts +10 -0
  215. package/dist/svg/singleInk.d.ts.map +1 -0
  216. package/dist/svg/singleInk.js +86 -0
  217. package/dist/svg/singleInk.js.map +1 -0
  218. package/dist/table/model.d.ts +53 -0
  219. package/dist/table/model.d.ts.map +1 -0
  220. package/dist/table/model.js +253 -0
  221. package/dist/table/model.js.map +1 -0
  222. package/dist/types.d.ts +429 -41
  223. package/dist/types.d.ts.map +1 -1
  224. package/dist/vdt.d.ts +181 -18
  225. package/dist/vdt.d.ts.map +1 -1
  226. package/dist/vdt.js +34 -0
  227. package/dist/vdt.js.map +1 -1
  228. package/package.json +6 -6
@@ -1,6 +1,9 @@
1
- import type { PostextConfig } from '../types';
1
+ import type { PostextConfig, ResolvedHeadingLevelConfig } from '../types';
2
2
  import { type BoundingBox, type ResolvedConfig } from '../vdt';
3
3
  export declare function resolveAllConfig(rawConfig?: PostextConfig): ResolvedConfig;
4
+ /** Index heading-level configs by level so per-block lookups in the
5
+ * placement loop are O(1) instead of a linear `.find()`. */
6
+ export declare function buildHeadingLevelMap(resolved: ResolvedConfig): Map<number, ResolvedHeadingLevelConfig>;
4
7
  export declare function computeBaselineGrid(resolved: ResolvedConfig): number;
5
8
  export declare function computeColumnBboxes(contentArea: BoundingBox, resolved: ResolvedConfig): BoundingBox[];
6
9
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/pipeline/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAc9C,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAElF,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAe1E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAUpE;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,cAAc,GACvB,WAAW,EAAE,CAyBf"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/pipeline/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAiB1E,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAElF,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAkB1E;AAED;6DAC6D;AAC7D,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,GACvB,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAIzC;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAUpE;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,cAAc,GACvB,WAAW,EAAE,CAyBf"}
@@ -1,4 +1,4 @@
1
- import { resolvePageConfig, resolveLayoutConfig, resolveBodyTextConfig, resolveHeadingsConfig, resolveUnorderedListsConfig, resolveOrderedListsConfig, resolveMathConfig, resolveHeaderFooterConfig, applyPaletteToConfig, applyPaletteToResolvedConfig, } from '../defaults';
1
+ import { resolvePageConfig, resolveLayoutConfig, resolveBodyTextConfig, resolveHeadingsConfig, resolveTableStyleConfig, resolveCaptionStyleConfig, resolveDiagramStyleConfig, resolveUnorderedListsConfig, resolveOrderedListsConfig, resolveMathConfig, resolveHeaderFooterConfig, applyPaletteToConfig, applyPaletteToResolvedConfig, } from '../defaults';
2
2
  import { dimensionToPx } from '../units';
3
3
  import { createBoundingBox } from '../vdt';
4
4
  export function resolveAllConfig(rawConfig) {
@@ -9,6 +9,9 @@ export function resolveAllConfig(rawConfig) {
9
9
  layout: resolveLayoutConfig(config?.layout),
10
10
  bodyText,
11
11
  headings: resolveHeadingsConfig(config?.headings),
12
+ tableStyle: resolveTableStyleConfig(config?.tableStyle, bodyText),
13
+ captionStyle: resolveCaptionStyleConfig(config?.captionStyle, bodyText),
14
+ diagramStyle: resolveDiagramStyleConfig(config?.diagramStyle),
12
15
  unorderedLists: resolveUnorderedListsConfig(config?.unorderedLists, bodyText),
13
16
  orderedLists: resolveOrderedListsConfig(config?.orderedLists, bodyText),
14
17
  math: resolveMathConfig(config?.math),
@@ -17,6 +20,14 @@ export function resolveAllConfig(rawConfig) {
17
20
  };
18
21
  return applyPaletteToResolvedConfig(resolved, rawConfig?.colorPalette);
19
22
  }
23
+ /** Index heading-level configs by level so per-block lookups in the
24
+ * placement loop are O(1) instead of a linear `.find()`. */
25
+ export function buildHeadingLevelMap(resolved) {
26
+ const map = new Map();
27
+ for (const lvl of resolved.headings.levels)
28
+ map.set(lvl.level, lvl);
29
+ return map;
30
+ }
20
31
  export function computeBaselineGrid(resolved) {
21
32
  const dpi = resolved.page.dpi;
22
33
  const bodyFontSizePx = dimensionToPx(resolved.bodyText.fontSize, dpi);
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/pipeline/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/pipeline/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Float planning (issue #49 §7 — Placement, revised model).
3
+ *
4
+ * In the revised model a resource is *incorporated by reference*: the first
5
+ * time it is mentioned (an inline `:ref` or a `::resource` directive, whichever
6
+ * comes first in reading order) the engine floats it to a band at the top or
7
+ * bottom of the page near that reference. The author never places it twice and
8
+ * the running text flows past the reference uninterrupted.
9
+ *
10
+ * This module is the pure planning half: it resolves each resource's placement
11
+ * and produces the ordered list of floats with the content-block index of their
12
+ * first reference. The geometry (reserving page bands, positioning the float
13
+ * blocks, deferring overflow to the next page) lives in the build pipeline,
14
+ * which owns the VDT and measurement context.
15
+ */
16
+ import type { ContentBlock } from '../parse';
17
+ import type { Resource, ResourceType, ResourceFloatPosition, ResourceFloatSpan } from '../types';
18
+ /** Resolved placement for a resource: never `undefined` fields. */
19
+ export interface ResolvedPlacement {
20
+ position: ResourceFloatPosition;
21
+ span: ResourceFloatSpan;
22
+ }
23
+ /** A planned float: the resource, its resolved placement, and the index of the
24
+ * content block where it is first referenced (its anchor). */
25
+ export interface PlannedFloat {
26
+ resourceId: string;
27
+ firstBlockIdx: number;
28
+ position: 'top' | 'bottom';
29
+ span: ResourceFloatSpan;
30
+ }
31
+ /** Resolve a resource's placement: own `placement` → its type's
32
+ * `defaultPlacement` → the built-in default (`top` / `column`). */
33
+ export declare function resolveResourcePlacement(resource: Resource, type: ResourceType | undefined): ResolvedPlacement;
34
+ /**
35
+ * Walk the parsed blocks in reading order and produce the floats to place, in
36
+ * first-reference order. A resource is floated when its resolved position is
37
+ * `'top'` or `'bottom'`; `'here'` resources are left for inline `::resource`
38
+ * placement and are not returned here. Resources with an unknown id or type are
39
+ * skipped (the warnings phase surfaces those).
40
+ */
41
+ export declare function computeFloatPlan(blocks: ContentBlock[], resources: Resource[], resourceTypes: ResourceType[]): PlannedFloat[];
42
+ /** The set of resource ids that float (so the build loop can skip their inline
43
+ * `::resource` placement). Derived from a {@link computeFloatPlan} result. */
44
+ export declare function floatedResourceIds(plan: PlannedFloat[]): Set<string>;
45
+ //# sourceMappingURL=floatPlacement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floatPlacement.d.ts","sourceRoot":"","sources":["../../src/pipeline/floatPlacement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB,mEAAmE;AACnE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;+DAC+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC3B,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;oEACoE;AACpE,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,YAAY,GAAG,SAAS,GAC7B,iBAAiB,CAKnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,EAAE,EACtB,SAAS,EAAE,QAAQ,EAAE,EACrB,aAAa,EAAE,YAAY,EAAE,GAC5B,YAAY,EAAE,CA6BhB;AAED;+EAC+E;AAC/E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAEpE"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Float planning (issue #49 §7 — Placement, revised model).
3
+ *
4
+ * In the revised model a resource is *incorporated by reference*: the first
5
+ * time it is mentioned (an inline `:ref` or a `::resource` directive, whichever
6
+ * comes first in reading order) the engine floats it to a band at the top or
7
+ * bottom of the page near that reference. The author never places it twice and
8
+ * the running text flows past the reference uninterrupted.
9
+ *
10
+ * This module is the pure planning half: it resolves each resource's placement
11
+ * and produces the ordered list of floats with the content-block index of their
12
+ * first reference. The geometry (reserving page bands, positioning the float
13
+ * blocks, deferring overflow to the next page) lives in the build pipeline,
14
+ * which owns the VDT and measurement context.
15
+ */
16
+ /** Resolve a resource's placement: own `placement` → its type's
17
+ * `defaultPlacement` → the built-in default (`top` / `column`). */
18
+ export function resolveResourcePlacement(resource, type) {
19
+ const position = resource.placement?.position ?? type?.defaultPlacement?.position ?? 'top';
20
+ const span = resource.placement?.span ?? type?.defaultPlacement?.span ?? 'column';
21
+ return { position, span };
22
+ }
23
+ /**
24
+ * Walk the parsed blocks in reading order and produce the floats to place, in
25
+ * first-reference order. A resource is floated when its resolved position is
26
+ * `'top'` or `'bottom'`; `'here'` resources are left for inline `::resource`
27
+ * placement and are not returned here. Resources with an unknown id or type are
28
+ * skipped (the warnings phase surfaces those).
29
+ */
30
+ export function computeFloatPlan(blocks, resources, resourceTypes) {
31
+ const resourceById = new Map();
32
+ for (const r of resources)
33
+ resourceById.set(r.id, r);
34
+ const typeById = new Map();
35
+ for (const t of resourceTypes)
36
+ typeById.set(t.id, t);
37
+ const plan = [];
38
+ const seen = new Set();
39
+ const record = (resourceId, blockIdx) => {
40
+ if (seen.has(resourceId))
41
+ return;
42
+ seen.add(resourceId);
43
+ const resource = resourceById.get(resourceId);
44
+ if (!resource)
45
+ return;
46
+ const type = typeById.get(resource.typeId);
47
+ const { position, span } = resolveResourcePlacement(resource, type);
48
+ if (position === 'here')
49
+ return;
50
+ plan.push({ resourceId, firstBlockIdx: blockIdx, position, span });
51
+ };
52
+ for (let i = 0; i < blocks.length; i++) {
53
+ const b = blocks[i];
54
+ if (b.type === 'resourceBlock' && b.resourceId)
55
+ record(b.resourceId, i);
56
+ for (const span of b.spans) {
57
+ if (span.ref?.resourceId)
58
+ record(span.ref.resourceId, i);
59
+ }
60
+ }
61
+ return plan;
62
+ }
63
+ /** The set of resource ids that float (so the build loop can skip their inline
64
+ * `::resource` placement). Derived from a {@link computeFloatPlan} result. */
65
+ export function floatedResourceIds(plan) {
66
+ return new Set(plan.map((f) => f.resourceId));
67
+ }
68
+ //# sourceMappingURL=floatPlacement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floatPlacement.js","sourceRoot":"","sources":["../../src/pipeline/floatPlacement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyBH;oEACoE;AACpE,MAAM,UAAU,wBAAwB,CACtC,QAAkB,EAClB,IAA8B;IAE9B,MAAM,QAAQ,GACZ,QAAQ,CAAC,SAAS,EAAE,QAAQ,IAAI,IAAI,EAAE,gBAAgB,EAAE,QAAQ,IAAI,KAAK,CAAC;IAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,EAAE,IAAI,IAAI,IAAI,EAAE,gBAAgB,EAAE,IAAI,IAAI,QAAQ,CAAC;IAClF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAsB,EACtB,SAAqB,EACrB,aAA6B;IAE7B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,SAAS;QAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,aAAa;QAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAErD,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,MAAM,GAAG,CAAC,UAAkB,EAAE,QAAgB,EAAE,EAAE;QACtD,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO;QACjC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrB,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,wBAAwB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACpE,IAAI,QAAQ,KAAK,MAAM;YAAE,OAAO;QAChC,IAAI,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QACrB,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,UAAU;YAAE,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QACxE,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,GAAG,EAAE,UAAU;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,IAAoB;IACrD,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -1,11 +1,27 @@
1
- import { type VDTDocument } from '../vdt';
1
+ import type { DocumentMetadata, ResolvedDesignSlot, ResolvedHeadingLevelConfig } from '../types';
2
+ import { type VDTDocument, type VDTDesignSlot } from '../vdt';
3
+ import type { DesignPlaceholderContext } from '../design/placeholders';
4
+ /** Measure the natural bottom of a heading's advanced-design slot when laid
5
+ * out against a container of the given `width` with unbounded height.
6
+ * Returns 0 if the level has no advanced design or the slot is empty. Used
7
+ * during body layout to enlarge a heading block's reserved height so
8
+ * subsequent blocks sit below the actual bottom of the design content
9
+ * rather than below the natural text bottom. Applies to both in-column
10
+ * headings (overlay in block bbox) and page-spanning openers (width is the
11
+ * full content area; passed in by the caller). */
12
+ export declare function measureHeadingAdvancedDesignHeight(level: ResolvedHeadingLevelConfig, heading: {
13
+ titleText: string;
14
+ formattedNumber: string;
15
+ chapterNumber: string;
16
+ }, width: number, dpi: number, metadata: DocumentMetadata, pageIndex: number): number;
17
+ export declare function layoutSlotToVdt(slot: ResolvedDesignSlot, container: {
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ }, pageIndex: number, placeholders: DesignPlaceholderContext, dpi: number): VDTDesignSlot | undefined;
2
23
  /**
3
- * After body placement finishes, attach header/footer slots (with their
4
- * rendered text/rule blocks) to every page of the document.
5
- *
6
- * Headers and footers are rendered inside the existing page margins — they
7
- * do not consume body space. Header elements stack upward from the body's
8
- * top edge; footer elements stack downward from the body's bottom edge.
24
+ * After body placement finishes, attach header/footer slots to every page.
9
25
  */
10
26
  export declare function buildHeadersAndFooters(doc: VDTDocument): void;
11
27
  //# sourceMappingURL=headerFooter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"headerFooter.d.ts","sourceRoot":"","sources":["../../src/pipeline/headerFooter.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,WAAW,EAMjB,MAAM,QAAQ,CAAC;AAsJhB;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAqC7D"}
1
+ {"version":3,"file":"headerFooter.d.ts","sourceRoot":"","sources":["../../src/pipeline/headerFooter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAA6B,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAC5H,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EAMnB,MAAM,QAAQ,CAAC;AAWhB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AA0GvE;;;;;;;mDAOmD;AACnD,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,0BAA0B,EACjC,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EAC9E,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,MAAM,GAChB,MAAM,CAsBR;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,kBAAkB,EACxB,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAClE,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,wBAAwB,EACtC,GAAG,EAAE,MAAM,GACV,aAAa,GAAG,SAAS,CAQ3B;AAsED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAmH7D"}
@@ -1,134 +1,294 @@
1
1
  import { createBoundingBox, } from '../vdt';
2
- import { dimensionToPx } from '../units';
3
- import { buildFontString, measureTextWidth } from '../measure';
4
- import { computeChapterTitles, resolvePlaceholders } from './placeholders';
2
+ import { computeChapterTitles, computeChapterNumbers } from './placeholders';
5
3
  import { computePageMetrics } from './buildHelpers';
6
- function pageMatchesParity(pageIndex, parity) {
7
- if (parity === 'all')
8
- return true;
9
- // Convention: use page-number parity (page 1 = odd).
10
- const pageNumber = pageIndex + 1;
11
- const isOdd = pageNumber % 2 === 1;
12
- return parity === 'odd' ? isOdd : !isOdd;
13
- }
14
- function elementHeightPx(el, dpi) {
15
- if (el.kind === 'text') {
16
- const fontSizePx = dimensionToPx(el.fontSize, dpi);
17
- // Approximate line height as 1.2 × font size.
18
- return fontSizePx * 1.2;
19
- }
20
- return dimensionToPx(el.thickness, dpi);
4
+ import { buildHeadingLevelMap } from './config';
5
+ import { layoutDesignSlot, } from '../design/layout';
6
+ /** Slots live in the page margin area between the body edge and the page
7
+ * edge. Header slot container: from `top-margin` edge down to body top.
8
+ * Footer slot container: from body bottom down to `bottom-margin` edge.
9
+ *
10
+ * We use a larger container (the full page vertical extent of the margin
11
+ * area) so that anchors like `bottom-center` / `bottom-right` for headers
12
+ * and `top-*` for footers align with the body edge (the body-facing side
13
+ * of the margin area) rather than the outer page edge.
14
+ *
15
+ * This matches the legacy semantics: `marginFromBody` was "distance from
16
+ * body edge" — the migration translates that to an offset of the same
17
+ * magnitude from the body-facing edge. */
18
+ function headerContainerBbox(contentArea, _pageHeight) {
19
+ // Header: spans from top of page to body top.
20
+ return { x: contentArea.x, y: 0, width: contentArea.width, height: contentArea.y };
21
21
  }
22
- function filterElementsByParity(slot, pageIndex) {
23
- return slot.elements.filter((el) => pageMatchesParity(pageIndex, el.parity));
22
+ function footerContainerBbox(contentArea, pageHeight) {
23
+ const bodyBottom = contentArea.y + contentArea.height;
24
+ return {
25
+ x: contentArea.x,
26
+ y: bodyBottom,
27
+ width: contentArea.width,
28
+ height: Math.max(0, pageHeight - bodyBottom),
29
+ };
24
30
  }
25
- function xForAlign(align, contentX, contentWidth, elementWidth, edgeInsetPx) {
31
+ function textAlignOffsetX(align, contentWidth, lineWidth) {
26
32
  if (align === 'left')
27
- return contentX + edgeInsetPx;
33
+ return 0;
28
34
  if (align === 'right')
29
- return contentX + contentWidth - elementWidth - edgeInsetPx;
30
- return contentX + (contentWidth - elementWidth) / 2;
35
+ return Math.max(0, contentWidth - lineWidth);
36
+ return Math.max(0, (contentWidth - lineWidth) / 2);
37
+ }
38
+ function primitiveToBlock(prim) {
39
+ if (prim.kind === 'text')
40
+ return textPrimitiveToBlock(prim);
41
+ if (prim.kind === 'rule')
42
+ return rulePrimitiveToBlock(prim);
43
+ return boxPrimitiveToBlock(prim);
31
44
  }
32
- function buildTextBlock(el, resolvedText, contentX, contentWidth, elementTop, dpi) {
33
- const fontSizePx = dimensionToPx(el.fontSize, dpi);
34
- const weight = el.fontWeight === 400 ? 'normal' : String(el.fontWeight);
35
- const style = el.italic ? 'italic' : 'normal';
36
- const fontString = buildFontString(el.fontFamily, fontSizePx, weight, style);
37
- const textWidth = measureTextWidth(resolvedText, fontString);
38
- const blockHeight = fontSizePx * 1.2;
39
- const edgeInsetPx = dimensionToPx(el.marginFromEdge, dpi);
40
- const x = xForAlign(el.align, contentX, contentWidth, textWidth, edgeInsetPx);
41
- // Baseline at ~0.8 of the block height, matching body text convention.
42
- const baseline = elementTop + blockHeight * 0.8;
45
+ function textPrimitiveToBlock(prim) {
46
+ const totalContentHeight = prim.lines.reduce((s, l) => Math.max(s, l.topY + l.height), 0);
47
+ const vOffset = prim.verticalAlign === 'top' ? 0
48
+ : prim.verticalAlign === 'bottom' ? Math.max(0, prim.contentHeight - totalContentHeight)
49
+ : Math.max(0, (prim.contentHeight - totalContentHeight) / 2);
50
+ const lines = prim.lines.map((l) => ({
51
+ text: l.text,
52
+ xOffset: prim.contentX + textAlignOffsetX(prim.align, prim.contentWidth, l.width),
53
+ baselineY: prim.y + prim.contentY + vOffset + l.baselineY,
54
+ width: l.width,
55
+ }));
43
56
  return {
44
57
  kind: 'text',
45
- bbox: createBoundingBox(x, elementTop, textWidth, blockHeight),
46
- text: resolvedText,
47
- align: el.align,
48
- fontString,
49
- color: el.color.hex,
50
- baseline,
58
+ bbox: createBoundingBox(prim.x, prim.y, prim.width, prim.height),
59
+ fontString: prim.fontString,
60
+ color: prim.color,
61
+ lines,
62
+ box: prim.box
63
+ ? {
64
+ backgroundColor: prim.box.backgroundColor,
65
+ borderColor: prim.box.borderColor,
66
+ borderWidthPx: prim.box.borderWidthPx,
67
+ borderRadiusPx: prim.box.borderRadiusPx,
68
+ }
69
+ : undefined,
70
+ clip: prim.needsClip,
51
71
  };
52
72
  }
53
- function buildRuleBlock(el, contentX, contentWidth, elementTop, dpi) {
54
- const thicknessPx = dimensionToPx(el.thickness, dpi);
55
- const widthPx = el.width === 'full' ? contentWidth : dimensionToPx(el.width, dpi);
56
- // `marginFromEdge` only meaningful for non-full rules — a full-width rule
57
- // already spans the content, so the inset would just crop it asymmetrically.
58
- const edgeInsetPx = el.width === 'full' ? 0 : dimensionToPx(el.marginFromEdge, dpi);
59
- const x = xForAlign(el.align, contentX, contentWidth, widthPx, edgeInsetPx);
73
+ function rulePrimitiveToBlock(prim) {
60
74
  return {
61
75
  kind: 'rule',
62
- bbox: createBoundingBox(x, elementTop, widthPx, thicknessPx),
63
- color: el.color.hex,
64
- thicknessPx,
76
+ bbox: createBoundingBox(prim.x, prim.y, prim.width, prim.height),
77
+ color: prim.color,
78
+ thicknessPx: prim.thicknessPx,
79
+ direction: prim.direction,
65
80
  };
66
81
  }
67
- /**
68
- * Layout elements for one slot on one page.
69
- *
70
- * Each element's `marginFromBody` is an absolute distance between its
71
- * body-facing edge and the body edge — elements are independent and never
72
- * push each other. Two elements with the same `marginFromBody` will overlap;
73
- * that's on the author to avoid.
74
- */
75
- function layoutSlot(slot, pageIndex, contentX, contentWidth, anchor, anchorY, metadata, allPages, chapterTitleByPageIndex, dpi) {
76
- const elements = filterElementsByParity(slot, pageIndex);
77
- if (elements.length === 0)
78
- return undefined;
79
- const page = allPages[pageIndex];
80
- const ctx = {
81
- page,
82
- allPages,
82
+ function boxPrimitiveToBlock(prim) {
83
+ return {
84
+ kind: 'box',
85
+ bbox: createBoundingBox(prim.x, prim.y, prim.width, prim.height),
86
+ box: {
87
+ backgroundColor: prim.box.backgroundColor,
88
+ borderColor: prim.box.borderColor,
89
+ borderWidthPx: prim.box.borderWidthPx,
90
+ borderRadiusPx: prim.box.borderRadiusPx,
91
+ },
92
+ };
93
+ }
94
+ /** Measure the natural bottom of a heading's advanced-design slot when laid
95
+ * out against a container of the given `width` with unbounded height.
96
+ * Returns 0 if the level has no advanced design or the slot is empty. Used
97
+ * during body layout to enlarge a heading block's reserved height so
98
+ * subsequent blocks sit below the actual bottom of the design content
99
+ * rather than below the natural text bottom. Applies to both in-column
100
+ * headings (overlay in block bbox) and page-spanning openers (width is the
101
+ * full content area; passed in by the caller). */
102
+ export function measureHeadingAdvancedDesignHeight(level, heading, width, dpi, metadata, pageIndex) {
103
+ if (!level.advancedDesign.enabled)
104
+ return 0;
105
+ if (level.advancedDesign.slot.elements.length === 0)
106
+ return 0;
107
+ const stubPage = { index: pageIndex, pageLabel: '1' };
108
+ const placeholders = {
109
+ kind: 'heading',
110
+ page: stubPage,
111
+ allPages: [stubPage],
83
112
  metadata,
84
- chapterTitleByPageIndex,
113
+ chapterTitleByPageIndex: [],
114
+ heading,
85
115
  };
86
- const blocks = [];
87
- let minY = anchorY;
88
- let maxBottom = anchorY;
89
- for (const el of elements) {
90
- const height = elementHeightPx(el, dpi);
91
- const marginPx = dimensionToPx(el.marginFromBody, dpi);
92
- // For a header (anchor='bottom' at body top): element's bottom edge sits
93
- // `marginPx` above the body. For a footer (anchor='top' at body bottom):
94
- // element's top edge sits `marginPx` below the body.
95
- const elementTop = anchor === 'bottom' ? anchorY - marginPx - height : anchorY + marginPx;
96
- if (el.kind === 'text') {
97
- const { text } = resolvePlaceholders(el.content, ctx);
98
- if (text.length > 0) {
99
- blocks.push(buildTextBlock(el, text, contentX, contentWidth, elementTop, dpi));
100
- }
101
- }
102
- else {
103
- blocks.push(buildRuleBlock(el, contentX, contentWidth, elementTop, dpi));
104
- }
105
- minY = Math.min(minY, elementTop);
106
- maxBottom = Math.max(maxBottom, elementTop + height);
116
+ const result = layoutDesignSlot(level.advancedDesign.slot, { container: { x: 0, y: 0, width, height: 1e6 }, dpi, placeholders }, pageIndex);
117
+ let bottom = 0;
118
+ for (const prim of result.primitives) {
119
+ bottom = Math.max(bottom, prim.y + prim.height);
107
120
  }
108
- if (blocks.length === 0)
121
+ return bottom;
122
+ }
123
+ export function layoutSlotToVdt(slot, container, pageIndex, placeholders, dpi) {
124
+ const result = layoutDesignSlot(slot, { container, dpi, placeholders }, pageIndex);
125
+ if (result.primitives.length === 0)
109
126
  return undefined;
110
- const slotBbox = createBoundingBox(contentX, minY, contentWidth, maxBottom - minY);
111
- return { bbox: slotBbox, blocks };
127
+ const blocks = result.primitives.map(primitiveToBlock);
128
+ return {
129
+ bbox: createBoundingBox(container.x, container.y, container.width, container.height),
130
+ blocks,
131
+ };
132
+ }
133
+ /** Container bbox for a page-spanning heading opener band: occupies the
134
+ * vertical band the heading block reserved in its column, extended
135
+ * horizontally across the full content area (both columns) — but NOT beyond
136
+ * the page margins. */
137
+ function openerContainerBbox(block, contentArea) {
138
+ return { x: contentArea.x, y: block.bbox.y, width: contentArea.width, height: block.bbox.height };
139
+ }
140
+ /** Find the first heading block on `page` that belongs to a level with
141
+ * `span === 'page'`. Returns the block and info needed to resolve
142
+ * placeholders in the design slot. Triggers regardless of whether
143
+ * `advancedDesign.enabled` is true — when false, the pipeline synthesises
144
+ * a default slot from the heading level typography. */
145
+ function findOpenerHeading(page, headingLevelByNumber) {
146
+ for (const col of page.columns) {
147
+ for (const block of col.blocks) {
148
+ if (block.type !== 'heading' || !block.headingLevel)
149
+ continue;
150
+ const lvl = headingLevelByNumber.get(block.headingLevel);
151
+ if (!lvl)
152
+ continue;
153
+ if (lvl.span !== 'page')
154
+ continue;
155
+ const full = block.lines
156
+ .map((ln) => (ln.segments ?? []).map((s) => s.text).join(''))
157
+ .join(' ');
158
+ const pref = block.numberPrefix ?? '';
159
+ const title = pref && full.startsWith(`${pref} `) ? full.slice(pref.length + 1) : full;
160
+ return { block, level: block.headingLevel, titleText: title, numberPrefix: pref };
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ /** Build a synthesised default design slot for a `span: 'page'` heading when
166
+ * the user has not configured an `advancedDesign.slot`. Renders as a single
167
+ * text element, anchored to fill the full-page-width container, using the
168
+ * heading level's resolved typography. Emits `{formattedNumber} {titleText}`
169
+ * when the heading carries a numberPrefix, otherwise `{titleText}`. */
170
+ function synthesiseDefaultOpenerSlot(level, hasNumberPrefix) {
171
+ const content = hasNumberPrefix ? '{formattedNumber} {titleText}' : '{titleText}';
172
+ const textEl = {
173
+ kind: 'text',
174
+ id: 'defaultHeadingOpener',
175
+ parity: 'all',
176
+ placement: {
177
+ anchor: { to: 'container', edge: 'top-left' },
178
+ offset: { x: { value: 0, unit: 'pt' }, y: { value: 0, unit: 'pt' } },
179
+ size: { width: 'fill', height: 'fill' },
180
+ },
181
+ content,
182
+ fontFamily: level.fontFamily,
183
+ fontSize: level.fontSize,
184
+ fontWeight: level.fontWeight,
185
+ italic: level.italic,
186
+ color: level.color,
187
+ align: 'left',
188
+ verticalAlign: 'middle',
189
+ lineHeight: level.lineHeight.unit === 'em' ? level.lineHeight.value : 1.2,
190
+ overflow: 'wrap',
191
+ hyphenate: true,
192
+ };
193
+ return { elements: [textEl] };
112
194
  }
113
195
  /**
114
- * After body placement finishes, attach header/footer slots (with their
115
- * rendered text/rule blocks) to every page of the document.
116
- *
117
- * Headers and footers are rendered inside the existing page margins — they
118
- * do not consume body space. Header elements stack upward from the body's
119
- * top edge; footer elements stack downward from the body's bottom edge.
196
+ * After body placement finishes, attach header/footer slots to every page.
120
197
  */
121
198
  export function buildHeadersAndFooters(doc) {
122
199
  const resolved = doc.config;
123
200
  const dpi = resolved.page.dpi;
124
201
  const { contentArea } = computePageMetrics(resolved);
125
202
  const chapterTitleByPageIndex = computeChapterTitles(doc.blocks, doc.pages.length, doc.pages);
203
+ const chapterNumberByPageIndex = computeChapterNumbers(doc.blocks, doc.pages.length, doc.pages);
204
+ const headingLevelByNumber = buildHeadingLevelMap(resolved);
126
205
  for (const page of doc.pages) {
127
206
  if (resolved.header.elements.length > 0) {
128
- page.header = layoutSlot(resolved.header, page.index, contentArea.x, contentArea.width, 'bottom', contentArea.y, doc.metadata, doc.pages, chapterTitleByPageIndex, dpi);
207
+ const placeholders = {
208
+ kind: 'header',
209
+ page,
210
+ allPages: doc.pages,
211
+ metadata: doc.metadata,
212
+ chapterTitleByPageIndex,
213
+ };
214
+ page.header = layoutSlotToVdt(resolved.header, headerContainerBbox(contentArea, page.height), page.index, placeholders, dpi);
215
+ }
216
+ const opener = findOpenerHeading(page, headingLevelByNumber);
217
+ if (opener) {
218
+ const level = headingLevelByNumber.get(opener.level);
219
+ if (level) {
220
+ const slot = level.advancedDesign.enabled && level.advancedDesign.slot.elements.length > 0
221
+ ? level.advancedDesign.slot
222
+ : synthesiseDefaultOpenerSlot(level, opener.numberPrefix.length > 0);
223
+ const placeholders = {
224
+ kind: 'heading',
225
+ page,
226
+ allPages: doc.pages,
227
+ metadata: doc.metadata,
228
+ chapterTitleByPageIndex,
229
+ heading: {
230
+ titleText: opener.titleText,
231
+ formattedNumber: opener.numberPrefix,
232
+ chapterNumber: chapterNumberByPageIndex[page.index] ?? '',
233
+ },
234
+ };
235
+ page.openerBand = layoutSlotToVdt(slot, openerContainerBbox(opener.block, contentArea), page.index, placeholders, dpi);
236
+ if (page.openerBand) {
237
+ opener.block.hidden = true;
238
+ }
239
+ }
240
+ }
241
+ // In-column heading advanced-design overlays: any heading whose level
242
+ // has `advancedDesign.enabled` and a non-empty slot gets its default
243
+ // text rendering replaced with a design overlay laid out inside the
244
+ // block's bbox. `span: 'page'` headings are handled via `openerBand`
245
+ // above (and are already `hidden`), so they're skipped here.
246
+ for (const col of page.columns) {
247
+ for (const block of col.blocks) {
248
+ if (block.hidden)
249
+ continue;
250
+ if (block.type !== 'heading' || !block.headingLevel)
251
+ continue;
252
+ const lvl = headingLevelByNumber.get(block.headingLevel);
253
+ if (!lvl)
254
+ continue;
255
+ if (lvl.span === 'page')
256
+ continue;
257
+ if (!lvl.advancedDesign.enabled)
258
+ continue;
259
+ if (lvl.advancedDesign.slot.elements.length === 0)
260
+ continue;
261
+ const full = block.lines
262
+ .map((ln) => (ln.segments ?? []).map((s) => s.text).join(''))
263
+ .join(' ');
264
+ const pref = block.numberPrefix ?? '';
265
+ const title = pref && full.startsWith(`${pref} `) ? full.slice(pref.length + 1) : full;
266
+ const placeholders = {
267
+ kind: 'heading',
268
+ page,
269
+ allPages: doc.pages,
270
+ metadata: doc.metadata,
271
+ chapterTitleByPageIndex,
272
+ heading: {
273
+ titleText: title,
274
+ formattedNumber: pref,
275
+ chapterNumber: chapterNumberByPageIndex[page.index] ?? '',
276
+ },
277
+ };
278
+ const overlay = layoutSlotToVdt(lvl.advancedDesign.slot, { x: block.bbox.x, y: block.bbox.y, width: block.bbox.width, height: block.bbox.height }, page.index, placeholders, dpi);
279
+ if (overlay)
280
+ block.designOverlay = overlay;
281
+ }
129
282
  }
130
283
  if (resolved.footer.elements.length > 0) {
131
- page.footer = layoutSlot(resolved.footer, page.index, contentArea.x, contentArea.width, 'top', contentArea.y + contentArea.height, doc.metadata, doc.pages, chapterTitleByPageIndex, dpi);
284
+ const placeholders = {
285
+ kind: 'footer',
286
+ page,
287
+ allPages: doc.pages,
288
+ metadata: doc.metadata,
289
+ chapterTitleByPageIndex,
290
+ };
291
+ page.footer = layoutSlotToVdt(resolved.footer, footerContainerBbox(contentArea, page.height), page.index, placeholders, dpi);
132
292
  }
133
293
  }
134
294
  }