2012-05-07 02:04:52 +00:00
|
|
|
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by your choice of either the
|
|
|
|
// FreeType License or the GNU General Public License version 2 (or
|
|
|
|
// any later version), both of which can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package truetype
|
|
|
|
|
|
|
|
// A Point is a co-ordinate pair plus whether it is ``on'' a contour or an
|
|
|
|
// ``off'' control point.
|
|
|
|
type Point struct {
|
2012-07-25 12:10:25 +00:00
|
|
|
X, Y int32
|
2012-05-07 02:04:52 +00:00
|
|
|
// The Flags' LSB means whether or not this Point is ``on'' the contour.
|
|
|
|
// Other bits are reserved for internal use.
|
2012-07-25 12:10:25 +00:00
|
|
|
Flags uint32
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
|
|
|
|
// series of glyphs from a Font.
|
|
|
|
type GlyphBuf struct {
|
2013-09-24 12:06:47 +00:00
|
|
|
// B is the glyph's bounding box.
|
2012-05-07 02:04:52 +00:00
|
|
|
B Bounds
|
2013-08-31 06:08:40 +00:00
|
|
|
// Point contains all Points from all contours of the glyph. If a
|
|
|
|
// Hinter was used to load a glyph then Unhinted contains those
|
|
|
|
// Points before they were hinted, and InFontUnits contains those
|
2013-09-24 12:06:47 +00:00
|
|
|
// Points before they were hinted and scaled.
|
|
|
|
Point, Unhinted, InFontUnits []Point
|
|
|
|
// End is the point indexes of the end point of each countour. The
|
|
|
|
// length of End is the number of contours in the glyph. The i'th
|
2012-05-07 02:04:52 +00:00
|
|
|
// contour consists of points Point[End[i-1]:End[i]], where End[-1]
|
|
|
|
// is interpreted to mean zero.
|
|
|
|
End []int
|
2013-10-16 07:54:54 +00:00
|
|
|
|
|
|
|
font *Font
|
|
|
|
hinter *Hinter
|
|
|
|
scale int32
|
2013-12-02 21:21:05 +00:00
|
|
|
// phantomPoints are the co-ordinates of the synthetic phantom points
|
|
|
|
// used for hinting and bounding box calculations.
|
|
|
|
phantomPoints [4]Point
|
|
|
|
// pp1x is the X co-ordinate of the first phantom point. The '1' is
|
|
|
|
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
|
|
|
|
// TODO: eliminate this and consistently use phantomPoints[0].X.
|
2013-11-04 22:58:40 +00:00
|
|
|
pp1x int32
|
|
|
|
// metricsSet is whether the glyph's metrics have been set yet. For a
|
|
|
|
// compound glyph, a sub-glyph may override the outer glyph's metrics.
|
2013-10-16 07:54:54 +00:00
|
|
|
metricsSet bool
|
2013-11-04 22:58:40 +00:00
|
|
|
// tmp is a scratch buffer.
|
|
|
|
tmp []Point
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flags for decoding a glyph's contours. These flags are documented at
|
|
|
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
|
|
|
const (
|
|
|
|
flagOnCurve = 1 << iota
|
|
|
|
flagXShortVector
|
|
|
|
flagYShortVector
|
|
|
|
flagRepeat
|
|
|
|
flagPositiveXShortVector
|
|
|
|
flagPositiveYShortVector
|
2013-08-31 06:08:40 +00:00
|
|
|
|
|
|
|
// The remaining flags are for internal use.
|
|
|
|
flagTouchedX
|
|
|
|
flagTouchedY
|
2012-05-07 02:04:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
|
|
|
|
// dependent on the value of the flag{X,Y}ShortVector bits.
|
|
|
|
const (
|
|
|
|
flagThisXIsSame = flagPositiveXShortVector
|
|
|
|
flagThisYIsSame = flagPositiveYShortVector
|
|
|
|
)
|
|
|
|
|
2013-10-16 07:54:54 +00:00
|
|
|
// Load loads a glyph's contours from a Font, overwriting any previously
|
|
|
|
// loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point
|
|
|
|
// units in 1 em. The Hinter is optional; if non-nil, then the resulting glyph
|
|
|
|
// will be hinted by the Font's bytecode instructions.
|
|
|
|
func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h *Hinter) error {
|
|
|
|
g.Point = g.Point[:0]
|
|
|
|
g.Unhinted = g.Unhinted[:0]
|
|
|
|
g.InFontUnits = g.InFontUnits[:0]
|
|
|
|
g.End = g.End[:0]
|
|
|
|
g.font = f
|
|
|
|
g.hinter = h
|
|
|
|
g.scale = scale
|
|
|
|
g.pp1x = 0
|
2013-12-02 21:21:05 +00:00
|
|
|
g.phantomPoints = [4]Point{}
|
2013-10-16 07:54:54 +00:00
|
|
|
g.metricsSet = false
|
|
|
|
|
|
|
|
if h != nil {
|
|
|
|
if err := h.init(f, scale); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := g.load(0, i, true); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
|
|
|
|
// and should be cleaned up once we have all the testScaling tests passing,
|
|
|
|
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
|
|
|
|
pp1x := g.pp1x
|
|
|
|
if h != nil {
|
|
|
|
pp1x = g.phantomPoints[0].X
|
|
|
|
}
|
|
|
|
if pp1x != 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
for i := range g.Point {
|
2013-12-02 21:21:05 +00:00
|
|
|
g.Point[i].X -= pp1x
|
2013-10-16 07:54:54 +00:00
|
|
|
}
|
2013-12-20 23:03:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set g.B to the 'control box', which is the bounding box of the Bézier
|
|
|
|
// curves' control points. This is easier to calculate, no smaller than
|
|
|
|
// and often equal to the tightest possible bounding box of the curves
|
|
|
|
// themselves. This approach is what C Freetype does. We can't just scale
|
|
|
|
// the nominal bounding box in the glyf data as the hinting process and
|
|
|
|
// phantom point adjustment may move points outside of that box.
|
|
|
|
if len(g.Point) == 0 {
|
|
|
|
g.B = Bounds{}
|
|
|
|
} else {
|
|
|
|
p := g.Point[0]
|
|
|
|
g.B.XMin = p.X
|
|
|
|
g.B.XMax = p.X
|
|
|
|
g.B.YMin = p.Y
|
|
|
|
g.B.YMax = p.Y
|
|
|
|
for _, p := range g.Point[1:] {
|
|
|
|
if g.B.XMin > p.X {
|
|
|
|
g.B.XMin = p.X
|
|
|
|
} else if g.B.XMax < p.X {
|
|
|
|
g.B.XMax = p.X
|
|
|
|
}
|
|
|
|
if g.B.YMin > p.Y {
|
|
|
|
g.B.YMin = p.Y
|
|
|
|
} else if g.B.YMax < p.Y {
|
|
|
|
g.B.YMax = p.Y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Snap the box to the grid, if hinting is on.
|
|
|
|
if h != nil {
|
|
|
|
g.B.XMin &^= 63
|
|
|
|
g.B.YMin &^= 63
|
|
|
|
g.B.XMax += 63
|
|
|
|
g.B.XMax &^= 63
|
|
|
|
g.B.YMax += 63
|
|
|
|
g.B.YMax &^= 63
|
|
|
|
}
|
2013-10-16 07:54:54 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error) {
|
2013-10-16 23:21:20 +00:00
|
|
|
// The recursion limit here is arbitrary, but defends against malformed glyphs.
|
|
|
|
if recursion >= 32 {
|
2013-10-16 07:54:54 +00:00
|
|
|
return UnsupportedError("excessive compound glyph recursion")
|
|
|
|
}
|
|
|
|
// Find the relevant slice of g.font.glyf.
|
|
|
|
var g0, g1 uint32
|
|
|
|
if g.font.locaOffsetFormat == locaOffsetFormatShort {
|
|
|
|
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
|
|
|
|
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
|
|
|
|
} else {
|
|
|
|
g0 = u32(g.font.loca, 4*int(i))
|
|
|
|
g1 = u32(g.font.loca, 4*int(i)+4)
|
|
|
|
}
|
|
|
|
if g0 == g1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
glyf := g.font.glyf[g0:g1]
|
2013-12-20 23:03:04 +00:00
|
|
|
|
|
|
|
// Decode the contour count and nominal bounding box.
|
|
|
|
// boundsYMin and boundsXMax, at offsets 4 and 6, are unused.
|
2013-10-16 07:54:54 +00:00
|
|
|
ne := int(int16(u16(glyf, 0)))
|
2013-12-20 23:03:04 +00:00
|
|
|
boundsXMin := int32(int16(u16(glyf, 2)))
|
|
|
|
boundsYMax := int32(int16(u16(glyf, 8)))
|
|
|
|
|
|
|
|
// Create the phantom points.
|
2013-10-16 07:54:54 +00:00
|
|
|
uhm, pp1x := g.font.unscaledHMetric(i), int32(0)
|
2013-12-20 23:03:04 +00:00
|
|
|
uvm := g.font.unscaledVMetric(i, boundsYMax)
|
2013-12-02 21:21:05 +00:00
|
|
|
g.phantomPoints = [4]Point{
|
2013-12-20 23:03:04 +00:00
|
|
|
{X: boundsXMin - uhm.LeftSideBearing},
|
|
|
|
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
|
|
|
|
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
|
|
|
|
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
|
2013-12-02 21:21:05 +00:00
|
|
|
}
|
2013-12-20 23:03:04 +00:00
|
|
|
|
|
|
|
// Load and hint the contours.
|
2013-10-16 07:54:54 +00:00
|
|
|
if ne < 0 {
|
|
|
|
if ne != -1 {
|
|
|
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
|
|
|
|
// "the values -2, -3, and so forth, are reserved for future use."
|
|
|
|
return UnsupportedError("negative number of contours")
|
|
|
|
}
|
2013-12-20 23:03:04 +00:00
|
|
|
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
|
|
|
|
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
|
2013-10-16 07:54:54 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
np0, ne0 := len(g.Point), len(g.End)
|
|
|
|
program := g.loadSimple(glyf, ne)
|
2013-12-20 23:03:04 +00:00
|
|
|
g.addPhantomsAndScale(np0, np0, true)
|
2013-11-26 09:06:51 +00:00
|
|
|
pp1x = g.Point[len(g.Point)-4].X
|
2013-10-16 07:54:54 +00:00
|
|
|
if g.hinter != nil {
|
|
|
|
if len(program) != 0 {
|
|
|
|
err := g.hinter.run(
|
|
|
|
program,
|
|
|
|
g.Point[np0:],
|
|
|
|
g.Unhinted[np0:],
|
|
|
|
g.InFontUnits[np0:],
|
|
|
|
g.End[ne0:],
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2013-11-26 09:06:51 +00:00
|
|
|
// Drop the four phantom points.
|
2013-10-29 20:50:48 +00:00
|
|
|
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
|
|
|
|
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
|
2013-10-16 07:54:54 +00:00
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
|
2013-10-29 20:50:48 +00:00
|
|
|
g.Point = g.Point[:len(g.Point)-4]
|
2013-10-16 07:54:54 +00:00
|
|
|
if np0 != 0 {
|
|
|
|
// The hinting program expects the []End values to be indexed relative
|
|
|
|
// to the inner glyph, not the outer glyph, so we delay adding np0 until
|
|
|
|
// after the hinting program (if any) has run.
|
|
|
|
for i := ne0; i < len(g.End); i++ {
|
|
|
|
g.End[i] += np0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if useMyMetrics && !g.metricsSet {
|
|
|
|
g.metricsSet = true
|
|
|
|
g.pp1x = pp1x
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// loadOffset is the initial offset for loadSimple and loadCompound. The first
|
|
|
|
// 10 bytes are the number of contours and the bounding box.
|
|
|
|
const loadOffset = 10
|
|
|
|
|
|
|
|
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
|
|
|
offset := loadOffset
|
|
|
|
for i := 0; i < ne; i++ {
|
|
|
|
g.End = append(g.End, 1+int(u16(glyf, offset)))
|
|
|
|
offset += 2
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note the TrueType hinting instructions.
|
|
|
|
instrLen := int(u16(glyf, offset))
|
|
|
|
offset += 2
|
|
|
|
program = glyf[offset : offset+instrLen]
|
|
|
|
offset += instrLen
|
|
|
|
|
|
|
|
np0 := len(g.Point)
|
|
|
|
np1 := np0 + int(g.End[len(g.End)-1])
|
|
|
|
|
|
|
|
// Decode the flags.
|
|
|
|
for i := np0; i < np1; {
|
|
|
|
c := uint32(glyf[offset])
|
2012-06-16 02:19:07 +00:00
|
|
|
offset++
|
2013-10-16 07:54:54 +00:00
|
|
|
g.Point = append(g.Point, Point{Flags: c})
|
2012-05-07 02:04:52 +00:00
|
|
|
i++
|
|
|
|
if c&flagRepeat != 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
count := glyf[offset]
|
2012-06-16 02:19:07 +00:00
|
|
|
offset++
|
2012-05-07 02:04:52 +00:00
|
|
|
for ; count > 0; count-- {
|
2013-10-16 07:54:54 +00:00
|
|
|
g.Point = append(g.Point, Point{Flags: c})
|
2012-05-07 02:04:52 +00:00
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-16 07:54:54 +00:00
|
|
|
// Decode the co-ordinates.
|
2012-05-07 02:04:52 +00:00
|
|
|
var x int16
|
2013-10-16 07:54:54 +00:00
|
|
|
for i := np0; i < np1; i++ {
|
2012-05-07 02:04:52 +00:00
|
|
|
f := g.Point[i].Flags
|
|
|
|
if f&flagXShortVector != 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
dx := int16(glyf[offset])
|
2012-06-16 02:19:07 +00:00
|
|
|
offset++
|
2012-05-07 02:04:52 +00:00
|
|
|
if f&flagPositiveXShortVector == 0 {
|
|
|
|
x -= dx
|
|
|
|
} else {
|
|
|
|
x += dx
|
|
|
|
}
|
|
|
|
} else if f&flagThisXIsSame == 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
x += int16(u16(glyf, offset))
|
2012-06-16 02:19:07 +00:00
|
|
|
offset += 2
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
g.Point[i].X = int32(x)
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
var y int16
|
2013-10-16 07:54:54 +00:00
|
|
|
for i := np0; i < np1; i++ {
|
2012-05-07 02:04:52 +00:00
|
|
|
f := g.Point[i].Flags
|
|
|
|
if f&flagYShortVector != 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
dy := int16(glyf[offset])
|
2012-06-16 02:19:07 +00:00
|
|
|
offset++
|
2012-05-07 02:04:52 +00:00
|
|
|
if f&flagPositiveYShortVector == 0 {
|
|
|
|
y -= dy
|
|
|
|
} else {
|
|
|
|
y += dy
|
|
|
|
}
|
|
|
|
} else if f&flagThisYIsSame == 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
y += int16(u16(glyf, offset))
|
2012-06-16 02:19:07 +00:00
|
|
|
offset += 2
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
2012-07-25 12:10:25 +00:00
|
|
|
g.Point[i].Y = int32(y)
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 07:54:54 +00:00
|
|
|
return program
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
|
2013-12-20 23:03:04 +00:00
|
|
|
func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
|
2013-11-04 22:58:40 +00:00
|
|
|
glyf []byte, useMyMetrics bool) error {
|
|
|
|
|
2012-05-07 02:04:52 +00:00
|
|
|
// Flags for decoding a compound glyph. These flags are documented at
|
|
|
|
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
|
|
|
const (
|
|
|
|
flagArg1And2AreWords = 1 << iota
|
|
|
|
flagArgsAreXYValues
|
|
|
|
flagRoundXYToGrid
|
|
|
|
flagWeHaveAScale
|
|
|
|
flagUnused
|
|
|
|
flagMoreComponents
|
|
|
|
flagWeHaveAnXAndYScale
|
|
|
|
flagWeHaveATwoByTwo
|
|
|
|
flagWeHaveInstructions
|
|
|
|
flagUseMyMetrics
|
|
|
|
flagOverlapCompound
|
|
|
|
)
|
2013-11-04 22:58:40 +00:00
|
|
|
np0, ne0 := len(g.Point), len(g.End)
|
|
|
|
offset := loadOffset
|
|
|
|
for {
|
2012-06-16 02:19:07 +00:00
|
|
|
flags := u16(glyf, offset)
|
2013-08-31 06:08:40 +00:00
|
|
|
component := Index(u16(glyf, offset+2))
|
2013-10-16 10:39:06 +00:00
|
|
|
dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
|
2012-05-07 02:04:52 +00:00
|
|
|
if flags&flagArg1And2AreWords != 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
dx = int32(int16(u16(glyf, offset+4)))
|
|
|
|
dy = int32(int16(u16(glyf, offset+6)))
|
2012-06-16 02:19:07 +00:00
|
|
|
offset += 8
|
2012-05-07 02:04:52 +00:00
|
|
|
} else {
|
2013-10-16 07:54:54 +00:00
|
|
|
dx = int32(int16(int8(glyf[offset+4])))
|
|
|
|
dy = int32(int16(int8(glyf[offset+5])))
|
2012-06-16 02:19:07 +00:00
|
|
|
offset += 6
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
if flags&flagArgsAreXYValues == 0 {
|
2013-10-16 07:54:54 +00:00
|
|
|
return UnsupportedError("compound glyph transform vector")
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
|
2013-10-16 10:39:06 +00:00
|
|
|
hasTransform = true
|
|
|
|
switch {
|
|
|
|
case flags&flagWeHaveAScale != 0:
|
|
|
|
transform[0] = int32(int16(u16(glyf, offset+0)))
|
|
|
|
transform[3] = transform[0]
|
|
|
|
offset += 2
|
|
|
|
case flags&flagWeHaveAnXAndYScale != 0:
|
|
|
|
transform[0] = int32(int16(u16(glyf, offset+0)))
|
|
|
|
transform[3] = int32(int16(u16(glyf, offset+2)))
|
|
|
|
offset += 4
|
|
|
|
case flags&flagWeHaveATwoByTwo != 0:
|
|
|
|
transform[0] = int32(int16(u16(glyf, offset+0)))
|
|
|
|
transform[1] = int32(int16(u16(glyf, offset+2)))
|
|
|
|
transform[2] = int32(int16(u16(glyf, offset+4)))
|
|
|
|
transform[3] = int32(int16(u16(glyf, offset+6)))
|
|
|
|
offset += 8
|
|
|
|
}
|
2013-10-09 20:43:32 +00:00
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
savedPP := g.phantomPoints
|
2013-10-16 07:54:54 +00:00
|
|
|
np0 := len(g.Point)
|
2013-10-16 23:21:20 +00:00
|
|
|
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
|
|
|
|
if err := g.load(recursion+1, component, componentUMM); err != nil {
|
2013-10-16 07:54:54 +00:00
|
|
|
return err
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
if flags&flagUseMyMetrics == 0 {
|
|
|
|
g.phantomPoints = savedPP
|
|
|
|
}
|
2013-10-16 10:39:06 +00:00
|
|
|
if hasTransform {
|
2013-11-04 22:58:40 +00:00
|
|
|
for j := np0; j < len(g.Point); j++ {
|
|
|
|
p := &g.Point[j]
|
2013-10-16 10:39:06 +00:00
|
|
|
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
|
|
|
|
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
|
|
|
|
newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) +
|
|
|
|
int32((int64(p.Y)*int64(transform[3])+1<<13)>>14)
|
|
|
|
p.X, p.Y = newX, newY
|
|
|
|
}
|
|
|
|
}
|
2013-10-16 07:54:54 +00:00
|
|
|
dx = g.font.scale(g.scale * dx)
|
|
|
|
dy = g.font.scale(g.scale * dy)
|
|
|
|
if flags&flagRoundXYToGrid != 0 {
|
|
|
|
dx = (dx + 32) &^ 63
|
|
|
|
dy = (dy + 32) &^ 63
|
|
|
|
}
|
2013-11-04 22:58:40 +00:00
|
|
|
for j := np0; j < len(g.Point); j++ {
|
|
|
|
p := &g.Point[j]
|
2013-10-16 07:54:54 +00:00
|
|
|
p.X += dx
|
|
|
|
p.Y += dy
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
2013-10-16 07:54:54 +00:00
|
|
|
// TODO: also adjust g.InFontUnits and g.Unhinted?
|
2012-05-07 02:04:52 +00:00
|
|
|
if flags&flagMoreComponents == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2013-11-04 22:58:40 +00:00
|
|
|
|
|
|
|
// Hint the compound glyph.
|
|
|
|
if g.hinter == nil || offset+2 > len(glyf) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
instrLen := int(u16(glyf, offset))
|
|
|
|
offset += 2
|
|
|
|
if instrLen == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
program := glyf[offset : offset+instrLen]
|
2013-12-20 23:03:04 +00:00
|
|
|
g.addPhantomsAndScale(np0, len(g.Point), false)
|
2013-11-06 21:53:15 +00:00
|
|
|
points, ends := g.Point[np0:], g.End[ne0:]
|
2013-11-04 22:58:40 +00:00
|
|
|
g.Point = g.Point[:len(g.Point)-4]
|
|
|
|
for j := range points {
|
|
|
|
points[j].Flags &^= flagTouchedX | flagTouchedY
|
|
|
|
}
|
2013-11-06 21:53:15 +00:00
|
|
|
// Temporarily adjust the ends to be relative to this compound glyph.
|
|
|
|
if np0 != 0 {
|
|
|
|
for i := range ends {
|
|
|
|
ends[i] -= np0
|
|
|
|
}
|
|
|
|
}
|
2013-11-04 22:58:40 +00:00
|
|
|
// Hinting instructions of a composite glyph completely refer to the
|
|
|
|
// (already) hinted subglyphs.
|
|
|
|
g.tmp = append(g.tmp[:0], points...)
|
2013-11-06 21:53:15 +00:00
|
|
|
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if np0 != 0 {
|
|
|
|
for i := range ends {
|
|
|
|
ends[i] += np0
|
|
|
|
}
|
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
copy(g.phantomPoints[:], points[len(points)-4:])
|
2013-11-06 21:53:15 +00:00
|
|
|
return nil
|
2013-11-04 22:58:40 +00:00
|
|
|
}
|
|
|
|
|
2013-12-20 23:03:04 +00:00
|
|
|
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple bool) {
|
2013-11-04 22:58:40 +00:00
|
|
|
// Add the four phantom points.
|
2013-12-02 21:21:05 +00:00
|
|
|
g.Point = append(g.Point, g.phantomPoints[:]...)
|
2013-11-04 22:58:40 +00:00
|
|
|
// Scale the points.
|
2013-11-26 09:06:51 +00:00
|
|
|
if simple && g.hinter != nil {
|
2013-12-02 21:21:05 +00:00
|
|
|
g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...)
|
2013-11-04 22:58:40 +00:00
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
for i := np1; i < len(g.Point); i++ {
|
2013-11-04 22:58:40 +00:00
|
|
|
p := &g.Point[i]
|
|
|
|
p.X = g.font.scale(g.scale * p.X)
|
|
|
|
p.Y = g.font.scale(g.scale * p.Y)
|
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
if g.hinter != nil {
|
2013-11-26 09:06:51 +00:00
|
|
|
// Round the 1st phantom point to the grid, shifting all other points equally.
|
2013-12-02 21:21:05 +00:00
|
|
|
// Note that "all other points" starts from np0, not np1.
|
|
|
|
// TODO: is the np0/np1 distinction actually a bug in C Freetype?
|
2013-11-26 09:06:51 +00:00
|
|
|
pp1x := g.Point[len(g.Point)-4].X
|
|
|
|
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
|
|
|
|
for i := np0; i < len(g.Point); i++ {
|
|
|
|
g.Point[i].X += dx
|
|
|
|
}
|
|
|
|
}
|
2013-12-02 21:21:05 +00:00
|
|
|
if simple {
|
|
|
|
g.Unhinted = append(g.Unhinted, g.Point[np1:]...)
|
|
|
|
}
|
2013-11-04 22:58:40 +00:00
|
|
|
}
|
|
|
|
// Round the 2nd and 4th phantom point to the grid.
|
|
|
|
p := &g.Point[len(g.Point)-3]
|
|
|
|
p.X = (p.X + 32) &^ 63
|
|
|
|
p = &g.Point[len(g.Point)-1]
|
|
|
|
p.Y = (p.Y + 32) &^ 63
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 07:54:54 +00:00
|
|
|
// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable.
|
2012-05-07 02:04:52 +00:00
|
|
|
|
|
|
|
// NewGlyphBuf returns a newly allocated GlyphBuf.
|
|
|
|
func NewGlyphBuf() *GlyphBuf {
|
2013-10-16 07:54:54 +00:00
|
|
|
return &GlyphBuf{
|
|
|
|
Point: make([]Point, 0, 256),
|
|
|
|
End: make([]int, 0, 32),
|
|
|
|
}
|
2012-05-07 02:04:52 +00:00
|
|
|
}
|