font: have Glyph return !ok for U+FFFD substitute

The other return values may still be non-zero, but this lets callers
identify when substitution happens.

"TODO: is falling back on the U+FFFD glyph the responsibility of the
Drawer or the Face?" was resolved. The answer is "the Face". For
kerning, the previous rune is unchanged (and not set to U+FFFD).

This also fixes an inconsistency in the basicfont.Face implementation,
where GlyphAdvance and GlyphBounds would unconditionally return a
non-zero advance, but Glyph could return a zero advance when the Face
doesn't have a U+FFFD entry.

Fixes golang/go#58252

Change-Id: Ie97e68e1d5e2efd13c9e84ad12db4495d83a5ca3
Reviewed-on: https://go-review.googlesource.com/c/image/+/474376
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Nigel Tao (INACTIVE; USE @golang.org INSTEAD) <nigeltao@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Nigel Tao <nigeltao@golang.org>
diff --git a/font/basicfont/basicfont.go b/font/basicfont/basicfont.go
index 1550381..173c061 100644
--- a/font/basicfont/basicfont.go
+++ b/font/basicfont/basicfont.go
@@ -89,41 +89,50 @@
 func (f *Face) Glyph(dot fixed.Point26_6, r rune) (
 	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
 
-loop:
-	for _, rr := range [2]rune{r, '\ufffd'} {
-		for _, rng := range f.Ranges {
-			if rr < rng.Low || rng.High <= rr {
-				continue
-			}
-			maskp.Y = (int(rr-rng.Low) + rng.Offset) * (f.Ascent + f.Descent)
-			ok = true
-			break loop
+	if found, rng := f.find(r); rng != nil {
+		maskp.Y = (int(found-rng.Low) + rng.Offset) * (f.Ascent + f.Descent)
+		x := int(dot.X+32)>>6 + f.Left
+		y := int(dot.Y+32) >> 6
+		dr = image.Rectangle{
+			Min: image.Point{
+				X: x,
+				Y: y - f.Ascent,
+			},
+			Max: image.Point{
+				X: x + f.Width,
+				Y: y + f.Descent,
+			},
 		}
-	}
-	if !ok {
-		return image.Rectangle{}, nil, image.Point{}, 0, false
-	}
 
-	x := int(dot.X+32)>>6 + f.Left
-	y := int(dot.Y+32) >> 6
-	dr = image.Rectangle{
-		Min: image.Point{
-			X: x,
-			Y: y - f.Ascent,
-		},
-		Max: image.Point{
-			X: x + f.Width,
-			Y: y + f.Descent,
-		},
+		return dr, f.Mask, maskp, fixed.I(f.Advance), r == found
 	}
-
-	return dr, f.Mask, maskp, fixed.I(f.Advance), true
+	return image.Rectangle{}, nil, image.Point{}, 0, false
 }
 
 func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
-	return fixed.R(0, -f.Ascent, f.Width, +f.Descent), fixed.I(f.Advance), true
+	if found, rng := f.find(r); rng != nil {
+		return fixed.R(0, -f.Ascent, f.Width, +f.Descent), fixed.I(f.Advance), r == found
+	}
+	return fixed.Rectangle26_6{}, 0, false
 }
 
 func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
-	return fixed.I(f.Advance), true
+	if found, rng := f.find(r); rng != nil {
+		return fixed.I(f.Advance), r == found
+	}
+	return 0, false
+}
+
+func (f *Face) find(r rune) (rune, *Range) {
+	for {
+		for i, rng := range f.Ranges {
+			if (rng.Low <= r) && (r < rng.High) {
+				return r, &f.Ranges[i]
+			}
+		}
+		if r == '\ufffd' {
+			return 0, nil
+		}
+		r = '\ufffd'
+	}
 }
diff --git a/font/font.go b/font/font.go
index d1a7535..6b9b9bc 100644
--- a/font/font.go
+++ b/font/font.go
@@ -38,7 +38,10 @@
 	// glyph at the sub-pixel destination location dot, and that glyph's
 	// advance width.
 	//
-	// It returns !ok if the face does not contain a glyph for r.
+	// It returns !ok if the face does not contain a glyph for r. This includes
+	// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
+	// or OpenType's .notdef glyph), in which case the other return values may
+	// still be non-zero.
 	//
 	// The contents of the mask image returned by one Glyph call may change
 	// after the next Glyph call. Callers that want to cache the mask must make
@@ -49,7 +52,10 @@
 	// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
 	// to the origin, and that glyph's advance width.
 	//
-	// It returns !ok if the face does not contain a glyph for r.
+	// It returns !ok if the face does not contain a glyph for r. This includes
+	// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
+	// or OpenType's .notdef glyph), in which case the other return values may
+	// still be non-zero.
 	//
 	// The glyph's ascent and descent are equal to -bounds.Min.Y and
 	// +bounds.Max.Y. The glyph's left-side and right-side bearings are equal
