forked from maplibre/maplibre-gl-js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathglyph_manager.test.ts
154 lines (126 loc) · 6.58 KB
/
glyph_manager.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import {describe, afterEach, test, expect, vi} from 'vitest';
import {parseGlyphPbf} from '../style/parse_glyph_pbf';
import {GlyphManager} from './glyph_manager';
import fs from 'fs';
import {type RequestManager} from '../util/request_manager';
describe('GlyphManager', () => {
const GLYPHS = {};
for (const glyph of parseGlyphPbf(fs.readFileSync('./test/unit/assets/0-255.pbf'))) {
GLYPHS[glyph.id] = glyph;
}
const identityTransform = ((url) => ({url})) as any as RequestManager;
const createLoadGlyphRangeStub = () => {
return vi.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((stack, range, urlTemplate, transform) => {
expect(stack).toBe('Arial Unicode MS');
expect(range).toBe(0);
expect(urlTemplate).toBe('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
expect(transform).toBe(identityTransform);
return Promise.resolve(GLYPHS);
});
};
const createGlyphManager = (font?) => {
const manager = new GlyphManager(identityTransform, font);
manager.setURL('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
return manager;
};
afterEach(() => {
vi.clearAllMocks();
});
test('GlyphManager requests 0-255 PBF', async () => {
createLoadGlyphRangeStub();
const manager = createGlyphManager();
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [55]});
expect(returnedGlyphs['Arial Unicode MS']['55'].metrics.advance).toBe(12);
});
test('GlyphManager doesn\'t request twice 0-255 PBF if a glyph is missing', async () => {
const stub = createLoadGlyphRangeStub();
const manager = createGlyphManager();
await manager.getGlyphs({'Arial Unicode MS': [0.5]});
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);
// We remove all requests as in getGlyphs code.
delete manager.entries['Arial Unicode MS'].requests[0];
await manager.getGlyphs({'Arial Unicode MS': [0.5]});
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);
});
test('GlyphManager requests remote CJK PBF', async () => {
vi.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((_stack, _range, _urlTemplate, _transform) => {
return Promise.resolve(GLYPHS);
});
const manager = createGlyphManager();
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x5e73]});
expect(returnedGlyphs['Arial Unicode MS'][0x5e73]).toBeNull(); // The fixture returns a PBF without the glyph we requested
});
test('GlyphManager does not cache CJK chars that should be rendered locally', async () => {
vi.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((_stack, range, _urlTemplate, _transform) => {
const overlappingGlyphs = {};
const start = range * 256;
const end = start + 256;
for (let i = start, j = 0; i < end; i++, j++) {
overlappingGlyphs[i] = GLYPHS[j];
}
return Promise.resolve(overlappingGlyphs);
});
const manager = createGlyphManager('sans-serif');
//Request char that overlaps Katakana range
let returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x3005]});
expect(returnedGlyphs['Arial Unicode MS'][0x3005]).not.toBeNull();
//Request char from Katakana range (te テ)
returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30C6]});
const glyph = returnedGlyphs['Arial Unicode MS'][0x30c6];
//Ensure that te is locally generated.
expect(glyph.bitmap.height).toBe(12);
expect(glyph.bitmap.width).toBe(12);
});
test('GlyphManager generates CJK PBF locally', async () => {
const manager = createGlyphManager('sans-serif');
// Chinese character píng 平
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x5e73]});
expect(returnedGlyphs['Arial Unicode MS'][0x5e73].metrics.advance).toBe(0.5);
});
test('GlyphManager generates Katakana PBF locally', async () => {
const manager = createGlyphManager('sans-serif');
// Katakana letter te テ
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(returnedGlyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(0.5);
});
test('GlyphManager generates Hiragana PBF locally', async () => {
const manager = createGlyphManager('sans-serif');
//Hiragana letter te て
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x3066]});
expect(returnedGlyphs['Arial Unicode MS'][0x3066].metrics.advance).toBe(0.5);
});
test('GlyphManager consistently generates CJKV text locally', async () => {
const manager = createGlyphManager('sans-serif');
// Space
expect(manager._doesCharSupportLocalGlyph(0x0020)).toBe(false);
// Chinese character píng 平
expect(manager._doesCharSupportLocalGlyph(0x5e73)).toBe(true);
// Chinese character biáng 𰻞
expect(manager._doesCharSupportLocalGlyph(0x30EDE)).toBe(true);
// Katakana letter te テ
expect(manager._doesCharSupportLocalGlyph(0x30c6)).toBe(true);
// Hiragana letter te て
expect(manager._doesCharSupportLocalGlyph(0x3066)).toBe(true);
// Hangul letter a 아
expect(manager._doesCharSupportLocalGlyph(0xC544)).toBe(true);
// Japanese full-width dash ー
expect(manager._doesCharSupportLocalGlyph(0x30FC)).toBe(true);
// Halfwidth and Fullwidth Forms: full-width exclamation !
expect(manager._doesCharSupportLocalGlyph(0xFF01)).toBe(true);
// CJK Symbols and Punctuation: Japanese Post mark 〒
expect(manager._doesCharSupportLocalGlyph(0x3012)).toBe(true);
});
test('GlyphManager caches locally generated glyphs', async () => {
const manager = createGlyphManager('sans-serif');
const drawSpy = GlyphManager.TinySDF.prototype.draw = vi.fn().mockImplementation(() => {
return {data: new Uint8ClampedArray(60 * 60)} as any;
});
// Katakana letter te
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(returnedGlyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(24);
await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(drawSpy).toHaveBeenCalledTimes(1);
});
});