font/sfnt: support non-zero offsets in format-4 cmap tables.
Change-Id: I52592fcde96ce2f3b7700a29169b517a813f9f3c
Reviewed-on: https://go-review.googlesource.com/36371
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/cmap.go b/font/sfnt/cmap.go
index 9db98a8..e6e114e 100644
--- a/font/sfnt/cmap.go
+++ b/font/sfnt/cmap.go
@@ -167,6 +167,8 @@
offset: u16(buf[6*len(entries)+2+2*i:]),
}
}
+ indexesBase := f.cmap.offset + offset
+ indexesLength := f.cmap.length - offset
f.cached.glyphIndex = func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
if uint32(r) > 0xffff {
@@ -184,11 +186,15 @@
} else if entry.offset == 0 {
return GlyphIndex(c + entry.delta), nil
} else {
- // TODO: support the glyphIdArray as per
- // https://www.microsoft.com/typography/OTSPEC/cmap.htm
- //
- // This will probably use the *Font and *Buffer arguments.
- return 0, errUnsupportedCmapFormat
+ offset := uint32(entry.offset) + 2*uint32(h-len(entries)+int(c-entry.start))
+ if offset > indexesLength || offset+2 > indexesLength {
+ return 0, errInvalidCmapTable
+ }
+ x, err := b.view(&f.src, int(indexesBase+offset), 2)
+ if err != nil {
+ return 0, err
+ }
+ return GlyphIndex(u16(x)), nil
}
}
return 0, nil
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 61281e7..b7f4bdc 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -73,7 +73,6 @@
errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version")
errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings")
- errUnsupportedCmapFormat = errors.New("sfnt: unsupported cmap format")
errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph")
errUnsupportedGlyphDataLength = errors.New("sfnt: unsupported glyph data length")
errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding")
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index c68d78c..c6d6741 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -88,6 +88,62 @@
}
}
+func TestGoRegularGlyphIndex(t *testing.T) {
+ f, err := Parse(goregular.TTF)
+ if err != nil {
+ t.Fatalf("Parse: %v", err)
+ }
+
+ testCases := []struct {
+ r rune
+ want GlyphIndex
+ }{
+ // Glyphs that aren't present in Go Regular.
+ {'\u001f', 0}, // U+001F <control>
+ {'\u0200', 0}, // U+0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+ {'\u2000', 0}, // U+2000 EN QUAD
+
+ // The want values below can be verified by running the ttx tool on
+ // Go-Regular.ttf.
+ //
+ // The actual values are ad hoc, and result from whatever tools the
+ // Bigelow & Holmes type foundry used and the order in which they
+ // crafted the glyphs. They may change over time as newer versions of
+ // the font are released. In practice, though, running this test with
+ // coverage analysis suggests that it covers both the zero and non-zero
+ // cmapEntry16.offset cases for a format-4 cmap table.
+
+ {'\u0020', 3}, // U+0020 SPACE
+ {'\u0021', 4}, // U+0021 EXCLAMATION MARK
+ {'\u0022', 5}, // U+0022 QUOTATION MARK
+ {'\u0023', 6}, // U+0023 NUMBER SIGN
+ {'\u0024', 223}, // U+0024 DOLLAR SIGN
+ {'\u0025', 7}, // U+0025 PERCENT SIGN
+ {'\u0026', 8}, // U+0026 AMPERSAND
+ {'\u0027', 9}, // U+0027 APOSTROPHE
+
+ {'\u03bd', 423}, // U+03BD GREEK SMALL LETTER NU
+ {'\u03be', 424}, // U+03BE GREEK SMALL LETTER XI
+ {'\u03bf', 438}, // U+03BF GREEK SMALL LETTER OMICRON
+ {'\u03c0', 208}, // U+03C0 GREEK SMALL LETTER PI
+ {'\u03c1', 425}, // U+03C1 GREEK SMALL LETTER RHO
+ {'\u03c2', 426}, // U+03C2 GREEK SMALL LETTER FINAL SIGMA
+ }
+
+ var b Buffer
+ for _, tc := range testCases {
+ got, err := f.GlyphIndex(&b, tc.r)
+ if err != nil {
+ t.Errorf("r=%q: %v", tc.r, err)
+ continue
+ }
+ if got != tc.want {
+ t.Errorf("r=%q: got %d, want %d", tc.r, got, tc.want)
+ continue
+ }
+ }
+}
+
func TestGlyphIndex(t *testing.T) {
data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/cmapTest.ttf"))
if err != nil {