postext 1.58.3 → 1.59.1
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__/defaults/headingsBalancing.test.js +8 -0
- package/dist/__tests__/defaults/headingsBalancing.test.js.map +1 -1
- package/dist/__tests__/design/titleTextWrapped.test.js +13 -10
- package/dist/__tests__/design/titleTextWrapped.test.js.map +1 -1
- package/dist/__tests__/exercises/config.test.d.ts +2 -0
- package/dist/__tests__/exercises/config.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/config.test.js +63 -0
- package/dist/__tests__/exercises/config.test.js.map +1 -0
- package/dist/__tests__/exercises/digital.test.d.ts +2 -0
- package/dist/__tests__/exercises/digital.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/digital.test.js +163 -0
- package/dist/__tests__/exercises/digital.test.js.map +1 -0
- package/dist/__tests__/exercises/languages.test.d.ts +2 -0
- package/dist/__tests__/exercises/languages.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/languages.test.js +109 -0
- package/dist/__tests__/exercises/languages.test.js.map +1 -0
- package/dist/__tests__/exercises/layout.test.d.ts +2 -0
- package/dist/__tests__/exercises/layout.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/layout.test.js +77 -0
- package/dist/__tests__/exercises/layout.test.js.map +1 -0
- package/dist/__tests__/exercises/model.test.d.ts +2 -0
- package/dist/__tests__/exercises/model.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/model.test.js +90 -0
- package/dist/__tests__/exercises/model.test.js.map +1 -0
- package/dist/__tests__/exercises/parse.test.d.ts +2 -0
- package/dist/__tests__/exercises/parse.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/parse.test.js +37 -0
- package/dist/__tests__/exercises/parse.test.js.map +1 -0
- package/dist/__tests__/exercises/refs.test.d.ts +2 -0
- package/dist/__tests__/exercises/refs.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/refs.test.js +46 -0
- package/dist/__tests__/exercises/refs.test.js.map +1 -0
- package/dist/__tests__/exercises/solutions.test.d.ts +2 -0
- package/dist/__tests__/exercises/solutions.test.d.ts.map +1 -0
- package/dist/__tests__/exercises/solutions.test.js +53 -0
- package/dist/__tests__/exercises/solutions.test.js.map +1 -0
- package/dist/__tests__/exports.test.js +14 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/parse/containers.test.js +2 -2
- package/dist/__tests__/parse/containers.test.js.map +1 -1
- package/dist/__tests__/pipeline/fillShortColumns.test.d.ts +2 -0
- package/dist/__tests__/pipeline/fillShortColumns.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline/fillShortColumns.test.js +163 -0
- package/dist/__tests__/pipeline/fillShortColumns.test.js.map +1 -0
- package/dist/__tests__/pipeline/spanHeadingMidPage.test.js +52 -0
- package/dist/__tests__/pipeline/spanHeadingMidPage.test.js.map +1 -1
- package/dist/canvas-backend/blockRender.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.js +18 -0
- package/dist/canvas-backend/blockRender.js.map +1 -1
- package/dist/canvas-backend/chip.d.ts +3 -0
- package/dist/canvas-backend/chip.d.ts.map +1 -1
- package/dist/canvas-backend/chip.js +31 -1
- package/dist/canvas-backend/chip.js.map +1 -1
- package/dist/configWarnings.d.ts +3 -1
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +6 -2
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/chipStyles.d.ts.map +1 -1
- package/dist/defaults/chipStyles.js +7 -0
- package/dist/defaults/chipStyles.js.map +1 -1
- package/dist/defaults/exercises.d.ts +111 -0
- package/dist/defaults/exercises.d.ts.map +1 -0
- package/dist/defaults/exercises.js +255 -0
- package/dist/defaults/exercises.js.map +1 -0
- package/dist/defaults/headings.d.ts +1 -0
- package/dist/defaults/headings.d.ts.map +1 -1
- package/dist/defaults/headings.js +7 -0
- package/dist/defaults/headings.js.map +1 -1
- package/dist/defaults/index.d.ts +2 -0
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +16 -0
- package/dist/defaults/index.js.map +1 -1
- package/dist/defaults/shared.d.ts +4 -0
- package/dist/defaults/shared.d.ts.map +1 -1
- package/dist/defaults/shared.js +6 -0
- package/dist/defaults/shared.js.map +1 -1
- package/dist/design/elementKeys.d.ts +2 -0
- package/dist/design/elementKeys.d.ts.map +1 -1
- package/dist/design/elementKeys.js +1 -1
- package/dist/design/elementKeys.js.map +1 -1
- package/dist/exercises/build.d.ts +60 -0
- package/dist/exercises/build.d.ts.map +1 -0
- package/dist/exercises/build.js +177 -0
- package/dist/exercises/build.js.map +1 -0
- package/dist/exercises/configWarnings.d.ts +11 -0
- package/dist/exercises/configWarnings.d.ts.map +1 -0
- package/dist/exercises/configWarnings.js +175 -0
- package/dist/exercises/configWarnings.js.map +1 -0
- package/dist/exercises/designs.d.ts +38 -0
- package/dist/exercises/designs.d.ts.map +1 -0
- package/dist/exercises/designs.js +143 -0
- package/dist/exercises/designs.js.map +1 -0
- package/dist/exercises/digital.d.ts +69 -0
- package/dist/exercises/digital.d.ts.map +1 -0
- package/dist/exercises/digital.js +58 -0
- package/dist/exercises/digital.js.map +1 -0
- package/dist/exercises/expand.d.ts +74 -0
- package/dist/exercises/expand.d.ts.map +1 -0
- package/dist/exercises/expand.js +1089 -0
- package/dist/exercises/expand.js.map +1 -0
- package/dist/exercises/grids.d.ts +62 -0
- package/dist/exercises/grids.d.ts.map +1 -0
- package/dist/exercises/grids.js +535 -0
- package/dist/exercises/grids.js.map +1 -0
- package/dist/exercises/model.d.ts +24 -0
- package/dist/exercises/model.d.ts.map +1 -0
- package/dist/exercises/model.js +360 -0
- package/dist/exercises/model.js.map +1 -0
- package/dist/exercises/record.d.ts +94 -0
- package/dist/exercises/record.d.ts.map +1 -0
- package/dist/exercises/record.js +9 -0
- package/dist/exercises/record.js.map +1 -0
- package/dist/exercises/runtime.d.ts +65 -0
- package/dist/exercises/runtime.d.ts.map +1 -0
- package/dist/exercises/runtime.js +1261 -0
- package/dist/exercises/runtime.js.map +1 -0
- package/dist/exercises/shuffle.d.ts +13 -0
- package/dist/exercises/shuffle.d.ts.map +1 -0
- package/dist/exercises/shuffle.js +56 -0
- package/dist/exercises/shuffle.js.map +1 -0
- package/dist/exercises/styles.d.ts +51 -0
- package/dist/exercises/styles.d.ts.map +1 -0
- package/dist/exercises/styles.js +221 -0
- package/dist/exercises/styles.js.map +1 -0
- package/dist/exercises/text.d.ts +31 -0
- package/dist/exercises/text.d.ts.map +1 -0
- package/dist/exercises/text.js +82 -0
- package/dist/exercises/text.js.map +1 -0
- package/dist/exercises/types.d.ts +125 -0
- package/dist/exercises/types.d.ts.map +1 -0
- package/dist/exercises/types.js +9 -0
- package/dist/exercises/types.js.map +1 -0
- package/dist/exercises/warnings.d.ts +16 -0
- package/dist/exercises/warnings.d.ts.map +1 -0
- package/dist/exercises/warnings.js +55 -0
- package/dist/exercises/warnings.js.map +1 -0
- package/dist/html-backend.d.ts +6 -0
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +67 -6
- package/dist/html-backend.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/measure/cache.d.ts.map +1 -1
- package/dist/measure/cache.js +6 -1
- package/dist/measure/cache.js.map +1 -1
- package/dist/measure/rich.d.ts.map +1 -1
- package/dist/measure/rich.js +11 -1
- package/dist/measure/rich.js.map +1 -1
- package/dist/migrate/markdownRuns.js +1 -1
- package/dist/migrate/markdownRuns.js.map +1 -1
- package/dist/numbering.d.ts +3 -0
- package/dist/numbering.d.ts.map +1 -1
- package/dist/numbering.js +6 -0
- package/dist/numbering.js.map +1 -1
- package/dist/parse/blockParser.d.ts.map +1 -1
- package/dist/parse/blockParser.js +47 -4
- package/dist/parse/blockParser.js.map +1 -1
- package/dist/parse/exerciseGaps.d.ts +50 -0
- package/dist/parse/exerciseGaps.d.ts.map +1 -0
- package/dist/parse/exerciseGaps.js +151 -0
- package/dist/parse/exerciseGaps.js.map +1 -0
- package/dist/parse/index.d.ts +1 -1
- package/dist/parse/index.d.ts.map +1 -1
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/sourceMapping.d.ts.map +1 -1
- package/dist/parse/sourceMapping.js +21 -0
- package/dist/parse/sourceMapping.js.map +1 -1
- package/dist/parse/types.d.ts +49 -1
- package/dist/parse/types.d.ts.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +124 -29
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +1 -0
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/chips.d.ts.map +1 -1
- package/dist/pipeline/chips.js +2 -0
- package/dist/pipeline/chips.js.map +1 -1
- package/dist/pipeline/columnBalancing.d.ts +19 -4
- package/dist/pipeline/columnBalancing.d.ts.map +1 -1
- package/dist/pipeline/columnBalancing.js +31 -3
- package/dist/pipeline/columnBalancing.js.map +1 -1
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +6 -0
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +29 -1
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/continuation.d.ts.map +1 -1
- package/dist/pipeline/continuation.js +21 -6
- package/dist/pipeline/continuation.js.map +1 -1
- package/dist/pipeline/measureContentBlock.d.ts +9 -1
- package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
- package/dist/pipeline/measureContentBlock.js +4 -3
- package/dist/pipeline/measureContentBlock.js.map +1 -1
- package/dist/pipeline/outline.d.ts.map +1 -1
- package/dist/pipeline/outline.js +1 -0
- package/dist/pipeline/outline.js.map +1 -1
- package/dist/pipeline/textWrap.js +1 -1
- package/dist/pipeline/textWrap.js.map +1 -1
- package/dist/types.d.ts +331 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +74 -4
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/package.json +1 -1
package/dist/pipeline/build.js
CHANGED
|
@@ -22,6 +22,9 @@ import { stampCentralBaselines } from './verticalMetrics.js';
|
|
|
22
22
|
import { orientLinesToFrames } from './mirrorFrame.js';
|
|
23
23
|
import { verseMarginTopPx } from './buildBlockKind.js';
|
|
24
24
|
import { verseLinesSettings, versesLineByLine } from './verseLines.js';
|
|
25
|
+
import { exerciseOutline, exerciseSettingsFor, expandDocumentExercises, markTurnedBoxes, nameExerciseAnchors, needsExerciseExpansion, withExerciseStyles } from '../exercises/build.js';
|
|
26
|
+
import { hasExercises } from '../exercises/model.js';
|
|
27
|
+
import { exerciseDigitalOf } from '../exercises/digital.js';
|
|
25
28
|
import { arabicNumbersOf, resolveAllConfig, computeBaselineGrid, computeColumnBboxes, resolvedDirection, resolvedLocale, validateConfigCache } from './config.js';
|
|
26
29
|
import { resolveDebugConfig } from '../defaults/debug.js';
|
|
27
30
|
import { defaultFontSet, documentFontFaces, faceRequestKey, fontFallbacks } from '../fonts/faces.js';
|
|
@@ -63,7 +66,7 @@ import { comicStripExtent, comicStripOffset, comicStripPlacement, comicStripWidt
|
|
|
63
66
|
import { comicSpreadGutter, isComicSpread, layoutComicSpread } from '../comics/spread.js';
|
|
64
67
|
import { comicTextPanelsSupported, layoutComicTextFrames, layoutStripTextFrames, planComicTextPanels, wrapComicTextPanels } from '../comics/textPanels.js';
|
|
65
68
|
import { applyStyleAttrs, computePageMetrics, composedLeafConfig, flowPageMirrored, pageMirrored, sheetRectToFlow, isMarkerBlock, nextNonMarkerBlock, spaceLinesAfter, prevNonMarkerBlock, rollbackTrailingBlocks, } from './buildHelpers.js';
|
|
66
|
-
import { measureContentBlock } from './measureContentBlock.js';
|
|
69
|
+
import { measureContentBlock, wordSpacingProfile } from './measureContentBlock.js';
|
|
67
70
|
import { containerStyle, paragraphStyleIdOf, planParagraphContainers } from './paragraphContainers.js';
|
|
68
71
|
import { appendChapterEndNotes, footnoteIdsOfLines, footnoteParagraphStyle, noteContentBlock, noteNumberPositionOf, numberFootnotes, footnoteFormatOf, numberFootnotesByPlacement, sameFootnoteNumbers, splitFootnoteDefinitions, withFootnoteStyle, } from './footnotes.js';
|
|
69
72
|
import { planParts, derivePartMeasureContext } from './parts.js';
|
|
@@ -75,6 +78,7 @@ import { enumerateCurrentPageSlots, measureFloatBand, floatGapAbove, floatGapBel
|
|
|
75
78
|
import { computeHeadingContext, computeResourceNumberingState, } from './resourceNumbering.js';
|
|
76
79
|
import { documentLocale, effectiveResourceTypes } from '../defaults/resourceTypes.js';
|
|
77
80
|
import { resolveCalloutStylesConfig } from '../defaults/calloutStyles.js';
|
|
81
|
+
import { relinkToPalette } from '../defaults/shared.js';
|
|
78
82
|
import { codeBoxStyle, codeParseOptions, wrapCodeBlocks } from './codeBlocks.js';
|
|
79
83
|
import { expandScoreBlocks, scoreInfoOf, scoreKeepHeight } from './scoreBlocks.js';
|
|
80
84
|
import { buildCodeLineNumbers, codeOverflowWarnings } from './codeLines.js';
|
|
@@ -185,6 +189,11 @@ function chapterNumbersOfContent(blocks, resolved, prefixes, values, ordinalOffs
|
|
|
185
189
|
* looseness counts from that optimum, so such a paragraph asks it for one
|
|
186
190
|
* line less (EF-129: asked for one more, it could only match the target by
|
|
187
191
|
* falling back to its optimum, unchecked).
|
|
192
|
+
*
|
|
193
|
+
* With `headings.balancing.fillShortColumns` (#1236) `extraLines` may be
|
|
194
|
+
* -1: the paragraph is set a line short, its tracking rungs taken in the
|
|
195
|
+
* other direction (tighter letters), and the outcome recorded as negative
|
|
196
|
+
* tracking.
|
|
188
197
|
*/
|
|
189
198
|
function measureLooseParagraph(rawBlock, blockIdx, columnWidth, ctx, styleOverride, extraLines, trackingLadder, looseOutcome, offGrid) {
|
|
190
199
|
const base = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, { styleOverride });
|
|
@@ -211,23 +220,32 @@ function measureLooseParagraph(rawBlock, blockIdx, columnWidth, ctx, styleOverri
|
|
|
211
220
|
const onGrid = ctx.resolved.cjk.grid.enabled && composesAsCjk(rawBlock.text);
|
|
212
221
|
const ladder = trackingLadder.length > 1 && (onGrid || mostlyJoiningScript(rawBlock.text)) ? trackingLadder.slice(0, 1) : trackingLadder;
|
|
213
222
|
const gridCap = onGrid ? { gridTrackingEm: ctx.resolved.headings.balancing.maxTracking / 1000 } : {};
|
|
214
|
-
|
|
223
|
+
// With `fillShortColumns` (#1236) the limit is the one the runt fix
|
|
224
|
+
// keeps (EF-65): no justified line looser than `maxWordSpacing` or than
|
|
225
|
+
// the paragraph's own loosest one, and no more lines set ragged past 3x
|
|
226
|
+
// than it had. A narrow column of long words often passes the absolute
|
|
227
|
+
// limit in every setting of a paragraph, the breaker's own included.
|
|
228
|
+
const natural = ctx.resolved.headings.balancing.fillShortColumns ? wordSpacingProfile(base.measured.lines) : undefined;
|
|
229
|
+
const maxWordSpacing = Math.max(ctx.resolved.bodyText.maxWordSpacing, natural?.loosest ?? 0) + 1e-9;
|
|
215
230
|
// A CJK line spread past its tracking cap (`cjkLoose`) is past the
|
|
216
231
|
// limit too.
|
|
217
|
-
const withinLimit = (lines) =>
|
|
232
|
+
const withinLimit = (lines) => natural
|
|
233
|
+
? lines.every((l) => !l.cjkLoose) && ((p) => p.loosest <= maxWordSpacing && p.ragged <= natural.ragged)(wordSpacingProfile(lines))
|
|
234
|
+
: lines.every((l) => l.isLastLine || ((l.justifiedSpaceRatio === undefined || l.justifiedSpaceRatio <= maxWordSpacing) && !l.cjkLoose));
|
|
218
235
|
const gains = (loose) => loose !== null
|
|
219
236
|
&& loose.measured.lines.length === target
|
|
220
237
|
&& !loose.measured.lastLineRunt
|
|
221
238
|
&& withinLimit(loose.measured.lines);
|
|
239
|
+
const sign = extraLines < 0 ? -1 : 1;
|
|
222
240
|
for (const tracking of ladder) {
|
|
223
241
|
const loose = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, {
|
|
224
242
|
styleOverride,
|
|
225
243
|
looseness,
|
|
226
|
-
trackingEm: tracking > 0 ? tracking / 1000 : undefined,
|
|
244
|
+
trackingEm: tracking > 0 ? (sign * tracking) / 1000 : undefined,
|
|
227
245
|
...gridCap,
|
|
228
246
|
});
|
|
229
247
|
if (gains(loose)) {
|
|
230
|
-
looseOutcome.set(blockIdx, tracking);
|
|
248
|
+
looseOutcome.set(blockIdx, sign * tracking);
|
|
231
249
|
return loose;
|
|
232
250
|
}
|
|
233
251
|
}
|
|
@@ -288,7 +306,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
288
306
|
* band above (a page-span box, a top float band) or a caption below has
|
|
289
307
|
* shortened it, so what does not fit it may still fit a full column. */
|
|
290
308
|
const shortSideColumn = (page, col) => col.bbox.height < page.contentArea.height - 0.5;
|
|
291
|
-
|
|
309
|
+
// Rebound once the content is read when it holds exercises (#1227): the
|
|
310
|
+
// config then also carries their chip and paragraph styles.
|
|
311
|
+
let resolved = resolveAllConfig(config);
|
|
292
312
|
// Bitmaps sized by the document's `layout.bitmapResolution` carry it as
|
|
293
313
|
// their own `resolution` from here on (#631); under the default policy
|
|
294
314
|
// the content is untouched.
|
|
@@ -506,7 +526,21 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
506
526
|
// The citations of resource captions and notes (#529), formatted with
|
|
507
527
|
// the text's.
|
|
508
528
|
const captionCitations = citationsApplied && citationsApplied.captions.size > 0 ? citationsApplied.captions : undefined;
|
|
509
|
-
|
|
529
|
+
// Exercises (#1227): each set as a box of ordinary blocks, its solutions
|
|
530
|
+
// where the book prints them.
|
|
531
|
+
const exerciseExpansion = needsExerciseExpansion(citationsApplied?.blocks ?? parsedBody)
|
|
532
|
+
? expandDocumentExercises(citationsApplied?.blocks ?? parsedBody, resolved, computeColumnBboxes(pageMetrics.contentArea, resolved)[0]?.width ?? pageMetrics.contentArea.width, continuation, content.outline)
|
|
533
|
+
: undefined;
|
|
534
|
+
if (exerciseExpansion) {
|
|
535
|
+
resolved = withExerciseStyles(resolved);
|
|
536
|
+
// The document's config names the exercises' chip and paragraph
|
|
537
|
+
// styles too: an output that styles its text from the config (a
|
|
538
|
+
// reflowable EPUB, #1231) finds them there.
|
|
539
|
+
doc.config = resolved;
|
|
540
|
+
doc.exercises = exerciseExpansion.records;
|
|
541
|
+
doc.exerciseDigital = exerciseDigitalOf(exerciseSettingsFor(resolved));
|
|
542
|
+
}
|
|
543
|
+
const footnoteSplit = splitFootnoteDefinitions(exerciseExpansion?.blocks ?? citationsApplied?.blocks ?? parsedBody);
|
|
510
544
|
const footnoteDefs = footnoteSplit.defs;
|
|
511
545
|
// Numbered by page or column (`numbering: 'page'`), a note takes the
|
|
512
546
|
// number the previous build gave it where it landed; the first build
|
|
@@ -534,7 +568,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
534
568
|
const resourceIds = new Set((content.resources ?? []).map((r) => r.id));
|
|
535
569
|
const anchorRefs = hasAnchorRefs(parsedBlocks, resourceIds);
|
|
536
570
|
const outline = content.outline
|
|
537
|
-
?? (hasTocDirective(parsedBlocks) || hasIndexDirective(parsedBlocks) || anchorRefs
|
|
571
|
+
?? (hasTocDirective(parsedBlocks) || hasIndexDirective(parsedBlocks) || anchorRefs
|
|
572
|
+
// A reference to an exercise prints its name (#1225).
|
|
573
|
+
? nameExerciseAnchors(computeOutline(parsedBlocks, resolved, headingStart, continuation?.statementCounters), exerciseExpansion?.records ?? [])
|
|
574
|
+
: undefined);
|
|
538
575
|
// A story carried in from another chapter (#943) may refer to anchors too.
|
|
539
576
|
const carriedRefs = continuation?.jumps?.some((j) => j.outline) === true;
|
|
540
577
|
const anchorRefContext = anchorRefs || carriedRefs
|
|
@@ -630,6 +667,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
630
667
|
const syntheticBoxes = [
|
|
631
668
|
...(spanEmbeds.wrapped ? [spanEmbedStyle(bodyStyle.lineHeightPx, resolved.layout.inlineResourceGap !== 'above')] : []),
|
|
632
669
|
...(codeBoxes.wrapped ? [codeBoxStyle(resolved)] : []),
|
|
670
|
+
// The boxes exercises are set in (#1227).
|
|
671
|
+
...(exerciseExpansion?.wrapped ? exerciseExpansion.boxes : []),
|
|
633
672
|
// The frameless box of a composed page's story (#855).
|
|
634
673
|
...(composedWrap.wrapped || comicText.wrapped ? [frameBoxStyle(Math.max(resolved.bodyText.avoidOrphans ? resolved.bodyText.orphanMinLines : 1, resolved.bodyText.avoidWidows ? resolved.bodyText.widowMinLines : 1))] : []),
|
|
635
674
|
// The boxes of main-flow groups: sub-columns a body line apart in the
|
|
@@ -638,12 +677,16 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
638
677
|
? flowColumnsStyles(bodyStyle.lineHeightPx, dimensionToPx(resolved.layout.gutterWidth, resolved.page.dpi, bodyStyle.fontSizePx), resolved.bodyText.paragraphSpacing === false ? { value: 0, unit: 'px' } : { value: bodyStyle.marginBottomPx, unit: 'px' }, Math.max(resolved.bodyText.avoidOrphans ? resolved.bodyText.orphanMinLines : 1, resolved.bodyText.avoidWidows ? resolved.bodyText.widowMinLines : 1))
|
|
639
678
|
: []),
|
|
640
679
|
];
|
|
680
|
+
const exerciseBoxIds = new Set(exerciseExpansion?.wrapped ? exerciseExpansion.boxes.map((b) => b.id) : []);
|
|
641
681
|
const calloutStyles = syntheticBoxes.length > 0
|
|
642
|
-
? [...resolved.calloutStyles, ...resolveCalloutStylesConfig(syntheticBoxes, resolved.bodyText, resolved.headings, resolved.unorderedLists, config?.locale)
|
|
682
|
+
? [...resolved.calloutStyles, ...resolveCalloutStylesConfig(syntheticBoxes, resolved.bodyText, resolved.headings, resolved.unorderedLists, config?.locale)
|
|
683
|
+
// The exercise boxes' defaults (a solutions title, a bank's frame)
|
|
684
|
+
// link to `main-color`: set them to the document's palette.
|
|
685
|
+
.map((s) => (exerciseBoxIds.has(s.id) ? relinkToPalette(s, config?.colorPalette) : s))]
|
|
643
686
|
: resolved.calloutStyles;
|
|
644
687
|
// A box nested in another looks its style up in the configuration it is
|
|
645
688
|
// laid out with: the listings' box among them.
|
|
646
|
-
const calloutResolved = codeBoxes.wrapped || flowGroups.wrapped || composedWrap.wrapped || comicText.wrapped ? { ...resolved, calloutStyles } : resolved;
|
|
689
|
+
const calloutResolved = codeBoxes.wrapped || flowGroups.wrapped || composedWrap.wrapped || comicText.wrapped || exerciseExpansion?.wrapped ? { ...resolved, calloutStyles } : resolved;
|
|
647
690
|
const blockquoteStyle = resolveBlockquoteStyle(resolved);
|
|
648
691
|
const listLevelIndentsPx = computeLevelIndentsPx(resolved, bodyStyle.fontSizePx);
|
|
649
692
|
const orderedMetrics = computeOrderedListRunMetrics(contentBlocks, resolved, bodyStyle.fontSizePx);
|
|
@@ -3336,6 +3379,17 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3336
3379
|
&& !(lvl.advancedDesign.enabled && lvl.advancedDesign.slot.elements.length > 0)
|
|
3337
3380
|
&& !headingIsHidden(raw, lvl);
|
|
3338
3381
|
};
|
|
3382
|
+
/** Whether a heading is measured across the content area: a `span:
|
|
3383
|
+
* 'page'` level (or style) that prints, painted by the default opener or
|
|
3384
|
+
* by a design of its own, which lays out across the page as well. At the
|
|
3385
|
+
* column width a designed opener's title broke into more lines than the
|
|
3386
|
+
* design paints, and those lines set its band: taller than the same
|
|
3387
|
+
* heading without a design, so that it no longer fitted under the text
|
|
3388
|
+
* of a page the plain one opens on (#1224). */
|
|
3389
|
+
const measuredAcrossPage = (raw) => {
|
|
3390
|
+
const lvl = headingLevels.forBlock(raw);
|
|
3391
|
+
return lvl?.span === 'page' && !headingIsHidden(raw, lvl);
|
|
3392
|
+
};
|
|
3339
3393
|
/** Page-spanning heading placed in `headingCol`, reserving `h` px from the
|
|
3340
3394
|
* top of the page: the same vertical band is reserved in every other
|
|
3341
3395
|
* column of the page, so body text under the opener band starts below it
|
|
@@ -3522,6 +3576,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3522
3576
|
/** The band (`page:band`) a keep-with-next rollback last took each
|
|
3523
3577
|
* heading out of, by content index. */
|
|
3524
3578
|
const headingLeftBand = new Map();
|
|
3579
|
+
/** The band (`page:band`) each page-span heading opened under the text
|
|
3580
|
+
* of its page was set at the head of (#1238), by content index. */
|
|
3581
|
+
const spanHeadingOpenedHere = new Map();
|
|
3525
3582
|
/**
|
|
3526
3583
|
* A `span: 'page'` heading with `spanBreak: false` (#539) opens where the
|
|
3527
3584
|
* text reaches: the band the flow is in closes level under its text (a
|
|
@@ -3556,7 +3613,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3556
3613
|
// opener paints it) or its design's band, with its margins.
|
|
3557
3614
|
const width = page.contentArea.width;
|
|
3558
3615
|
const measureCtx = partPlan.byBlock[blockIdx] ? partMeasureCtx : sectionMeasureCtx(sectionPlan.byBlock[blockIdx]);
|
|
3559
|
-
const measured = measureContentBlock(rawBlock, blockIdx,
|
|
3616
|
+
const measured = measureContentBlock(rawBlock, blockIdx, measuredAcrossPage(rawBlock) ? width : cols[0].bbox.width, measureCtx);
|
|
3560
3617
|
if (!measured)
|
|
3561
3618
|
return false;
|
|
3562
3619
|
const style = measured.kind.style;
|
|
@@ -3586,6 +3643,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3586
3643
|
uncapBand(cols, uncappedBottoms);
|
|
3587
3644
|
if (!closeBandAt(page, cols, cutY, top, cursor))
|
|
3588
3645
|
return false;
|
|
3646
|
+
spanHeadingOpenedHere.set(blockIdx, `${page.index}:${currentBand(page, cursor)}`);
|
|
3589
3647
|
pendingSpacing = 0;
|
|
3590
3648
|
return true;
|
|
3591
3649
|
};
|
|
@@ -3976,6 +4034,21 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3976
4034
|
n++;
|
|
3977
4035
|
return n;
|
|
3978
4036
|
};
|
|
4037
|
+
/** Whether a block moving on whole may take the `run` headings closing
|
|
4038
|
+
* `col` along. A run that is all the column holds would open the next
|
|
4039
|
+
* column just as it opens this one, and is rolled back no further —
|
|
4040
|
+
* unless it opens a band a page-span heading cut under the text of the
|
|
4041
|
+
* page (`spanBreak: false`, #1238): rolled back, that heading opens the
|
|
4042
|
+
* next page instead (`headingLeftBand`), with the block it is kept with. */
|
|
4043
|
+
const headingRunMayLeave = (col, run) => {
|
|
4044
|
+
if (run <= 0)
|
|
4045
|
+
return false;
|
|
4046
|
+
if (run < col.blocks.length)
|
|
4047
|
+
return true;
|
|
4048
|
+
const head = col.blocks[0];
|
|
4049
|
+
return head.contentIndex !== undefined
|
|
4050
|
+
&& spanHeadingOpenedHere.get(head.contentIndex) === `${cursor.pageIndex}:${col.band ?? 0}`;
|
|
4051
|
+
};
|
|
3979
4052
|
/**
|
|
3980
4053
|
* An index group divided between two columns keeps at least
|
|
3981
4054
|
* `index.groups.splitMinEntries` entries on each side (#712). `entry` is
|
|
@@ -5482,7 +5555,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5482
5555
|
break;
|
|
5483
5556
|
}
|
|
5484
5557
|
pendingSpacing = 0;
|
|
5485
|
-
if (part === 0 && resolved.headings.keepWithNext &&
|
|
5558
|
+
if (part === 0 && resolved.headings.keepWithNext && headingRunMayLeave(curCol, run)) {
|
|
5486
5559
|
const rolledBack = rollbackHeadings(curCol);
|
|
5487
5560
|
advanceToNextColumn(doc, cursor, geomResolved, contentArea, pageWidthPx, pageHeightPx, onNewPage);
|
|
5488
5561
|
return (rolledBack[0].contentIndex ?? startIdx - rolledBack.length) - 1;
|
|
@@ -5874,7 +5947,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5874
5947
|
const spacingBefore = Math.max(pendingSpacing, floatGapPx);
|
|
5875
5948
|
if (col.blocks.length > 0 && height > col.availableHeight - spacingBefore + FIT_EPS) {
|
|
5876
5949
|
const run = trailingHeadingRun(col);
|
|
5877
|
-
if (
|
|
5950
|
+
if (headingRunMayLeave(col, run)) {
|
|
5878
5951
|
const rolled = rollbackHeadings(col);
|
|
5879
5952
|
pendingSpacing = 0;
|
|
5880
5953
|
advanceToNextColumn(doc, cursor, geomResolved, contentArea, pageWidthPx, pageHeightPx, onAtomicNewPage(height));
|
|
@@ -7118,11 +7191,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
7118
7191
|
&& knownOutcome === undefined
|
|
7119
7192
|
&& (looseGained.get(budget.group) ?? 0) >= budget.need;
|
|
7120
7193
|
const tryLoose = looseLines !== undefined && !budgetMet && knownOutcome !== null;
|
|
7121
|
-
// A page-span heading
|
|
7122
|
-
// default opener
|
|
7123
|
-
//
|
|
7124
|
-
//
|
|
7125
|
-
const measureWidth = rawBlock.type === 'heading' &&
|
|
7194
|
+
// A page-span heading is painted across the content area, by the
|
|
7195
|
+
// default opener or by its design: measured at that width, its band
|
|
7196
|
+
// holds the title as it is painted (EF-100), and a design sets it no
|
|
7197
|
+
// taller than the lines it paints (#1224).
|
|
7198
|
+
const measureWidth = rawBlock.type === 'heading' && measuredAcrossPage(rawBlock)
|
|
7126
7199
|
? doc.pages[cursor.pageIndex].contentArea.width
|
|
7127
7200
|
: col.bbox.width;
|
|
7128
7201
|
// A resource on a vertical page stands upright, its frame at most as
|
|
@@ -7598,8 +7671,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
7598
7671
|
* layout with text in both columns, a styled section with its own
|
|
7599
7672
|
* margins — is broken again there: its placed lines stay, the rest is
|
|
7600
7673
|
* broken for the new column. Not the loose paragraphs balancing runs
|
|
7601
|
-
* long (placed whole where they were measured), nor a
|
|
7602
|
-
*
|
|
7674
|
+
* long (placed whole where they were measured), nor a page-span
|
|
7675
|
+
* heading, painted across the page. */
|
|
7603
7676
|
let brokenForWidth = measureWidth;
|
|
7604
7677
|
let laterWidths = [];
|
|
7605
7678
|
/** Text wrap (#627): the lines of the block set short beside what text
|
|
@@ -7607,7 +7680,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
7607
7680
|
let wrapInsets = [];
|
|
7608
7681
|
const wrapsBeside = canSplit && runsBesideWrap(rawBlock);
|
|
7609
7682
|
const rebreaksForColumn = !tryLoose && rawBlock.toc === undefined && rawBlock.index === undefined
|
|
7610
|
-
&& (canSplit || (vdtType === 'heading' && !
|
|
7683
|
+
&& (canSplit || (vdtType === 'heading' && !measuredAcrossPage(rawBlock)));
|
|
7611
7684
|
/** Measure the block again for the columns it is set in (see
|
|
7612
7685
|
* `laterWidths`), with `extra` options. */
|
|
7613
7686
|
const measureForColumns = (extra) => measureContentBlock(rawBlock, blockIdx, brokenForWidth, blockMeasureCtx, {
|
|
@@ -8157,7 +8230,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
8157
8230
|
if (partIndex === 0 && curCol.blocks.length > 0) {
|
|
8158
8231
|
// Moving on whole must not strand a heading closing the column.
|
|
8159
8232
|
const headingRun = trailingHeadingRun(curCol);
|
|
8160
|
-
if (resolved.headings.keepWithNext &&
|
|
8233
|
+
if (resolved.headings.keepWithNext && headingRunMayLeave(curCol, headingRun)) {
|
|
8161
8234
|
const rolledBack = rollbackHeadings(curCol);
|
|
8162
8235
|
if (rolledBack.length > 0) {
|
|
8163
8236
|
blockIdx = (rolledBack[0].contentIndex ?? blockIdx - rolledBack.length) - 1;
|
|
@@ -8728,7 +8801,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
8728
8801
|
// Only a short column (under a float, a page-span box) lets the run
|
|
8729
8802
|
// move on to a taller one.
|
|
8730
8803
|
const headingRun = trailingHeadingRun(curCol);
|
|
8731
|
-
const runFillsColumn = headingRun > 0 && headingRun
|
|
8804
|
+
const runFillsColumn = headingRun > 0 && !headingRunMayLeave(curCol, headingRun);
|
|
8732
8805
|
const strands = partIndex === 0 && vdtType !== 'heading'
|
|
8733
8806
|
&& headingRun > 0 && !runFillsColumn;
|
|
8734
8807
|
const pullsRun = vdtType === 'heading' && (!runFillsColumn || shortColumn);
|
|
@@ -9076,6 +9149,9 @@ export function* buildDocumentGen(content, config, cache, options) {
|
|
|
9076
9149
|
syncFontSet(fontSet);
|
|
9077
9150
|
validateConfigCache(config);
|
|
9078
9151
|
const doc = yield* buildDocumentRounds(content, config, cache, options);
|
|
9152
|
+
// Boxes printed upside down (#1229): their blocks learn the point they
|
|
9153
|
+
// turn about.
|
|
9154
|
+
markTurnedBoxes(doc);
|
|
9079
9155
|
// References and markup the source names wrongly: read from the source
|
|
9080
9156
|
// once, located on the pages of the finished layout. Kept apart from
|
|
9081
9157
|
// `doc.warnings`, whose entries keep their postext 1.4 shape.
|
|
@@ -9391,10 +9467,10 @@ function* buildDocumentRounds(content, config, cache, options) {
|
|
|
9391
9467
|
if (!hasAnchorRefs(parsed, new Set((content.resources ?? []).map((r) => r.id))))
|
|
9392
9468
|
return false;
|
|
9393
9469
|
const resolved = resolveAllConfig(config);
|
|
9394
|
-
return printsAnchorPages(parsed, anchorTargetsOf(
|
|
9470
|
+
return printsAnchorPages(parsed, anchorTargetsOf(ownOutline(parsed, resolved, content.continuation)), resolved.crossRefs.defaultStyle);
|
|
9395
9471
|
};
|
|
9396
9472
|
if (hasTocDirective(parsed) || hasIndexDirective(parsed) || pageRefs()) {
|
|
9397
|
-
let outline =
|
|
9473
|
+
let outline = ownOutline(parsed, resolveAllConfig(config), content.continuation);
|
|
9398
9474
|
let doc = withIndexMarks(yield* buildDocumentNumbered({ ...content, outline }, config, cache, options, 0, citing), content);
|
|
9399
9475
|
for (let round = 0; round < MAX_TOC_ROUNDS; round++) {
|
|
9400
9476
|
const after = outlineFromDoc(doc, outline);
|
|
@@ -9408,6 +9484,12 @@ function* buildDocumentRounds(content, config, cache, options) {
|
|
|
9408
9484
|
}
|
|
9409
9485
|
return withIndexMarks(yield* buildDocumentNumbered(content, config, cache, options, 0, citing), content);
|
|
9410
9486
|
}
|
|
9487
|
+
/** A document's own outline, its exercises' anchors named as a reference
|
|
9488
|
+
* prints them ("Exercise 3", #1225). */
|
|
9489
|
+
function ownOutline(parsed, resolved, continuation) {
|
|
9490
|
+
const outline = computeOutline(parsed, resolved, continuation?.headings, continuation?.statementCounters);
|
|
9491
|
+
return hasExercises(parsed) ? nameExerciseAnchors(outline, exerciseOutline(parsed, resolved, continuation).records) : outline;
|
|
9492
|
+
}
|
|
9411
9493
|
/** The number each note's marker prints in `doc` (its first marker). */
|
|
9412
9494
|
function printedFootnoteNumbers(doc) {
|
|
9413
9495
|
const out = new Map();
|
|
@@ -9620,6 +9702,7 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9620
9702
|
stable: score === 0,
|
|
9621
9703
|
failedLoose: old?.failedLoose ?? new Set(),
|
|
9622
9704
|
failedLines: old?.failedLines ?? new Set(),
|
|
9705
|
+
failedTight: old?.failedTight ?? new Set(),
|
|
9623
9706
|
};
|
|
9624
9707
|
});
|
|
9625
9708
|
};
|
|
@@ -9798,7 +9881,7 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9798
9881
|
let hit = false;
|
|
9799
9882
|
for (const k of newLoose)
|
|
9800
9883
|
if (!cands || cands.has(k)) {
|
|
9801
|
-
s
|
|
9884
|
+
failLoose(s, k);
|
|
9802
9885
|
hit = true;
|
|
9803
9886
|
}
|
|
9804
9887
|
if (hit && looseFirst)
|
|
@@ -9817,6 +9900,13 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9817
9900
|
return true;
|
|
9818
9901
|
return blacklist(null);
|
|
9819
9902
|
};
|
|
9903
|
+
/** The paragraphs the current proposal sets a line short (#1236): their
|
|
9904
|
+
* failures go to the segment's `failedTight`, the others' to
|
|
9905
|
+
* `failedLoose`. */
|
|
9906
|
+
let tightKeys = new Set();
|
|
9907
|
+
const failLoose = (s, k) => {
|
|
9908
|
+
(tightKeys.has(k) ? s.failedTight : s.failedLoose).add(k);
|
|
9909
|
+
};
|
|
9820
9910
|
let balancingPasses = 0;
|
|
9821
9911
|
function* balance() {
|
|
9822
9912
|
while (balancingPasses < MAX_BALANCING_PASSES_PER_DOCUMENT) {
|
|
@@ -9827,11 +9917,14 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9827
9917
|
const owner = keyOwners(gaps);
|
|
9828
9918
|
const failedLoose = new Set();
|
|
9829
9919
|
const failedLines = new Set();
|
|
9920
|
+
const failedTight = new Set();
|
|
9830
9921
|
for (const s of segments) {
|
|
9831
9922
|
for (const k of s.failedLoose)
|
|
9832
9923
|
failedLoose.add(k);
|
|
9833
9924
|
for (const k of s.failedLines)
|
|
9834
9925
|
failedLines.add(k);
|
|
9926
|
+
for (const k of s.failedTight)
|
|
9927
|
+
failedTight.add(k);
|
|
9835
9928
|
}
|
|
9836
9929
|
const proposal = proposeBalanceLines(best.doc, best.forcedBreakPages, applied, {
|
|
9837
9930
|
maxLinesPerHeading: balancing.maxLinesPerHeading,
|
|
@@ -9847,6 +9940,7 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9847
9940
|
optimalLineBreaking: best.doc.config.bodyText.optimalLineBreaking,
|
|
9848
9941
|
failedLoose,
|
|
9849
9942
|
failedLines,
|
|
9943
|
+
...(balancing.fillShortColumns ? { fillShortColumns: true, failedTight } : {}),
|
|
9850
9944
|
closingBox: balancing.closingBox,
|
|
9851
9945
|
...(onGrid && balancing.gridLines === 'off' ? { gridLines: false } : {}),
|
|
9852
9946
|
});
|
|
@@ -9879,6 +9973,7 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9879
9973
|
}
|
|
9880
9974
|
newLoose.push(k);
|
|
9881
9975
|
}
|
|
9976
|
+
tightKeys = new Set(newLoose.filter((k) => proposal.loose.get(k) < 0));
|
|
9882
9977
|
const trying = new Set([...newLines, ...newLoose].map((k) => owner.get(k)));
|
|
9883
9978
|
if (trying.size === 0) {
|
|
9884
9979
|
// No stretch point can absorb the remaining gaps — stable.
|
|
@@ -9926,7 +10021,7 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9926
10021
|
// first) stay eligible for a later proposal.
|
|
9927
10022
|
const looseFailed = keysLoose.filter((k) => next.looseOutcome.get(k) === null);
|
|
9928
10023
|
for (const k of looseFailed)
|
|
9929
|
-
s
|
|
10024
|
+
failLoose(s, k);
|
|
9930
10025
|
const looseWon = keysLoose.filter((k) => typeof next.looseOutcome.get(k) === 'number');
|
|
9931
10026
|
// A box closing its column that moves down onto the column's last
|
|
9932
10027
|
// grid slot (or level with the column beside it) closes less than a
|
|
@@ -9969,11 +10064,11 @@ citing = { hints: new Map(), refused: new Set() }) {
|
|
|
9969
10064
|
if (!boxKeys.has(k))
|
|
9970
10065
|
s.failedLines.add(k);
|
|
9971
10066
|
for (const k of keysLoose)
|
|
9972
|
-
s
|
|
10067
|
+
failLoose(s, k);
|
|
9973
10068
|
}
|
|
9974
10069
|
else if (looseFailed.length > 0 || looseWon.length > 0) {
|
|
9975
10070
|
for (const k of looseWon)
|
|
9976
|
-
s
|
|
10071
|
+
failLoose(s, k);
|
|
9977
10072
|
}
|
|
9978
10073
|
else {
|
|
9979
10074
|
s.done = true;
|