postext 1.35.1 → 1.37.0

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 (273) hide show
  1. package/dist/__bench__/composedPage.bench.test.d.ts +2 -0
  2. package/dist/__bench__/composedPage.bench.test.d.ts.map +1 -0
  3. package/dist/__bench__/composedPage.bench.test.js +95 -0
  4. package/dist/__bench__/composedPage.bench.test.js.map +1 -0
  5. package/dist/__tests__/composed/continuation.test.d.ts +2 -0
  6. package/dist/__tests__/composed/continuation.test.d.ts.map +1 -0
  7. package/dist/__tests__/composed/continuation.test.js +352 -0
  8. package/dist/__tests__/composed/continuation.test.js.map +1 -0
  9. package/dist/__tests__/composed/contrast.test.d.ts +2 -0
  10. package/dist/__tests__/composed/contrast.test.d.ts.map +1 -0
  11. package/dist/__tests__/composed/contrast.test.js +124 -0
  12. package/dist/__tests__/composed/contrast.test.js.map +1 -0
  13. package/dist/__tests__/composed/corpus.test.js +7 -1
  14. package/dist/__tests__/composed/corpus.test.js.map +1 -1
  15. package/dist/__tests__/composed/fit.test.d.ts +2 -0
  16. package/dist/__tests__/composed/fit.test.d.ts.map +1 -0
  17. package/dist/__tests__/composed/fit.test.js +408 -0
  18. package/dist/__tests__/composed/fit.test.js.map +1 -0
  19. package/dist/__tests__/composed/groups.test.js +14 -0
  20. package/dist/__tests__/composed/groups.test.js.map +1 -1
  21. package/dist/__tests__/composed/headingDesigns.test.d.ts +2 -0
  22. package/dist/__tests__/composed/headingDesigns.test.d.ts.map +1 -0
  23. package/dist/__tests__/composed/headingDesigns.test.js +262 -0
  24. package/dist/__tests__/composed/headingDesigns.test.js.map +1 -0
  25. package/dist/__tests__/composed/labels.test.d.ts +2 -0
  26. package/dist/__tests__/composed/labels.test.d.ts.map +1 -0
  27. package/dist/__tests__/composed/labels.test.js +326 -0
  28. package/dist/__tests__/composed/labels.test.js.map +1 -0
  29. package/dist/__tests__/composed/layout.test.js +2 -1
  30. package/dist/__tests__/composed/layout.test.js.map +1 -1
  31. package/dist/__tests__/composed/outsidePrint.test.js +5 -2
  32. package/dist/__tests__/composed/outsidePrint.test.js.map +1 -1
  33. package/dist/__tests__/composed/pageMemo.test.d.ts +2 -0
  34. package/dist/__tests__/composed/pageMemo.test.d.ts.map +1 -0
  35. package/dist/__tests__/composed/pageMemo.test.js +128 -0
  36. package/dist/__tests__/composed/pageMemo.test.js.map +1 -0
  37. package/dist/__tests__/composed/sourceMap.test.d.ts +2 -0
  38. package/dist/__tests__/composed/sourceMap.test.d.ts.map +1 -0
  39. package/dist/__tests__/composed/sourceMap.test.js +395 -0
  40. package/dist/__tests__/composed/sourceMap.test.js.map +1 -0
  41. package/dist/__tests__/composed/spreadLabels.test.d.ts +2 -0
  42. package/dist/__tests__/composed/spreadLabels.test.d.ts.map +1 -0
  43. package/dist/__tests__/composed/spreadLabels.test.js +131 -0
  44. package/dist/__tests__/composed/spreadLabels.test.js.map +1 -0
  45. package/dist/__tests__/composed/templateFrameRects.test.d.ts +2 -0
  46. package/dist/__tests__/composed/templateFrameRects.test.d.ts.map +1 -0
  47. package/dist/__tests__/composed/templateFrameRects.test.js +37 -0
  48. package/dist/__tests__/composed/templateFrameRects.test.js.map +1 -0
  49. package/dist/__tests__/design/imageAltText.test.js +2 -1
  50. package/dist/__tests__/design/imageAltText.test.js.map +1 -1
  51. package/dist/__tests__/exports.test.js +33 -0
  52. package/dist/__tests__/exports.test.js.map +1 -1
  53. package/dist/__tests__/migrate/fixtures/textbookPort.d.ts +14 -0
  54. package/dist/__tests__/migrate/fixtures/textbookPort.d.ts.map +1 -0
  55. package/dist/__tests__/migrate/fixtures/textbookPort.js +116 -0
  56. package/dist/__tests__/migrate/fixtures/textbookPort.js.map +1 -0
  57. package/dist/__tests__/migrate/textToMarkdown.test.d.ts +2 -0
  58. package/dist/__tests__/migrate/textToMarkdown.test.d.ts.map +1 -0
  59. package/dist/__tests__/migrate/textToMarkdown.test.js +268 -0
  60. package/dist/__tests__/migrate/textToMarkdown.test.js.map +1 -0
  61. package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts +2 -0
  62. package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts.map +1 -0
  63. package/dist/__tests__/paragraphStyleLetterSpacing.test.js +89 -0
  64. package/dist/__tests__/paragraphStyleLetterSpacing.test.js.map +1 -0
  65. package/dist/__tests__/pictureBelowPrintSize.test.d.ts +2 -0
  66. package/dist/__tests__/pictureBelowPrintSize.test.d.ts.map +1 -0
  67. package/dist/__tests__/pictureBelowPrintSize.test.js +82 -0
  68. package/dist/__tests__/pictureBelowPrintSize.test.js.map +1 -0
  69. package/dist/__tests__/pipeline/contourWrap.test.d.ts +2 -0
  70. package/dist/__tests__/pipeline/contourWrap.test.d.ts.map +1 -0
  71. package/dist/__tests__/pipeline/contourWrap.test.js +412 -0
  72. package/dist/__tests__/pipeline/contourWrap.test.js.map +1 -0
  73. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +1 -1
  74. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
  75. package/dist/__tests__/toc.test.js +3 -2
  76. package/dist/__tests__/toc.test.js.map +1 -1
  77. package/dist/bundle/codec.d.ts +16 -0
  78. package/dist/bundle/codec.d.ts.map +1 -1
  79. package/dist/bundle/codec.js +44 -2
  80. package/dist/bundle/codec.js.map +1 -1
  81. package/dist/bundle/index.d.ts +1 -1
  82. package/dist/bundle/index.d.ts.map +1 -1
  83. package/dist/bundle/index.js +1 -1
  84. package/dist/bundle/index.js.map +1 -1
  85. package/dist/canvas-backend/composed.d.ts +5 -2
  86. package/dist/canvas-backend/composed.d.ts.map +1 -1
  87. package/dist/canvas-backend/composed.js +10 -5
  88. package/dist/canvas-backend/composed.js.map +1 -1
  89. package/dist/canvas-backend/index.d.ts.map +1 -1
  90. package/dist/canvas-backend/index.js +37 -19
  91. package/dist/canvas-backend/index.js.map +1 -1
  92. package/dist/canvas-backend/renderResourceBlock.d.ts +5 -1
  93. package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
  94. package/dist/canvas-backend/renderResourceBlock.js +60 -1
  95. package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
  96. package/dist/composed/continuation.d.ts +102 -0
  97. package/dist/composed/continuation.d.ts.map +1 -0
  98. package/dist/composed/continuation.js +87 -0
  99. package/dist/composed/continuation.js.map +1 -0
  100. package/dist/composed/contour.d.ts +21 -0
  101. package/dist/composed/contour.d.ts.map +1 -0
  102. package/dist/composed/contour.js +34 -0
  103. package/dist/composed/contour.js.map +1 -0
  104. package/dist/composed/geometry.d.ts +8 -0
  105. package/dist/composed/geometry.d.ts.map +1 -1
  106. package/dist/composed/geometry.js +26 -0
  107. package/dist/composed/geometry.js.map +1 -1
  108. package/dist/composed/ids.d.ts +22 -0
  109. package/dist/composed/ids.d.ts.map +1 -0
  110. package/dist/composed/ids.js +32 -0
  111. package/dist/composed/ids.js.map +1 -0
  112. package/dist/composed/labels.d.ts +154 -0
  113. package/dist/composed/labels.d.ts.map +1 -0
  114. package/dist/composed/labels.js +524 -0
  115. package/dist/composed/labels.js.map +1 -0
  116. package/dist/composed/pageMemo.d.ts +73 -0
  117. package/dist/composed/pageMemo.d.ts.map +1 -0
  118. package/dist/composed/pageMemo.js +289 -0
  119. package/dist/composed/pageMemo.js.map +1 -0
  120. package/dist/composed/paint.d.ts +29 -9
  121. package/dist/composed/paint.d.ts.map +1 -1
  122. package/dist/composed/paint.js +41 -7
  123. package/dist/composed/paint.js.map +1 -1
  124. package/dist/composed/reflowLog.d.ts +14 -2
  125. package/dist/composed/reflowLog.d.ts.map +1 -1
  126. package/dist/composed/reflowLog.js +64 -7
  127. package/dist/composed/reflowLog.js.map +1 -1
  128. package/dist/composed/slotChecks.d.ts.map +1 -1
  129. package/dist/composed/slotChecks.js +3 -1
  130. package/dist/composed/slotChecks.js.map +1 -1
  131. package/dist/composed/sourceMap.d.ts +97 -0
  132. package/dist/composed/sourceMap.d.ts.map +1 -0
  133. package/dist/composed/sourceMap.js +748 -0
  134. package/dist/composed/sourceMap.js.map +1 -0
  135. package/dist/composed/spread.d.ts +3 -0
  136. package/dist/composed/spread.d.ts.map +1 -1
  137. package/dist/composed/spread.js +11 -12
  138. package/dist/composed/spread.js.map +1 -1
  139. package/dist/composed/templates.d.ts +1 -0
  140. package/dist/composed/templates.d.ts.map +1 -1
  141. package/dist/composed/templates.js +4 -0
  142. package/dist/composed/templates.js.map +1 -1
  143. package/dist/composed/types.d.ts +15 -32
  144. package/dist/composed/types.d.ts.map +1 -1
  145. package/dist/composed/validate.d.ts.map +1 -1
  146. package/dist/composed/validate.js +3 -2
  147. package/dist/composed/validate.js.map +1 -1
  148. package/dist/composed/wrap.d.ts.map +1 -1
  149. package/dist/composed/wrap.js +7 -1
  150. package/dist/composed/wrap.js.map +1 -1
  151. package/dist/configWarnings.d.ts +15 -0
  152. package/dist/configWarnings.d.ts.map +1 -1
  153. package/dist/configWarnings.js +29 -17
  154. package/dist/configWarnings.js.map +1 -1
  155. package/dist/contour/index.d.ts +134 -0
  156. package/dist/contour/index.d.ts.map +1 -0
  157. package/dist/contour/index.js +354 -0
  158. package/dist/contour/index.js.map +1 -0
  159. package/dist/contour/png.d.ts +16 -0
  160. package/dist/contour/png.d.ts.map +1 -0
  161. package/dist/contour/png.js +173 -0
  162. package/dist/contour/png.js.map +1 -0
  163. package/dist/contrast.d.ts +5 -3
  164. package/dist/contrast.d.ts.map +1 -1
  165. package/dist/contrast.js +46 -5
  166. package/dist/contrast.js.map +1 -1
  167. package/dist/defaults/paragraphStyles.d.ts +1 -1
  168. package/dist/defaults/paragraphStyles.d.ts.map +1 -1
  169. package/dist/defaults/paragraphStyles.js +5 -1
  170. package/dist/defaults/paragraphStyles.js.map +1 -1
  171. package/dist/design/slots.d.ts +12 -10
  172. package/dist/design/slots.d.ts.map +1 -1
  173. package/dist/design/slots.js +26 -2
  174. package/dist/design/slots.js.map +1 -1
  175. package/dist/fonts/faces.d.ts.map +1 -1
  176. package/dist/fonts/faces.js +6 -0
  177. package/dist/fonts/faces.js.map +1 -1
  178. package/dist/highlightMarks.d.ts.map +1 -1
  179. package/dist/highlightMarks.js +6 -0
  180. package/dist/highlightMarks.js.map +1 -1
  181. package/dist/html-backend.d.ts.map +1 -1
  182. package/dist/html-backend.js +42 -4
  183. package/dist/html-backend.js.map +1 -1
  184. package/dist/index.d.ts +18 -7
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +18 -4
  187. package/dist/index.js.map +1 -1
  188. package/dist/measure/rich.d.ts.map +1 -1
  189. package/dist/measure/rich.js +2 -0
  190. package/dist/measure/rich.js.map +1 -1
  191. package/dist/migrate/markdownRuns.d.ts +70 -0
  192. package/dist/migrate/markdownRuns.d.ts.map +1 -0
  193. package/dist/migrate/markdownRuns.js +215 -0
  194. package/dist/migrate/markdownRuns.js.map +1 -0
  195. package/dist/migrate/pageDesign.d.ts +119 -0
  196. package/dist/migrate/pageDesign.d.ts.map +1 -0
  197. package/dist/migrate/pageDesign.js +446 -0
  198. package/dist/migrate/pageDesign.js.map +1 -0
  199. package/dist/migrate/textToMarkdown.d.ts +124 -0
  200. package/dist/migrate/textToMarkdown.d.ts.map +1 -0
  201. package/dist/migrate/textToMarkdown.js +716 -0
  202. package/dist/migrate/textToMarkdown.js.map +1 -0
  203. package/dist/parse/blockParser.d.ts.map +1 -1
  204. package/dist/parse/blockParser.js +44 -3
  205. package/dist/parse/blockParser.js.map +1 -1
  206. package/dist/parse/types.d.ts +20 -0
  207. package/dist/parse/types.d.ts.map +1 -1
  208. package/dist/pipeline/build.d.ts.map +1 -1
  209. package/dist/pipeline/build.js +298 -30
  210. package/dist/pipeline/build.js.map +1 -1
  211. package/dist/pipeline/buildMeasurement.d.ts +7 -2
  212. package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
  213. package/dist/pipeline/buildMeasurement.js +21 -5
  214. package/dist/pipeline/buildMeasurement.js.map +1 -1
  215. package/dist/pipeline/calloutLayout.d.ts +39 -14
  216. package/dist/pipeline/calloutLayout.d.ts.map +1 -1
  217. package/dist/pipeline/calloutLayout.js +50 -4
  218. package/dist/pipeline/calloutLayout.js.map +1 -1
  219. package/dist/pipeline/composedLayout.d.ts +51 -6
  220. package/dist/pipeline/composedLayout.d.ts.map +1 -1
  221. package/dist/pipeline/composedLayout.js +633 -78
  222. package/dist/pipeline/composedLayout.js.map +1 -1
  223. package/dist/pipeline/contentWarnings.d.ts +15 -0
  224. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  225. package/dist/pipeline/contentWarnings.js +98 -4
  226. package/dist/pipeline/contentWarnings.js.map +1 -1
  227. package/dist/pipeline/floatPlacement.d.ts +2 -0
  228. package/dist/pipeline/floatPlacement.d.ts.map +1 -1
  229. package/dist/pipeline/floatPlacement.js +1 -0
  230. package/dist/pipeline/floatPlacement.js.map +1 -1
  231. package/dist/pipeline/frameLayout.d.ts +7 -4
  232. package/dist/pipeline/frameLayout.d.ts.map +1 -1
  233. package/dist/pipeline/frameLayout.js +5 -2
  234. package/dist/pipeline/frameLayout.js.map +1 -1
  235. package/dist/pipeline/headerFooter.d.ts +20 -1
  236. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  237. package/dist/pipeline/headerFooter.js +225 -70
  238. package/dist/pipeline/headerFooter.js.map +1 -1
  239. package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
  240. package/dist/pipeline/measureContentBlock.js +1 -0
  241. package/dist/pipeline/measureContentBlock.js.map +1 -1
  242. package/dist/pipeline/mirrorFrame.d.ts.map +1 -1
  243. package/dist/pipeline/mirrorFrame.js +25 -2
  244. package/dist/pipeline/mirrorFrame.js.map +1 -1
  245. package/dist/pipeline/outline.d.ts.map +1 -1
  246. package/dist/pipeline/outline.js +2 -0
  247. package/dist/pipeline/outline.js.map +1 -1
  248. package/dist/pipeline/resourceLayout.d.ts +28 -0
  249. package/dist/pipeline/resourceLayout.d.ts.map +1 -1
  250. package/dist/pipeline/resourceLayout.js +21 -0
  251. package/dist/pipeline/resourceLayout.js.map +1 -1
  252. package/dist/pipeline/styles.d.ts +1 -1
  253. package/dist/pipeline/styles.d.ts.map +1 -1
  254. package/dist/pipeline/styles.js +4 -0
  255. package/dist/pipeline/styles.js.map +1 -1
  256. package/dist/pipeline/textWrap.d.ts +4 -1
  257. package/dist/pipeline/textWrap.d.ts.map +1 -1
  258. package/dist/pipeline/textWrap.js +17 -3
  259. package/dist/pipeline/textWrap.js.map +1 -1
  260. package/dist/pipeline/toc.d.ts.map +1 -1
  261. package/dist/pipeline/toc.js +1 -0
  262. package/dist/pipeline/toc.js.map +1 -1
  263. package/dist/types.d.ts +77 -2
  264. package/dist/types.d.ts.map +1 -1
  265. package/dist/vdt.d.ts +290 -15
  266. package/dist/vdt.d.ts.map +1 -1
  267. package/dist/vdt.js.map +1 -1
  268. package/dist/word/toDocx.d.ts.map +1 -1
  269. package/dist/word/toDocx.js +14 -0
  270. package/dist/word/toDocx.js.map +1 -1
  271. package/dist/word/word.test.js +11 -1
  272. package/dist/word/word.test.js.map +1 -1
  273. package/package.json +1 -1
