зеркало из https://github.com/golang/image.git
font/sfnt: support .dfont files.
Change-Id: Id7aa18c48c65586c688cee230ce87f4d88dae9b5 Reviewed-on: https://go-review.googlesource.com/40893 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Родитель
a74d51e6d3
Коммит
3210c0296b
|
@ -111,6 +111,30 @@ func TestProprietaryAppleGeezaPro1(t *testing.T) {
|
|||
testProprietary(t, "apple", "GeezaPro.ttc?1", 1700, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica0(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?0", 2100, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica1(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?1", 2100, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica2(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?2", 2100, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica3(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?3", 2100, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica4(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?4", 1300, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHelvetica5(t *testing.T) {
|
||||
testProprietary(t, "apple", "Helvetica.dfont?5", 1300, -1)
|
||||
}
|
||||
|
||||
func TestProprietaryAppleHiragino0(t *testing.T) {
|
||||
testProprietary(t, "apple", "ヒラギノ角ゴシック W0.ttc?0", 9000, -1)
|
||||
}
|
||||
|
@ -320,8 +344,8 @@ kernLoop:
|
|||
// or 1 for a single font. It is not necessarily an exhaustive list of all
|
||||
// proprietary fonts tested.
|
||||
var proprietaryNumFonts = map[string]int{
|
||||
"apple/Helvetica.dfont?0": 6,
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?0": 2,
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?1": 2,
|
||||
"microsoft/Arial.ttf?0": 1,
|
||||
}
|
||||
|
||||
|
@ -342,6 +366,12 @@ var proprietaryVersions = map[string]string{
|
|||
"apple/Apple Symbols.ttf": "12.0d3e10",
|
||||
"apple/GeezaPro.ttc?0": "12.0d1e3",
|
||||
"apple/GeezaPro.ttc?1": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?0": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?1": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?2": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?3": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?4": "12.0d1e3",
|
||||
"apple/Helvetica.dfont?5": "12.0d1e3",
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?0": "11.0d7e1",
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?1": "11.0d7e1",
|
||||
|
||||
|
@ -364,6 +394,12 @@ var proprietaryFullNames = map[string]string{
|
|||
"apple/Apple Symbols.ttf": "Apple Symbols",
|
||||
"apple/GeezaPro.ttc?0": "Geeza Pro Regular",
|
||||
"apple/GeezaPro.ttc?1": "Geeza Pro Bold",
|
||||
"apple/Helvetica.dfont?0": "Helvetica",
|
||||
"apple/Helvetica.dfont?1": "Helvetica Bold",
|
||||
"apple/Helvetica.dfont?2": "Helvetica Oblique",
|
||||
"apple/Helvetica.dfont?3": "Helvetica Bold Oblique",
|
||||
"apple/Helvetica.dfont?4": "Helvetica Light",
|
||||
"apple/Helvetica.dfont?5": "Helvetica Light Oblique",
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?0": "Hiragino Sans W0",
|
||||
"apple/ヒラギノ角ゴシック W0.ttc?1": ".Hiragino Kaku Gothic Interface W0",
|
||||
|
||||
|
@ -422,6 +458,17 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
|
|||
'\u2030': 1728, // U+2030 PER MILLE SIGN
|
||||
},
|
||||
|
||||
"apple/Helvetica.dfont?0": {
|
||||
'\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
|
||||
'\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
|
||||
'\u0401': 473, // U+0401 CYRILLIC CAPITAL LETTER IO
|
||||
'\u200d': 611, // U+200D ZERO WIDTH JOINER
|
||||
'\u20ab': 1743, // U+20AB DONG SIGN
|
||||
'\u2229': 0, // U+2229 INTERSECTION
|
||||
'\u04e9': 1208, // U+04E9 CYRILLIC SMALL LETTER BARRED O
|
||||
'\U0001f100': 0, // U+0001F100 DIGIT ZERO FULL STOP
|
||||
},
|
||||
|
||||
"microsoft/Arial.ttf": {
|
||||
'\u0041': 36, // U+0041 LATIN CAPITAL LETTER A
|
||||
'\u00f1': 120, // U+00F1 LATIN SMALL LETTER N WITH TILDE
|
||||
|
@ -849,6 +896,40 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
|
|||
},
|
||||
},
|
||||
|
||||
"apple/Helvetica.dfont?0": {
|
||||
'i': {
|
||||
// - contour #0
|
||||
moveTo(132, 1066),
|
||||
lineTo(315, 1066),
|
||||
lineTo(315, 0),
|
||||
lineTo(132, 0),
|
||||
lineTo(132, 1066),
|
||||
// - contour #1
|
||||
moveTo(132, 1469),
|
||||
lineTo(315, 1469),
|
||||
lineTo(315, 1265),
|
||||
lineTo(132, 1265),
|
||||
lineTo(132, 1469),
|
||||
},
|
||||
},
|
||||
|
||||
"apple/Helvetica.dfont?1": {
|
||||
'i': {
|
||||
// - contour #0
|
||||
moveTo(426, 1220),
|
||||
lineTo(137, 1220),
|
||||
lineTo(137, 1483),
|
||||
lineTo(426, 1483),
|
||||
lineTo(426, 1220),
|
||||
// - contour #1
|
||||
moveTo(137, 1090),
|
||||
lineTo(426, 1090),
|
||||
lineTo(426, 0),
|
||||
lineTo(137, 0),
|
||||
lineTo(137, 1090),
|
||||
},
|
||||
},
|
||||
|
||||
"microsoft/Arial.ttf": {
|
||||
',': {
|
||||
// - contour #0
|
||||
|
|
|
@ -70,6 +70,7 @@ var (
|
|||
errInvalidBounds = errors.New("sfnt: invalid bounds")
|
||||
errInvalidCFFTable = errors.New("sfnt: invalid CFF table")
|
||||
errInvalidCmapTable = errors.New("sfnt: invalid cmap table")
|
||||
errInvalidDfont = errors.New("sfnt: invalid dfont")
|
||||
errInvalidFont = errors.New("sfnt: invalid font")
|
||||
errInvalidFontCollection = errors.New("sfnt: invalid font collection")
|
||||
errInvalidGlyphData = errors.New("sfnt: invalid glyph data")
|
||||
|
@ -303,6 +304,7 @@ func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) {
|
|||
type Collection struct {
|
||||
src source
|
||||
offsets []uint32
|
||||
isDfont bool
|
||||
}
|
||||
|
||||
// NumFonts returns the number of fonts in the collection.
|
||||
|
@ -310,8 +312,13 @@ func (c *Collection) NumFonts() int { return len(c.offsets) }
|
|||
|
||||
func (c *Collection) initialize() error {
|
||||
// The https://www.microsoft.com/typography/otspec/otff.htm "Font
|
||||
// Collections" section describes the TTC Header.
|
||||
buf, err := c.src.view(nil, 0, 12)
|
||||
// Collections" section describes the TTC header.
|
||||
//
|
||||
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
|
||||
// describes the dfont header.
|
||||
//
|
||||
// 16 is the maximum of sizeof(TTCHeader) and sizeof(DfontHeader).
|
||||
buf, err := c.src.view(nil, 0, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -319,6 +326,8 @@ func (c *Collection) initialize() error {
|
|||
switch u32(buf) {
|
||||
default:
|
||||
return errInvalidFontCollection
|
||||
case dfontResourceDataOffset:
|
||||
return c.parseDfont(buf, u32(buf[4:]), u32(buf[12:]))
|
||||
case 0x00010000, 0x4f54544f:
|
||||
// Try parsing it as a single font instead of a collection.
|
||||
c.offsets = []uint32{0}
|
||||
|
@ -343,13 +352,116 @@ func (c *Collection) initialize() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// dfontResourceDataOffset is the assumed value of a dfont file's resource data
|
||||
// offset.
|
||||
//
|
||||
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
|
||||
// says that "A Mac OS resource file... [starts with an] offset from start of
|
||||
// file to start of resource data section... [usually] 0x0100". In theory,
|
||||
// 0x00000100 isn't always a magic number for identifying dfont files. In
|
||||
// practice, it seems to work.
|
||||
const dfontResourceDataOffset = 0x00000100
|
||||
|
||||
// parseDfont parses a dfont resource map, as per
|
||||
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
|
||||
//
|
||||
// That unofficial wiki page lists all of its fields as *signed* integers,
|
||||
// which looks unusual. The actual file format might use *unsigned* integers in
|
||||
// various places, but until we have either an official specification or an
|
||||
// actual dfont file where this matters, we'll use signed integers and treat
|
||||
// negative values as invalid.
|
||||
func (c *Collection) parseDfont(buf []byte, resourceMapOffset, resourceMapLength uint32) error {
|
||||
if resourceMapOffset > maxTableOffset || resourceMapLength > maxTableLength {
|
||||
return errUnsupportedTableOffsetLength
|
||||
}
|
||||
|
||||
const headerSize = 28
|
||||
if resourceMapLength < headerSize {
|
||||
return errInvalidDfont
|
||||
}
|
||||
buf, err := c.src.view(buf, int(resourceMapOffset+24), 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
typeListOffset := int(int16(u16(buf)))
|
||||
|
||||
if typeListOffset < headerSize || resourceMapLength < uint32(typeListOffset)+2 {
|
||||
return errInvalidDfont
|
||||
}
|
||||
buf, err = c.src.view(buf, int(resourceMapOffset)+typeListOffset, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
typeCount := int(int16(u16(buf)))
|
||||
|
||||
const tSize = 8
|
||||
if typeCount < 0 || tSize*uint32(typeCount) > resourceMapLength-uint32(typeListOffset)-2 {
|
||||
return errInvalidDfont
|
||||
}
|
||||
buf, err = c.src.view(buf, int(resourceMapOffset)+typeListOffset+2, tSize*typeCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceCount, resourceListOffset := 0, 0
|
||||
for i := 0; i < typeCount; i++ {
|
||||
if u32(buf[tSize*i:]) != 0x73666e74 { // "sfnt".
|
||||
continue
|
||||
}
|
||||
|
||||
resourceCount = int(int16(u16(buf[tSize*i+4:])))
|
||||
if resourceCount < 0 {
|
||||
return errInvalidDfont
|
||||
}
|
||||
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
|
||||
// says that the value in the wire format is "the number of
|
||||
// resources of this type, minus one."
|
||||
resourceCount++
|
||||
|
||||
resourceListOffset = int(int16(u16(buf[tSize*i+6:])))
|
||||
if resourceListOffset < 0 {
|
||||
return errInvalidDfont
|
||||
}
|
||||
break
|
||||
}
|
||||
if resourceCount == 0 {
|
||||
return errInvalidDfont
|
||||
}
|
||||
if resourceCount > maxNumFonts {
|
||||
return errUnsupportedNumberOfFonts
|
||||
}
|
||||
|
||||
const rSize = 12
|
||||
if o, n := uint32(typeListOffset+resourceListOffset), rSize*uint32(resourceCount); o > resourceMapLength || n > resourceMapLength-o {
|
||||
return errInvalidDfont
|
||||
} else {
|
||||
buf, err = c.src.view(buf, int(resourceMapOffset+o), int(n))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.offsets = make([]uint32, resourceCount)
|
||||
for i := range c.offsets {
|
||||
o := 0xffffff & u32(buf[rSize*i+4:])
|
||||
// Offsets are relative to the resource data start, not the file start.
|
||||
// A particular resource's data also starts with a 4-byte length, which
|
||||
// we skip.
|
||||
o += dfontResourceDataOffset + 4
|
||||
if o > maxTableOffset {
|
||||
return errUnsupportedTableOffsetLength
|
||||
}
|
||||
c.offsets[i] = o
|
||||
}
|
||||
c.isDfont = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Font returns the i'th font in the collection.
|
||||
func (c *Collection) Font(i int) (*Font, error) {
|
||||
if i < 0 || len(c.offsets) <= i {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
f := &Font{src: c.src}
|
||||
if err := f.initialize(int(c.offsets[i])); err != nil {
|
||||
if err := f.initialize(int(c.offsets[i]), c.isDfont); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
|
@ -359,7 +471,7 @@ func (c *Collection) Font(i int) (*Font, error) {
|
|||
// source.
|
||||
func Parse(src []byte) (*Font, error) {
|
||||
f := &Font{src: source{b: src}}
|
||||
if err := f.initialize(0); err != nil {
|
||||
if err := f.initialize(0, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
|
@ -369,7 +481,7 @@ func Parse(src []byte) (*Font, error) {
|
|||
// io.ReaderAt data source.
|
||||
func ParseReaderAt(src io.ReaderAt) (*Font, error) {
|
||||
f := &Font{src: source{r: src}}
|
||||
if err := f.initialize(0); err != nil {
|
||||
if err := f.initialize(0, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
|
@ -462,11 +574,11 @@ func (f *Font) NumGlyphs() int { return len(f.cached.glyphData.locations) - 1 }
|
|||
// UnitsPerEm returns the number of units per em for f.
|
||||
func (f *Font) UnitsPerEm() Units { return f.cached.unitsPerEm }
|
||||
|
||||
func (f *Font) initialize(offset int) error {
|
||||
func (f *Font) initialize(offset int, isDfont bool) error {
|
||||
if !f.src.valid() {
|
||||
return errInvalidSourceData
|
||||
}
|
||||
buf, isPostScript, err := f.initializeTables(offset)
|
||||
buf, isPostScript, err := f.initializeTables(offset, isDfont)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -526,7 +638,7 @@ func (f *Font) initialize(offset int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err error) {
|
||||
func (f *Font) initializeTables(offset int, isDfont bool) (buf1 []byte, isPostScript bool, err error) {
|
||||
// https://www.microsoft.com/typography/otspec/otff.htm "Organization of an
|
||||
// OpenType Font" says that "The OpenType font starts with the Offset
|
||||
// Table", which is 12 bytes.
|
||||
|
@ -539,6 +651,8 @@ func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err
|
|||
switch u32(buf) {
|
||||
default:
|
||||
return nil, false, errInvalidFont
|
||||
case dfontResourceDataOffset:
|
||||
return nil, false, errInvalidSingleFont
|
||||
case 0x00010000:
|
||||
// No-op.
|
||||
case 0x4f54544f: // "OTTO".
|
||||
|
@ -567,6 +681,15 @@ func (f *Font) initializeTables(offset int) (buf1 []byte, isPostScript bool, err
|
|||
prevTag = tag
|
||||
|
||||
o, n := u32(b[8:12]), u32(b[12:16])
|
||||
// For dfont files, the offset is relative to the resource, not the
|
||||
// file.
|
||||
if isDfont {
|
||||
origO := o
|
||||
o += uint32(offset)
|
||||
if o < origO {
|
||||
return nil, false, errUnsupportedTableOffsetLength
|
||||
}
|
||||
}
|
||||
if o > maxTableOffset || n > maxTableLength {
|
||||
return nil, false, errUnsupportedTableOffsetLength
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче