【Go语言绘图】相片再加文字(二)
已被浏览: 48 次2020年十一月22日 来源于于: cnblogs/mfrank/p/14175084.html
这一篇将再度详尽详细介绍gg库文档制图文字相关的方法,重要包括:DrawStringAnchored()、DrawStringWrapped()、MeasureMultilineString()、WordWrap()下面来各有进行详尽详细介绍。DrawSt
这一篇将再度详尽详细介绍gg库文档制图文字相关的方法,重要包括:DrawStringAnchored()、DrawStringWrapped()、MeasureMultilineString()、WordWrap()下面来各有进行详尽详细介绍。
[标识:內容1]
倘若不细心究,可能会觉得这一方法是 DrawString() 方法的一个封裝,但看一下里面的进行便可以发现,实际情况正好相反。
// DrawString draws the specified text at the specified point. func (dc *Context) DrawString(s string, x, y float64) { dc.DrawStringAnchored(s, x, y, 0, 0) // DrawStringAnchored draws the specified text at the specified anchor point. // The anchor point is x - w * ax, y - h * ay, where w, h is the size of the // text. Use ax=0.5, ay=0.5 to center the text at the specified point. func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) { w, h := dc.MeasureString(s) x -= ax * w y += ay * h if dc.mask == nil { dc.drawString(dc.im, s, x, y) } else { im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height)) dc.drawString(im, s, x, y) draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
DrawStringAnchored() 方法重要有五个关键主要参数,第一个关键主要参数是要制图的标志符串,后面四个关键主要参数互相管理决策了锚点的位置,具体计算逻辑性性是(x - w * ax, y - h * ay),因而,当ax、ay设置为0时就是左两边两端对齐,这时候锚精准定位点置处于文字框左下角;设置为0.5时就是竖直垂直居中,这时候锚精准定位点置处于文字框中间;设置为1时就是右两边两端对齐,这时候锚精准定位点置处于文字控右上角。
大伙儿来看下具体实际效果:
func TestDrawStringAnchored(t *testing.T){ const S = 1024 dc := gg.NewContext(S, S) dc.SetRGB(1, 1, 1) dc.Clear() dc.SetRGB(0, 0, 0) if err := dc.LoadFontFace("gilmer-heavy.ttf", 96); err != nil { panic(err) dc.DrawStringAnchored("Hello, world!", 0, dc.FontHeight(), 0, 0) dc.DrawStringAnchored("Hello, world!", S/2, S/2, 0.5, 0.5) dc.DrawStringAnchored("Hello, world!", S, S-dc.FontHeight(), 1, 1) dc.SavePNG("out.png")
这儿务必注意的就是锚点的位置,当左两边两端对齐时,锚点在左下角,因而设置的 (0, dc.FontHeight()) 寓意着的是文字框左下角的位置,一样,当竖直垂直居中两边两端对齐时,(S/2, S/2) 寓意着的是文字框管理方法管理中心点的位置,右两边两端对齐时,(S, S-dc.FontHeight()) 寓意着的是文字框右上方点的位置。
DrawStringWrapped这一方法可以比较方便快捷的制图几写作本,还能自动式折行,大部分分十分于真正文字框的具体实际效果。
最先看个例子简单的掌握一下:
func TestDrawStringWrapped(t *testing.T){ const S = 1024 dc := gg.NewContext(S, S) dc.SetRGB(1, 1, 1) dc.Clear() dc.SetRGB(0, 0, 0) if err := dc.LoadFontFace("gilmer-heavy.ttf", 96); err != nil { panic(err) dc.DrawStringWrapped("Hello world! Hello Frank! Hello Alice!", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter) dc.SavePNG("out.png")
制图的具体实际效果下列:
可以看到,不仅自动式换行,而且还保持了英文英语单词的详尽性,没有将一个英文英语单词从这之中间分割起來。
这一方法的关键主要参数有点儿儿多,一目前八个关键主要参数。
第一个关键主要参数寓意着的是要制图的标志符串,比如这儿的Hello world! Hello Frank! Hello Alice!。第六个关键主要参数寓意着文本框的总宽。第七个关键主要参数寓意着行间距。
第二~5和第八个关键主要参数互相管理决策了锚点的位置。这儿的计算比之前稍微复杂一点,要大家来看看里面的具体进行:
// DrawStringWrapped word-wraps the specified string to the given max width // and then draws it at the specified anchor point using the given line // spacing and text alignment. func (dc *Context) DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) { lines := dc.WordWrap(s, width) // sync h formula with MeasureMultilineString h := float64(len(lines)) * dc.fontHeight * lineSpacing h -= (lineSpacing - 1) * dc.fontHeight x -= ax * width y -= ay * h switch align { case AlignLeft: ax = 0 case AlignCenter: ax = 0.5 x += width / 2 case AlignRight: ax = 1 x += width ay = 1 for _, line := range lines { dc.DrawStringAnchored(line, x, y, ax, ay) y += dc.fontHeight * lineSpacing
最开始依据 WordWrap() 方法来临到根据特殊总宽处理以后的每一行务必呈现的标志符串信息内容內容。
lines := dc.WordWrap(s, width)
接着计算行高,这儿计算的状况下是用行数乘以字体样式款式高度再乘以行间距,得到结果后再减去一个行间距。因而这一 lineSpacing 的含义是行间距相对性性于字体样式款式高度的倍率,当 lineSpacing 设置为1时,也就是行间距为0,设置为1.1时,寓意着行间距为字体样式款式高度的0.1倍。
h := float64(len(lines)) * dc.fontHeight * lineSpacing h -= (lineSpacing - 1) * dc.fontHeight
接着是有点儿儿绕的计算。
x -= ax * width y -= ay * h switch align { case AlignLeft: ax = 0 case AlignCenter: ax = 0.5 x += width / 2 case AlignRight: ax = 1 x += width ay = 1 for _, line := range lines { dc.DrawStringAnchored(line, x, y, ax, ay) y += dc.fontHeight * lineSpacing
可以看到,整体逻辑性性是先计算好首创作字的锚精准定位点置,接着对处理过的每个标志符串开启 DrawStringAnchored() 方法进行最终文字制图。大伙儿可以从下往上看,在循环系统系统软件制图之前,先设置了 ay = 1,也就是说锚点的偏位位置会在每一行的顶部,接着大伙儿来看这一ax:
switch align { case AlignLeft: ax = 0 case AlignCenter: ax = 0.5 x += width / 2 case AlignRight: ax = 1 x += width
根据传入的最后一个关键主要参数的不一样值,ax 会设置为不一样的值。当最后一个关键主要参数各有为 AlignLeft、AlignCenter、AlignRight时,ax 和 ay 的构成各有为:(0,1)、(0.5,1)、(1,1),锚点相对性性于单创作字的位置各有为左上方点、上中间位、右上方点。
接着大伙儿再聊看这一 y 的值:
y -= ay * h
y 的初始位置为传入的 y 值减去 ay (y轴偏位) 乘以整体文本框高度,寓意着的含义是初始锚点(x,y)相对性性于文本框的位置,各有传入0、0.5、1情况下别寓意着锚点处于文本框的上边线、正中线和下边在网上。在循环系统系统软件制图文字时,y 的值也会不断调整,寓意着单创作字的锚精准定位点置都不在断变化。
y += dc.fontHeight * lineSpacing
最后来看下 x 的值,初始标值初始锚点相对性性于传入的文本框总宽的相对性性位置,ax 各有为 0、0.5、1 时,各有寓意着初始锚精准定位点于整体文本框的左边线、竖直垂直居中水平线和右边在网上。
x -= ax * width
根据传入的最后一个关键主要参数的不一样,又会对x进行一次调整,那般调整之后,便能进行文字在文本框中左两边两端对齐、竖直垂直居中和右两边两端对齐的具体实际效果了。
switch align { case AlignLeft: ax = 0 case AlignCenter: ax = 0.5 x += width / 2 case AlignRight: ax = 1 x += width
看起来确实挺好用,不用再劳碌换行的事情了。但别高兴的太早,有一点务必注意。这一方法一直根据空格符符来分割标志符串,倘若标志符串没有空格符符,便能变为唯一一创作字的具体实际效果。
dc.DrawStringWrapped("HelloWorld!HelloFrank!HelloAlice!", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter)
你可以以能会觉得,英文英文英语单词正中间全是有时候间格的嘛,理应不用忧虑,但倘若是中文呢?
if err := dc.LoadFontFace("/Users/bytedance/Downloads/font/方正楷体接下去.ttf", 96); err != nil { panic(err) dc.DrawStringWrapped("倘若大伙儿把文字换为中文具体实际效果就没非常好了", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter)
另外,这一方法不可易限制文本框整体高度,因而倘若文本较长,就算可能适当换行,仍然会超出相片范围。
dc.DrawStringWrapped("比如它是一段较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 较长较长较长较长较长 的文字", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter)
另外,它是按照空格符符进行词原素分割的,因而不可易从英文英语单词的中间进行拆分,这就是优点,也是缺点。因为倘若有长英文英语单词的话,可能会导致提前换行,让一些行看起来比别的行短很多。因而要想精确控制,还是得用笨方式。
MeasureMultilineStringMeasureMultilineString() 方法可以精准精确测量几写作字的整体高度和总宽,务必传入用换行符分割好的文本行标志符串和行间距,里面的计算逻辑性性也十分简易。
func (dc *Context) MeasureMultilineString(s string, lineSpacing float64) (width, height float64) { lines := strings.Split(s, "\n") // sync h formula with DrawStringWrapped height = float64(len(lines)) * dc.fontHeight * lineSpacing height -= (lineSpacing - 1) * dc.fontHeight d := font.Drawer{ Face: dc.fontFace, // max width from lines for _, line := range lines { adv := d.MeasureString(line) currentWidth := float64(adv 6) // from gg.Context.MeasureString if currentWidth width { width = currentWidth return width, height
行高的计算紧随面DrawStringWrapped()方法是一样的:
h := float64(len(lines)) * dc.fontHeight * lineSpacing h -= (lineSpacing - 1) * dc.fontHeight
总宽则是取这类文本行中总宽很大的哪一个。
WordWrap这一方法是用以处理文本的,担负对文本根据特殊总宽进行分行,在 DrawStringWrapped() 方法中早就有一定的开启。它内部是开启wordWrap()涵数来进行的。
// WordWrap wraps the specified string to the given max width and current // font face. func (dc *Context) WordWrap(s string, w float64) []string { return wordWrap(dc, s, w)
wordWrap() 涵数做的事情便是先将文字按换行符分割,接着对每一个子标志符串按空格符符进行分割,再依据一个原素一个原素的拼凑来辨别出适合现如今行宽的很大标志符串。
func wordWrap(m measureStringer, s string, width float64) []string { var result []string for _, line := range strings.Split(s, "\n") { fields := splitOnSpace(line) if len(fields)%2 == 1 { fields = append(fields, "") x := "" for i := 0; i len(fields); i += 2 { w, _ := m.MeasureString(x + fields[i]) if w width { if x == "" { result = append(result, fields[i]) x = "" continue } else { result = append(result, x) x = "" x += fields[i] + fields[i+1] if x != "" { result = append(result, x) for i, line := range result { result[i] = strings.TrimSpace(line) return result务必注意的点 otf 字体样式款式文本文档加载
前边的内容中,加载字体样式款式文本文档都运用的是 LoadFontFace() 方法进行的,但务必注意的是,这一方法仅有加载 ttf 字体样式款式文本文档,也就是 true type font,无法加载 otf 字体样式款式文本文档,也就是 open type font。 因而倘若务必加载 otf 字体样式款式文本文档,则务必换一个姿势。
func getOpenTypeFontFace(fontFilePath string, fontSize, dpi float64)(*font.Face, error){ fontData, fontFileReadErr := ioutil.ReadFile(fontFilePath) if fontFileReadErr != nil { return nil, fontFileReadErr otfFont, parseErr := opentype.Parse(fontData) if parseErr != nil { return nil, parseErr otfFace, newFaceErr := opentype.NewFace(otfFont, opentype.FaceOptions{ Size: fontSize, DPI: dpi, if newFaceErr != nil { return nil, newFaceErr return otfFace, nil
来检验一下:
func TestUseOtfFile(t *testing.T){ filePath := "SourceHanSansCN-Bold-2.otf" fontFace, err := getOpenTypeFontFace(filePath, 100, 82) if err != nil { panic(err) const S = 1024 dc := gg.NewContext(S, S) dc.SetRGB(0, 1, 1) dc.Clear() dc.SetRGB(0, 0, 0) dc.SetFontFace(*fontFace) dc.DrawStringWrapped("比如它是一段 较长较长较长 较长较长较长 的文字", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter) dc.SavePNG("out.png")行高的难点
也是有一个务必注意的难点,之前在开发设计设计方案时也踩过坑。SetFontFace 与 LoadFontFace 计算 fontHeight 时姿势不一样,因而导致设置一样的字体样式款式规格时,最终的字体样式款式高度却不一致。
func (dc *Context) SetFontFace(fontFace font.Face) { dc.fontFace = fontFace dc.fontHeight = float64(fontFace.Metrics().Height) / 64 func (dc *Context) LoadFontFace(path string, points float64) error { face, err := LoadFontFace(path, points) if err == nil { dc.fontFace = face dc.fontHeight = points * 72 / 96 return err
可以看到对于行高的计算逻辑性性有着非常大区别,大伙儿可以用一个例子来简单验证一下:
func TestUseOtfFile(t *testing.T){ filePath := "/Users/bytedance/Downloads/font/方正楷体接下去.ttf" fontFace1, err := getOpenTypeFontFace(filePath, 100, 82) if err != nil { panic(err) const S = 1024 dc := gg.NewContext(S, S) dc.SetRGB(0, 1, 1) dc.Clear() dc.SetRGB(0, 0, 0) dc.SetFontFace(*fontFace1) dc.DrawStringWrapped("比如它是一段文字", S/2, S/2, 0.5, 0.5, S, 1, gg.AlignCenter) if err := dc.LoadFontFace("/Users/bytedance/Downloads/font/方正楷体接下去.ttf", 100); err != nil { panic(err) dc.DrawStringWrapped("比如它是一段文字", S/2, S/2 + 100, 0.5, 0.5, S, 1, gg.AlignCenter) dc.SavePNG("out.png")
可以看到,二写作本规格明显不一样。
到此,相关文字制图的相关内容便讲完后。这2~3篇讲解了gg库文档相关文字制图相关的内容,确信对于文字制图早就有着比较好的掌握。实践活动主题活动出最高境界,还是务必多改动要用用才掌握是怎样一件事儿。在之后的一篇里,会根据前边的内容进行一个小小的的的实战演练演习应用,要大家的技术专业专业知识真正应用起来~
倘若这篇内容对让你帮助,别忘了关心关注另个人收藏~
河北省省富宇自然环境维护设备机器设备较为比较有限公司是技术性技术专业从事UV光氧,VOC催化反应反映引燃设备机器设备,布袋除尘器,中小型型除尘器,脉冲信号除尘器,单机版版除尘器,旋风除尘器,湿式除尘器及除尘器架构,除尘器布袋,电磁感应磁感应脉冲信号阀,脉冲信号控制仪,星型卸料器等。
chuchenhb河北省省富宇自然环境维护设备机器设备较为比较有限公司生产制造生产制造各种各样各种各样规格型号型号规格沙石料厂除尘器、白灰窑除尘器、仓顶布袋除尘器、脉冲信号单机版版除尘器、除尘器袋笼、除尘布袋、电晕线、卸料器、减速机、电磁感应磁感应阀、膜片等各种各样各种各样除尘器配套设施设备产品。
入站排行莞讯网立足于于互联网服务东莞市市人民群众,将有高价位位值“新闻报道报导”马上有效地散布给东莞市市普通百姓。
搜經典经典励志将为您提供經典经典励志文章内容內容,經典经典励志名言,經典经典励志搞笑幽默搞笑段子,著名人员小小故事,經典经典励志签名,經典经典励志视频,經典经典励志音乐,經典经典励志视频,經典经典励志演说,經典经典励志书籍等經典经典励志类文字信息内容內容共享资源网站。
城经网(cteo.cn)是继书报刊、广播节目广播电台、电视机机台后的第四新闻报道新闻媒体,就是我国发展趋势迅速的地域性管理方法管理中心大成县市门户网网网之一。集中化化了优异的新闻媒体综合服务平台,将新闻报道新闻资讯与线上服务垂直触碰到一般人民群众,具备信息内容內容共享资源資源.
提供WordPress企业主题风格设计风格,WordPress出入口出口外贸主题风格设计风格,WordPress企业模版,WordPress企业主题风格设计风格完全免费免费下载,WordPress企业建设网站,WordPress CMS门户网网主题风格设计风格开发设计设计方案
富士软起动器在我国区销售市场市场销售公司是一家集富士软起动器,维修富士软起动器的企业,能为消费者提供迅捷,优质的富士软起动器维修和富士软起动器维护保养维护保养。 重要服务有富士软起动器,富士软起动器价钱,富士软起动器代理商商销售市场市场销售等