@@ -14,6 +14,7 @@ import { dimensionToPx } from '../units';
14
14
  import { resolveDesignLineHeight, resolveDesignSlot } from '../defaults/headerFooter';
15
15
  import { resolvePageTemplate } from '../composed/templates';
16
16
  import { splitSpreadDecoration } from '../composed/spread';
17
+ import { frameOfBlock } from '../composed/paint';
17
18
  import { layoutDesignSlot, textVerticalOffset, trailingTracking, } from '../design/layout';
18
19
  import { resolveDesignText } from '../design/placeholders';
19
20
  import { plainDesignText } from '../design/richText';
@@ -315,6 +316,9 @@ export function measureHeadingDesign(level, heading, width, dpi, metadata, pageI
315
316
  });
316
317
  return { height: Math.max(bottom, minHeightPx), textFloor };
317
318
  }
319
+ /** `{ configPath }` when there is one. */
320
+ const withPath = (configPath) => (configPath !== undefined ? { configPath } : {});
321
+ const ATTR_ELEMENT = /^\s*\{attr\.([A-Za-z_][A-Za-z0-9_-]*)\}\s*$/;
318
322
  const HEADING_LINE_PLACEHOLDER = /\{(number|numberDecimal|numberRoman|numberRomanLower|numberAlpha|numberAlphaLower|numberWords|numberWordsLower|numberOrdinalWords|numberOrdinalWordsLower|numberHan|chapterNumber|chapterTitle)\}/;
