Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions font/glyphs.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ const maxCompositeNesting = 20 // protect against malicious fonts

// use the `glyf` table to fetch the contour points,
// applying variation if needed.
// for composite, recursively calls itself; allPoints includes phantom points and will be at least of length 4
// for composite, recursively calls itself; allPoints includes phantom points and is
// always at least of length phantomCount, even for a missing, too-deeply-nested or
// otherwise malformed glyph (in which case an empty-but-valid outline is produced).
func (f *Face) getPointsForGlyphRec(gid tables.GlyphID, currentDepth int, currentGlyphs glyphSet, allPoints *[]contourPoint /* OUT */) {
// adapted from harfbuzz/src/OT/glyf/Glyph.hh

if currentDepth > maxCompositeNesting || int(gid) >= len(f.glyf) {
// The glyph is missing or too deeply nested; still contribute the
// phantom points so the invariant documented above holds and callers
// never see a slice shorter than phantomCount.
*allPoints = append(*allPoints, make([]contourPoint, phantomCount)...)
return
}

Expand Down Expand Up @@ -104,11 +110,9 @@ func (f *Face) getPointsForGlyphRec(gid tables.GlyphID, currentDepth int, curren

f.getPointsForGlyphRec(item.GlyphIndex, currentDepth+1, currentGlyphs, &compPoints)

// getPointsForGlyphRec guarantees at least the phantom points, so a
// component always contributes a well-formed slice here.
LC := len(compPoints)
if LC < phantomCount { // in case of max depth reached
delete(currentGlyphs, item.GlyphIndex)
return
}

/* Copy phantom points from component if USE_MY_METRICS flag set */
if item.HasUseMyMetrics() {
Expand Down
22 changes: 22 additions & 0 deletions font/renderer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,25 @@ func TestColorGlyphs(t *testing.T) {
_, ok = face.GlyphDataColor(0)
tu.Assert(t, ok)
}

// A composite glyph whose component references an out-of-range (or otherwise
// unresolvable) glyph produces fewer contour points than the phantom points
// appended for a well-formed glyph. The glyph data accessors must treat this
// as an empty glyph rather than panicking with a negative slice bound.
func TestGlyphDataMalformedComposite(t *testing.T) {
f := &Font{
glyf: tables.Glyf{
// glyph 0: composite referencing the out-of-range component 5
{Data: tables.CompositeGlyph{Glyphs: []tables.CompositeGlyphPart{{GlyphIndex: 5}}}},
},
}
face := Face{Font: f}

// must not panic, and yields an empty outline
out, _ := face.GlyphDataOutline(0)
tu.Assert(t, len(out.Segments) == 0)

if gd, ok := face.GlyphData(0).(GlyphOutline); ok {
tu.Assert(t, len(gd.Segments) == 0)
}
}
Loading