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
6package truetype
7
8import (
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.
17type 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.
26type 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.
62const (
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.
77const (
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.
85func (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
173func (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.
271const loadOffset = 10
272
273func (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
348func (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
486func (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