Skip to content

Fix CBDT emoji font crash and COLR v1/composite glyph crashes#366

Open
jspears wants to merge 8 commits intofoliojs:masterfrom
jspears:fix-cbdt-emoji-font-crash
Open

Fix CBDT emoji font crash and COLR v1/composite glyph crashes#366
jspears wants to merge 8 commits intofoliojs:masterfrom
jspears:fix-cbdt-emoji-font-crash

Conversation

@jspears
Copy link

@jspears jspears commented Feb 24, 2026

Summary

Fixes three crash scenarios when working with color emoji fonts:

1. CBDT emoji font crash (NotoColorEmoji)

Fonts using CBDT color bitmaps instead of glyf/CFF outlines cause getGlyph() to return null, crashing layout() with "Cannot read properties of null".

Fix: Wrapped layout() in try-catch with fallback that extracts CBDT bitmaps directly via _cmapProcessor.lookup() and renders as square images.

2. COLR v1 null baseGlyphRecord crash

COLR v1 fonts (e.g. OpenMoji COLRv1) use paint-based records instead of v0 baseGlyphRecord. The COLRGlyph.layers getter crashes with:

TypeError: Cannot read properties of null (reading 'length')

at colr.baseGlyphRecord.length because baseGlyphRecord is null in v1 fonts.

Fix: Added null guard in COLRGlyph.layers — returns null when baseGlyphRecord is missing, allowing callers to fall back gracefully.

Repro: fontkit.openSync('OpenMoji-color-colr1_svg.ttf').glyphsForString('😀')[0].layers

3. Composite glyph _getContours crash in COLR fonts

In COLR fonts (e.g. OpenMoji COLRv0), composite TTF glyphs reference component glyph IDs via getGlyph(). Since the font has COLR+CPAL tables, getGlyph() returns a COLRGlyph — but COLRGlyph doesn't have _getContours(), crashing with:

TypeError: this._font.getGlyph(...)._getContours is not a function

Fix: In TTFGlyph._getContours(), use _getBaseGlyph() to get the TTF outline for composite component resolution. Skip components that lack _getContours.

Repro: fontkit.openSync('OpenMoji-color-glyf_colr_0.ttf').layout('😀🚀✅')

Changes

File Change
src/glyph/COLRGlyph.js Null guard for baseGlyphRecord in layers getter
src/glyph/TTFGlyph.js Use _getBaseGlyph() for composite component resolution
test/glyphs.js 5 new test cases for COLR v0 and v1
test/data/OpenMoji/ Added COLR v0 test font

Testing

All 568 tests pass (5 new). The only pre-existing failure is an unrelated i18n test (fontkit.setDefaultLanguage).

Agent-Id: agent-9256c1ef-d67c-4777-af65-729a56806d97
Linked-Note-Id: e547533f-3492-4e79-b31b-f63b18b084e0
Agent-Id: agent-035b8bf0-d70e-4c95-ac0d-1b3dad51c12e
Linked-Note-Id: 5b8e8269-583a-4cfd-910b-007d58db224b
Agent-Id: agent-6f8ac66c-00ba-478e-acc3-163077223271
Linked-Note-Id: 02ec8fd8-557c-40a6-afa7-2f5111c133c5
…Apple Emoji

Adds test fonts and test cases to verify PR foliojs#366 (CBDT glyph support)
works with multiple emoji font formats:
- OpenMoji CBDT (CBDT/CBLC tables) - tests the new CBDTGlyph code path
- OpenMoji COLRv1+SVG (COLR/CPAL tables) - tests COLR glyph handling
- Twemoji Mozilla (COLR/CPAL tables) - tests COLR glyph handling
- Apple emoji (SBIX table) - tests existing SBIX support

Tests cover: basic glyph creation, layout, advance width, ZWJ sequences,
skin tone modifiers, and flag sequences. All 563 tests pass.

Agent-Id: agent-45802c07-04d0-4e6c-80db-653eaa3c7b6f
Test data for emoji font compatibility tests:
- OpenMoji-color-cbdt.ttf (CBDT/CBLC bitmap emoji)
- OpenMoji-color-colr1_svg.ttf (COLRv1 + SVG emoji)
- Twemoji.Mozilla.ttf (COLR/CPAL emoji)

Agent-Id: agent-45802c07-04d0-4e6c-80db-653eaa3c7b6f
Two fixes for COLR emoji font handling:

1. COLRGlyph.layers getter: COLR v1 fonts (e.g. OpenMoji COLRv1) use
   paint-based records instead of v0 baseGlyphRecord. fontkit only
   supports v0, so the layers getter now returns null instead of
   crashing with:
     TypeError: Cannot read properties of null (reading 'length')

2. TTFGlyph._getContours: When a composite TTF glyph references a
   component glyph ID that getGlyph() resolves as a COLRGlyph,
   calling _getContours() crashes because COLRGlyph doesn't have
   that method. Fixed by using _getBaseGlyph() to get the TTF outline
   for component resolution, skipping components that lack _getContours.
     TypeError: this._font.getGlyph(...)._getContours is not a function

Added test cases for both issues using OpenMoji COLR v0 and v1 fonts.
@jspears jspears changed the title Fix cbdt emoji font crash Fix CBDT emoji font crash and COLR v1/composite glyph crashes Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant