@effing/canvas 0.22.3 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -18
- package/dist/index.js +329 -232
- 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,
|
|
@@ -410,14 +644,7 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
|
|
|
410
644
|
fontStyle,
|
|
411
645
|
ctx
|
|
412
646
|
);
|
|
413
|
-
|
|
414
|
-
if (fontMetrics) {
|
|
415
|
-
const typoAscent = fontMetrics.sTypoAscender / fontMetrics.unitsPerEm * fontSize;
|
|
416
|
-
const typoDescent = -fontMetrics.sTypoDescender / fontMetrics.unitsPerEm * fontSize;
|
|
417
|
-
baselineY = totalHeight + (lineHeightPx + typoAscent - typoDescent) / 2;
|
|
418
|
-
} else {
|
|
419
|
-
baselineY = totalHeight + (lineHeightPx + metrics.ascent - metrics.descent) / 2;
|
|
420
|
-
}
|
|
647
|
+
const baselineY = totalHeight + (lineHeightPx + metrics.ascent - metrics.descent) / 2;
|
|
421
648
|
segments.push({
|
|
422
649
|
text: line,
|
|
423
650
|
x,
|
|
@@ -432,7 +659,9 @@ function layoutText(text, style, maxWidth, ctx, emojiEnabled) {
|
|
|
432
659
|
ascent: metrics.ascent,
|
|
433
660
|
textDecoration,
|
|
434
661
|
letterSpacing,
|
|
435
|
-
lineIndex: i
|
|
662
|
+
lineIndex: i,
|
|
663
|
+
textStrokeWidth,
|
|
664
|
+
textStrokeColor
|
|
436
665
|
});
|
|
437
666
|
totalHeight += lineHeightPx;
|
|
438
667
|
maxLineWidth = Math.max(maxLineWidth, lineWidth);
|
|
@@ -2168,14 +2397,42 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2168
2397
|
ctx.fillStyle = seg.color;
|
|
2169
2398
|
const x = offsetX + seg.x;
|
|
2170
2399
|
const y = offsetY + seg.y;
|
|
2400
|
+
const hasStroke = seg.textStrokeWidth !== void 0 && seg.textStrokeWidth > 0;
|
|
2171
2401
|
if (emojiStyle) {
|
|
2172
|
-
await drawSegmentWithEmoji(
|
|
2402
|
+
await drawSegmentWithEmoji(
|
|
2403
|
+
ctx,
|
|
2404
|
+
seg,
|
|
2405
|
+
x,
|
|
2406
|
+
y,
|
|
2407
|
+
textShadow,
|
|
2408
|
+
emojiStyle,
|
|
2409
|
+
hasStroke
|
|
2410
|
+
);
|
|
2173
2411
|
} else if (seg.letterSpacing && seg.letterSpacing !== 0) {
|
|
2412
|
+
if (hasStroke) {
|
|
2413
|
+
drawStrokeWithLetterSpacing(
|
|
2414
|
+
ctx,
|
|
2415
|
+
seg.text,
|
|
2416
|
+
x,
|
|
2417
|
+
y,
|
|
2418
|
+
seg.letterSpacing,
|
|
2419
|
+
seg.textStrokeWidth,
|
|
2420
|
+
seg.textStrokeColor ?? seg.color
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2174
2423
|
drawTextWithLetterSpacing(ctx, seg.text, x, y, seg.letterSpacing);
|
|
2175
2424
|
} else {
|
|
2176
2425
|
if (textShadow) {
|
|
2177
2426
|
drawTextShadow(ctx, seg.text, x, y, textShadow);
|
|
2178
2427
|
}
|
|
2428
|
+
if (hasStroke) {
|
|
2429
|
+
ctx.save();
|
|
2430
|
+
ctx.lineWidth = seg.textStrokeWidth;
|
|
2431
|
+
ctx.strokeStyle = seg.textStrokeColor ?? seg.color;
|
|
2432
|
+
ctx.lineJoin = "round";
|
|
2433
|
+
ctx.strokeText(seg.text, x, y);
|
|
2434
|
+
ctx.restore();
|
|
2435
|
+
}
|
|
2179
2436
|
ctx.fillText(seg.text, x, y);
|
|
2180
2437
|
}
|
|
2181
2438
|
if (seg.textDecoration) {
|
|
@@ -2183,7 +2440,7 @@ async function drawText(ctx, segments, offsetX, offsetY, textShadow, emojiStyle)
|
|
|
2183
2440
|
}
|
|
2184
2441
|
}
|
|
2185
2442
|
}
|
|
2186
|
-
async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle) {
|
|
2443
|
+
async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle, hasStroke) {
|
|
2187
2444
|
const letterSpacing = seg.letterSpacing ?? 0;
|
|
2188
2445
|
const runs = splitTextIntoRuns(
|
|
2189
2446
|
seg.text,
|
|
@@ -2199,6 +2456,26 @@ async function drawSegmentWithEmoji(ctx, seg, x, y, textShadow, emojiStyle) {
|
|
|
2199
2456
|
if (textShadow) {
|
|
2200
2457
|
drawTextShadow(ctx, run.text, x + run.x, y, textShadow);
|
|
2201
2458
|
}
|
|
2459
|
+
if (hasStroke) {
|
|
2460
|
+
if (letterSpacing !== 0) {
|
|
2461
|
+
drawStrokeWithLetterSpacing(
|
|
2462
|
+
ctx,
|
|
2463
|
+
run.text,
|
|
2464
|
+
x + run.x,
|
|
2465
|
+
y,
|
|
2466
|
+
letterSpacing,
|
|
2467
|
+
seg.textStrokeWidth,
|
|
2468
|
+
seg.textStrokeColor ?? seg.color
|
|
2469
|
+
);
|
|
2470
|
+
} else {
|
|
2471
|
+
ctx.save();
|
|
2472
|
+
ctx.lineWidth = seg.textStrokeWidth;
|
|
2473
|
+
ctx.strokeStyle = seg.textStrokeColor ?? seg.color;
|
|
2474
|
+
ctx.lineJoin = "round";
|
|
2475
|
+
ctx.strokeText(run.text, x + run.x, y);
|
|
2476
|
+
ctx.restore();
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2202
2479
|
if (letterSpacing !== 0) {
|
|
2203
2480
|
drawTextWithLetterSpacing(ctx, run.text, x + run.x, y, letterSpacing);
|
|
2204
2481
|
} else {
|
|
@@ -2224,6 +2501,19 @@ function drawTextWithLetterSpacing(ctx, text, x, y, letterSpacing) {
|
|
|
2224
2501
|
currentX += metrics.width + letterSpacing;
|
|
2225
2502
|
}
|
|
2226
2503
|
}
|
|
2504
|
+
function drawStrokeWithLetterSpacing(ctx, text, x, y, letterSpacing, strokeWidth, strokeColor) {
|
|
2505
|
+
ctx.save();
|
|
2506
|
+
ctx.lineWidth = strokeWidth;
|
|
2507
|
+
ctx.strokeStyle = strokeColor;
|
|
2508
|
+
ctx.lineJoin = "round";
|
|
2509
|
+
let currentX = x;
|
|
2510
|
+
for (const char of text) {
|
|
2511
|
+
ctx.strokeText(char, currentX, y);
|
|
2512
|
+
const metrics = ctx.measureText(char);
|
|
2513
|
+
currentX += metrics.width + letterSpacing;
|
|
2514
|
+
}
|
|
2515
|
+
ctx.restore();
|
|
2516
|
+
}
|
|
2227
2517
|
function drawTextShadow(ctx, text, x, y, shadow) {
|
|
2228
2518
|
const parts = shadow.match(
|
|
2229
2519
|
/(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(.*)/
|
|
@@ -2783,6 +3073,20 @@ function expandStyle(raw, fontFamilies) {
|
|
|
2783
3073
|
}
|
|
2784
3074
|
delete style.textBox;
|
|
2785
3075
|
}
|
|
3076
|
+
if (style.WebkitTextStroke !== void 0) {
|
|
3077
|
+
const val = String(style.WebkitTextStroke).trim();
|
|
3078
|
+
const match = val.match(/^(\S+)\s+(.+)$/);
|
|
3079
|
+
if (match) {
|
|
3080
|
+
if (style.WebkitTextStrokeWidth === void 0)
|
|
3081
|
+
style.WebkitTextStrokeWidth = parseValue(match[1]);
|
|
3082
|
+
if (style.WebkitTextStrokeColor === void 0)
|
|
3083
|
+
style.WebkitTextStrokeColor = match[2];
|
|
3084
|
+
} else {
|
|
3085
|
+
if (style.WebkitTextStrokeWidth === void 0)
|
|
3086
|
+
style.WebkitTextStrokeWidth = parseValue(val);
|
|
3087
|
+
}
|
|
3088
|
+
delete style.WebkitTextStroke;
|
|
3089
|
+
}
|
|
2786
3090
|
if (typeof style.fontFamily === "string") {
|
|
2787
3091
|
const families = style.fontFamily.split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
2788
3092
|
if (fontFamilies) {
|
|
@@ -2796,226 +3100,6 @@ function expandStyle(raw, fontFamilies) {
|
|
|
2796
3100
|
return style;
|
|
2797
3101
|
}
|
|
2798
3102
|
|
|
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
3103
|
// src/jsx/yoga.ts
|
|
3020
3104
|
import Yoga, {
|
|
3021
3105
|
Align,
|
|
@@ -3399,14 +3483,22 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
3399
3483
|
const rawChildren = props.children;
|
|
3400
3484
|
if (rawChildren !== void 0 && rawChildren !== null) {
|
|
3401
3485
|
const childArray = Array.isArray(rawChildren) ? rawChildren.flat(Infinity) : [rawChildren];
|
|
3486
|
+
const ws = style.whiteSpace;
|
|
3487
|
+
const collapsesWhitespace = ws === void 0 || ws === "normal" || ws === "nowrap" || ws === "pre-line";
|
|
3488
|
+
let prevWasBr = false;
|
|
3402
3489
|
for (const child of childArray) {
|
|
3403
3490
|
if (child === null || child === void 0 || typeof child === "boolean")
|
|
3404
3491
|
continue;
|
|
3492
|
+
let processedChild = child;
|
|
3493
|
+
if (prevWasBr && collapsesWhitespace && typeof child === "string") {
|
|
3494
|
+
processedChild = child.trimStart();
|
|
3495
|
+
}
|
|
3496
|
+
prevWasBr = isBrElement(child);
|
|
3405
3497
|
const childYogaNode = createYogaNode();
|
|
3406
3498
|
yogaNode.insertChild(childYogaNode, children.length);
|
|
3407
3499
|
children.push(
|
|
3408
3500
|
await buildNode(
|
|
3409
|
-
|
|
3501
|
+
processedChild,
|
|
3410
3502
|
style,
|
|
3411
3503
|
childYogaNode,
|
|
3412
3504
|
viewportWidth,
|
|
@@ -3443,6 +3535,11 @@ function extractLayout(node, yogaNode) {
|
|
|
3443
3535
|
height: layout.height
|
|
3444
3536
|
};
|
|
3445
3537
|
}
|
|
3538
|
+
function isBrElement(child) {
|
|
3539
|
+
if (child === null || child === void 0 || typeof child !== "object")
|
|
3540
|
+
return false;
|
|
3541
|
+
return child.type === "br";
|
|
3542
|
+
}
|
|
3446
3543
|
function extractTextContent(children) {
|
|
3447
3544
|
if (children === void 0 || children === null) return void 0;
|
|
3448
3545
|
if (typeof children === "string") return children;
|