font/sfnt: support parsing legacy fonts with OS2 table version <= 1

Library assumes that OS/2 header size is at least 96 bytes,
which is not the case for fonts with OS/2 table version <= 1.

This CL adds a version test and handles the legacy header.

Fixes golang/go#28339

Change-Id: I79bd8f8bbf262c1caaf4e66888446159b5e4fb43
Reviewed-on: https://go-review.googlesource.com/c/144079
Reviewed-by: Nigel Tao <nigeltao@golang.org>
Reviewed-by: Elias Naur <elias.naur@gmail.com>
This commit is contained in:
Denys Smirnov 2018-10-24 03:36:30 +03:00 committed by Nigel Tao
parent 9b1e201e7c
commit 2a2258ff36

View File

@ -637,7 +637,7 @@ func (f *Font) initialize(offset int, isDfont bool) error {
if err != nil { if err != nil {
return err return err
} }
buf, xHeight, capHeight, err := f.parseOS2(buf) buf, os2Vers, xHeight, capHeight, err := f.parseOS2(buf)
if err != nil { if err != nil {
return err return err
} }
@ -664,6 +664,15 @@ func (f *Font) initialize(offset int, isDfont bool) error {
f.cached.unitsPerEm = unitsPerEm f.cached.unitsPerEm = unitsPerEm
f.cached.xHeight = xHeight f.cached.xHeight = xHeight
if os2Vers <= 1 {
xh, ch, err := f.initOS2Version1()
if err != nil {
return err
}
f.cached.xHeight = xh
f.cached.capHeight = ch
}
return nil return nil
} }
@ -1065,22 +1074,80 @@ func (f *Font) parseGlyphData(buf []byte, numGlyphs int32, indexToLocFormat, isP
return buf, ret, isColorBitmap, nil return buf, ret, isColorBitmap, nil
} }
func (f *Font) parseOS2(buf []byte) (buf1 []byte, xHeight, capHeight int32, err error) { func (f *Font) glyphTopOS2(b *Buffer, ppem fixed.Int26_6, r rune) (int32, error) {
// https://docs.microsoft.com/da-dk/typography/opentype/spec/os2 ind, err := f.GlyphIndex(b, r)
if err != nil && err != ErrNotFound {
return 0, err
} else if ind == 0 {
return 0, nil
}
// Y axis points down
var min fixed.Int26_6
seg, err := f.LoadGlyph(b, ind, ppem, nil)
if err != nil {
return 0, err
}
for _, s := range seg {
for _, p := range s.Args {
if p.Y < min {
min = p.Y
}
}
}
return int32(min), nil
}
func (f *Font) initOS2Version1() (xHeight, capHeight int32, err error) {
ppem := fixed.Int26_6(f.UnitsPerEm())
var b Buffer
// sxHeight equal to the top of the unscaled and unhinted glyph bounding box
// of the glyph encoded at U+0078 (LATIN SMALL LETTER X).
xh, err := f.glyphTopOS2(&b, ppem, 'x')
if err != nil {
return 0, 0, err
}
// sCapHeight may be set equal to the top of the unscaled and unhinted glyph
// bounding box of the glyph encoded at U+0048 (LATIN CAPITAL LETTER H).
ch, err := f.glyphTopOS2(&b, ppem, 'H')
if err != nil {
return 0, 0, err
}
return int32(xh), int32(ch), nil
}
func (f *Font) parseOS2(buf []byte) (buf1 []byte, version uint16, xHeight, capHeight int32, err error) {
// https://docs.microsoft.com/da-dk/typography/opentype/spec/os2
if f.os2.length < 2 {
return nil, 0, 0, 0, errInvalidOS2Table
}
vers, err := f.src.u16(buf, f.os2, 0)
if err != nil {
return nil, 0, 0, 0, err
}
if vers <= 1 {
const headerSize = 86
if f.os2.length < headerSize {
return nil, 0, 0, 0, errInvalidOS2Table
}
// Will resolve xHeight and capHeight later, see initOS2Version1.
return buf, vers, 0, 0, nil
}
const headerSize = 96 const headerSize = 96
if f.os2.length < headerSize { if f.os2.length < headerSize {
return nil, 0, 0, errInvalidOS2Table return nil, 0, 0, 0, errInvalidOS2Table
} }
xh, err := f.src.u16(buf, f.os2, 86) xh, err := f.src.u16(buf, f.os2, 86)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, 0, err
} }
ch, err := f.src.u16(buf, f.os2, 88) ch, err := f.src.u16(buf, f.os2, 88)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, 0, err
} }
return buf, int32(int16(xh)), int32(int16(ch)), nil return buf, vers, int32(int16(xh)), int32(int16(ch)), nil
} }
func (f *Font) parsePost(buf []byte, numGlyphs int32) (buf1 []byte, postTableVersion uint32, err error) { func (f *Font) parsePost(buf []byte, numGlyphs int32) (buf1 []byte, postTableVersion uint32, err error) {