319
323
  const METADATA_ELEMENT = /^\s*\{(title|subtitle|author|publishDate)\}\s*$/;
320
324
  /** The source a text element maps back to, from what its content renders:
@@ -407,7 +411,9 @@ export function layoutSlotToVdt(slot, container, pageIndex, placeholders, dpi, e
407
411
  // maps back.
408
412
  const keepMap = !el.inlineMarks || src.text === undefined
409
413
  || plainDesignText(resolveDesignText(el.content, placeholders, el.inlineMarks === true)).includes(src.text);
410
- sourceByElement.set(el.id, keepMap ? withPrintedTitle(el, src, placeholders) : { start: src.start, end: src.end });
414
+ const attr = ATTR_ELEMENT.exec(el.content)?.[1];
415
+ const mapped = keepMap ? withPrintedTitle(el, src, placeholders) : { start: src.start, end: src.end };
416
+ sourceByElement.set(el.id, attr !== undefined && extras.attrSources?.[attr] ? { ...mapped, attr } : mapped);
411
417
  }
412
418
  }
413
419
  // The texts cut to fit (#628), flagged on the element's own block (not
@@ -419,8 +425,30 @@ export function layoutSlotToVdt(slot, container, pageIndex, placeholders, dpi, e
419
425
  truncated.set(issue.elementId, { mode: issue.mode, text: issue.text });
420
426
  }
421
427
  }
428
+ // Where each element is written in the configuration (#857).
429
+ const pathById = new Map();
430
+ if (extras?.configPath !== undefined)
431
+ slot.elements.forEach((el, i) => pathById.set(el.id, `${extras.configPath}/elements/${i}`));
432
+ for (const [id, path] of extras?.elementPaths ?? [])
433
+ pathById.set(id, path);
434
+ // An image picked by an attribute (`resourceId: '{attr.fondo}'`).
435
+ const imageAttrById = new Map();
436
+ for (const el of slot.elements) {
437
+ const attr = el.kind === 'image' ? ATTR_ELEMENT.exec(el.resourceId ?? '')?.[1] : undefined;
438
+ if (attr !== undefined && extras?.attrs?.[attr] !== undefined)
439
+ imageAttrById.set(el.id, attr);
440
+ }
422
441
  const blocks = result.primitives.map((prim) => {
423
442
  const block = primitiveToBlock(prim);
443
+ if (prim.id) {
444
+ block.elementId = prim.id;
445
+ const path = pathById.get(prim.id);
446
+ if (path !== undefined)
447
+ block.configPath = path;
448
+ const attr = block.kind === 'image' ? imageAttrById.get(prim.id) : undefined;
449
+ if (attr !== undefined)
450
+ block.attr = attr;
451
+ }
424
452
  const cut = prim.kind === 'text' && !prim.dropCap ? truncated.get(prim.id) : undefined;
425
453
  if (block.kind === 'text' && cut && extras?.slot)
426
454
  block.truncated = { elementId: prim.id, slot: extras.slot, mode: cut.mode, text: cut.text };
@@ -434,6 +462,8 @@ export function layoutSlotToVdt(slot, container, pageIndex, placeholders, dpi, e
434
462
  if (src.prefixLen)
435
463
  block.sourcePrefixLen = src.prefixLen;
436
464
  }
465
+ if (src.attr !== undefined)
466
+ block.attr = src.attr;
437
467
  }
438
468
  return block;
439
469
  });
@@ -852,6 +882,23 @@ function pagesWithPartMarks(doc) {
852
882
  });
853
883
  return doc.pages.map((p, i) => (marked.has(i) && !p.partInfo ? { ...p, partInfo: marked.get(i) } : p));
854
884
  }
885
+ /**
886
+ * The page frames a heading design inside a composed-page frame is laid out
887
+ * with (#906): in the frame's blocks' own coordinates, where a frame whose
888
+ * text the reflow ladder scaled (#858) lays its content out `1 / scale` as
889
+ * large from its top-left, so a design element anchored to the page lands
890
+ * on the page once the frame is painted scaled. A turned frame's
891
+ * (`rotate`) are left upright: what is anchored to the page turns with the
892
+ * frame.
893
+ */
894
+ export function framesInFrame(frames, frame) {
895
+ const s = frame.scale;
896
+ if (!frames || !(s > 0) || Math.abs(s - 1) < 1e-9)
897
+ return frames;
898
+ const o = frame.rect;
899
+ const back = (r) => ({ x: o.x + (r.x - o.x) / s, y: o.y + (r.y - o.y) / s, width: r.width / s, height: r.height / s });
900
+ return { ...frames, page: back(frames.page), bleed: back(frames.bleed), ...(frames.outer ? { outer: back(frames.outer) } : {}) };
901
+ }
855
902
  export function buildHeadersAndFooters(doc, resourceById, inputs = {}) {
856
903
  // Each page's slots measure their text in the writing mode they are set
857
904
  // in (see the loop); the mode in force before is put back.
@@ -878,7 +925,10 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
878
925
  };