@@ -60,7 +66,10 @@
 
 	// GlyphAdvance returns the advance width of r's glyph.
 	//
-	// It returns !ok if the face does not contain a glyph for r.
+	// It returns !ok if the face does not contain a glyph for r. This includes
+	// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
+	// or OpenType's .notdef glyph), in which case the other return values may
+	// still be non-zero.
 	GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
 
 	// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
@@ -150,14 +159,10 @@
 		if prevC >= 0 {
 			d.Dot.X += d.Face.Kern(prevC, c)
 		}
-		dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
+		dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
+		if !dr.Empty() {
+			draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
 		}
-		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
 		d.Dot.X += advance
 		prevC = c
 	}
@@ -170,14 +175,10 @@
 		if prevC >= 0 {
 			d.Dot.X += d.Face.Kern(prevC, c)
 		}
-		dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
+		dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
+		if !dr.Empty() {
+			draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
 		}
-		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
 		d.Dot.X += advance
 		prevC = c
 	}
@@ -227,16 +228,12 @@
 		if prevC >= 0 {
 			advance += f.Kern(prevC, c)
 		}
-		b, a, ok := f.GlyphBounds(c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
+		b, a, _ := f.GlyphBounds(c)
+		if !b.Empty() {
+			b.Min.X += advance
+			b.Max.X += advance
+			bounds = bounds.Union(b)
 		}
-		b.Min.X += advance
-		b.Max.X += advance
-		bounds = bounds.Union(b)
 		advance += a
 		prevC = c
 	}
@@ -251,16 +248,12 @@
 		if prevC >= 0 {
 			advance += f.Kern(prevC, c)
 		}
-		b, a, ok := f.GlyphBounds(c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
+		b, a, _ := f.GlyphBounds(c)
+		if !b.Empty() {
+			b.Min.X += advance
+			b.Max.X += advance
+			bounds = bounds.Union(b)
 		}
-		b.Min.X += advance
-		b.Max.X += advance
-		bounds = bounds.Union(b)
 		advance += a
 		prevC = c
 	}
@@ -278,13 +271,7 @@
 		if prevC >= 0 {
 			advance += f.Kern(prevC, c)
 		}
-		a, ok := f.GlyphAdvance(c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
-		}
+		a, _ := f.GlyphAdvance(c)
 		advance += a
 		prevC = c
 	}
@@ -298,13 +285,7 @@
 		if prevC >= 0 {
 			advance += f.Kern(prevC, c)
 		}
-		a, ok := f.GlyphAdvance(c)
-		if !ok {
-			// TODO: is falling back on the U+FFFD glyph the responsibility of
-			// the Drawer or the Face?
-			// TODO: set prevC = '\ufffd'?
-			continue
-		}
+		a, _ := f.GlyphAdvance(c)
 		advance += a
 		prevC = c
 	}
diff --git a/font/opentype/opentype.go b/font/opentype/opentype.go
index 231fdbe..694ac47 100644
--- a/font/opentype/opentype.go
+++ b/font/opentype/opentype.go
@@ -133,8 +133,8 @@
 
 // Kern satisfies the font.Face interface.
 func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 {
-	x0 := f.index(r0)
-	x1 := f.index(r1)
+	x0, _ := f.f.GlyphIndex(&f.buf, r0)
+	x1, _ := f.f.GlyphIndex(&f.buf, r1)
 	k, err := f.f.Kern(&f.buf, x0, x1, fixed.Int26_6(f.f.UnitsPerEm()), f.hinting)
 	if err != nil {
 		return 0
@@ -251,22 +251,19 @@
 	}
 	f.rast.Draw(&f.mask, f.mask.Bounds(), image.Opaque, image.Point{})
 
-	return dr, &f.mask, f.mask.Rect.Min, advance, true
+	return dr, &f.mask, f.mask.Rect.Min, advance, x != 0
 }
 
 // GlyphBounds satisfies the font.Face interface.
 func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
-	bounds, advance, err := f.f.GlyphBounds(&f.buf, f.index(r), f.scale, f.hinting)
-	return bounds, advance, err == nil
+	x, _ := f.f.GlyphIndex(&f.buf, r)
+	bounds, advance, err := f.f.GlyphBounds(&f.buf, x, f.scale, f.hinting)
+	return bounds, advance, (err == nil) && (x != 0)
 }
 
 // GlyphAdvance satisfies the font.Face interface.
 func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
-	advance, err := f.f.GlyphAdvance(&f.buf, f.index(r), f.scale, f.hinting)
-	return advance, err == nil
-}
-
-func (f *Face) index(r rune) sfnt.GlyphIndex {
 	x, _ := f.f.GlyphIndex(&f.buf, r)
-	return x
+	advance, err := f.f.GlyphAdvance(&f.buf, x, f.scale, f.hinting)
+	return advance, (err == nil) && (x != 0)
 }