1
2
3
4
5
6 package truetype
7
8 import (
9 "golang.org/x/image/font"
10 "golang.org/x/image/math/fixed"
11 )
12
13
14
15
16
17 type Point struct {
18 X, Y fixed.Int26_6
19
20
21 Flags uint32
22 }
23
24
25
26 type GlyphBuf struct {
27
28 AdvanceWidth fixed.Int26_6
29
30 Bounds fixed.Rectangle26_6
31
32
33
34
35 Points, Unhinted, InFontUnits []Point
36
37
38
39
40 Ends []int
41
42 font *Font
43 scale fixed.Int26_6
44 hinting font.Hinting
45 hinter hinter
46
47
48 phantomPoints [4]Point
49
50
51
52 pp1x fixed.Int26_6
53
54
55 metricsSet bool
56
57 tmp []Point
58 }
59
60
61
62 const (
63 flagOnCurve = 1 << iota
64 flagXShortVector
65 flagYShortVector
66 flagRepeat
67 flagPositiveXShortVector
68 flagPositiveYShortVector
69
70
71 flagTouchedX
72 flagTouchedY
73 )
74
75
76
77 const (
78 flagThisXIsSame = flagPositiveXShortVector
79 flagThisYIsSame = flagPositiveYShortVector
80 )
81
82
83
84
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
106
107
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
135
136
137
138
139
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
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
175 if recursion >= 32 {
176 return UnsupportedError("excessive compound glyph recursion")
177 }
178
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
189
190
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
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
213 return nil
214 }
215
216
217 if ne < 0 {
218 if ne != -1 {
219
220
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
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
255
256
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
270
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
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
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
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
352
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
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
462 program := glyf[offset : offset+instrLen]
463
464 if np0 != 0 {
465 for i := range ends {
466 ends[i] -= np0
467 }
468 }
469
470
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
488 g.Points = append(g.Points, g.phantomPoints[:]...)
489
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
502
503
504
505
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
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