879
926
  // Page roles drive the per-element `pages` filter of every slot below.
880
927
  classifyPages(doc, resolved);
881
- const chapterTitleByPageIndex = computeChapterTitles(doc.blocks, doc.pages.length, doc.pages);
928
+ // The headings a composed page's frames set (#874) count as running
929
+ // marks too: they live in the page's floats, not in `doc.blocks`.
930
+ const markBlocks = withComposedHeadings(doc);
931
+ const chapterTitleByPageIndex = computeChapterTitles(markBlocks, doc.pages.length, doc.pages);
882
932
  // A heading style with an empty `numberingTemplate` prints no number, not
883
933
  // even the chapter ordinal `{chapterNumber}` falls back to.
884
934
  const numberlessStyles = new Set(resolved.headingStyles.filter((s) => s.numberingTemplate === '').map((s) => s.id));
@@ -887,12 +937,12 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
887
937
  : undefined;
888
938
  // The digits of the chapter ordinals and page counts the slots print.
889
939
  const numerals = resolved.numerals;
890
- const chapterNumberByPageIndex = computeChapterNumbers(doc.blocks, doc.pages.length, doc.pages, doc.chapterOrdinalOffset ?? 0, numberless, numerals);
940
+ const chapterNumberByPageIndex = computeChapterNumbers(markBlocks, doc.pages.length, doc.pages, doc.chapterOrdinalOffset ?? 0, numberless, numerals);
891
941
  // The chapter in force at the top of each page (`{chapterTitleAtTop}`,
