3 分钟带你通过 Go 生成宣传海报!

2022-12-05 14:07:15 浏览数 (1)

对于公网应用来说,海报功能是非常非常重要的,它不仅能扩大应用的知名度,还能起到营销的作用。

那从技术上讲,海报该怎么去做呢?

大部分应用都会选择前端渲染,比如 canvas 渲染,这是最常见的。当然也有一些应用选择后端生成,两者各有各的好处,也有各自的缺点。

这里我们不去讨论前端怎么渲染,只讲后端怎么生成。

先来看下我们将要完成的效果,这是没有加文字和二维码的图片:

这是加上我们的二维码和汉字的样子:

因为微信不允许使用二维码的图片,所以我给打马赛克了。

一、使用 Go 的 image 库

Go 在处理图形上,并没有其他语言,比如 Java、Python 这些库多。

主要 Go 的发力点并不在上面,但是如果工作需要用到 Go 来做海报,也不是不可以。

首先我们可以看到 Go 的官方包里面也提供了 image 包,可以用它来处理图片。

但是,并不是特别建议新手小白直接用这包来处理图片,那你会触碰到很多底层的知识。

更加建议使用一些别人封装好的库,使用起来会更加便捷顺手。所以这里我们就直接跳过了哈,嘻嘻。

二、github.com/golang/freetype

这里我推荐的第一个库是这个,他相对来说封装得不是那么面目全非,所以代码量会多一些,但是他比较能熟悉 image 标准库的使用。

1、安装

这个库的官网地址:https://github.com/golang/freetype

安装命令:

代码语言:javascript复制
go get github.com/golang/freetype

然后就可以直接使用了,不过后面我们会用到重置图片大小,需要用到另外一个库,这里也可以一起安装下:

代码语言:javascript复制
go get github.com/nfnt/resize

2、准备材料

在实际开发中,你需要向设计师要两张图,一张是最后的效果图,一张是把需要生成的素材抠掉了的图。

如果里面涉及到自定义字体的还得让他们提供字体给我们。

3、上代码

首先来看下我的文件结构:

  • cmd 是我的入口文件
  • public 素材这些我都放这下面了
    • fonts 这里面我放的是我们的自定义字体
    • image 这里我放了两张图,bg.png 背景图 和 wx.png 二维码图。

接下来直接上代码:

代码语言:javascript复制
package main

import (
 "bytes"
 "fmt"
 "github.com/golang/freetype"
 "github.com/golang/freetype/truetype"
 "github.com/nfnt/resize"
 "image"
 "image/draw"
 "image/jpeg"
 "image/png"
 "io"
 "io/ioutil"
 "log"
 "net/http"
 "os"
 "strings"
)

var (
 fontKai *truetype.Font // 字体
)

func main() {

 // 根据路径打开模板文件
 templateFile, err := os.Open("./public/image/bg.png")
 if err != nil {
  panic(err)
 }
 defer templateFile.Close()
 // 解码
 templateFileImage, err := png.Decode(templateFile)
 if err != nil {
  panic(err)
 }
 // 新建一张和模板文件一样大小的画布
 newTemplateImage := image.NewRGBA(templateFileImage.Bounds())
 // 将模板图片画到新建的画布上
 draw.Draw(newTemplateImage, templateFileImage.Bounds(), templateFileImage, templateFileImage.Bounds().Min, draw.Over)

 // 加载字体文件  这里我们加载两种字体文件
 fontKai, err = loadFont("./public/fonts/kz_wyzt.ttf")
 if err != nil {
  log.Panicln(err.Error())
  return
 }

 // 向图片中写入文字
 // 在写入之前有一些准备工作
 content := freetype.NewContext()
 content.SetClip(newTemplateImage.Bounds())
 content.SetDst(newTemplateImage)
 // 设置字体颜色
 content.SetSrc(image.White)
 // 设置字体分辨率
 content.SetDPI(72)
 // 设置字体大小
 content.SetFontSize(40)
 // 设置字体样式,就是我们上面加载的字体
 content.SetFont(fontKai)
 //  正式写入文字
 // 参数1:要写入的文字
 // 参数2:文字坐标
 content.DrawString("电话:13800008888 地址:xxx_xxx_xxx 商铺", freetype.Pt(151, 2100))

 // 读取二维码图片
 erCodeTemplateFile, err := os.Open("./public/image/wx.png")
 if err != nil {
  panic(err)
 }
 defer templateFile.Close()
 // 解码
 imageData, err := png.Decode(erCodeTemplateFile)
 if err != nil {
  panic(err)
 }
 //   2、重新调整要粘贴图片尺寸
 imageData = resize.Resize(300, 300, imageData, resize.Lanczos3)
 //  粘贴缩略图
 draw.Draw(newTemplateImage,
  newTemplateImage.Bounds().Add(image.Pt(1200, 1800)),
  imageData,
  imageData.Bounds().Min,
  draw.Over)
 // 保存图片
 saveFile(newTemplateImage)
}

