// Implements the Font interface. func (f *TruetypeFont) Measure(i FontIndex) (*GlyphMetrics, error) { // Load into glyph buffer. if f.buf == nil { f.buf = truetype.NewGlyphBuf() } index := truetype.Index(i) scale := f.Font.FUnitsPerEm() err := f.buf.Load(f.Font, scale, index, f.Hinting) if err != nil { return nil, err } hMetric := f.Font.HMetric(scale, index) vMetric := f.Font.VMetric(scale, index) b := f.buf.B return &GlyphMetrics{ Bounds: GlyphBounds{ Min: Point{X: int(b.XMin), Y: int(b.YMin)}, Max: Point{X: int(b.XMax), Y: int(b.YMax)}, }, BearingX: int(hMetric.LeftSideBearing), AdvanceX: int(hMetric.AdvanceWidth), BearingY: int(vMetric.TopSideBearing), AdvanceY: int(vMetric.AdvanceHeight), }, nil }
// Render draws rune r front the specified font at the specified dpi and scale. It returns a // grayscale image that is just large enough to contain the rune. func Render(font *truetype.Font, r rune, dpi, scale float64) (*image.Gray, error) { glyph := truetype.NewGlyphBuf() index := font.Index(r) glyph.Load(font, font.FUnitsPerEm(), index, truetype.FullHinting) ctx := freetype.NewContext() boxer := makeBoundingBoxer() ctx.SetSrc(image.NewUniform(color.White)) ctx.SetDst(boxer) ctx.SetClip(boxer.largeBounds) ctx.SetFontSize(250) ctx.SetDPI(dpi) ctx.SetFont(font) if err := glyph.Load(font, font.FUnitsPerEm(), font.Index(r), truetype.FullHinting); err != nil { return nil, fmt.Errorf("Unable to load glyph: %v\n", err) } var rp raster.Point rp.X = ctx.PointToFix32(0) rp.Y = ctx.PointToFix32(100) ctx.DrawString(string(r), rp) boxer.complete() g := gift.New( gift.Resize(int(float64(boxer.Bounds().Dx())*scale+0.5), int(float64(boxer.Bounds().Dy())*scale+0.5), gift.CubicResampling), ) dst := image.NewGray(g.Bounds(boxer.Bounds())) g.Draw(dst, boxer) return dst, nil }
func main() { flag.Parse() fmt.Printf("Loading fontfile %q\n", *fontfile) b, err := ioutil.ReadFile(*fontfile) if err != nil { log.Println(err) return } font, err := truetype.Parse(b) if err != nil { log.Println(err) return } fupe := font.FUnitsPerEm() printBounds(font.Bounds(fupe)) fmt.Printf("FUnitsPerEm:%d\n\n", fupe) c0, c1 := 'A', 'V' i0 := font.Index(c0) hm := font.HMetric(fupe, i0) g := truetype.NewGlyphBuf() err = g.Load(font, fupe, i0, nil) if err != nil { log.Println(err) return } fmt.Printf("'%c' glyph\n", c0) fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing) printGlyph(g) i1 := font.Index(c1) fmt.Printf("\n'%c', '%c' Kerning:%d\n", c0, c1, font.Kerning(fupe, i0, i1)) }
// NewContext creates a new Context. func NewContext() *Context { return &Context{ r: raster.NewRasterizer(0, 0), glyphBuf: truetype.NewGlyphBuf(), fontSize: 12, dpi: 72, scale: 12 << 6, } }
// NewGraphicContextWithPainter creates a new Graphic context from an image and a Painter (see Freetype-go) func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext { width, height := img.Bounds().Dx(), img.Bounds().Dy() dpi := 92 gc := &ImageGraphicContext{ NewStackGraphicContext(), img, painter, raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), truetype.NewGlyphBuf(), dpi, } return gc }
func (f *font) glyph(r rune) *glyph { if g, found := f.glyphs[r]; found { return g } idx := f.ttf.Index(r) gb := truetype.NewGlyphBuf() err := gb.Load(f.ttf, f.scale, idx, truetype.Hinting(truetype.FullHinting)) if err != nil { panic(err) } g := glyph(*gb) f.glyphs[r] = &g return &g }
func MakeTextLine(font_name, text string, width int, r, g, b, a float64) *TextLine { var w TextLine w.EmbeddedWidget = &BasicWidget{CoreWidget: &w} font, ok := basic_fonts[font_name] if !ok { panic(fmt.Sprintf("Unable to find a font registered as '%s'.", font_name)) } w.font = font w.glyph_buf = truetype.NewGlyphBuf() w.next_text = text w.context = freetype.NewContext() w.context.SetDPI(132) w.context.SetFontSize(12) w.SetColor(r, g, b, a) w.Request_dims = Dims{width, 35} return &w }
func printGlyph(font *truetype.Font, c rune, resolution int32) { var idx = font.Index(c) var hm = font.HMetric(resolution, idx) var g = truetype.NewGlyphBuf() err := g.Load(font, resolution, idx, truetype.NoHinting) if err != nil { log.Println(err) return } fmt.Printf("'%c' glyph\n", c) fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing) printGlyphCurve(g) c1 := 'A' i1 := font.Index(c1) fmt.Printf("\n'%c', '%c' Kerning:%d\n", c, c1, font.Kerning(resolution, idx, i1)) }
// Implements the Font interface. func (f *TruetypeFont) Lookup(i FontIndex) (glyphData interface{}, err error) { // Load into glyph buffer. if f.buf == nil { f.buf = truetype.NewGlyphBuf() } index := truetype.Index(i) scale := f.Font.FUnitsPerEm() err = f.buf.Load(f.Font, scale, index, f.Hinting) if err != nil { return nil, err } // Expand each contour. var ( glyph QuadGlyph e0 int ) for _, e1 := range f.buf.End { f.expandContour(&glyph, f.buf.Point[e0:e1]) e0 = e1 } return glyph, nil }
func genGlyphs(font *truetype.Font, size int, text string) (glyphs []*glyph) { scale := int32(float64(size) * dpi * (64.0 / 72.0)) clip := image.Rect(0, 0, width, height) // Calculate the rasterizer's bounds to handle the largest glyph. b := font.Bounds(scale) xmin := int(b.XMin) >> 6 ymin := -int(b.YMax) >> 6 xmax := int(b.XMax+63) >> 6 ymax := -int(b.YMin-63) >> 6 r := raster.NewRasterizer(xmax-xmin, ymax-ymin) buf := truetype.NewGlyphBuf() for _, variant := range []string{strings.ToUpper(text), strings.ToLower(text)} { pt := Pt(30, 10+int(pointToFix32(float64(size))>>8)) for _, char := range variant { idx := font.Index(char) buf.Load(font, scale, idx, truetype.FullHinting) // Calculate the integer-pixel bounds for the glyph. xmin := int(raster.Fix32(buf.B.XMin<<2)) >> 8 ymin := int(-raster.Fix32(buf.B.YMax<<2)) >> 8 xmax := int(raster.Fix32(buf.B.XMax<<2)+0xff) >> 8 ymax := int(-raster.Fix32(buf.B.YMin<<2)+0xff) >> 8 fx := raster.Fix32(-xmin << 8) fy := raster.Fix32(-ymin << 8) ix := int(pt.X >> 8) iy := int(pt.Y >> 8) // Rasterize the glyph's vectors. r.Clear() e0 := 0 for _, e1 := range buf.End { drawContour(r, buf.Point[e0:e1], fx, fy) e0 = e1 } mask := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin)) r.Rasterize(raster.NewAlphaSrcPainter(mask)) pt.X += raster.Fix32(buf.AdvanceWidth << 2) offset := image.Point{xmin + ix, ymin + iy} glyphRect := mask.Bounds().Add(offset) dr := clip.Intersect(glyphRect) mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} glyphs = append(glyphs, &glyph{ mask: mask, mp: mp, dr: dr, }) } } return }
func main() { flag.Parse() data, err := ioutil.ReadFile(*fontfile) if err != nil { fmt.Printf("Unable to read file: %v", err) os.Exit(1) } font, err := freetype.ParseFont(data) if err != nil { fmt.Printf("Unable to parse font file: %v", err) os.Exit(1) } all := *runes == "ALL" var usable []rune fmt.Printf("This font supports the following runes:\n") fmt.Printf("Rune, Val, Index\n") var gCheck truetype.GlyphBuf for r := rune(1); r < 65535; r++ { index := font.Index(r) if index == 0 { continue } err := gCheck.Load(font, 1, index, truetype.NoHinting) if err == nil { fmt.Printf("'%c': %d, %d\n", r, r, index) } if all { usable = append(usable, r) } } if all { *runes = string(usable) } rIn := make(chan rune) riOut := make(chan runeImage) var wg sync.WaitGroup // Send all of the runes along rIn then close it. go func() { for _, r := range *runes { fmt.Printf("Processing %c\n", r) rIn <- r } close(rIn) }() // Bring up *threads goroutines. Each one reads from rIn, processes the rune and sends a // runeImage along riOut. for i := 0; i < *threads; i++ { wg.Add(1) go func() { defer wg.Done() for r := range rIn { res, err := Render(font, r, *dpi, 0.05) if err != nil { fmt.Printf("Unable to render glyph: %v", err) os.Exit(1) } riOut <- runeImage{r: r, img: res} } }() } // Block until all of the above goroutines are done then close riOut. go func() { wg.Wait() close(riOut) }() // Take the results from riOut, put them into a slice and sort them. var ris runeImageSlice for ri := range riOut { ris = append(ris, ri) } sort.Sort(ris) // Now that runes are sorted, pack them. The packing is simple, just fill up a row until the // next rune wouldn't fit, then go to the next row. The runes are sorted first by largest // height then by smallest width, which isn't necessarily optimal, but it is good in general. // The +2 on several values here is so that there is a small buffer between runes so that we // don't accidentally draw part of one rune while render another. width := *width x, y := 0, 0 cdy := ris[0].img.Bounds().Dy() + 2 for _, ri := range ris { dx := ri.img.Bounds().Dx() + 2 if x+dx > width { x = 0 y += cdy cdy = ri.img.Bounds().Dy() + 2 } x += dx } y += cdy height := 1 for height < y { height *= 2 } // Now that we know the position of all the runes in the atlas, actually draw them onto a single // image. atlas := image.NewGray(image.Rect(0, 0, width, height)) x, y = 0, 0 cdy = ris[0].img.Bounds().Dy() + 2 var dict text.Dictionary dict.Runes = make(map[rune]text.RuneInfo) for _, ri := range ris { dx := ri.img.Bounds().Dx() + 2 if x+dx > width { x = 0 y += cdy cdy = ri.img.Bounds().Dy() + 2 } dict.Runes[ri.r] = text.RuneInfo{PixBounds: ri.img.Bounds().Add(image.Point{x + 1, y + 1})} draw.Draw(atlas, dict.Runes[ri.r].PixBounds, ri.img, image.Point{}, draw.Over) x += dx } // Save the output.png file so that we can see what the atlas looks like. f, err := os.Create(fmt.Sprintf("%s.png", *output)) if err != nil { fmt.Printf("Unable to make output file: %v", err) os.Exit(1) } err = png.Encode(f, atlas) if err != nil { fmt.Printf("Unable to encode png: %v", err) os.Exit(1) } f.Close() // There are several more values that need to go into a dictionary, so we'll go through all of // the runes that we're using and look up those values now. dict.Pix = atlas.Pix dict.Dx = int32(atlas.Bounds().Dx()) dict.Dy = int32(atlas.Bounds().Dy()) for _, r := range *runes { index := font.Index(r) ri := dict.Runes[r] ri.AdvanceHeight = int(font.VMetric(font.FUnitsPerEm(), index).AdvanceHeight) ri.TopSideBearing = int(font.VMetric(font.FUnitsPerEm(), index).TopSideBearing) ri.AdvanceWidth = int(font.HMetric(font.FUnitsPerEm(), index).AdvanceWidth) ri.LeftSideBearing = int(font.HMetric(font.FUnitsPerEm(), index).LeftSideBearing) glyph := truetype.NewGlyphBuf() glyph.Load(font, font.FUnitsPerEm(), index, truetype.FullHinting) ri.GlyphBounds.Min.X = int(glyph.B.XMin) ri.GlyphBounds.Min.Y = int(glyph.B.YMin) ri.GlyphBounds.Max.X = int(glyph.B.XMax) ri.GlyphBounds.Max.Y = int(glyph.B.YMax) dict.Runes[r] = ri } // Also store a mapping from rune pair to the kerning for that pair, if this font has that info. dict.Kerning = make(map[text.RunePair]int) for _, r0 := range *runes { for _, r1 := range *runes { kern := font.Kerning(font.FUnitsPerEm(), font.Index(r0), font.Index(r1)) if kern == 0 { continue } dict.Kerning[text.RunePair{r0, r1}] = int(kern) } } dict.GlyphMax.Min.X = int(font.Bounds(font.FUnitsPerEm()).XMin) dict.GlyphMax.Min.Y = int(font.Bounds(font.FUnitsPerEm()).YMin) dict.GlyphMax.Max.X = int(font.Bounds(font.FUnitsPerEm()).XMax) dict.GlyphMax.Max.Y = int(font.Bounds(font.FUnitsPerEm()).YMax) // Now store the output.dict file so we can use it with text.LoadDictionary() { f, err := os.Create(fmt.Sprintf("%s.dict", *output)) if err != nil { fmt.Printf("Failed to create output file: %v\n", err) os.Exit(1) } enc := gob.NewEncoder(f) err = enc.Encode(dict) if err != nil { fmt.Printf("Failed to encode data to output file: %v\n", err) os.Exit(1) } f.Close() } }