892
942
  // `{chapterNumberAtTop}`): the one a page runs on from, even where a new
893
943
  // chapter starts lower down (EF-187).
894
- const chapterTitleAtTopByPageIndex = computeChapterTitlesAtTop(doc.blocks, doc.pages.length, doc.pages);
895
- const chapterNumberAtTopByPageIndex = computeChapterNumbersAtTop(doc.blocks, doc.pages.length, doc.pages, doc.chapterOrdinalOffset ?? 0, numberless, numerals);
944
+ const chapterTitleAtTopByPageIndex = computeChapterTitlesAtTop(markBlocks, doc.pages.length, doc.pages);
945
+ const chapterNumberAtTopByPageIndex = computeChapterNumbersAtTop(markBlocks, doc.pages.length, doc.pages, doc.chapterOrdinalOffset ?? 0, numberless, numerals);
896
946
  // A heading design (opener, in-column overlay, contents part row) prints
897
947
  // the chapter its block belongs to, not the page's: where two chapters
898
948
  // meet on a page, the page value is the later one's. Its height was
@@ -947,7 +997,9 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
947
997
  // and bleed boxes are the sheet's (centred, so their own mirror images).
948
998
  const frames = pageIsVertical(page) ? verticalFrames : physicalFrames;
949
999
  const extras = {
950
- frames, pageRole: page.role, resourceById,
1000
+ // A composed page reports its template's role (`role`, default
1001
+ // `'composed'`) to the `pages` filter of its slots' elements.
1002
+ frames, pageRole: page.composed?.role ?? page.role, resourceById,
951
1003
  metadataSources: doc.metadataSources, metadata: doc.metadata,
952
1004
  // Right-to-left text: the document's direction, and the mirror of a
953
1005
  // right-to-left page's flow, which the slots below are painted in.
@@ -977,6 +1029,25 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
977
1029
  const templateSlot = (own, kind, inherited) => own === false ? { elements: [] } : own ? resolveDesignSlot(own, kind) : inherited;
978
1030
  const headerSlot = templateSlot(template?.header, 'header', section?.header ?? resolved.header);
979
1031
  const footerSlot = templateSlot(template?.footer, 'footer', section?.footer ?? resolved.footer);
1032
+ // Where each running head is written (#857): the template's own, the
1033
+ // styled section's, or the document's.
1034
+ const sectionAt = section ? resolved.headingStyles.indexOf(section) : -1;
1035
+ const slotPath = (kind) => {
1036
+ const own = template?.[kind];
1037
+ if (own === false)
1038
+ return undefined;
1039
+ if (own)
1040
+ return kind === 'header' ? template.headerPath : template.footerPath;
1041
+ if (section?.[kind] && sectionAt >= 0)
1042
+ return `/headingStyles/${sectionAt}/${kind}`;
1043
+ return `/${kind}`;
1044
+ };
1045
+ // `{attr.x}` of the page fence in the running heads of a composed page
1046
+ // maps back to the attribute (#857).
1047
+ const pageAttrSources = page.composed
1048
+ ? Object.fromEntries(Object.entries(page.composed.attrSources).map(([k, v]) => [k, { start: v.sourceStart, end: v.sourceEnd }]))
1049
+ : undefined;
1050
+ const headExtras = page.composed ? { ...sheetExtras, attrs: page.composed.attrs, attrSources: pageAttrSources } : sheetExtras;
980
1051
  // A comic page is all panels: no running heads or folio unless
981
1052
  // `comics.runningHeads` asks for them (its folio still counts).
982
1053
  const furniture = !page.comic || resolvedComics(resolved).runningHeads;
@@ -999,7 +1070,7 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
999
1070
  partNumberByPageIndex,
1000
1071
  partPaletteByPageIndex,
1001
1072
  };
