# 3.16 在图片上绘制文字

项目地址：<https://github.com/EDDYCJY/go-gin-example>

## 知识点

* 字体库使用
* 图片合成

## 本文目标

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

## 实现

这里使用的是 [微软雅黑](https://github.com/EDDYCJY/go-gin-example/blob/master/runtime/fonts/msyhbd.ttc) 的字体，请点击进行下载并**存放到 runtime/fonts 目录**下（字体文件占 16 MB 大小）

### 安装

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

### 绘制文字

打开 service/article\_service/article\_poster.go 文件，增加绘制文字的业务逻辑，如下：

```
type DrawText struct {
    JPG    draw.Image
    Merged *os.File

    Title string
    X0    int
    Y0    int
    Size0 float64

    SubTitle string
    X1       int
    Y1       int
    Size1    float64
}

func (a *ArticlePosterBg) DrawPoster(d *DrawText, fontName string) error {
    fontSource := setting.AppSetting.RuntimeRootPath + setting.AppSetting.FontSavePath + fontName
    fontSourceBytes, err := ioutil.ReadFile(fontSource)
    if err != nil {
        return err
    }

    trueTypeFont, err := freetype.ParseFont(fontSourceBytes)
    if err != nil {
        return err
    }

    fc := freetype.NewContext()
    fc.SetDPI(72)
    fc.SetFont(trueTypeFont)
    fc.SetFontSize(d.Size0)
    fc.SetClip(d.JPG.Bounds())
    fc.SetDst(d.JPG)
    fc.SetSrc(image.Black)

    pt := freetype.Pt(d.X0, d.Y0)
    _, err = fc.DrawString(d.Title, pt)
    if err != nil {
        return err
    }

    fc.SetFontSize(d.Size1)
    _, err = fc.DrawString(d.SubTitle, freetype.Pt(d.X1, d.Y1))
    if err != nil {
        return err
    }

    err = jpeg.Encode(d.Merged, d.JPG, nil)
    if err != nil {
        return err
    }

    return nil
}
```

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

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

```
func NewContext() *Context {
    return &Context{
        r:        raster.NewRasterizer(0, 0),
        fontSize: 12,
        dpi:      72,
        scale:    12 << 6,
    }
}
```

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

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

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

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

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

7、fc.SetSrc：设置绘制操作的源图像，通常为 [image.Uniform](https://golang.org/pkg/image/#Uniform)

```
var (
        // Black is an opaque black uniform image.
        Black = NewUniform(color.Black)
        // White is an opaque white uniform image.
        White = NewUniform(color.White)
        // Transparent is a fully transparent uniform image.
        Transparent = NewUniform(color.Transparent)
        // Opaque is a fully opaque uniform image.
        Opaque = NewUniform(color.Opaque)
)
```

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

### 业务逻辑

打开 service/article\_service/article\_poster.go 方法，在 Generate 方法增加绘制文字的代码逻辑，如下：

```
func (a *ArticlePosterBg) Generate() (string, string, error) {
    fullPath := qrcode.GetQrCodeFullPath()
    fileName, path, err := a.Qr.Encode(fullPath)
    if err != nil {
        return "", "", err
    }

    if !a.CheckMergedImage(path) {
        ...

        draw.Draw(jpg, jpg.Bounds(), bgImage, bgImage.Bounds().Min, draw.Over)
        draw.Draw(jpg, jpg.Bounds(), qrImage, qrImage.Bounds().Min.Sub(image.Pt(a.Pt.X, a.Pt.Y)), draw.Over)

        err = a.DrawPoster(&DrawText{
            JPG:    jpg,
            Merged: mergedF,

            Title: "Golang Gin 系列文章",
            X0:    80,
            Y0:    160,
            Size0: 42,

            SubTitle: "---煎鱼",
            X1:       320,
            Y1:       220,
            Size1:    36,
        }, "msyhbd.ttc")

        if err != nil {
            return "", "", err
        }
    }

    return fileName, path, nil
}
```

## 验证

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

![image](https://i.imgur.com/qaaG0LE.jpg)

## 总结

在本章节在 [连载十五](https://github.com/EDDYCJY/blog/blob/master/golang/gin/2018-07-04-Gin%E5%AE%9E%E8%B7%B5-%E8%BF%9E%E8%BD%BD%E5%8D%81%E4%BA%94-%E7%94%9F%E6%88%90%E4%BA%8C%E7%BB%B4%E7%A0%81-%E5%90%88%E5%B9%B6%E6%B5%B7%E6%8A%A5.md) 的基础上增加了绘制文字，在实现上并不困难，而这两块需求一般会同时出现，大家可以多加练习，了解里面的逻辑和其他 API 😁

## 参考

### 本系列示例代码

* [go-gin-example](https://github.com/EDDYCJY/go-gin-example)

## 关于

### 修改记录

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

## ？

如果有任何疑问或错误，欢迎在 [issues](https://github.com/EDDYCJY/blog) 进行提问或给予修正意见，如果喜欢或对你有所帮助，欢迎 Star，对作者是一种鼓励和推进。

### 我的公众号

![image](https://image.eddycjy.com/8d0b0c3a11e74efd5fdfd7910257e70b.jpg)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://eddycjy.gitbook.io/golang/di-3-ke-gin/font.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
