postext 0.3.30 → 0.3.31

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 (39) hide show
  1. package/dist/__tests__/pipeline/paragraphStyles.test.js +19 -0
  2. package/dist/__tests__/pipeline/paragraphStyles.test.js.map +1 -1
  3. package/dist/__tests__/pipeline/raggedLines.test.d.ts +2 -0
  4. package/dist/__tests__/pipeline/raggedLines.test.d.ts.map +1 -0
  5. package/dist/__tests__/pipeline/raggedLines.test.js +26 -0
  6. package/dist/__tests__/pipeline/raggedLines.test.js.map +1 -0
  7. package/dist/__tests__/pipeline/spanFloatAtCut.test.js +6 -2
  8. package/dist/__tests__/pipeline/spanFloatAtCut.test.js.map +1 -1
  9. package/dist/__tests__/pipeline/trailingBand.test.js +67 -0
  10. package/dist/__tests__/pipeline/trailingBand.test.js.map +1 -1
  11. package/dist/pipeline/bandCaps.d.ts +10 -6
  12. package/dist/pipeline/bandCaps.d.ts.map +1 -1
  13. package/dist/pipeline/bandCaps.js +33 -17
  14. package/dist/pipeline/bandCaps.js.map +1 -1
  15. package/dist/pipeline/build.d.ts.map +1 -1
  16. package/dist/pipeline/build.js +35 -19
  17. package/dist/pipeline/build.js.map +1 -1
  18. package/dist/pipeline/columnBalancing.d.ts.map +1 -1
  19. package/dist/pipeline/columnBalancing.js +4 -1
  20. package/dist/pipeline/columnBalancing.js.map +1 -1
  21. package/dist/pipeline/paragraphContainers.d.ts.map +1 -1
  22. package/dist/pipeline/paragraphContainers.js +4 -1
  23. package/dist/pipeline/paragraphContainers.js.map +1 -1
  24. package/dist/pipeline/raggedLines.d.ts +16 -0
  25. package/dist/pipeline/raggedLines.d.ts.map +1 -0
  26. package/dist/pipeline/raggedLines.js +28 -0
  27. package/dist/pipeline/raggedLines.js.map +1 -0
  28. package/dist/vdt.d.ts +4 -0
  29. package/dist/vdt.d.ts.map +1 -1
  30. package/dist/vdt.js.map +1 -1
  31. package/package.json +1 -1
  32. package/dist/__tests__/pipeline/raggedUrl.test.d.ts +0 -2
  33. package/dist/__tests__/pipeline/raggedUrl.test.d.ts.map +0 -1
  34. package/dist/__tests__/pipeline/raggedUrl.test.js +0 -22
  35. package/dist/__tests__/pipeline/raggedUrl.test.js.map +0 -1
  36. package/dist/pipeline/raggedUrl.d.ts +0 -15
  37. package/dist/pipeline/raggedUrl.d.ts.map +0 -1
  38. package/dist/pipeline/raggedUrl.js +0 -28
  39. package/dist/pipeline/raggedUrl.js.map +0 -1
@@ -24,7 +24,10 @@ import { defaultResourceTypes } from '../defaults/resourceTypes';
24
24
  import { buildHeadersAndFooters, measureHeadingAdvancedDesignHeight } from './headerFooter';
25
25
  import { totalGapLines, proposeBalanceLines, collectColumnGaps, firstDivergentColumn, MAX_BALANCING_PASSES, balanceKey } from './columnBalancing';
26
26
  import { applyBandCap, uncapBand, columnBottom, bandCapLines, bandTop, resolveBandCaps, resolveTrailingCaps, bandCapLinesAroundZone, } from './bandCaps';
