From 14bb08c1df8db9ec6c8a05520d4eee67971235d9 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 27 Sep 2018 20:03:23 +0200 Subject: mod tidy --- vendor/golang.org/x/image/font/sfnt/sfnt.go | 1537 --------------------------- 1 file changed, 1537 deletions(-) delete mode 100644 vendor/golang.org/x/image/font/sfnt/sfnt.go (limited to 'vendor/golang.org/x/image/font/sfnt/sfnt.go') diff --git a/vendor/golang.org/x/image/font/sfnt/sfnt.go b/vendor/golang.org/x/image/font/sfnt/sfnt.go deleted file mode 100644 index 21a7927..0000000 --- a/vendor/golang.org/x/image/font/sfnt/sfnt.go +++ /dev/null @@ -1,1537 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate go run gen.go - -// Package sfnt implements a decoder for SFNT font file formats, including -// TrueType and OpenType. -package sfnt // import "golang.org/x/image/font/sfnt" - -// This implementation was written primarily to the -// https://www.microsoft.com/en-us/Typography/OpenTypeSpecification.aspx -// specification. Additional documentation is at -// http://developer.apple.com/fonts/TTRefMan/ -// -// The pyftinspect tool from https://github.com/fonttools/fonttools is useful -// for inspecting SFNT fonts. -// -// The ttfdump tool is also useful. For example: -// ttfdump -t cmap ../testdata/CFFTest.otf dump.txt - -import ( - "errors" - "io" - - "golang.org/x/image/font" - "golang.org/x/image/math/fixed" - "golang.org/x/text/encoding/charmap" -) - -// These constants are not part of the specifications, but are limitations used -// by this implementation. -const ( - // This value is arbitrary, but defends against parsing malicious font - // files causing excessive memory allocations. For reference, Adobe's - // SourceHanSansSC-Regular.otf has 65535 glyphs and: - // - its format-4 cmap table has 1581 segments. - // - its format-12 cmap table has 16498 segments. - // - // TODO: eliminate this constraint? If the cmap table is very large, load - // some or all of it lazily (at the time Font.GlyphIndex is called) instead - // of all of it eagerly (at the time Font.initialize is called), while - // keeping an upper bound on the memory used? This will make the code in - // cmap.go more complicated, considering that all of the Font methods are - // safe to call concurrently, as long as each call has a different *Buffer. - maxCmapSegments = 20000 - - // TODO: similarly, load subroutine locations lazily. Adobe's - // SourceHanSansSC-Regular.otf has up to 30000 subroutines. - maxNumSubroutines = 40000 - - maxCompoundRecursionDepth = 8 - maxCompoundStackSize = 64 - maxGlyphDataLength = 64 * 1024 - maxHintBits = 256 - maxNumFontDicts = 256 - maxNumFonts = 256 - maxNumTables = 256 - maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation. - - // (maxTableOffset + maxTableLength) will not overflow an int32. - maxTableLength = 1 << 29 - maxTableOffset = 1 << 29 -) - -var ( - // ErrColoredGlyph indicates that the requested glyph is not a monochrome - // vector glyph, such as a colored (bitmap or vector) emoji glyph. - ErrColoredGlyph = errors.New("sfnt: colored glyph") - // ErrNotFound indicates that the requested value was not found. - ErrNotFound = errors.New("sfnt: not found") - - 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") - errInvalidGlyphDataLength = errors.New("sfnt: invalid glyph data length") - errInvalidHeadTable = errors.New("sfnt: invalid head table") - errInvalidHheaTable = errors.New("sfnt: invalid hhea table") - errInvalidHmtxTable = errors.New("sfnt: invalid hmtx table") - errInvalidKernTable = errors.New("sfnt: invalid kern table") - errInvalidLocaTable = errors.New("sfnt: invalid loca table") - errInvalidLocationData = errors.New("sfnt: invalid location data") - errInvalidMaxpTable = errors.New("sfnt: invalid maxp table") - errInvalidNameTable = errors.New("sfnt: invalid name table") - errInvalidPostTable = errors.New("sfnt: invalid post table") - errInvalidSingleFont = errors.New("sfnt: invalid single font (data is a font collection)") - errInvalidSourceData = errors.New("sfnt: invalid source data") - errInvalidTableOffset = errors.New("sfnt: invalid table offset") - errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order") - errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string") - - errUnsupportedCFFFDSelectTable = errors.New("sfnt: unsupported CFF FDSelect table") - errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version") - errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings") - errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph") - errUnsupportedGlyphDataLength = errors.New("sfnt: unsupported glyph data length") - errUnsupportedKernTable = errors.New("sfnt: unsupported kern table") - errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding") - errUnsupportedNumberOfCmapSegments = errors.New("sfnt: unsupported number of cmap segments") - errUnsupportedNumberOfFontDicts = errors.New("sfnt: unsupported number of font dicts") - errUnsupportedNumberOfFonts = errors.New("sfnt: unsupported number of fonts") - errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints") - errUnsupportedNumberOfSubroutines = errors.New("sfnt: unsupported number of subroutines") - errUnsupportedNumberOfTables = errors.New("sfnt: unsupported number of tables") - errUnsupportedPlatformEncoding = errors.New("sfnt: unsupported platform encoding") - errUnsupportedPostTable = errors.New("sfnt: unsupported post table") - errUnsupportedTableOffsetLength = errors.New("sfnt: unsupported table offset or length") - errUnsupportedType2Charstring = errors.New("sfnt: unsupported Type 2 Charstring") -) - -// GlyphIndex is a glyph index in a Font. -type GlyphIndex uint16 - -// NameID identifies a name table entry. -// -// See the "Name IDs" section of -// https://www.microsoft.com/typography/otspec/name.htm -type NameID uint16 - -const ( - NameIDCopyright NameID = 0 - NameIDFamily = 1 - NameIDSubfamily = 2 - NameIDUniqueIdentifier = 3 - NameIDFull = 4 - NameIDVersion = 5 - NameIDPostScript = 6 - NameIDTrademark = 7 - NameIDManufacturer = 8 - NameIDDesigner = 9 - NameIDDescription = 10 - NameIDVendorURL = 11 - NameIDDesignerURL = 12 - NameIDLicense = 13 - NameIDLicenseURL = 14 - NameIDTypographicFamily = 16 - NameIDTypographicSubfamily = 17 - NameIDCompatibleFull = 18 - NameIDSampleText = 19 - NameIDPostScriptCID = 20 - NameIDWWSFamily = 21 - NameIDWWSSubfamily = 22 - NameIDLightBackgroundPalette = 23 - NameIDDarkBackgroundPalette = 24 - NameIDVariationsPostScriptPrefix = 25 -) - -// Units are an integral number of abstract, scalable "font units". The em -// square is typically 1000 or 2048 "font units". This would map to a certain -// number (e.g. 30 pixels) of physical pixels, depending on things like the -// display resolution (DPI) and font size (e.g. a 12 point font). -type Units int32 - -// scale returns x divided by unitsPerEm, rounded to the nearest fixed.Int26_6 -// value (1/64th of a pixel). -func scale(x fixed.Int26_6, unitsPerEm Units) fixed.Int26_6 { - if x >= 0 { - x += fixed.Int26_6(unitsPerEm) / 2 - } else { - x -= fixed.Int26_6(unitsPerEm) / 2 - } - return x / fixed.Int26_6(unitsPerEm) -} - -func u16(b []byte) uint16 { - _ = b[1] // Bounds check hint to compiler. - return uint16(b[0])<<8 | uint16(b[1])<<0 -} - -func u32(b []byte) uint32 { - _ = b[3] // Bounds check hint to compiler. - return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])<<0 -} - -// source is a source of byte data. Conceptually, it is like an io.ReaderAt, -// except that a common source of SFNT font data is in-memory instead of -// on-disk: a []byte containing the entire data, either as a global variable -// (e.g. "goregular.TTF") or the result of an ioutil.ReadFile call. In such -// cases, as an optimization, we skip the io.Reader / io.ReaderAt model of -// copying from the source to a caller-supplied buffer, and instead provide -// direct access to the underlying []byte data. -type source struct { - b []byte - r io.ReaderAt - - // TODO: add a caching layer, if we're using the io.ReaderAt? Note that - // this might make a source no longer safe to use concurrently. -} - -// valid returns whether exactly one of s.b and s.r is nil. -func (s *source) valid() bool { - return (s.b == nil) != (s.r == nil) -} - -// viewBufferWritable returns whether the []byte returned by source.view can be -// written to by the caller, including by passing it to the same method -// (source.view) on other receivers (i.e. different sources). -// -// In other words, it returns whether the source's underlying data is an -// io.ReaderAt, not a []byte. -func (s *source) viewBufferWritable() bool { - return s.b == nil -} - -// view returns the length bytes at the given offset. buf is an optional -// scratch buffer to reduce allocations when calling view multiple times. A nil -// buf is valid. The []byte returned may be a sub-slice of buf[:cap(buf)], or -// it may be an unrelated slice. In any case, the caller should not modify the -// contents of the returned []byte, other than passing that []byte back to this -// method on the same source s. -func (s *source) view(buf []byte, offset, length int) ([]byte, error) { - if 0 > offset || offset > offset+length { - return nil, errInvalidBounds - } - - // Try reading from the []byte. - if s.b != nil { - if offset+length > len(s.b) { - return nil, errInvalidBounds - } - return s.b[offset : offset+length], nil - } - - // Read from the io.ReaderAt. - if length <= cap(buf) { - buf = buf[:length] - } else { - // Round length up to the nearest KiB. The slack can lead to fewer - // allocations if the buffer is re-used for multiple source.view calls. - n := length - n += 1023 - n &^= 1023 - buf = make([]byte, length, n) - } - if n, err := s.r.ReadAt(buf, int64(offset)); n != length { - return nil, err - } - return buf, nil -} - -// u16 returns the uint16 in the table t at the relative offset i. -// -// buf is an optional scratch buffer as per the source.view method. -func (s *source) u16(buf []byte, t table, i int) (uint16, error) { - if i < 0 || uint(t.length) < uint(i+2) { - return 0, errInvalidBounds - } - buf, err := s.view(buf, int(t.offset)+i, 2) - if err != nil { - return 0, err - } - return u16(buf), nil -} - -// u32 returns the uint32 in the table t at the relative offset i. -// -// buf is an optional scratch buffer as per the source.view method. -func (s *source) u32(buf []byte, t table, i int) (uint32, error) { - if i < 0 || uint(t.length) < uint(i+4) { - return 0, errInvalidBounds - } - buf, err := s.view(buf, int(t.offset)+i, 4) - if err != nil { - return 0, err - } - return u32(buf), nil -} - -// table is a section of the font data. -type table struct { - offset, length uint32 -} - -// ParseCollection parses an SFNT font collection, such as TTC or OTC data, -// from a []byte data source. -// -// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it -// will return a collection containing 1 font. -func ParseCollection(src []byte) (*Collection, error) { - c := &Collection{src: source{b: src}} - if err := c.initialize(); err != nil { - return nil, err - } - return c, nil -} - -// ParseCollectionReaderAt parses an SFNT collection, such as TTC or OTC data, -// from an io.ReaderAt data source. -// -// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it -// will return a collection containing 1 font. -func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) { - c := &Collection{src: source{r: src}} - if err := c.initialize(); err != nil { - return nil, err - } - return c, nil -} - -// Collection is a collection of one or more fonts. -// -// All of the Collection methods are safe to call concurrently. -type Collection struct { - src source - offsets []uint32 - isDfont bool -} - -// NumFonts returns the number of fonts in the collection. -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. - // - // 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 - } - // These cases match the switch statement in Font.initializeTables. - 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} - case 0x74746366: // "ttcf". - numFonts := u32(buf[8:]) - if numFonts == 0 || numFonts > maxNumFonts { - return errUnsupportedNumberOfFonts - } - buf, err = c.src.view(nil, 12, int(4*numFonts)) - if err != nil { - return err - } - c.offsets = make([]uint32, numFonts) - for i := range c.offsets { - o := u32(buf[4*i:]) - if o > maxTableOffset { - return errUnsupportedTableOffsetLength - } - c.offsets[i] = o - } - } - 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]), c.isDfont); err != nil { - return nil, err - } - return f, nil -} - -// Parse parses an SFNT font, such as TTF or OTF data, from a []byte data -// source. -func Parse(src []byte) (*Font, error) { - f := &Font{src: source{b: src}} - if err := f.initialize(0, false); err != nil { - return nil, err - } - return f, nil -} - -// ParseReaderAt parses an SFNT font, such as TTF or OTF data, from an -// io.ReaderAt data source. -func ParseReaderAt(src io.ReaderAt) (*Font, error) { - f := &Font{src: source{r: src}} - if err := f.initialize(0, false); err != nil { - return nil, err - } - return f, nil -} - -// Font is an SFNT font. -// -// Many of its methods take a *Buffer argument, as re-using buffers can reduce -// the total memory allocation of repeated Font method calls, such as measuring -// and rasterizing every unique glyph in a string of text. If efficiency is not -// a concern, passing a nil *Buffer is valid, and implies using a temporary -// buffer for a single call. -// -// It is valid to re-use a *Buffer with multiple Font method calls, even with -// different *Font receivers, as long as they are not concurrent calls. -// -// All of the Font methods are safe to call concurrently, as long as each call -// has a different *Buffer (or nil). -// -// The Font methods that don't take a *Buffer argument are always safe to call -// concurrently. -// -// Some methods provide lengths or coordinates, e.g. bounds, font metrics and -// control points. All of these methods take a ppem parameter, which is the -// number of pixels in 1 em, expressed as a 26.6 fixed point value. For -// example, if 1 em is 10 pixels then ppem is fixed.I(10), which equals -// fixed.Int26_6(10 << 6). -// -// To get those lengths or coordinates in terms of font units instead of -// pixels, use ppem = fixed.Int26_6(f.UnitsPerEm()) and if those methods take a -// font.Hinting parameter, use font.HintingNone. The return values will have -// type fixed.Int26_6, but those numbers can be converted back to Units with no -// further scaling necessary. -type Font struct { - src source - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Required Tables". - cmap table - head table - hhea table - hmtx table - maxp table - name table - os2 table - post table - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Tables Related to TrueType Outlines". - // - // This implementation does not support hinting, so it does not read the - // cvt, fpgm gasp or prep tables. - glyf table - loca table - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Tables Related to PostScript Outlines". - // - // TODO: cff2, vorg? - cff table - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Tables Related to Bitmap Glyphs". - // - // TODO: Others? - cblc table - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Advanced Typographic Tables". - // - // TODO: base, gdef, gpos, gsub, jstf, math? - - // https://www.microsoft.com/typography/otspec/otff.htm#otttables - // "Other OpenType Tables". - // - // TODO: hdmx, vmtx? Others? - kern table - - cached struct { - ascent int32 - glyphData glyphData - glyphIndex glyphIndexFunc - bounds [4]int16 - descent int32 - indexToLocFormat bool // false means short, true means long. - isColorBitmap bool - isPostScript bool - kernNumPairs int32 - kernOffset int32 - lineGap int32 - numHMetrics int32 - postTableVersion uint32 - unitsPerEm Units - } -} - -// NumGlyphs returns the number of glyphs in f. -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, isDfont bool) error { - if !f.src.valid() { - return errInvalidSourceData - } - buf, isPostScript, err := f.initializeTables(offset, isDfont) - if err != nil { - return err - } - - // The order of these parseXxx calls matters. Later calls may depend on - // information parsed by earlier calls, such as the maxp table's numGlyphs. - // To enforce these dependencies, such information is passed and returned - // explicitly, and the f.cached fields are only set afterwards. - // - // When implementing new parseXxx methods, take care not to call methods - // such as Font.NumGlyphs that implicitly depend on f.cached fields. - - buf, bounds, indexToLocFormat, unitsPerEm, err := f.parseHead(buf) - if err != nil { - return err - } - buf, numGlyphs, err := f.parseMaxp(buf, isPostScript) - if err != nil { - return err - } - buf, glyphData, isColorBitmap, err := f.parseGlyphData(buf, numGlyphs, indexToLocFormat, isPostScript) - if err != nil { - return err - } - buf, glyphIndex, err := f.parseCmap(buf) - if err != nil { - return err - } - buf, kernNumPairs, kernOffset, err := f.parseKern(buf) - if err != nil { - return err - } - buf, ascent, descent, lineGap, numHMetrics, err := f.parseHhea(buf, numGlyphs) - if err != nil { - return err - } - buf, err = f.parseHmtx(buf, numGlyphs, numHMetrics) - if err != nil { - return err - } - buf, postTableVersion, err := f.parsePost(buf, numGlyphs) - if err != nil { - return err - } - - f.cached.ascent = ascent - f.cached.glyphData = glyphData - f.cached.glyphIndex = glyphIndex - f.cached.bounds = bounds - f.cached.descent = descent - f.cached.indexToLocFormat = indexToLocFormat - f.cached.isColorBitmap = isColorBitmap - f.cached.isPostScript = isPostScript - f.cached.kernNumPairs = kernNumPairs - f.cached.kernOffset = kernOffset - f.cached.lineGap = lineGap - f.cached.numHMetrics = numHMetrics - f.cached.postTableVersion = postTableVersion - f.cached.unitsPerEm = unitsPerEm - - return nil -} - -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. - buf, err := f.src.view(nil, offset, 12) - if err != nil { - return nil, false, err - } - // When updating the cases in this switch statement, also update the - // Collection.initialize method. - switch u32(buf) { - default: - return nil, false, errInvalidFont - case dfontResourceDataOffset: - return nil, false, errInvalidSingleFont - case 0x00010000: - // No-op. - case 0x4f54544f: // "OTTO". - isPostScript = true - case 0x74746366: // "ttcf". - return nil, false, errInvalidSingleFont - } - numTables := int(u16(buf[4:])) - if numTables > maxNumTables { - return nil, false, errUnsupportedNumberOfTables - } - - // "The Offset Table is followed immediately by the Table Record entries... - // sorted in ascending order by tag", 16 bytes each. - buf, err = f.src.view(buf, offset+12, 16*numTables) - if err != nil { - return nil, false, err - } - for b, first, prevTag := buf, true, uint32(0); len(b) > 0; b = b[16:] { - tag := u32(b) - if first { - first = false - } else if tag <= prevTag { - return nil, false, errInvalidTableTagOrder - } - 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 - } - // We ignore the checksums, but "all tables must begin on four byte - // boundries [sic]". - if o&3 != 0 { - return nil, false, errInvalidTableOffset - } - - // Match the 4-byte tag as a uint32. For example, "OS/2" is 0x4f532f32. - switch tag { - case 0x43424c43: - f.cblc = table{o, n} - case 0x43464620: - f.cff = table{o, n} - case 0x4f532f32: - f.os2 = table{o, n} - case 0x636d6170: - f.cmap = table{o, n} - case 0x676c7966: - f.glyf = table{o, n} - case 0x68656164: - f.head = table{o, n} - case 0x68686561: - f.hhea = table{o, n} - case 0x686d7478: - f.hmtx = table{o, n} - case 0x6b65726e: - f.kern = table{o, n} - case 0x6c6f6361: - f.loca = table{o, n} - case 0x6d617870: - f.maxp = table{o, n} - case 0x6e616d65: - f.name = table{o, n} - case 0x706f7374: - f.post = table{o, n} - } - } - return buf, isPostScript, nil -} - -func (f *Font) parseCmap(buf []byte) (buf1 []byte, glyphIndex glyphIndexFunc, err error) { - // https://www.microsoft.com/typography/OTSPEC/cmap.htm - - const headerSize, entrySize = 4, 8 - if f.cmap.length < headerSize { - return nil, nil, errInvalidCmapTable - } - u, err := f.src.u16(buf, f.cmap, 2) - if err != nil { - return nil, nil, err - } - numSubtables := int(u) - if f.cmap.length < headerSize+entrySize*uint32(numSubtables) { - return nil, nil, errInvalidCmapTable - } - - var ( - bestWidth int - bestOffset uint32 - bestLength uint32 - bestFormat uint16 - ) - - // Scan all of the subtables, picking the widest supported one. See the - // platformEncodingWidth comment for more discussion of width. - for i := 0; i < numSubtables; i++ { - buf, err = f.src.view(buf, int(f.cmap.offset)+headerSize+entrySize*i, entrySize) - if err != nil { - return nil, nil, err - } - pid := u16(buf) - psid := u16(buf[2:]) - width := platformEncodingWidth(pid, psid) - if width <= bestWidth { - continue - } - offset := u32(buf[4:]) - - if offset > f.cmap.length-4 { - return nil, nil, errInvalidCmapTable - } - buf, err = f.src.view(buf, int(f.cmap.offset+offset), 4) - if err != nil { - return nil, nil, err - } - format := u16(buf) - if !supportedCmapFormat(format, pid, psid) { - continue - } - length := uint32(u16(buf[2:])) - - bestWidth = width - bestOffset = offset - bestLength = length - bestFormat = format - } - - if bestWidth == 0 { - return nil, nil, errUnsupportedCmapEncodings - } - return f.makeCachedGlyphIndex(buf, bestOffset, bestLength, bestFormat) -} - -func (f *Font) parseHead(buf []byte) (buf1 []byte, bounds [4]int16, indexToLocFormat bool, unitsPerEm Units, err error) { - // https://www.microsoft.com/typography/otspec/head.htm - - if f.head.length != 54 { - return nil, [4]int16{}, false, 0, errInvalidHeadTable - } - - u, err := f.src.u16(buf, f.head, 18) - if err != nil { - return nil, [4]int16{}, false, 0, err - } - if u == 0 { - return nil, [4]int16{}, false, 0, errInvalidHeadTable - } - unitsPerEm = Units(u) - - for i := range bounds { - u, err := f.src.u16(buf, f.head, 36+2*i) - if err != nil { - return nil, [4]int16{}, false, 0, err - } - bounds[i] = int16(u) - } - - u, err = f.src.u16(buf, f.head, 50) - if err != nil { - return nil, [4]int16{}, false, 0, err - } - indexToLocFormat = u != 0 - return buf, bounds, indexToLocFormat, unitsPerEm, nil -} - -func (f *Font) parseHhea(buf []byte, numGlyphs int32) (buf1 []byte, ascent, descent, lineGap, numHMetrics int32, err error) { - // https://www.microsoft.com/typography/OTSPEC/hhea.htm - - if f.hhea.length != 36 { - return nil, 0, 0, 0, 0, errInvalidHheaTable - } - u, err := f.src.u16(buf, f.hhea, 34) - if err != nil { - return nil, 0, 0, 0, 0, err - } - if int32(u) > numGlyphs || u == 0 { - return nil, 0, 0, 0, 0, errInvalidHheaTable - } - a, err := f.src.u16(buf, f.hhea, 4) - if err != nil { - return nil, 0, 0, 0, 0, err - } - d, err := f.src.u16(buf, f.hhea, 6) - if err != nil { - return nil, 0, 0, 0, 0, err - } - l, err := f.src.u16(buf, f.hhea, 8) - if err != nil { - return nil, 0, 0, 0, 0, err - } - return buf, int32(int16(a)), int32(int16(d)), int32(int16(l)), int32(u), nil -} - -func (f *Font) parseHmtx(buf []byte, numGlyphs, numHMetrics int32) (buf1 []byte, err error) { - // https://www.microsoft.com/typography/OTSPEC/hmtx.htm - - if f.hmtx.length != uint32(2*numGlyphs+2*numHMetrics) { - return nil, errInvalidHmtxTable - } - return buf, nil -} - -func (f *Font) parseKern(buf []byte) (buf1 []byte, kernNumPairs, kernOffset int32, err error) { - // https://www.microsoft.com/typography/otspec/kern.htm - - if f.kern.length == 0 { - return buf, 0, 0, nil - } - const headerSize = 4 - if f.kern.length < headerSize { - return nil, 0, 0, errInvalidKernTable - } - buf, err = f.src.view(buf, int(f.kern.offset), headerSize) - if err != nil { - return nil, 0, 0, err - } - offset := int(f.kern.offset) + headerSize - length := int(f.kern.length) - headerSize - - switch version := u16(buf); version { - case 0: - if numTables := int(u16(buf[2:])); numTables == 0 { - return buf, 0, 0, nil - } else if numTables > 1 { - // TODO: support multiple subtables. For now, fall through and use - // only the first one. - } - return f.parseKernVersion0(buf, offset, length) - case 1: - if buf[2] != 0 || buf[3] != 0 { - return nil, 0, 0, errUnsupportedKernTable - } - // Microsoft's https://www.microsoft.com/typography/otspec/kern.htm - // says that "Apple has extended the definition of the 'kern' table to - // provide additional functionality. The Apple extensions are not - // supported on Windows." - // - // The format is relatively complicated, including encoding a state - // machine, but rarely seen. We follow Microsoft's and FreeType's - // behavior and simply ignore it. Theoretically, we could follow - // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html - // but it doesn't seem worth the effort. - return buf, 0, 0, nil - } - return nil, 0, 0, errUnsupportedKernTable -} - -func (f *Font) parseKernVersion0(buf []byte, offset, length int) (buf1 []byte, kernNumPairs, kernOffset int32, err error) { - const headerSize = 6 - if length < headerSize { - return nil, 0, 0, errInvalidKernTable - } - buf, err = f.src.view(buf, offset, headerSize) - if err != nil { - return nil, 0, 0, err - } - if version := u16(buf); version != 0 { - return nil, 0, 0, errUnsupportedKernTable - } - subtableLength := int(u16(buf[2:])) - if subtableLength < headerSize || length < subtableLength { - return nil, 0, 0, errInvalidKernTable - } - if coverageBits := buf[5]; coverageBits != 0x01 { - // We only support horizontal kerning. - return nil, 0, 0, errUnsupportedKernTable - } - offset += headerSize - length -= headerSize - subtableLength -= headerSize - - switch format := buf[4]; format { - case 0: - return f.parseKernFormat0(buf, offset, subtableLength) - case 2: - // If we could find such a font, we could write code to support it, but - // a comment in the equivalent FreeType code (sfnt/ttkern.c) says that - // they've never seen such a font. - } - return nil, 0, 0, errUnsupportedKernTable -} - -func (f *Font) parseKernFormat0(buf []byte, offset, length int) (buf1 []byte, kernNumPairs, kernOffset int32, err error) { - const headerSize, entrySize = 8, 6 - if length < headerSize { - return nil, 0, 0, errInvalidKernTable - } - buf, err = f.src.view(buf, offset, headerSize) - if err != nil { - return nil, 0, 0, err - } - kernNumPairs = int32(u16(buf)) - if length != headerSize+entrySize*int(kernNumPairs) { - return nil, 0, 0, errInvalidKernTable - } - return buf, kernNumPairs, int32(offset) + headerSize, nil -} - -func (f *Font) parseMaxp(buf []byte, isPostScript bool) (buf1 []byte, numGlyphs int32, err error) { - // https://www.microsoft.com/typography/otspec/maxp.htm - - if isPostScript { - if f.maxp.length != 6 { - return nil, 0, errInvalidMaxpTable - } - } else { - if f.maxp.length != 32 { - return nil, 0, errInvalidMaxpTable - } - } - u, err := f.src.u16(buf, f.maxp, 4) - if err != nil { - return nil, 0, err - } - return buf, int32(u), nil -} - -type glyphData struct { - // The glyph data for the i'th glyph index is in - // src[locations[i+0]:locations[i+1]]. - // - // The slice length equals 1 plus the number of glyphs. - locations []uint32 - - // For PostScript fonts, the bytecode for the i'th global or local - // subroutine is in src[x[i+0]:x[i+1]]. - // - // The []uint32 slice length equals 1 plus the number of subroutines - gsubrs []uint32 - singleSubrs []uint32 - multiSubrs [][]uint32 - - fdSelect fdSelect -} - -func (f *Font) parseGlyphData(buf []byte, numGlyphs int32, indexToLocFormat, isPostScript bool) (buf1 []byte, ret glyphData, isColorBitmap bool, err error) { - if isPostScript { - p := cffParser{ - src: &f.src, - base: int(f.cff.offset), - offset: int(f.cff.offset), - end: int(f.cff.offset + f.cff.length), - } - ret, err = p.parse(numGlyphs) - if err != nil { - return nil, glyphData{}, false, err - } - } else if f.loca.length != 0 { - ret.locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs) - if err != nil { - return nil, glyphData{}, false, err - } - } else if f.cblc.length != 0 { - isColorBitmap = true - // TODO: parse the CBLC (and CBDT) tables. For now, we return a font - // with empty glyphs. - ret.locations = make([]uint32, numGlyphs+1) - } - - if len(ret.locations) != int(numGlyphs+1) { - return nil, glyphData{}, false, errInvalidLocationData - } - - return buf, ret, isColorBitmap, nil -} - -func (f *Font) parsePost(buf []byte, numGlyphs int32) (buf1 []byte, postTableVersion uint32, err error) { - // https://www.microsoft.com/typography/otspec/post.htm - - const headerSize = 32 - if f.post.length < headerSize { - return nil, 0, errInvalidPostTable - } - u, err := f.src.u32(buf, f.post, 0) - if err != nil { - return nil, 0, err - } - switch u { - case 0x20000: - if f.post.length < headerSize+2+2*uint32(numGlyphs) { - return nil, 0, errInvalidPostTable - } - case 0x30000: - // No-op. - default: - return nil, 0, errUnsupportedPostTable - } - return buf, u, nil -} - -// Bounds returns the union of a Font's glyphs' bounds. -// -// In the returned Rectangle26_6's (x, y) coordinates, the Y axis increases -// down. -func (f *Font) Bounds(b *Buffer, ppem fixed.Int26_6, h font.Hinting) (fixed.Rectangle26_6, error) { - // The 0, 3, 2, 1 indices are to flip the Y coordinates. OpenType's Y axis - // increases up. Go's standard graphics libraries' Y axis increases down. - r := fixed.Rectangle26_6{ - Min: fixed.Point26_6{ - X: +scale(fixed.Int26_6(f.cached.bounds[0])*ppem, f.cached.unitsPerEm), - Y: -scale(fixed.Int26_6(f.cached.bounds[3])*ppem, f.cached.unitsPerEm), - }, - Max: fixed.Point26_6{ - X: +scale(fixed.Int26_6(f.cached.bounds[2])*ppem, f.cached.unitsPerEm), - Y: -scale(fixed.Int26_6(f.cached.bounds[1])*ppem, f.cached.unitsPerEm), - }, - } - if h == font.HintingFull { - // Quantize the Min down and Max up to a whole pixel. - r.Min.X = (r.Min.X + 0) &^ 63 - r.Min.Y = (r.Min.Y + 0) &^ 63 - r.Max.X = (r.Max.X + 63) &^ 63 - r.Max.Y = (r.Max.Y + 63) &^ 63 - } - return r, nil -} - -// TODO: API for looking up glyph variants?? For example, some fonts may -// provide both slashed and dotted zero glyphs ('0'), or regular and 'old -// style' numerals, and users can direct software to choose a variant. - -type glyphIndexFunc func(f *Font, b *Buffer, r rune) (GlyphIndex, error) - -// GlyphIndex returns the glyph index for the given rune. -// -// It returns (0, nil) if there is no glyph for r. -// https://www.microsoft.com/typography/OTSPEC/cmap.htm says that "Character -// codes that do not correspond to any glyph in the font should be mapped to -// glyph index 0. The glyph at this location must be a special glyph -// representing a missing character, commonly known as .notdef." -func (f *Font) GlyphIndex(b *Buffer, r rune) (GlyphIndex, error) { - return f.cached.glyphIndex(f, b, r) -} - -func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) (buf []byte, offset, length uint32, err error) { - xx := int(x) - if f.NumGlyphs() <= xx { - return nil, 0, 0, ErrNotFound - } - i := f.cached.glyphData.locations[xx+0] - j := f.cached.glyphData.locations[xx+1] - if j < i { - return nil, 0, 0, errInvalidGlyphDataLength - } - if j-i > maxGlyphDataLength { - return nil, 0, 0, errUnsupportedGlyphDataLength - } - buf, err = b.view(&f.src, int(i), int(j-i)) - return buf, i, j - i, err -} - -// LoadGlyphOptions are the options to the Font.LoadGlyph method. -type LoadGlyphOptions struct { - // TODO: transform / hinting. -} - -// LoadGlyph returns the vector segments for the x'th glyph. ppem is the number -// of pixels in 1 em. -// -// If b is non-nil, the segments become invalid to use once b is re-used. -// -// In the returned Segments' (x, y) coordinates, the Y axis increases down. -// -// It returns ErrNotFound if the glyph index is out of range. It returns -// ErrColoredGlyph if the glyph is not a monochrome vector glyph, such as a -// colored (bitmap or vector) emoji glyph. -func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *LoadGlyphOptions) ([]Segment, error) { - if b == nil { - b = &Buffer{} - } - - b.segments = b.segments[:0] - if f.cached.isColorBitmap { - return nil, ErrColoredGlyph - } - if f.cached.isPostScript { - buf, offset, length, err := f.viewGlyphData(b, x) - if err != nil { - return nil, err - } - b.psi.type2Charstrings.initialize(f, b, x) - if err := b.psi.run(psContextType2Charstring, buf, offset, length); err != nil { - return nil, err - } - if !b.psi.type2Charstrings.ended { - return nil, errInvalidCFFTable - } - } else if err := loadGlyf(f, b, x, 0, 0); err != nil { - return nil, err - } - - // Scale the segments. If we want to support hinting, we'll have to push - // the scaling computation into the PostScript / TrueType specific glyph - // loading code, such as the appendGlyfSegments body, since TrueType - // hinting bytecode works on the scaled glyph vectors. For now, though, - // it's simpler to scale as a post-processing step. - // - // We also flip the Y coordinates. OpenType's Y axis increases up. Go's - // standard graphics libraries' Y axis increases down. - for i := range b.segments { - a := &b.segments[i].Args - for j := range a { - a[j].X = +scale(a[j].X*ppem, f.cached.unitsPerEm) - a[j].Y = -scale(a[j].Y*ppem, f.cached.unitsPerEm) - } - } - - // TODO: look at opts to transform / hint the Buffer.segments. - - return b.segments, nil -} - -// GlyphName returns the name of the x'th glyph. -// -// Not every font contains glyph names. If not present, GlyphName will return -// ("", nil). -// -// If present, the glyph name, provided by the font, is assumed to follow the -// Adobe Glyph List Specification: -// https://github.com/adobe-type-tools/agl-specification/blob/master/README.md -// -// This is also known as the "Adobe Glyph Naming convention", the "Adobe -// document [for] Unicode and Glyph Names" or "PostScript glyph names". -// -// It returns ErrNotFound if the glyph index is out of range. -func (f *Font) GlyphName(b *Buffer, x GlyphIndex) (string, error) { - if int(x) >= f.NumGlyphs() { - return "", ErrNotFound - } - if f.cached.postTableVersion != 0x20000 { - return "", nil - } - if b == nil { - b = &Buffer{} - } - - // The wire format for a Version 2 post table is documented at: - // https://www.microsoft.com/typography/otspec/post.htm - const glyphNameIndexOffset = 34 - - buf, err := b.view(&f.src, int(f.post.offset)+glyphNameIndexOffset+2*int(x), 2) - if err != nil { - return "", err - } - u := u16(buf) - if u < numBuiltInPostNames { - i := builtInPostNamesOffsets[u+0] - j := builtInPostNamesOffsets[u+1] - return builtInPostNamesData[i:j], nil - } - // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html - // says that "32768 through 65535 are reserved for future use". - if u > 32767 { - return "", errUnsupportedPostTable - } - u -= numBuiltInPostNames - - // Iterate through the list of Pascal-formatted strings. A linear scan is - // clearly O(u), which isn't great (as the obvious loop, calling - // Font.GlyphName, to get all of the glyph names in a font has quadratic - // complexity), but the wire format doesn't suggest a better alternative. - - offset := glyphNameIndexOffset + 2*f.NumGlyphs() - buf, err = b.view(&f.src, int(f.post.offset)+offset, int(f.post.length)-offset) - if err != nil { - return "", err - } - - for { - if len(buf) == 0 { - return "", errInvalidPostTable - } - n := 1 + int(buf[0]) - if len(buf) < n { - return "", errInvalidPostTable - } - if u == 0 { - return string(buf[1:n]), nil - } - buf = buf[n:] - u-- - } -} - -// GlyphAdvance returns the advance width for the x'th glyph. ppem is the -// number of pixels in 1 em. -// -// It returns ErrNotFound if the glyph index is out of range. -func (f *Font) GlyphAdvance(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, h font.Hinting) (fixed.Int26_6, error) { - if int(x) >= f.NumGlyphs() { - return 0, ErrNotFound - } - if b == nil { - b = &Buffer{} - } - - // https://www.microsoft.com/typography/OTSPEC/hmtx.htm says that "As an - // optimization, the number of records can be less than the number of - // glyphs, in which case the advance width value of the last record applies - // to all remaining glyph IDs." - if n := GlyphIndex(f.cached.numHMetrics - 1); x > n { - x = n - } - - buf, err := b.view(&f.src, int(f.hmtx.offset)+int(4*x), 2) - if err != nil { - return 0, err - } - adv := fixed.Int26_6(u16(buf)) - adv = scale(adv*ppem, f.cached.unitsPerEm) - if h == font.HintingFull { - // Quantize the fixed.Int26_6 value to the nearest pixel. - adv = (adv + 32) &^ 63 - } - return adv, nil -} - -// Kern returns the horizontal adjustment for the kerning pair (x0, x1). A -// positive kern means to move the glyphs further apart. ppem is the number of -// pixels in 1 em. -// -// It returns ErrNotFound if either glyph index is out of range. -func (f *Font) Kern(b *Buffer, x0, x1 GlyphIndex, ppem fixed.Int26_6, h font.Hinting) (fixed.Int26_6, error) { - // TODO: how should this work with the GPOS table and CFF fonts? - // https://www.microsoft.com/typography/otspec/kern.htm says that - // "OpenType™ fonts containing CFF outlines are not supported by the 'kern' - // table and must use the 'GPOS' OpenType Layout table." - - if n := f.NumGlyphs(); int(x0) >= n || int(x1) >= n { - return 0, ErrNotFound - } - // Not every font has a kern table. If it doesn't, or if that table is - // ignored, there's no need to allocate a Buffer. - if f.cached.kernNumPairs == 0 { - return 0, nil - } - if b == nil { - b = &Buffer{} - } - - key := uint32(x0)<<16 | uint32(x1) - lo, hi := int32(0), f.cached.kernNumPairs - for lo < hi { - i := (lo + hi) / 2 - - // TODO: this view call inside the inner loop can lead to many small - // reads instead of fewer larger reads, which can be expensive. We - // should be able to do better, although we don't want to make (one) - // arbitrarily large read. Perhaps we should round up reads to 4K or 8K - // chunks. For reference, Arial.ttf's kern table is 5472 bytes. - // Times_New_Roman.ttf's kern table is 5220 bytes. - const entrySize = 6 - buf, err := b.view(&f.src, int(f.cached.kernOffset+i*entrySize), entrySize) - if err != nil { - return 0, err - } - - k := u32(buf) - if k < key { - lo = i + 1 - } else if k > key { - hi = i - } else { - kern := fixed.Int26_6(int16(u16(buf[4:]))) - kern = scale(kern*ppem, f.cached.unitsPerEm) - if h == font.HintingFull { - // Quantize the fixed.Int26_6 value to the nearest pixel. - kern = (kern + 32) &^ 63 - } - return kern, nil - } - } - return 0, nil -} - -// Metrics returns the metrics of this font. -func (f *Font) Metrics(b *Buffer, ppem fixed.Int26_6, h font.Hinting) (font.Metrics, error) { - m := font.Metrics{ - Height: scale(fixed.Int26_6(f.cached.ascent-f.cached.descent+f.cached.lineGap)*ppem, f.cached.unitsPerEm), - Ascent: +scale(fixed.Int26_6(f.cached.ascent)*ppem, f.cached.unitsPerEm), - Descent: -scale(fixed.Int26_6(f.cached.descent)*ppem, f.cached.unitsPerEm), - } - if h == font.HintingFull { - // Quantize up to a whole pixel. - m.Height = (m.Height + 63) &^ 63 - m.Ascent = (m.Ascent + 63) &^ 63 - m.Descent = (m.Descent + 63) &^ 63 - } - return m, nil -} - -// Name returns the name value keyed by the given NameID. -// -// It returns ErrNotFound if there is no value for that key. -func (f *Font) Name(b *Buffer, id NameID) (string, error) { - if b == nil { - b = &Buffer{} - } - - const headerSize, entrySize = 6, 12 - if f.name.length < headerSize { - return "", errInvalidNameTable - } - buf, err := b.view(&f.src, int(f.name.offset), headerSize) - if err != nil { - return "", err - } - numSubtables := u16(buf[2:]) - if f.name.length < headerSize+entrySize*uint32(numSubtables) { - return "", errInvalidNameTable - } - stringOffset := u16(buf[4:]) - - seen := false - for i, n := 0, int(numSubtables); i < n; i++ { - buf, err := b.view(&f.src, int(f.name.offset)+headerSize+entrySize*i, entrySize) - if err != nil { - return "", err - } - if u16(buf[6:]) != uint16(id) { - continue - } - seen = true - - var stringify func([]byte) (string, error) - switch u32(buf) { - default: - continue - case pidMacintosh<<16 | psidMacintoshRoman: - stringify = stringifyMacintosh - case pidWindows<<16 | psidWindowsUCS2: - stringify = stringifyUCS2 - } - - nameLength := u16(buf[8:]) - nameOffset := u16(buf[10:]) - buf, err = b.view(&f.src, int(f.name.offset)+int(nameOffset)+int(stringOffset), int(nameLength)) - if err != nil { - return "", err - } - return stringify(buf) - } - - if seen { - return "", errUnsupportedPlatformEncoding - } - return "", ErrNotFound -} - -func stringifyMacintosh(b []byte) (string, error) { - for _, c := range b { - if c >= 0x80 { - // b contains some non-ASCII bytes. - s, _ := charmap.Macintosh.NewDecoder().Bytes(b) - return string(s), nil - } - } - // b contains only ASCII bytes. - return string(b), nil -} - -func stringifyUCS2(b []byte) (string, error) { - if len(b)&1 != 0 { - return "", errInvalidUCS2String - } - r := make([]rune, len(b)/2) - for i := range r { - r[i] = rune(u16(b)) - b = b[2:] - } - return string(r), nil -} - -// Buffer holds re-usable buffers that can reduce the total memory allocation -// of repeated Font method calls. -// -// See the Font type's documentation comment for more details. -type Buffer struct { - // buf is a byte buffer for when a Font's source is an io.ReaderAt. - buf []byte - // segments holds glyph vector path segments. - segments []Segment - // compoundStack holds the components of a TrueType compound glyph. - compoundStack [maxCompoundStackSize]struct { - glyphIndex GlyphIndex - dx, dy int16 - hasTransform bool - transformXX int16 - transformXY int16 - transformYX int16 - transformYY int16 - } - // psi is a PostScript interpreter for when the Font is an OpenType/CFF - // font. - psi psInterpreter -} - -func (b *Buffer) view(src *source, offset, length int) ([]byte, error) { - buf, err := src.view(b.buf, offset, length) - if err != nil { - return nil, err - } - // Only update b.buf if it is safe to re-use buf. - if src.viewBufferWritable() { - b.buf = buf - } - return buf, nil -} - -// Segment is a segment of a vector path. -type Segment struct { - // Op is the operator. - Op SegmentOp - // Args is up to three (x, y) coordinates. The Y axis increases down. - Args [3]fixed.Point26_6 -} - -// SegmentOp is a vector path segment's operator. -type SegmentOp uint32 - -const ( - SegmentOpMoveTo SegmentOp = iota - SegmentOpLineTo - SegmentOpQuadTo - SegmentOpCubeTo -) - -// translateArgs applies a translation to args. -func translateArgs(args *[3]fixed.Point26_6, dx, dy fixed.Int26_6) { - args[0].X += dx - args[0].Y += dy - args[1].X += dx - args[1].Y += dy - args[2].X += dx - args[2].Y += dy -} - -// transformArgs applies an affine transformation to args. The t?? arguments -// are 2.14 fixed point values. -func transformArgs(args *[3]fixed.Point26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) { - args[0] = tform(txx, txy, tyx, tyy, dx, dy, args[0]) - args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[1]) - args[2] = tform(txx, txy, tyx, tyy, dx, dy, args[2]) -} - -func tform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, p fixed.Point26_6) fixed.Point26_6 { - const half = 1 << 13 - return fixed.Point26_6{ - X: dx + - fixed.Int26_6((int64(p.X)*int64(txx)+half)>>14) + - fixed.Int26_6((int64(p.Y)*int64(tyx)+half)>>14), - Y: dy + - fixed.Int26_6((int64(p.X)*int64(txy)+half)>>14) + - fixed.Int26_6((int64(p.Y)*int64(tyy)+half)>>14), - } -} -- cgit v1.2.3