// 根据路径加载字体文件
// path 字体的路径
func loadFont(path string) (font *truetype.Font, err error) {
 var fontBytes []byte
 fontBytes, err = ioutil.ReadFile(path) // 读取字体文件
 if err != nil {
  err = fmt.Errorf("加载字体文件出错:%s", err.Error())
  return
 }
 font, err = freetype.ParseFont(fontBytes) // 解析字体文件
 if err != nil {
  err = fmt.Errorf("解析字体文件出错,%s", err.Error())
  return
 }
 return
}

func saveFile(pic *image.RGBA) {
 dstFile, err := os.Create("./public/out/1.png")
 if err != nil {
  fmt.Println(err)
 }
 defer dstFile.Close()
 png.Encode(dstFile, pic)
}

大部分代码都写了注释,如果你有疑问欢迎加我们交流群提问。

三、github.com/hitailang/poster

咱们做事绝对不能一杆子捅到底,还得再准备一个备用库。

前不久一个同学,发消息问我,有个库不能用了,库的 GitHub 都打不开了,问我怎么办?

我能怎么办?这显然是作者关库了呗,不想开放了哇,遇到这种要么找别人下好的给你,要么就直接换库呗,找个替代库。

这个库在封装上比较便利,使用起来比上一个库更加便捷。

1、安装

官方地址:https://github.com/hitailang/poster

安装命令:

代码语言:javascript复制
go get github.com/hitailang/poster

2、上代码

这个库的官方 GitHub 首页就有详细的 demo,我这里就直接抄过来了:

代码语言:javascript复制

func main(){
    nullHandler := &handler.NullHandler{}
 ctx := &handler.Context{
  //图片都绘在这个PNG载体上
  PngCarrier: core.NewPNG(0, 0, 750, 1334),
 }
 //绘制背景图
 backgroundHandler := &handler.BackgroundHandler{
  X:    0, // 图片x坐标
  Y:    0, // 图片y坐标
  Path: "./assets/background.png", //图片路径
 }
 //绘制圆形图像
 imageCircleLocalHandler := &handler.ImageCircleLocalHandler{
  X:   30, // 图片x坐标
  Y:   50, // 图片y坐标
        Path: "./assets/reward.png",
  //URL: "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLJT9ncWLPov6rAzn4VCPSC4QoAvdangHRB1JgszqCvffggAysvzpm5MDb72Io4g9YAScHEw7xSWg/132", //图片路径
 }
 //绘制本地图像
 imageLocalHandler := &handler.ImageLocalHandler{
  X:    30, // 图片x坐标
  Y:    400,// 图片y坐标
  Path: "./assets/reward.png", //图片路径
 }

 //绘制二维码
 qrCodeHandler := &handler.QRCodeHandler{
  X:   30, // 二维码x坐标
  Y:   860,// 二维码y坐标
  URL: "https://github.com/hitailang/poster", // 二维码跳转URL地址
 }
 //绘制文字
 textHandler1 := &handler.TextHandler{
  Next:     handler.Next{},
  X:        180, // 文字x坐标
  Y:        105, // 文字y坐标
  Size:     20,  // 文字大小
  R:        255, // 文字颜色RGB值
  G:        241,
  B:        250,
  Text:     "如果觉得这个库对您有用", // 文字内容
  FontPath: "./assets/msyh.ttf",  // 字体文件
 }
 //绘制文字
 textHandler2 := &handler.TextHandler{
  Next:     handler.Next{},
  X:        180,
  Y:        150,
  Size:     22,
  R:        255,
  G:        241,
  B:        250,
  Text:     "请随意赞赏~~",
  FontPath: "./assets/msyh.ttf",
 }
 //结束绘制,把前面的内容合并成一张图片,输出到build目录
 endHandler := &handler.EndHandler{
  Output: "./build/poster_"   xid.New().String()   ".png",
 }

 // 链式调用绘制过程
 nullHandler.
  SetNext(backgroundHandler).
  SetNext(imageCircleLocalHandler).
  SetNext(textHandler1).
  SetNext(textHandler2).
  SetNext(imageLocalHandler).
  SetNext(qrCodeHandler).
  SetNext(endHandler)

 // 开始执行业务
 if err := nullHandler.Run(ctx); err != nil {
  // 异常
  fmt.Println("Fail | Error:"   err.Error())
  return
 }
 // 成功
 fmt.Println("Success")
 return
}

这个库,有两个缺点:

  • 官方的 handler 在绘制图片时,不支持缩放大小,所以如果有需要调整大小的,要么自己实现它的 handler,要么调整图片大小
  • 官方的库在绘制圆形头像时,只支持绘制 png 格式的,否则在剪切时会出现噪点

四、总结

一般开发中很少会让后台绘制海报,毕竟现在大部分海报上都会带有分享者,也就是用户的头像信息啥的,属于专属图片。

所以这种场景大部分都是提供模板给前端绘制。

但是有些场景,比如商品信息,活动信息等,这些统一的一样的,还是有在后端生成的。

你学废了么?

0 人点赞