...

Source file src/github.com/golang/freetype/truetype/glyph.go

Documentation: github.com/golang/freetype/truetype

     1  // Copyright 2010 The Freetype-Go Authors. All rights reserved.
     2  // Use of this source code is governed by your choice of either the
     3  // FreeType License or the GNU General Public License version 2 (or
     4  // any later version), both of which can be found in the LICENSE file.
     5  
     6  package truetype
     7  
     8  import (
     9  	"golang.org/x/image/font"
    10  	"golang.org/x/image/math/fixed"
    11  )
    12  
    13  // TODO: implement VerticalHinting.
    14  
    15  // A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
    16  // control point.
    17  type Point struct {
    18  	X, Y fixed.Int26_6
    19  	// The Flags' LSB means whether or not this Point is 'on' the contour.
    20  	// Other bits are reserved for internal use.
    21  	Flags uint32
    22  }
    23  
    24  // A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
    25  // series of glyphs from a Font.
    26  type GlyphBuf struct {
    27  	// AdvanceWidth is the glyph's advance width.
    28  	AdvanceWidth fixed.Int26_6
    29  	// Bounds is the glyph's bounding box.
    30  	Bounds fixed.Rectangle26_6
    31  	// Points contains all Points from all contours of the glyph. If hinting
    32  	// was used to load a glyph then Unhinted contains those Points before they
    33  	// were hinted, and InFontUnits contains those Points before they were
    34  	// hinted and scaled.
    35  	Points, Unhinted, InFontUnits []Point
    36  	// Ends is the point indexes of the end point of each contour. The length
    37  	// of Ends is the number of contours in the glyph. The i'th contour
    38  	// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
    39  	// interpreted to mean zero.
    40  	Ends []int
    41  
    42  	font    *Font
    43  	scale   fixed.Int26_6
    44  	hinting font.Hinting
    45  	hinter  hinter
    46  	// phantomPoints are the co-ordinates of the synthetic phantom points
    47  	// used for hinting and bounding box calculations.
    48  	phantomPoints [4]Point
    49  	// pp1x is the X co-ordinate of the first phantom point. The '1' is
    50  	// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
    51  	// TODO: eliminate this and consistently use phantomPoints[0].X.
    52  	pp1x fixed.Int26_6
    53  	// metricsSet is whether the glyph's metrics have been set yet. For a
    54  	// compound glyph, a sub-glyph may override the outer glyph's metrics.
    55  	metricsSet bool
    56  	// tmp is a scratch buffer.
    57  	tmp []Point
    58  }
    59  
    60  // Flags for decoding a glyph's contours. These flags are documented at
    61  // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
    62  const (
    63  	flagOnCurve = 1 << iota
    64  	flagXShortVector
    65  	flagYShortVector
    66  	flagRepeat
    67  	flagPositiveXShortVector
    68  	flagPositiveYShortVector
    69  
    70  	// The remaining flags are for internal use.
    71  	flagTouchedX
    72  	flagTouchedY
    73  )
    74  
    75  // The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
    76  // dependent on the value of the flag{X,Y}ShortVector bits.
    77  const (
    78  	flagThisXIsSame = flagPositiveXShortVector
    79  	flagThisYIsSame = flagPositiveYShortVector
    80  )
    81  
    82  // Load loads a glyph's contours from a Font, overwriting any previously loaded
    83  // contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
    84  // 1 em, i is the glyph index, and h is the hinting policy.
    85  func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
    86  	g.Points = g.Points[:0]
    87  	g.Unhinted = g.Unhinted[:0]
    88  	g.InFontUnits = g.InFontUnits[:0]
    89  	g.Ends = g.Ends[:0]
    90  	g.font = f
    91  	g.hinting = h
    92  	g.scale = scale
    93  	g.pp1x = 0
    94  	g.phantomPoints = [4]Point{}
    95  	g.metricsSet = false
    96  
    97  	if h != font.HintingNone {
    98  		if err := g.hinter.init(f, scale); err != nil {
    99  			return err
   100  		}
   101  	}
   102  	if err := g.load(0, i, true); err != nil {
   103  		return err
   104  	}
   105  	// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
   106  	// and should be cleaned up once we have all the testScaling tests passing,
   107  	// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
   108  	pp1x := g.pp1x
   109  	if h != font.HintingNone {
   110  		pp1x = g.phantomPoints[0].X
   111  	}
   112  	if pp1x != 0 {
   113  		for i := range g.Points {
   114  			g.Points[i].X -= pp1x
   115  		}
   116  	}
   117  
   118  	advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
   119  	if h != font.HintingNone {
   120  		if len(f.hdmx) >= 8 {
   121  			if n := u32(f.hdmx, 4); n > 3+uint32(i) {
   122  				for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
   123  					if fixed.Int26_6(hdmx[0]) == scale>>6 {
   124  						advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
   125  						break
   126  					}
   127  				}
   128  			}
   129  		}
   130  		advanceWidth = (advanceWidth + 32) &^ 63
   131  	}
   132  	g.AdvanceWidth = advanceWidth
   133  
   134  	// Set g.Bounds to the 'control box', which is the bounding box of the
   135  	// Bézier curves' control points. This is easier to calculate, no smaller
   136  	// than and often equal to the tightest possible bounding box of the curves
   137  	// themselves. This approach is what C Freetype does. We can't just scale
   138  	// the nominal bounding box in the glyf data as the hinting process and
   139  	// phantom point adjustment may move points outside of that box.
   140  	if len(g.Points) == 0 {
   141  		g.Bounds = fixed.Rectangle26_6{}
   142  	} else {
   143  		p := g.Points[0]
   144  		g.Bounds.Min.X = p.X
   145  		g.Bounds.Max.X = p.X
   146  		g.Bounds.Min.Y = p.Y
   147  		g.Bounds.Max.Y = p.Y
   148  		for _, p := range g.Points[1:] {
   149  			if g.Bounds.Min.X > p.X {
   150  				g.Bounds.Min.X = p.X
   151  			} else if g.Bounds.Max.X < p.X {
   152  				g.Bounds.Max.X = p.X
   153  			}
   154  			if g.Bounds.Min.Y > p.Y {
   155  				g.Bounds.Min.Y = p.Y
   156  			} else if g.Bounds.Max.Y < p.Y {
   157  				g.Bounds.Max.Y = p.Y
   158  			}
   159  		}
   160  		// Snap the box to the grid, if hinting is on.
   161  		if h != font.HintingNone {
   162  			g.Bounds.Min.X &^= 63
   163  			g.Bounds.Min.Y &^= 63
   164  			g.Bounds.Max.X += 63
   165  			g.Bounds.Max.X &^= 63
   166  			g.Bounds.Max.Y += 63
   167  			g.Bounds.Max.Y &^= 63
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
   174  	// The recursion limit here is arbitrary, but defends against malformed glyphs.
   175  	if recursion >= 32 {
   176  		return UnsupportedError("excessive compound glyph recursion")
   177  	}
   178  	// Find the relevant slice of g.font.glyf.
   179  	var g0, g1 uint32
   180  	if g.font.locaOffsetFormat == locaOffsetFormatShort {
   181  		g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
   182  		g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
   183  	} else {
   184  		g0 = u32(g.font.loca, 4*int(i))
   185  		g1 = u32(g.font.loca, 4*int(i)+4)
   186  	}
   187  
   188  	// Decode the contour count and nominal bounding box, from the first
   189  	// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
   190  	// and 6, are unused.
   191  	glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
   192  	if g0+10 <= g1 {
   193  		glyf = g.font.glyf[g0:g1]
   194  		ne = int(int16(u16(glyf, 0)))
   195  		boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
   196  		boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
   197  	}
   198  
   199  	// Create the phantom points.
   200  	uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
   201  	uvm := g.font.unscaledVMetric(i, boundsYMax)
   202  	g.phantomPoints = [4]Point{
   203  		{X: boundsXMin - uhm.LeftSideBearing},
   204  		{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
   205  		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
   206  		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
   207  	}
   208  	if len(glyf) == 0 {
   209  		g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
   210  		copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
   211  		g.Points = g.Points[:len(g.Points)-4]
   212  		// TODO: also trim g.InFontUnits and g.Unhinted?
   213  		return nil
   214  	}
   215  
   216  	// Load and hint the contours.
   217  	if ne < 0 {
   218  		if ne != -1 {
   219  			// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
   220  			// "the values -2, -3, and so forth, are reserved for future use."
   221  			return UnsupportedError("negative number of contours")
   222  		}
   223  		pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
   224  		if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
   225  			return err
   226  		}
   227  	} else {
   228  		np0, ne0 := len(g.Points), len(g.Ends)
   229  		program := g.loadSimple(glyf, ne)
   230  		g.addPhantomsAndScale(np0, np0, true, true)
   231  		pp1x = g.Points[len(g.Points)-4].X
   232  		if g.hinting != font.HintingNone {
   233  			if len(program) != 0 {
   234  				err := g.hinter.run(
   235  					program,
   236  					g.Points[np0:],
   237  					g.Unhinted[np0:],
   238  					g.InFontUnits[np0:],
   239  					g.Ends[ne0:],
   240  				)
   241  				if err != nil {
   242  					return err
   243  				}
   244  			}
   245  			// Drop the four phantom points.
   246  			g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
   247  			g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
   248  		}
   249  		if useMyMetrics {
   250  			copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
   251  		}
   252  		g.Points = g.Points[:len(g.Points)-4]
   253  		if np0 != 0 {
   254  			// The hinting program expects the []Ends values to be indexed
   255  			// relative to the inner glyph, not the outer glyph, so we delay
   256  			// adding np0 until after the hinting program (if any) has run.
   257  			for i := ne0; i < len(g.Ends); i++ {
   258  				g.Ends[i] += np0
   259  			}
   260  		}
   261  	}
   262  	if useMyMetrics && !g.metricsSet {
   263  		g.metricsSet = true
   264  		g.pp1x = pp1x
   265  	}
   266  	return nil
   267  }
   268  
   269  // loadOffset is the initial offset for loadSimple and loadCompound. The first
   270  // 10 bytes are the number of contours and the bounding box.
   271  const loadOffset = 10
   272  
   273  func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
   274  	offset := loadOffset
   275  	for i := 0; i < ne; i++ {
   276  		g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
   277  		offset += 2
   278  	}
   279  
   280  	// Note the TrueType hinting instructions.
   281  	instrLen := int(u16(glyf, offset))
   282  	offset += 2
   283  	program = glyf[offset : offset+instrLen]
   284  	offset += instrLen
   285  
   286  	if ne == 0 {
   287  		return program
   288  	}
   289  
   290  	np0 := len(g.Points)
   291  	np1 := np0 + int(g.Ends[len(g.Ends)-1])
   292  
   293  	// Decode the flags.
   294  	for i := np0; i < np1; {
   295  		c := uint32(glyf[offset])
   296  		offset++
   297  		g.Points = append(g.Points, Point{Flags: c})
   298  		i++
   299  		if c&flagRepeat != 0 {
   300  			count := glyf[offset]
   301  			offset++
   302  			for ; count > 0; count-- {
   303  				g.Points = append(g.Points, Point{Flags: c})
   304  				i++
   305  			}
   306  		}
   307  	}
   308  
   309  	// Decode the co-ordinates.
   310  	var x int16
   311  	for i := np0; i < np1; i++ {
   312  		f := g.Points[i].Flags
   313  		if f&flagXShortVector != 0 {
   314  			dx := int16(glyf[offset])
   315  			offset++
   316  			if f&flagPositiveXShortVector == 0 {
   317  				x -= dx
   318  			} else {
   319  				x += dx
   320  			}
   321  		} else if f&flagThisXIsSame == 0 {
   322  			x += int16(u16(glyf, offset))
   323  			offset += 2
   324  		}
   325  		g.Points[i].X = fixed.Int26_6(x)
   326  	}
   327  	var y int16
   328  	for i := np0; i < np1; i++ {
   329  		f := g.Points[i].Flags
   330  		if f&flagYShortVector != 0 {
   331  			dy := int16(glyf[offset])
   332  			offset++
   333  			if f&flagPositiveYShortVector == 0 {
   334  				y -= dy
   335  			} else {
   336  				y += dy
   337  			}
   338  		} else if f&flagThisYIsSame == 0 {
   339  			y += int16(u16(glyf, offset))
   340  			offset += 2
   341  		}
   342  		g.Points[i].Y = fixed.Int26_6(y)
   343  	}
   344  
   345  	return program
   346  }
   347  
   348  func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
   349  	glyf []byte, useMyMetrics bool) error {
   350  
   351  	// Flags for decoding a compound glyph. These flags are documented at
   352  	// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
   353  	const (
   354  		flagArg1And2AreWords = 1 << iota
   355  		flagArgsAreXYValues
   356  		flagRoundXYToGrid
   357  		flagWeHaveAScale
   358  		flagUnused
   359  		flagMoreComponents
   360  		flagWeHaveAnXAndYScale
   361  		flagWeHaveATwoByTwo
   362  		flagWeHaveInstructions
   363  		flagUseMyMetrics
   364  		flagOverlapCompound
   365  	)
   366  	np0, ne0 := len(g.Points), len(g.Ends)
   367  	offset := loadOffset
   368  	for {
   369  		flags := u16(glyf, offset)
   370  		component := Index(u16(glyf, offset+2))
   371  		dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
   372  		if flags&flagArg1And2AreWords != 0 {
   373  			dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
   374  			dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
   375  			offset += 8
   376  		} else {
   377  			dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
   378  			dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
   379  			offset += 6
   380  		}
   381  		if flags&flagArgsAreXYValues == 0 {
   382  			return UnsupportedError("compound glyph transform vector")
   383  		}
   384  		if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
   385  			hasTransform = true
   386  			switch {
   387  			case flags&flagWeHaveAScale != 0:
   388  				transform[0] = int16(u16(glyf, offset+0))
   389  				transform[3] = transform[0]
   390  				offset += 2
   391  			case flags&flagWeHaveAnXAndYScale != 0:
   392  				transform[0] = int16(u16(glyf, offset+0))
   393  				transform[3] = int16(u16(glyf, offset+2))
   394  				offset += 4
   395  			case flags&flagWeHaveATwoByTwo != 0:
   396  				transform[0] = int16(u16(glyf, offset+0))
   397  				transform[1] = int16(u16(glyf, offset+2))
   398  				transform[2] = int16(u16(glyf, offset+4))
   399  				transform[3] = int16(u16(glyf, offset+6))
   400  				offset += 8
   401  			}
   402  		}
   403  		savedPP := g.phantomPoints
   404  		np0 := len(g.Points)
   405  		componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
   406  		if err := g.load(recursion+1, component, componentUMM); err != nil {
   407  			return err
   408  		}
   409  		if flags&flagUseMyMetrics == 0 {
   410  			g.phantomPoints = savedPP
   411  		}
   412  		if hasTransform {
   413  			for j := np0; j < len(g.Points); j++ {
   414  				p := &g.Points[j]
   415  				newX := 0 +
   416  					fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
   417  					fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
   418  				newY := 0 +
   419  					fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
   420  					fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
   421  				p.X, p.Y = newX, newY
   422  			}
   423  		}
   424  		dx = g.font.scale(g.scale * dx)
   425  		dy = g.font.scale(g.scale * dy)
   426  		if flags&flagRoundXYToGrid != 0 {
   427  			dx = (dx + 32) &^ 63
   428  			dy = (dy + 32) &^ 63
   429  		}
   430  		for j := np0; j < len(g.Points); j++ {
   431  			p := &g.Points[j]
   432  			p.X += dx
   433  			p.Y += dy
   434  		}
   435  		// TODO: also adjust g.InFontUnits and g.Unhinted?
   436  		if flags&flagMoreComponents == 0 {
   437  			break
   438  		}
   439  	}
   440  
   441  	instrLen := 0
   442  	if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
   443  		instrLen = int(u16(glyf, offset))
   444  		offset += 2
   445  	}
   446  
   447  	g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
   448  	points, ends := g.Points[np0:], g.Ends[ne0:]
   449  	g.Points = g.Points[:len(g.Points)-4]
   450  	for j := range points {
   451  		points[j].Flags &^= flagTouchedX | flagTouchedY
   452  	}
   453  
   454  	if instrLen == 0 {
   455  		if !g.metricsSet {
   456  			copy(g.phantomPoints[:], points[len(points)-4:])
   457  		}
   458  		return nil
   459  	}
   460  
   461  	// Hint the compound glyph.
   462  	program := glyf[offset : offset+instrLen]
   463  	// Temporarily adjust the ends to be relative to this compound glyph.
   464  	if np0 != 0 {
   465  		for i := range ends {
   466  			ends[i] -= np0
   467  		}
   468  	}
   469  	// Hinting instructions of a composite glyph completely refer to the
   470  	// (already) hinted subglyphs.
   471  	g.tmp = append(g.tmp[:0], points...)
   472  	if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
   473  		return err
   474  	}
   475  	if np0 != 0 {
   476  		for i := range ends {
   477  			ends[i] += np0
   478  		}
   479  	}
   480  	if !g.metricsSet {
   481  		copy(g.phantomPoints[:], points[len(points)-4:])
   482  	}
   483  	return nil
   484  }
   485  
   486  func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
   487  	// Add the four phantom points.
   488  	g.Points = append(g.Points, g.phantomPoints[:]...)
   489  	// Scale the points.
   490  	if simple && g.hinting != font.HintingNone {
   491  		g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
   492  	}
   493  	for i := np1; i < len(g.Points); i++ {
   494  		p := &g.Points[i]
   495  		p.X = g.font.scale(g.scale * p.X)
   496  		p.Y = g.font.scale(g.scale * p.Y)
   497  	}
   498  	if g.hinting == font.HintingNone {
   499  		return
   500  	}
   501  	// Round the 1st phantom point to the grid, shifting all other points equally.
   502  	// Note that "all other points" starts from np0, not np1.
   503  	// TODO: delete this adjustment and the np0/np1 distinction, when
   504  	// we update the compatibility tests to C Freetype 2.5.3.
   505  	// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
   506  	if adjust {
   507  		pp1x := g.Points[len(g.Points)-4].X
   508  		if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
   509  			for i := np0; i < len(g.Points); i++ {
   510  				g.Points[i].X += dx
   511  			}
   512  		}
   513  	}
   514  	if simple {
   515  		g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
   516  	}
   517  	// Round the 2nd and 4th phantom point to the grid.
   518  	p := &g.Points[len(g.Points)-3]
   519  	p.X = (p.X + 32) &^ 63
   520  	p = &g.Points[len(g.Points)-1]
   521  	p.Y = (p.Y + 32) &^ 63
   522  }
   523  

View as plain text