Skip to content

Commit 96f830f

Browse files
committed
Fall back to local glyph rendering if glyph PBF is unavailable
1 parent ae25dd5 commit 96f830f

File tree

8 files changed

+57
-64
lines changed

8 files changed

+57
-64
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
- Fix 3D map freezing when camera is adjusted against map bounds. ([#4537](https://github.com/maplibre/maplibre-gl-js/issues/4537))
1313
- Fix `getStyle()` to return a clone so the object cannot be internally changed ([#4488](https://github.com/maplibre/maplibre-gl-js/issues/4488))
1414
- Prefer local glyph rendering for all CJKV characters, not just those in the CJK Unified Ideographs, Hiragana, Katakana, and Hangul Syllables blocks. ([#4560](https://github.com/maplibre/maplibre-gl-js/pull/4560)))
15-
- Fix crash on missing glyph PBF.
15+
- If a glyph PBF is unavailable or lacks a glyph for a character in a `text-field`, try to render it locally instead of crashing. ([#4564](https://github.com/maplibre/maplibre-gl-js/pull/4564))
1616
- - _...Add new stuff here..._
1717

1818
## 4.5.2

src/render/glyph_manager.test.ts

+7
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ describe('GlyphManager', () => {
131131
expect(manager._doesCharSupportLocalGlyph(0xC544)).toBe(true);
132132
});
133133

134+
test('GlyphManager generates missing PBF locally', async () => {
135+
const manager = createGlyphManager('sans-serif');
136+
137+
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x10e1]});
138+
expect(returnedGlyphs['Arial Unicode MS'][0x10e1].metrics.advance).toBe(12);
139+
});
140+
134141
test('GlyphManager caches locally generated glyphs', async () => {
135142

136143
const manager = createGlyphManager('sans-serif');

src/render/glyph_manager.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class GlyphManager {
8484
return {stack, id, glyph};
8585
}
8686

87-
glyph = this._tinySDF(entry, stack, id);
87+
glyph = this._tinySDF(entry, stack, id, false);
8888
if (glyph) {
8989
entry.glyphs[id] = glyph;
9090
return {stack, id, glyph};
@@ -108,7 +108,22 @@ export class GlyphManager {
108108
entry.requests[range] = promise;
109109
}
110110

111-
const response = await entry.requests[range];
111+
let response;
112+
try {
113+
response = await entry.requests[range];
114+
} catch (e) {
115+
glyph = this._tinySDF(entry, stack, id, true);
116+
const begin = range * 256;
117+
const end = begin + 255;
118+
const codePoint = id.toString(16).toUpperCase();
119+
if (glyph) {
120+
console.log(`Unable to load glyph range ${range}, ${begin}-${end}. Rendering codepoint U+${codePoint} locally instead.`);
121+
entry.glyphs[id] = glyph;
122+
return {stack, id, glyph};
123+
} else {
124+
console.warn(`Unable to load glyph range ${range}, ${begin}-${end}, or render codepoint U+${codePoint} locally.`);
125+
}
126+
}
112127
for (const id in response) {
113128
if (!this._doesCharSupportLocalGlyph(+id)) {
114129
entry.glyphs[+id] = response[+id];
@@ -129,13 +144,13 @@ export class GlyphManager {
129144
/\p{Ideo}|\p{sc=Hang}|\p{sc=Hira}|\p{sc=Kana}/u.test(String.fromCodePoint(id));
130145
}
131146

132-
_tinySDF(entry: Entry, stack: string, id: number): StyleGlyph {
147+
_tinySDF(entry: Entry, stack: string, id: number, force: boolean): StyleGlyph {
133148
const fontFamily = this.localIdeographFontFamily;
134149
if (!fontFamily) {
135150
return;
136151
}
137152

138-
if (!this._doesCharSupportLocalGlyph(id)) {
153+
if (!force && !this._doesCharSupportLocalGlyph(id)) {
139154
return;
140155
}
141156

src/style/load_glyph_range.test.ts

+24-49
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {loadGlyphRange} from './load_glyph_range';
55
import {fakeServer} from 'nise';
66
import {bufferToArrayBuffer} from '../util/test/util';
77

8-
describe('loadGlyphRange', () => {
8+
test('loadGlyphRange', async () => {
99
global.fetch = null;
1010

1111
const transform = jest.fn().mockImplementation((url) => {
@@ -14,52 +14,27 @@ describe('loadGlyphRange', () => {
1414

1515
const manager = new RequestManager(transform);
1616

17-
afterEach(() => {
18-
jest.clearAllMocks();
19-
});
20-
21-
test('requests and receives a glyph range', async () => {
22-
const server = fakeServer.create();
23-
server.respondWith(bufferToArrayBuffer(fs.readFileSync(path.join(__dirname, '../../test/unit/assets/0-255.pbf'))));
24-
25-
const promise = loadGlyphRange('Arial Unicode MS', 0, 'https://localhost/fonts/v1/{fontstack}/{range}.pbf', manager);
26-
server.respond();
27-
const result = await promise;
28-
29-
expect(transform).toHaveBeenCalledTimes(1);
30-
expect(transform).toHaveBeenCalledWith('https://localhost/fonts/v1/Arial Unicode MS/0-255.pbf', 'Glyphs');
31-
32-
expect(Object.keys(result)).toHaveLength(223);
33-
for (const key in result) {
34-
const id = Number(key);
35-
const glyph = result[id];
36-
37-
expect(glyph.id).toBe(Number(id));
38-
expect(glyph.metrics).toBeTruthy();
39-
expect(typeof glyph.metrics.width).toBe('number');
40-
expect(typeof glyph.metrics.height).toBe('number');
41-
expect(typeof glyph.metrics.top).toBe('number');
42-
expect(typeof glyph.metrics.advance).toBe('number');
43-
}
44-
expect(server.requests[0].url).toBe('https://localhost/fonts/v1/Arial Unicode MS/0-255.pbf');
45-
});
46-
47-
test('warns on missing glyph range', async () => {
48-
jest.spyOn(console, 'warn').mockImplementation(() => { });
49-
50-
const server = fakeServer.create();
51-
52-
const promise = loadGlyphRange('Arial Unicode MS', 2, 'https://localhost/fonts/v1/{fontstack}/{range}.pbf', manager);
53-
server.respond();
54-
expect(async () => {
55-
const result = await promise;
56-
expect(console.warn).toHaveBeenCalledTimes(1);
57-
expect(Object.keys(result)).toHaveLength(0);
58-
}).not.toThrow();
59-
60-
expect(transform).toHaveBeenCalledTimes(1);
61-
expect(transform).toHaveBeenCalledWith('https://localhost/fonts/v1/Arial Unicode MS/512-767.pbf', 'Glyphs');
62-
63-
expect(server.requests[0].url).toBe('https://localhost/fonts/v1/Arial Unicode MS/512-767.pbf');
64-
});
17+
const server = fakeServer.create();
18+
server.respondWith(bufferToArrayBuffer(fs.readFileSync(path.join(__dirname, '../../test/unit/assets/0-255.pbf'))));
19+
20+
const promise = loadGlyphRange('Arial Unicode MS', 0, 'https://localhost/fonts/v1/{fontstack}/{range}.pbf', manager);
21+
server.respond();
22+
const result = await promise;
23+
24+
expect(transform).toHaveBeenCalledTimes(1);
25+
expect(transform).toHaveBeenCalledWith('https://localhost/fonts/v1/Arial Unicode MS/0-255.pbf', 'Glyphs');
26+
27+
expect(Object.keys(result)).toHaveLength(223);
28+
for (const key in result) {
29+
const id = Number(key);
30+
const glyph = result[id];
31+
32+
expect(glyph.id).toBe(Number(id));
33+
expect(glyph.metrics).toBeTruthy();
34+
expect(typeof glyph.metrics.width).toBe('number');
35+
expect(typeof glyph.metrics.height).toBe('number');
36+
expect(typeof glyph.metrics.top).toBe('number');
37+
expect(typeof glyph.metrics.advance).toBe('number');
38+
}
39+
expect(server.requests[0].url).toBe('https://localhost/fonts/v1/Arial Unicode MS/0-255.pbf');
6540
});

src/style/load_glyph_range.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,14 @@ export async function loadGlyphRange(fontstack: string,
1818
ResourceType.Glyphs
1919
);
2020

21+
const response = await getArrayBuffer(request, new AbortController());
22+
if (!response || !response.data) {
23+
throw new Error(`Could not load glyph range. range: ${range}, ${begin}-${end}`);
24+
}
2125
const glyphs = {};
22-
try {
23-
const response = await getArrayBuffer(request, new AbortController());
24-
if (!response || !response.data) {
25-
throw new Error(`Could not load glyph range. range: ${range}, ${begin}-${end}`);
26-
}
2726

28-
for (const glyph of parseGlyphPbf(response.data)) {
29-
glyphs[glyph.id] = glyph;
30-
}
31-
} catch (e) {
32-
console.warn(`Could not load glyph range. range: ${range}, ${begin}-${end}`, e);
27+
for (const glyph of parseGlyphPbf(response.data)) {
28+
glyphs[glyph.id] = glyph;
3329
}
3430

3531
return glyphs;
Loading
Loading
Loading

0 commit comments

Comments
 (0)