postext 1.36.0 → 1.38.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.
- package/dist/__tests__/comics/frames.test.d.ts +2 -0
- package/dist/__tests__/comics/frames.test.d.ts.map +1 -0
- package/dist/__tests__/comics/frames.test.js +251 -0
- package/dist/__tests__/comics/frames.test.js.map +1 -0
- package/dist/__tests__/composed/continuation.test.d.ts +2 -0
- package/dist/__tests__/composed/continuation.test.d.ts.map +1 -0
- package/dist/__tests__/composed/continuation.test.js +352 -0
- package/dist/__tests__/composed/continuation.test.js.map +1 -0
- package/dist/__tests__/composed/continuationRules.test.d.ts +2 -0
- package/dist/__tests__/composed/continuationRules.test.d.ts.map +1 -0
- package/dist/__tests__/composed/continuationRules.test.js +389 -0
- package/dist/__tests__/composed/continuationRules.test.js.map +1 -0
- package/dist/__tests__/composed/contrast.test.d.ts +2 -0
- package/dist/__tests__/composed/contrast.test.d.ts.map +1 -0
- package/dist/__tests__/composed/contrast.test.js +124 -0
- package/dist/__tests__/composed/contrast.test.js.map +1 -0
- package/dist/__tests__/composed/corpus.test.js +7 -1
- package/dist/__tests__/composed/corpus.test.js.map +1 -1
- package/dist/__tests__/composed/coverImages.test.d.ts +2 -0
- package/dist/__tests__/composed/coverImages.test.d.ts.map +1 -0
- package/dist/__tests__/composed/coverImages.test.js +75 -0
- package/dist/__tests__/composed/coverImages.test.js.map +1 -0
- package/dist/__tests__/composed/decorationSize.test.d.ts +2 -0
- package/dist/__tests__/composed/decorationSize.test.d.ts.map +1 -0
- package/dist/__tests__/composed/decorationSize.test.js +79 -0
- package/dist/__tests__/composed/decorationSize.test.js.map +1 -0
- package/dist/__tests__/composed/emptyFrames.test.d.ts +2 -0
- package/dist/__tests__/composed/emptyFrames.test.d.ts.map +1 -0
- package/dist/__tests__/composed/emptyFrames.test.js +129 -0
- package/dist/__tests__/composed/emptyFrames.test.js.map +1 -0
- package/dist/__tests__/composed/fit.test.js +19 -10
- package/dist/__tests__/composed/fit.test.js.map +1 -1
- package/dist/__tests__/composed/groups.test.js +34 -0
- package/dist/__tests__/composed/groups.test.js.map +1 -1
- package/dist/__tests__/composed/headingDesigns.test.d.ts +2 -0
- package/dist/__tests__/composed/headingDesigns.test.d.ts.map +1 -0
- package/dist/__tests__/composed/headingDesigns.test.js +262 -0
- package/dist/__tests__/composed/headingDesigns.test.js.map +1 -0
- package/dist/__tests__/composed/labels.test.d.ts +2 -0
- package/dist/__tests__/composed/labels.test.d.ts.map +1 -0
- package/dist/__tests__/composed/labels.test.js +326 -0
- package/dist/__tests__/composed/labels.test.js.map +1 -0
- package/dist/__tests__/composed/outsidePrint.test.js +5 -2
- package/dist/__tests__/composed/outsidePrint.test.js.map +1 -1
- package/dist/__tests__/composed/ruledFrames.test.d.ts +2 -0
- package/dist/__tests__/composed/ruledFrames.test.d.ts.map +1 -0
- package/dist/__tests__/composed/ruledFrames.test.js +93 -0
- package/dist/__tests__/composed/ruledFrames.test.js.map +1 -0
- package/dist/__tests__/composed/sourceMap.test.js +26 -0
- package/dist/__tests__/composed/sourceMap.test.js.map +1 -1
- package/dist/__tests__/composed/spread.test.js +82 -0
- package/dist/__tests__/composed/spread.test.js.map +1 -1
- package/dist/__tests__/composed/spreadLabels.test.d.ts +2 -0
- package/dist/__tests__/composed/spreadLabels.test.d.ts.map +1 -0
- package/dist/__tests__/composed/spreadLabels.test.js +131 -0
- package/dist/__tests__/composed/spreadLabels.test.js.map +1 -0
- package/dist/__tests__/exports.test.js +23 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/migrate/fixtures/textbookPort.d.ts +14 -0
- package/dist/__tests__/migrate/fixtures/textbookPort.d.ts.map +1 -0
- package/dist/__tests__/migrate/fixtures/textbookPort.js +116 -0
- package/dist/__tests__/migrate/fixtures/textbookPort.js.map +1 -0
- package/dist/__tests__/migrate/labelsOnPictures.test.d.ts +2 -0
- package/dist/__tests__/migrate/labelsOnPictures.test.d.ts.map +1 -0
- package/dist/__tests__/migrate/labelsOnPictures.test.js +114 -0
- package/dist/__tests__/migrate/labelsOnPictures.test.js.map +1 -0
- package/dist/__tests__/migrate/textToMarkdown.test.d.ts +2 -0
- package/dist/__tests__/migrate/textToMarkdown.test.d.ts.map +1 -0
- package/dist/__tests__/migrate/textToMarkdown.test.js +268 -0
- package/dist/__tests__/migrate/textToMarkdown.test.js.map +1 -0
- package/dist/__tests__/paragraphDropCap.test.js +38 -0
- package/dist/__tests__/paragraphDropCap.test.js.map +1 -1
- package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts +2 -0
- package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts.map +1 -0
- package/dist/__tests__/paragraphStyleLetterSpacing.test.js +89 -0
- package/dist/__tests__/paragraphStyleLetterSpacing.test.js.map +1 -0
- package/dist/__tests__/pictureBelowPrintSize.test.d.ts +2 -0
- package/dist/__tests__/pictureBelowPrintSize.test.d.ts.map +1 -0
- package/dist/__tests__/pictureBelowPrintSize.test.js +82 -0
- package/dist/__tests__/pictureBelowPrintSize.test.js.map +1 -0
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +1 -1
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
- package/dist/__tests__/resourceTypePartial.test.d.ts +2 -0
- package/dist/__tests__/resourceTypePartial.test.d.ts.map +1 -0
- package/dist/__tests__/resourceTypePartial.test.js +60 -0
- package/dist/__tests__/resourceTypePartial.test.js.map +1 -0
- package/dist/canvas-backend/composed.d.ts +8 -2
- package/dist/canvas-backend/composed.d.ts.map +1 -1
- package/dist/canvas-backend/composed.js +52 -29
- package/dist/canvas-backend/composed.js.map +1 -1
- package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
- package/dist/canvas-backend/headerFooter.js +13 -0
- package/dist/canvas-backend/headerFooter.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +53 -24
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.d.ts +5 -1
- package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.js +60 -1
- package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
- package/dist/comics/frames.d.ts +82 -0
- package/dist/comics/frames.d.ts.map +1 -0
- package/dist/comics/frames.js +182 -0
- package/dist/comics/frames.js.map +1 -0
- package/dist/comics/index.d.ts +4 -1
- package/dist/comics/index.d.ts.map +1 -1
- package/dist/comics/index.js +3 -1
- package/dist/comics/index.js.map +1 -1
- package/dist/comics/layoutPage.d.ts +9 -0
- package/dist/comics/layoutPage.d.ts.map +1 -1
- package/dist/comics/layoutPage.js +53 -68
- package/dist/comics/layoutPage.js.map +1 -1
- package/dist/comics/page.d.ts.map +1 -1
- package/dist/comics/page.js +10 -1
- package/dist/comics/page.js.map +1 -1
- package/dist/comics/sourceMap.d.ts +60 -0
- package/dist/comics/sourceMap.d.ts.map +1 -0
- package/dist/comics/sourceMap.js +166 -0
- package/dist/comics/sourceMap.js.map +1 -0
- package/dist/comics/spread.d.ts.map +1 -1
- package/dist/comics/spread.js +6 -0
- package/dist/comics/spread.js.map +1 -1
- package/dist/comics/textPanels.d.ts +113 -0
- package/dist/comics/textPanels.d.ts.map +1 -0
- package/dist/comics/textPanels.js +327 -0
- package/dist/comics/textPanels.js.map +1 -0
- package/dist/comics/transform.d.ts +6 -2
- package/dist/comics/transform.d.ts.map +1 -1
- package/dist/comics/transform.js +14 -1
- package/dist/comics/transform.js.map +1 -1
- package/dist/comics/types.d.ts +10 -0
- package/dist/comics/types.d.ts.map +1 -1
- package/dist/composed/continuation.d.ts +152 -0
- package/dist/composed/continuation.d.ts.map +1 -0
- package/dist/composed/continuation.js +189 -0
- package/dist/composed/continuation.js.map +1 -0
- package/dist/composed/groups.d.ts +3 -0
- package/dist/composed/groups.d.ts.map +1 -1
- package/dist/composed/groups.js +34 -4
- package/dist/composed/groups.js.map +1 -1
- package/dist/composed/labels.d.ts +154 -0
- package/dist/composed/labels.d.ts.map +1 -0
- package/dist/composed/labels.js +524 -0
- package/dist/composed/labels.js.map +1 -0
- package/dist/composed/pageMemo.d.ts +3 -2
- package/dist/composed/pageMemo.d.ts.map +1 -1
- package/dist/composed/pageMemo.js +3 -1
- package/dist/composed/pageMemo.js.map +1 -1
- package/dist/composed/paint.d.ts +28 -3
- package/dist/composed/paint.d.ts.map +1 -1
- package/dist/composed/paint.js +48 -2
- package/dist/composed/paint.js.map +1 -1
- package/dist/composed/reflowLog.d.ts +3 -1
- package/dist/composed/reflowLog.d.ts.map +1 -1
- package/dist/composed/reflowLog.js +20 -6
- package/dist/composed/reflowLog.js.map +1 -1
- package/dist/composed/rules.d.ts +31 -0
- package/dist/composed/rules.d.ts.map +1 -0
- package/dist/composed/rules.js +45 -0
- package/dist/composed/rules.js.map +1 -0
- package/dist/composed/slotChecks.d.ts +2 -1
- package/dist/composed/slotChecks.d.ts.map +1 -1
- package/dist/composed/slotChecks.js +56 -2
- package/dist/composed/slotChecks.js.map +1 -1
- package/dist/composed/sourceMap.d.ts +0 -21
- package/dist/composed/sourceMap.d.ts.map +1 -1
- package/dist/composed/sourceMap.js +102 -8
- package/dist/composed/sourceMap.js.map +1 -1
- package/dist/composed/spread.d.ts +23 -5
- package/dist/composed/spread.d.ts.map +1 -1
- package/dist/composed/spread.js +52 -27
- package/dist/composed/spread.js.map +1 -1
- package/dist/composed/templates.d.ts +11 -0
- package/dist/composed/templates.d.ts.map +1 -1
- package/dist/composed/templates.js +51 -2
- package/dist/composed/templates.js.map +1 -1
- package/dist/composed/types.d.ts +43 -3
- package/dist/composed/types.d.ts.map +1 -1
- package/dist/composed/validate.d.ts.map +1 -1
- package/dist/composed/validate.js +5 -3
- package/dist/composed/validate.js.map +1 -1
- package/dist/configWarnings.d.ts +15 -0
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +40 -18
- package/dist/configWarnings.js.map +1 -1
- package/dist/contrast.d.ts +5 -3
- package/dist/contrast.d.ts.map +1 -1
- package/dist/contrast.js +46 -5
- package/dist/contrast.js.map +1 -1
- package/dist/defaults/headerFooter.d.ts.map +1 -1
- package/dist/defaults/headerFooter.js +4 -0
- package/dist/defaults/headerFooter.js.map +1 -1
- package/dist/defaults/paragraphStyles.d.ts +1 -1
- package/dist/defaults/paragraphStyles.d.ts.map +1 -1
- package/dist/defaults/paragraphStyles.js +5 -1
- package/dist/defaults/paragraphStyles.js.map +1 -1
- package/dist/defaults/resourceTypes.d.ts +15 -0
- package/dist/defaults/resourceTypes.d.ts.map +1 -1
- package/dist/defaults/resourceTypes.js +39 -1
- package/dist/defaults/resourceTypes.js.map +1 -1
- package/dist/design/layout.d.ts +7 -0
- package/dist/design/layout.d.ts.map +1 -1
- package/dist/design/layout.js +109 -16
- package/dist/design/layout.js.map +1 -1
- package/dist/design/slots.d.ts +12 -10
- package/dist/design/slots.d.ts.map +1 -1
- package/dist/design/slots.js +26 -2
- package/dist/design/slots.js.map +1 -1
- package/dist/fonts/faces.d.ts.map +1 -1
- package/dist/fonts/faces.js +6 -0
- package/dist/fonts/faces.js.map +1 -1
- package/dist/highlightMarks.d.ts.map +1 -1
- package/dist/highlightMarks.js +6 -0
- package/dist/highlightMarks.js.map +1 -1
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +83 -7
- package/dist/html-backend.js.map +1 -1
- package/dist/htmlComic.d.ts +3 -0
- package/dist/htmlComic.d.ts.map +1 -1
- package/dist/htmlComic.js +2 -0
- package/dist/htmlComic.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/migrate/markdownRuns.d.ts +70 -0
- package/dist/migrate/markdownRuns.d.ts.map +1 -0
- package/dist/migrate/markdownRuns.js +215 -0
- package/dist/migrate/markdownRuns.js.map +1 -0
- package/dist/migrate/pageDesign.d.ts +152 -0
- package/dist/migrate/pageDesign.d.ts.map +1 -0
- package/dist/migrate/pageDesign.js +573 -0
- package/dist/migrate/pageDesign.js.map +1 -0
- package/dist/migrate/textToMarkdown.d.ts +156 -0
- package/dist/migrate/textToMarkdown.d.ts.map +1 -0
- package/dist/migrate/textToMarkdown.js +913 -0
- package/dist/migrate/textToMarkdown.js.map +1 -0
- package/dist/parse/blockParser.js +26 -0
- package/dist/parse/blockParser.js.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +388 -41
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
- package/dist/pipeline/buildMeasurement.js +12 -1
- package/dist/pipeline/buildMeasurement.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts +25 -1
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +100 -2
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/composedLayout.d.ts +16 -2
- package/dist/pipeline/composedLayout.d.ts.map +1 -1
- package/dist/pipeline/composedLayout.js +337 -70
- package/dist/pipeline/composedLayout.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts +15 -0
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +106 -1
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/dropCap.d.ts +5 -0
- package/dist/pipeline/dropCap.d.ts.map +1 -1
- package/dist/pipeline/dropCap.js +23 -2
- package/dist/pipeline/dropCap.js.map +1 -1
- package/dist/pipeline/frameLayout.d.ts +21 -3
- package/dist/pipeline/frameLayout.d.ts.map +1 -1
- package/dist/pipeline/frameLayout.js +52 -4
- package/dist/pipeline/frameLayout.js.map +1 -1
- package/dist/pipeline/headerFooter.d.ts +11 -1
- package/dist/pipeline/headerFooter.d.ts.map +1 -1
- package/dist/pipeline/headerFooter.js +210 -68
- package/dist/pipeline/headerFooter.js.map +1 -1
- package/dist/pipeline/measureContentBlock.d.ts +1 -1
- package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
- package/dist/pipeline/measureContentBlock.js +4 -1
- package/dist/pipeline/measureContentBlock.js.map +1 -1
- package/dist/pipeline/mirrorFrame.d.ts.map +1 -1
- package/dist/pipeline/mirrorFrame.js +25 -2
- package/dist/pipeline/mirrorFrame.js.map +1 -1
- package/dist/pipeline/resourceLayout.d.ts +28 -0
- package/dist/pipeline/resourceLayout.d.ts.map +1 -1
- package/dist/pipeline/resourceLayout.js +21 -0
- package/dist/pipeline/resourceLayout.js.map +1 -1
- package/dist/pipeline/resourceNumbering.d.ts.map +1 -1
- package/dist/pipeline/resourceNumbering.js +13 -4
- package/dist/pipeline/resourceNumbering.js.map +1 -1
- package/dist/pipeline/styles.d.ts +1 -1
- package/dist/pipeline/styles.d.ts.map +1 -1
- package/dist/pipeline/styles.js +4 -0
- package/dist/pipeline/styles.js.map +1 -1
- package/dist/preflight.js +5 -1
- package/dist/preflight.js.map +1 -1
- package/dist/types.d.ts +45 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +228 -9
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/dist/word/toDocx.d.ts.map +1 -1
- package/dist/word/toDocx.js +14 -0
- package/dist/word/toDocx.js.map +1 -1
- package/dist/word/word.test.js +11 -1
- package/dist/word/word.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,14 +7,16 @@ import { FRAME_ATTR } from '../composed/wrap';
|
|
|
7
7
|
import { framePlaceable, nextFrameOf, groupPlaceable, placeBox, unionRect } from '../composed/geometry';
|
|
8
8
|
import { composedOrder, groupOfFrames, layoutGroup } from '../composed/groups';
|
|
9
9
|
import { reflowEvents } from '../composed/reflowLog';
|
|
10
|
-
import { anchorTarget,
|
|
10
|
+
import { anchorTarget, resolveContinueIn } from '../composed/templates';
|
|
11
|
+
import { collapsedOnAddedPage, collapseFrames, continuationTemplate, continuesCollapsed, continuesOffPage, trailingFrames } from '../composed/continuation';
|
|
11
12
|
import { resolvePageTemplate } from '../composed/templates';
|
|
12
13
|
import { frameElementId } from '../composed/ids';
|
|
14
|
+
import { frameLinePx, frameRuleBlocks } from '../composed/rules';
|
|
13
15
|
import { offsetCalloutToAbsolute } from './calloutLayout';
|
|
14
16
|
import { isMarkerBlock } from './buildHelpers';
|
|
15
|
-
import { CUT_START, fragmentRange, makeCalloutLayouter, splitCalloutFragment } from './frameLayout';
|
|
17
|
+
import { CUT_START, fragmentRange, frameCutRules, makeCalloutLayouter, splitCalloutFragment } from './frameLayout';
|
|
16
18
|
const MM_PER_IN = 25.4;
|
|
17
|
-
const NO_FIT = { patches: new Map(), text: new Map(), pictures: new Map(), yieldAll: new Set() };
|
|
19
|
+
const NO_FIT = { patches: new Map(), text: new Map(), pictures: new Map(), yieldAll: new Set(), goOn: new Set() };
|
|
18
20
|
/** Step of the text and picture scales the ladder tries (DESIGN §5.4). */
|
|
19
21
|
const FIT_STEP = 0.025;
|
|
20
22
|
/** Lowest text scale an `allowShrink` proposal goes to. */
|
|
@@ -46,18 +48,30 @@ const fixedHeight = (f) => {
|
|
|
46
48
|
const h = f.height;
|
|
47
49
|
return h !== null && typeof h === 'object' && 'value' in h && 'unit' in h ? f.height : undefined;
|
|
48
50
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const byName = new Map(template.frames.map((f) => [f.name, f]));
|
|
54
|
-
const groupOf = groupOfFrames(template);
|
|
51
|
+
/** The frame before each frame of a chain on the page. On a page added by
|
|
52
|
+
* continuation (#859) a frame that receives content starts its chain. */
|
|
53
|
+
function chainPrev(template, receivers) {
|
|
54
|
+
const names = new Set(template.frames.map((f) => f.name));
|
|
55
55
|
const prev = new Map();
|
|
56
56
|
for (const f of template.frames) {
|
|
57
57
|
const n = nextFrameOf(f);
|
|
58
|
-
if (n &&
|
|
58
|
+
if (n && names.has(n) && !prev.has(n) && !receivers?.has(n))
|
|
59
59
|
prev.set(n, f.name);
|
|
60
60
|
}
|
|
61
|
+
return prev;
|
|
62
|
+
}
|
|
63
|
+
const sameCut = (a, b) => a.child === b.child && a.line === b.line && JSON.stringify(a.group ?? null) === JSON.stringify(b.group ?? null);
|
|
64
|
+
export function layoutComposedPage(given) {
|
|
65
|
+
// A page added by continuation (#859) lays its template with the
|
|
66
|
+
// continuation's rules (and, for the template laid again, its other
|
|
67
|
+
// frames collapsed).
|
|
68
|
+
const input = given.continuing ? { ...given, template: continuationTemplate(given.template, given.continuing) } : given;
|
|
69
|
+
const { template, fence, dpi } = input;
|
|
70
|
+
const fi = fence.fenceIndex;
|
|
71
|
+
const mmPx = dpi / MM_PER_IN;
|
|
72
|
+
const byName = new Map(template.frames.map((f) => [f.name, f]));
|
|
73
|
+
const groupOf = groupOfFrames(template);
|
|
74
|
+
const prev = chainPrev(template, input.continuing?.receivers);
|
|
61
75
|
const headOf = (name) => {
|
|
62
76
|
let at = name;
|
|
63
77
|
const seen = new Set([at]);
|
|
@@ -71,19 +85,97 @@ export function layoutComposedPage(input) {
|
|
|
71
85
|
// twice.
|
|
72
86
|
const memo = new Map();
|
|
73
87
|
let runs = 0;
|
|
74
|
-
const keyOf = (tpl, fit) => JSON.stringify([tpl.id, [...fit.patches], [...fit.text], [...fit.pictures], [...fit.yieldAll]]);
|
|
88
|
+
const keyOf = (tpl, fit) => JSON.stringify([tpl.id, [...fit.patches], [...fit.text], [...fit.pictures], [...fit.yieldAll], [...fit.goOn]]);
|
|
89
|
+
/** Frames a page added by continuation lays with no room (#859). */
|
|
90
|
+
const collapsedHere = input.continuing ? collapsedOnAddedPage(given.template, input.continuing) : new Set();
|
|
91
|
+
const holdsStory = (name) => input.stories.has(headOf(name));
|
|
92
|
+
/** The empty frames that go with the rest of a frame cut for a page
|
|
93
|
+
* laid from this template again (#912), by that frame. */
|
|
94
|
+
const travelling = (tpl, fit) => {
|
|
95
|
+
const out = new Map();
|
|
96
|
+
for (const f of tpl.frames) {
|
|
97
|
+
if (!fit.goOn.has(f.name) || !continuesCollapsed(tpl, f, input.continuing?.collapse ?? false))
|
|
98
|
+
continue;
|
|
99
|
+
const t = trailingFrames(tpl, f.name, holdsStory);
|
|
100
|
+
if (t.length > 0)
|
|
101
|
+
out.set(f.name, t);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
};
|
|
75
105
|
const compose = (fit, tpl = template) => {
|
|
76
106
|
const key = keyOf(tpl, fit);
|
|
77
107
|
const known = memo.get(key);
|
|
78
108
|
if (known)
|
|
79
109
|
return known;
|
|
80
110
|
runs++;
|
|
81
|
-
const
|
|
111
|
+
const travel = travelling(tpl, fit);
|
|
112
|
+
const lay = (leaving, whole = new Set()) => composePage(input, collapseFrames(patchTemplate(tpl, fit.patches), leaving, whole), fit, leaving.size > 0 ? new Set([...collapsedHere, ...leaving]) : collapsedHere);
|
|
113
|
+
const all = new Map(travel);
|
|
114
|
+
let c = lay(new Set([...travel.values()].flat()));
|
|
115
|
+
// The empty frames go with the rest only when there is a rest: a frame
|
|
116
|
+
// that holds all of it once they leave keeps them after it…
|
|
117
|
+
const staying = [...travel.keys()].filter((f) => !c.spills.has(f));
|
|
118
|
+
if (staying.length > 0) {
|
|
119
|
+
for (const f of staying)
|
|
120
|
+
travel.delete(f);
|
|
121
|
+
c = lay(new Set([...travel.values()].flat()));
|
|
122
|
+
// …unless they then do not fit under it at their size: the whole box
|
|
123
|
+
// goes on with them, and takes no room here.
|
|
124
|
+
const whole = staying.filter((f) => squeezes(c, f));
|
|
125
|
+
if (whole.length > 0) {
|
|
126
|
+
for (const f of whole)
|
|
127
|
+
travel.set(f, all.get(f));
|
|
128
|
+
c = lay(new Set([...whole, ...[...travel.values()].flat()]), new Set(whole));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// A box none of whose content fits goes on whole and takes no room
|
|
132
|
+
// here either.
|
|
133
|
+
const empty = [...fit.goOn].filter((f) => { const sp = c.spills.get(f); return sp !== undefined && !sp.advanced && sp.pieces === 0 && !frameIn(c, f)?.collapsed; });
|
|
134
|
+
if (empty.length > 0) {
|
|
135
|
+
for (const f of empty)
|
|
136
|
+
if (all.has(f))
|
|
137
|
+
travel.set(f, all.get(f));
|
|
138
|
+
c = lay(new Set([...empty, ...[...travel.values()].flat()]), new Set(empty));
|
|
139
|
+
}
|
|
82
140
|
memo.set(key, c);
|
|
83
141
|
return c;
|
|
84
142
|
};
|
|
85
143
|
const frameIn = (c, name) => c.frames.find((f) => f.name === name);
|
|
86
144
|
const overflows = (c, name) => (frameIn(c, name)?.overflowPx ?? 0) > 0.5;
|
|
145
|
+
/** A frame that can go on to another page squeezes the frames after it
|
|
146
|
+
* in its stack below their declared size (#912): the stack reached the
|
|
147
|
+
* foot of its area and the last frames were cut to fit, an answer space
|
|
148
|
+
* under the box lost its lines. The ladder treats it as content that
|
|
149
|
+
* does not fit, so the box goes on and they keep their size. */
|
|
150
|
+
const squeezes = (c, name) => {
|
|
151
|
+
const frame = byName.get(name);
|
|
152
|
+
if (!frame || !continuesOffPage(frame.overflow.continueIn))
|
|
153
|
+
return false;
|
|
154
|
+
const g = groupOf.get(name);
|
|
155
|
+
if (!g || g.kind !== 'stack')
|
|
156
|
+
return false;
|
|
157
|
+
for (const other of g.frames.slice(g.frames.indexOf(name) + 1)) {
|
|
158
|
+
const f = byName.get(other);
|
|
159
|
+
const v = frameIn(c, other);
|
|
160
|
+
if (!f || !v || v.collapsed)
|
|
161
|
+
continue;
|
|
162
|
+
const min = f.minHeight ? dimensionToPx(f.minHeight, dpi, input.emPx) : fixedHeight(f) ? dimensionToPx(fixedHeight(f), dpi, input.emPx) : 0;
|
|
163
|
+
if (v.rect.height < min - 0.5)
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
};
|
|
168
|
+
/** The frame's content does not fit, or it squeezes the frames after it. */
|
|
169
|
+
const short = (c, name) => overflows(c, name) || squeezes(c, name);
|
|
170
|
+
/** The ladder's last step (#859): the frame is cut at its foot and the
|
|
171
|
+
* rest goes on on another page, its text and pictures back at full
|
|
172
|
+
* size (they go on anyway). */
|
|
173
|
+
const goingOn = (f, frame) => {
|
|
174
|
+
const text = new Map(f.text);
|
|
175
|
+
text.delete(headOf(frame.name));
|
|
176
|
+
const pictures = new Map([...f.pictures].filter(([, p]) => p.by !== frame.name));
|
|
177
|
+
return { ...f, text, pictures, goOn: new Set([...f.goOn, frame.name]) };
|
|
178
|
+
};
|
|
87
179
|
// The ladder, frame by frame in template order (ties break there).
|
|
88
180
|
let fit = NO_FIT;
|
|
89
181
|
let cur = compose(fit);
|
|
@@ -101,7 +193,7 @@ export function layoutComposedPage(input) {
|
|
|
101
193
|
const attempt = (next, name) => {
|
|
102
194
|
fit = next;
|
|
103
195
|
cur = compose(fit);
|
|
104
|
-
return !
|
|
196
|
+
return !short(cur, name);
|
|
105
197
|
};
|
|
106
198
|
/** The largest scale among `steps` (descending) at which `name` fits,
|
|
107
199
|
* checked at the lowest first, then by bisection; adopted. */
|
|
@@ -114,7 +206,7 @@ export function layoutComposedPage(input) {
|
|
|
114
206
|
let hi = steps.length - 1; // fits at hi
|
|
115
207
|
while (lo < hi && runs < COMPOSE_BUDGET) {
|
|
116
208
|
const mid = Math.floor((lo + hi) / 2);
|
|
117
|
-
if (!
|
|
209
|
+
if (!short(compose(make(steps[mid])), name))
|
|
118
210
|
hi = mid;
|
|
119
211
|
else
|
|
120
212
|
lo = mid + 1;
|
|
@@ -174,7 +266,7 @@ export function layoutComposedPage(input) {
|
|
|
174
266
|
};
|
|
175
267
|
for (const frame of template.frames) {
|
|
176
268
|
const name = frame.name;
|
|
177
|
-
if (!
|
|
269
|
+
if (!short(cur, name) || runs >= COMPOSE_BUDGET)
|
|
178
270
|
continue;
|
|
179
271
|
const steps = [];
|
|
180
272
|
tried.set(name, steps);
|
|
@@ -189,32 +281,57 @@ export function layoutComposedPage(input) {
|
|
|
189
281
|
if (movesOthers(frame))
|
|
190
282
|
steps.push('push');
|
|
191
283
|
// 3. Pictures of the frame and its group, down to imageMinScale.
|
|
192
|
-
const
|
|
193
|
-
|
|
284
|
+
const scaleImages = () => {
|
|
285
|
+
const heads = frame.fit.scaleImages ? pictureHeads(frame) : [];
|
|
286
|
+
if (heads.length === 0 || frame.fit.imageMinScale >= 1)
|
|
287
|
+
return false;
|
|
194
288
|
steps.push('scaleImages');
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
289
|
+
return searchScale(scaleSteps(frame.fit.imageMinScale), (s) => withPictures(fit, heads, s, name), name);
|
|
290
|
+
};
|
|
198
291
|
// 4. Redistribute: every other frame of the group gives space (or
|
|
199
292
|
// width) back, down to its minimum or its content.
|
|
200
|
-
const
|
|
201
|
-
|
|
293
|
+
const redistribute = () => {
|
|
294
|
+
const g = groupOf.get(name);
|
|
295
|
+
if (!g || g.frames.filter((m) => byName.has(m)).length <= 1)
|
|
296
|
+
return false;
|
|
202
297
|
steps.push('redistribute');
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
298
|
+
return attempt({ ...fit, yieldAll: new Set([...fit.yieldAll, g.name]) }, name);
|
|
299
|
+
};
|
|
206
300
|
// 5. The frame's text (its whole story), down to fit.minScale.
|
|
207
|
-
|
|
301
|
+
const shrinkText = () => {
|
|
302
|
+
if (!frame.fit.shrinkText || frame.fit.minScale >= 1 || !holdsText(frame))
|
|
303
|
+
return false;
|
|
208
304
|
steps.push('shrinkText');
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
305
|
+
return searchScale(scaleSteps(frame.fit.minScale), (s) => withText(fit, headOf(name), s), name);
|
|
306
|
+
};
|
|
307
|
+
// 6. Continuation on another page (`overflow.continueIn: 'next-page'`,
|
|
308
|
+
// `'template:<id>'` or `'page:<id>'`, #859). On a page added by
|
|
309
|
+
// continuation, a frame that would take none of what it receives
|
|
310
|
+
// sends nothing on (it would go on for ever): it warns.
|
|
311
|
+
const goOn = () => {
|
|
312
|
+
if (!continuesOffPage(frame.overflow.continueIn))
|
|
313
|
+
return false;
|
|
217
314
|
steps.push('continue');
|
|
315
|
+
const before = { fit, cur };
|
|
316
|
+
// (Only for a story that came from another page: a page's own story
|
|
317
|
+
// may go on whole.)
|
|
318
|
+
const received = input.continuing?.receivers.has(headOf(name)) ?? false;
|
|
319
|
+
if (attempt(goingOn(fit, frame), name) && (!received || cur.spills.get(name)?.advanced !== false))
|
|
320
|
+
return true;
|
|
321
|
+
({ fit, cur } = before);
|
|
322
|
+
return false;
|
|
323
|
+
};
|
|
324
|
+
// A frame whose content can go on to another page goes on before its
|
|
325
|
+
// pictures and text shrink (`fit.preferContinue`, #920): a story keeps
|
|
326
|
+
// one size from page to page. The pictures and the text shrink only
|
|
327
|
+
// when it cannot go on (the last page continuation may add, or a
|
|
328
|
+
// receiver that takes none of it).
|
|
329
|
+
const order = continuesOffPage(frame.overflow.continueIn) && frame.fit.preferContinue
|
|
330
|
+
? [redistribute, goOn, scaleImages, shrinkText]
|
|
331
|
+
: [scaleImages, redistribute, shrinkText, goOn];
|
|
332
|
+
for (const step of order)
|
|
333
|
+
if (step())
|
|
334
|
+
break;
|
|
218
335
|
}
|
|
219
336
|
// 7. Warn, with what would make each frame that still overflows fit.
|
|
220
337
|
const warnings = [...(cur.notes ?? [])];
|
|
@@ -278,12 +395,34 @@ export function layoutComposedPage(input) {
|
|
|
278
395
|
sourceStart: contentBlocks[input.startIdx].sourceStart + input.bodyOffset,
|
|
279
396
|
sourceEnd: contentBlocks[input.endIdx].sourceEnd + input.bodyOffset,
|
|
280
397
|
};
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
398
|
+
// What goes on on other pages (#859), in template order.
|
|
399
|
+
const continuations = [];
|
|
400
|
+
const trailing = new Map([...travelling(template, fit)].filter(([f, names]) => cur.spills.has(f) && names.every((n) => frameIn(cur, n)?.collapsed)));
|
|
401
|
+
for (const frame of template.frames) {
|
|
402
|
+
const spill = cur.spills.get(frame.name);
|
|
403
|
+
const story = spill ? input.stories.get(spill.head) : undefined;
|
|
404
|
+
if (!spill || !story)
|
|
405
|
+
continue;
|
|
406
|
+
const came = input.continuing?.receivers.get(spill.head);
|
|
407
|
+
continuations.push({
|
|
408
|
+
frame: frame.name,
|
|
409
|
+
fromFrameId: spill.frameId,
|
|
410
|
+
head: spill.head,
|
|
411
|
+
// `{attr.x}` read from the fence (#917).
|
|
412
|
+
rule: resolveContinueIn(frame.overflow.continueIn, fence.attrs),
|
|
413
|
+
template: template.id,
|
|
414
|
+
collapsed: input.continuing?.collapse ?? false,
|
|
415
|
+
story,
|
|
416
|
+
cut: spill.cut,
|
|
417
|
+
pieces: spill.pieces,
|
|
418
|
+
headBlockId: spill.headBlockId,
|
|
419
|
+
// A story that jumped here (#917) goes on named by this page's frame.
|
|
420
|
+
slot: (input.continuing?.jump ? undefined : came?.slot) ?? frame.name,
|
|
421
|
+
...(trailing.has(frame.name) ? { trailing: trailing.get(frame.name) } : {}),
|
|
422
|
+
...(frame.overflow.fromMark !== undefined ? { fromMark: frame.overflow.fromMark } : {}),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
return { composed, blocks, warnings, laid, ...(fitted.size > 0 ? { fitted } : {}), ...(continuations.length > 0 ? { continuations } : {}) };
|
|
287
426
|
}
|
|
288
427
|
const mmDim = (value) => ({ value, unit: 'mm' });
|
|
289
428
|
/** What would make `frame`'s content fit, each value worked out from the
|
|
@@ -315,9 +454,12 @@ function overflowProposals(c) {
|
|
|
315
454
|
...(frame.fit.shrinkText ? {} : { also: [{ path: path('fit/shrinkText'), value: true }] }),
|
|
316
455
|
});
|
|
317
456
|
}
|
|
457
|
+
// A frame that holds content (an empty one is short of its own box, #911:
|
|
458
|
+
// more height, text or pages do not help it).
|
|
459
|
+
const holds = cur.needs.has(name);
|
|
318
460
|
// A higher maximum: the frame's height plus the shortfall, in whole mm.
|
|
319
461
|
const fixed = fixedHeight(frame);
|
|
320
|
-
if (frame.maxHeight || fixed) {
|
|
462
|
+
if (holds && (frame.maxHeight || fixed)) {
|
|
321
463
|
const want = Math.ceil((vdt.rect.height + vdt.overflowPx) / mmPx - 1e-6);
|
|
322
464
|
const patch = { maxHeight: mmDim(want), ...(fixed ? { height: 'auto', minHeight: fixed } : {}) };
|
|
323
465
|
if (fits(c.patched(fit, name, patch)))
|
|
@@ -330,7 +472,9 @@ function overflowProposals(c) {
|
|
|
330
472
|
if (g) {
|
|
331
473
|
for (const other of g.frames) {
|
|
332
474
|
const f = c.byName.get(other);
|
|
333
|
-
|
|
475
|
+
// An empty frame's minimum is the room it stands for (an answer
|
|
476
|
+
// space, a picture slot), not room to take (#911).
|
|
477
|
+
if (!f || other === name || f.pin || !f.minHeight || !cur.needs.has(other))
|
|
334
478
|
continue;
|
|
335
479
|
const box = cur.frames.find((x) => x.name === other);
|
|
336
480
|
if (!box)
|
|
@@ -347,8 +491,16 @@ function overflowProposals(c) {
|
|
|
347
491
|
}
|
|
348
492
|
}
|
|
349
493
|
}
|
|
494
|
+
// An empty frame cut short by its group: a lower minimum of its own, the
|
|
495
|
+
// height it was left (#911).
|
|
496
|
+
if (!holds && g && frame.minHeight) {
|
|
497
|
+
const lowered = Math.floor(vdt.rect.height / mmPx + 1e-6);
|
|
498
|
+
if (lowered * mmPx < dimensionToPx(frame.minHeight, c.dpi, c.emPx) - 0.5 && fits(c.patched(fit, name, { minHeight: mmDim(lowered) }))) {
|
|
499
|
+
out.push({ kind: 'lowerMin', path: path('minHeight'), value: mmDim(lowered), frame: name });
|
|
500
|
+
}
|
|
501
|
+
}
|
|
350
502
|
// Continue on the next page (#859 lays it out).
|
|
351
|
-
if (!frame.overflow.continueIn)
|
|
503
|
+
if (holds && !frame.overflow.continueIn)
|
|
352
504
|
out.push({ kind: 'continueNextPage', path: path('overflow/continueIn'), value: 'next-page' });
|
|
353
505
|
// Another template of the book with a frame of this name, where the
|
|
354
506
|
// content fits as it is.
|
|
@@ -403,17 +555,17 @@ export function keepChromeAt(style, scale) {
|
|
|
403
555
|
};
|
|
404
556
|
}
|
|
405
557
|
/** Lay out every frame of the page once, with the reflow ladder's `fit`. */
|
|
406
|
-
function composePage(input, template, fit) {
|
|
558
|
+
function composePage(input, template, fit, collapsed) {
|
|
407
559
|
const { pass, fence, dpi } = input;
|
|
408
560
|
const fi = fence.fenceIndex;
|
|
409
|
-
const
|
|
561
|
+
const cont = input.continuing;
|
|
562
|
+
const cutRules = frameCutRules(pass.resolved);
|
|
563
|
+
/** A frame's id: `cp<fence>:<name>`; on a page added by continuation
|
|
564
|
+
* (#859) `~<k>`, a frame that receives content named by the frame of
|
|
565
|
+
* the fence's page that first sent it on. */
|
|
566
|
+
const frameIdOf = (name) => (cont && !cont.jump ? frameElementId(fi, cont.receivers.get(name)?.slot ?? name, cont.generation) : frameElementId(fi, name));
|
|
410
567
|
const byName = new Map(template.frames.map((f) => [f.name, f]));
|
|
411
|
-
const prev =
|
|
412
|
-
for (const f of template.frames) {
|
|
413
|
-
const n = nextFrameOf(f);
|
|
414
|
-
if (n && byName.has(n) && !prev.has(n))
|
|
415
|
-
prev.set(n, f.name);
|
|
416
|
-
}
|
|
568
|
+
const prev = chainPrev(template, cont?.receivers);
|
|
417
569
|
/** The head of the chain a frame belongs to. */
|
|
418
570
|
const headOf = (name) => {
|
|
419
571
|
let at = name;
|
|
@@ -426,8 +578,29 @@ function composePage(input, template, fit) {
|
|
|
426
578
|
};
|
|
427
579
|
/** The text scale of a frame's story (#858). */
|
|
428
580
|
const textScale = (frame) => fit.text.get(headOf(frame.name)) ?? 1;
|
|
581
|
+
/** A page number a mark's `{page}` is laid out with (#917): a guess of
|
|
582
|
+
* the right width, the build writing the true one once pages are
|
|
583
|
+
* numbered (`VDTDesignTextBlock.pageMark`). */
|
|
584
|
+
const toGuess = String(input.page.index + 2);
|
|
585
|
+
const fromGuess = String(Math.max(1, input.page.index));
|
|
429
586
|
/** The style a frame is laid out in: its box as drawn at any text scale. */
|
|
430
|
-
const styleOf = (frame) =>
|
|
587
|
+
const styleOf = (frame) => {
|
|
588
|
+
const style = keepChromeAt(input.styleOf(frame), textScale(frame));
|
|
589
|
+
if (!fit.goOn.has(frame.name))
|
|
590
|
+
return style;
|
|
591
|
+
// A frame cut for another page (#859) ends with its mark: its own
|
|
592
|
+
// words, none (`false`), or its style's localized ones.
|
|
593
|
+
const mark = frame.overflow.mark;
|
|
594
|
+
return { ...style, continuesMarkerEnabled: mark !== false, continuesMarker: typeof mark === 'string' ? withPage(mark, toGuess) : style.continuesMarker };
|
|
595
|
+
};
|
|
596
|
+
/** The mark of a frame that receives a story from another page (#917):
|
|
597
|
+
* its own `overflow.fromMark`, else the sending frame's, set at the
|
|
598
|
+
* head of the first piece it lays here. */
|
|
599
|
+
const fromMarkOf = (frame) => {
|
|
600
|
+
const came = cont?.receivers.get(headOf(frame.name));
|
|
601
|
+
const words = frame.overflow.fromMark ?? came?.fromMark;
|
|
602
|
+
return came && words !== undefined ? withPage(words, fromGuess) : undefined;
|
|
603
|
+
};
|
|
431
604
|
/** The scale of a frame's pictures (#858), below 1, or undefined. */
|
|
432
605
|
const pictureScale = (frame) => fit.pictures.get(headOf(frame.name))?.scale;
|
|
433
606
|
/** Rectangles in a frame's box coordinates, in its laid-out (scaled)
|
|
@@ -454,13 +627,16 @@ function composePage(input, template, fit) {
|
|
|
454
627
|
const headFrame = byName.get(head);
|
|
455
628
|
if (!story || !headFrame)
|
|
456
629
|
return undefined;
|
|
630
|
+
// Content from the page before (#859) goes on where it was cut, its
|
|
631
|
+
// blocks numbered on from its pieces there.
|
|
632
|
+
const came = cont?.receivers.get(head);
|
|
457
633
|
const st = {
|
|
458
634
|
story,
|
|
459
635
|
L: makeCalloutLayouter(pass, story.startIdx, story.plan, styleOf(frame)),
|
|
460
|
-
cut: CUT_START,
|
|
461
|
-
pieces: 0,
|
|
462
|
-
headBlockId: frameIdOf(head),
|
|
463
|
-
headFrameId: frameIdOf(head),
|
|
636
|
+
cut: came?.cut ?? CUT_START,
|
|
637
|
+
pieces: came?.pieces ?? 0,
|
|
638
|
+
headBlockId: came?.headBlockId ?? frameIdOf(head),
|
|
639
|
+
headFrameId: came?.fromFrameId ?? frameIdOf(head),
|
|
464
640
|
};
|
|
465
641
|
states.set(head, st);
|
|
466
642
|
return st;
|
|
@@ -488,6 +664,8 @@ function composePage(input, template, fit) {
|
|
|
488
664
|
const declaredRefOf = (name) => declared.get(name) ?? groupRect(name, declared, groupDeclared, declaredRefOf);
|
|
489
665
|
/** Pinned frames laid so far: a group's frames flow round them. */
|
|
490
666
|
const pinned = [];
|
|
667
|
+
/** How far a stack cut each frame below its least height (#911). */
|
|
668
|
+
const boxShort = new Map();
|
|
491
669
|
const frames = [];
|
|
492
670
|
const vdtByName = new Map();
|
|
493
671
|
const blocks = [];
|
|
@@ -497,6 +675,7 @@ function composePage(input, template, fit) {
|
|
|
497
675
|
/** What `fitBox` did to each frame (#861), last placement wins. */
|
|
498
676
|
const fitted = new Map();
|
|
499
677
|
const laid = new Set();
|
|
678
|
+
const spills = new Map();
|
|
500
679
|
const pushed = seed
|
|
501
680
|
? [...seed].map(([name, box]) => ({ name, box, ...(seedShapes?.get(name) ? { contour: seedShapes.get(name) } : {}) }))
|
|
502
681
|
: [];
|
|
@@ -536,7 +715,10 @@ function composePage(input, template, fit) {
|
|
|
536
715
|
const holds = reached && !atEnd(st);
|
|
537
716
|
const continuation = st !== undefined && st.pieces > 0;
|
|
538
717
|
const blockId = st && st.pieces > 0 ? `${st.headBlockId}-cont-${st.pieces}` : frameIdOf(frame.name);
|
|
539
|
-
|
|
718
|
+
// The first piece here of a story that came from another page.
|
|
719
|
+
const came = cont?.receivers.get(headOf(frame.name));
|
|
720
|
+
const fromMark = st && came && continuation && st.pieces === came.pieces ? fromMarkOf(frame) : undefined;
|
|
721
|
+
return { frame, st, before, holds, continuation, blockId, ...(fromMark !== undefined ? { fromMark } : {}) };
|
|
540
722
|
};
|
|
541
723
|
/** The content's height at `width`, running round the pushing frames
|
|
542
724
|
* below the top of `at` (the frame's box as first placed). A frame
|
|
@@ -554,8 +736,8 @@ function composePage(input, template, fit) {
|
|
|
554
736
|
if (known !== undefined)
|
|
555
737
|
return known;
|
|
556
738
|
const exclusions = at ? unscale(exclusionsFor(p.frame, { ...at, width }, true), s) : undefined;
|
|
557
|
-
const opts = { ...(exclusions ? { exclusions } : {}), ...(pic !== undefined ? { pictureScale: pic } : {}) };
|
|
558
|
-
const r = p.st.L.layoutRange(p.st.cut, p.st.L.end, width / s, p.blockId, p.continuation, input.mirrored, opts.exclusions || opts.pictureScale !== undefined ? opts : undefined);
|
|
739
|
+
const opts = { ...(exclusions ? { exclusions } : {}), ...(pic !== undefined ? { pictureScale: pic } : {}), ...(p.fromMark !== undefined ? { continuedMark: p.fromMark } : {}) };
|
|
740
|
+
const r = p.st.L.layoutRange(p.st.cut, p.st.L.end, width / s, p.blockId, p.continuation, input.mirrored, opts.exclusions || opts.pictureScale !== undefined || opts.continuedMark !== undefined ? opts : undefined);
|
|
559
741
|
const h = r.totalHeight * s;
|
|
560
742
|
memo.set(key, h);
|
|
561
743
|
return h;
|
|
@@ -652,6 +834,7 @@ function composePage(input, template, fit) {
|
|
|
652
834
|
pin: f.pin,
|
|
653
835
|
fixedHeight: !auto && !fill,
|
|
654
836
|
need: natural,
|
|
837
|
+
floor: minH,
|
|
655
838
|
};
|
|
656
839
|
if (yieldAll && mode === 'final' && !f.pin) {
|
|
657
840
|
// Down to its minimum, never under what its content needs, never
|
|
@@ -663,6 +846,14 @@ function composePage(input, template, fit) {
|
|
|
663
846
|
if (group.kind === 'stack')
|
|
664
847
|
item.min = Math.min(height, Math.max(minH, natural(box.width)));
|
|
665
848
|
}
|
|
849
|
+
if (fit.goOn.has(f.name) && mode === 'final' && !f.pin) {
|
|
850
|
+
// A frame cut for another page (#859) gives the room first,
|
|
851
|
+
// down to its minimum: the frames after it in the group keep
|
|
852
|
+
// their size (#912), and its rest goes on.
|
|
853
|
+
item.shrink = Math.max(f.shrink, 1);
|
|
854
|
+
item.priority = Number.NEGATIVE_INFINITY;
|
|
855
|
+
item.min = minH;
|
|
856
|
+
}
|
|
666
857
|
return item;
|
|
667
858
|
});
|
|
668
859
|
const firstFree = items.find((it) => !it.pin) ?? items[0];
|
|
@@ -684,7 +875,7 @@ function composePage(input, template, fit) {
|
|
|
684
875
|
for (const it of items)
|
|
685
876
|
if (it.pin)
|
|
686
877
|
out.set(it.name, it.own);
|
|
687
|
-
return { rects: out, box: ownBox ?? unionRect([...out.values()]) ?? box };
|
|
878
|
+
return { rects: out, box: ownBox ?? unionRect([...out.values()]) ?? box, items };
|
|
688
879
|
};
|
|
689
880
|
let naturals = preps.map((p) => naturalOf(p));
|
|
690
881
|
let fin = solve('final', naturals);
|
|
@@ -696,6 +887,20 @@ function composePage(input, template, fit) {
|
|
|
696
887
|
fin = solve('final', naturals);
|
|
697
888
|
}
|
|
698
889
|
const decl = solve('declared', naturals);
|
|
890
|
+
// A frame the stack cut below the least it may have (its
|
|
891
|
+
// `minHeight`, a fixed height its own) is short of its box, content
|
|
892
|
+
// or none (#911): an empty answer space or picture slot overflows
|
|
893
|
+
// too.
|
|
894
|
+
for (const it of fin.items) {
|
|
895
|
+
if (it.pin || group.kind !== 'stack')
|
|
896
|
+
continue;
|
|
897
|
+
const least = it.fixedHeight && (it.floor ?? 0) <= 0.5 ? it.own.height : it.floor ?? 0;
|
|
898
|
+
const got = fin.rects.get(it.name).height;
|
|
899
|
+
if (least - got > 0.5)
|
|
900
|
+
boxShort.set(it.name, least - got);
|
|
901
|
+
else
|
|
902
|
+
boxShort.delete(it.name);
|
|
903
|
+
}
|
|
699
904
|
for (const m of members) {
|
|
700
905
|
rects.set(m.name, fin.rects.get(m.name));
|
|
701
906
|
declared.set(m.name, decl.rects.get(m.name));
|
|
@@ -718,6 +923,7 @@ function composePage(input, template, fit) {
|
|
|
718
923
|
const laidH = rect.height / s;
|
|
719
924
|
let result;
|
|
720
925
|
let overflowPx = 0;
|
|
926
|
+
let goesOn = false;
|
|
721
927
|
const from = st?.cut ?? CUT_START;
|
|
722
928
|
if (holds) {
|
|
723
929
|
// What its content needs at its width, measured only when a
|
|
@@ -727,22 +933,29 @@ function composePage(input, template, fit) {
|
|
|
727
933
|
let known;
|
|
728
934
|
needs.set(frame.name, () => (known ??= naturalOf(startAt)(width)));
|
|
729
935
|
const exclusions = unscale(exclusionsFor(frame, rect, false), s);
|
|
730
|
-
const extra = { ...(exclusions ? { exclusions } : {}), ...(pic !== undefined ? { pictureScale: pic } : {}) };
|
|
936
|
+
const extra = { ...(exclusions ? { exclusions } : {}), ...(pic !== undefined ? { pictureScale: pic } : {}), ...(p.fromMark !== undefined ? { continuedMark: p.fromMark } : {}) };
|
|
731
937
|
const opts = { boxHeight: laidH, verticalAlign: frame.verticalAlign, ...extra };
|
|
732
938
|
const whole = st.L.layoutRange(from, st.L.end, laidW, blockId, continuation, input.mirrored, opts);
|
|
733
939
|
const nextName = nextFrameOf(frame);
|
|
734
940
|
const nextFrame = nextName !== undefined ? byName.get(nextName) : undefined;
|
|
735
|
-
|
|
736
|
-
|
|
941
|
+
const chained = nextFrame !== undefined && !vdtByName.has(nextFrame.name);
|
|
942
|
+
// Cut for another page (#859) when the chain ends here.
|
|
943
|
+
goesOn = !chained && fit.goOn.has(frame.name);
|
|
944
|
+
if ((whole.overflowPx ?? 0) > 0.5 && (chained || goesOn)) {
|
|
945
|
+
// The rest goes on in the next frame of the chain, or on another
|
|
946
|
+
// page.
|
|
737
947
|
const minLines = Math.max(1, style.splitMinLines);
|
|
738
|
-
|
|
948
|
+
// A heading stays with what follows it, a list keeps two items
|
|
949
|
+
// on each side of the cut (#913).
|
|
950
|
+
const frag = splitCalloutFragment(pass, st.L, from, laidW, laidH, blockId, continuation, minLines, input.mirrored, extra.exclusions || extra.pictureScale !== undefined || extra.continuedMark !== undefined ? extra : undefined, cutRules);
|
|
739
951
|
const to = frag?.to ?? from;
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
952
|
+
// A frame none of whose content fits draws nothing: the whole box
|
|
953
|
+
// goes on (#912), not a tab and a mark over an empty piece.
|
|
954
|
+
result = frag ? st.L.layoutRange(from, to, laidW, blockId, continuation, input.mirrored, opts) : undefined;
|
|
743
955
|
st.cut = to;
|
|
744
956
|
}
|
|
745
957
|
else {
|
|
958
|
+
goesOn = false;
|
|
746
959
|
result = whole;
|
|
747
960
|
overflowPx = (whole.overflowPx ?? 0) * s;
|
|
748
961
|
st.cut = st.L.end;
|
|
@@ -752,6 +965,26 @@ function composePage(input, template, fit) {
|
|
|
752
965
|
// A styled frame with nothing (left) to hold still draws its box.
|
|
753
966
|
result = st.L.layoutRange(st.L.end, st.L.end, laidW, blockId, true, input.mirrored, { boxHeight: laidH });
|
|
754
967
|
}
|
|
968
|
+
// A frame cut below its least height overflows by the cut, holding
|
|
969
|
+
// content or not (#911).
|
|
970
|
+
overflowPx = Math.max(overflowPx, boxShort.get(frame.name) ?? 0);
|
|
971
|
+
// Ruled lines (#914): on the box, over its background, in its
|
|
972
|
+
// laid-out coordinates; an empty frame keeps them for the decoration.
|
|
973
|
+
let emptyRules;
|
|
974
|
+
if (frame.rules) {
|
|
975
|
+
const linePx = frameLinePx(style, dpi);
|
|
976
|
+
if (result?.frame.designOverlay && result.frame.callout) {
|
|
977
|
+
result.frame.designOverlay.blocks.push(...frameRuleBlocks(frame.rules, result.frame.callout.innerRect, linePx, s, dpi, input.emPx));
|
|
978
|
+
}
|
|
979
|
+
else if (!result) {
|
|
980
|
+
const pad = (d) => dimensionToPx(d, dpi, input.emPx);
|
|
981
|
+
const p = style.padding;
|
|
982
|
+
const inner = { x: rect.x + pad(p.left), y: rect.y + pad(p.top), width: rect.width - pad(p.left) - pad(p.right), height: rect.height - pad(p.top) - pad(p.bottom) };
|
|
983
|
+
const blocks = frameRuleBlocks(frame.rules, inner, linePx, 1, dpi, input.emPx);
|
|
984
|
+
if (blocks.length > 0)
|
|
985
|
+
emptyRules = blocks;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
755
988
|
const vdt = {
|
|
756
989
|
id: frameIdOf(frame.name),
|
|
757
990
|
name: frame.name,
|
|
@@ -765,6 +998,8 @@ function composePage(input, template, fit) {
|
|
|
765
998
|
blockIds: [],
|
|
766
999
|
configPath: frame.configPath,
|
|
767
1000
|
...(frame.exclude === 'push' ? { push: true } : {}),
|
|
1001
|
+
...(collapsed.has(frame.name) ? { collapsed: true } : {}),
|
|
1002
|
+
...(emptyRules ? { rules: emptyRules } : {}),
|
|
768
1003
|
};
|
|
769
1004
|
const decl = declared.get(frame.name);
|
|
770
1005
|
if (decl && !sameRect(decl, rect))
|
|
@@ -775,6 +1010,18 @@ function composePage(input, template, fit) {
|
|
|
775
1010
|
if (q)
|
|
776
1011
|
q.continuesTo = vdt.id;
|
|
777
1012
|
}
|
|
1013
|
+
else if (holds && cont?.receivers.has(frame.name)) {
|
|
1014
|
+
// Content from the page before (#859).
|
|
1015
|
+
vdt.continuedFrom = cont.receivers.get(frame.name).fromFrameId;
|
|
1016
|
+
}
|
|
1017
|
+
if (result) {
|
|
1018
|
+
// The marks whose `{page}` the build fills in (#917).
|
|
1019
|
+
if (goesOn && typeof frame.overflow.mark === 'string' && frame.overflow.mark.includes('{page}'))
|
|
1020
|
+
tagPageMark(result.frame, withPage(frame.overflow.mark, toGuess), frame.overflow.mark, 'to');
|
|
1021
|
+
const fromWords = frame.overflow.fromMark ?? cont?.receivers.get(headOf(frame.name))?.fromMark;
|
|
1022
|
+
if (p.fromMark !== undefined && fromWords?.includes('{page}'))
|
|
1023
|
+
tagPageMark(result.frame, p.fromMark, fromWords, 'from');
|
|
1024
|
+
}
|
|
778
1025
|
if (result && st) {
|
|
779
1026
|
const range = fragmentRange(st.L, from, st.cut);
|
|
780
1027
|
commitFrame(result, rect, st.story, range, input, vdt, blocks);
|
|
@@ -789,6 +1036,11 @@ function composePage(input, template, fit) {
|
|
|
789
1036
|
if (s !== 1 && holds)
|
|
790
1037
|
fitLog.push({ frame: frame.name, kind: 'textScaled', scale: s });
|
|
791
1038
|
}
|
|
1039
|
+
if (goesOn && st) {
|
|
1040
|
+
// Laid here even when none of it fits: the rest goes on (#912).
|
|
1041
|
+
laid.add(headOf(frame.name));
|
|
1042
|
+
spills.set(frame.name, { head: headOf(frame.name), cut: st.cut, advanced: !sameCut(from, st.cut), pieces: st.pieces, headBlockId: st.headBlockId, frameId: vdt.id });
|
|
1043
|
+
}
|
|
792
1044
|
if (overflowPx > 0.5) {
|
|
793
1045
|
const overflowMm = Math.round((overflowPx / dpi) * MM_PER_IN * 10) / 10;
|
|
794
1046
|
overflowLog.push({ frame: frame.name, kind: 'overflow', toMm: overflowMm });
|
|
@@ -828,11 +1080,11 @@ function composePage(input, template, fit) {
|
|
|
828
1080
|
fill(prepare(f), solved.get(f.name));
|
|
829
1081
|
}
|
|
830
1082
|
const log = mergeLog(reflowEvents(motionsOf(template, rects, declared), dpi / MM_PER_IN), [...fitLog, ...overflowLog], template);
|
|
831
|
-
return { frames, blocks, log, laid, needs, pushRects, pushShapes, fitted, notes };
|
|
1083
|
+
return { frames, blocks, log, laid, needs, pushRects, pushShapes, fitted, notes, spills };
|
|
832
1084
|
};
|
|
833
1085
|
const first = run();
|
|
834
|
-
const { frames, blocks, log, laid, needs, fitted, notes } = first.pushRects.size > 0 ? run(first.pushRects, first.pushShapes) : first;
|
|
835
|
-
return { frames, blocks, log, laid, needs, template, fitted, ...(notes.length > 0 ? { notes } : {}) };
|
|
1086
|
+
const { frames, blocks, log, laid, needs, fitted, notes, spills } = first.pushRects.size > 0 ? run(first.pushRects, first.pushShapes) : first;
|
|
1087
|
+
return { frames, blocks, log, laid, needs, template, fitted, spills, ...(notes.length > 0 ? { notes } : {}) };
|
|
836
1088
|
}
|
|
837
1089
|
const sameRect = (a, b) => Math.abs(a.x - b.x) < 0.01 && Math.abs(a.y - b.y) < 0.01 && Math.abs(a.width - b.width) < 0.01 && Math.abs(a.height - b.height) < 0.01;
|
|
838
1090
|
/** Each laid frame's declared and final boxes, in template order, with
|
|
@@ -1002,4 +1254,19 @@ export function composedPageAt(pages, idx) {
|
|
|
1002
1254
|
return p;
|
|
1003
1255
|
return undefined;
|
|
1004
1256
|
}
|
|
1257
|
+
/** `words` with `{page}` as `page` (#917). */
|
|
1258
|
+
const withPage = (words, page) => words.split('{page}').join(page);
|
|
1259
|
+
/** Mark the design text of `box` that sets `shown` as a continuation mark
|
|
1260
|
+
* whose `{page}` the build fills in once pages are numbered (#917). */
|
|
1261
|
+
function tagPageMark(box, shown, text, page) {
|
|
1262
|
+
const want = shown.trim();
|
|
1263
|
+
for (const b of box.designOverlay?.blocks ?? []) {
|
|
1264
|
+
if (b.kind !== 'text' || !b.artifact || b.pageMark)
|
|
1265
|
+
continue;
|
|
1266
|
+
if (b.lines.map((l) => l.text).join(' ').trim() !== want && b.lines.map((l) => l.text).join('').trim() !== want)
|
|
1267
|
+
continue;
|
|
1268
|
+
b.pageMark = { text: text.trim(), page };
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1005
1272
|
//# sourceMappingURL=composedLayout.js.map
|