@jasy/pdf 1.0.0-beta.3 → 1.0.0-beta.4
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/api/content.d.ts +5 -5
- package/dist/api/content.js +12 -6
- package/dist/api/font-url.d.ts +1 -2
- package/dist/api/font-url.js +8 -74
- package/dist/api/forms.d.ts +3 -0
- package/dist/api/forms.js +3 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -0
- package/dist/api/layout.d.ts +6 -0
- package/dist/api/layout.js +7 -2
- package/dist/api/page-builder.d.ts +4 -0
- package/dist/api/page-builder.js +4 -0
- package/dist/api/structure.d.ts +10 -2
- package/dist/api/structure.js +9 -6
- package/dist/api/table.d.ts +5 -3
- package/dist/api/table.js +99 -22
- package/dist/common/diagnostics.d.ts +53 -0
- package/dist/common/diagnostics.js +77 -0
- package/dist/edit/acroform-reader.js +7 -7
- package/dist/edit/appearance.js +3 -3
- package/dist/edit/attachments.d.ts +52 -0
- package/dist/edit/attachments.js +178 -0
- package/dist/edit/document.d.ts +38 -8
- package/dist/edit/document.js +208 -51
- package/dist/edit/fill.js +8 -23
- package/dist/edit/filters.d.ts +15 -0
- package/dist/edit/filters.js +157 -0
- package/dist/edit/index.d.ts +4 -1
- package/dist/edit/index.js +3 -1
- package/dist/edit/objects.d.ts +3 -4
- package/dist/edit/objects.js +5 -18
- package/dist/elements/canvas-element.d.ts +3 -0
- package/dist/elements/canvas-element.js +14 -3
- package/dist/elements/container-element.d.ts +5 -1
- package/dist/elements/container-element.js +35 -15
- package/dist/elements/forms/checkbox-element.d.ts +2 -0
- package/dist/elements/forms/checkbox-element.js +12 -3
- package/dist/elements/forms/choice-element.d.ts +6 -1
- package/dist/elements/forms/choice-element.js +17 -6
- package/dist/elements/forms/field-font.d.ts +8 -0
- package/dist/elements/forms/field-font.js +5 -0
- package/dist/elements/forms/push-button-element.d.ts +6 -1
- package/dist/elements/forms/push-button-element.js +16 -4
- package/dist/elements/forms/radio-element.d.ts +2 -0
- package/dist/elements/forms/radio-element.js +12 -3
- package/dist/elements/forms/signature-element.d.ts +6 -1
- package/dist/elements/forms/signature-element.js +16 -4
- package/dist/elements/forms/text-field-element.d.ts +6 -1
- package/dist/elements/forms/text-field-element.js +16 -4
- package/dist/elements/image-element.d.ts +39 -1
- package/dist/elements/image-element.js +95 -12
- package/dist/elements/layout/anchor-element.d.ts +8 -10
- package/dist/elements/layout/anchor-element.js +10 -18
- package/dist/elements/layout/bookmark-element.d.ts +9 -11
- package/dist/elements/layout/bookmark-element.js +11 -20
- package/dist/elements/layout/default-text-style-element.d.ts +11 -13
- package/dist/elements/layout/default-text-style-element.js +14 -31
- package/dist/elements/layout/deferred-element.d.ts +12 -4
- package/dist/elements/layout/deferred-element.js +24 -7
- package/dist/elements/layout/expanded-element.d.ts +7 -0
- package/dist/elements/layout/expanded-element.js +40 -18
- package/dist/elements/layout/keep-together-element.d.ts +8 -18
- package/dist/elements/layout/keep-together-element.js +21 -45
- package/dist/elements/layout/link-element.d.ts +8 -13
- package/dist/elements/layout/link-element.js +9 -19
- package/dist/elements/layout/padding-element.d.ts +15 -1
- package/dist/elements/layout/padding-element.js +55 -27
- package/dist/elements/layout/page-break-element.d.ts +4 -1
- package/dist/elements/layout/page-break-element.js +6 -1
- package/dist/elements/layout/page-builder-element.d.ts +5 -0
- package/dist/elements/layout/page-builder-element.js +11 -0
- package/dist/elements/layout/positioned-element.d.ts +14 -2
- package/dist/elements/layout/positioned-element.js +36 -10
- package/dist/elements/layout/proxy-element.d.ts +43 -0
- package/dist/elements/layout/proxy-element.js +94 -0
- package/dist/elements/layout/repeating-header-element.d.ts +6 -0
- package/dist/elements/layout/repeating-header-element.js +40 -13
- package/dist/elements/layout/rotated-box-element.d.ts +6 -0
- package/dist/elements/layout/rotated-box-element.js +27 -14
- package/dist/elements/layout/rotated-element.d.ts +7 -0
- package/dist/elements/layout/rotated-element.js +25 -2
- package/dist/elements/layout/struct-group.d.ts +9 -17
- package/dist/elements/layout/struct-group.js +10 -35
- package/dist/elements/line-element.d.ts +1 -0
- package/dist/elements/line-element.js +6 -0
- package/dist/elements/page-element.d.ts +10 -3
- package/dist/elements/page-element.js +39 -31
- package/dist/elements/pdf-document-element.d.ts +3 -0
- package/dist/elements/pdf-document-element.js +7 -0
- package/dist/elements/pdf-element.d.ts +75 -15
- package/dist/elements/pdf-element.js +68 -18
- package/dist/elements/rectangle-element.d.ts +14 -0
- package/dist/elements/rectangle-element.js +97 -52
- package/dist/elements/row-element.d.ts +11 -0
- package/dist/elements/row-element.js +58 -8
- package/dist/elements/svg-element.d.ts +30 -6
- package/dist/elements/svg-element.js +88 -17
- package/dist/elements/text-element.d.ts +32 -13
- package/dist/elements/text-element.js +119 -97
- package/dist/forms/acroform.d.ts +9 -1
- package/dist/forms/acroform.js +83 -34
- package/dist/forms/appearance.d.ts +17 -6
- package/dist/forms/appearance.js +18 -16
- package/dist/ir/display-list.d.ts +12 -2
- package/dist/layout/box-constraints.d.ts +5 -0
- package/dist/layout/box-constraints.js +14 -0
- package/dist/layout/collect-images.d.ts +6 -4
- package/dist/layout/collect-images.js +6 -4
- package/dist/layout/flex-item.d.ts +9 -0
- package/dist/layout/flex-item.js +39 -0
- package/dist/layout/fragmentation.d.ts +12 -4
- package/dist/layout/fragmentation.js +37 -14
- package/dist/layout/layout-cache.d.ts +53 -0
- package/dist/layout/layout-cache.js +107 -0
- package/dist/renderer/anchor-renderer.d.ts +2 -1
- package/dist/renderer/anchor-renderer.js +5 -4
- package/dist/renderer/bookmark-renderer.d.ts +2 -1
- package/dist/renderer/bookmark-renderer.js +5 -4
- package/dist/renderer/canvas-renderer.d.ts +2 -1
- package/dist/renderer/canvas-renderer.js +5 -2
- package/dist/renderer/container-renderer.d.ts +2 -1
- package/dist/renderer/container-renderer.js +6 -5
- package/dist/renderer/default-text-style-renderer.d.ts +2 -1
- package/dist/renderer/default-text-style-renderer.js +2 -3
- package/dist/renderer/deferred-renderer.d.ts +2 -1
- package/dist/renderer/deferred-renderer.js +2 -3
- package/dist/renderer/expanded-renderer.d.ts +2 -1
- package/dist/renderer/expanded-renderer.js +2 -3
- package/dist/renderer/form-field-renderer.d.ts +2 -1
- package/dist/renderer/form-field-renderer.js +16 -3
- package/dist/renderer/image-renderer.d.ts +2 -1
- package/dist/renderer/image-renderer.js +5 -2
- package/dist/renderer/keep-together-renderer.d.ts +2 -1
- package/dist/renderer/keep-together-renderer.js +2 -3
- package/dist/renderer/line-renderer.d.ts +2 -1
- package/dist/renderer/line-renderer.js +7 -2
- package/dist/renderer/link-renderer.d.ts +2 -1
- package/dist/renderer/link-renderer.js +36 -5
- package/dist/renderer/padding-renderer.d.ts +2 -1
- package/dist/renderer/padding-renderer.js +5 -5
- package/dist/renderer/page-break-renderer.d.ts +6 -5
- package/dist/renderer/page-break-renderer.js +14 -7
- package/dist/renderer/page-builder-renderer.d.ts +2 -1
- package/dist/renderer/page-builder-renderer.js +2 -3
- package/dist/renderer/page-renderer.js +28 -7
- package/dist/renderer/pdf-backend.d.ts +4 -3
- package/dist/renderer/pdf-backend.js +5 -5
- package/dist/renderer/pdf-document-renderer.d.ts +1 -8
- package/dist/renderer/pdf-document-renderer.js +15 -16
- package/dist/renderer/pdf-renderer.js +11 -11
- package/dist/renderer/positioned-renderer.d.ts +6 -1
- package/dist/renderer/positioned-renderer.js +6 -5
- package/dist/renderer/rectangle-renderer.d.ts +2 -1
- package/dist/renderer/rectangle-renderer.js +9 -4
- package/dist/renderer/repeating-header-renderer.d.ts +2 -1
- package/dist/renderer/repeating-header-renderer.js +6 -5
- package/dist/renderer/rotated-renderer.d.ts +2 -1
- package/dist/renderer/rotated-renderer.js +8 -5
- package/dist/renderer/row-renderer.d.ts +2 -1
- package/dist/renderer/row-renderer.js +6 -5
- package/dist/renderer/struct-group-renderer.d.ts +2 -1
- package/dist/renderer/struct-group-renderer.js +2 -3
- package/dist/renderer/svg-renderer.d.ts +2 -1
- package/dist/renderer/svg-renderer.js +8 -2
- package/dist/renderer/text-renderer.d.ts +4 -6
- package/dist/renderer/text-renderer.js +101 -111
- package/dist/svg/gradients.js +5 -0
- package/dist/svg/index.js +10 -1
- package/dist/svg/style.d.ts +2 -0
- package/dist/svg/style.js +11 -0
- package/dist/text/advance.d.ts +4 -0
- package/dist/text/advance.js +17 -4
- package/dist/text/bidi.d.ts +0 -2
- package/dist/text/bidi.js +0 -10
- package/dist/text/glyph-coverage.d.ts +1 -1
- package/dist/text/glyph-coverage.js +7 -3
- package/dist/text/hyphen-breaks.d.ts +17 -0
- package/dist/text/hyphen-breaks.js +54 -0
- package/dist/text/line-breaker.d.ts +34 -31
- package/dist/text/line-breaker.js +364 -176
- package/dist/text/line-metrics.d.ts +6 -3
- package/dist/text/line-metrics.js +7 -2
- package/dist/text/pdf-text-string.d.ts +21 -0
- package/dist/text/pdf-text-string.js +114 -0
- package/dist/text/text-decoration.d.ts +5 -5
- package/dist/text/text-decoration.js +5 -5
- package/dist/text/word-splitting.d.ts +15 -2
- package/dist/text/word-splitting.js +34 -8
- package/dist/utils/afm-parser.d.ts +2 -0
- package/dist/utils/afm-parser.js +10 -1
- package/dist/utils/dest-registry.d.ts +1 -1
- package/dist/utils/dest-registry.js +3 -2
- package/dist/utils/fetch-bytes.d.ts +19 -0
- package/dist/utils/fetch-bytes.js +79 -0
- package/dist/utils/flex-layout.d.ts +13 -13
- package/dist/utils/flex-layout.js +318 -216
- package/dist/utils/font-metrics.d.ts +4 -3
- package/dist/utils/image-helper.js +2 -1
- package/dist/utils/pdf-object-manager.d.ts +11 -3
- package/dist/utils/pdf-object-manager.js +39 -13
- package/dist/utils/renderer-registry.d.ts +43 -2
- package/dist/utils/renderer-registry.js +40 -0
- package/dist/utils/struct-tree.d.ts +24 -0
- package/dist/utils/struct-tree.js +61 -9
- package/dist/utils/ttf-parser.d.ts +4 -0
- package/dist/utils/ttf-parser.js +27 -4
- package/dist/validators/element-validator.js +2 -1
- package/package.json +1 -1
- package/dist/elements/layout/sized-container-element.d.ts +0 -14
- package/dist/elements/layout/sized-container-element.js +0 -32
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { alignSelfOf, flexibleOf, flexShrinkOf, orderOf } from "../layout/flex-item.js";
|
|
2
2
|
import { BoxConstraints } from "../layout/box-constraints.js";
|
|
3
|
+
const sameConstraints = (a, b) => a.minWidth === b.minWidth &&
|
|
4
|
+
a.maxWidth === b.maxWidth &&
|
|
5
|
+
a.minHeight === b.minHeight &&
|
|
6
|
+
a.maxHeight === b.maxHeight;
|
|
3
7
|
export const VERTICAL_AXIS = {
|
|
4
8
|
mainHorizontal: false,
|
|
5
9
|
mainOf: (s) => s.height,
|
|
@@ -33,21 +37,24 @@ function crossOffset(align, crossExtent, childCross) {
|
|
|
33
37
|
* container where nobody sets one comes back with the identical array.
|
|
34
38
|
*/
|
|
35
39
|
function inLayoutOrder(children, reverse) {
|
|
36
|
-
const ordered = children.some((c) => c
|
|
37
|
-
? [...children].sort((a, b) => a
|
|
40
|
+
const ordered = children.some((c) => orderOf(c) !== 0)
|
|
41
|
+
? [...children].sort((a, b) => orderOf(a) - orderOf(b))
|
|
38
42
|
: children;
|
|
39
43
|
return reverse ? [...ordered].reverse() : ordered;
|
|
40
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The main-axis alignment as the container means it. Reversed, the axis runs from the other end: `start`
|
|
47
|
+
* is where the first child of the REVERSED order sits - the right of a Row, the bottom of a Column (CSS
|
|
48
|
+
* `row-reverse` with `flex-start`, Flutter's `MainAxisAlignment.start` on a reversed axis). `center`,
|
|
49
|
+
* `between` and `around` are symmetric and stay.
|
|
50
|
+
*/
|
|
51
|
+
function mainAlignOf(options) {
|
|
52
|
+
const main = options.main ?? "start";
|
|
53
|
+
if (!options.reverse)
|
|
54
|
+
return main;
|
|
55
|
+
return main === "start" ? "end" : main === "end" ? "start" : main;
|
|
56
|
+
}
|
|
41
57
|
export class FlexLayoutHelper {
|
|
42
|
-
/**
|
|
43
|
-
* Lays out a flex line along `axis`, IN SOURCE ORDER, and places every child.
|
|
44
|
-
* Fixed children take their natural main extent; flex (`ExpandedElement`) children
|
|
45
|
-
* split the leftover main space by their `flex`. `gap` is inserted between children.
|
|
46
|
-
* `main` distributes any leftover when there is no flex child; `cross` positions/sizes
|
|
47
|
-
* each child across the line. Returns the total main extent consumed and the cross
|
|
48
|
-
* extent occupied. Vertical with `gap 0`, `main start`, `cross stretch` reproduces the
|
|
49
|
-
* previous Column layout exactly.
|
|
50
|
-
*/
|
|
51
58
|
/**
|
|
52
59
|
* Lays out the children along `axis`, on one line or several.
|
|
53
60
|
*
|
|
@@ -55,12 +62,12 @@ export class FlexLayoutHelper {
|
|
|
55
62
|
* straight through, which is what keeps every existing document byte-identical. With `wrap` the
|
|
56
63
|
* children are split into lines first and the SAME engine runs once per line.
|
|
57
64
|
*/
|
|
58
|
-
static layout(children, axis, mainAvail, crossAvail,
|
|
65
|
+
static layout(children, axis, mainAvail, crossAvail, options, ctx, mode = "layout") {
|
|
59
66
|
if (!options.wrap || children.length === 0 || !Number.isFinite(mainAvail)) {
|
|
60
67
|
// An unbounded main axis has no edge to wrap at, so wrapping there is meaningless, not an error.
|
|
61
|
-
return FlexLayoutHelper.layoutLine(children, axis, mainAvail, crossAvail,
|
|
68
|
+
return FlexLayoutHelper.layoutLine(children, axis, mainAvail, crossAvail, 0, options, ctx, mode);
|
|
62
69
|
}
|
|
63
|
-
return FlexLayoutHelper.layoutWrapped(children, axis, mainAvail, crossAvail,
|
|
70
|
+
return FlexLayoutHelper.layoutWrapped(children, axis, mainAvail, crossAvail, options, ctx, mode);
|
|
64
71
|
}
|
|
65
72
|
/**
|
|
66
73
|
* The wrapping path: split into lines, lay each one out, then place the lines across the axis.
|
|
@@ -69,21 +76,19 @@ export class FlexLayoutHelper {
|
|
|
69
76
|
* is the same measure-then-place shape the single-line engine already uses, and it is what
|
|
70
77
|
* `alignContent` needs: the block of lines can only be centred once its total is known.
|
|
71
78
|
*/
|
|
72
|
-
static layoutWrapped(children, axis, mainAvail, crossAvail,
|
|
79
|
+
static layoutWrapped(children, axis, mainAvail, crossAvail, options, ctx, mode) {
|
|
73
80
|
const gap = options.gap ?? 0;
|
|
74
81
|
const ordered = inLayoutOrder(children, options.reverse ?? false);
|
|
75
|
-
// Which children share a line. A child
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// A plain child's extent does not depend on the line, so it is laid out ONCE and remembered.
|
|
81
|
-
// Without this each membership probe re-lays every child already on the line - O(n^2) subtree
|
|
82
|
-
// layouts, which is expensive the moment those children hold a Table or a wrapping paragraph.
|
|
82
|
+
// Which children share a line. A child counts with its natural main extent; a flex child with its
|
|
83
|
+
// BASIS, which is 0 unless it asked for one - so it never forces a break by itself. A PERCENTAGE
|
|
84
|
+
// child's size depends on how many gaps the line ends up with, so it is asked again for every
|
|
85
|
+
// candidate membership. A plain child's extent does not, so it is measured ONCE and remembered -
|
|
86
|
+
// otherwise each membership probe re-lays every child already on the line (O(n^2) subtree layouts).
|
|
83
87
|
const measured = new Map();
|
|
84
88
|
const naturalMain = (child, base) => {
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
const flexible = flexibleOf(child);
|
|
90
|
+
if (flexible)
|
|
91
|
+
return flexible.getBasis(base);
|
|
87
92
|
const factor = child.relativeSizeFactor(axis.mainHorizontal);
|
|
88
93
|
if (factor !== undefined)
|
|
89
94
|
return base * factor;
|
|
@@ -92,9 +97,9 @@ export class FlexLayoutHelper {
|
|
|
92
97
|
return cached;
|
|
93
98
|
// Measured UNCAPPED, the same way the single-line engine measures a plain child. Handing the
|
|
94
99
|
// line width in as a cap would silently squash a child wider than the line - CSS lets such a
|
|
95
|
-
// child overflow, and so does our own non-wrapping path.
|
|
96
|
-
|
|
97
|
-
const main = axis.mainOf(child.
|
|
100
|
+
// child overflow, and so does our own non-wrapping path. A stack holding a Spacer counts with its
|
|
101
|
+
// content too: its Spacer only grows into what is left once the line is laid out.
|
|
102
|
+
const main = axis.mainOf(child.measure(axis.measureConstraints(crossAvail, Infinity), ctx));
|
|
98
103
|
measured.set(child, main);
|
|
99
104
|
return main;
|
|
100
105
|
};
|
|
@@ -117,16 +122,19 @@ export class FlexLayoutHelper {
|
|
|
117
122
|
}
|
|
118
123
|
// Each line keeps SOURCE order internally: the ordering was applied above, and applying it again
|
|
119
124
|
// per line would reverse twice.
|
|
120
|
-
const lineOptions = { ...options, wrap: false, reverse: false };
|
|
121
|
-
const measure = lines.map((line) => FlexLayoutHelper.layoutLine(line, axis, mainAvail,
|
|
122
|
-
|
|
125
|
+
const lineOptions = { ...options, wrap: false, reverse: false, main: mainAlignOf(options) };
|
|
126
|
+
const measure = lines.map((line) => FlexLayoutHelper.layoutLine(line, axis, mainAvail,
|
|
127
|
+
// Across a Column a child gets the column's width, as a block does in CSS (a paragraph wraps
|
|
128
|
+
// inside it); across a Row it is as tall as its content. Either way the line is only as big as
|
|
129
|
+
// what is on it, so `stretch` fills the line and not the container.
|
|
130
|
+
axis.mainHorizontal ? Infinity : crossAvail, 0, lineOptions, ctx, "measure", true));
|
|
123
131
|
const heights = measure.map((m) => m.crossUsed);
|
|
124
132
|
const totalCross = heights.reduce((n, h) => n + h, 0) + Math.max(0, lines.length - 1) * gap;
|
|
125
133
|
// `alignContent` distributes the BLOCK of lines across the axis, using the same vocabulary the main
|
|
126
134
|
// axis uses. Only meaningful in a bounded cross axis with room left over.
|
|
127
135
|
const slack = Number.isFinite(crossAvail) ? crossAvail - totalCross : 0;
|
|
128
136
|
const align = options.alignContent ?? "start";
|
|
129
|
-
let cursor =
|
|
137
|
+
let cursor = 0;
|
|
130
138
|
let between = gap;
|
|
131
139
|
if (slack > 0) {
|
|
132
140
|
if (align === "center")
|
|
@@ -143,203 +151,297 @@ export class FlexLayoutHelper {
|
|
|
143
151
|
}
|
|
144
152
|
lines.forEach((line, i) => {
|
|
145
153
|
FlexLayoutHelper.layoutLine(line, axis, mainAvail, heights[i], // the line's own extent, so `stretch` fills the line and not the container
|
|
146
|
-
|
|
154
|
+
cursor, lineOptions, ctx, mode);
|
|
147
155
|
cursor += heights[i];
|
|
148
156
|
if (i < lines.length - 1)
|
|
149
157
|
cursor += between;
|
|
150
158
|
});
|
|
151
|
-
return { mainUsed: mainAvail, crossUsed: Math.max(totalCross, cursor
|
|
159
|
+
return { mainUsed: mainAvail, crossUsed: Math.max(totalCross, cursor) };
|
|
152
160
|
}
|
|
153
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Lays out ONE flex line along `axis` and places every child. Fixed children take their natural main
|
|
163
|
+
* extent; flex children split the leftover by their `flex`; `gap` goes between children. `main`
|
|
164
|
+
* distributes leftover when nothing flexes; `cross` positions or stretches each child across the line.
|
|
165
|
+
* Returns the main extent consumed and the cross extent occupied.
|
|
166
|
+
*
|
|
167
|
+
* The steps run in this order, and the order is part of the result: `leftover` is taken after the
|
|
168
|
+
* shrink and before the share, and the main-axis spacing reads exactly that value.
|
|
169
|
+
*/
|
|
170
|
+
static layoutLine(children, axis, mainAvail, crossAvail, crossOrigin, options, ctx, mode,
|
|
171
|
+
// The line's cross extent is its content, not `crossAvail`: one line of a wrapping container.
|
|
172
|
+
crossFromContent = false) {
|
|
154
173
|
const gap = options.gap ?? 0;
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const needsBound = child.relativeSizeFactor(axis.mainHorizontal) !== undefined ||
|
|
178
|
-
child.needsBoundedMain(axis.mainHorizontal);
|
|
179
|
-
return needsBound ? percentBase : Infinity;
|
|
174
|
+
const ordered = inLayoutOrder(children, options.reverse ?? false);
|
|
175
|
+
const totalGap = Math.max(0, ordered.length - 1) * gap;
|
|
176
|
+
const line = {
|
|
177
|
+
children: ordered,
|
|
178
|
+
axis,
|
|
179
|
+
mainAvail,
|
|
180
|
+
crossAvail,
|
|
181
|
+
ctx,
|
|
182
|
+
totalGap,
|
|
183
|
+
// The main extent a percentage child resolves against: the line minus the gaps between its items,
|
|
184
|
+
// so N children at (100/N)% + gaps fit exactly (the flexbox `width: 33%` + gap trap). Only finite
|
|
185
|
+
// on a bounded main axis - a fraction of an unbounded one has no meaning, so `%` shrink-wraps.
|
|
186
|
+
percentBase: mainAvail !== Infinity ? Math.max(0, mainAvail - totalGap) : Infinity,
|
|
187
|
+
fixedMain: 0,
|
|
188
|
+
crossUsed: 0,
|
|
189
|
+
totalFlex: 0,
|
|
190
|
+
totalBasis: 0,
|
|
191
|
+
sizes: new Map(),
|
|
192
|
+
under: new Map(),
|
|
193
|
+
shrunkCap: new Map(),
|
|
194
|
+
sharedCap: new Map(),
|
|
195
|
+
shrunkBasis: new Map(),
|
|
180
196
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let fixedMain = 0;
|
|
185
|
-
let totalFlex = 0;
|
|
186
|
-
let totalBasis = 0;
|
|
187
|
-
let crossUsed = 0;
|
|
188
|
-
const fixedSize = new Map();
|
|
189
|
-
for (const child of children) {
|
|
190
|
-
if (child instanceof FlexiblePDFElement) {
|
|
191
|
-
totalFlex += child.getFlex();
|
|
192
|
-
totalBasis += child.getBasis(percentBase);
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
const size = child.calculateLayout(axis.measureConstraints(crossAvail, mainCapFor(child)), axis.offsetAt(mainStart, crossOrigin), ctx);
|
|
196
|
-
fixedMain += axis.mainOf(size);
|
|
197
|
-
crossUsed = Math.max(crossUsed, axis.crossOf(size));
|
|
198
|
-
fixedSize.set(child, size);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
// Shrinking: the line overflows and some children said they would give space back. CSS weights a
|
|
202
|
-
// shrinker's share by `shrink x its own size`, so a big item yields more than a small one - which is
|
|
203
|
-
// why this cannot reuse the grow maths above. Nobody shrinks by default, so a document that never
|
|
204
|
-
// asks for it never enters this block.
|
|
205
|
-
const shrinkers = children.filter((c) => c.flexShrink > 0 && !(c instanceof FlexiblePDFElement));
|
|
206
|
-
const overflow = fixedMain + totalBasis + totalGap - mainAvail;
|
|
207
|
-
if (shrinkers.length > 0 && Number.isFinite(mainAvail) && overflow > 0) {
|
|
208
|
-
// Every child is floored at what it can actually hold - its `min-content` extent, which is CSS's
|
|
209
|
-
// automatic `min-width` on a flex item. Without it a badly overflowing line would grind a
|
|
210
|
-
// paragraph down to one word per line: the box gets narrow, the text gets tall, and nothing is
|
|
211
|
-
// gained. Past that floor a child overflows again, which is what a browser does too.
|
|
212
|
-
const naturalOf = (c) => axis.mainOf(fixedSize.get(c));
|
|
213
|
-
const floorOf = new Map(shrinkers.map((c) => [
|
|
214
|
-
c,
|
|
215
|
-
// Never above its natural size: the shrink pass may only take space away, never hand it out.
|
|
216
|
-
// Never below zero either - when the GAPS alone outgrow the line a proportional share asks
|
|
217
|
-
// for more than a child has, and a negative constraint is nonsense even though
|
|
218
|
-
// `constrainWidth` would floor it further down anyway.
|
|
219
|
-
Math.min(naturalOf(c), Math.max(0, c.minIntrinsicMain(axis.mainHorizontal, ctx))),
|
|
220
|
-
]));
|
|
221
|
-
// CSS resolves this iteratively (Flexbox 9.7): a child that lands on its floor is FROZEN there
|
|
222
|
-
// and the share it could not absorb is handed to the rest. A single pass would leave that share
|
|
223
|
-
// unspent, so one unsqueezable child would keep the whole line overflowing while its neighbours
|
|
224
|
-
// still had room to give.
|
|
225
|
-
const target = new Map(shrinkers.map((c) => [c, naturalOf(c)]));
|
|
226
|
-
const frozen = new Set();
|
|
227
|
-
let flexible = [...shrinkers];
|
|
228
|
-
// What the frozen ones took off the overflow; the rest is always shared out from scratch, never
|
|
229
|
-
// from what a previous round already assigned.
|
|
230
|
-
let absorbedByFrozen = 0;
|
|
231
|
-
while (flexible.length > 0) {
|
|
232
|
-
const toAbsorb = overflow - absorbedByFrozen;
|
|
233
|
-
if (toAbsorb <= 1e-9)
|
|
234
|
-
break;
|
|
235
|
-
const weight = (c) => c.flexShrink * naturalOf(c);
|
|
236
|
-
const totalWeight = flexible.reduce((n, c) => n + weight(c), 0);
|
|
237
|
-
if (totalWeight <= 0)
|
|
238
|
-
break;
|
|
239
|
-
const froze = [];
|
|
240
|
-
for (const child of flexible) {
|
|
241
|
-
const natural = naturalOf(child);
|
|
242
|
-
const floor = floorOf.get(child);
|
|
243
|
-
const wanted = natural - (weight(child) / totalWeight) * toAbsorb;
|
|
244
|
-
if (wanted < floor) {
|
|
245
|
-
target.set(child, floor);
|
|
246
|
-
froze.push(child);
|
|
247
|
-
absorbedByFrozen += natural - floor;
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
target.set(child, wanted);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
if (froze.length === 0)
|
|
254
|
-
break;
|
|
255
|
-
for (const c of froze)
|
|
256
|
-
frozen.add(c);
|
|
257
|
-
flexible = flexible.filter((c) => !frozen.has(c));
|
|
258
|
-
}
|
|
259
|
-
for (const child of shrinkers) {
|
|
260
|
-
const natural = naturalOf(child);
|
|
261
|
-
const capped = target.get(child);
|
|
262
|
-
if (capped >= natural)
|
|
263
|
-
continue;
|
|
264
|
-
shrunkCap.set(child, capped);
|
|
265
|
-
// Re-measure NOW, not in pass 2: a narrower child may wrap to more lines, and the line's
|
|
266
|
-
// cross extent is settled just below.
|
|
267
|
-
const size = child.calculateLayout(axis.measureConstraints(crossAvail, capped), axis.offsetAt(mainStart, crossOrigin), ctx);
|
|
268
|
-
fixedMain += axis.mainOf(size) - natural;
|
|
269
|
-
crossUsed = Math.max(crossUsed, axis.crossOf(size));
|
|
270
|
-
fixedSize.set(child, size);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
const leftover = mainAvail - fixedMain - totalBasis - totalGap;
|
|
197
|
+
measureLine(line);
|
|
198
|
+
shrinkLine(line);
|
|
199
|
+
const leftover = mainAvail - line.fixedMain - line.totalBasis - totalGap;
|
|
274
200
|
// A flex child on an UNBOUNDED main axis has no leftover space to claim. It must collapse to zero,
|
|
275
201
|
// never to `Infinity`: an infinite extent would become the offset of every following sibling and get
|
|
276
202
|
// written into the content stream verbatim, silently corrupting the page from that point on.
|
|
277
203
|
const remaining = Number.isFinite(leftover) ? Math.max(leftover, 0) : 0;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (totalFlex === 0 && mainAvail !== Infinity && leftover > 0) {
|
|
282
|
-
if (main === "center")
|
|
283
|
-
leadingSpace = leftover / 2;
|
|
284
|
-
else if (main === "end")
|
|
285
|
-
leadingSpace = leftover;
|
|
286
|
-
else if (main === "between" && count > 1)
|
|
287
|
-
betweenSpace = gap + leftover / (count - 1);
|
|
288
|
-
else if (main === "around") {
|
|
289
|
-
const unit = leftover / count;
|
|
290
|
-
leadingSpace = unit / 2;
|
|
291
|
-
betweenSpace = gap + unit;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// Measure flex children too (at their main share) so a tall/wrapping flex cell counts
|
|
295
|
-
// toward the line's cross extent - they're placed in pass 2, but crossExtent needs them now.
|
|
296
|
-
if (totalFlex > 0) {
|
|
297
|
-
for (const child of children) {
|
|
298
|
-
if (child instanceof FlexiblePDFElement) {
|
|
299
|
-
const mainExtent = child.getBasis(percentBase) + (child.getFlex() / totalFlex) * remaining;
|
|
300
|
-
const size = child.calculateLayout(axis.flexConstraints(mainExtent, crossAvail), axis.offsetAt(mainStart, crossOrigin), ctx);
|
|
301
|
-
crossUsed = Math.max(crossUsed, axis.crossOf(size));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
204
|
+
const weight = shareLeftover(line, remaining);
|
|
205
|
+
const spacing = mainSpacing(mainAlignOf(options), gap, ordered.length, mainAvail, leftover, weight);
|
|
206
|
+
measureFlex(line, weight, remaining);
|
|
305
207
|
// The cross extent children align within: the bounded line size, else the tallest child.
|
|
306
|
-
const crossExtent = crossAvail !== Infinity ? crossAvail : crossUsed;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
208
|
+
const crossExtent = crossAvail !== Infinity && !crossFromContent ? crossAvail : line.crossUsed;
|
|
209
|
+
return placeLine(line, options.cross ?? "stretch", crossExtent, crossOrigin, spacing, weight, remaining, mode);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/** A flex child's basis on this line: what it asked for, or less once the line has shrunk it. */
|
|
213
|
+
function basisOf(line, child, flexible) {
|
|
214
|
+
return line.shrunkBasis.get(child) ?? flexible.getBasis(line.percentBase);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The main cap to hand a child: its shrunk or shared cap once the line is settled; `percentBase` for a
|
|
218
|
+
* percentage child, so its fraction resolves; unbounded for everyone else, who keep their natural size.
|
|
219
|
+
*/
|
|
220
|
+
function mainCapFor(line, child) {
|
|
221
|
+
const target = line.shrunkCap.get(child) ?? line.sharedCap.get(child);
|
|
222
|
+
if (target !== undefined)
|
|
223
|
+
return target;
|
|
224
|
+
if (line.percentBase === Infinity)
|
|
225
|
+
return Infinity;
|
|
226
|
+
return child.relativeSizeFactor(line.axis.mainHorizontal) !== undefined
|
|
227
|
+
? line.percentBase
|
|
228
|
+
: Infinity;
|
|
229
|
+
}
|
|
230
|
+
/** Measures `child` and remembers the size with the constraints it answers. */
|
|
231
|
+
function measureInto(line, child, constraints) {
|
|
232
|
+
const size = child.measure(constraints, line.ctx);
|
|
233
|
+
line.sizes.set(child, size);
|
|
234
|
+
line.under.set(child, constraints);
|
|
235
|
+
return size;
|
|
236
|
+
}
|
|
237
|
+
/** Measures a fixed child again at a new main cap, moving the line's totals with it. */
|
|
238
|
+
function remeasure(line, child, cap) {
|
|
239
|
+
const before = line.axis.mainOf(line.sizes.get(child));
|
|
240
|
+
const size = measureInto(line, child, line.axis.measureConstraints(line.crossAvail, cap));
|
|
241
|
+
line.fixedMain += line.axis.mainOf(size) - before;
|
|
242
|
+
line.crossUsed = Math.max(line.crossUsed, line.axis.crossOf(size));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Step 1: measure the fixed children (main extent + cross size) and total the flex. A flex child's BASIS
|
|
246
|
+
* is reserved here too: it is main extent the leftover no longer contains, exactly like a fixed child's
|
|
247
|
+
* size. With the default basis of 0 it adds nothing.
|
|
248
|
+
*/
|
|
249
|
+
function measureLine(line) {
|
|
250
|
+
const { axis } = line;
|
|
251
|
+
for (const child of line.children) {
|
|
252
|
+
const flexible = flexibleOf(child);
|
|
253
|
+
if (flexible) {
|
|
254
|
+
line.totalFlex += flexible.getFlex();
|
|
255
|
+
line.totalBasis += flexible.getBasis(line.percentBase);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const size = measureInto(line, child, axis.measureConstraints(line.crossAvail, mainCapFor(line, child)));
|
|
259
|
+
line.fixedMain += axis.mainOf(size);
|
|
260
|
+
line.crossUsed = Math.max(line.crossUsed, axis.crossOf(size));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Step 2: the line overflows and some children said they would give space back. CSS weights a shrinker's
|
|
265
|
+
* share by `shrink x its own size`, so a big item yields more than a small one - for a flex child that
|
|
266
|
+
* size is its basis, so a Spacer (basis 0) gives nothing. A document that never overflows never gets past
|
|
267
|
+
* the first test.
|
|
268
|
+
*/
|
|
269
|
+
function shrinkLine(line) {
|
|
270
|
+
const { axis } = line;
|
|
271
|
+
const overflow = line.fixedMain + line.totalBasis + line.totalGap - line.mainAvail;
|
|
272
|
+
if (!Number.isFinite(line.mainAvail) || !(overflow > 0))
|
|
273
|
+
return;
|
|
274
|
+
const shrinkers = line.children.filter((c) => {
|
|
275
|
+
if (!(flexShrinkOf(c) > 0))
|
|
276
|
+
return false;
|
|
277
|
+
const flexible = flexibleOf(c);
|
|
278
|
+
return !flexible || flexible.getBasis(line.percentBase) > 0;
|
|
279
|
+
});
|
|
280
|
+
if (shrinkers.length === 0)
|
|
281
|
+
return;
|
|
282
|
+
// Every child is floored at what it can actually hold - its `min-content` extent, which is CSS's
|
|
283
|
+
// automatic `min-width` on a flex item. Without it a badly overflowing line would grind a paragraph
|
|
284
|
+
// down to one word per line. Past that floor a child overflows again, as in a browser.
|
|
285
|
+
const naturalOf = (c) => {
|
|
286
|
+
const flexible = flexibleOf(c);
|
|
287
|
+
return flexible ? flexible.getBasis(line.percentBase) : axis.mainOf(line.sizes.get(c));
|
|
288
|
+
};
|
|
289
|
+
const floorOf = new Map(shrinkers.map((c) => [
|
|
290
|
+
c,
|
|
291
|
+
// Never above its natural size (the shrink only takes space away) and never below zero (when the
|
|
292
|
+
// GAPS alone outgrow the line, a proportional share asks for more than a child has).
|
|
293
|
+
Math.min(naturalOf(c), Math.max(0, c.minIntrinsicMain(axis.mainHorizontal, line.ctx))),
|
|
294
|
+
]));
|
|
295
|
+
// CSS resolves this iteratively (Flexbox 9.7): a child that lands on its floor is FROZEN there and the
|
|
296
|
+
// share it could not absorb is handed to the rest. A single pass would leave that share unspent.
|
|
297
|
+
const target = new Map(shrinkers.map((c) => [c, naturalOf(c)]));
|
|
298
|
+
const frozen = new Set();
|
|
299
|
+
let flexible = [...shrinkers];
|
|
300
|
+
// What the frozen ones took off the overflow; the rest is always shared out from scratch.
|
|
301
|
+
let absorbedByFrozen = 0;
|
|
302
|
+
while (flexible.length > 0) {
|
|
303
|
+
const toAbsorb = overflow - absorbedByFrozen;
|
|
304
|
+
if (toAbsorb <= 1e-9)
|
|
305
|
+
break;
|
|
306
|
+
const weight = (c) => flexShrinkOf(c) * naturalOf(c);
|
|
307
|
+
const totalWeight = flexible.reduce((n, c) => n + weight(c), 0);
|
|
308
|
+
if (totalWeight <= 0)
|
|
309
|
+
break;
|
|
310
|
+
const froze = [];
|
|
311
|
+
for (const child of flexible) {
|
|
312
|
+
const natural = naturalOf(child);
|
|
313
|
+
const floor = floorOf.get(child);
|
|
314
|
+
const wanted = natural - (weight(child) / totalWeight) * toAbsorb;
|
|
315
|
+
if (wanted < floor) {
|
|
316
|
+
target.set(child, floor);
|
|
317
|
+
froze.push(child);
|
|
318
|
+
absorbedByFrozen += natural - floor;
|
|
332
319
|
}
|
|
333
320
|
else {
|
|
334
|
-
|
|
335
|
-
const size = child.calculateLayout(axis.measureConstraints(stretch ? crossExtent : crossAvail, mainCapFor(child)), axis.offsetAt(mainPos, crossOrigin + crossOffset(align, crossExtent, childCross)), ctx);
|
|
336
|
-
mainExtent = axis.mainOf(size);
|
|
337
|
-
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
321
|
+
target.set(child, wanted);
|
|
338
322
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
323
|
+
}
|
|
324
|
+
if (froze.length === 0)
|
|
325
|
+
break;
|
|
326
|
+
for (const c of froze)
|
|
327
|
+
frozen.add(c);
|
|
328
|
+
flexible = flexible.filter((c) => !frozen.has(c));
|
|
329
|
+
}
|
|
330
|
+
for (const child of shrinkers) {
|
|
331
|
+
const capped = target.get(child);
|
|
332
|
+
if (capped >= naturalOf(child))
|
|
333
|
+
continue;
|
|
334
|
+
if (flexibleOf(child)) {
|
|
335
|
+
// A flex child is measured later, at its basis plus its share; it starts from less.
|
|
336
|
+
line.totalBasis -= naturalOf(child) - capped;
|
|
337
|
+
line.shrunkBasis.set(child, capped);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
line.shrunkCap.set(child, capped);
|
|
341
|
+
// Re-measured NOW: a narrower child may wrap to more lines, and the line's cross extent is
|
|
342
|
+
// settled before anything is placed.
|
|
343
|
+
remeasure(line, child, capped);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Step 3: a stack holding a Spacer grows like an `Expanded` of flex 1 whose basis is its content - it
|
|
348
|
+
* shares what is left with the flex children. Handing it the whole line squeezed its siblings; handing
|
|
349
|
+
* it a share before the line was settled could leave it less than its own content. Returns the total
|
|
350
|
+
* weight the leftover is shared by.
|
|
351
|
+
*/
|
|
352
|
+
function shareLeftover(line, remaining) {
|
|
353
|
+
const { axis } = line;
|
|
354
|
+
const sharing = line.children.filter((child) => line.percentBase !== Infinity &&
|
|
355
|
+
!flexibleOf(child) &&
|
|
356
|
+
child.relativeSizeFactor(axis.mainHorizontal) === undefined &&
|
|
357
|
+
child.needsBoundedMain(axis.mainHorizontal));
|
|
358
|
+
const weight = line.totalFlex + sharing.length;
|
|
359
|
+
if (remaining > 0) {
|
|
360
|
+
for (const child of sharing) {
|
|
361
|
+
const grown = axis.mainOf(line.sizes.get(child)) + remaining / weight;
|
|
362
|
+
line.sharedCap.set(child, grown);
|
|
363
|
+
remeasure(line, child, grown);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return weight;
|
|
367
|
+
}
|
|
368
|
+
/** Step 4: the main-axis distribution - only with nothing to flex and bounded, positive space. */
|
|
369
|
+
function mainSpacing(main, gap, count, mainAvail, leftover, weight) {
|
|
370
|
+
if (weight !== 0 || mainAvail === Infinity || !(leftover > 0))
|
|
371
|
+
return { leading: 0, between: gap };
|
|
372
|
+
if (main === "center")
|
|
373
|
+
return { leading: leftover / 2, between: gap };
|
|
374
|
+
if (main === "end")
|
|
375
|
+
return { leading: leftover, between: gap };
|
|
376
|
+
if (main === "between" && count > 1)
|
|
377
|
+
return { leading: 0, between: gap + leftover / (count - 1) };
|
|
378
|
+
if (main === "around") {
|
|
379
|
+
const unit = leftover / count;
|
|
380
|
+
return { leading: unit / 2, between: gap + unit };
|
|
381
|
+
}
|
|
382
|
+
return { leading: 0, between: gap };
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Step 5: measure the flex children at their share, so a tall or wrapping flex cell counts toward the
|
|
386
|
+
* line's cross extent before anything is placed.
|
|
387
|
+
*/
|
|
388
|
+
function measureFlex(line, weight, remaining) {
|
|
389
|
+
for (const child of line.children) {
|
|
390
|
+
const flexible = flexibleOf(child);
|
|
391
|
+
if (!flexible)
|
|
392
|
+
continue;
|
|
393
|
+
// A pure `flexBasis` slot (`flex: 0`) has no share but still has a size across the line.
|
|
394
|
+
const share = weight > 0 ? (flexible.getFlex() / weight) * remaining : 0;
|
|
395
|
+
const mainExtent = basisOf(line, child, flexible) + share;
|
|
396
|
+
const size = measureInto(line, child, line.axis.flexConstraints(mainExtent, line.crossAvail));
|
|
397
|
+
line.crossUsed = Math.max(line.crossUsed, line.axis.crossOf(size));
|
|
344
398
|
}
|
|
345
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Step 6: place each child at the running main position, offset across by its alignment. This is the
|
|
402
|
+
* ONE layout a child gets. Measuring, it only sizes - and reuses a size already measured under the same
|
|
403
|
+
* constraints, or the measuring itself would double at every level.
|
|
404
|
+
*
|
|
405
|
+
* `stretch` caps a child's cross to `crossExtent`, not `crossAvail`, so siblings end up equal across the
|
|
406
|
+
* axis. A child may override the container with its own `alignSelf` (CSS).
|
|
407
|
+
*/
|
|
408
|
+
function placeLine(line, cross, crossExtent, crossOrigin, spacing, weight, remaining, mode) {
|
|
409
|
+
const { axis, ctx } = line;
|
|
410
|
+
const place = (child, constraints, offset) => {
|
|
411
|
+
if (mode === "layout")
|
|
412
|
+
return child.calculateLayout(constraints, offset, ctx);
|
|
413
|
+
const known = line.under.get(child);
|
|
414
|
+
return known && sameConstraints(known, constraints)
|
|
415
|
+
? line.sizes.get(child)
|
|
416
|
+
: child.measure(constraints, ctx);
|
|
417
|
+
};
|
|
418
|
+
const last = line.children.length - 1;
|
|
419
|
+
let mainPos = spacing.leading;
|
|
420
|
+
let placedCross = 0;
|
|
421
|
+
line.children.forEach((child, index) => {
|
|
422
|
+
let mainExtent;
|
|
423
|
+
const flexible = flexibleOf(child);
|
|
424
|
+
if (flexible) {
|
|
425
|
+
// `totalFlex` is 0 when every flex child asked for `flex: 0` (a pure `flexBasis` slot). The share
|
|
426
|
+
// is then 0/0 - a NaN that becomes the offset of every later sibling. Such a child takes its basis.
|
|
427
|
+
const share = weight > 0 ? (flexible.getFlex() / weight) * remaining : 0;
|
|
428
|
+
mainExtent = basisOf(line, child, flexible) + share;
|
|
429
|
+
// A flex child fills the MAIN axis and its cross size is only known after layout, so `alignSelf`
|
|
430
|
+
// is a no-op here - and that has to hold for the CROSS CONSTRAINT too, or an `alignSelf: "start"`
|
|
431
|
+
// flex child would get an unbounded cross axis on a shrink-wrapping line.
|
|
432
|
+
const size = place(child, axis.flexConstraints(mainExtent, cross === "stretch" ? crossExtent : line.crossAvail), axis.offsetAt(mainPos, crossOrigin));
|
|
433
|
+
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
const align = alignSelfOf(child) ?? cross;
|
|
437
|
+
const childCross = axis.crossOf(line.sizes.get(child));
|
|
438
|
+
const size = place(child, axis.measureConstraints(align === "stretch" ? crossExtent : line.crossAvail, mainCapFor(line, child)), axis.offsetAt(mainPos, crossOrigin + crossOffset(align, crossExtent, childCross)));
|
|
439
|
+
mainExtent = axis.mainOf(size);
|
|
440
|
+
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
441
|
+
}
|
|
442
|
+
mainPos += mainExtent;
|
|
443
|
+
if (index < last)
|
|
444
|
+
mainPos += spacing.between;
|
|
445
|
+
});
|
|
446
|
+
return { mainUsed: mainPos, crossUsed: placedCross };
|
|
447
|
+
}
|
|
@@ -31,9 +31,10 @@ export interface FontMetrics {
|
|
|
31
31
|
/** Whether the document draws this code point as colour emoji rather than from the text font - it
|
|
32
32
|
* is missing from that font on purpose, so glyph coverage must not touch it. */
|
|
33
33
|
rendersAsEmoji?(codePoint: number, fontFamily: string, fontStyle: FontStyle): boolean;
|
|
34
|
-
/** Told when a code point had to be removed because no font could draw it
|
|
35
|
-
* object used only for measuring (a test double) has
|
|
36
|
-
|
|
34
|
+
/** Told when a code point had to be removed because no font could draw it - `about` is the element
|
|
35
|
+
* it was removed from. Optional: a metrics object used only for measuring (a test double) has
|
|
36
|
+
* nothing to report to. */
|
|
37
|
+
reportMissingGlyph?(codePoints: number[], about?: object, page?: number): void;
|
|
37
38
|
/** How many glyphs the run will DRAW, when a ligature made that differ from its code-point count.
|
|
38
39
|
* Absent or `undefined` means "the same", which is every Latin run. */
|
|
39
40
|
shapedGlyphCount?(text: string, fontFamily?: string, fontStyle?: FontStyle, ligatures?: boolean): number | undefined;
|
|
@@ -9,7 +9,8 @@ DataView.prototype.getUint24 = function (byteOffset, littleEndian = false) {
|
|
|
9
9
|
return littleEndian ? (b3 << 16) | (b2 << 8) | b1 : (b1 << 16) | (b2 << 8) | b3;
|
|
10
10
|
};
|
|
11
11
|
export async function getImageDimensions(buffer) {
|
|
12
|
-
|
|
12
|
+
// The view's own window: a `Uint8Array` may be a slice of a larger buffer (a Node `Buffer` usually is).
|
|
13
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
13
14
|
// Check for JPEG (0xFFD8 is the start of JPEG file)
|
|
14
15
|
if (dataView.getUint16(0) === 0xffd8) {
|
|
15
16
|
let offset = 2;
|