1002
- page.header = asFurniture(withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(headerSlot, headerContainerBbox(sheetArea, metrics.physical.trimBox), page.index + pageIndexOffset, placeholders, dpi, { ...sheetExtras, slot: 'header' })));
1073
+ page.header = asFurniture(withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(headerSlot, headerContainerBbox(sheetArea, metrics.physical.trimBox), page.index + pageIndexOffset, placeholders, dpi, { ...headExtras, slot: 'header', ...withPath(slotPath('header')) })));
1003
1074
  }
1004
1075
  // Back of a part divider: a blank page right after a part page takes the
1005
1076
  // part's verso design (the model book tints the whole leaf). The part
@@ -1124,11 +1195,84 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
1124
1195
  }
1125
1196
  }
1126
1197
  }
1127
- // In-column heading advanced-design overlays: any heading whose level
1128
- // has `advancedDesign.enabled` and a non-empty slot gets its default
1129
- // text rendering replaced with a design overlay laid out inside the
1130
- // block's bbox. `span: 'page'` headings are handled via `openerBand`
1131
- // above (and are already `hidden`), so they're skipped here.
1198
+ // Heading advanced-design overlays: any heading whose level has
1199
+ // `advancedDesign.enabled` and a non-empty slot gets its default text
1200
+ // rendering replaced with a design overlay laid out inside the block's
1201
+ // bbox. `span: 'page'` headings are handled via `openerBand` above (and
1202
+ // are already `hidden`), so they're skipped here, except in a box.
1203
+ // `boxFrames`: the page frames in the coordinates of the frame the
1204
+ // heading is set in (see `framesInFrame`).
1205
+ const overlayHeading = (block, col, boxFrames) => {
1206
+ if (block.type !== 'heading' || !block.headingLevel)
1207
+ return;
1208
+ // A heading set in a box or a frame (#906) is laid out as in a page
1209
+ // column, against its own block, whatever its span: there is no page
1210
+ // band to open inside a box. Its style and attributes are on
1211
+ // `boxDesign`.
1212
+ const inBox = block.containerId !== undefined;
1213
+ const own = inBox ? block.boxDesign : undefined;
1214
+ if (inBox && !own)
1215
+ return;
1216
+ const lvl = headingLevels.forLevel(block.headingLevel, own ? own.styleId : block.headingStyleId);
1217
+ if (!lvl)
1218
+ return;
1219
+ const attrs = own ? own.attrs : block.attrs;
1220
+ // A page-span heading opened mid-page (`spanBreak: false`, #539)
1221
+ // heads a band under the page's first: its design, or the default
1222
+ // opener, is laid out across the content area at the heading.
1223
+ const midPage = !inBox && lvl.span === 'page' && (col?.band ?? 0) > 0 && !headingIsHidden(block, lvl);
1224
+ const designed = lvl.advancedDesign.enabled && lvl.advancedDesign.slot.elements.length > 0;
1225
+ if (lvl.span === 'page' && !midPage && !inBox)
1226
+ return;
1227
+ if (!midPage && !designed)
1228
+ return;
1229
+ const pref = block.numberPrefix ?? '';
1230
+ // The lines joined back into the title (EF-162), with a forced break
1231
+ // (`\\`) as a newline, as the block's height was measured
1232
+ // (`build.ts`) and as an opener prints it (EF-152).
1233
+ const { titleText: title, marked } = own
1234
+ ? defaultOpenerTitle(block.lines, pref, own.titleBreaks, own.titleLength ?? -1, block.numberSeparator)
1235
+ : defaultOpenerTitle(block.lines, pref, block.titleBreaks, block.titleLength ?? -1, block.numberSeparator);
1236
+ const placeholders = {
1237
+ kind: 'heading',
1238
+ page,
1239
+ allPages: doc.pages,
1240
+ metadata: doc.metadata,
1241
+ chapterTitleByPageIndex,
1242
+ chapterNumberByPageIndex,
1243
+ chapterAttrsByPageIndex,
1244
+ bookTotalPages,
1245
+ numerals,
1246
+ marksFor,
1247
+ partTitleByPageIndex,
1248
+ partNumberByPageIndex,
1249
+ partPaletteByPageIndex,
1250
+ heading: {
1251
+ titleText: title,
1252
+ ...(marked !== undefined ? { titleMarked: marked } : {}),
1253
+ formattedNumber: pref,
1254
+ numericValue: block.headingNumber,
1255
+ locale,
1256
+ chapterNumber: chapterNumberOf(block, page.index),
1257
+ attrs,
1258
+ },
1259
+ };
1260
+ const overlay = layoutSlotToVdt(designed
1261
+ ? lvl.advancedDesign.slot
1262
+ : synthesiseDefaultOpenerSlot(lvl, pref.length > 0, resolved.headings.textAlign, marked !== undefined), midPage
1263
+ ? openerContainerBbox(block, contentArea)
1264
+ : { x: block.bbox.x, y: block.bbox.y, width: block.bbox.width, height: block.bbox.height }, page.index + pageIndexOffset, placeholders, dpi, {
1265
+ ...extras,
1266
+ ...(boxFrames ? { frames: boxFrames } : {}),
1267
+ titleSource: headingTitleSource(block, title),
1268
+ attrSources: own ? own.attrSources : block.attrSources,
1269
+ attrs,
1270
+ headingSource: headingLineSource(block),
1271
+ slot: 'heading',
1272
+ });
1273
+ if (overlay)
1274
+ block.designOverlay = overlay;
1275
+ };
1132
1276
  for (const col of page.columns) {
1133
1277
  for (const block of col.blocks) {
1134
1278
  if (block.hidden)
@@ -1171,63 +1315,24 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
1171
1315
  block.designOverlay = overlay;
1172
1316
  continue;
1173
1317
  }
1174
- if (block.type !== 'heading' || !block.headingLevel)
1175
- continue;
1176
- const lvl = headingLevels.forLevel(block.headingLevel, block.headingStyleId);
1177
- if (!lvl)
1178
- continue;
1179
- // A page-span heading opened mid-page (`spanBreak: false`, #539)
1180
- // heads a band under the page's first: its design, or the default
1181
- // opener, is laid out across the content area at the heading.
1182
- const midPage = lvl.span === 'page' && (col.band ?? 0) > 0 && !headingIsHidden(block, lvl);
1183
- const designed = lvl.advancedDesign.enabled && lvl.advancedDesign.slot.elements.length > 0;
1184
- if (lvl.span === 'page' && !midPage)
1185
- continue;
1186
- if (!midPage && !designed)
1318
+ overlayHeading(block, col);
1319
+ }
1320
+ }
1321
+ // The headings of boxes and frames set out of the columns (#906): fixed
1322
+ // and floated callouts, the frames of a composed page and the halves of
1323
+ // a spread's frames shown on this page.
1324
+ const composed = page.composed;
1325
+ for (const block of page.floats ?? []) {
1326
+ if (block.type !== 'heading' || block.hidden || block.containerId === undefined)
1327
+ continue;
1328
+ const frame = composed ? frameOfBlock(composed, block.id) : undefined;
1329
+ overlayHeading(block, undefined, frame ? framesInFrame(extras.frames, frame) : undefined);
1330
+ }
1331
+ for (const frame of composed?.frames ?? []) {
1332
+ for (const block of frame.facingBlocks ?? []) {
1333
+ if (block.type !== 'heading' || block.hidden || block.containerId === undefined)
1187
1334
  continue;
1188
- const pref = block.numberPrefix ?? '';
1189
- // The lines joined back into the title (EF-162), with a forced break
1190
- // (`\\`) as a newline, as the block's height was measured
1191
- // (`build.ts`) and as an opener prints it (EF-152).
1192
- const { titleText: title, marked } = defaultOpenerTitle(block.lines, pref, block.titleBreaks, block.titleLength ?? -1, block.numberSeparator);
1193
- const placeholders = {
1194
- kind: 'heading',
1195
- page,
1196
- allPages: doc.pages,
1197
- metadata: doc.metadata,
1198
- chapterTitleByPageIndex,
1199
- chapterNumberByPageIndex,
1200
- chapterAttrsByPageIndex,
1201
- bookTotalPages,
1202
- numerals,
1203
- marksFor,
1204
- partTitleByPageIndex,
1205
- partNumberByPageIndex,
1206
- partPaletteByPageIndex,
1207
- heading: {
1208
- titleText: title,
1209
- ...(marked !== undefined ? { titleMarked: marked } : {}),
1210
- formattedNumber: pref,
1211
- numericValue: block.headingNumber,
1212
- locale,
1213
- chapterNumber: chapterNumberOf(block, page.index),
1214
- attrs: block.attrs,
1215
- },
1216
- };
1217
- const overlay = layoutSlotToVdt(designed
1218
- ? lvl.advancedDesign.slot
1219
- : synthesiseDefaultOpenerSlot(lvl, pref.length > 0, resolved.headings.textAlign, marked !== undefined), midPage
1220
- ? openerContainerBbox(block, contentArea)
1221
- : { x: block.bbox.x, y: block.bbox.y, width: block.bbox.width, height: block.bbox.height }, page.index + pageIndexOffset, placeholders, dpi, {
1222
- ...extras,
1223
- titleSource: headingTitleSource(block, title),
1224
- attrSources: block.attrSources,
1225
- attrs: block.attrs,
1226
- headingSource: headingLineSource(block),
1227
- slot: 'heading',
1228
- });
1229
- if (overlay)
1230
- block.designOverlay = overlay;
1335
+ overlayHeading(block, undefined, framesInFrame(extras.frames, frame));
1231
1336
  }
1232
1337
  }
