glyph.go 15.0 KB
Newer Older
1 2 3 4 5 6 7
// 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

8
import (
9
	"golang.org/x/image/font"
10 11 12
	"golang.org/x/image/math/fixed"
)

13
// TODO: implement VerticalHinting.
14

15 16
// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
// 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 25 26
}

// 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 {
27
	// AdvanceWidth is the glyph's advance width.
28
	AdvanceWidth fixed.Int26_6
29 30
	// Bounds is the glyph's bounding box.
	Bounds fixed.Rectangle26_6
31 32 33 34 35 36 37 38 39 40
	// Points contains all Points from all contours of the glyph. If hinting
	// was used to load a glyph then Unhinted contains those Points before they
	// were hinted, and InFontUnits contains those Points before they were
	// hinted and scaled.
	Points, Unhinted, InFontUnits []Point
	// Ends is the point indexes of the end point of each contour. The length
	// of Ends is the number of contours in the glyph. The i'th contour
	// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
	// interpreted to mean zero.
	Ends []int
41

42
	font    *Font
43
	scale   fixed.Int26_6
44
	hinting font.Hinting
45
	hinter  hinter
46 47 48 49 50 51
	// 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.
52
	pp1x fixed.Int26_6
53 54
	// 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.
55
	metricsSet bool
56 57
	// tmp is a scratch buffer.
	tmp []Point
58 59 60 61 62 63 64 65 66 67 68
}

// 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
69 70 71 72

	// The remaining flags are for internal use.
	flagTouchedX
	flagTouchedY
73 74 75 76 77 78 79 80 81
)

// 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
)

82 83 84
// 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, 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 88
	g.Unhinted = g.Unhinted[:0]
	g.InFontUnits = g.InFontUnits[:0]
89
	g.Ends = g.Ends[:0]
90
	g.font = f
91
	g.hinting = h
92 93
	g.scale = scale
	g.pp1x = 0
94
	g.phantomPoints = [4]Point{}
95 96
	g.metricsSet = false

97
	if h != font.HintingNone {
98
		if err := g.hinter.init(f, scale); err != nil {
99 100 101 102 103 104
			return err
		}
	}
	if err := g.load(0, i, true); err != nil {
		return err
	}
105 106 107 108
	// 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
109
	if h != font.HintingNone {
110 111 112
		pp1x = g.phantomPoints[0].X
	}
	if pp1x != 0 {
113 114
		for i := range g.Points {
			g.Points[i].X -= pp1x
115
		}
116 117
	}

118
	advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
119
	if h != font.HintingNone {
120 121 122
		if len(f.hdmx) >= 8 {
			if n := u32(f.hdmx, 4); n > 3+uint32(i) {
				for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
123 124
					if fixed.Int26_6(hdmx[0]) == scale>>6 {
						advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
125 126 127 128 129 130 131 132 133
						break
					}
				}
			}
		}
		advanceWidth = (advanceWidth + 32) &^ 63
	}
	g.AdvanceWidth = advanceWidth

134 135 136
	// Set g.Bounds 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
137 138 139
	// 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.
140
	if len(g.Points) == 0 {
141
		g.Bounds = fixed.Rectangle26_6{}
142
	} else {
143
		p := g.Points[0]
144 145 146 147
		g.Bounds.Min.X = p.X
		g.Bounds.Max.X = p.X
		g.Bounds.Min.Y = p.Y
		g.Bounds.Max.Y = p.Y
148
		for _, p := range g.Points[1:] {
149 150 151 152
			if g.Bounds.Min.X > p.X {
				g.Bounds.Min.X = p.X
			} else if g.Bounds.Max.X < p.X {
				g.Bounds.Max.X = p.X
153
			}
154 155 156 157
			if g.Bounds.Min.Y > p.Y {
				g.Bounds.Min.Y = p.Y
			} else if g.Bounds.Max.Y < p.Y {
				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 163 164 165 166 167
			g.Bounds.Min.X &^= 63
			g.Bounds.Min.Y &^= 63
			g.Bounds.Max.X += 63
			g.Bounds.Max.X &^= 63
			g.Bounds.Max.Y += 63
			g.Bounds.Max.Y &^= 63
168
		}
169 170 171 172
	}
	return nil
}

173
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
174 175
	// The recursion limit here is arbitrary, but defends against malformed glyphs.
	if recursion >= 32 {
176 177 178 179 180 181 182 183 184 185 186
		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)
	}
187

188 189 190
	// Decode the contour count and nominal bounding box, from the first
	// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
	// and 6, are unused.
191
	glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
192 193 194
	if g0+10 <= g1 {
		glyf = g.font.glyf[g0:g1]
		ne = int(int16(u16(glyf, 0)))
195 196
		boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
		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 204 205 206
		{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},
207
	}
208
	if len(glyf) == 0 {
209 210 211
		g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
		copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
		g.Points = g.Points[:len(g.Points)-4]
212
		// TODO: also trim g.InFontUnits and g.Unhinted?
213 214
		return nil
	}
215 216

	// Load and hint the contours.
217 218 219 220 221 222
	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")
		}
223 224
		pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
		if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
225 226 227
			return err
		}
	} 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 234 235
			if len(program) != 0 {
				err := g.hinter.run(
					program,
236
					g.Points[np0:],
237 238
					g.Unhinted[np0:],
					g.InFontUnits[np0:],
239
					g.Ends[ne0:],
240 241 242 243 244
				)
				if err != nil {
					return err
				}
			}
