在图片上绘制文字

知识点

  • 字体库使用
  • 图片合成

本文目标

主要实现合并后的海报上绘制文字的功能(这个需求也是常见的很了),内容比较简单。

实现

这里使用的是 微软雅黑 的字体,请点击进行下载并存放到 runtime/fonts 目录下(字体文件占 16 MB 大小)

安装

  1. $ go get -u github.com/golang/freetype

绘制文字

打开 service/article_service/article_poster.go 文件,增加绘制文字的业务逻辑,如下:

  1. type DrawText struct {
  2. JPG draw.Image
  3. Merged *os.File
  4. Title string
  5. X0 int
  6. Y0 int
  7. Size0 float64
  8. SubTitle string
  9. X1 int
  10. Y1 int
  11. Size1 float64
  12. }
  13. func (a *ArticlePosterBg) DrawPoster(d *DrawText, fontName string) error {
  14. fontSource := setting.AppSetting.RuntimeRootPath + setting.AppSetting.FontSavePath + fontName
  15. fontSourceBytes, err := ioutil.ReadFile(fontSource)
  16. if err != nil {
  17. return err
  18. }
  19. trueTypeFont, err := freetype.ParseFont(fontSourceBytes)
  20. if err != nil {
  21. return err
  22. }
  23. fc := freetype.NewContext()
  24. fc.SetDPI(72)
  25. fc.SetFont(trueTypeFont)
  26. fc.SetFontSize(d.Size0)
  27. fc.SetClip(d.JPG.Bounds())
  28. fc.SetDst(d.JPG)
  29. fc.SetSrc(image.Black)
  30. pt := freetype.Pt(d.X0, d.Y0)
  31. _, err = fc.DrawString(d.Title, pt)
  32. if err != nil {
  33. return err
  34. }
  35. fc.SetFontSize(d.Size1)
  36. _, err = fc.DrawString(d.SubTitle, freetype.Pt(d.X1, d.Y1))
  37. if err != nil {
  38. return err
  39. }
  40. err = jpeg.Encode(d.Merged, d.JPG, nil)
  41. if err != nil {
  42. return err
  43. }
  44. return nil
  45. }

这里主要使用了 freetype 包,分别涉及如下细项:

1、freetype.NewContext:创建一个新的 Context,会对其设置一些默认值

  1. func NewContext() *Context {
  2. return &Context{
  3. r: raster.NewRasterizer(0, 0),
  4. fontSize: 12,
  5. dpi: 72,
  6. scale: 12 << 6,
  7. }
  8. }

2、fc.SetDPI:设置屏幕每英寸的分辨率

3、fc.SetFont:设置用于绘制文本的字体

4、fc.SetFontSize:以磅为单位设置字体大小

5、fc.SetClip:设置剪裁矩形以进行绘制

6、fc.SetDst:设置目标图像

7、fc.SetSrc:设置绘制操作的源图像,通常为 image.Uniform

  1. var (
  2. // Black is an opaque black uniform image.
  3. Black = NewUniform(color.Black)
  4. // White is an opaque white uniform image.
  5. White = NewUniform(color.White)
  6. // Transparent is a fully transparent uniform image.
  7. Transparent = NewUniform(color.Transparent)
  8. // Opaque is a fully opaque uniform image.
  9. Opaque = NewUniform(color.Opaque)
  10. )

8、fc.DrawString:根据 Pt 的坐标值绘制给定的文本内容

业务逻辑

打开 service/article_service/article_poster.go 方法,在 Generate 方法增加绘制文字的代码逻辑,如下:

  1. func (a *ArticlePosterBg) Generate() (string, string, error) {
  2. fullPath := qrcode.GetQrCodeFullPath()
  3. fileName, path, err := a.Qr.Encode(fullPath)
  4. if err != nil {
  5. return "", "", err
  6. }
  7. if !a.CheckMergedImage(path) {
  8. ...
  9. draw.Draw(jpg, jpg.Bounds(), bgImage, bgImage.Bounds().Min, draw.Over)
  10. draw.Draw(jpg, jpg.Bounds(), qrImage, qrImage.Bounds().Min.Sub(image.Pt(a.Pt.X, a.Pt.Y)), draw.Over)
  11. err = a.DrawPoster(&DrawText{
  12. JPG: jpg,
  13. Merged: mergedF,
  14. Title: "Golang Gin 系列文章",
  15. X0: 80,
  16. Y0: 160,
  17. Size0: 42,
  18. SubTitle: "---煎鱼",
  19. X1: 320,
  20. Y1: 220,
  21. Size1: 36,
  22. }, "msyhbd.ttc")
  23. if err != nil {
  24. return "", "", err
  25. }
  26. }
  27. return fileName, path, nil
  28. }

验证

访问生成文章海报的接口 $HOST/api/v1/articles/poster/generate?token=$token,检查其生成结果,如下图

image

总结

在本章节在 连载十五 的基础上增加了绘制文字,在实现上并不困难,而这两块需求一般会同时出现,大家可以多加练习,了解里面的逻辑和其他 API 😁

参考

本系列示例代码

关于

修改记录

  • 第一版:2018 年 02 月 16 日发布文章
  • 第二版:2019 年 10 月 02 日修改文章

如果有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见,如果喜欢或对你有所帮助,欢迎 Star,对作者是一种鼓励和推进。

我的公众号

image