英文:
Aligning text in golang with Truetype
问题
我正在尝试在一个使用freetype/truetype的Golang项目中将一些文本渲染到png上。如附件所示,我正在尝试在列中渲染4个字母,每个字母都居中于列中。我已经使用truetype API获取了字形的边界和宽度,但是无法将它们转换为给我每个字形的准确偏移量。例如,对于O
字形,给定我使用的字体,我得到了以下尺寸:
Bounds {XMin:0 YMin:-64 XMax:512 YMax:704}
Advance width: 512
最后一个尺寸是从GlyphBuf返回的。
我使用以下代码进行渲染:
tileOffset := (int(tileWidth) * i) + int(tileWidth/2)
pt := freetype.Pt(tileOffset, (imgH-newCharHeight)-int(size))
我该如何使用truetype返回的字形尺寸来正确偏移字母?我尝试使用AdvanceWidth
,如这个plotinum代码(第160行)中所述,但是这并不能给我在所有字形上得到一致的结果。
英文:
I am trying to render some text on a png in a Golang project using freetype/truetype. As you can see from the attachment, I am trying to render 4 letters in columns - each letter centered in the column. Have used the truetype api to get bounds and widths of the glyphs but have been unable to convert these to give me an accurate offset for each glyph. For example with the O
glyph, given the font I using. I get the following dimensions:
Bounds {XMin:0 YMin:-64 XMax:512 YMax:704}
Advance width: 512
With the last dimension being returned from GlyphBuf.
I rendered it using the following:
tileOffset := (int(tileWidth) * i) + int(tileWidth/2)
pt := freetype.Pt(tileOffset, (imgH-newCharHeight)-int(size))
How can I use the glyph dimensions returned by truetype to offset the letters correctly? I have tried using the AdvanceWidth
as detailed in this plotinum code (line 160) but that does not give me a consistent result across all glyphs.
答案1
得分: 11
根据Simon的建议,正确的解决方案是使用AdvanceWidth:
粗略示例:
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"image"
"bufio"
"image/draw"
"image/png"
"image/color"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"github.com/golang/freetype"
"os"
)
var (
dpi = flag.Float64("dpi", 72, "屏幕每英寸的分辨率")
fontfile = flag.String("fontfile", "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", "ttf字体文件的文件名")
hinting = flag.String("hinting", "none", "none | full")
size = flag.Float64("size", 125, "字体大小(以点为单位)")
spacing = flag.Float64("spacing", 1.5, "行间距(例如,2表示双倍行距)")
wonb = flag.Bool("whiteonblack", false, "白色文本在黑色背景上")
text = string("JOJO")
)
func main() {
flag.Parse()
fmt.Printf("加载字体文件 %q\n", *fontfile)
b, err := ioutil.ReadFile(*fontfile)
if err != nil {
log.Println(err)
return
}
f, err := truetype.Parse(b)
if err != nil {
log.Println(err)
return
}
// Freetype 上下文
fg, bg := image.Black, image.White
rgba := image.NewRGBA(image.Rect(0, 0, 1000, 200))
draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
c := freetype.NewContext()
c.SetDPI(*dpi)
c.SetFont(f)
c.SetFontSize(*size)
c.SetClip(rgba.Bounds())
c.SetDst(rgba)
c.SetSrc(fg)
switch *hinting {
default:
c.SetHinting(font.HintingNone)
case "full":
c.SetHinting(font.HintingFull)
}
// 创建一些背景
// 绘制指南线。
ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff}
for rcount := 0; rcount < 4; rcount ++ {
for i := 0; i < 200; i++ {
rgba.Set(250*rcount, i, ruler)
}
}
// Truetype 相关
opts := truetype.Options{}
opts.Size = 125.0
face := truetype.NewFace(f, &opts)
// 计算宽度并打印到图像上
for i, x := range(text) {
awidth, ok := face.GlyphAdvance(rune(x))
if ok != true {
log.Println(err)
return
}
iwidthf := int(float64(awidth) / 64)
fmt.Printf("%+v\n", iwidthf)
pt := freetype.Pt(i*250+(125-iwidthf/2), 128)
c.DrawString(string(x), pt)
fmt.Printf("%+v\n", awidth)
}
// 将该RGBA图像保存到磁盘。
outFile, err := os.Create("out.png")
if err != nil {
log.Println(err)
os.Exit(1)
}
defer outFile.Close()
bf := bufio.NewWriter(outFile)
err = png.Encode(bf, rgba)
if err != nil {
log.Println(err)
os.Exit(1)
}
err = bf.Flush()
if err != nil {
log.Println(err)
os.Exit(1)
}
fmt.Println("已成功写入 out.png。")
}
英文:
As suggested by Simon the correct solution is to use AdvanceWidth:
Crude example:
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"image"
"bufio"
"image/draw"
"image/png"
"image/color"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"github.com/golang/freetype"
"os"
)
var (
dpi = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch")
fontfile = flag.String("fontfile", "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", "filename of the ttf font")
hinting = flag.String("hinting", "none", "none | full")
size = flag.Float64("size", 125, "font size in points")
spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)")
wonb = flag.Bool("whiteonblack", false, "white text on a black background")
text = string("JOJO")
)
func main() {
flag.Parse()
fmt.Printf("Loading fontfile %q\n", *fontfile)
b, err := ioutil.ReadFile(*fontfile)
if err != nil {
log.Println(err)
return
}
f, err := truetype.Parse(b)
if err != nil {
log.Println(err)
return
}
// Freetype context
fg, bg := image.Black, image.White
rgba := image.NewRGBA(image.Rect(0, 0, 1000, 200))
draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
c := freetype.NewContext()
c.SetDPI(*dpi)
c.SetFont(f)
c.SetFontSize(*size)
c.SetClip(rgba.Bounds())
c.SetDst(rgba)
c.SetSrc(fg)
switch *hinting {
default:
c.SetHinting(font.HintingNone)
case "full":
c.SetHinting(font.HintingFull)
}
// Make some background
// Draw the guidelines.
ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff}
for rcount := 0; rcount < 4; rcount ++ {
for i := 0; i < 200; i++ {
rgba.Set(250*rcount, i, ruler)
}
}
// Truetype stuff
opts := truetype.Options{}
opts.Size = 125.0
face := truetype.NewFace(f, &opts)
// Calculate the widths and print to image
for i, x := range(text) {
awidth, ok := face.GlyphAdvance(rune(x))
if ok != true {
log.Println(err)
return
}
iwidthf := int(float64(awidth) / 64)
fmt.Printf("%+v\n", iwidthf)
pt := freetype.Pt(i*250+(125-iwidthf/2), 128)
c.DrawString(string(x), pt)
fmt.Printf("%+v\n", awidth)
}
// Save that RGBA image to disk.
outFile, err := os.Create("out.png")
if err != nil {
log.Println(err)
os.Exit(1)
}
defer outFile.Close()
bf := bufio.NewWriter(outFile)
err = png.Encode(bf, rgba)
if err != nil {
log.Println(err)
os.Exit(1)
}
err = bf.Flush()
if err != nil {
log.Println(err)
os.Exit(1)
}
fmt.Println("Wrote out.png OK.")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论