1233
1338
  if (furniture && footerSlot.elements.length > 0) {
@@ -1249,7 +1354,7 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
1249
1354
  partNumberByPageIndex,
1250
1355
  partPaletteByPageIndex,
1251
1356
  };
1252
- page.footer = asFurniture(withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(footerSlot, footerContainerBbox(sheetArea, metrics.physical.trimBox), page.index + pageIndexOffset, placeholders, dpi, { ...sheetExtras, slot: 'footer' })));
1357
+ page.footer = asFurniture(withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(footerSlot, footerContainerBbox(sheetArea, metrics.physical.trimBox), page.index + pageIndexOffset, placeholders, dpi, { ...headExtras, slot: 'footer', ...withPath(slotPath('footer')) })));
1253
1358
  }
1254
1359
  // The template's decoration, laid with the frames' final boxes (its
1255
1360
  // elements may anchor to `'#frame'`), on the sheet like the running
@@ -1318,7 +1423,11 @@ function layoutHeadersAndFooters(doc, resourceById, inputs) {
1318
1423
  partNumberByPageIndex,
1319
1424
  partPaletteByPageIndex,
1320
1425
  };
1321
- const decoration = withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(resolveDesignSlot(template.decoration, 'header'), container, page.index + pageIndexOffset, placeholders, dpi, { ...sheetExtras, frames: { ...slotFrames, named, declared }, attrs: page.composed.attrs, attrSources }));
1426
+ const decoration = withMeasureWritingMode('horizontal-tb', () => layoutSlotToVdt(resolveDesignSlot(template.decoration, 'header'), container, page.index + pageIndexOffset, placeholders, dpi, {
1427
+ ...sheetExtras, frames: { ...slotFrames, named, declared }, attrs: page.composed.attrs, attrSources,
1428
+ // Each element's pointer, from the template that sets it (#857).
1429
+ elementPaths: new Map(resolveDesignSlot(template.decoration, 'header').elements.map((el, i) => [el.id, template.decorationPaths[i]])),
1430
+ }));
1322
1431
  if (decoration && pair) {
1323
1432
  const halves = splitSpreadDecoration(decoration, shift, { left: bleedBox, right: { ...bleedBox, x: bleedBox.x + shift } });
1324
1433
  pair.left.composed.decoration = halves.left;
@@ -1353,4 +1462,50 @@ export function stampPageBackgrounds(doc, palettes, pageColor, basePalette) {
1353
1462
  page.background = hex;
1354
1463
  }
1355
1464
  }
