@effing/canvas 0.22.3 → 0.23.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/README.md +21 -18
- package/dist/index.js +328 -224
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -192,24 +192,27 @@ const png = canvas.encodeSync("png");
|
|
|
192
192
|
|
|
193
193
|
### Typography
|
|
194
194
|
|
|
195
|
-
| Property
|
|
196
|
-
|
|
|
197
|
-
| `fontFamily`
|
|
198
|
-
| `fontSize`
|
|
199
|
-
| `fontWeight`
|
|
200
|
-
| `fontStyle`
|
|
201
|
-
| `textAlign`
|
|
202
|
-
| `textDecoration`
|
|
203
|
-
| `textTransform`
|
|
204
|
-
| `lineHeight`
|
|
205
|
-
| `letterSpacing`
|
|
206
|
-
| `whiteSpace`
|
|
207
|
-
| `wordBreak`
|
|
208
|
-
| `textOverflow`
|
|
209
|
-
| `lineClamp`
|
|
210
|
-
| `textBox`
|
|
211
|
-
| `textBoxTrim`
|
|
212
|
-
| `textBoxEdge`
|
|
195
|
+
| Property | Values / Notes |
|
|
196
|
+
| ----------------------- | ------------------------------------------------------------- |
|
|
197
|
+
| `fontFamily` | Font name (inherited) |
|
|
198
|
+
| `fontSize` | Number or CSS length (inherited) |
|
|
199
|
+
| `fontWeight` | Numeric weight (inherited) |
|
|
200
|
+
| `fontStyle` | `normal`, `italic` (inherited) |
|
|
201
|
+
| `textAlign` | `left`, `center`, `right`, `justify` (inherited) |
|
|
202
|
+
| `textDecoration` | String (inherited) |
|
|
203
|
+
| `textTransform` | `none`, `uppercase`, `lowercase`, `capitalize` (inherited) |
|
|
204
|
+
| `lineHeight` | Number or string (inherited) |
|
|
205
|
+
| `letterSpacing` | Number or CSS length (inherited) |
|
|
206
|
+
| `whiteSpace` | `normal`, `nowrap`, `pre`, `pre-wrap`, `pre-line` (inherited) |
|
|
207
|
+
| `wordBreak` | `normal`, `break-all`, `break-word`, `keep-all` (inherited) |
|
|
208
|
+
| `textOverflow` | `clip`, `ellipsis` (inherited) |
|
|
209
|
+
| `lineClamp` | Number — max visible lines (adds ellipsis) |
|
|
210
|
+
| `textBox` | Shorthand for `textBoxTrim` and `textBoxEdge` |
|
|
211
|
+
| `textBoxTrim` | `none`, `trim-start`, `trim-end`, `trim-both` (inherited) |
|
|
212
|
+
| `textBoxEdge` | e.g. `"cap alphabetic"` (inherited) |
|
|
213
|
+
| `WebkitTextStroke` | Shorthand, e.g. `"2px red"` (inherited) |
|
|
214
|
+
| `WebkitTextStrokeWidth` | CSS length (inherited) |
|
|
215
|
+
| `WebkitTextStrokeColor` | Any CSS color (inherited) |
|
|
213
216
|
|
|
214
217
|
### Effects
|
|
215
218
|
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,229 @@ function renderLottieFrame(ctx, animation, frame) {
|
|
|
23
23
|
// src/jsx/draw/index.ts
|
|
24
24
|
import { loadImage as loadImage3 } from "@napi-rs/canvas";
|
|
25
25
|
|
|
26
|
+
// src/jsx/style/compute.ts
|
|
27
|
+
var ROOT_FONT_SIZE = 16;
|
|
28
|
+
var DEFAULT_STYLE = {
|
|
29
|
+
display: "flex",
|
|
30
|
+
flexDirection: "row",
|
|
31
|
+
flexWrap: "nowrap",
|
|
32
|
+
flexGrow: 0,
|
|
33
|
+
flexShrink: 0,
|
|
34
|
+
alignItems: "stretch",
|
|
35
|
+
justifyContent: "flex-start",
|
|
36
|
+
position: "relative",
|
|
37
|
+
fontSize: ROOT_FONT_SIZE,
|
|
38
|
+
fontWeight: 400,
|
|
39
|
+
fontStyle: "normal",
|
|
40
|
+
color: "black",
|
|
41
|
+
lineHeight: "normal",
|
|
42
|
+
textAlign: "left",
|
|
43
|
+
whiteSpace: "normal",
|
|
44
|
+
wordBreak: "normal",
|
|
45
|
+
textOverflow: "clip",
|
|
46
|
+
opacity: 1,
|
|
47
|
+
overflow: "visible"
|
|
48
|
+
};
|
|
49
|
+
var INHERITABLE_PROPS = [
|
|
50
|
+
"color",
|
|
51
|
+
"fontSize",
|
|
52
|
+
"fontFamily",
|
|
53
|
+
"fontWeight",
|
|
54
|
+
"fontStyle",
|
|
55
|
+
"textAlign",
|
|
56
|
+
"textTransform",
|
|
57
|
+
"textDecoration",
|
|
58
|
+
"lineHeight",
|
|
59
|
+
"letterSpacing",
|
|
60
|
+
"whiteSpace",
|
|
61
|
+
"wordBreak",
|
|
62
|
+
"textOverflow",
|
|
63
|
+
"lineClamp",
|
|
64
|
+
"textBoxTrim",
|
|
65
|
+
"textBoxEdge",
|
|
66
|
+
"WebkitTextStrokeWidth",
|
|
67
|
+
"WebkitTextStrokeColor"
|
|
68
|
+
];
|
|
69
|
+
function resolveStyle(rawStyle, parentStyle) {
|
|
70
|
+
const style = { ...rawStyle };
|
|
71
|
+
for (const prop of INHERITABLE_PROPS) {
|
|
72
|
+
if (style[prop] === void 0 && parentStyle[prop] !== void 0) {
|
|
73
|
+
style[prop] = parentStyle[prop];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (typeof style.fontSize === "string") {
|
|
77
|
+
const parentFontSize = typeof parentStyle.fontSize === "number" ? parentStyle.fontSize : ROOT_FONT_SIZE;
|
|
78
|
+
const resolved = resolveUnit(
|
|
79
|
+
style.fontSize,
|
|
80
|
+
0,
|
|
81
|
+
0,
|
|
82
|
+
parentFontSize,
|
|
83
|
+
ROOT_FONT_SIZE
|
|
84
|
+
);
|
|
85
|
+
style.fontSize = typeof resolved === "number" ? resolved : parentFontSize;
|
|
86
|
+
}
|
|
87
|
+
if (typeof style.lineHeight === "string") {
|
|
88
|
+
const str = style.lineHeight;
|
|
89
|
+
if (str.endsWith("%")) {
|
|
90
|
+
style.lineHeight = parseFloat(str) / 100;
|
|
91
|
+
} else {
|
|
92
|
+
const parsed = parseFloat(str);
|
|
93
|
+
if (!isNaN(parsed)) {
|
|
94
|
+
style.lineHeight = parsed;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (typeof style.letterSpacing === "string") {
|
|
99
|
+
const currentFontSize = typeof style.fontSize === "number" ? style.fontSize : typeof parentStyle.fontSize === "number" ? parentStyle.fontSize : ROOT_FONT_SIZE;
|
|
100
|
+
const resolved = resolveUnit(
|
|
101
|
+
style.letterSpacing,
|
|
102
|
+
0,
|
|
103
|
+
0,
|
|
104
|
+
currentFontSize,
|
|
105
|
+
ROOT_FONT_SIZE
|
|
106
|
+
);
|
|
107
|
+
if (typeof resolved === "number") {
|
|
108
|
+
style.letterSpacing = resolved;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return style;
|
|
112
|
+
}
|
|
113
|
+
var DIMENSION_PROPS = [
|
|
114
|
+
"width",
|
|
115
|
+
"height",
|
|
116
|
+
"minWidth",
|
|
117
|
+
"minHeight",
|
|
118
|
+
"maxWidth",
|
|
119
|
+
"maxHeight",
|
|
120
|
+
"top",
|
|
121
|
+
"right",
|
|
122
|
+
"bottom",
|
|
123
|
+
"left",
|
|
124
|
+
"marginTop",
|
|
125
|
+
"marginRight",
|
|
126
|
+
"marginBottom",
|
|
127
|
+
"marginLeft",
|
|
128
|
+
"paddingTop",
|
|
129
|
+
"paddingRight",
|
|
130
|
+
"paddingBottom",
|
|
131
|
+
"paddingLeft",
|
|
132
|
+
"rowGap",
|
|
133
|
+
"columnGap",
|
|
134
|
+
"flexBasis",
|
|
135
|
+
"borderTopWidth",
|
|
136
|
+
"borderRightWidth",
|
|
137
|
+
"borderBottomWidth",
|
|
138
|
+
"borderLeftWidth",
|
|
139
|
+
"borderTopLeftRadius",
|
|
140
|
+
"borderTopRightRadius",
|
|
141
|
+
"borderBottomRightRadius",
|
|
142
|
+
"borderBottomLeftRadius",
|
|
143
|
+
"WebkitTextStrokeWidth"
|
|
144
|
+
];
|
|
145
|
+
function resolveUnit(value, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
146
|
+
if (value.endsWith("%") || value === "auto") return value;
|
|
147
|
+
if (value.endsWith("vmin")) {
|
|
148
|
+
const n = parseFloat(value);
|
|
149
|
+
return isNaN(n) ? value : n / 100 * Math.min(viewportWidth, viewportHeight);
|
|
150
|
+
}
|
|
151
|
+
if (value.endsWith("vmax")) {
|
|
152
|
+
const n = parseFloat(value);
|
|
153
|
+
return isNaN(n) ? value : n / 100 * Math.max(viewportWidth, viewportHeight);
|
|
154
|
+
}
|
|
155
|
+
if (value.endsWith("vw")) {
|
|
156
|
+
const n = parseFloat(value);
|
|
157
|
+
return isNaN(n) ? value : n / 100 * viewportWidth;
|
|
158
|
+
}
|
|
159
|
+
if (value.endsWith("vh")) {
|
|
160
|
+
const n = parseFloat(value);
|
|
161
|
+
return isNaN(n) ? value : n / 100 * viewportHeight;
|
|
162
|
+
}
|
|
163
|
+
if (value.endsWith("rem")) {
|
|
164
|
+
const n = parseFloat(value);
|
|
165
|
+
return isNaN(n) ? value : n * rootFontSize;
|
|
166
|
+
}
|
|
167
|
+
if (value.endsWith("em")) {
|
|
168
|
+
const n = parseFloat(value);
|
|
169
|
+
return isNaN(n) ? value : n * fontSize;
|
|
170
|
+
}
|
|
171
|
+
if (value.endsWith("px")) {
|
|
172
|
+
const n = parseFloat(value);
|
|
173
|
+
return isNaN(n) ? value : n;
|
|
174
|
+
}
|
|
175
|
+
if (value.endsWith("pt")) {
|
|
176
|
+
const n = parseFloat(value);
|
|
177
|
+
return isNaN(n) ? value : n * (96 / 72);
|
|
178
|
+
}
|
|
179
|
+
if (value.endsWith("pc")) {
|
|
180
|
+
const n = parseFloat(value);
|
|
181
|
+
return isNaN(n) ? value : n * 16;
|
|
182
|
+
}
|
|
183
|
+
if (value.endsWith("in")) {
|
|
184
|
+
const n = parseFloat(value);
|
|
185
|
+
return isNaN(n) ? value : n * 96;
|
|
186
|
+
}
|
|
187
|
+
if (value.endsWith("cm")) {
|
|
188
|
+
const n = parseFloat(value);
|
|
189
|
+
return isNaN(n) ? value : n * (96 / 2.54);
|
|
190
|
+
}
|
|
191
|
+
if (value.endsWith("mm")) {
|
|
192
|
+
const n = parseFloat(value);
|
|
193
|
+
return isNaN(n) ? value : n * (96 / 25.4);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
function resolveUnits(style, viewportWidth, viewportHeight, rootFontSize = ROOT_FONT_SIZE) {
|
|
198
|
+
const fontSize = typeof style.fontSize === "number" ? style.fontSize : rootFontSize;
|
|
199
|
+
for (const prop of DIMENSION_PROPS) {
|
|
200
|
+
const value = style[prop];
|
|
201
|
+
if (typeof value !== "string") continue;
|
|
202
|
+
const resolved = resolveUnit(
|
|
203
|
+
value,
|
|
204
|
+
viewportWidth,
|
|
205
|
+
viewportHeight,
|
|
206
|
+
fontSize,
|
|
207
|
+
rootFontSize
|
|
208
|
+
);
|
|
209
|
+
if (resolved !== value) {
|
|
210
|
+
style[prop] = resolved;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (style.transform) {
|
|
214
|
+
style.transform = resolveTransformUnits(
|
|
215
|
+
style.transform,
|
|
216
|
+
viewportWidth,
|
|
217
|
+
viewportHeight,
|
|
218
|
+
fontSize,
|
|
219
|
+
rootFontSize
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (style.transformOrigin) {
|
|
223
|
+
style.transformOrigin = resolveTransformUnits(
|
|
224
|
+
style.transformOrigin,
|
|
225
|
+
viewportWidth,
|
|
226
|
+
viewportHeight,
|
|
227
|
+
fontSize,
|
|
228
|
+
rootFontSize
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return style;
|
|
232
|
+
}
|
|
233
|
+
function resolveTransformUnits(transform, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
234
|
+
return transform.replace(
|
|
235
|
+
/(-?\d*\.?\d+)(vw|vh|vmin|vmax|em|rem|px|pt|pc|in|cm|mm)\b/g,
|
|
236
|
+
(match) => {
|
|
237
|
+
const resolved = resolveUnit(
|
|
238
|
+
match,
|
|
239
|
+
viewportWidth,
|
|
240
|
+
viewportHeight,
|
|
241
|
+
fontSize,
|
|
242
|
+
rootFontSize
|
|
243
|
+
);
|
|
244
|
+
return typeof resolved === "number" ? String(resolved) : match;
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
26
249
|
// src/jsx/font.ts
|
|
27
250
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
28
251
|
|
|
@@ -312,6 +535,17 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
|
|
|
312
535
|
const wordBreak = style.wordBreak ?? "normal";
|
|
313
536
|
const textOverflow = style.textOverflow ?? "clip";
|
|
314
537
|
const textDecoration = style.textDecoration;
|
|
538
|
+
let textStrokeWidth;
|
|
539
|
+
const rawStrokeWidth = style.WebkitTextStrokeWidth;
|
|
540
|
+
if (rawStrokeWidth !== void 0) {
|
|
541
|
+
if (typeof rawStrokeWidth === "number") {
|
|
542
|
+
textStrokeWidth = rawStrokeWidth;
|
|
543
|
+
} else {
|
|
544
|
+
const resolved = resolveUnit(String(rawStrokeWidth), 0, 0, fontSize, 16);
|
|
545
|
+
textStrokeWidth = typeof resolved === "number" ? resolved : void 0;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const textStrokeColor = style.WebkitTextStrokeColor;
|
|
315
549
|
const measure = emojiEnabled ? (word, ls) => emojiAwareMeasureWord(
|
|
316
550
|
word,
|
|
317
551
|
fontSize,
|
|
@@ -432,7 +666,9 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
|
|
|
432
666
|
ascent: metrics.ascent,
|
|
433
667
|
textDecoration,
|
|
434
668
|
letterSpacing,
|
|
435
|
-
lineIndex: i
|
|
669
|
+
lineIndex: i,
|
|
670
|
+
textStrokeWidth,
|
|
671
|
+
textStrokeColor
|
|
436
672
|
});
|
|
437
673
|
totalHeight += lineHeightPx;
|
|
438
674
|
maxLineWidth = Math.max(maxLineWidth, lineWidth);
|
|
@@ -2168,14 +2404,42 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2168
2404
|
ctx.fillStyle = seg.color;
|
|
2169
2405
|
const x = offsetX + seg.x;
|
|
2170
2406
|
const y = offsetY + seg.y;
|
|
2407
|
+
const hasStroke = seg.textStrokeWidth !== void 0 && seg.textStrokeWidth > 0;
|
|
2171
2408
|
if (emojiStyle) {
|
|
2172
|
-
await drawSegmentWithEmoji(
|
|
2409
|
+
await drawSegmentWithEmoji(
|
|
2410
|
+
ctx,
|
|
2411
|
+
seg,
|
|
2412
|
+
x,
|
|
2413
|
+
y,
|
|
2414
|
+
textShadow,
|
|
2415
|
+
emojiStyle,
|
|
2416
|
+
hasStroke
|
|
2417
|
+
);
|
|
2173
2418
|
} else if (seg.letterSpacing && seg.letterSpacing !== 0) {
|
|
2419
|
+
if (hasStroke) {
|
|
2420
|
+
drawStrokeWithLetterSpacing(
|
|
2421
|
+
ctx,
|
|
2422
|
+
seg.text,
|
|
2423
|
+
x,
|
|
2424
|
+
y,
|
|
2425
|
+
seg.letterSpacing,
|
|
2426
|
+
seg.textStrokeWidth,
|
|
2427
|
+
seg.textStrokeColor ?? seg.color
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2174
2430
|
drawTextWithLetterSpacing(ctx, seg.text, x, y, seg.letterSpacing);
|
|
2175
2431
|
} else {
|
|
2176
2432
|
if (textShadow) {
|
|
2177
2433
|
drawTextShadow(ctx, seg.text, x, y, textShadow);
|
|
2178
2434
|
}
|
|
2435
|
+
if (hasStroke) {
|
|
2436
|
+
ctx.save();
|
|
2437
|
+
ctx.lineWidth = seg.textStrokeWidth;
|
|
2438
|
+
ctx.strokeStyle = seg.textStrokeColor ?? seg.color;
|
|
2439
|
+
ctx.lineJoin = "round";
|
|
2440
|
+
ctx.strokeText(seg.text, x, y);
|
|
2441
|
+
ctx.restore();
|
|
2442
|
+
}
|
|
2179
2443
|
ctx.fillText(seg.text, x, y);
|
|
2180
2444
|
}
|
|
2181
2445
|
if (seg.textDecoration) {
|
|
@@ -2183,7 +2447,7 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2183
2447
|
}
|
|
2184
2448
|
}
|
|
2185
2449
|
}
|
|
2186
|
-
async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle) {
|
|
2450
|
+
async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle, hasStroke) {
|
|
2187
2451
|
const letterSpacing = seg.letterSpacing ?? 0;
|
|
2188
2452
|
const runs = splitTextIntoRuns(
|
|
2189
2453
|
seg.text,
|
|
@@ -2199,6 +2463,26 @@ async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle) {
|
|
|
2199
2463
|
if (textShadow) {
|
|
2200
2464
|
drawTextShadow(ctx, run.text, x + run.x, y, textShadow);
|
|
2201
2465
|
}
|
|
2466
|
+
if (hasStroke) {
|
|
2467
|
+
if (letterSpacing !== 0) {
|
|
2468
|
+
drawStrokeWithLetterSpacing(
|
|
2469
|
+
ctx,
|
|
2470
|
+
run.text,
|
|
2471
|
+
x + run.x,
|
|
2472
|
+
y,
|
|
2473
|
+
letterSpacing,
|
|
2474
|
+
seg.textStrokeWidth,
|
|
2475
|
+
seg.textStrokeColor ?? seg.color
|
|
2476
|
+
);
|
|
2477
|
+
} else {
|
|
2478
|
+
ctx.save();
|
|
2479
|
+
ctx.lineWidth = seg.textStrokeWidth;
|
|
2480
|
+
ctx.strokeStyle = seg.textStrokeColor ?? seg.color;
|
|
2481
|
+
ctx.lineJoin = "round";
|
|
2482
|
+
ctx.strokeText(run.text, x + run.x, y);
|
|
2483
|
+
ctx.restore();
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2202
2486
|
if (letterSpacing !== 0) {
|
|
2203
2487
|
drawTextWithLetterSpacing(ctx, run.text, x + run.x, y, letterSpacing);
|
|
2204
2488
|
} else {
|
|
@@ -2224,6 +2508,19 @@ function drawTextWithLetterSpacing(ctx, text, x, y, letterSpacing) {
|
|
|
2224
2508
|
currentX += metrics.width + letterSpacing;
|
|
2225
2509
|
}
|
|
2226
2510
|
}
|
|
2511
|
+
function drawStrokeWithLetterSpacing(ctx, text, x, y, letterSpacing, strokeWidth, strokeColor) {
|
|
2512
|
+
ctx.save();
|
|
2513
|
+
ctx.lineWidth = strokeWidth;
|
|
2514
|
+
ctx.strokeStyle = strokeColor;
|
|
2515
|
+
ctx.lineJoin = "round";
|
|
2516
|
+
let currentX = x;
|
|
2517
|
+
for (const char of text) {
|
|
2518
|
+
ctx.strokeText(char, currentX, y);
|
|
2519
|
+
const metrics = ctx.measureText(char);
|
|
2520
|
+
currentX += metrics.width + letterSpacing;
|
|
2521
|
+
}
|
|
2522
|
+
ctx.restore();
|
|
2523
|
+
}
|
|
2227
2524
|
function drawTextShadow(ctx, text, x, y, shadow) {
|
|
2228
2525
|
const parts = shadow.match(
|
|
2229
2526
|
/(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(.*)/
|
|
@@ -2783,6 +3080,20 @@ function expandStyle(raw, fontFamilies) {
|
|
|
2783
3080
|
}
|
|
2784
3081
|
delete style.textBox;
|
|
2785
3082
|
}
|
|
3083
|
+
if (style.WebkitTextStroke !== void 0) {
|
|
3084
|
+
const val = String(style.WebkitTextStroke).trim();
|
|
3085
|
+
const match = val.match(/^(\S+)\s+(.+)$/);
|
|
3086
|
+
if (match) {
|
|
3087
|
+
if (style.WebkitTextStrokeWidth === void 0)
|
|
3088
|
+
style.WebkitTextStrokeWidth = parseValue(match[1]);
|
|
3089
|
+
if (style.WebkitTextStrokeColor === void 0)
|
|
3090
|
+
style.WebkitTextStrokeColor = match[2];
|
|
3091
|
+
} else {
|
|
3092
|
+
if (style.WebkitTextStrokeWidth === void 0)
|
|
3093
|
+
style.WebkitTextStrokeWidth = parseValue(val);
|
|
3094
|
+
}
|
|
3095
|
+
delete style.WebkitTextStroke;
|
|
3096
|
+
}
|
|
2786
3097
|
if (typeof style.fontFamily === "string") {
|
|
2787
3098
|
const families = style.fontFamily.split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
2788
3099
|
if (fontFamilies) {
|
|
@@ -2796,226 +3107,6 @@ function expandStyle(raw, fontFamilies) {
|
|
|
2796
3107
|
return style;
|
|
2797
3108
|
}
|
|
2798
3109
|
|
|
2799
|
-
// src/jsx/style/compute.ts
|
|
2800
|
-
var ROOT_FONT_SIZE = 16;
|
|
2801
|
-
var DEFAULT_STYLE = {
|
|
2802
|
-
display: "flex",
|
|
2803
|
-
flexDirection: "row",
|
|
2804
|
-
flexWrap: "nowrap",
|
|
2805
|
-
flexGrow: 0,
|
|
2806
|
-
flexShrink: 0,
|
|
2807
|
-
alignItems: "stretch",
|
|
2808
|
-
justifyContent: "flex-start",
|
|
2809
|
-
position: "relative",
|
|
2810
|
-
fontSize: ROOT_FONT_SIZE,
|
|
2811
|
-
fontWeight: 400,
|
|
2812
|
-
fontStyle: "normal",
|
|
2813
|
-
color: "black",
|
|
2814
|
-
lineHeight: "normal",
|
|
2815
|
-
textAlign: "left",
|
|
2816
|
-
whiteSpace: "normal",
|
|
2817
|
-
wordBreak: "normal",
|
|
2818
|
-
textOverflow: "clip",
|
|
2819
|
-
opacity: 1,
|
|
2820
|
-
overflow: "visible"
|
|
2821
|
-
};
|
|
2822
|
-
var INHERITABLE_PROPS = [
|
|
2823
|
-
"color",
|
|
2824
|
-
"fontSize",
|
|
2825
|
-
"fontFamily",
|
|
2826
|
-
"fontWeight",
|
|
2827
|
-
"fontStyle",
|
|
2828
|
-
"textAlign",
|
|
2829
|
-
"textTransform",
|
|
2830
|
-
"textDecoration",
|
|
2831
|
-
"lineHeight",
|
|
2832
|
-
"letterSpacing",
|
|
2833
|
-
"whiteSpace",
|
|
2834
|
-
"wordBreak",
|
|
2835
|
-
"textOverflow",
|
|
2836
|
-
"lineClamp",
|
|
2837
|
-
"textBoxTrim",
|
|
2838
|
-
"textBoxEdge"
|
|
2839
|
-
];
|
|
2840
|
-
function resolveStyle(rawStyle, parentStyle) {
|
|
2841
|
-
const style = { ...rawStyle };
|
|
2842
|
-
for (const prop of INHERITABLE_PROPS) {
|
|
2843
|
-
if (style[prop] === void 0 && parentStyle[prop] !== void 0) {
|
|
2844
|
-
style[prop] = parentStyle[prop];
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
if (typeof style.fontSize === "string") {
|
|
2848
|
-
const parentFontSize = typeof parentStyle.fontSize === "number" ? parentStyle.fontSize : ROOT_FONT_SIZE;
|
|
2849
|
-
const resolved = resolveUnit(
|
|
2850
|
-
style.fontSize,
|
|
2851
|
-
0,
|
|
2852
|
-
0,
|
|
2853
|
-
parentFontSize,
|
|
2854
|
-
ROOT_FONT_SIZE
|
|
2855
|
-
);
|
|
2856
|
-
style.fontSize = typeof resolved === "number" ? resolved : parentFontSize;
|
|
2857
|
-
}
|
|
2858
|
-
if (typeof style.lineHeight === "string") {
|
|
2859
|
-
const str = style.lineHeight;
|
|
2860
|
-
if (str.endsWith("%")) {
|
|
2861
|
-
style.lineHeight = parseFloat(str) / 100;
|
|
2862
|
-
} else {
|
|
2863
|
-
const parsed = parseFloat(str);
|
|
2864
|
-
if (!isNaN(parsed)) {
|
|
2865
|
-
style.lineHeight = parsed;
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
2869
|
-
if (typeof style.letterSpacing === "string") {
|
|
2870
|
-
const currentFontSize = typeof style.fontSize === "number" ? style.fontSize : typeof parentStyle.fontSize === "number" ? parentStyle.fontSize : ROOT_FONT_SIZE;
|
|
2871
|
-
const resolved = resolveUnit(
|
|
2872
|
-
style.letterSpacing,
|
|
2873
|
-
0,
|
|
2874
|
-
0,
|
|
2875
|
-
currentFontSize,
|
|
2876
|
-
ROOT_FONT_SIZE
|
|
2877
|
-
);
|
|
2878
|
-
if (typeof resolved === "number") {
|
|
2879
|
-
style.letterSpacing = resolved;
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2882
|
-
return style;
|
|
2883
|
-
}
|
|
2884
|
-
var DIMENSION_PROPS = [
|
|
2885
|
-
"width",
|
|
2886
|
-
"height",
|
|
2887
|
-
"minWidth",
|
|
2888
|
-
"minHeight",
|
|
2889
|
-
"maxWidth",
|
|
2890
|
-
"maxHeight",
|
|
2891
|
-
"top",
|
|
2892
|
-
"right",
|
|
2893
|
-
"bottom",
|
|
2894
|
-
"left",
|
|
2895
|
-
"marginTop",
|
|
2896
|
-
"marginRight",
|
|
2897
|
-
"marginBottom",
|
|
2898
|
-
"marginLeft",
|
|
2899
|
-
"paddingTop",
|
|
2900
|
-
"paddingRight",
|
|
2901
|
-
"paddingBottom",
|
|
2902
|
-
"paddingLeft",
|
|
2903
|
-
"rowGap",
|
|
2904
|
-
"columnGap",
|
|
2905
|
-
"flexBasis",
|
|
2906
|
-
"borderTopWidth",
|
|
2907
|
-
"borderRightWidth",
|
|
2908
|
-
"borderBottomWidth",
|
|
2909
|
-
"borderLeftWidth",
|
|
2910
|
-
"borderTopLeftRadius",
|
|
2911
|
-
"borderTopRightRadius",
|
|
2912
|
-
"borderBottomRightRadius",
|
|
2913
|
-
"borderBottomLeftRadius"
|
|
2914
|
-
];
|
|
2915
|
-
function resolveUnit(value, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
2916
|
-
if (value.endsWith("%") || value === "auto") return value;
|
|
2917
|
-
if (value.endsWith("vmin")) {
|
|
2918
|
-
const n = parseFloat(value);
|
|
2919
|
-
return isNaN(n) ? value : n / 100 * Math.min(viewportWidth, viewportHeight);
|
|
2920
|
-
}
|
|
2921
|
-
if (value.endsWith("vmax")) {
|
|
2922
|
-
const n = parseFloat(value);
|
|
2923
|
-
return isNaN(n) ? value : n / 100 * Math.max(viewportWidth, viewportHeight);
|
|
2924
|
-
}
|
|
2925
|
-
if (value.endsWith("vw")) {
|
|
2926
|
-
const n = parseFloat(value);
|
|
2927
|
-
return isNaN(n) ? value : n / 100 * viewportWidth;
|
|
2928
|
-
}
|
|
2929
|
-
if (value.endsWith("vh")) {
|
|
2930
|
-
const n = parseFloat(value);
|
|
2931
|
-
return isNaN(n) ? value : n / 100 * viewportHeight;
|
|
2932
|
-
}
|
|
2933
|
-
if (value.endsWith("rem")) {
|
|
2934
|
-
const n = parseFloat(value);
|
|
2935
|
-
return isNaN(n) ? value : n * rootFontSize;
|
|
2936
|
-
}
|
|
2937
|
-
if (value.endsWith("em")) {
|
|
2938
|
-
const n = parseFloat(value);
|
|
2939
|
-
return isNaN(n) ? value : n * fontSize;
|
|
2940
|
-
}
|
|
2941
|
-
if (value.endsWith("px")) {
|
|
2942
|
-
const n = parseFloat(value);
|
|
2943
|
-
return isNaN(n) ? value : n;
|
|
2944
|
-
}
|
|
2945
|
-
if (value.endsWith("pt")) {
|
|
2946
|
-
const n = parseFloat(value);
|
|
2947
|
-
return isNaN(n) ? value : n * (96 / 72);
|
|
2948
|
-
}
|
|
2949
|
-
if (value.endsWith("pc")) {
|
|
2950
|
-
const n = parseFloat(value);
|
|
2951
|
-
return isNaN(n) ? value : n * 16;
|
|
2952
|
-
}
|
|
2953
|
-
if (value.endsWith("in")) {
|
|
2954
|
-
const n = parseFloat(value);
|
|
2955
|
-
return isNaN(n) ? value : n * 96;
|
|
2956
|
-
}
|
|
2957
|
-
if (value.endsWith("cm")) {
|
|
2958
|
-
const n = parseFloat(value);
|
|
2959
|
-
return isNaN(n) ? value : n * (96 / 2.54);
|
|
2960
|
-
}
|
|
2961
|
-
if (value.endsWith("mm")) {
|
|
2962
|
-
const n = parseFloat(value);
|
|
2963
|
-
return isNaN(n) ? value : n * (96 / 25.4);
|
|
2964
|
-
}
|
|
2965
|
-
return value;
|
|
2966
|
-
}
|
|
2967
|
-
function resolveUnits(style, viewportWidth, viewportHeight, rootFontSize = ROOT_FONT_SIZE) {
|
|
2968
|
-
const fontSize = typeof style.fontSize === "number" ? style.fontSize : rootFontSize;
|
|
2969
|
-
for (const prop of DIMENSION_PROPS) {
|
|
2970
|
-
const value = style[prop];
|
|
2971
|
-
if (typeof value !== "string") continue;
|
|
2972
|
-
const resolved = resolveUnit(
|
|
2973
|
-
value,
|
|
2974
|
-
viewportWidth,
|
|
2975
|
-
viewportHeight,
|
|
2976
|
-
fontSize,
|
|
2977
|
-
rootFontSize
|
|
2978
|
-
);
|
|
2979
|
-
if (resolved !== value) {
|
|
2980
|
-
style[prop] = resolved;
|
|
2981
|
-
}
|
|
2982
|
-
}
|
|
2983
|
-
if (style.transform) {
|
|
2984
|
-
style.transform = resolveTransformUnits(
|
|
2985
|
-
style.transform,
|
|
2986
|
-
viewportWidth,
|
|
2987
|
-
viewportHeight,
|
|
2988
|
-
fontSize,
|
|
2989
|
-
rootFontSize
|
|
2990
|
-
);
|
|
2991
|
-
}
|
|
2992
|
-
if (style.transformOrigin) {
|
|
2993
|
-
style.transformOrigin = resolveTransformUnits(
|
|
2994
|
-
style.transformOrigin,
|
|
2995
|
-
viewportWidth,
|
|
2996
|
-
viewportHeight,
|
|
2997
|
-
fontSize,
|
|
2998
|
-
rootFontSize
|
|
2999
|
-
);
|
|
3000
|
-
}
|
|
3001
|
-
return style;
|
|
3002
|
-
}
|
|
3003
|
-
function resolveTransformUnits(transform, viewportWidth, viewportHeight, fontSize, rootFontSize) {
|
|
3004
|
-
return transform.replace(
|
|
3005
|
-
/(-?\d*\.?\d+)(vw|vh|vmin|vmax|em|rem|px|pt|pc|in|cm|mm)\b/g,
|
|
3006
|
-
(match) => {
|
|
3007
|
-
const resolved = resolveUnit(
|
|
3008
|
-
match,
|
|
3009
|
-
viewportWidth,
|
|
3010
|
-
viewportHeight,
|
|
3011
|
-
fontSize,
|
|
3012
|
-
rootFontSize
|
|
3013
|
-
);
|
|
3014
|
-
return typeof resolved === "number" ? String(resolved) : match;
|
|
3015
|
-
}
|
|
3016
|
-
);
|
|
3017
|
-
}
|
|
3018
|
-
|
|
3019
3110
|
// src/jsx/yoga.ts
|
|
3020
3111
|
import Yoga, {
|
|
3021
3112
|
Align,
|
|
@@ -3399,14 +3490,22 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
3399
3490
|
const rawChildren = props.children;
|
|
3400
3491
|
if (rawChildren !== void 0 && rawChildren !== null) {
|
|
3401
3492
|
const childArray = Array.isArray(rawChildren) ? rawChildren.flat(Infinity) : [rawChildren];
|
|
3493
|
+
const ws = style.whiteSpace;
|
|
3494
|
+
const collapsesWhitespace = ws === void 0 || ws === "normal" || ws === "nowrap" || ws === "pre-line";
|
|
3495
|
+
let prevWasBr = false;
|
|
3402
3496
|
for (const child of childArray) {
|
|
3403
3497
|
if (child === null || child === void 0 || typeof child === "boolean")
|
|
3404
3498
|
continue;
|
|
3499
|
+
let processedChild = child;
|
|
3500
|
+
if (prevWasBr && collapsesWhitespace && typeof child === "string") {
|
|
3501
|
+
processedChild = child.trimStart();
|
|
3502
|
+
}
|
|
3503
|
+
prevWasBr = isBrElement(child);
|
|
3405
3504
|
const childYogaNode = createYogaNode();
|
|
3406
3505
|
yogaNode.insertChild(childYogaNode, children.length);
|
|
3407
3506
|
children.push(
|
|
3408
3507
|
await buildNode(
|
|
3409
|
-
|
|
3508
|
+
processedChild,
|
|
3410
3509
|
style,
|
|
3411
3510
|
childYogaNode,
|
|
3412
3511
|
viewportWidth,
|
|
@@ -3443,6 +3542,11 @@ function extractLayout(node, yogaNode) {
|
|
|
3443
3542
|
height: layout.height
|
|
3444
3543
|
};
|
|
3445
3544
|
}
|
|
3545
|
+
function isBrElement(child) {
|
|
3546
|
+
if (child === null || child === void 0 || typeof child !== "object")
|
|
3547
|
+
return false;
|
|
3548
|
+
return child.type === "br";
|
|
3549
|
+
}
|
|
3446
3550
|
function extractTextContent(children) {
|
|
3447
3551
|
if (children === void 0 || children === null) return void 0;
|
|
3448
3552
|
if (typeof children === "string") return children;
|