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 return nil 213 } 214 215 // Load and hint the contours. 216 if ne < 0 { 217 if ne != -1 { 218 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that 219 // "the values -2, -3, and so forth, are reserved for future use." 220 return UnsupportedError("negative number of contours") 221 } 222 pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing)) 223 if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil { 224 return err 225 } 226 } else { 227 np0, ne0 := len(g.Points), len(g.Ends) 228 program := g.loadSimple(glyf, ne) 229 g.addPhantomsAndScale(np0, np0, true, true) 230 pp1x = g.Points[len(g.Points)-4].X 231 if g.hinting != font.HintingNone { 232 if len(program) != 0 { 233 err := g.hinter.run( 234 program, 235 g.Points[np0:], 236 g.Unhinted[np0:], 237 g.InFontUnits[np0:], 238 g.Ends[ne0:], 239 ) 240 if err != nil { 241 return err 242 } 243 } 244 // Drop the four phantom points. 245 g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4] 246 g.Unhinted = g.Unhinted[:len(g.Unhinted)-4] 247 } 248 if useMyMetrics { 249 copy(g.phantomPoints[:], g.Points[len(g.Points)-4:]) 250 } 251 g.Points = g.Points[:len(g.Points)-4] 252 if np0 != 0 { 253 // The hinting program expects the []Ends values to be indexed 254 // relative to the inner glyph, not the outer glyph, so we delay 255 // adding np0 until after the hinting program (if any) has run. 256 for i := ne0; i < len(g.Ends); i++ { 257 g.Ends[i] += np0 258 } 259 } 260 } 261 if useMyMetrics && !g.metricsSet { 262 g.metricsSet = true 263 g.pp1x = pp1x 264 } 265 return nil 266} 267 268// loadOffset is the initial offset for loadSimple and loadCompound. The first 269// 10 bytes are the number of contours and the bounding box. 270const loadOffset = 10 271 272func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) { 273 offset := loadOffset 274 for i := 0; i < ne; i++ { 275 g.Ends = append(g.Ends, 1+int(u16(glyf, offset))) 276 offset += 2 277 } 278 279 // Note the TrueType hinting instructions. 280 instrLen := int(u16(glyf, offset)) 281 offset += 2 282 program = glyf[offset : offset+instrLen] 283 offset += instrLen 284 285 np0 := len(g.Points) 286 np1 := np0 + int(g.Ends[len(g.Ends)-1]) 287 288 // Decode the flags. 289 for i := np0; i < np1; { 290 c := uint32(glyf[offset]) 291 offset++ 292 g.Points = append(g.Points, Point{Flags: c}) 293 i++ 294 if c&flagRepeat != 0 { 295 count := glyf[offset] 296 offset++ 297 for ; count > 0; count-- { 298 g.Points = append(g.Points, Point{Flags: c}) 299 i++ 300 } 301 } 302 } 303 304 // Decode the co-ordinates. 305 var x int16 306 for i := np0; i < np1; i++ { 307 f := g.Points[i].Flags 308 if f&flagXShortVector != 0 { 309 dx := int16(glyf[offset]) 310 offset++ 311 if f&flagPositiveXShortVector == 0 { 312 x -= dx 313 } else { 314 x += dx 315 } 316 } else if f&flagThisXIsSame == 0 { 317 x += int16(u16(glyf, offset)) 318 offset += 2 319 } 320 g.Points[i].X = fixed.Int26_6(x) 321 } 322 var y int16 323 for i := np0; i < np1; i++ { 324 f := g.Points[i].Flags 325 if f&flagYShortVector != 0 { 326 dy := int16(glyf[offset]) 327 offset++ 328 if f&flagPositiveYShortVector == 0 { 329 y -= dy 330 } else { 331 y += dy 332 } 333 } else if f&flagThisYIsSame == 0 { 334 y += int16(u16(glyf, offset)) 335 offset += 2 336 } 337 g.Points[i].Y = fixed.Int26_6(y) 338 } 339 340 return program 341} 342 343func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index, 344 glyf []byte, useMyMetrics bool) error { 345 346 // Flags for decoding a compound glyph. These flags are documented at 347 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html. 348 const ( 349 flagArg1And2AreWords = 1 << iota 350 flagArgsAreXYValues 351 flagRoundXYToGrid 352 flagWeHaveAScale 353 flagUnused 354 flagMoreComponents 355 flagWeHaveAnXAndYScale 356 flagWeHaveATwoByTwo 357 flagWeHaveInstructions 358 flagUseMyMetrics 359 flagOverlapCompound 360 ) 361 np0, ne0 := len(g.Points), len(g.Ends) 362 offset := loadOffset 363 for { 364 flags := u16(glyf, offset) 365 component := Index(u16(glyf, offset+2)) 366 dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false 367 if flags&flagArg1And2AreWords != 0 { 368 dx = fixed.Int26_6(int16(u16(glyf, offset+4))) 369 dy = fixed.Int26_6(int16(u16(glyf, offset+6))) 370 offset += 8 371 } else { 372 dx = fixed.Int26_6(int16(int8(glyf[offset+4]))) 373 dy = fixed.Int26_6(int16(int8(glyf[offset+5]))) 374 offset += 6 375 } 376 if flags&flagArgsAreXYValues == 0 { 377 return UnsupportedError("compound glyph transform vector") 378 } 379 if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 { 380 hasTransform = true 381 switch { 382 case flags&flagWeHaveAScale != 0: 383 transform[0] = int16(u16(glyf, offset+0)) 384 transform[3] = transform[0] 385 offset += 2 386 case flags&flagWeHaveAnXAndYScale != 0: 387 transform[0] = int16(u16(glyf, offset+0)) 388 transform[3] = int16(u16(glyf, offset+2)) 389 offset += 4 390 case flags&flagWeHaveATwoByTwo != 0: 391 transform[0] = int16(u16(glyf, offset+0)) 392 transform[1] = int16(u16(glyf, offset+2)) 393 transform[2] = int16(u16(glyf, offset+4)) 394 transform[3] = int16(u16(glyf, offset+6)) 395 offset += 8 396 } 397 } 398 savedPP := g.phantomPoints 399 np0 := len(g.Points) 400 componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0) 401 if err := g.load(recursion+1, component, componentUMM); err != nil { 402 return err 403 } 404 if flags&flagUseMyMetrics == 0 { 405 g.phantomPoints = savedPP 406 } 407 if hasTransform { 408 for j := np0; j < len(g.Points); j++ { 409 p := &g.Points[j] 410 newX := 0 + 411 fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) + 412 fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14) 413 newY := 0 + 414 fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) + 415 fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14) 416 p.X, p.Y = newX, newY 417 } 418 } 419 dx = g.font.scale(g.scale * dx) 420 dy = g.font.scale(g.scale * dy) 421 if flags&flagRoundXYToGrid != 0 { 422 dx = (dx + 32) &^ 63 423 dy = (dy + 32) &^ 63 424 } 425 for j := np0; j < len(g.Points); j++ { 426 p := &g.Points[j] 427 p.X += dx 428 p.Y += dy 429 } 430 // TODO: also adjust g.InFontUnits and g.Unhinted? 431 if flags&flagMoreComponents == 0 { 432 break 433 } 434 } 435 436 instrLen := 0 437 if g.hinting != font.HintingNone && offset+2 <= len(glyf) { 438 instrLen = int(u16(glyf, offset)) 439 offset += 2 440 } 441 442 g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0) 443 points, ends := g.Points[np0:], g.Ends[ne0:] 444 g.Points = g.Points[:len(g.Points)-4] 445 for j := range points { 446 points[j].Flags &^= flagTouchedX | flagTouchedY 447 } 448 449 if instrLen == 0 { 450 if !g.metricsSet { 451 copy(g.phantomPoints[:], points[len(points)-4:]) 452 } 453 return nil 454 } 455 456 // Hint the compound glyph. 457 program := glyf[offset : offset+instrLen] 458 // Temporarily adjust the ends to be relative to this compound glyph. 459 if np0 != 0 { 460 for i := range ends { 461 ends[i] -= np0 462 } 463 } 464 // Hinting instructions of a composite glyph completely refer to the 465 // (already) hinted subglyphs. 466 g.tmp = append(g.tmp[:0], points...) 467 if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil { 468 return err 469 } 470 if np0 != 0 { 471 for i := range ends { 472 ends[i] += np0 473 } 474 } 475 if !g.metricsSet { 476 copy(g.phantomPoints[:], points[len(points)-4:]) 477 } 478 return nil 479} 480 481func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) { 482 // Add the four phantom points. 483 g.Points = append(g.Points, g.phantomPoints[:]...) 484 // Scale the points. 485 if simple && g.hinting != font.HintingNone { 486 g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...) 487 } 488 for i := np1; i < len(g.Points); i++ { 489 p := &g.Points[i] 490 p.X = g.font.scale(g.scale * p.X) 491 p.Y = g.font.scale(g.scale * p.Y) 492 } 493 if g.hinting == font.HintingNone { 494 return 495 } 496 // Round the 1st phantom point to the grid, shifting all other points equally. 497 // Note that "all other points" starts from np0, not np1. 498 // TODO: delete this adjustment and the np0/np1 distinction, when 499 // we update the compatibility tests to C Freetype 2.5.3. 500 // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06 501 if adjust { 502 pp1x := g.Points[len(g.Points)-4].X 503 if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 { 504 for i := np0; i < len(g.Points); i++ { 505 g.Points[i].X += dx 506 } 507 } 508 } 509 if simple { 510 g.Unhinted = append(g.Unhinted, g.Points[np1:]...) 511 } 512 // Round the 2nd and 4th phantom point to the grid. 513 p := &g.Points[len(g.Points)-3] 514 p.X = (p.X + 32) &^ 63 515 p = &g.Points[len(g.Points)-1] 516 p.Y = (p.Y + 32) &^ 63 517} 518