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