ntk 8.16.2 → 8.17.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/lib/fontconfig.js +19 -1
- package/lib/text/layout.js +35 -3
- package/package.json +1 -1
package/lib/fontconfig.js
CHANGED
|
@@ -226,8 +226,26 @@ export function platformFamilies(family, platform = globalThis.process?.platform
|
|
|
226
226
|
return changed ? out.join(',') : family;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
/**
|
|
230
|
+
* A family name as fontconfig's pattern syntax reads it (FcNameParse): a
|
|
231
|
+
* `-` starts the point size, a `:` the properties, a `,` the next family,
|
|
232
|
+
* and a `\` escapes each of them. Unescaped, the CSS list
|
|
233
|
+
* `tablet-gothic-condensed, "arial narrow", arial` was the family `tablet`
|
|
234
|
+
* and a size fc-match could not read, the families after it were gone, and
|
|
235
|
+
* the face was fontconfig's default for nothing: Verdana, where a browser
|
|
236
|
+
* sets Arial Narrow.
|
|
237
|
+
*/
|
|
238
|
+
function fcName(name) {
|
|
239
|
+
return name.replace(/[\\\-:,]/g, '\\$&');
|
|
240
|
+
}
|
|
241
|
+
|
|
229
242
|
function patternFor({ family, weight, style }) {
|
|
230
|
-
let fc = platformFamilies(family || 'sans-serif')
|
|
243
|
+
let fc = String(platformFamilies(family || 'sans-serif'))
|
|
244
|
+
.split(',')
|
|
245
|
+
.map((name) => name.trim().replace(/^["']|["']$/g, ''))
|
|
246
|
+
.filter(Boolean)
|
|
247
|
+
.map(fcName)
|
|
248
|
+
.join(',');
|
|
231
249
|
const fcWeight = normalizeWeight(weight);
|
|
232
250
|
if (fcWeight !== undefined) fc += `:weight=${fcWeight}`;
|
|
233
251
|
if (style && style.includes('italic')) fc += ':slant=italic';
|
package/lib/text/layout.js
CHANGED
|
@@ -110,6 +110,30 @@ function sameItem(before, span) {
|
|
|
110
110
|
return before === span || span.kernAcross || before.kernAcross;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* The width a line's entries take as the line is fitted, white space and
|
|
115
|
+
* all: each item's part — a span's, or a run of them `sameItem` joins —
|
|
116
|
+
* rounded as the layout rounds (`LineFit`), with its kerning. A line's
|
|
117
|
+
* `advance`: where the text goes on after the line on the same one, as it
|
|
118
|
+
* does after a piece of a line composed a piece at a time, the white space
|
|
119
|
+
* it ends on is inside the line, and in the item it ends, which is rounded
|
|
120
|
+
* once with it, as Blink measures an element's text with its spaces.
|
|
121
|
+
*/
|
|
122
|
+
function fittedEntries(entries, round) {
|
|
123
|
+
let fitted = 0;
|
|
124
|
+
let item = 0;
|
|
125
|
+
let span = null;
|
|
126
|
+
for (const e of entries) {
|
|
127
|
+
if (span !== null && !sameItem(span, e.span)) {
|
|
128
|
+
fitted += round(item);
|
|
129
|
+
item = 0;
|
|
130
|
+
}
|
|
131
|
+
item += (e.kern ?? 0) + e.run.width + (e.kernAfter ?? 0);
|
|
132
|
+
span = e.span;
|
|
133
|
+
}
|
|
134
|
+
return entries.length ? fitted + round(item) : 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
113
137
|
/** How a layout rounds its items before it adds them (`LineFit`). */
|
|
114
138
|
function itemRounding(options) {
|
|
115
139
|
return options.fit === 'items' ? snapUp : unrounded;
|
|
@@ -187,12 +211,15 @@ function isWsGlyph(g) {
|
|
|
187
211
|
* is that sum. Default: the advances as they add up (`LineFit`)
|
|
188
212
|
*
|
|
189
213
|
* The result is inspectable before/without drawing: `width`, `height`,
|
|
190
|
-
* `truncated`, and `lines[] = { x, y, height, baseline, width,
|
|
191
|
-
* descent, runs, start, end }` with
|
|
214
|
+
* `truncated`, and `lines[] = { x, y, height, baseline, width, advance,
|
|
215
|
+
* ascent, descent, runs, start, end }` with
|
|
192
216
|
* `runs[] = { x, width, run, span, start, end }` in visual order
|
|
193
217
|
* (`start`/`end` are logical UTF-16 ranges into the full text). A line's
|
|
194
218
|
* box is `y` to `y + height`; its glyphs sit centred in it, from
|
|
195
|
-
* `baseline - ascent` to `baseline + descent`.
|
|
219
|
+
* `baseline - ascent` to `baseline + descent`. Its `width` leaves out the
|
|
220
|
+
* white space it ends on, as a line's end does; its `advance` keeps it, in
|
|
221
|
+
* the item it ends, which is how far the line moves the pen where text goes
|
|
222
|
+
* on after it on the same line.
|
|
196
223
|
*
|
|
197
224
|
* `caretPosition(index)` / `indexAt(x, y)` map logical code-point indices
|
|
198
225
|
* to visual caret geometry and back (bidi/ligature/trailing-whitespace
|
|
@@ -334,6 +361,9 @@ export class TextLayout {
|
|
|
334
361
|
}
|
|
335
362
|
}
|
|
336
363
|
}
|
|
364
|
+
// how far the line moves the pen where text goes on after it on the
|
|
365
|
+
// same line: its trailing white space kept, in the item it ends
|
|
366
|
+
let advance = fittedEntries(entries, round);
|
|
337
367
|
let trailing = stripTrailingWhitespace(entries);
|
|
338
368
|
const contentEnd = trailing
|
|
339
369
|
? trailing.start
|
|
@@ -363,6 +393,7 @@ export class TextLayout {
|
|
|
363
393
|
// whatever whitespace was stripped is gone for good, not merely
|
|
364
394
|
// pushed past the line edge: there is no wrap for it to precede
|
|
365
395
|
trailing = null;
|
|
396
|
+
advance = null;
|
|
366
397
|
lineEnd = contentEnd;
|
|
367
398
|
}
|
|
368
399
|
entries = reorderRuns(entries);
|
|
@@ -424,6 +455,7 @@ export class TextLayout {
|
|
|
424
455
|
height: box,
|
|
425
456
|
baseline: y + leading + ascent,
|
|
426
457
|
width: fitted,
|
|
458
|
+
advance: trailing && advance !== null ? Math.max(fitted, advance) : fitted,
|
|
427
459
|
ascent,
|
|
428
460
|
descent,
|
|
429
461
|
runs,
|