Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+354
View File
@@ -0,0 +1,354 @@
// Copyright 2015 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.
// Package font defines an interface for font faces, for drawing text on an
// image.
//
// Other packages provide font face implementations. For example, a truetype
// package would provide one based on .ttf font files.
package font // import "golang.org/x/image/font"
import (
"image"
"image/draw"
"io"
"unicode/utf8"
"golang.org/x/image/math/fixed"
)
// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
// The Drawer or the Face?
// Face is a font face. Its glyphs are often derived from a font file, such as
// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
// different faces, even if derived from the same font file.
//
// A Face is not safe for concurrent use by multiple goroutines, as its methods
// may re-use implementation-specific caches and mask image buffers.
//
// To create a Face, look to other packages that implement specific font file
// formats.
type Face interface {
io.Closer
// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
// glyph at the sub-pixel destination location dot, and that glyph's
// advance width.
//
// It returns !ok if the face does not contain a glyph for r. This includes
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
// or OpenType's .notdef glyph), in which case the other return values may
// still be non-zero.
//
// The contents of the mask image returned by one Glyph call may change
// after the next Glyph call. Callers that want to cache the mask must make
// a copy.
Glyph(dot fixed.Point26_6, r rune) (
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool)
// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
// to the origin, and that glyph's advance width.
//
// It returns !ok if the face does not contain a glyph for r. This includes
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
// or OpenType's .notdef glyph), in which case the other return values may
// still be non-zero.
//
// The glyph's ascent and descent are equal to -bounds.Min.Y and
// +bounds.Max.Y. The glyph's left-side and right-side bearings are equal
// to bounds.Min.X and advance-bounds.Max.X. A visual depiction of what
// these metrics are is at
// https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png
GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool)
// GlyphAdvance returns the advance width of r's glyph.
//
// It returns !ok if the face does not contain a glyph for r. This includes
// returning !ok for a fallback glyph (such as substituting a U+FFFD glyph
// or OpenType's .notdef glyph), in which case the other return values may
// still be non-zero.
GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool)
// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
// positive kern means to move the glyphs further apart.
Kern(r0, r1 rune) fixed.Int26_6
// Metrics returns the metrics for this Face.
Metrics() Metrics
// TODO: ColoredGlyph for various emoji?
// TODO: Ligatures? Shaping?
}
// Metrics holds the metrics for a Face. A visual depiction is at
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
type Metrics struct {
// Height is the recommended amount of vertical space between two lines of
// text.
Height fixed.Int26_6
// Ascent is the distance from the top of a line to its baseline.
Ascent fixed.Int26_6
// Descent is the distance from the bottom of a line to its baseline. The
// value is typically positive, even though a descender goes below the
// baseline.
Descent fixed.Int26_6
// XHeight is the distance from the top of non-ascending lowercase letters
// to the baseline.
XHeight fixed.Int26_6
// CapHeight is the distance from the top of uppercase letters to the
// baseline.
CapHeight fixed.Int26_6
// CaretSlope is the slope of a caret as a vector with the Y axis pointing up.
// The slope {0, 1} is the vertical caret.
CaretSlope image.Point
}
// Drawer draws text on a destination image.
//
// A Drawer is not safe for concurrent use by multiple goroutines, since its
// Face is not.
type Drawer struct {
// Dst is the destination image.
Dst draw.Image
// Src is the source image.
Src image.Image
// Face provides the glyph mask images.
Face Face
// Dot is the baseline location to draw the next glyph. The majority of the
// affected pixels will be above and to the right of the dot, but some may
// be below or to the left. For example, drawing a 'j' in an italic face
// may affect pixels below and to the left of the dot.
Dot fixed.Point26_6
// TODO: Clip image.Image?
// TODO: SrcP image.Point for Src images other than *image.Uniform? How
// does it get updated during DrawString?
}
// TODO: should DrawString return the last rune drawn, so the next DrawString
// call can kern beforehand? Or should that be the responsibility of the caller
// if they really want to do that, since they have to explicitly shift d.Dot
// anyway? What if ligatures span more than two runes? What if grapheme
// clusters span multiple runes?
//
// TODO: do we assume that the input is in any particular Unicode Normalization
// Form?
//
// TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take
// io.RuneReader, we can't assume that we can rewind the stream.
//
// TODO: how does this work with line breaking: drawing text up until a
// vertical line? Should DrawString return the number of runes drawn?
// DrawBytes draws s at the dot and advances the dot's location.
//
// It is equivalent to DrawString(string(s)) but may be more efficient.
func (d *Drawer) DrawBytes(s []byte) {
prevC := rune(-1)
for len(s) > 0 {
c, size := utf8.DecodeRune(s)
s = s[size:]
if prevC >= 0 {
d.Dot.X += d.Face.Kern(prevC, c)
}
dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
if !dr.Empty() {
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
}
d.Dot.X += advance
prevC = c
}
}
// DrawString draws s at the dot and advances the dot's location.
func (d *Drawer) DrawString(s string) {
prevC := rune(-1)
for _, c := range s {
if prevC >= 0 {
d.Dot.X += d.Face.Kern(prevC, c)
}
dr, mask, maskp, advance, _ := d.Face.Glyph(d.Dot, c)
if !dr.Empty() {
draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
}
d.Dot.X += advance
prevC = c
}
}
// BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as
// the advance.
//
// It is equivalent to BoundBytes(string(s)) but may be more efficient.
func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
bounds, advance = BoundBytes(d.Face, s)
bounds.Min = bounds.Min.Add(d.Dot)
bounds.Max = bounds.Max.Add(d.Dot)
return
}
// BoundString returns the bounding box of s, drawn at the drawer dot, as well
// as the advance.
func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
bounds, advance = BoundString(d.Face, s)
bounds.Min = bounds.Min.Add(d.Dot)
bounds.Max = bounds.Max.Add(d.Dot)
return
}
// MeasureBytes returns how far dot would advance by drawing s.
//
// It is equivalent to MeasureString(string(s)) but may be more efficient.
func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) {
return MeasureBytes(d.Face, s)
}
// MeasureString returns how far dot would advance by drawing s.
func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) {
return MeasureString(d.Face, s)
}
// BoundBytes returns the bounding box of s with f, drawn at a dot equal to the
// origin, as well as the advance.
//
// It is equivalent to BoundString(string(s)) but may be more efficient.
func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
prevC := rune(-1)
for len(s) > 0 {
c, size := utf8.DecodeRune(s)
s = s[size:]
if prevC >= 0 {
advance += f.Kern(prevC, c)
}
b, a, _ := f.GlyphBounds(c)
if !b.Empty() {
b.Min.X += advance
b.Max.X += advance
bounds = bounds.Union(b)
}
advance += a
prevC = c
}
return
}
// BoundString returns the bounding box of s with f, drawn at a dot equal to the
// origin, as well as the advance.
func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
prevC := rune(-1)
for _, c := range s {
if prevC >= 0 {
advance += f.Kern(prevC, c)
}
b, a, _ := f.GlyphBounds(c)
if !b.Empty() {
b.Min.X += advance
b.Max.X += advance
bounds = bounds.Union(b)
}
advance += a
prevC = c
}
return
}
// MeasureBytes returns how far dot would advance by drawing s with f.
//
// It is equivalent to MeasureString(string(s)) but may be more efficient.
func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) {
prevC := rune(-1)
for len(s) > 0 {
c, size := utf8.DecodeRune(s)
s = s[size:]
if prevC >= 0 {
advance += f.Kern(prevC, c)
}
a, _ := f.GlyphAdvance(c)
advance += a
prevC = c
}
return advance
}
// MeasureString returns how far dot would advance by drawing s with f.
func MeasureString(f Face, s string) (advance fixed.Int26_6) {
prevC := rune(-1)
for _, c := range s {
if prevC >= 0 {
advance += f.Kern(prevC, c)
}
a, _ := f.GlyphAdvance(c)
advance += a
prevC = c
}
return advance
}
// Hinting selects how to quantize a vector font's glyph nodes.
//
// Not all fonts support hinting.
type Hinting int
const (
HintingNone Hinting = iota
HintingVertical
HintingFull
)
// Stretch selects a normal, condensed, or expanded face.
//
// Not all fonts support stretches.
type Stretch int
const (
StretchUltraCondensed Stretch = -4
StretchExtraCondensed Stretch = -3
StretchCondensed Stretch = -2
StretchSemiCondensed Stretch = -1
StretchNormal Stretch = +0
StretchSemiExpanded Stretch = +1
StretchExpanded Stretch = +2
StretchExtraExpanded Stretch = +3
StretchUltraExpanded Stretch = +4
)
// Style selects a normal, italic, or oblique face.
//
// Not all fonts support styles.
type Style int
const (
StyleNormal Style = iota
StyleItalic
StyleOblique
)
// Weight selects a normal, light or bold face.
//
// Not all fonts support weights.
//
// The named Weight constants (e.g. WeightBold) correspond to CSS' common
// weight names (e.g. "Bold"), but the numerical values differ, so that in Go,
// the zero value means to use a normal weight. For the CSS names and values,
// see https://developer.mozilla.org/en/docs/Web/CSS/font-weight
type Weight int
const (
WeightThin Weight = -3 // CSS font-weight value 100.
WeightExtraLight Weight = -2 // CSS font-weight value 200.
WeightLight Weight = -1 // CSS font-weight value 300.
WeightNormal Weight = +0 // CSS font-weight value 400.
WeightMedium Weight = +1 // CSS font-weight value 500.
WeightSemiBold Weight = +2 // CSS font-weight value 600.
WeightBold Weight = +3 // CSS font-weight value 700.
WeightExtraBold Weight = +4 // CSS font-weight value 800.
WeightBlack Weight = +5 // CSS font-weight value 900.
)
File diff suppressed because it is too large Load Diff
+269
View File
@@ -0,0 +1,269 @@
// Copyright 2017 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.
// Package opentype implements a glyph rasterizer for TTF (TrueType Fonts) and
// OTF (OpenType Fonts).
//
// This package provides a high-level API, centered on the NewFace function,
// implementing the golang.org/x/image/font.Face interface.
//
// The sibling golang.org/x/image/font/sfnt package provides a low-level API.
package opentype // import "golang.org/x/image/font/opentype"
import (
"image"
"image/draw"
"io"
"golang.org/x/image/font"
"golang.org/x/image/font/sfnt"
"golang.org/x/image/math/fixed"
"golang.org/x/image/vector"
)
// ParseCollection parses an OpenType 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) {
return sfnt.ParseCollection(src)
}
// ParseCollectionReaderAt parses an OpenType 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) {
return sfnt.ParseCollectionReaderAt(src)
}
// Collection is a collection of one or more fonts.
//
// All of the Collection methods are safe to call concurrently.
type Collection = sfnt.Collection
// Parse parses an OpenType font, such as TTF or OTF data, from a []byte data
// source.
func Parse(src []byte) (*Font, error) {
return sfnt.Parse(src)
}
// ParseReaderAt parses an OpenType font, such as TTF or OTF data, from an
// io.ReaderAt data source.
func ParseReaderAt(src io.ReaderAt) (*Font, error) {
return sfnt.ParseReaderAt(src)
}
// Font is an OpenType font, also known as an SFNT font.
//
// All of the Font methods are safe to call concurrently, as long as each call
// has a different *sfnt.Buffer (or nil).
//
// The Font methods that don't take a *sfnt.Buffer argument are always safe to
// call concurrently.
type Font = sfnt.Font
// FaceOptions describes the possible options given to NewFace when
// creating a new font.Face from a Font.
type FaceOptions struct {
Size float64 // Size is the font size in points
DPI float64 // DPI is the dots per inch resolution
Hinting font.Hinting // Hinting selects how to quantize a vector font's glyph nodes
}
func defaultFaceOptions() *FaceOptions {
return &FaceOptions{
Size: 12,
DPI: 72,
Hinting: font.HintingNone,
}
}
// Face implements the font.Face interface for Font values.
//
// A Face is not safe to use concurrently.
type Face struct {
f *Font
hinting font.Hinting
scale fixed.Int26_6
metrics font.Metrics
metricsSet bool
buf sfnt.Buffer
rast vector.Rasterizer
mask image.Alpha
}
// NewFace returns a new font.Face for the given Font.
//
// If opts is nil, sensible defaults will be used.
func NewFace(f *Font, opts *FaceOptions) (font.Face, error) {
if opts == nil {
opts = defaultFaceOptions()
}
face := &Face{
f: f,
hinting: opts.Hinting,
scale: fixed.Int26_6(0.5 + (opts.Size * opts.DPI * 64 / 72)),
}
return face, nil
}
// Close satisfies the font.Face interface.
func (f *Face) Close() error {
return nil
}
// Metrics satisfies the font.Face interface.
func (f *Face) Metrics() font.Metrics {
if !f.metricsSet {
var err error
f.metrics, err = f.f.Metrics(&f.buf, f.scale, f.hinting)
if err != nil {
f.metrics = font.Metrics{}
}
f.metricsSet = true
}
return f.metrics
}
// Kern satisfies the font.Face interface.
func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 {
x0, _ := f.f.GlyphIndex(&f.buf, r0)
x1, _ := f.f.GlyphIndex(&f.buf, r1)
k, err := f.f.Kern(&f.buf, x0, x1, fixed.Int26_6(f.f.UnitsPerEm()), f.hinting)
if err != nil {
return 0
}
return k
}
// Glyph satisfies the font.Face interface.
func (f *Face) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
x, err := f.f.GlyphIndex(&f.buf, r)
if err != nil {
return image.Rectangle{}, nil, image.Point{}, 0, false
}
// Call f.f.GlyphAdvance before f.f.LoadGlyph because the LoadGlyph docs
// say this about the &f.buf argument: the segments become invalid to use
// once [the buffer] is re-used.
advance, err = f.f.GlyphAdvance(&f.buf, x, f.scale, f.hinting)
if err != nil {
return image.Rectangle{}, nil, image.Point{}, 0, false
}
segments, err := f.f.LoadGlyph(&f.buf, x, f.scale, nil)
if err != nil {
return image.Rectangle{}, nil, image.Point{}, 0, false
}
// Numerical notation used below:
// - 2 is an integer, "two"
// - 2:16 is a 26.6 fixed point number, "two and a quarter"
// - 2.5 is a float32 number, "two and a half"
// Using 26.6 fixed point numbers means that there are 64 sub-pixel units
// in 1 integer pixel unit.
// Translate the sub-pixel bounding box from glyph space (where the glyph
// origin is at (0:00, 0:00)) to dst space (where the glyph origin is at
// the dot). dst space is the coordinate space that contains both the dot
// (a sub-pixel position) and dr (an integer-pixel rectangle).
dBounds := segments.Bounds().Add(dot)
// Quantize the sub-pixel bounds (dBounds) to integer-pixel bounds (dr).
dr.Min.X = dBounds.Min.X.Floor()
dr.Min.Y = dBounds.Min.Y.Floor()
dr.Max.X = dBounds.Max.X.Ceil()
dr.Max.Y = dBounds.Max.Y.Ceil()
width := dr.Dx()
height := dr.Dy()
if width < 0 || height < 0 {
return image.Rectangle{}, nil, image.Point{}, 0, false
}
// Calculate the sub-pixel bias to convert from glyph space to rasterizer
// space. In glyph space, the segments may be to the left or right and
// above or below the glyph origin. In rasterizer space, the segments
// should only be right and below (or equal to) the top-left corner (0.0,
// 0.0). They should also be left and above (or equal to) the bottom-right
// corner (width, height), as the rasterizer should enclose the glyph
// bounding box.
//
// For example, suppose that dot.X was at the sub-pixel position 25:48,
// three quarters of the way into the 26th pixel, and that bounds.Min.X was
// 1:20. We then have dBounds.Min.X = 1:20 + 25:48 = 27:04, dr.Min.X = 27
// and biasX = 25:48 - 27:00 = -1:16. A vertical stroke at 1:20 in glyph
// space becomes (1:20 + -1:16) = 0:04 in rasterizer space. 0:04 as a
// fixed.Int26_6 value is float32(4)/64.0 = 0.0625 as a float32 value.
biasX := dot.X - fixed.Int26_6(dr.Min.X<<6)
biasY := dot.Y - fixed.Int26_6(dr.Min.Y<<6)
// Configure the mask image, re-allocating its buffer if necessary.
nPixels := width * height
if cap(f.mask.Pix) < nPixels {
f.mask.Pix = make([]uint8, 2*nPixels)
}
f.mask.Pix = f.mask.Pix[:nPixels]
f.mask.Stride = width
f.mask.Rect.Min.X = 0
f.mask.Rect.Min.Y = 0
f.mask.Rect.Max.X = width
f.mask.Rect.Max.Y = height
// Rasterize the biased segments, converting from fixed.Int26_6 to float32.
f.rast.Reset(width, height)
f.rast.DrawOp = draw.Src
for _, seg := range segments {
switch seg.Op {
case sfnt.SegmentOpMoveTo:
f.rast.MoveTo(
float32(seg.Args[0].X+biasX)/64,
float32(seg.Args[0].Y+biasY)/64,
)
case sfnt.SegmentOpLineTo:
f.rast.LineTo(
float32(seg.Args[0].X+biasX)/64,
float32(seg.Args[0].Y+biasY)/64,
)
case sfnt.SegmentOpQuadTo:
f.rast.QuadTo(
float32(seg.Args[0].X+biasX)/64,
float32(seg.Args[0].Y+biasY)/64,
float32(seg.Args[1].X+biasX)/64,
float32(seg.Args[1].Y+biasY)/64,
)
case sfnt.SegmentOpCubeTo:
f.rast.CubeTo(
float32(seg.Args[0].X+biasX)/64,
float32(seg.Args[0].Y+biasY)/64,
float32(seg.Args[1].X+biasX)/64,
float32(seg.Args[1].Y+biasY)/64,
float32(seg.Args[2].X+biasX)/64,
float32(seg.Args[2].Y+biasY)/64,
)
}
}
f.rast.Draw(&f.mask, f.mask.Bounds(), image.Opaque, image.Point{})
return dr, &f.mask, f.mask.Rect.Min, advance, x != 0
}
// GlyphBounds satisfies the font.Face interface.
func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
x, _ := f.f.GlyphIndex(&f.buf, r)
bounds, advance, err := f.f.GlyphBounds(&f.buf, x, f.scale, f.hinting)
return bounds, advance, (err == nil) && (x != 0)
}
// GlyphAdvance satisfies the font.Face interface.
func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
x, _ := f.f.GlyphIndex(&f.buf, r)
advance, err := f.f.GlyphAdvance(&f.buf, x, f.scale, f.hinting)
return advance, (err == nil) && (x != 0)
}
+312
View File
@@ -0,0 +1,312 @@
// Copyright 2017 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.
package sfnt
import (
"golang.org/x/text/encoding/charmap"
)
// Platform IDs and Platform Specific IDs as per
// https://www.microsoft.com/typography/otspec/name.htm
const (
pidUnicode = 0
pidMacintosh = 1
pidWindows = 3
psidUnicode2BMPOnly = 3
psidUnicode2FullRepertoire = 4
// Note that FontForge may generate a bogus Platform Specific ID (value 10)
// for the Unicode Platform ID (value 0). See
// https://github.com/fontforge/fontforge/issues/2728
psidMacintoshRoman = 0
psidWindowsSymbol = 0
psidWindowsUCS2 = 1
psidWindowsUCS4 = 10
)
// platformEncodingWidth returns the number of bytes per character assumed by
// the given Platform ID and Platform Specific ID.
//
// Very old fonts, from before Unicode was widely adopted, assume only 1 byte
// per character: a character map.
//
// Old fonts, from when Unicode meant the Basic Multilingual Plane (BMP),
// assume that 2 bytes per character is sufficient.
//
// Recent fonts naturally support the full range of Unicode code points, which
// can take up to 4 bytes per character. Such fonts might still choose one of
// the legacy encodings if e.g. their repertoire is limited to the BMP, for
// greater compatibility with older software, or because the resultant file
// size can be smaller.
func platformEncodingWidth(pid, psid uint16) int {
switch pid {
case pidUnicode:
switch psid {
case psidUnicode2BMPOnly:
return 2
case psidUnicode2FullRepertoire:
return 4
}
case pidMacintosh:
switch psid {
case psidMacintoshRoman:
return 1
}
case pidWindows:
switch psid {
case psidWindowsSymbol:
return 2
case psidWindowsUCS2:
return 2
case psidWindowsUCS4:
return 4
}
}
return 0
}
// The various cmap formats are described at
// https://www.microsoft.com/typography/otspec/cmap.htm
var supportedCmapFormat = func(format, pid, psid uint16) bool {
switch format {
case 0:
return pid == pidMacintosh && psid == psidMacintoshRoman
case 4:
return true
case 6:
return true
case 12:
return true
}
return false
}
func (f *Font) makeCachedGlyphIndex(buf []byte, offset, length uint32, format uint16) ([]byte, glyphIndexFunc, error) {
switch format {
case 0:
return f.makeCachedGlyphIndexFormat0(buf, offset, length)
case 4:
return f.makeCachedGlyphIndexFormat4(buf, offset, length)
case 6:
return f.makeCachedGlyphIndexFormat6(buf, offset, length)
case 12:
return f.makeCachedGlyphIndexFormat12(buf, offset, length)
}
panic("unreachable")
}
func (f *Font) makeCachedGlyphIndexFormat0(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
if length != 6+256 || offset+length > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
var err error
buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(length))
if err != nil {
return nil, nil, err
}
var table [256]byte
copy(table[:], buf[6:])
return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
x, ok := charmap.Macintosh.EncodeRune(r)
if !ok {
// The source rune r is not representable in the Macintosh-Roman encoding.
return 0, nil
}
return GlyphIndex(table[x]), nil
}, nil
}
func (f *Font) makeCachedGlyphIndexFormat4(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
const headerSize = 14
if offset+headerSize > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
var err error
buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
if err != nil {
return nil, nil, err
}
offset += headerSize
segCount := u16(buf[6:])
if segCount&1 != 0 {
return nil, nil, errInvalidCmapTable
}
segCount /= 2
if segCount > maxCmapSegments {
return nil, nil, errUnsupportedNumberOfCmapSegments
}
eLength := 8*uint32(segCount) + 2
if offset+eLength > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
if err != nil {
return nil, nil, err
}
offset += eLength
entries := make([]cmapEntry16, segCount)
for i := range entries {
entries[i] = cmapEntry16{
end: u16(buf[0*len(entries)+0+2*i:]),
start: u16(buf[2*len(entries)+2+2*i:]),
delta: u16(buf[4*len(entries)+2+2*i:]),
offset: u16(buf[6*len(entries)+2+2*i:]),
}
}
indexesBase := f.cmap.offset + offset
indexesLength := f.cmap.length - offset
return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
if uint32(r) > 0xffff {
return 0, nil
}
c := uint16(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 if entry.offset == 0 {
return GlyphIndex(c + entry.delta), nil
} else {
offset := uint32(entry.offset) + 2*uint32(h-len(entries)+int(c-entry.start))
if offset > indexesLength || offset+2 > indexesLength {
return 0, errInvalidCmapTable
}
if b == nil {
b = &Buffer{}
}
x, err := b.view(&f.src, int(indexesBase+offset), 2)
if err != nil {
return 0, err
}
return GlyphIndex(u16(x)), nil
}
}
return 0, nil
}, nil
}
func (f *Font) makeCachedGlyphIndexFormat6(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
const headerSize = 10
if offset+headerSize > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
var err error
buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
if err != nil {
return nil, nil, err
}
offset += headerSize
firstCode := u16(buf[6:])
entryCount := u16(buf[8:])
eLength := 2 * uint32(entryCount)
if offset+eLength > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
if entryCount != 0 {
buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
if err != nil {
return nil, nil, err
}
offset += eLength
}
entries := make([]uint16, entryCount)
for i := range entries {
entries[i] = u16(buf[2*i:])
}
return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
if uint16(r) < firstCode {
return 0, nil
}
c := int(uint16(r) - firstCode)
if c >= len(entries) {
return 0, nil
}
return GlyphIndex(entries[c]), nil
}, nil
}
func (f *Font) makeCachedGlyphIndexFormat12(buf []byte, offset, _ uint32) ([]byte, glyphIndexFunc, error) {
const headerSize = 16
if offset+headerSize > f.cmap.length {
return nil, nil, errInvalidCmapTable
}
var err error
buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
if err != nil {
return nil, nil, err
}
length := u32(buf[4:])
if f.cmap.length < offset || length > f.cmap.length-offset {
return nil, nil, errInvalidCmapTable
}
offset += headerSize
numGroups := u32(buf[12:])
if numGroups > maxCmapSegments {
return nil, nil, errUnsupportedNumberOfCmapSegments
}
eLength := 12 * numGroups
if headerSize+eLength != length {
return nil, nil, errInvalidCmapTable
}
buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
if err != nil {
return nil, 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:]),
}
}
return buf, 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
}, nil
}
type cmapEntry16 struct {
end, start, delta, offset uint16
}
type cmapEntry32 struct {
start, end, delta uint32
}
+68
View File
@@ -0,0 +1,68 @@
// generated by go run gen.go; DO NOT EDIT
package sfnt
const numBuiltInPostNames = 258
const builtInPostNamesData = "" +
".notdef.nullnonmarkingreturnspaceexclamquotedblnumbersigndollarp" +
"ercentampersandquotesingleparenleftparenrightasteriskpluscommahy" +
"phenperiodslashzeroonetwothreefourfivesixseveneightninecolonsemi" +
"colonlessequalgreaterquestionatABCDEFGHIJKLMNOPQRSTUVWXYZbracket" +
"leftbackslashbracketrightasciicircumunderscoregraveabcdefghijklm" +
"nopqrstuvwxyzbraceleftbarbracerightasciitildeAdieresisAringCcedi" +
"llaEacuteNtildeOdieresisUdieresisaacuteagraveacircumflexadieresi" +
"satildearingccedillaeacuteegraveecircumflexedieresisiacuteigrave" +
"icircumflexidieresisntildeoacuteograveocircumflexodieresisotilde" +
"uacuteugraveucircumflexudieresisdaggerdegreecentsterlingsectionb" +
"ulletparagraphgermandblsregisteredcopyrighttrademarkacutedieresi" +
"snotequalAEOslashinfinityplusminuslessequalgreaterequalyenmupart" +
"ialdiffsummationproductpiintegralordfeminineordmasculineOmegaaeo" +
"slashquestiondownexclamdownlogicalnotradicalflorinapproxequalDel" +
"taguillemotleftguillemotrightellipsisnonbreakingspaceAgraveAtild" +
"eOtildeOEoeendashemdashquotedblleftquotedblrightquoteleftquoteri" +
"ghtdividelozengeydieresisYdieresisfractioncurrencyguilsinglleftg" +
"uilsinglrightfifldaggerdblperiodcenteredquotesinglbasequotedblba" +
"seperthousandAcircumflexEcircumflexAacuteEdieresisEgraveIacuteIc" +
"ircumflexIdieresisIgraveOacuteOcircumflexappleOgraveUacuteUcircu" +
"mflexUgravedotlessicircumflextildemacronbrevedotaccentringcedill" +
"ahungarumlautogonekcaronLslashlslashScaronscaronZcaronzcaronbrok" +
"enbarEthethYacuteyacuteThornthornminusmultiplyonesuperiortwosupe" +
"riorthreesuperioronehalfonequarterthreequartersfrancGbrevegbreve" +
"IdotaccentScedillascedillaCacutecacuteCcaronccarondcroat"
var builtInPostNamesOffsets = [...]uint16{
0x0000, 0x0007, 0x000c, 0x001c, 0x0021, 0x0027, 0x002f, 0x0039,
0x003f, 0x0046, 0x004f, 0x005a, 0x0063, 0x006d, 0x0075, 0x0079,
0x007e, 0x0084, 0x008a, 0x008f, 0x0093, 0x0096, 0x0099, 0x009e,
0x00a2, 0x00a6, 0x00a9, 0x00ae, 0x00b3, 0x00b7, 0x00bc, 0x00c5,
0x00c9, 0x00ce, 0x00d5, 0x00dd, 0x00df, 0x00e0, 0x00e1, 0x00e2,
0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea,
0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2,
0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x0104,
0x010d, 0x0119, 0x0124, 0x012e, 0x0133, 0x0134, 0x0135, 0x0136,
0x0137, 0x0138, 0x0139, 0x013a, 0x013b, 0x013c, 0x013d, 0x013e,
0x013f, 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146,
0x0147, 0x0148, 0x0149, 0x014a, 0x014b, 0x014c, 0x014d, 0x0156,
0x0159, 0x0163, 0x016d, 0x0176, 0x017b, 0x0183, 0x0189, 0x018f,
0x0198, 0x01a1, 0x01a7, 0x01ad, 0x01b8, 0x01c1, 0x01c7, 0x01cc,
0x01d4, 0x01da, 0x01e0, 0x01eb, 0x01f4, 0x01fa, 0x0200, 0x020b,
0x0214, 0x021a, 0x0220, 0x0226, 0x0231, 0x023a, 0x0240, 0x0246,
0x024c, 0x0257, 0x0260, 0x0266, 0x026c, 0x0270, 0x0278, 0x027f,
0x0285, 0x028e, 0x0298, 0x02a2, 0x02ab, 0x02b4, 0x02b9, 0x02c1,
0x02c9, 0x02cb, 0x02d1, 0x02d9, 0x02e2, 0x02eb, 0x02f7, 0x02fa,
0x02fc, 0x0307, 0x0310, 0x0317, 0x0319, 0x0321, 0x032c, 0x0338,
0x033d, 0x033f, 0x0345, 0x0351, 0x035b, 0x0365, 0x036c, 0x0372,
0x037d, 0x0382, 0x038f, 0x039d, 0x03a5, 0x03b5, 0x03bb, 0x03c1,
0x03c7, 0x03c9, 0x03cb, 0x03d1, 0x03d7, 0x03e3, 0x03f0, 0x03f9,
0x0403, 0x0409, 0x0410, 0x0419, 0x0422, 0x042a, 0x0432, 0x043f,
0x044d, 0x044f, 0x0451, 0x045a, 0x0468, 0x0476, 0x0482, 0x048d,
0x0498, 0x04a3, 0x04a9, 0x04b2, 0x04b8, 0x04be, 0x04c9, 0x04d2,
0x04d8, 0x04de, 0x04e9, 0x04ee, 0x04f4, 0x04fa, 0x0505, 0x050b,
0x0513, 0x051d, 0x0522, 0x0528, 0x052d, 0x0536, 0x053a, 0x0541,
0x054d, 0x0553, 0x0558, 0x055e, 0x0564, 0x056a, 0x0570, 0x0576,
0x057c, 0x0585, 0x0588, 0x058b, 0x0591, 0x0597, 0x059c, 0x05a1,
0x05a6, 0x05ae, 0x05b9, 0x05c4, 0x05d1, 0x05d8, 0x05e2, 0x05ef,
0x05f4, 0x05fa, 0x0600, 0x060a, 0x0612, 0x061a, 0x0620, 0x0626,
0x062c, 0x0632, 0x0638,
}
+550
View File
@@ -0,0 +1,550 @@
// Copyright 2019 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.
package sfnt
import (
"sort"
)
const (
hexScriptLatn = uint32(0x6c61746e) // latn
hexScriptDFLT = uint32(0x44464c54) // DFLT
hexFeatureKern = uint32(0x6b65726e) // kern
)
// kernFunc returns the unscaled kerning value for kerning pair a+b.
// Returns ErrNotFound if no kerning is specified for this pair.
type kernFunc func(a, b GlyphIndex) (int16, error)
func (f *Font) parseGPOSKern(buf []byte) ([]byte, []kernFunc, error) {
// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
if f.gpos.length == 0 {
return buf, nil, nil
}
const headerSize = 10 // GPOS header v1.1 is 14 bytes, but we don't support FeatureVariations
if f.gpos.length < headerSize {
return buf, nil, errInvalidGPOSTable
}
buf, err := f.src.view(buf, int(f.gpos.offset), headerSize)
if err != nil {
return buf, nil, err
}
// check for version 1.0/1.1
if u16(buf) != 1 || u16(buf[2:]) > 1 {
return buf, nil, errUnsupportedGPOSTable
}
scriptListOffset := u16(buf[4:])
featureListOffset := u16(buf[6:])
lookupListOffset := u16(buf[8:])
// get all feature indices for latn script
buf, featureIdxs, err := f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptLatn)
if err != nil {
return buf, nil, err
}
if len(featureIdxs) == 0 {
// get all feature indices for DFLT script
buf, featureIdxs, err = f.parseGPOSScriptFeatures(buf, int(f.gpos.offset)+int(scriptListOffset), hexScriptDFLT)
if err != nil {
return buf, nil, err
}
if len(featureIdxs) == 0 {
return buf, nil, nil
}
}
// get all lookup indices for kern features
buf, lookupIdx, err := f.parseGPOSFeaturesLookup(buf, int(f.gpos.offset)+int(featureListOffset), featureIdxs, hexFeatureKern)
if err != nil {
return buf, nil, err
}
// LookupTableList: lookupCount,[]lookups
buf, numLookupTables, err := f.src.varLenView(buf, int(f.gpos.offset)+int(lookupListOffset), 2, 0, 2)
if err != nil {
return buf, nil, err
}
var kernFuncs []kernFunc
lookupTables:
for _, n := range lookupIdx {
if n > numLookupTables {
return buf, nil, errInvalidGPOSTable
}
tableOffset := int(f.gpos.offset) + int(lookupListOffset) + int(u16(buf[2+n*2:]))
// LookupTable: lookupType, lookupFlag, subTableCount, []subtableOffsets, markFilteringSet
buf, numSubTables, err := f.src.varLenView(buf, tableOffset, 8, 4, 2)
if err != nil {
return buf, nil, err
}
flags := u16(buf[2:])
subTableOffsets := make([]int, numSubTables)
for i := 0; i < int(numSubTables); i++ {
subTableOffsets[i] = int(tableOffset) + int(u16(buf[6+i*2:]))
}
switch lookupType := u16(buf); lookupType {
case 2: // PairPos table
case 9:
// Extension Positioning table defines an additional u32 offset
// to allow subtables to exceed the 16-bit limit.
for i := range subTableOffsets {
buf, err = f.src.view(buf, subTableOffsets[i], 8)
if err != nil {
return buf, nil, err
}
if format := u16(buf); format != 1 {
return buf, nil, errUnsupportedExtensionPosFormat
}
if lookupType := u16(buf[2:]); lookupType != 2 {
continue lookupTables
}
subTableOffsets[i] += int(u32(buf[4:]))
}
default: // other types are not supported
continue
}
if flags&0x0010 > 0 {
// useMarkFilteringSet enabled, skip as it is not supported
continue
}
for _, subTableOffset := range subTableOffsets {
buf, err = f.src.view(buf, int(subTableOffset), 4)
if err != nil {
return buf, nil, err
}
format := u16(buf)
var lookupIndex indexLookupFunc
buf, lookupIndex, err = f.makeCachedCoverageLookup(buf, subTableOffset+int(u16(buf[2:])))
if err != nil {
return buf, nil, err
}
switch format {
case 1: // Adjustments for Glyph Pairs
buf, kern, err := f.parsePairPosFormat1(buf, subTableOffset, lookupIndex)
if err != nil {
return buf, nil, err
}
if kern != nil {
kernFuncs = append(kernFuncs, kern)
}
case 2: // Class Pair Adjustment
buf, kern, err := f.parsePairPosFormat2(buf, subTableOffset, lookupIndex)
if err != nil {
return buf, nil, err
}
if kern != nil {
kernFuncs = append(kernFuncs, kern)
}
}
}
}
return buf, kernFuncs, nil
}
func (f *Font) parsePairPosFormat1(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) {
// PairPos Format 1: posFormat, coverageOffset, valueFormat1,
// valueFormat2, pairSetCount, []pairSetOffsets
var err error
var nPairs int
buf, nPairs, err = f.src.varLenView(buf, offset, 10, 8, 2)
if err != nil {
return buf, nil, err
}
// check valueFormat1 and valueFormat2 flags
if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 {
// we only support kerning with X_ADVANCE for first glyph
return buf, nil, nil
}
// PairPos table contains an array of offsets to PairSet
// tables, which contains an array of PairValueRecords.
// Calculate length of complete PairPos table by jumping to
// last PairSet.
// We need to iterate all offsets to find the last pair as
// offsets are not sorted and can be repeated.
var lastPairSetOffset int
for n := 0; n < nPairs; n++ {
pairOffset := int(u16(buf[10+n*2:]))
if pairOffset > lastPairSetOffset {
lastPairSetOffset = pairOffset
}
}
buf, err = f.src.view(buf, offset+lastPairSetOffset, 2)
if err != nil {
return buf, nil, err
}
pairValueCount := int(u16(buf))
// Each PairSet contains the secondGlyph (u16) and one or more value records (all u16).
// We only support lookup tables with one value record (X_ADVANCE, see valueFormat1/2 above).
lastPairSetLength := 2 + pairValueCount*4
length := lastPairSetOffset + lastPairSetLength
buf, err = f.src.view(buf, offset, length)
if err != nil {
return buf, nil, err
}
kern := makeCachedPairPosGlyph(lookupIndex, nPairs, buf)
return buf, kern, nil
}
func (f *Font) parsePairPosFormat2(buf []byte, offset int, lookupIndex indexLookupFunc) ([]byte, kernFunc, error) {
// PairPos Format 2:
// posFormat, coverageOffset, valueFormat1, valueFormat2,
// classDef1Offset, classDef2Offset, class1Count, class2Count,
// []class1Records
var err error
buf, err = f.src.view(buf, offset, 16)
if err != nil {
return buf, nil, err
}
// check valueFormat1 and valueFormat2 flags
if u16(buf[4:]) != 0x04 || u16(buf[6:]) != 0x00 {
// we only support kerning with X_ADVANCE for first glyph
return buf, nil, nil
}
numClass1 := int(u16(buf[12:]))
numClass2 := int(u16(buf[14:]))
cdef1Offset := offset + int(u16(buf[8:]))
cdef2Offset := offset + int(u16(buf[10:]))
var cdef1, cdef2 classLookupFunc
buf, cdef1, err = f.makeCachedClassLookup(buf, cdef1Offset)
if err != nil {
return buf, nil, err
}
buf, cdef2, err = f.makeCachedClassLookup(buf, cdef2Offset)
if err != nil {
return buf, nil, err
}
buf, err = f.src.view(buf, offset+16, numClass1*numClass2*2)
if err != nil {
return buf, nil, err
}
kern := makeCachedPairPosClass(
lookupIndex,
numClass1,
numClass2,
cdef1,
cdef2,
buf,
)
return buf, kern, nil
}
// parseGPOSScriptFeatures returns all indices of features in FeatureTable that
// are valid for the given script.
// Returns features from DefaultLangSys, different languages are not supported.
// However, all observed fonts either do not use different languages or use the
// same features as DefaultLangSys.
func (f *Font) parseGPOSScriptFeatures(buf []byte, offset int, script uint32) ([]byte, []int, error) {
// ScriptList table: scriptCount, []scriptRecords{scriptTag, scriptOffset}
buf, numScriptTables, err := f.src.varLenView(buf, offset, 2, 0, 6)
if err != nil {
return buf, nil, err
}
// Search ScriptTables for script
var scriptTableOffset uint16
for i := 0; i < numScriptTables; i++ {
scriptTag := u32(buf[2+i*6:])
if scriptTag == script {
scriptTableOffset = u16(buf[2+i*6+4:])
break
}
}
if scriptTableOffset == 0 {
return buf, nil, nil
}
// Script table: defaultLangSys, langSysCount, []langSysRecords{langSysTag, langSysOffset}
buf, err = f.src.view(buf, offset+int(scriptTableOffset), 2)
if err != nil {
return buf, nil, err
}
defaultLangSysOffset := u16(buf)
if defaultLangSysOffset == 0 {
return buf, nil, nil
}
// LangSys table: lookupOrder (reserved), requiredFeatureIndex, featureIndexCount, []featureIndices
buf, numFeatures, err := f.src.varLenView(buf, offset+int(scriptTableOffset)+int(defaultLangSysOffset), 6, 4, 2)
if err != nil {
return buf, nil, err
}
featureIdxs := make([]int, numFeatures)
for i := range featureIdxs {
featureIdxs[i] = int(u16(buf[6+i*2:]))
}
return buf, featureIdxs, nil
}
func (f *Font) parseGPOSFeaturesLookup(buf []byte, offset int, featureIdxs []int, feature uint32) ([]byte, []int, error) {
// FeatureList table: featureCount, []featureRecords{featureTag, featureOffset}
buf, numFeatureTables, err := f.src.varLenView(buf, offset, 2, 0, 6)
if err != nil {
return buf, nil, err
}
lookupIdx := make([]int, 0, 4)
for _, fidx := range featureIdxs {
if fidx > numFeatureTables {
return buf, nil, errInvalidGPOSTable
}
featureTag := u32(buf[2+fidx*6:])
if featureTag != feature {
continue
}
featureOffset := u16(buf[2+fidx*6+4:])
buf, numLookups, err := f.src.varLenView(nil, offset+int(featureOffset), 4, 2, 2)
if err != nil {
return buf, nil, err
}
for i := 0; i < numLookups; i++ {
lookupIdx = append(lookupIdx, int(u16(buf[4+i*2:])))
}
}
return buf, lookupIdx, nil
}
func makeCachedPairPosGlyph(cov indexLookupFunc, num int, buf []byte) kernFunc {
glyphs := make([]byte, len(buf))
copy(glyphs, buf)
return func(a, b GlyphIndex) (int16, error) {
idx, found := cov(a)
if !found {
return 0, ErrNotFound
}
if idx >= num {
return 0, ErrNotFound
}
offset := int(u16(glyphs[10+idx*2:]))
if offset+1 >= len(glyphs) {
return 0, errInvalidGPOSTable
}
count := int(u16(glyphs[offset:]))
for i := 0; i < count; i++ {
secondGlyphIndex := GlyphIndex(int(u16(glyphs[offset+2+i*4:])))
if secondGlyphIndex == b {
return int16(u16(glyphs[offset+2+i*4+2:])), nil
}
if secondGlyphIndex > b {
return 0, ErrNotFound
}
}
return 0, ErrNotFound
}
}
func makeCachedPairPosClass(cov indexLookupFunc, num1, num2 int, cdef1, cdef2 classLookupFunc, buf []byte) kernFunc {
glyphs := make([]byte, len(buf))
copy(glyphs, buf)
return func(a, b GlyphIndex) (int16, error) {
// check coverage to avoid selection of default class 0
_, found := cov(a)
if !found {
return 0, ErrNotFound
}
idxa := cdef1(a)
idxb := cdef2(b)
return int16(u16(glyphs[(idxb+idxa*num2)*2:])), nil
}
}
// indexLookupFunc returns the index into a PairPos table for the provided glyph.
// Returns false if the glyph is not covered by this lookup.
type indexLookupFunc func(GlyphIndex) (int, bool)
func (f *Font) makeCachedCoverageLookup(buf []byte, offset int) ([]byte, indexLookupFunc, error) {
var err error
buf, err = f.src.view(buf, offset, 2)
if err != nil {
return buf, nil, err
}
switch u16(buf) {
case 1:
// Coverage Format 1: coverageFormat, glyphCount, []glyphArray
buf, _, err = f.src.varLenView(buf, offset, 4, 2, 2)
if err != nil {
return buf, nil, err
}
return buf, makeCachedCoverageList(buf[2:]), nil
case 2:
// Coverage Format 2: coverageFormat, rangeCount, []rangeRecords{startGlyphID, endGlyphID, startCoverageIndex}
buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6)
if err != nil {
return buf, nil, err
}
return buf, makeCachedCoverageRange(buf[2:]), nil
default:
return buf, nil, errUnsupportedCoverageFormat
}
}
func makeCachedCoverageList(buf []byte) indexLookupFunc {
num := int(u16(buf))
list := make([]byte, len(buf)-2)
copy(list, buf[2:])
return func(gi GlyphIndex) (int, bool) {
idx := sort.Search(num, func(i int) bool {
return gi <= GlyphIndex(u16(list[i*2:]))
})
if idx < num && GlyphIndex(u16(list[idx*2:])) == gi {
return idx, true
}
return 0, false
}
}
func makeCachedCoverageRange(buf []byte) indexLookupFunc {
num := int(u16(buf))
ranges := make([]byte, len(buf)-2)
copy(ranges, buf[2:])
return func(gi GlyphIndex) (int, bool) {
if num == 0 {
return 0, false
}
// ranges is an array of startGlyphID, endGlyphID and startCoverageIndex
// Ranges are non-overlapping.
// The following GlyphIDs/index pairs are stored as follows:
// pairs: 130=0, 131=1, 132=2, 133=3, 134=4, 135=5, 137=6
// ranges: 130, 135, 0 137, 137, 6
// startCoverageIndex is used to calculate the index without counting
// the length of the preceding ranges
idx := sort.Search(num, func(i int) bool {
return gi <= GlyphIndex(u16(ranges[i*6:]))
})
// idx either points to a matching start, or to the next range (or idx==num)
// e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range
// check if gi is the start of a range, but only if sort.Search returned a valid result
if idx < num {
if start := u16(ranges[idx*6:]); gi == GlyphIndex(start) {
return int(u16(ranges[idx*6+4:])), true
}
}
// check if gi is in previous range
if idx > 0 {
idx--
start, end := u16(ranges[idx*6:]), u16(ranges[idx*6+2:])
if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) {
return int(u16(ranges[idx*6+4:]) + uint16(gi) - start), true
}
}
return 0, false
}
}
// classLookupFunc returns the class ID for the provided glyph. Returns 0
// (default class) for glyphs not covered by this lookup.
type classLookupFunc func(GlyphIndex) int
func (f *Font) makeCachedClassLookup(buf []byte, offset int) ([]byte, classLookupFunc, error) {
var err error
buf, err = f.src.view(buf, offset, 2)
if err != nil {
return buf, nil, err
}
switch u16(buf) {
case 1:
// ClassDefFormat 1: classFormat, startGlyphID, glyphCount, []classValueArray
buf, _, err = f.src.varLenView(buf, offset, 6, 4, 2)
if err != nil {
return buf, nil, err
}
return buf, makeCachedClassLookupFormat1(buf), nil
case 2:
// ClassDefFormat 2: classFormat, classRangeCount, []classRangeRecords
buf, _, err = f.src.varLenView(buf, offset, 4, 2, 6)
if err != nil {
return buf, nil, err
}
return buf, makeCachedClassLookupFormat2(buf), nil
default:
return buf, nil, errUnsupportedClassDefFormat
}
}
func makeCachedClassLookupFormat1(buf []byte) classLookupFunc {
startGI := u16(buf[2:])
num := u16(buf[4:])
classIDs := make([]byte, len(buf)-4)
copy(classIDs, buf[6:])
return func(gi GlyphIndex) int {
// classIDs is an array of target class IDs. gi is the index into that array (minus startGI).
if gi < GlyphIndex(startGI) || gi >= GlyphIndex(startGI+num) {
// default to class 0
return 0
}
return int(u16(classIDs[(int(gi)-int(startGI))*2:]))
}
}
func makeCachedClassLookupFormat2(buf []byte) classLookupFunc {
num := int(u16(buf[2:]))
classRanges := make([]byte, len(buf)-2)
copy(classRanges, buf[4:])
return func(gi GlyphIndex) int {
if num == 0 {
return 0 // default to class 0
}
// classRange is an array of startGlyphID, endGlyphID and target class ID.
// Ranges are non-overlapping.
// E.g. 130, 135, 1 137, 137, 5 etc
idx := sort.Search(num, func(i int) bool {
return gi <= GlyphIndex(u16(classRanges[i*6:]))
})
// idx either points to a matching start, or to the next range (or idx==num)
// e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range
// check if gi is the start of a range, but only if sort.Search returned a valid result
if idx < num {
if start := u16(classRanges[idx*6:]); gi == GlyphIndex(start) {
return int(u16(classRanges[idx*6+4:]))
}
}
// check if gi is in previous range
if idx > 0 {
idx--
start, end := u16(classRanges[idx*6:]), u16(classRanges[idx*6+2:])
if gi >= GlyphIndex(start) && gi <= GlyphIndex(end) {
return int(u16(classRanges[idx*6+4:]))
}
}
// default to class 0
return 0
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+578
View File
@@ -0,0 +1,578 @@
// Copyright 2017 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.
package sfnt
import (
"golang.org/x/image/math/fixed"
)
// Flags for simple (non-compound) glyphs.
//
// See https://www.microsoft.com/typography/OTSPEC/glyf.htm
const (
flagOnCurve = 1 << 0 // 0x0001
flagXShortVector = 1 << 1 // 0x0002
flagYShortVector = 1 << 2 // 0x0004
flagRepeat = 1 << 3 // 0x0008
// The same flag bits are overloaded to have two meanings, dependent on the
// value of the flag{X,Y}ShortVector bits.
flagPositiveXShortVector = 1 << 4 // 0x0010
flagThisXIsSame = 1 << 4 // 0x0010
flagPositiveYShortVector = 1 << 5 // 0x0020
flagThisYIsSame = 1 << 5 // 0x0020
)
// Flags for compound glyphs.
//
// See https://www.microsoft.com/typography/OTSPEC/glyf.htm
const (
flagArg1And2AreWords = 1 << 0 // 0x0001
flagArgsAreXYValues = 1 << 1 // 0x0002
flagRoundXYToGrid = 1 << 2 // 0x0004
flagWeHaveAScale = 1 << 3 // 0x0008
flagReserved4 = 1 << 4 // 0x0010
flagMoreComponents = 1 << 5 // 0x0020
flagWeHaveAnXAndYScale = 1 << 6 // 0x0040
flagWeHaveATwoByTwo = 1 << 7 // 0x0080
flagWeHaveInstructions = 1 << 8 // 0x0100
flagUseMyMetrics = 1 << 9 // 0x0200
flagOverlapCompound = 1 << 10 // 0x0400
flagScaledComponentOffset = 1 << 11 // 0x0800
flagUnscaledComponentOffset = 1 << 12 // 0x1000
)
func midPoint(p, q fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{
X: (p.X + q.X) / 2,
Y: (p.Y + q.Y) / 2,
}
}
func parseLoca(src *source, loca table, glyfOffset uint32, indexToLocFormat bool, numGlyphs int32) (locations []uint32, err error) {
if indexToLocFormat {
if loca.length != 4*uint32(numGlyphs+1) {
return nil, errInvalidLocaTable
}
} else {
if loca.length != 2*uint32(numGlyphs+1) {
return nil, errInvalidLocaTable
}
}
locations = make([]uint32, numGlyphs+1)
buf, err := src.view(nil, int(loca.offset), int(loca.length))
if err != nil {
return nil, err
}
if indexToLocFormat {
for i := range locations {
locations[i] = 1*uint32(u32(buf[4*i:])) + glyfOffset
}
} else {
for i := range locations {
locations[i] = 2*uint32(u16(buf[2*i:])) + glyfOffset
}
}
return locations, nil
}
// https://www.microsoft.com/typography/OTSPEC/glyf.htm says that "Each
// glyph begins with the following [10 byte] header".
const glyfHeaderLen = 10
func loadGlyf(f *Font, b *Buffer, x GlyphIndex, stackBottom, recursionDepth uint32) error {
data, _, _, err := f.viewGlyphData(b, x)
if err != nil {
return err
}
if len(data) == 0 {
return nil
}
if len(data) < glyfHeaderLen {
return errInvalidGlyphData
}
index := glyfHeaderLen
numContours, numPoints := int16(u16(data)), 0
switch {
case numContours == -1:
// We have a compound glyph. No-op.
case numContours == 0:
return nil
case numContours > 0:
// We have a simple (non-compound) glyph.
index += 2 * int(numContours)
if index > len(data) {
return errInvalidGlyphData
}
// The +1 for numPoints is because the value in the file format is
// inclusive, but Go's slice[:index] semantics are exclusive.
numPoints = 1 + int(u16(data[index-2:]))
default:
return errInvalidGlyphData
}
if numContours < 0 {
return loadCompoundGlyf(f, b, data[glyfHeaderLen:], stackBottom, recursionDepth)
}
// Skip the hinting instructions.
index += 2
if index > len(data) {
return errInvalidGlyphData
}
hintsLength := int(u16(data[index-2:]))
index += hintsLength
if index > len(data) {
return errInvalidGlyphData
}
// For simple (non-compound) glyphs, the remainder of the glyf data
// consists of (flags, x, y) points: the Bézier curve segments. These are
// stored in columns (all the flags first, then all the x coordinates, then
// all the y coordinates), not rows, as it compresses better.
//
// Decoding those points in row order involves two passes. The first pass
// determines the indexes (relative to the data slice) of where the flags,
// the x coordinates and the y coordinates each start.
flagIndex := int32(index)
xIndex, yIndex, ok := findXYIndexes(data, index, numPoints)
if !ok {
return errInvalidGlyphData
}
// The second pass decodes each (flags, x, y) tuple in row order.
g := glyfIter{
data: data,
flagIndex: flagIndex,
xIndex: xIndex,
yIndex: yIndex,
endIndex: glyfHeaderLen,
// The -1 on prevEnd and finalEnd are because the contour-end index in
// the file format is inclusive, but Go's slice[:index] is exclusive.
prevEnd: -1,
finalEnd: int32(numPoints - 1),
numContours: int32(numContours),
}
for g.nextContour() {
for g.nextSegment() {
b.segments = append(b.segments, g.seg)
}
}
return g.err
}
func findXYIndexes(data []byte, index, numPoints int) (xIndex, yIndex int32, ok bool) {
xDataLen := 0
yDataLen := 0
for i := 0; ; {
if i > numPoints {
return 0, 0, false
}
if i == numPoints {
break
}
repeatCount := 1
if index >= len(data) {
return 0, 0, false
}
flag := data[index]
index++
if flag&flagRepeat != 0 {
if index >= len(data) {
return 0, 0, false
}
repeatCount += int(data[index])
index++
}
xSize := 0
if flag&flagXShortVector != 0 {
xSize = 1
} else if flag&flagThisXIsSame == 0 {
xSize = 2
}
xDataLen += xSize * repeatCount
ySize := 0
if flag&flagYShortVector != 0 {
ySize = 1
} else if flag&flagThisYIsSame == 0 {
ySize = 2
}
yDataLen += ySize * repeatCount
i += repeatCount
}
if index+xDataLen+yDataLen > len(data) {
return 0, 0, false
}
return int32(index), int32(index + xDataLen), true
}
func loadCompoundGlyf(f *Font, b *Buffer, data []byte, stackBottom, recursionDepth uint32) error {
if recursionDepth++; recursionDepth == maxCompoundRecursionDepth {
return errUnsupportedCompoundGlyph
}
// Read and process the compound glyph's components. They are two separate
// for loops, since reading parses the elements of the data slice, and
// processing can overwrite the backing array.
stackTop := stackBottom
for {
if stackTop >= maxCompoundStackSize {
return errUnsupportedCompoundGlyph
}
elem := &b.compoundStack[stackTop]
stackTop++
if len(data) < 4 {
return errInvalidGlyphData
}
flags := u16(data)
elem.glyphIndex = GlyphIndex(u16(data[2:]))
if flags&flagArg1And2AreWords == 0 {
if len(data) < 6 {
return errInvalidGlyphData
}
elem.dx = int16(int8(data[4]))
elem.dy = int16(int8(data[5]))
data = data[6:]
} else {
if len(data) < 8 {
return errInvalidGlyphData
}
elem.dx = int16(u16(data[4:]))
elem.dy = int16(u16(data[6:]))
data = data[8:]
}
if flags&flagArgsAreXYValues == 0 {
return errUnsupportedCompoundGlyph
}
elem.hasTransform = flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0
if elem.hasTransform {
switch {
case flags&flagWeHaveAScale != 0:
if len(data) < 2 {
return errInvalidGlyphData
}
elem.transformXX = int16(u16(data))
elem.transformXY = 0
elem.transformYX = 0
elem.transformYY = elem.transformXX
data = data[2:]
case flags&flagWeHaveAnXAndYScale != 0:
if len(data) < 4 {
return errInvalidGlyphData
}
elem.transformXX = int16(u16(data[0:]))
elem.transformXY = 0
elem.transformYX = 0
elem.transformYY = int16(u16(data[2:]))
data = data[4:]
case flags&flagWeHaveATwoByTwo != 0:
if len(data) < 8 {
return errInvalidGlyphData
}
elem.transformXX = int16(u16(data[0:]))
elem.transformXY = int16(u16(data[2:]))
elem.transformYX = int16(u16(data[4:]))
elem.transformYY = int16(u16(data[6:]))
data = data[8:]
}
}
if flags&flagMoreComponents == 0 {
break
}
}
// To support hinting, we'd have to save the remaining bytes in data here
// and interpret them after the for loop below, since that for loop's
// loadGlyf calls can overwrite the backing array.
for i := stackBottom; i < stackTop; i++ {
elem := &b.compoundStack[i]
base := len(b.segments)
if err := loadGlyf(f, b, elem.glyphIndex, stackTop, recursionDepth); err != nil {
return err
}
dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy)
segments := b.segments[base:]
if elem.hasTransform {
txx := elem.transformXX
txy := elem.transformXY
tyx := elem.transformYX
tyy := elem.transformYY
for j := range segments {
transformArgs(&segments[j].Args, txx, txy, tyx, tyy, dx, dy)
}
} else {
for j := range segments {
translateArgs(&segments[j].Args, dx, dy)
}
}
}
return nil
}
type glyfIter struct {
data []byte
err error
// Various indices into the data slice. See the "Decoding those points in
// row order" comment above.
flagIndex int32
xIndex int32
yIndex int32
// endIndex points to the uint16 that is the inclusive point index of the
// current contour's end. prevEnd is the previous contour's end. finalEnd
// should match the final contour's end.
endIndex int32
prevEnd int32
finalEnd int32
// c and p count the current contour and point, up to numContours and
// numPoints.
c, numContours int32
p, nPoints int32
// The next two groups of fields track points and segments. Points are what
// the underlying file format provides. Bézier curve segments are what the
// rasterizer consumes.
//
// Points are either on-curve or off-curve. Two consecutive on-curve points
// define a linear curve segment between them. N off-curve points between
// on-curve points define N quadratic curve segments. The TrueType glyf
// format does not use cubic curves. If N is greater than 1, some of these
// segment end points are implicit, the midpoint of two off-curve points.
// Given the points A, B1, B2, ..., BN, C, where A and C are on-curve and
// all the Bs are off-curve, the segments are:
//
// - A, B1, midpoint(B1, B2)
// - midpoint(B1, B2), B2, midpoint(B2, B3)
// - midpoint(B2, B3), B3, midpoint(B3, B4)
// - ...
// - midpoint(BN-1, BN), BN, C
//
// Note that the sequence of Bs may wrap around from the last point in the
// glyf data to the first. A and C may also be the same point (the only
// explicit on-curve point), or there may be no explicit on-curve points at
// all (but still implicit ones between explicit off-curve points).
// Points.
x, y int16
on bool
flag uint8
repeats uint8
// Segments.
closing bool
closed bool
firstOnCurveValid bool
firstOffCurveValid bool
lastOffCurveValid bool
firstOnCurve fixed.Point26_6
firstOffCurve fixed.Point26_6
lastOffCurve fixed.Point26_6
seg Segment
}
func (g *glyfIter) nextContour() (ok bool) {
if g.c == g.numContours {
if g.prevEnd != g.finalEnd {
g.err = errInvalidGlyphData
}
return false
}
g.c++
end := int32(u16(g.data[g.endIndex:]))
g.endIndex += 2
if (end <= g.prevEnd) || (g.finalEnd < end) {
g.err = errInvalidGlyphData
return false
}
g.nPoints = end - g.prevEnd
g.p = 0
g.prevEnd = end
g.closing = false
g.closed = false
g.firstOnCurveValid = false
g.firstOffCurveValid = false
g.lastOffCurveValid = false
return true
}
func (g *glyfIter) close() {
switch {
case !g.firstOffCurveValid && !g.lastOffCurveValid:
g.closed = true
g.seg = Segment{
Op: SegmentOpLineTo,
Args: [3]fixed.Point26_6{g.firstOnCurve},
}
case !g.firstOffCurveValid && g.lastOffCurveValid:
g.closed = true
g.seg = Segment{
Op: SegmentOpQuadTo,
Args: [3]fixed.Point26_6{g.lastOffCurve, g.firstOnCurve},
}
case g.firstOffCurveValid && !g.lastOffCurveValid:
g.closed = true
g.seg = Segment{
Op: SegmentOpQuadTo,
Args: [3]fixed.Point26_6{g.firstOffCurve, g.firstOnCurve},
}
case g.firstOffCurveValid && g.lastOffCurveValid:
g.lastOffCurveValid = false
g.seg = Segment{
Op: SegmentOpQuadTo,
Args: [3]fixed.Point26_6{
g.lastOffCurve,
midPoint(g.lastOffCurve, g.firstOffCurve),
},
}
}
}
func (g *glyfIter) nextSegment() (ok bool) {
for !g.closed {
if g.closing || !g.nextPoint() {
g.closing = true
g.close()
return true
}
// Convert the tuple (g.x, g.y) to a fixed.Point26_6, since the latter
// is what's held in a Segment. The input (g.x, g.y) is a pair of int16
// values, measured in font units, since that is what the underlying
// format provides. The output is a pair of fixed.Int26_6 values. A
// fixed.Int26_6 usually represents a 26.6 fixed number of pixels, but
// this here is just a straight numerical conversion, with no scaling
// factor. A later step scales the Segment.Args values by such a factor
// to convert e.g. 1792 font units to 10.5 pixels at 2048 font units
// per em and 12 ppem (pixels per em).
p := fixed.Point26_6{
X: fixed.Int26_6(g.x),
Y: fixed.Int26_6(g.y),
}
if !g.firstOnCurveValid {
if g.on {
g.firstOnCurve = p
g.firstOnCurveValid = true
g.seg = Segment{
Op: SegmentOpMoveTo,
Args: [3]fixed.Point26_6{p},
}
return true
} else if !g.firstOffCurveValid {
g.firstOffCurve = p
g.firstOffCurveValid = true
continue
} else {
g.firstOnCurve = midPoint(g.firstOffCurve, p)
g.firstOnCurveValid = true
g.lastOffCurve = p
g.lastOffCurveValid = true
g.seg = Segment{
Op: SegmentOpMoveTo,
Args: [3]fixed.Point26_6{g.firstOnCurve},
}
return true
}
} else if !g.lastOffCurveValid {
if !g.on {
g.lastOffCurve = p
g.lastOffCurveValid = true
continue
} else {
g.seg = Segment{
Op: SegmentOpLineTo,
Args: [3]fixed.Point26_6{p},
}
return true
}
} else {
if !g.on {
g.seg = Segment{
Op: SegmentOpQuadTo,
Args: [3]fixed.Point26_6{
g.lastOffCurve,
midPoint(g.lastOffCurve, p),
},
}
g.lastOffCurve = p
g.lastOffCurveValid = true
return true
} else {
g.seg = Segment{
Op: SegmentOpQuadTo,
Args: [3]fixed.Point26_6{g.lastOffCurve, p},
}
g.lastOffCurveValid = false
return true
}
}
}
return false
}
func (g *glyfIter) nextPoint() (ok bool) {
if g.p == g.nPoints {
return false
}
g.p++
if g.repeats > 0 {
g.repeats--
} else {
g.flag = g.data[g.flagIndex]
g.flagIndex++
if g.flag&flagRepeat != 0 {
g.repeats = g.data[g.flagIndex]
g.flagIndex++
}
}
if g.flag&flagXShortVector != 0 {
if g.flag&flagPositiveXShortVector != 0 {
g.x += int16(g.data[g.xIndex])
} else {
g.x -= int16(g.data[g.xIndex])
}
g.xIndex += 1
} else if g.flag&flagThisXIsSame == 0 {
g.x += int16(u16(g.data[g.xIndex:]))
g.xIndex += 2
}
if g.flag&flagYShortVector != 0 {
if g.flag&flagPositiveYShortVector != 0 {
g.y += int16(g.data[g.yIndex])
} else {
g.y -= int16(g.data[g.yIndex])
}
g.yIndex += 1
} else if g.flag&flagThisYIsSame == 0 {
g.y += int16(u16(g.data[g.yIndex:]))
g.yIndex += 2
}
g.on = g.flag&flagOnCurve != 0
return true
}