27
- import { raggedUrlLines } from './raggedUrl';
27
+ import { raggedLooseLines } from './raggedLines';
28
+ /** Tolerance for "does this block fit" checks against a column's free
29
+ * height, absorbing floating-point drift between grid multiples. */
30
+ const FIT_EPS = 0.01;
28
31
  export class BuildCancelledError extends Error {
29
32
  constructor() {
30
33
  super('Build cancelled');
@@ -828,7 +831,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
828
831
  for (const [spanIndex, cap] of bandCaps) {
829
832
  if (cap.startContentIndex !== contentIndex || cap.startPart !== part)
830
833
  continue;
831
- applyBandCap(bandColumns(page, band), cap.lines * baselineGrid, uncappedBottoms, cap.zone);
834
+ applyBandCap(bandColumns(page, band), cap.lines * baselineGrid, uncappedBottoms, cap.zone, cap.kind === 'trailing');
832
835
  activeCap = { spanIndex, pageIndex: page.index, band };
833
836
  bandCapsApplied.add(spanIndex);
834
837
  break;
@@ -863,13 +866,16 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
863
866
  const cols = bandColumns(page, band).filter((c) => c.bbox.height > 0.5);
864
867
  if (cols.length < 2 || cols.some((c) => c.forcedBreak))
865
868
  return;
866
- if (bandCaps?.has(boundaryIndex)) {
867
- if (activeCap && activeCap.spanIndex === boundaryIndex
868
- && activeCap.pageIndex === page.index && activeCap.band === band) {
869
- spanPlacedInBand.add(boundaryIndex);
870
- }
869
+ if (activeCap && activeCap.spanIndex === boundaryIndex
870
+ && activeCap.pageIndex === page.index && activeCap.band === band) {
871
+ spanPlacedInBand.add(boundaryIndex);
871
872
  return;
872
873
  }
874
+ // A cap in force for this boundary that did not open the band the
875
+ // boundary is reached in (an earlier cap moved the flow under it, or
876
+ // its own band overflowed) still gets a fresh proposal below: the
877
+ // driver replaces a cap that no longer applies with it, and ignores it
878
+ // while retrying an applied cap a line taller.
873
879
  if (activeCap && activeCap.pageIndex === page.index && activeCap.band === band)
874
880
  return;
875
881
  if (!cols.some((c) => c.blocks.length > 0))
@@ -1949,11 +1955,16 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
1949
1955
  ? paragraphContainers.byId.get(rawBlock.containerId)
1950
1956
  : undefined;
1951
1957
  if (pc) {
1958
+ // Margins collapse with the pending spacing; a negative one pulls
1959
+ // the flow up past it instead (the container starts inside the
1960
+ // space the previous block left, or the next block inside the
1961
+ // container's).
1962
+ const collapse = (margin) => margin < 0 ? pendingSpacing + margin : Math.max(pendingSpacing, margin);
1952
1963
  if (rawBlock.type === 'containerStart') {
1953
- pendingSpacing = Math.max(pendingSpacing, pc.marginTopPx);
1964
+ pendingSpacing = collapse(pc.marginTopPx);
1954
1965
  }
1955
1966
  else if (contentBlocks[blockIdx - 1]?.type !== 'paragraph') {
1956
- pendingSpacing = Math.max(pendingSpacing, pc.marginBottomPx);
1967
+ pendingSpacing = collapse(pc.marginBottomPx);
1957
1968
  }
1958
1969
  }
1959
1970
  continue;
@@ -2120,8 +2131,9 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2120
2131
  // List items may split too — orphan/widow protection per-list is gated by
2121
2132
  // `avoidOrphansInLists` / `avoidWidowsInLists`; bullet stays on first part.
2122
2133
  const canSplit = vdtType === 'paragraph' || vdtType === 'blockquote' || vdtType === 'listItem';
2123
- // A justified line a link leaves with too few spaces is set ragged.
2124
- let remainingLines = [...raggedUrlLines(measured.lines, style.textAlign, rawBlock.text)];
2134
+ // A justified line the breaker could not fill (a link breaking at its
2135
+ // joints, a last word that cannot come up) is set ragged, not stretched.
2136
+ let remainingLines = [...raggedLooseLines(measured.lines, style.textAlign)];
2125
2137
  let partIndex = 0;
2126
2138
  /** Times this block left an EMPTY short column (see `shortColumn`) —
2127
2139
  * bounded so a page whose columns are all short (footnotes, design
@@ -2251,7 +2263,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2251
2263
  : 1;
2252
2264
  const splitAt = remainingLines.length - 1;
2253
2265
  if (splitAt >= effectiveWidowMin) {
2254
- if (spacingBefore > 0)
2266
+ if (spacingBefore !== 0)
2255
2267
  curCol.availableHeight -= spacingBefore;
2256
2268
  const splitLines = remainingLines.slice(0, splitAt);
2257
2269
  const blk = createVDTBlock(id, vdtType, style.fontString, style.color, style.textAlign);
@@ -2327,8 +2339,10 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2327
2339
  const shortColumn = shortColumnMoves < 4
2328
2340
  && !uncappedBottoms.has(curCol)
2329
2341
  && curCol.bbox.height < contentArea.height - baselineGrid;
2330
- // Block fits in current column
2331
- if (effectiveRemainHeight <= effectiveAvailable) {
2342
+ // Block fits in current column (a hair of tolerance: a capped column
2343
+ // and the grid lines balancing adds above a block differ by floating
2344
+ // point noise, which must not push the block over the cut).
2345
+ if (effectiveRemainHeight <= effectiveAvailable + FIT_EPS) {
2332
2346
  // Heading keep-with-next: never leave a heading as the last block of a
2333
2347
  // column. If the following (non-heading) block wouldn't have room to
2334
2348
  // place at least its widow-minimum number of lines after this heading,
@@ -2373,8 +2387,8 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2373
2387
  continue;
2374
2388
  }
2375
2389
  }
2376
- // Consume spacing
2377
- if (spacingBefore > 0) {
2390
+ // Consume spacing (negative: a container margin pulling the block up)
2391
+ if (spacingBefore !== 0) {
2378
2392
  curCol.availableHeight -= spacingBefore;
2379
2393
  }
2380
2394
  const partId = partIndex === 0 ? id : `${id}-cont-${partIndex}`;
@@ -2437,7 +2451,9 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2437
2451
  // the grid (e.g. marginBottom is an exact multiple of baselineGrid),
2438
2452
  // don't round up to the next line.
2439
2453
  const snappedBottom = Math.ceil((naturalBottom - 0.01) / baselineGrid) * baselineGrid;
2440
- h = snappedBottom - usedHeight;
2454
+ // A negative margin below a container tail may snap the flow back
2455
+ // above the text's own bottom; never below the block's top.
2456
+ h = Math.max(0, snappedBottom - usedHeight);
2441
2457
  }
2442
2458
  placeBlockInColumn(blk, h, curCol, cursor);
2443
2459
  finalizeListItem(blk, partIndex === 0);
@@ -2483,8 +2499,8 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
2483
2499
  slackWeight: resolved.bodyText.slackWeight,
2484
2500
  });
2485
2501
  if (choice.splitAt > 0) {
2486
- // Consume spacing
2487
- if (spacingBefore > 0) {
2502
+ // Consume spacing (negative: a container margin pulling the block up)
2503
+ if (spacingBefore !== 0) {
2488
2504
  curCol.availableHeight -= spacingBefore;
2489
2505
  }
2490
2506
  const partId = partIndex === 0 ? id : `${id}-cont-${partIndex}`;