@jasy/pdf 1.0.0-beta.4 → 1.0.0-beta.5
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/color.d.ts +1 -24
- package/dist/api/color.js +2 -278
- package/dist/api/content.d.ts +7 -2
- package/dist/api/content.js +17 -7
- package/dist/api/descriptor.js +30 -3
- package/dist/api/flow.d.ts +55 -0
- package/dist/api/flow.js +71 -0
- package/dist/api/gradient.d.ts +1 -53
- package/dist/api/gradient.js +2 -97
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/layout.js +4 -3
- package/dist/api/table.js +3 -1
- package/dist/api/text.d.ts +8 -8
- package/dist/api/text.js +6 -0
- package/dist/canvas/painter.d.ts +2 -2
- package/dist/canvas/painter.js +2 -2
- package/dist/common/color-input.d.ts +24 -0
- package/dist/common/color-input.js +278 -0
- package/dist/common/diagnostics.d.ts +9 -1
- package/dist/common/gradient.d.ts +53 -0
- package/dist/common/gradient.js +97 -0
- package/dist/elements/container-element.d.ts +3 -0
- package/dist/elements/container-element.js +17 -5
- package/dist/elements/image-element.d.ts +37 -26
- package/dist/elements/image-element.js +88 -50
- package/dist/elements/index.d.ts +3 -0
- package/dist/elements/index.js +3 -0
- package/dist/elements/layout/column-break-element.d.ts +17 -0
- package/dist/elements/layout/column-break-element.js +26 -0
- package/dist/elements/layout/deferred-element.d.ts +9 -1
- package/dist/elements/layout/deferred-element.js +18 -3
- package/dist/elements/layout/expanded-element.d.ts +4 -0
- package/dist/elements/layout/expanded-element.js +14 -1
- package/dist/elements/layout/flow-element.d.ts +109 -0
- package/dist/elements/layout/flow-element.js +317 -0
- package/dist/elements/layout/keep-together-element.js +14 -2
- package/dist/elements/layout/padding-element.d.ts +3 -0
- package/dist/elements/layout/padding-element.js +9 -2
- package/dist/elements/layout/page-builder-element.js +2 -2
- package/dist/elements/layout/positioned-element.d.ts +9 -2
- package/dist/elements/layout/positioned-element.js +6 -2
- package/dist/elements/layout/proxy-element.d.ts +4 -0
- package/dist/elements/layout/proxy-element.js +12 -0
- package/dist/elements/layout/region-element.d.ts +53 -0
- package/dist/elements/layout/region-element.js +108 -0
- package/dist/elements/layout/repeating-header-element.d.ts +3 -0
- package/dist/elements/layout/repeating-header-element.js +16 -1
- package/dist/elements/layout/rotated-element.d.ts +2 -0
- package/dist/elements/layout/rotated-element.js +8 -0
- package/dist/elements/pdf-element.d.ts +32 -3
- package/dist/elements/pdf-element.js +50 -1
- package/dist/elements/rectangle-element.d.ts +12 -6
- package/dist/elements/rectangle-element.js +31 -12
- package/dist/elements/row-element.d.ts +6 -12
- package/dist/elements/row-element.js +34 -4
- package/dist/elements/svg-element.js +4 -10
- package/dist/elements/text-element.d.ts +17 -1
- package/dist/elements/text-element.js +63 -40
- package/dist/ir/display-list.d.ts +6 -1
- package/dist/layout/box-constraints.d.ts +12 -0
- package/dist/layout/box-constraints.js +17 -0
- package/dist/layout/collect-images.js +8 -4
- package/dist/layout/flex-item.d.ts +2 -0
- package/dist/layout/flex-item.js +14 -2
- package/dist/layout/fragmentation.d.ts +11 -5
- package/dist/layout/fragmentation.js +27 -7
- package/dist/layout/layout-cache.d.ts +8 -8
- package/dist/layout/layout-cache.js +10 -7
- package/dist/platform/browser-fs.d.ts +1 -2
- package/dist/platform/browser-fs.js +3 -6
- package/dist/platform/node-fs.d.ts +0 -1
- package/dist/platform/node-fs.js +0 -4
- package/dist/renderer/column-break-renderer.d.ts +8 -0
- package/dist/renderer/column-break-renderer.js +15 -0
- package/dist/renderer/emoji-image.js +3 -1
- package/dist/renderer/flow-renderer.d.ts +16 -0
- package/dist/renderer/flow-renderer.js +21 -0
- package/dist/renderer/image-renderer.js +31 -54
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +2 -0
- package/dist/renderer/page-renderer.js +3 -4
- package/dist/renderer/pdf-backend.js +9 -2
- package/dist/renderer/pdf-document-renderer.js +2 -1
- package/dist/renderer/pdf-renderer.js +9 -0
- package/dist/renderer/positioned-renderer.d.ts +14 -2
- package/dist/renderer/positioned-renderer.js +40 -4
- package/dist/renderer/rectangle-renderer.d.ts +1 -1
- package/dist/renderer/rectangle-renderer.js +11 -6
- package/dist/renderer/rotated-renderer.js +3 -2
- package/dist/renderer/text-renderer.js +25 -28
- package/dist/svg/index.d.ts +1 -1
- package/dist/svg/index.js +26 -5
- package/dist/svg/style.js +1 -1
- package/dist/text/line-breaker.d.ts +23 -0
- package/dist/text/line-breaker.js +142 -65
- package/dist/text/line-metrics.d.ts +2 -0
- package/dist/text/line-metrics.js +5 -1
- package/dist/text/word-splitting.js +11 -5
- package/dist/utils/flex-layout.d.ts +3 -1
- package/dist/utils/flex-layout.js +141 -33
- package/dist/utils/image-helper.d.ts +59 -11
- package/dist/utils/image-helper.js +207 -80
- package/dist/utils/pdf-object-manager.d.ts +6 -3
- package/dist/utils/pdf-object-manager.js +31 -7
- package/dist/utils/renderer-registry.d.ts +6 -0
- package/dist/utils/renderer-registry.js +1 -1
- package/dist/utils/ttf-parser.js +3 -2
- package/package.json +1 -1
|
@@ -29,8 +29,8 @@ export function splitLongWord(word, room, font, metrics, letterSpacing, options)
|
|
|
29
29
|
// the blunt one - which is what an e-mail address or an IBAN needs, having no valid point at all.
|
|
30
30
|
const head = (hyphenate ? longestHyphenatedPrefix(rest, room, width, hyphenate) : null) ??
|
|
31
31
|
(breakWord ? longestPrefix(rest, room, width) : null);
|
|
32
|
-
//
|
|
33
|
-
//
|
|
32
|
+
// Hyphenation found nothing and break-word is off, or one character is all that is left: leave the
|
|
33
|
+
// remainder whole and let it overflow.
|
|
34
34
|
if (head === null)
|
|
35
35
|
break;
|
|
36
36
|
// A hyphenator that returns text other than the word is not splitting it: leave it whole.
|
|
@@ -90,9 +90,15 @@ function longestPrefix(word, room, width) {
|
|
|
90
90
|
taken += char;
|
|
91
91
|
count += 1;
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
// looping on it would never terminate.
|
|
95
|
-
if (count === 0 || count === chars.length)
|
|
93
|
+
if (count === chars.length)
|
|
96
94
|
return null;
|
|
95
|
+
// Not even one character fits - the box is narrower than a glyph: one character per line, as CSS
|
|
96
|
+
// `overflow-wrap` sets it. Each pass still takes one, so the loop ends.
|
|
97
|
+
if (count === 0) {
|
|
98
|
+
if (chars.length === 1)
|
|
99
|
+
return null;
|
|
100
|
+
taken = chars[0];
|
|
101
|
+
count = 1;
|
|
102
|
+
}
|
|
97
103
|
return { text: taken, taken, rest: chars.slice(count).join(""), hyphen: false };
|
|
98
104
|
}
|
|
@@ -29,7 +29,8 @@ export interface FlexAxis {
|
|
|
29
29
|
* unbounded main axis and shrink-wraps as before.
|
|
30
30
|
*/
|
|
31
31
|
measureConstraints(crossAvail: number, mainMax?: number): BoxConstraints;
|
|
32
|
-
/** Constraints for a flex child (fills
|
|
32
|
+
/** Constraints for a flex child: its main extent is SET (it fills its share, whatever size it asked
|
|
33
|
+
* for), across it is an ordinary child. */
|
|
33
34
|
flexConstraints(mainExtent: number, crossAvail: number): BoxConstraints;
|
|
34
35
|
/** Offset (from the stack's top-left) of a child at main position `mainPos`, cross `crossPos`. */
|
|
35
36
|
offsetAt(mainPos: number, crossPos: number): Offset;
|
|
@@ -58,6 +59,7 @@ export declare class FlexLayoutHelper {
|
|
|
58
59
|
static layout(children: PDFElement[], axis: FlexAxis, mainAvail: number, crossAvail: number, options: FlexOptions, ctx: LayoutContext, mode?: FlexMode): {
|
|
59
60
|
mainUsed: number;
|
|
60
61
|
crossUsed: number;
|
|
62
|
+
widestLine?: number;
|
|
61
63
|
};
|
|
62
64
|
/**
|
|
63
65
|
* The wrapping path: split into lines, lay each one out, then place the lines across the axis.
|
|
@@ -9,7 +9,7 @@ export const VERTICAL_AXIS = {
|
|
|
9
9
|
mainOf: (s) => s.height,
|
|
10
10
|
crossOf: (s) => s.width,
|
|
11
11
|
measureConstraints: (crossAvail, mainMax = Infinity) => BoxConstraints.loose(crossAvail, mainMax),
|
|
12
|
-
flexConstraints: (mainExtent, crossAvail) => BoxConstraints
|
|
12
|
+
flexConstraints: (mainExtent, crossAvail) => new BoxConstraints(0, crossAvail, mainExtent, mainExtent),
|
|
13
13
|
offsetAt: (mainPos, crossPos) => ({ x: crossPos, y: mainPos }),
|
|
14
14
|
};
|
|
15
15
|
export const HORIZONTAL_AXIS = {
|
|
@@ -17,7 +17,7 @@ export const HORIZONTAL_AXIS = {
|
|
|
17
17
|
mainOf: (s) => s.width,
|
|
18
18
|
crossOf: (s) => s.height,
|
|
19
19
|
measureConstraints: (crossAvail, mainMax = Infinity) => BoxConstraints.loose(mainMax, crossAvail),
|
|
20
|
-
flexConstraints: (mainExtent, crossAvail) => BoxConstraints
|
|
20
|
+
flexConstraints: (mainExtent, crossAvail) => new BoxConstraints(mainExtent, mainExtent, 0, crossAvail),
|
|
21
21
|
offsetAt: (mainPos, crossPos) => ({ x: mainPos, y: crossPos }),
|
|
22
22
|
};
|
|
23
23
|
/** Cross-axis offset of a child of size `childCross` within `crossExtent`. */
|
|
@@ -123,12 +123,24 @@ export class FlexLayoutHelper {
|
|
|
123
123
|
// Each line keeps SOURCE order internally: the ordering was applied above, and applying it again
|
|
124
124
|
// per line would reverse twice.
|
|
125
125
|
const lineOptions = { ...options, wrap: false, reverse: false, main: mainAlignOf(options) };
|
|
126
|
-
const
|
|
127
|
-
// Across a Column a child gets the column's width, as a block does in CSS (a paragraph wraps
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
const
|
|
126
|
+
const measureAt = (line, cross) => FlexLayoutHelper.layoutLine(line, axis, mainAvail, cross, 0, lineOptions, ctx, "measure", true);
|
|
127
|
+
// Across a Column a child gets the column's width, as a block does in CSS (a paragraph wraps inside
|
|
128
|
+
// it); across a Row it is as tall as its content. Either way the line is only as big as what is on
|
|
129
|
+
// it, so `stretch` fills the line and not the container. A region-sized child (a Flow) has no
|
|
130
|
+
// content height: its line shares what the other lines leave of the Row's height.
|
|
131
|
+
const regionLine = (line) => axis.mainHorizontal && line.some((c) => c.needsRegion());
|
|
132
|
+
const measure = lines.map((line) => regionLine(line) ? null : measureAt(line, axis.mainHorizontal ? Infinity : crossAvail));
|
|
133
|
+
const regionLines = lines.filter(regionLine).length;
|
|
134
|
+
if (regionLines > 0) {
|
|
135
|
+
const taken = measure.reduce((n, m) => n + (m?.crossUsed ?? 0), 0) + Math.max(0, lines.length - 1) * gap;
|
|
136
|
+
const share = Math.max(0, crossAvail - taken) / regionLines;
|
|
137
|
+
lines.forEach((line, i) => {
|
|
138
|
+
if (regionLine(line))
|
|
139
|
+
measure[i] = measureAt(line, share);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const sized = measure;
|
|
143
|
+
const heights = sized.map((m) => m.crossUsed);
|
|
132
144
|
const totalCross = heights.reduce((n, h) => n + h, 0) + Math.max(0, lines.length - 1) * gap;
|
|
133
145
|
// `alignContent` distributes the BLOCK of lines across the axis, using the same vocabulary the main
|
|
134
146
|
// axis uses. Only meaningful in a bounded cross axis with room left over.
|
|
@@ -156,7 +168,12 @@ export class FlexLayoutHelper {
|
|
|
156
168
|
if (i < lines.length - 1)
|
|
157
169
|
cursor += between;
|
|
158
170
|
});
|
|
159
|
-
return {
|
|
171
|
+
return {
|
|
172
|
+
mainUsed: mainAvail,
|
|
173
|
+
crossUsed: Math.max(totalCross, cursor),
|
|
174
|
+
// A child wider than the line runs past it, in a line of its own.
|
|
175
|
+
widestLine: Math.max(...sized.map((m) => m.mainUsed)),
|
|
176
|
+
};
|
|
160
177
|
}
|
|
161
178
|
/**
|
|
162
179
|
* Lays out ONE flex line along `axis` and places every child. Fixed children take their natural main
|
|
@@ -193,6 +210,9 @@ export class FlexLayoutHelper {
|
|
|
193
210
|
shrunkCap: new Map(),
|
|
194
211
|
sharedCap: new Map(),
|
|
195
212
|
shrunkBasis: new Map(),
|
|
213
|
+
flexFloor: new Map(),
|
|
214
|
+
flexCeil: new Map(),
|
|
215
|
+
flexExtent: new Map(),
|
|
196
216
|
};
|
|
197
217
|
measureLine(line);
|
|
198
218
|
shrinkLine(line);
|
|
@@ -203,10 +223,11 @@ export class FlexLayoutHelper {
|
|
|
203
223
|
const remaining = Number.isFinite(leftover) ? Math.max(leftover, 0) : 0;
|
|
204
224
|
const weight = shareLeftover(line, remaining);
|
|
205
225
|
const spacing = mainSpacing(mainAlignOf(options), gap, ordered.length, mainAvail, leftover, weight);
|
|
206
|
-
|
|
226
|
+
shareFlex(line, weight, remaining);
|
|
227
|
+
measureFlex(line, options.cross ?? "stretch");
|
|
207
228
|
// The cross extent children align within: the bounded line size, else the tallest child.
|
|
208
229
|
const crossExtent = crossAvail !== Infinity && !crossFromContent ? crossAvail : line.crossUsed;
|
|
209
|
-
return placeLine(line, options.cross ?? "stretch", crossExtent, crossOrigin, spacing,
|
|
230
|
+
return placeLine(line, options.cross ?? "stretch", crossExtent, crossOrigin, spacing, mode);
|
|
210
231
|
}
|
|
211
232
|
}
|
|
212
233
|
/** A flex child's basis on this line: what it asked for, or less once the line has shrunk it. */
|
|
@@ -253,6 +274,17 @@ function measureLine(line) {
|
|
|
253
274
|
if (flexible) {
|
|
254
275
|
line.totalFlex += flexible.getFlex();
|
|
255
276
|
line.totalBasis += flexible.getBasis(line.percentBase);
|
|
277
|
+
if (Number.isFinite(line.mainAvail)) {
|
|
278
|
+
// Down a column, a flex child keeps its content's height (CSS `min-height: auto`): squeezed
|
|
279
|
+
// below it, it would draw over the sibling that follows.
|
|
280
|
+
const content = axis.mainHorizontal
|
|
281
|
+
? 0
|
|
282
|
+
: axis.mainOf(child.measure(axis.measureConstraints(line.crossAvail, Infinity), line.ctx));
|
|
283
|
+
const floor = Math.max(0, child.minIntrinsicMain(axis.mainHorizontal, line.ctx), content);
|
|
284
|
+
line.flexFloor.set(child, floor);
|
|
285
|
+
// A max below the floor loses, as in CSS.
|
|
286
|
+
line.flexCeil.set(child, Math.max(floor, child.maxExtent(axis.mainHorizontal)));
|
|
287
|
+
}
|
|
256
288
|
continue;
|
|
257
289
|
}
|
|
258
290
|
const size = measureInto(line, child, axis.measureConstraints(line.crossAvail, mainCapFor(line, child)));
|
|
@@ -263,12 +295,17 @@ function measureLine(line) {
|
|
|
263
295
|
/**
|
|
264
296
|
* Step 2: the line overflows and some children said they would give space back. CSS weights a shrinker's
|
|
265
297
|
* 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
|
|
267
|
-
* the first
|
|
298
|
+
* size is its basis, so a Spacer (basis 0) gives nothing. A flex child counts at least at its floor, so
|
|
299
|
+
* its siblings make room for what it holds. A document that never overflows never gets past the first
|
|
300
|
+
* test.
|
|
268
301
|
*/
|
|
269
302
|
function shrinkLine(line) {
|
|
270
303
|
const { axis } = line;
|
|
271
|
-
|
|
304
|
+
let reserved = 0;
|
|
305
|
+
for (const [child, floor] of line.flexFloor) {
|
|
306
|
+
reserved += Math.max(0, floor - flexibleOf(child).getBasis(line.percentBase));
|
|
307
|
+
}
|
|
308
|
+
const overflow = line.fixedMain + line.totalBasis + reserved + line.totalGap - line.mainAvail;
|
|
272
309
|
if (!Number.isFinite(line.mainAvail) || !(overflow > 0))
|
|
273
310
|
return;
|
|
274
311
|
const shrinkers = line.children.filter((c) => {
|
|
@@ -382,30 +419,100 @@ function mainSpacing(main, gap, count, mainAvail, leftover, weight) {
|
|
|
382
419
|
return { leading: 0, between: gap };
|
|
383
420
|
}
|
|
384
421
|
/**
|
|
385
|
-
* Step 5:
|
|
386
|
-
*
|
|
422
|
+
* Step 5: each flex child's main extent - its basis plus its share of the leftover, held between its
|
|
423
|
+
* floor and its ceiling. CSS resolves that iteratively (Flexbox 9.7): clamp everyone, and by the sign of
|
|
424
|
+
* the total change freeze the ones held at their floor (it grew) or at their ceiling (it shrank); the
|
|
425
|
+
* rest share what is left again. Nothing clamped: the plain share.
|
|
387
426
|
*/
|
|
388
|
-
function
|
|
427
|
+
function shareFlex(line, weight, remaining) {
|
|
428
|
+
const items = [];
|
|
389
429
|
for (const child of line.children) {
|
|
390
430
|
const flexible = flexibleOf(child);
|
|
391
|
-
if (
|
|
392
|
-
|
|
431
|
+
if (flexible)
|
|
432
|
+
items.push([child, flexible]);
|
|
433
|
+
}
|
|
434
|
+
// `totalFlex` is 0 when every flex child asked for `flex: 0` (a pure `flexBasis` slot). The share is
|
|
435
|
+
// then 0/0 - a NaN that becomes the offset of every later sibling. Such a child takes its basis.
|
|
436
|
+
const extentAt = (child, flexible, part, of) => basisOf(line, child, flexible) + (of > 0 ? (flexible.getFlex() / of) * part : 0);
|
|
437
|
+
for (const [child, flexible] of items) {
|
|
438
|
+
line.flexExtent.set(child, extentAt(child, flexible, remaining, weight));
|
|
439
|
+
}
|
|
440
|
+
// The flex children's part of the leftover; a Spacer stack's share (`shareLeftover`) stays its own.
|
|
441
|
+
let pot = weight > 0 ? (remaining * line.totalFlex) / weight : 0;
|
|
442
|
+
let active = items;
|
|
443
|
+
for (;;) {
|
|
444
|
+
let change = 0;
|
|
445
|
+
const clamped = new Map();
|
|
446
|
+
for (const [child] of active) {
|
|
447
|
+
const extent = line.flexExtent.get(child);
|
|
448
|
+
const held = Math.min(Math.max(extent, line.flexFloor.get(child) ?? 0), line.flexCeil.get(child) ?? Infinity);
|
|
449
|
+
if (held !== extent)
|
|
450
|
+
clamped.set(child, held);
|
|
451
|
+
change += held - extent;
|
|
452
|
+
}
|
|
453
|
+
if (clamped.size === 0)
|
|
454
|
+
return;
|
|
455
|
+
const froze = active.filter(([child]) => {
|
|
456
|
+
const held = clamped.get(child);
|
|
457
|
+
return (held !== undefined &&
|
|
458
|
+
(change > 0
|
|
459
|
+
? held > line.flexExtent.get(child)
|
|
460
|
+
: change < 0
|
|
461
|
+
? held < line.flexExtent.get(child)
|
|
462
|
+
: true));
|
|
463
|
+
});
|
|
464
|
+
for (const [child, flexible] of froze) {
|
|
465
|
+
const held = clamped.get(child);
|
|
466
|
+
pot -= held - basisOf(line, child, flexible);
|
|
467
|
+
line.flexExtent.set(child, held);
|
|
468
|
+
}
|
|
469
|
+
active = active.filter((item) => !froze.includes(item));
|
|
470
|
+
const activeFlex = active.reduce((n, [, flexible]) => n + flexible.getFlex(), 0);
|
|
471
|
+
for (const [child, flexible] of active) {
|
|
472
|
+
line.flexExtent.set(child, extentAt(child, flexible, Math.max(0, pot), activeFlex));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Step 6: measure the flex children at their extent, so a tall or wrapping flex cell counts toward the
|
|
478
|
+
* line's cross extent before anything is placed.
|
|
479
|
+
*/
|
|
480
|
+
function measureFlex(line, cross) {
|
|
481
|
+
for (const [child, mainExtent] of line.flexExtent) {
|
|
393
482
|
// A pure `flexBasis` slot (`flex: 0`) has no share but still has a size across the line.
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
483
|
+
const crossMax = (alignSelfOf(child) ?? cross) === "stretch"
|
|
484
|
+
? line.crossAvail
|
|
485
|
+
: ownCross(line, child, mainExtent);
|
|
486
|
+
const size = measureInto(line, child, line.axis.flexConstraints(mainExtent, crossMax));
|
|
397
487
|
line.crossUsed = Math.max(line.crossUsed, line.axis.crossOf(size));
|
|
398
488
|
}
|
|
399
489
|
}
|
|
400
490
|
/**
|
|
401
|
-
*
|
|
491
|
+
* Across the line, a flex child that is not stretched takes what its content needs and is aligned like
|
|
492
|
+
* any other item. Its content is measured against the line's cross bound - an unbounded one would stop a
|
|
493
|
+
* text in a Column from wrapping.
|
|
494
|
+
*/
|
|
495
|
+
function ownCross(line, child, mainExtent) {
|
|
496
|
+
const flexible = flexibleOf(child);
|
|
497
|
+
const content = flexible.layoutChildren()[0];
|
|
498
|
+
if (!content)
|
|
499
|
+
return 0;
|
|
500
|
+
// In the context it is laid out in: a wrapper on the way to the `Expanded` may hand one down.
|
|
501
|
+
let ctx = line.ctx;
|
|
502
|
+
for (let el = child; el !== flexible; el = el.proxied())
|
|
503
|
+
ctx = el.proxiedContext(ctx);
|
|
504
|
+
const constraints = line.axis.flexConstraints(mainExtent, line.crossAvail);
|
|
505
|
+
return line.axis.crossOf(content.measure(constraints, ctx));
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Step 7: place each child at the running main position, offset across by its alignment. This is the
|
|
402
509
|
* ONE layout a child gets. Measuring, it only sizes - and reuses a size already measured under the same
|
|
403
510
|
* constraints, or the measuring itself would double at every level.
|
|
404
511
|
*
|
|
405
512
|
* `stretch` caps a child's cross to `crossExtent`, not `crossAvail`, so siblings end up equal across the
|
|
406
513
|
* axis. A child may override the container with its own `alignSelf` (CSS).
|
|
407
514
|
*/
|
|
408
|
-
function placeLine(line, cross, crossExtent, crossOrigin, spacing,
|
|
515
|
+
function placeLine(line, cross, crossExtent, crossOrigin, spacing, mode) {
|
|
409
516
|
const { axis, ctx } = line;
|
|
410
517
|
const place = (child, constraints, offset) => {
|
|
411
518
|
if (mode === "layout")
|
|
@@ -421,21 +528,22 @@ function placeLine(line, cross, crossExtent, crossOrigin, spacing, weight, remai
|
|
|
421
528
|
line.children.forEach((child, index) => {
|
|
422
529
|
let mainExtent;
|
|
423
530
|
const flexible = flexibleOf(child);
|
|
531
|
+
// A region-sized child (a Flow) takes the height the Row was given, never one its siblings make.
|
|
532
|
+
const regionSized = axis.mainHorizontal && child.needsRegion();
|
|
424
533
|
if (flexible) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const size = place(child, axis.flexConstraints(mainExtent, cross === "stretch" ? crossExtent : line.crossAvail), axis.offsetAt(mainPos, crossOrigin));
|
|
534
|
+
mainExtent = line.flexExtent.get(child);
|
|
535
|
+
const align = alignSelfOf(child) ?? cross;
|
|
536
|
+
// Stretched, it is capped at the line; otherwise it may take what the line offers, and is
|
|
537
|
+
// aligned by what it takes.
|
|
538
|
+
const cap = align === "stretch" && !regionSized ? crossExtent : line.crossAvail;
|
|
539
|
+
const own = align === "stretch" || regionSized ? cap : ownCross(line, child, mainExtent);
|
|
540
|
+
const size = place(child, axis.flexConstraints(mainExtent, cap), axis.offsetAt(mainPos, crossOrigin + crossOffset(align, crossExtent, own)));
|
|
433
541
|
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
434
542
|
}
|
|
435
543
|
else {
|
|
436
544
|
const align = alignSelfOf(child) ?? cross;
|
|
437
545
|
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)));
|
|
546
|
+
const size = place(child, axis.measureConstraints(align === "stretch" && !regionSized ? crossExtent : line.crossAvail, mainCapFor(line, child)), axis.offsetAt(mainPos, crossOrigin + crossOffset(align, crossExtent, childCross)));
|
|
439
547
|
mainExtent = axis.mainOf(size);
|
|
440
548
|
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
441
549
|
}
|
|
@@ -3,11 +3,56 @@ declare global {
|
|
|
3
3
|
getUint24(byteOffset: number, littleEndian?: boolean): number;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/** Dots per inch, per axis. */
|
|
7
|
+
export interface Resolution {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ImageDimensions {
|
|
12
|
+
/** In pixels - what the XObject declares. */
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
/** The resolution the file declares; absent when it declares none. */
|
|
16
|
+
dpi?: Resolution;
|
|
17
|
+
/** How the stored pixels turn upright (EXIF, a JPEG only); absent when they already are. */
|
|
18
|
+
orientation?: Orientation;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* EXIF orientation: 2 mirrored, 3 turned half, 4 flipped, 5 transposed, 6 turned a quarter clockwise,
|
|
22
|
+
* 7 transversed, 8 turned a quarter counterclockwise. 5-8 swap width and height.
|
|
23
|
+
*/
|
|
24
|
+
export type Orientation = 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
25
|
+
/**
|
|
26
|
+
* The matrix that turns the image's unit square upright, inside it - PDF's image space, y up, the first
|
|
27
|
+
* stored row at the top. Applied before the placement scales it, so the box stays the box.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ORIENTATION_MATRIX: Record<Orientation, readonly number[]>;
|
|
30
|
+
/** The resolution of a file that declares none: CSS's reference pixel, 1/96 inch. */
|
|
31
|
+
export declare const DEFAULT_DPI = 96;
|
|
32
|
+
/**
|
|
33
|
+
* An image's own size in points, upright: its pixels at the resolution the file declares, else 96 dpi,
|
|
34
|
+
* turned by its orientation.
|
|
35
|
+
*/
|
|
36
|
+
export declare function naturalSize(dims: ImageDimensions): {
|
|
7
37
|
width: number;
|
|
8
38
|
height: number;
|
|
39
|
+
};
|
|
40
|
+
/** Thrown for a JPEG we cannot embed as it is. Says what it is and what to do. */
|
|
41
|
+
export declare class JpegFormatError extends Error {
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
/** The colors an image's samples are in. */
|
|
45
|
+
export type ImageColorSpace = "gray" | "rgb" | "cmyk";
|
|
46
|
+
/** What an image's samples mean: their color space, and whether they are stored inverted. */
|
|
47
|
+
export interface ImageColor {
|
|
48
|
+
space: ImageColorSpace;
|
|
49
|
+
/** Photoshop stores CMYK inverted (an Adobe `APP14` segment). */
|
|
50
|
+
inverted?: boolean;
|
|
9
51
|
}
|
|
10
|
-
|
|
52
|
+
/** The color of a JPEG as it is embedded (`DCTDecode`): the space it was saved in. */
|
|
53
|
+
export declare function jpegColor(bytes: Uint8Array): ImageColor;
|
|
54
|
+
/** Read from the header alone, synchronously - layout can ask it. */
|
|
55
|
+
export declare function readImageDimensions(buffer: Uint8Array): ImageDimensions;
|
|
11
56
|
/**
|
|
12
57
|
* Converts the given image to grayscale and returns its binary data.
|
|
13
58
|
* @param imagePath Path to the input image file.
|
|
@@ -26,19 +71,22 @@ export declare function decodePngToRgbFlate(pngBuffer: Uint8Array): Promise<{
|
|
|
26
71
|
height: number;
|
|
27
72
|
}>;
|
|
28
73
|
export declare function convertImageToGrayscaleBuffer(imagePath: string): Promise<Uint8Array>;
|
|
74
|
+
/** The drawn size of an image in its box, and where it sits there. */
|
|
29
75
|
export interface FitResult {
|
|
30
76
|
width: number;
|
|
31
77
|
height: number;
|
|
32
78
|
offsetX: number;
|
|
33
79
|
offsetY: number;
|
|
34
80
|
}
|
|
35
|
-
|
|
36
|
-
export
|
|
81
|
+
/** Where an image smaller or larger than its box sits in it, per axis: 0 = start, 0.5 = center, 1 = end. */
|
|
82
|
+
export interface FitAlign {
|
|
83
|
+
x: number;
|
|
84
|
+
y: number;
|
|
85
|
+
}
|
|
86
|
+
/** Scaled to fit inside the box, whole image visible. */
|
|
87
|
+
export declare function applyContainFit(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|
|
88
|
+
/** Scaled to cover the box, the overhang cut off. */
|
|
89
|
+
export declare function applyCoverFit(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|
|
37
90
|
export declare function applyFillFit(containerWidth: number, containerHeight: number): FitResult;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
height: number;
|
|
41
|
-
offsetX: number;
|
|
42
|
-
offsetY: number;
|
|
43
|
-
};
|
|
44
|
-
export {};
|
|
91
|
+
/** At its own size (`naturalSize`), the overhang cut off. */
|
|
92
|
+
export declare function applyFitNone(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|