1465
+ /** `doc.blocks` as the running marks read them (#874): on a composed page
1466
+ * only its headings count, in frame order, so a chapter that opens in one
1467
+ * of its frames opens the page (`{chapterTitleAtTop}`) even though the
1468
+ * story boxes of the frames come first. The same array when the document
1469
+ * has no composed page. */
1470
+ function withComposedHeadings(doc) {
1471
+ const composed = new Set();
1472
+ for (const page of doc.pages)
1473
+ if (page.composed)
1474
+ composed.add(page.index);
1475
+ if (composed.size === 0)
1476
+ return doc.blocks;
1477
+ const out = [];
1478
+ const seen = new Set();
1479
+ let lastComposed = -1;
1480
+ const pushFrames = (index) => {
1481
+ const page = doc.pages[index];
1482
+ if (!page?.composed)
1483
+ return;
1484
+ const byId = new Map((page.floats ?? []).map((b) => [b.id, b]));
1485
+ for (const f of page.composed.frames) {
1486
+ for (const id of f.blockIds) {
1487
+ const b = byId.get(id);
1488
+ if (b && b.headingLevel !== undefined && !seen.has(b.id)) {
1489
+ seen.add(b.id);
1490
+ out.push(b);
1491
+ }
1492
+ }
1493
+ }
1494
+ };
1495
+ for (const b of doc.blocks) {
1496
+ if (!composed.has(b.pageIndex)) {
1497
+ out.push(b);
1498
+ continue;
1499
+ }
1500
+ if (b.pageIndex !== lastComposed) {
1501
+ lastComposed = b.pageIndex;
1502
+ pushFrames(b.pageIndex);
1503
+ }
1504
+ if (b.headingLevel !== undefined && !seen.has(b.id)) {
1505
+ seen.add(b.id);
1506
+ out.push(b);
1507
+ }
1508
+ }
1509
+ return out;
1510
+ }
1356
1511
  //# sourceMappingURL=headerFooter.js.map