postext 0.3.45 → 0.3.47
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.
- package/dist/__tests__/pipeline/calloutColumnsLabel.test.js +0 -1
- package/dist/__tests__/pipeline/calloutColumnsLabel.test.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +21 -1
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +23 -13
- package/dist/html-backend.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/build.d.ts +9 -0
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +64 -7
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/calloutLayout.js +1 -1
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/types.d.ts +9 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/pipeline/build.js
CHANGED
|
@@ -84,7 +84,14 @@ function measureLooseParagraph(rawBlock, blockIdx, columnWidth, ctx, styleOverri
|
|
|
84
84
|
* @internal Exposed for tests only; use `buildDocument`.
|
|
85
85
|
*/
|
|
86
86
|
export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
87
|
-
const { balanceExtraPx, balanceLooseness, balanceLooseBudget, bandCaps } = hints;
|
|
87
|
+
const { balanceExtraPx, balanceLooseness, balanceLooseBudget, bandCaps, captionUnder } = hints;
|
|
88
|
+
const captionUnderProposals = new Set();
|
|
89
|
+
/** Side columns whose foot a figure's side caption has cut, by the figure. */
|
|
90
|
+
const asideCutBy = new WeakMap();
|
|
91
|
+
/** A side column that does not run the page's whole content height: a
|
|
92
|
+
* band above (a page-span box, a top float band) or a caption below has
|
|
93
|
+
* shortened it, so what does not fit it may still fit a full column. */
|
|
94
|
+
const shortSideColumn = (page, col) => col.bbox.height < page.contentArea.height - 0.5;
|
|
88
95
|
const resolved = resolveAllConfig(config);
|
|
89
96
|
// Tracking rungs a loose paragraph may climb: none, then 5‰ steps up to
|
|
90
97
|
// the cap (`headings.balancing.maxTracking`, thousandths of an em), the
|
|
@@ -243,7 +250,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
243
250
|
pendingFloats.push(stamped);
|
|
244
251
|
}
|
|
245
252
|
};
|
|
246
|
-
/** Floated boxes (`placement: 'top' | 'bottom'`, `span` column / page):
|
|
253
|
+
/** Floated boxes (`placement: 'auto' | 'top' | 'bottom'`, `span` column / page):
|
|
247
254
|
* a `:::callout` that leaves the flow like a resource and takes the
|
|
248
255
|
* first free band after the point it occurs at — the foot of the
|
|
249
256
|
* current page, or the head / foot of a page the flow opens later —
|
|
@@ -604,7 +611,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
604
611
|
// The caption beside the figure, in the band's side column.
|
|
605
612
|
let aside;
|
|
606
613
|
let sideCol;
|
|
607
|
-
if (f.captionSide && !pageSpan && !side && !rotated) {
|
|
614
|
+
if (f.captionSide && !pageSpan && !side && !rotated && !captionUnder?.has(f.resourceId)) {
|
|
608
615
|
const sc = sideColumnOf(page, first.band ?? 0);
|
|
609
616
|
if (sc && sc.bbox.width > 0.5) {
|
|
610
617
|
sideCol = sc;
|
|
@@ -639,6 +646,19 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
639
646
|
}
|
|
640
647
|
}
|
|
641
648
|
}
|
|
649
|
+
// A bottom float's caption cuts the side column's foot; when the side
|
|
650
|
+
// stack already reaches into that band (a box set beside the text
|
|
651
|
+
// above), the caption goes under the figure instead of behind the box.
|
|
652
|
+
if (aside && sideCol && position === 'bottom' && measure.asideHeight !== undefined) {
|
|
653
|
+
const captionTop = y + measure.height - measure.asideHeight - floatGapPx;
|
|
654
|
+
if (sideUsedBottom(sideCol) > captionTop + 0.5) {
|
|
655
|
+
const plain = measureFloat(f.resourceId, width, slice, rotated);
|
|
656
|
+
if (plain) {
|
|
657
|
+
({ need, y } = measureFloatBand(position, plain, targetCols, page.contentArea, baselineGrid, floatGapPx, bottomOf));
|
|
658
|
+
return { need, y, measure: plain, slice, width, xLeft, rotated };
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
642
662
|
return { need, y, measure, slice, width, xLeft, rotated, ...(aside ? { aside, sideCol } : {}) };
|
|
643
663
|
};
|
|
644
664
|
/** Free height `col` keeps for the flow once a float band `probe` is
|
|
@@ -704,6 +724,16 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
704
724
|
if (need > first.availableHeight + 0.01) {
|
|
705
725
|
if (mode === 'strict' || sideUsedBottom(first) > first.bbox.y + 0.5)
|
|
706
726
|
return 'defer';
|
|
727
|
+
// An empty column shortened by a band above it (a page-span box,
|
|
728
|
+
// a top float) is no measure of the float: when a full column
|
|
729
|
+
// would hold it, it waits for one instead of overflowing this one.
|
|
730
|
+
if (shortSideColumn(page, first) && measure.height <= page.contentArea.height + 0.01)
|
|
731
|
+
return 'defer';
|
|
732
|
+
// Overflowing a column a side caption has shortened: the next pass
|
|
733
|
+
// sets that caption under its figure and gives the column back.
|
|
734
|
+
const cutBy = asideCutBy.get(first);
|
|
735
|
+
if (cutBy !== undefined)
|
|
736
|
+
captionUnderProposals.add(cutBy);
|
|
707
737
|
}
|
|
708
738
|
const built = buildFloatBlock(f.resourceId, xLeft, width, slice);
|
|
709
739
|
if (!built)
|
|
@@ -824,6 +854,8 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
824
854
|
const cut = Math.max(0, sideCol.bbox.y + sideCol.bbox.height - (y + built.height - measure.asideHeight - floatGapPx));
|
|
825
855
|
sideCol.bbox.height = Math.max(0, sideCol.bbox.height - cut);
|
|
826
856
|
sideCol.availableHeight = Math.max(0, sideCol.availableHeight - cut);
|
|
857
|
+
if (cut > 0.5)
|
|
858
|
+
asideCutBy.set(sideCol, f.resourceId);
|
|
827
859
|
}
|
|
828
860
|
}
|
|
829
861
|
for (const col of targetCols) {
|
|
@@ -2197,6 +2229,15 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
2197
2229
|
if (!fits()) {
|
|
2198
2230
|
if (mode === 'strict' || used > side.bbox.y + 0.5)
|
|
2199
2231
|
return false;
|
|
2232
|
+
// A box that a full side column would hold waits for one rather than
|
|
2233
|
+
// overflowing an empty column a band above has shortened.
|
|
2234
|
+
if (shortSideColumn(page, side) && result.totalHeight <= page.contentArea.height + 0.01)
|
|
2235
|
+
return false;
|
|
2236
|
+
// Same as a side float: a box that overflows a caption-shortened
|
|
2237
|
+
// column asks for that caption to go under its figure.
|
|
2238
|
+
const cutBy = asideCutBy.get(side);
|
|
2239
|
+
if (cutBy !== undefined)
|
|
2240
|
+
captionUnderProposals.add(cutBy);
|
|
2200
2241
|
}
|
|
2201
2242
|
const frame = result.frame;
|
|
2202
2243
|
stampCalloutSource(frame, startIdx, plan);
|
|
@@ -2277,7 +2318,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
2277
2318
|
// this point (the foot of the current page, or the head / foot of a
|
|
2278
2319
|
// page the flow opens later) and the text after them fills the page.
|
|
2279
2320
|
// A side box always stacks beside the text it interrupts.
|
|
2280
|
-
if ((placement === 'top' || placement === 'bottom') && span !== 'side') {
|
|
2321
|
+
if ((placement === 'auto' || placement === 'top' || placement === 'bottom') && span !== 'side') {
|
|
2281
2322
|
enqueueCalloutFloat(startIdx, plan, style, L, placement, span);
|
|
2282
2323
|
return undefined;
|
|
2283
2324
|
}
|
|
@@ -3284,7 +3325,7 @@ export function buildDocumentPass(content, config, cache, options, hints = {}) {
|
|
|
3284
3325
|
buildHeadersAndFooters(doc, resourceById);
|
|
3285
3326
|
doc.converged = true;
|
|
3286
3327
|
doc.iterationCount = 1;
|
|
3287
|
-
return { doc, forcedBreakPages, bandCapProposals, spanPlacedInBand, bandCapsApplied, looseOutcome };
|
|
3328
|
+
return { doc, forcedBreakPages, bandCapProposals, spanPlacedInBand, bandCapsApplied, looseOutcome, captionUnderProposals };
|
|
3288
3329
|
}
|
|
3289
3330
|
/** Passes a document printing its own contents gets at most, beyond the
|
|
3290
3331
|
* first, for the page labels it prints to settle. */
|
|
@@ -3363,16 +3404,31 @@ function* buildDocumentBalanced(content, config, cache, options, tocRound = 0) {
|
|
|
3363
3404
|
const passOptions = onProgress
|
|
3364
3405
|
? { ...options, onProgress: (p) => onProgress({ ...p, pass: passIndex }) }
|
|
3365
3406
|
: options;
|
|
3407
|
+
// Side captions moved under their figure (see `PassHints.captionUnder`):
|
|
3408
|
+
// once a pass asks for one, every later pass keeps it.
|
|
3409
|
+
const captionUnder = new Set();
|
|
3366
3410
|
const runPass = (hints) => {
|
|
3367
3411
|
passIndex++;
|
|
3368
3412
|
const started = onPass ? now() : 0;
|
|
3369
|
-
const result = buildDocumentPass(content, config, cache, passOptions, hints);
|
|
3413
|
+
const result = buildDocumentPass(content, config, cache, passOptions, { ...hints, captionUnder });
|
|
3414
|
+
for (const id of result.captionUnderProposals)
|
|
3415
|
+
captionUnder.add(id);
|
|
3370
3416
|
onPass?.({ pass: passIndex, tocRound, ms: now() - started, pages: result.doc.pages.length });
|
|
3371
3417
|
return result;
|
|
3372
3418
|
};
|
|
3373
3419
|
// The first pass is a pass too: let a cancel land before the caps run.
|
|
3374
|
-
|
|
3420
|
+
let initial = runPass();
|
|
3375
3421
|
yield;
|
|
3422
|
+
// A side box or side float spilled over a figure's side caption: place
|
|
3423
|
+
// again with that caption under the figure (a moved caption can free or
|
|
3424
|
+
// crowd another side column, hence the short loop).
|
|
3425
|
+
for (let round = 0; round < 3 && initial.captionUnderProposals.size > 0; round++) {
|
|
3426
|
+
const before = captionUnder.size;
|
|
3427
|
+
initial = runPass();
|
|
3428
|
+
yield;
|
|
3429
|
+
if (captionUnder.size === before)
|
|
3430
|
+
break;
|
|
3431
|
+
}
|
|
3376
3432
|
// --- Band caps (page-span blocks mid-page) -----------------------------
|
|
3377
3433
|
// A span block that arrived in an uneven band proposes a cap; the driver
|
|
3378
3434
|
// re-places the document with it (and grows / drops caps whose band
|
|
@@ -3529,6 +3585,7 @@ function* buildDocumentBalanced(content, config, cache, options, tocRound = 0) {
|
|
|
3529
3585
|
spanPlacedInBand: mergeSet(best.spanPlacedInBand, next.spanPlacedInBand),
|
|
3530
3586
|
bandCapsApplied: mergeSet(best.bandCapsApplied, next.bandCapsApplied),
|
|
3531
3587
|
looseOutcome: mergeMap(best.looseOutcome, next.looseOutcome),
|
|
3588
|
+
captionUnderProposals: new Set([...best.captionUnderProposals, ...next.captionUnderProposals]),
|
|
3532
3589
|
};
|
|
3533
3590
|
};
|
|
3534
3591
|
/**
|