font/sfnt: support cmap format 12.
Change-Id: I2791f5aec860bbb16c6c6945703827afd55c11dc
Reviewed-on: https://go-review.googlesource.com/36291
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/cmap.go b/font/sfnt/cmap.go
index be302d2..3adda03 100644
--- a/font/sfnt/cmap.go
+++ b/font/sfnt/cmap.go
@@ -80,7 +80,7 @@
case 4:
return true
case 12:
- // TODO: implement.
+ return true
}
return false
}
@@ -92,7 +92,7 @@
case 4:
return f.makeCachedGlyphIndexFormat4(buf, offset, length)
case 12:
- // TODO: implement, including a cmapEntry32 type (32, not 16).
+ return f.makeCachedGlyphIndexFormat12(buf, offset, length)
}
panic("unreachable")
}
@@ -196,6 +196,68 @@
return buf, nil
}
+func (f *Font) makeCachedGlyphIndexFormat12(buf []byte, offset, _ uint32) ([]byte, error) {
+ const headerSize = 16
+ if offset+headerSize > f.cmap.length {
+ return nil, errInvalidCmapTable
+ }
+ var err error
+ buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
+ if err != nil {
+ return nil, err
+ }
+ offset += headerSize
+
+ length := u32(buf[4:])
+ numGroups := u32(buf[12:])
+ if f.cmap.length < offset || length > f.cmap.length-offset {
+ return nil, errInvalidCmapTable
+ }
+ if numGroups > maxCmapSegments {
+ return nil, errUnsupportedNumberOfCmapSegments
+ }
+
+ eLength := 12 * numGroups
+ if headerSize+eLength != length {
+ return nil, errInvalidCmapTable
+ }
+ buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
+ if err != nil {
+ return nil, err
+ }
+ offset += eLength
+
+ entries := make([]cmapEntry32, numGroups)
+ for i := range entries {
+ entries[i] = cmapEntry32{
+ start: u32(buf[0+12*i:]),
+ end: u32(buf[4+12*i:]),
+ delta: u32(buf[8+12*i:]),
+ }
+ }
+
+ f.cached.glyphIndex = func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
+ c := uint32(r)
+ for i, j := 0, len(entries); i < j; {
+ h := i + (j-i)/2
+ entry := &entries[h]
+ if c < entry.start {
+ j = h
+ } else if entry.end < c {
+ i = h + 1
+ } else {
+ return GlyphIndex(c - entry.start + entry.delta), nil
+ }
+ }
+ return 0, nil
+ }
+ return buf, nil
+}
+
type cmapEntry16 struct {
end, start, delta, offset uint16
}
+
+type cmapEntry32 struct {
+ start, end, delta uint32
+}
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index f1c6ac1..c68d78c 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -94,7 +94,7 @@
t.Fatal(err)
}
- for _, format := range []int{-1, 0, 4} {
+ for _, format := range []int{-1, 0, 4, 12} {
testGlyphIndex(t, data, format)
}
}
@@ -158,26 +158,26 @@
{'\u4e2d', 12},
{'\u4e2e', 0},
- /*
- TODO: support runes above U+FFFF, i.e. cmap format 12.
+ {'\U0001f0a0', 0},
+ {'\U0001f0a1', 13},
+ {'\U0001f0a2', 0},
- {'\U0001f0a0', 0},
- {'\U0001f0a1', 13},
- {'\U0001f0a2', 0},
-
- {'\U0001f0b0', 0},
- {'\U0001f0b1', 14},
- {'\U0001f0b2', 15},
- {'\U0001f0b3', 0},
- */
+ {'\U0001f0b0', 0},
+ {'\U0001f0b1', 14},
+ {'\U0001f0b2', 15},
+ {'\U0001f0b3', 0},
}
var b Buffer
for _, tc := range testCases {
want := tc.want
- // cmap format 0, with the Macintosh Roman encoding, can only represent
- // a limited set of non-ASCII runes, e.g. U+00FF.
- if cmapFormat == 0 && tc.r > '\u007f' && tc.r != '\u00ff' {
+ switch {
+ case cmapFormat == 0 && tc.r > '\u007f' && tc.r != '\u00ff':
+ // cmap format 0, with the Macintosh Roman encoding, can only
+ // represent a limited set of non-ASCII runes, e.g. U+00FF.
+ want = 0
+ case cmapFormat == 4 && tc.r > '\uffff':
+ // cmap format 4 only supports the Basic Multilingual Plane (BMP).
want = 0
}