245
			// Drop the four phantom points.
246 247
			g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
			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 255 256 257 258
			// The hinting program expects the []Ends 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.Ends); i++ {
				g.Ends[i] += np0
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
			}
		}
	}
	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++ {
276
		g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
277 278 279 280 281 282 283 284 285
		offset += 2
	}

	// Note the TrueType hinting instructions.
	instrLen := int(u16(glyf, offset))
	offset += 2
	program = glyf[offset : offset+instrLen]
	offset += instrLen

286 287 288 289
	if ne == 0 {
		return program
	}

290 291
	np0 := len(g.Points)
	np1 := np0 + int(g.Ends[len(g.Ends)-1])
292 293 294 295

	// Decode the flags.
	for i := np0; i < np1; {
		c := uint32(glyf[offset])
296
		offset++
297
		g.Points = append(g.Points, Point{Flags: c})
298 299
		i++
		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 305 306 307 308
				i++
			}
		}
	}

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 317 318 319 320 321
			if f&flagPositiveXShortVector == 0 {
				x -= dx
			} else {
				x += dx
			}
		} 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 334 335 336 337 338
			if f&flagPositiveYShortVector == 0 {
				y -= dy
			} else {
				y += dy
			}
		} 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 350
	glyf []byte, useMyMetrics bool) error {

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	// 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
	)
366
	np0, ne0 := len(g.Points), len(g.Ends)
367 368
	offset := loadOffset
	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 374
			dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
			dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
375
			offset += 8
376
		} else {
377 378
			dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
			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 386 387
			hasTransform = true
			switch {
			case flags&flagWeHaveAScale != 0:
388
				transform[0] = int16(u16(glyf, offset+0))
389 390 391
				transform[3] = transform[0]
				offset += 2
			case flags&flagWeHaveAnXAndYScale != 0:
392 393
				transform[0] = int16(u16(glyf, offset+0))
				transform[3] = int16(u16(glyf, offset+2))
394 395
				offset += 4
			case flags&flagWeHaveATwoByTwo != 0:
396 397 398 399
				transform[0] = int16(u16(glyf, offset+0))
				transform[1] = int16(u16(glyf, offset+2))
				transform[2] = int16(u16(glyf, offset+4))
				transform[3] = int16(u16(glyf, offset+6))
400 401
				offset += 8
			}
402
		}
403
		savedPP := g.phantomPoints
404
		np0 := len(g.Points)
405 406
		componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
		if err := g.load(recursion+1, component, componentUMM); err != nil {
407
			return err
408
		}
409 410 411
		if flags&flagUseMyMetrics == 0 {
			g.phantomPoints = savedPP
		}
412
		if hasTransform {
413 414
			for j := np0; j < len(g.Points); j++ {
				p := &g.Points[j]
415 416 417 418 419 420
				newX := 0 +
					fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
					fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
				newY := 0 +
					fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
					fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
421 422 423
				p.X, p.Y = newX, newY
			}
		}
424 425 426 427 428 429
		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
		}
430 431
		for j := np0; j < len(g.Points); j++ {
			p := &g.Points[j]
432 433
			p.X += dx
			p.Y += dy
434
		}
435
		// TODO: also adjust g.InFontUnits and g.Unhinted?
436 437 438 439
		if flags&flagMoreComponents == 0 {
			break
		}
	}
440

441
	instrLen := 0
442
	if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
443 444
		instrLen = int(u16(glyf, offset))
		offset += 2
445
	}
446

447 448 449
	g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
	points, ends := g.Points[np0:], g.Ends[ne0:]
	g.Points = g.Points[:len(g.Points)-4]
450 451 452
	for j := range points {
		points[j].Flags &^= flagTouchedX | flagTouchedY
	}
453 454 455 456 457 458 459 460 461 462

	if instrLen == 0 {
		if !g.metricsSet {
			copy(g.phantomPoints[:], points[len(points)-4:])
		}
		return nil
	}

	// Hint the compound glyph.
	program := glyf[offset : offset+instrLen]
463 464 465 466 467 468
	// Temporarily adjust the ends to be relative to this compound glyph.
	if np0 != 0 {
		for i := range ends {
			ends[i] -= np0
		}
	}
469 470 471
	// Hinting instructions of a composite glyph completely refer to the
	// (already) hinted subglyphs.
	g.tmp = append(g.tmp[:0], points...)
472 473 474 475 476 477 478 479
	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
		}
	}
480 481 482
	if !g.metricsSet {
		copy(g.phantomPoints[:], points[len(points)-4:])
	}
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 494
	for i := np1; i < len(g.Points); i++ {
		p := &g.Points[i]
495 496 497
		p.X = g.font.scale(g.scale * p.X)
		p.Y = g.font.scale(g.scale * p.Y)
	}
498
	if g.hinting == font.HintingNone {
499 500 501 502 503 504 505 506
		return
	}
	// Round the 1st phantom point to the grid, shifting all other points equally.
	// Note that "all other points" starts from np0, not np1.
	// TODO: delete this adjustment and the np0/np1 distinction, when
	// we update the compatibility tests to C Freetype 2.5.3.
	// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
	if adjust {
507
		pp1x := g.Points[len(g.Points)-4].X
508
		if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
509 510
			for i := np0; i < len(g.Points); i++ {
				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
}