Beispiel #1
0
func loadFont(path string) (*truetype.Font, error) {
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return truetype.Parse(file)
}
Beispiel #2
0
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))
}
Beispiel #3
0
Datei: font.go Projekt: velour/ui
func getFont(path string, sizePts int) font {
	sizePx := int(float64(sizePts)/ptInch*pxInch + 0.5)

	if f, ok := fonts[path]; ok {
		f.size = sizePx
		return f
	}

	in, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer in.Close()

	fdata, err := ioutil.ReadAll(in)
	if err != nil {
		panic(err)
	}

	f := font{
		path:    path,
		Context: freetype.NewContext(),
	}
	if f.Font, err = truetype.Parse(fdata); err != nil {
		panic(err)
	}
	f.SetFont(f.Font)
	f.SetDPI(pxInch)
	fonts[path] = f

	f.size = sizePx
	return f
}
Beispiel #4
0
/*
生成验证码
bg:背景色
fg:前景色
length:字符长度
width:宽度
height:高度
size:字体大小
fontPath:字体文件路径
*/
func GenerateCaptcha(bg, fg *image.Uniform, length int, width int, height int, size float64, fontPath string) *Captcha {
	fontBytes, err := ioutil.ReadFile(fontPath)
	if err != nil {
		panic(err)
	}
	font, err := truetype.Parse(fontBytes)

	if err != nil {
		panic(err)
	}
	cap := &Captcha{}
	cap.Text = randString(length)
	cap.Image = image.NewRGBA(image.Rect(0, 0, width, height))
	draw.Draw(cap.Image, cap.Image.Bounds(), bg, image.ZP, draw.Src)
	c := freetype.NewContext()
	c.SetFont(font)
	c.SetFontSize(size)
	c.SetClip(cap.Image.Bounds())
	c.SetDst(cap.Image)
	c.SetSrc(fg)
	pt := freetype.Pt(0, int(c.PointToFix32(size)>>8))
	for _, s := range cap.Text {
		_, err = c.DrawString(string(s), pt)
		if err != nil {
			panic(err)
			return nil
		}
		pt.X += c.PointToFix32(size * 0.5)
	}
	return cap
}
Beispiel #5
0
// LoadFont loads the given truetype font file data and returns a new
// *TruetypeFont. For easier loading, consider using LoadTTFFile instead. This
// method is simply short-hand for:
//  tt, err := truetype.Parse(data)
//  if err != nil {
//      return nil, err
//  }
//  return &TruetypeFont{
//      Font: tt,
//  }
func LoadFont(data []byte) (*TruetypeFont, error) {
	f, err := truetype.Parse(data)
	if err != nil {
		return nil, err
	}
	return &TruetypeFont{
		Font: f,
	}, nil
}
Beispiel #6
0
func init() {
	font, err := truetype.Parse(luxisr_ttf())
	if err != nil {
		// TODO: log error
		println("error!")
		println(err.Error())
	}

	draw2d.RegisterFont(DefaultFontData, font)
}
Beispiel #7
0
func LoadFont(path string) (*truetype.Font, error) {
	bs, err := ioutil.ReadFile(path)

	// quick debug
	if err != nil {
		fmt.Println(err)
	}
	f, err := truetype.Parse(bs)
	return f, err
}
Beispiel #8
0
func loadFont(fontFileName string) *truetype.Font {
	fontBytes, err := ioutil.ReadFile(path.Join(fontFolder, fontFileName))
	if err != nil {
		log.Println(err)
		return nil
	}
	font, err := truetype.Parse(fontBytes)
	if err != nil {
		log.Println(err)
		return nil
	}
	return font
}
Beispiel #9
0
//AddFont will add a new font file to the list
func (fm *FontManager) AddFont(pathToFontFile string) error {
	fontBytes, err := ioutil.ReadFile(pathToFontFile)
	if err != nil {
		return err
	}

	font, err := truetype.Parse(fontBytes)

	if err != nil {
		return err
	}
	fm.fontFiles = append(fm.fontFiles, pathToFontFile)
	fm.fontObjects[pathToFontFile] = font

	return nil
}
Beispiel #10
0
func loadFont() (*truetype.Font, error) {
	f, err := os.Open(filepath.Join(datadir, "fonts", "luxisr.ttf"))
	if err != nil {
		return nil, err
	}
	data, err := ioutil.ReadAll(f)
	f.Close()
	if err != nil {
		return nil, err
	}
	font, err := truetype.Parse(data)
	if err != nil {
		return nil, err
	}
	return font, nil
}
Beispiel #11
0
func defaultFont() *truetype.Font {
	goroot := os.Getenv("GOROOT")
	if goroot == "" {
		log.Fatal("no goroot set")
	}
	path := goroot + "/src/pkg/freetype-go.googlecode.com/hg/luxi-fonts/luxisr.ttf"
	// Read the font data.
	fontBytes, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}
	font, err := truetype.Parse(fontBytes)
	if err != nil {
		log.Fatal(err)
	}
	return font
}
Beispiel #12
0
func analyzeFont(ttfFile string) SortedGS {
	defer trackTime(time.Now(), "analyze_font")
	f, err := ioutil.ReadFile(ttfFile)
	if err != nil {
		log.Fatal(err)
	}
	font, err := truetype.Parse(f)
	if err != nil {
		log.Fatal(err)
	}
	a := make(SortedGS, len(*alphabet))
	for i, char := range *alphabet {
		rgba := getRGBA(string(char), font)
		nbp := getNumBlackPixels(rgba)
		a[i] = &Artifact{string(char), nbp, nbp}
	}
	a.Normalize()
	sort.Sort(a)
	a = removeDuplicates(a)
	//saveCharacterMap(a)
	return a
}
Beispiel #13
0
func testFontFile(fontfile string) (*truetype.Font, error) {
	fontfile = LocateFont(fontfile)
	fmt.Printf("Testing fontfile %q\n", fontfile)

	b, err := ioutil.ReadFile(fontfile)
	if err != nil {
		return nil, err
	}
	font, err := truetype.Parse(b)
	if err != nil {
		return nil, err
	}
	fupe := font.FUnitsPerEm()
	printBounds(font.Bounds(font.FUnitsPerEm()))
	fmt.Printf("FUnitsPerEm:%d\n\n", fupe)

	// printGlyph(font, 'A', fupe)
	printGlyph(font, 'V', fupe)
	// printGlyph(font, 'a', fupe)
	// printGlyph(font, 'v', fupe)
	return font, nil
}
Beispiel #14
0
func newFont(data []byte, size int) (*font, error) {
	ttf, err := truetype.Parse(data)
	if err != nil {
		return nil, err
	}

	scale := int32(size << 6)
	bounds := ttf.Bounds(scale)
	glyphMaxSizeDips := math.Size{
		W: int(bounds.XMax-bounds.XMin) >> 6,
		H: int(bounds.YMax-bounds.YMin) >> 6,
	}
	ascentDips := int(bounds.YMax >> 6)

	return &font{
		size:             size,
		scale:            scale,
		glyphMaxSizeDips: glyphMaxSizeDips,
		ascentDips:       ascentDips,
		ttf:              ttf,
		resolutions:      make(map[resolution]*glyphTable),
		glyphs:           make(map[rune]*glyph),
	}, nil
}
Beispiel #15
0
// ParseFont just calls the Parse function from the freetype/truetype package.
// It is provided here so that code that imports this package doesn't need
// to also include the freetype/truetype package.
func ParseFont(b []byte) (*truetype.Font, error) {
	return truetype.Parse(b)
}
Beispiel #16
0
func main() {
	b, _ := ioutil.ReadFile("./roboto/roboto-light.ttf")

	font, ferr := tt.Parse(b)

	if ferr != nil {
		fmt.Println("can't parse font %v , len %v", ferr.Error(), len(b))
	}

	fc := ft.NewContext()
	fc.SetFont(font)

	glfw.SetErrorCallback(errorCallback)

	if !glfw.Init() {
		panic("Can't init glfw!")
	}
	defer glfw.Terminate()

	window, err := glfw.CreateWindow(800, 600, "Testing", nil, nil)
	if err != nil {
		panic(err)
	}

	window.MakeContextCurrent()

	gl.Init()

	program := gl.CreateProgram()

	vertexShader := gl.CreateShader(gl.VERTEX_SHADER)
	vertexShader.Source(`
        attribute vec4 a_position;
        attribute vec2 a_coord;
        varying vec2 v_coord;
        void main() {
          gl_Position = a_position;
          v_coord = a_coord;
        }
    `)
	vertexShader.Compile()
	fmt.Printf("vertex: %v\n", vertexShader.GetInfoLog())

	fragmentShader := gl.CreateShader(gl.FRAGMENT_SHADER)
	fragmentShader.Source(`
        varying vec2 v_coord;
        uniform sampler2D s_picture;
        uniform vec4 color;
        uniform bool has_picture;
        void main() {
            if(has_picture) {
	            gl_FragColor = texture2D(s_picture, v_coord);
	        } else {
                gl_FragColor = color;
	        }
        } 
    `)

	fragmentShader.Compile()
	fmt.Printf("fragment %v \n", fragmentShader.GetInfoLog())

	program.AttachShader(vertexShader)
	program.AttachShader(fragmentShader)

	program.Link()

	// ini

	//gl.MatrixMode(gl.PROJECTION)
	//gl.Ortho(0, 640, 0, 480, 0, 1)

	gl.ClearColor(0.5, 0.5, 0.5, 0.0)

	root := widget.Widget{
		Name:       "Red",
		Rect:       image.Rect(0, 0, 800, 600),
		Background: color.RGBA{255, 128, 128, 126},
		OnClick:    widget.ClickInner,
		OnDrag:     widget.DragInner,
		OnHover:    widget.HoverInner,
		OnResize:   widget.ResizeItself,
	}

	blue := root.AddWidget(&widget.Widget{
		Name:       "Blue",
		Rect:       image.Rect(100, 100, 200, 200),
		Image:      LoadImage("./test.png"),
		Background: color.RGBA{128, 128, 255, 126},
		OnClick: func(w *widget.Widget, p image.Point) {
			root.SetTop(w)
			fmt.Println("Clicked blue box")
			widget.ClickInner(w, p)
		},
		OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool {
			widget.DragInner(w, p, d)
			widget.DragItself(w, p, d)
			return true
		},
		OnResize: widget.ResizeItself,
	})

	blue.AddWidget(&widget.Widget{
		Name:       "White",
		Rect:       image.Rect(90, 90, 100, 100),
		Background: color.RGBA{250, 250, 250, 250},
		OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool {
			widget.DragItself(w, p, d)
			blue.Resize(d)
			return true
		},
	})

	root.AddWidget(&widget.Widget{
		Name:       "Green",
		Rect:       image.Rect(100, 300, 200, 400),
		Background: color.RGBA{128, 255, 128, 126},
		OnClick: func(w *widget.Widget, p image.Point) {
			root.SetTop(w)
			w.Image = LoadImage("./test2.png")
		},
		OnDrag: widget.DragItself,
	})

	root.AddWidget(&widget.Widget{
		Name:       "Black",
		Rect:       image.Rect(100, 400, 150, 450),
		Background: color.RGBA{0, 0, 0, 126},
		OnHover: func(w *widget.Widget, p0 image.Point, p1 image.Point) {
			if p1.In(w.Rect) {
				w.Background = color.RGBA{255, 255, 255, 126}
			} else {
				w.Background = color.RGBA{0, 0, 0, 126}
			}
		},
	})

	white := root.AddWidget(&widget.Widget{
		Name:       "White",
		Text:       "Меня зовут Светлана, я из города Иваново. «Единая Россия» очень много сделала достижений: они подняли экономик… экономику, мы стали более лучшие… одеваться, и не было того что щас — это очень большие достижения! В сельском хозяйстве очень хорошо. (Гладин: Что именно в сельском хозяйстве они сделали?) Стало больше… земель за-а… много, ну… я не знаю даже как сказать… засеивать больше земель… а-а-а вот, овощи там, рожь — вот это всё. Что еще сказать… Так как у нас страна многонациональная, у нас в Москве очень много людей, которые очень помогают нам… с других городов… (вопрос Гладина: Вы считаете это достижение «Единой России»?) Да, это большое достижение! Очень хорошее даже! Видите ну… да… Видите ну у нас в Иванове очень хорошая стала медицина… а…что ещё… благоустройство в городах хорошее… с жильём… никаких проблем. Люди подмогают очень хорошо",
		Rect:       image.Rect(400, 200, 700, 500),
		Foreground: color.RGBA{0, 0, 0, 0},
		Background: color.RGBA{255, 255, 255, 126},
		OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool {
			root.SetTop(w)
			widget.DragInner(w, p, d)
			widget.DragItself(w, p, d)
			return true
		},
		OnResize: widget.ResizeItself,
		Padding:  image.Rect(20, 20, 20, 20),
	})

	white.AddWidget(&widget.Widget{
		Name:       "White",
		Rect:       image.Rect(290, 290, 300, 300),
		Background: color.RGBA{0, 0, 0, 250},
		OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool {
			widget.DragItself(w, p, d)
			white.Resize(d)
			return true
		},
	})

	x0 := 0.0
	y0 := 0.0

	window.SetMouseButtonCallback(func(w *glfw.Window, but glfw.MouseButton, act glfw.Action, key glfw.ModifierKey) {
		xpos, ypos := w.GetCursorPosition()

		if act == glfw.Press {
			root.Click(image.Point{X: int(xpos), Y: int(ypos)})

			x0, y0 = xpos, ypos
		}
	})

	window.SetCursorPositionCallback(func(w *glfw.Window, xpos float64, ypos float64) {
		root.Hover(image.Point{X: int(x0), Y: int(y0)}, image.Point{X: int(xpos), Y: int(ypos)})

		if w.GetMouseButton(glfw.MouseButtonLeft) == glfw.Press {
			root.Drag(image.Point{X: int(x0), Y: int(y0)}, image.Point{X: int(xpos - x0), Y: int(ypos - y0)})
			x0, y0 = xpos, ypos
		}

		x0, y0 = xpos, ypos
	})

	width0, height0 := window.GetSize()

	window.SetSizeCallback(func(w *glfw.Window, width int, height int) {
		gl.Viewport(0, 0, width, height)
		root.Rect.Max = image.Point{width, height}
		width0, height0 = width, height
	})

	/*switch(color_type){
	case PNG_COLOR_TYPE_GRAY:
		return GL_LUMINANCE;
	case PNG_COLOR_TYPE_GRAY_ALPHA:
		return GL_LUMINANCE_ALPHA;
	case PNG_COLOR_TYPE_RGB:
		return GL_RGB;
	case PNG_COLOR_TYPE_RGB_ALPHA:
		return GL_RGBA;
	*/

	/*
	   here init texture pool

	   texturePool := make([widget.Widget]texture)
	*/
	for !window.ShouldClose() {
		//Do OpenGL stuff
		program.Use()

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		root.DrawBy(image.Point{}, func(w *widget.Widget, corner image.Point) {
			var texture gl.Texture

			if w.Image != nil {
				texture = gl.GenTexture()
				texture.Bind(gl.TEXTURE_2D)
				gl.ActiveTexture(gl.TEXTURE0)

				gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
					w.Image.Rect.Dx(), w.Image.Rect.Dy(),
					0, gl.RGBA, gl.UNSIGNED_BYTE, w.Image.Pix)
			}

			leftX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0
			leftY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy())

			rightX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0
			rightY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy())

			bottomX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0
			bottomY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy())

			topX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0
			topY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy())

			vertices := []float32{
				leftX, leftY,
				rightX, rightY,
				bottomX, bottomY,
				topX, topY,
			}

			texturePoints := []float32{
				0.0, 0.0, 0.0, 1.0,
				1.0, 0.0, 1.0, 1.0,
			}

			s_picture := program.GetUniformLocation("s_picture")
			s_picture.Uniform1i(0)

			has_picture := program.GetUniformLocation("has_picture")

			if w.Image != nil {
				has_picture.Uniform1i(1)
			} else {
				has_picture.Uniform1i(0)
			}

			col := program.GetUniformLocation("color")
			r, g, b, a := w.Background.RGBA()
			col.Uniform4f(float32(r)/float32(0xFFFF), float32(g)/float32(0xFFFF), float32(b)/float32(0xFFFF), float32(a)/float32(0xFFFF))

			gl.PixelStorei(gl.UNPACK_ALIGNMENT, gl.UNSIGNED_BYTE)
			gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)

			a_position := program.GetAttribLocation("a_position")
			a_position.AttribPointer(2, gl.FLOAT, false, 0, vertices)
			a_position.EnableArray()

			a_coord := program.GetAttribLocation("a_coord")
			a_coord.AttribPointer(2, gl.FLOAT, false, 0, texturePoints)
			a_coord.EnableArray()

			gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
			gl.Flush()

			texture.Delete()

			if len(w.Text) > 0 {
				rct := w.Rect
				rct.Max = rct.Max.Sub(w.Rect.Min)
				rct.Min = rct.Min.Sub(w.Rect.Min)

				fg := image.NewRGBA(rct)
				fgu := image.NewUniform(color.RGBA{0, 16, 32, 255})
				draw.Draw(fg, fg.Bounds(), fgu, image.ZP, draw.Src)

				bg := image.NewRGBA(rct)
				bgu := image.NewUniform(color.RGBA{255, 255, 255, 255})
				draw.Draw(bg, bg.Bounds(), bgu, image.ZP, draw.Src)

				lineHeight := 20.0

				fc.SetDPI(100.0)
				fc.SetFont(font)
				fc.SetFontSize(12.0)
				fc.SetClip(bg.Bounds())
				fc.SetDst(bg)
				fc.SetSrc(fg)

				p0 := ft.Pt(w.Padding.Min.X, w.Padding.Min.Y)
				p := p0
				for _, s := range w.Text {
					p, _ = fc.DrawString(string(s), p)
					if int(p.X>>8) > rct.Max.X-w.Padding.Max.X-w.Padding.Min.X {
						p.X = p0.X
						p.Y += raster.Fix32(lineHeight * 256)
					}
				}

				var texture gl.Texture

				if bg != nil {
					texture = gl.GenTexture()
					texture.Bind(gl.TEXTURE_2D)
					gl.ActiveTexture(gl.TEXTURE0)

					gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
						bg.Rect.Dx(), bg.Rect.Dy(),
						0, gl.RGBA, gl.UNSIGNED_BYTE, bg.Pix)
				}

				leftX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0
				leftY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy())

				rightX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0
				rightY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy())

				bottomX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0
				bottomY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy())

				topX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0
				topY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy())

				vertices := []float32{
					leftX, leftY,
					rightX, rightY,
					bottomX, bottomY,
					topX, topY,
				}

				texturePoints := []float32{
					0.0, 0.0, 0.0, 1.0,
					1.0, 0.0, 1.0, 1.0,
				}

				s_picture := program.GetUniformLocation("s_picture")
				s_picture.Uniform1i(0)

				has_picture := program.GetUniformLocation("has_picture")

				if bg != nil {
					has_picture.Uniform1i(1)
				} else {
					has_picture.Uniform1i(0)
				}

				col := program.GetUniformLocation("color")
				r, g, b, a := w.Background.RGBA()
				col.Uniform4f(float32(r)/float32(0xFFFF), float32(g)/float32(0xFFFF), float32(b)/float32(0xFFFF), float32(a)/float32(0xFFFF))

				gl.PixelStorei(gl.UNPACK_ALIGNMENT, gl.UNSIGNED_BYTE)
				gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)

				a_position := program.GetAttribLocation("a_position")
				a_position.AttribPointer(2, gl.FLOAT, false, 0, vertices)
				a_position.EnableArray()

				a_coord := program.GetAttribLocation("a_coord")
				a_coord.AttribPointer(2, gl.FLOAT, false, 0, texturePoints)
				a_coord.EnableArray()

				gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
				gl.Flush()

				texture.Delete()
			}
		})

		window.SwapBuffers()
		glfw.PollEvents()
	}
}
Beispiel #17
0
// LoadTruetype loads a truetype font from the given stream and
// applies the given font scale in points.
//
// The low and high values determine the lower and upper rune limits
// we should load for this font. For standard ASCII this would be: 32, 127.
func LoadTruetype(r io.Reader, scale int32, low, high rune) (*Font, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	// Read the truetype font.
	ttf, err := truetype.Parse(data)
	if err != nil {
		return nil, err
	}

	// Create our FontConfig type.
	var fc FontConfig
	fc.Low = low
	fc.High = high
	fc.Glyphs = make(Charset, high-low+1)

	// Create an image, large enough to store all requested glyphs.
	//
	// We limit the image to 16 glyphs per row. Then add as many rows as
	// needed to encompass all glyphs, while making sure the resulting image
	// has power-of-two dimensions.
	gc := int32(len(fc.Glyphs))
	glyphsPerRow := int32(16)
	glyphsPerCol := (gc / glyphsPerRow) + 1

	gb := ttf.Bounds(scale)
	gw := (gb.XMax - gb.XMin)
	// why?
	gh := (gb.YMax - gb.YMin) + 5

	iw := Pow2(uint32(gw * glyphsPerRow))
	ih := Pow2(uint32(gh * glyphsPerCol))

	fg, bg := image.White, image.Transparent
	rect := image.Rect(0, 0, int(iw), int(ih))
	img := image.NewRGBA(rect)
	draw.Draw(img, img.Bounds(), bg, image.ZP, draw.Src)

	// Use a freetype context to do the drawing.
	c := freetype.NewContext()
	c.SetDPI(72) // Do not change this.  It is required in order to have a properly aligned bounding box!!!
	c.SetFont(ttf)
	c.SetFontSize(float64(scale))
	c.SetClip(img.Bounds())
	c.SetDst(img)
	c.SetSrc(fg)

	// Iterate over all relevant glyphs in the truetype font and
	// draw them all to the image buffer.
	//
	// For each glyph, we also create a corresponding Glyph structure
	// for our Charset. It contains the appropriate glyph coordinate offsets.
	var gi int
	var gx, gy int32

	for ch := low; ch <= high; ch++ {
		index := ttf.Index(ch)
		metric := ttf.HMetric(scale, index)

		fc.Glyphs[gi].Advance = int(metric.AdvanceWidth)
		fc.Glyphs[gi].X = int(gx)
		fc.Glyphs[gi].Y = int(gy)
		fc.Glyphs[gi].Width = int(gw)
		fc.Glyphs[gi].Height = int(gh)

		pt := freetype.Pt(int(gx), int(gy)+int(c.PointToFix32(float64(scale))>>8))
		c.DrawString(string(ch), pt)

		if gi%16 == 0 {
			gx = 0
			gy += gh
		} else {
			gx += gw
		}
		gi++
	}
	return loadFont(img, &fc)
}