diff --git a/CHANGELOG.md b/CHANGELOG.md index b3da31c264a..c37b1fa21b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Fix 3D map freezing when camera is adjusted against map bounds. ([#4537](https://github.com/maplibre/maplibre-gl-js/issues/4537)) - Fix `getStyle()` to return a clone so the object cannot be internally changed ([#4488](https://github.com/maplibre/maplibre-gl-js/issues/4488)) - 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))) +- 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)) - - _...Add new stuff here..._ ## 4.5.2 diff --git a/src/render/glyph_manager.test.ts b/src/render/glyph_manager.test.ts index 05d07f2fed8..b2aac6aee83 100644 --- a/src/render/glyph_manager.test.ts +++ b/src/render/glyph_manager.test.ts @@ -131,6 +131,13 @@ describe('GlyphManager', () => { expect(manager._doesCharSupportLocalGlyph(0xC544)).toBe(true); }); + test('GlyphManager generates missing PBF locally', async () => { + const manager = createGlyphManager('sans-serif'); + + const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x10e1]}); + expect(returnedGlyphs['Arial Unicode MS'][0x10e1].metrics.advance).toBe(12); + }); + test('GlyphManager caches locally generated glyphs', async () => { const manager = createGlyphManager('sans-serif'); diff --git a/src/render/glyph_manager.ts b/src/render/glyph_manager.ts index 0efe9f066f2..db0f39e5f73 100644 --- a/src/render/glyph_manager.ts +++ b/src/render/glyph_manager.ts @@ -2,6 +2,7 @@ import {loadGlyphRange} from '../style/load_glyph_range'; import TinySDF from '@mapbox/tiny-sdf'; import {AlphaImage} from '../util/image'; +import {warnOnce} from '../util/util'; import type {StyleGlyph} from '../style/style_glyph'; import type {RequestManager} from '../util/request_manager'; @@ -84,7 +85,7 @@ export class GlyphManager { return {stack, id, glyph}; } - glyph = this._tinySDF(entry, stack, id); + glyph = this._tinySDF(entry, stack, id, false); if (glyph) { entry.glyphs[id] = glyph; return {stack, id, glyph}; @@ -108,7 +109,22 @@ export class GlyphManager { entry.requests[range] = promise; } - const response = await entry.requests[range]; + let response; + try { + response = await entry.requests[range]; + } catch (e) { + glyph = this._tinySDF(entry, stack, id, true); + const begin = range * 256; + const end = begin + 255; + const codePoint = id.toString(16).toUpperCase(); + if (glyph) { + warnOnce(`Unable to load glyph range ${range}, ${begin}-${end}. Rendering codepoint U+${codePoint} locally instead. ${e}`); + entry.glyphs[id] = glyph; + return {stack, id, glyph}; + } else { + warnOnce(`Unable to load glyph range ${range}, ${begin}-${end}, or render codepoint U+${codePoint} locally. ${e}`); + } + } for (const id in response) { if (!this._doesCharSupportLocalGlyph(+id)) { entry.glyphs[+id] = response[+id]; @@ -129,13 +145,13 @@ export class GlyphManager { /\p{Ideo}|\p{sc=Hang}|\p{sc=Hira}|\p{sc=Kana}/u.test(String.fromCodePoint(id)); } - _tinySDF(entry: Entry, stack: string, id: number): StyleGlyph { + _tinySDF(entry: Entry, stack: string, id: number, force: boolean): StyleGlyph { const fontFamily = this.localIdeographFontFamily; if (!fontFamily) { return; } - if (!this._doesCharSupportLocalGlyph(id)) { + if (!force && !this._doesCharSupportLocalGlyph(id)) { return; } diff --git a/test/integration/render/tests/text-local-ideographs/cjk/expected-mac.png b/test/integration/render/tests/text-local-glyphs/cjk/expected-mac.png similarity index 100% rename from test/integration/render/tests/text-local-ideographs/cjk/expected-mac.png rename to test/integration/render/tests/text-local-glyphs/cjk/expected-mac.png diff --git a/test/integration/render/tests/text-local-ideographs/cjk/expected-ubuntu.png b/test/integration/render/tests/text-local-glyphs/cjk/expected-ubuntu.png similarity index 100% rename from test/integration/render/tests/text-local-ideographs/cjk/expected-ubuntu.png rename to test/integration/render/tests/text-local-glyphs/cjk/expected-ubuntu.png diff --git a/test/integration/render/tests/text-local-ideographs/cjk/expected-windows.png b/test/integration/render/tests/text-local-glyphs/cjk/expected-windows.png similarity index 100% rename from test/integration/render/tests/text-local-ideographs/cjk/expected-windows.png rename to test/integration/render/tests/text-local-glyphs/cjk/expected-windows.png diff --git a/test/integration/render/tests/text-local-ideographs/cjk/style.json b/test/integration/render/tests/text-local-glyphs/cjk/style.json similarity index 100% rename from test/integration/render/tests/text-local-ideographs/cjk/style.json rename to test/integration/render/tests/text-local-glyphs/cjk/style.json diff --git a/test/integration/render/tests/text-local-glyphs/missing/expected-mac.png b/test/integration/render/tests/text-local-glyphs/missing/expected-mac.png new file mode 100644 index 00000000000..6a6bbfc88d5 Binary files /dev/null and b/test/integration/render/tests/text-local-glyphs/missing/expected-mac.png differ diff --git a/test/integration/render/tests/text-local-glyphs/missing/expected-ubuntu.png b/test/integration/render/tests/text-local-glyphs/missing/expected-ubuntu.png new file mode 100644 index 00000000000..a59e6a550ce Binary files /dev/null and b/test/integration/render/tests/text-local-glyphs/missing/expected-ubuntu.png differ diff --git a/test/integration/render/tests/text-local-glyphs/missing/expected-windows.png b/test/integration/render/tests/text-local-glyphs/missing/expected-windows.png new file mode 100644 index 00000000000..7b82bd12aea Binary files /dev/null and b/test/integration/render/tests/text-local-glyphs/missing/expected-windows.png differ diff --git a/test/integration/render/tests/text-local-glyphs/missing/style.json b/test/integration/render/tests/text-local-glyphs/missing/style.json new file mode 100644 index 00000000000..99081eaca4d --- /dev/null +++ b/test/integration/render/tests/text-local-glyphs/missing/style.json @@ -0,0 +1,43 @@ +{ + "version": 8, + "metadata": { + "test": { + "pixelRatio": 2, + "localIdeographFontFamily": "sans-serif", + "width": 800, + "height": 600 + } + }, + "zoom": 8, + "sources": { + "sample": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name_en": "Georgia", + "name_ka": "საქართველო" + } + } + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "sample-text", + "type": "symbol", + "source": "sample", + "layout": { + "text-anchor": "top", + "text-field": "{name_en} ({name_ka})", + "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"], + "text-size": 30, + "text-offset": [0, 0] + } + } + ] +}