ts-maps 0.3.4 → 0.3.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.
|
@@ -86,6 +86,7 @@ export declare interface VectorTileLayoutProperties {
|
|
|
86
86
|
'text-font'?: string | string[]
|
|
87
87
|
'text-italic'?: boolean
|
|
88
88
|
'text-bold'?: boolean
|
|
89
|
+
'text-letter-spacing'?: number | unknown
|
|
89
90
|
'icon-image'?: string | unknown
|
|
90
91
|
'icon-size'?: number
|
|
91
92
|
'icon-rotate'?: number
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// GlyphAtlas — rasterises font glyphs to a single backing canvas and packs
|
|
2
|
-
// them via a simple shelf-packer. Each glyph gets an approximate signed
|
|
3
|
-
// distance field generated by a cheap 2-pass Chamfer 3-4 scan so callers can
|
|
4
|
-
// resample the atlas at any target size with tolerably smooth edges.
|
|
5
|
-
//
|
|
6
|
-
// Zero runtime deps. Designed to live alongside Canvas2D rendering today and
|
|
7
|
-
// graduate to WebGL sampling later without changing the public API.
|
|
8
1
|
export declare interface GlyphMetrics {
|
|
9
2
|
codePoint: number
|
|
10
3
|
x: number
|
|
@@ -26,8 +19,8 @@ export declare class GlyphAtlas {
|
|
|
26
19
|
constructor(opts?: GlyphAtlasOptions);
|
|
27
20
|
getOrAddGlyph(codePoint: number, style?: { italic?: boolean, bold?: boolean }): GlyphMetrics;
|
|
28
21
|
measure(text: string, style?: { italic?: boolean, bold?: boolean, family?: string }): { width: number, height: number };
|
|
29
|
-
advances(text: string, size: number, style?: { italic?: boolean, bold?: boolean, family?: string }): Array<{ char: string, advance: number }>;
|
|
30
|
-
measureText(text: string, size: number, style?: { italic?: boolean, bold?: boolean, family?: string }): { width: number, height: number, ascent: number, descent: number };
|
|
22
|
+
advances(text: string, size: number, style?: { italic?: boolean, bold?: boolean, family?: string, letterSpacing?: number }): Array<{ char: string, advance: number }>;
|
|
23
|
+
measureText(text: string, size: number, style?: { italic?: boolean, bold?: boolean, family?: string, letterSpacing?: number }): { width: number, height: number, ascent: number, descent: number };
|
|
31
24
|
fontString(size: number, style?: { italic?: boolean, bold?: boolean, family?: string }): string;
|
|
32
25
|
resolveFont(textFont: string | string[] | undefined): { family: string, bold: boolean, italic: boolean };
|
|
33
26
|
drawText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, options: {
|
|
@@ -38,6 +31,7 @@ export declare class GlyphAtlas {
|
|
|
38
31
|
italic?: boolean
|
|
39
32
|
bold?: boolean
|
|
40
33
|
family?: string
|
|
34
|
+
letterSpacing?: number
|
|
41
35
|
}): void;
|
|
42
36
|
_buildSDF(imageData: ImageData): ImageData;
|
|
43
37
|
}
|
|
@@ -168,6 +168,23 @@ var exports_GlyphAtlas = {};
|
|
|
168
168
|
__export(exports_GlyphAtlas, {
|
|
169
169
|
GlyphAtlas: () => GlyphAtlas
|
|
170
170
|
});
|
|
171
|
+
function applyTracking(ctx, spacing) {
|
|
172
|
+
if (!spacing)
|
|
173
|
+
return true;
|
|
174
|
+
if (!("letterSpacing" in ctx))
|
|
175
|
+
return false;
|
|
176
|
+
const tracked = ctx;
|
|
177
|
+
tracked.letterSpacing = `${spacing}px`;
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function trackingWidth(text, spacing) {
|
|
181
|
+
if (!spacing)
|
|
182
|
+
return 0;
|
|
183
|
+
let count = 0;
|
|
184
|
+
for (const _ of text)
|
|
185
|
+
count++;
|
|
186
|
+
return count * spacing;
|
|
187
|
+
}
|
|
171
188
|
function styleKey(k) {
|
|
172
189
|
if (!k)
|
|
173
190
|
return "r";
|
|
@@ -230,8 +247,9 @@ class GlyphAtlas {
|
|
|
230
247
|
if (ctx) {
|
|
231
248
|
ctx.save();
|
|
232
249
|
ctx.font = this.fontString(size, style);
|
|
250
|
+
const tracking = style?.letterSpacing ?? 0;
|
|
233
251
|
for (const ch of text)
|
|
234
|
-
out.push({ char: ch, advance: ctx.measureText(ch).width });
|
|
252
|
+
out.push({ char: ch, advance: ctx.measureText(ch).width + tracking });
|
|
235
253
|
ctx.restore();
|
|
236
254
|
return out;
|
|
237
255
|
}
|
|
@@ -249,15 +267,18 @@ class GlyphAtlas {
|
|
|
249
267
|
const atlas = this.measure(text, style);
|
|
250
268
|
const scale = size / this._fontSize;
|
|
251
269
|
const height = atlas.height * scale;
|
|
252
|
-
|
|
270
|
+
const tracking = trackingWidth(text, style?.letterSpacing);
|
|
271
|
+
return { width: atlas.width * scale + tracking, height, ascent: height * 0.8, descent: height * 0.2 };
|
|
253
272
|
}
|
|
254
273
|
ctx.save();
|
|
255
274
|
ctx.font = this.fontString(size, style);
|
|
275
|
+
const spaced = applyTracking(ctx, style?.letterSpacing);
|
|
256
276
|
const m = ctx.measureText(text);
|
|
277
|
+
const width = spaced ? m.width : m.width + trackingWidth(text, style?.letterSpacing);
|
|
257
278
|
ctx.restore();
|
|
258
279
|
const ascent = m.fontBoundingBoxAscent || m.actualBoundingBoxAscent || size * 0.8;
|
|
259
280
|
const descent = m.fontBoundingBoxDescent || m.actualBoundingBoxDescent || size * 0.2;
|
|
260
|
-
return { width
|
|
281
|
+
return { width, height: ascent + descent, ascent, descent };
|
|
261
282
|
}
|
|
262
283
|
fontString(size, style) {
|
|
263
284
|
const parts = [];
|
|
@@ -312,6 +333,7 @@ class GlyphAtlas {
|
|
|
312
333
|
const style = { italic: !!options.italic, bold: !!options.bold, family: options.family };
|
|
313
334
|
ctx.save();
|
|
314
335
|
ctx.font = this.fontString(options.size, style);
|
|
336
|
+
applyTracking(ctx, options.letterSpacing);
|
|
315
337
|
ctx.textAlign = "left";
|
|
316
338
|
ctx.textBaseline = "alphabetic";
|
|
317
339
|
if (options.haloColor && options.haloWidth && options.haloWidth > 0) {
|
package/dist/index.js
CHANGED
|
@@ -8319,6 +8319,23 @@ var exports_GlyphAtlas = {};
|
|
|
8319
8319
|
__export(exports_GlyphAtlas, {
|
|
8320
8320
|
GlyphAtlas: () => GlyphAtlas
|
|
8321
8321
|
});
|
|
8322
|
+
function applyTracking(ctx, spacing) {
|
|
8323
|
+
if (!spacing)
|
|
8324
|
+
return true;
|
|
8325
|
+
if (!("letterSpacing" in ctx))
|
|
8326
|
+
return false;
|
|
8327
|
+
const tracked = ctx;
|
|
8328
|
+
tracked.letterSpacing = `${spacing}px`;
|
|
8329
|
+
return true;
|
|
8330
|
+
}
|
|
8331
|
+
function trackingWidth(text, spacing) {
|
|
8332
|
+
if (!spacing)
|
|
8333
|
+
return 0;
|
|
8334
|
+
let count = 0;
|
|
8335
|
+
for (const _ of text)
|
|
8336
|
+
count++;
|
|
8337
|
+
return count * spacing;
|
|
8338
|
+
}
|
|
8322
8339
|
function styleKey(k) {
|
|
8323
8340
|
if (!k)
|
|
8324
8341
|
return "r";
|
|
@@ -8381,8 +8398,9 @@ class GlyphAtlas {
|
|
|
8381
8398
|
if (ctx) {
|
|
8382
8399
|
ctx.save();
|
|
8383
8400
|
ctx.font = this.fontString(size, style);
|
|
8401
|
+
const tracking = style?.letterSpacing ?? 0;
|
|
8384
8402
|
for (const ch of text)
|
|
8385
|
-
out.push({ char: ch, advance: ctx.measureText(ch).width });
|
|
8403
|
+
out.push({ char: ch, advance: ctx.measureText(ch).width + tracking });
|
|
8386
8404
|
ctx.restore();
|
|
8387
8405
|
return out;
|
|
8388
8406
|
}
|
|
@@ -8400,15 +8418,18 @@ class GlyphAtlas {
|
|
|
8400
8418
|
const atlas = this.measure(text, style);
|
|
8401
8419
|
const scale = size / this._fontSize;
|
|
8402
8420
|
const height = atlas.height * scale;
|
|
8403
|
-
|
|
8421
|
+
const tracking = trackingWidth(text, style?.letterSpacing);
|
|
8422
|
+
return { width: atlas.width * scale + tracking, height, ascent: height * 0.8, descent: height * 0.2 };
|
|
8404
8423
|
}
|
|
8405
8424
|
ctx.save();
|
|
8406
8425
|
ctx.font = this.fontString(size, style);
|
|
8426
|
+
const spaced = applyTracking(ctx, style?.letterSpacing);
|
|
8407
8427
|
const m = ctx.measureText(text);
|
|
8428
|
+
const width = spaced ? m.width : m.width + trackingWidth(text, style?.letterSpacing);
|
|
8408
8429
|
ctx.restore();
|
|
8409
8430
|
const ascent = m.fontBoundingBoxAscent || m.actualBoundingBoxAscent || size * 0.8;
|
|
8410
8431
|
const descent = m.fontBoundingBoxDescent || m.actualBoundingBoxDescent || size * 0.2;
|
|
8411
|
-
return { width
|
|
8432
|
+
return { width, height: ascent + descent, ascent, descent };
|
|
8412
8433
|
}
|
|
8413
8434
|
fontString(size, style) {
|
|
8414
8435
|
const parts = [];
|
|
@@ -8463,6 +8484,7 @@ class GlyphAtlas {
|
|
|
8463
8484
|
const style = { italic: !!options.italic, bold: !!options.bold, family: options.family };
|
|
8464
8485
|
ctx.save();
|
|
8465
8486
|
ctx.font = this.fontString(options.size, style);
|
|
8487
|
+
applyTracking(ctx, options.letterSpacing);
|
|
8466
8488
|
ctx.textAlign = "left";
|
|
8467
8489
|
ctx.textBaseline = "alphabetic";
|
|
8468
8490
|
if (options.haloColor && options.haloWidth && options.haloWidth > 0) {
|
|
@@ -11552,8 +11574,9 @@ function drawSymbol(ctx, rings, styleLayer, feature, zoom, featureState, glyphAt
|
|
|
11552
11574
|
return !collision.hits({ ...box, priority });
|
|
11553
11575
|
return collision.tryInsert({ ...box, priority });
|
|
11554
11576
|
};
|
|
11555
|
-
const
|
|
11556
|
-
const
|
|
11577
|
+
const letterSpacing = (resolveLayoutExpression(layout?.["text-letter-spacing"], zoom, feature, featureState) ?? 0) * textSize;
|
|
11578
|
+
const textStyle = { italic, bold, family: font.family, letterSpacing };
|
|
11579
|
+
const drawOptions = { color: textColor, haloColor, haloWidth, size: textSize, italic, bold, family: font.family, letterSpacing };
|
|
11557
11580
|
const placeAlongLine = layout?.["symbol-placement"] === "line" || layout?.["symbol-placement"] === "line-center";
|
|
11558
11581
|
if (placeAlongLine && text && feature.type !== 1) {
|
|
11559
11582
|
drawLineLabel(ctx, rings, project, {
|