【转载】golang后端动态生成图片、一文搞懂image/draw使用、一张图片粘贴到另一张图片上、根据url获取图片内容、在图片周围画线、invalidJPEGformat: missingSOIm

golang后端动态生成图片、一文搞懂image/draw使用、一张图片粘贴到另一张图片上、根据url获取图片内容、在图片周围画线、invalidJPEGformat: missingSOImarke

后端动态生成图片、在图片上写文字、将另一张图片贴到模板图片上、图片生成边框

最近做项目,遇到一个需求,在后端根据模板文件动态的生成图片然后返回给前端,原以为是一个很简单的需求,但在此过程中遇到了很多的坑,在全网也没有找到一篇合适的博客来讲清楚这件事,特此记录。

首先准备一张模板图片:

在这里插入图片描述

既然要写字,肯定要用到字体,字体我们可以从windows中获取亦可以到网上下载,windows中字体路径为:

C:\Windows\Fonts

1、在图片上写字
代码如下:

package main

import (
	"fmt"
	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io/ioutil"
	"log"
	"os"
)

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

func main() {
	// 根据路径打开模板文件
	templateFile, err := os.Open("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\template.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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\simkai.ttf")
	if err != nil {
		log.Panicln(err.Error())
		return
	}
	fontTtf, err = loadFont("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\STXINWEI.TTF")
	if err != nil {
		log.Panicln(err.Error())
		return
	}

	// 向图片中写入文字
	// 在写入之前有一些准备工作
	content := freetype.NewContext()
	content.SetClip(newTemplateImage.Bounds())
	content.SetDst(newTemplateImage)
	content.SetSrc(image.Black) // 设置字体颜色
	content.SetDPI(72)          // 设置字体分辨率

	content.SetFontSize(40)  // 设置字体大小
	content.SetFont(fontKai) // 设置字体样式,就是我们上面加载的字体

	// 	正式写入文字
	// 参数1:要写入的文字
	// 参数2:文字坐标
	content.DrawString("yida同志:", freetype.Pt(160, 375))
	content.DrawString("您在2022年度中表现突出,忠诚奉献、认真负责,", freetype.Pt(230, 450))
	content.DrawString("被评为", freetype.Pt(160, 520))

	content.DrawString("特发此证,以资鼓励。", freetype.Pt(645, 520))
	// 设置字体大小
	content.SetFontSize(48)
	// 设置字体颜色
	content.SetSrc(image.NewUniform(color.RGBA{R: 237, G: 39, B: 90, A: 255}))
	content.DrawString("“最佳奉献奖”,", freetype.Pt(280, 520))

	content.SetFont(fontTtf) // 设置字体样式
	// 设置字体大小
	content.SetFontSize(32)
	// 设置字体颜色
	content.SetSrc(image.Black)
	content.DrawString("东风战略导弹部队", freetype.Pt(898, 660))
	content.DrawString("二零二二年五月", freetype.Pt(898, 726))

	// 保存图片  ---> 在此我们统一将文件保存到:C:\Users\yida\GolandProjects\GoProjectDemo\A-go-study\dst.png
	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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\dst.png")
	if err != nil {
		fmt.Println(err)
	}
	defer dstFile.Close()
	png.Encode(dstFile, pic)
}


生成的图片如下:
在这里插入图片描述

如何在证书中再粘贴一张图片呢

在做项目的时候有一个需求,是用户传一个图片的地址,这个图片可能是存到七牛云或者阿里云上的,然后要将这个图片的缩略图放到证书上,此时我们应该怎么做呢?

  1. 根据地址获取到图片的内容 存储在七牛云上的一张图片地址:http://qiniu.yueda.vip/123.png
  2. 原图片可能很大,直接粘贴到模板上不合适,所以要先伸缩成固定的大小
  3. 将固定大小的图片粘贴到模板上

完整代码如下:

package main

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

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

func main() {
	// 根据路径打开模板文件
	templateFile, err := os.Open("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\template.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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\simkai.ttf")
	if err != nil {
		log.Panicln(err.Error())
		return
	}
	fontTtf, err = loadFont("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\STXINWEI.TTF")
	if err != nil {
		log.Panicln(err.Error())
		return
	}

	// 向图片中写入文字
	writeWord2Pic(newTemplateImage)

	// ====================向模板中粘贴图片 begin========================
	// 		1、根据地址获取图片内容
	imageData, err := getDataByUrl("http://qiniu.yueda.vip/123.png")
	if err != nil {
		fmt.Println("根据地址获取图片失败,err:", err.Error())
		return
	}
	// 		2、重新调整要粘贴图片尺寸
	imageData = resize.Resize(387, 183, imageData, resize.Lanczos3)
	// 	粘贴缩略图
	draw.Draw(newTemplateImage,
		newTemplateImage.Bounds().Add(image.Pt(228, 558)),
		imageData,
		imageData.Bounds().Min,
		draw.Over)

	// ====================向模板中粘贴图片 结束========================

	// 保存图片  ---> 在此我们统一将文件保存到:C:\Users\yida\GolandProjects\GoProjectDemo\A-go-study\dst.png
	saveFile(newTemplateImage)
}

func writeWord2Pic(newTemplateImage *image.RGBA) {
	// 在写入之前有一些准备工作
	content := freetype.NewContext()
	content.SetClip(newTemplateImage.Bounds())
	content.SetDst(newTemplateImage)
	content.SetSrc(image.Black) // 设置字体颜色
	content.SetDPI(72)          // 设置字体分辨率

	content.SetFontSize(40)  // 设置字体大小
	content.SetFont(fontKai) // 设置字体样式,就是我们上面加载的字体

	// 	正式写入文字
	// 参数1:要写入的文字
	// 参数2:文字坐标
	content.DrawString("yida同志:", freetype.Pt(160, 375))
	content.DrawString("您在2022年度中表现突出,忠诚奉献、认真负责,", freetype.Pt(230, 450))
	content.DrawString("被评为", freetype.Pt(160, 520))

	content.DrawString("特发此证,以资鼓励。", freetype.Pt(645, 520))
	// 设置字体大小
	content.SetFontSize(48)
	// 设置字体颜色
	content.SetSrc(image.NewUniform(color.RGBA{R: 237, G: 39, B: 90, A: 255}))
	content.DrawString("“最佳奉献奖”,", freetype.Pt(280, 520))

	content.SetFont(fontTtf) // 设置字体样式
	// 设置字体大小
	content.SetFontSize(32)
	// 设置字体颜色
	content.SetSrc(image.Black)
	content.DrawString("东风战略导弹部队", freetype.Pt(898, 660))
	content.DrawString("二零二二年五月", freetype.Pt(898, 726))
}

// 根据路径加载字体文件
// 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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\dst.png")
	if err != nil {
		fmt.Println(err)
	}
	defer dstFile.Close()
	png.Encode(dstFile, pic)
}

// 根据地址获取图片内容
func getDataByUrl(url string) (img image.Image, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = fmt.Errorf("[%s]通过url获取数据失败,err:%s", url, err.Error())
		return
	}
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(res.Body)

	// 读取获取的[]byte数据
	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		err = fmt.Errorf("读取数据失败,err:%s", err.Error())
		return
	}

	if !strings.HasSuffix(url, ".jpg") &&
		!strings.HasSuffix(url, ".jpeg") &&
		!strings.HasSuffix(url, ".png") {
		err = fmt.Errorf("[%s]不支持的图片类型,暂只支持.jpg、.png文件类型", url)
		return
	}

	// []byte 转 io.Reader
	reader := bytes.NewReader(data)
	if strings.HasSuffix(url, ".jpg") || strings.HasSuffix(url, ".jpeg") {
		// 此处jgeg.decode 有坑,明明是.jpg的图片但 会报 invalid JPEG format: missing SOI marker 错误
		// 所以当报错时我们再用 png.decode 试试
		img, err = jpeg.Decode(reader)
		if err != nil {
			fmt.Printf("jpeg.Decode err:%s", err.Error())
			reader2 := bytes.NewReader(data)
			img, err = png.Decode(reader2)
			if err != nil {
				err = fmt.Errorf("===>png.Decode err:%s", err.Error())
				return
			}
		}
	}

	if strings.HasSuffix(url, ".png") {
		img, err = png.Decode(reader)
		if err != nil {
			err = fmt.Errorf("png.Decode err:%s", err.Error())
			return
		}
	}

	return
}


图片效果1

在这里插入图片描述

原本到这里就应该结束的,但是,。。。但是产品经理说粘贴的图片周围要加一圈线框,这样好看,类似这样:

在这里插入图片描述
我说那在图片模板里直接加好不行吗,图片模板里加好了我图片直接粘贴到框里面,产品说不行,图片可能是竖向的图,我说那你给我两个模板吧,产品经理说不行,你就简单的画一条线,很容易的。。。

没办法,没能偷到懒,只好肝

其实这简简单单的4条线真不简单,线框到图片之间是透明的,我的想法是这样的:

  1. 先新建一个透明的图层
  2. 将图片粘贴到透明图层上
  3. 在图片四周画上线条
  4. 将画好线的图片粘贴到模板图片上
package main

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

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

func main() {
	// 根据路径打开模板文件
	templateFile, err := os.Open("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\template.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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\simkai.ttf")
	if err != nil {
		log.Panicln(err.Error())
		return
	}
	fontTtf, err = loadFont("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\STXINWEI.TTF")
	if err != nil {
		log.Panicln(err.Error())
		return
	}

	// 向图片中写入文字
	writeWord2Pic(newTemplateImage)

	// ====================向模板中粘贴图片 begin========================
	// 		1、根据地址获取图片内容
	imageData, err := getDataByUrl("http://qiniu.yueda.vip/123.png")
	if err != nil {
		fmt.Println("根据地址获取图片失败,err:", err.Error())
		return
	}
	// 图片到边框距离
	pic2FramePadding := 20
	// 获取全景图原始的尺寸
	dx := imageData.Bounds().Dx()
	dy := imageData.Bounds().Dy()
	// 		2、重新调整要粘贴图片尺寸
	if dx > dy { // 判断是横图还是竖图
		imageData = resize.Resize(uint(387-pic2FramePadding), uint(183-pic2FramePadding), imageData, resize.Lanczos3)
	} else {
		imageData = resize.Resize(uint(387/2-pic2FramePadding), uint(183-pic2FramePadding), imageData, resize.Lanczos3)
	}

	// 新建一个透明图层
	transparentImg := image.NewRGBA(image.Rect(0, 0, imageData.Bounds().Dx()+pic2FramePadding, imageData.Bounds().Dy()+pic2FramePadding))
	// 将缩略图放到透明图层上
	draw.Draw(transparentImg,
		image.Rect(pic2FramePadding/2, pic2FramePadding/2, transparentImg.Bounds().Dx(), transparentImg.Bounds().Dy()),
		imageData,
		image.Point{},
		draw.Over)

	// 图片周围画线
	lineToPic(transparentImg)

	// 	粘贴缩略图
	draw.Draw(newTemplateImage,
		transparentImg.Bounds().Add(image.Pt(228, 558)),
		transparentImg,
		transparentImg.Bounds().Min,
		draw.Over)

	// ====================向模板中粘贴图片 结束========================

	// 保存图片  ---> 在此我们统一将文件保存到:C:\Users\yida\GolandProjects\GoProjectDemo\A-go-study\dst.png
	saveFile(newTemplateImage)
}
func lineToPic(transparentImg *image.RGBA) {
	gc := draw2dimg.NewGraphicContext(transparentImg)
	gc.SetStrokeColor(color.RGBA{ // 线框颜色
		R: uint8(36),
		G: uint8(106),
		B: uint8(96),
		A: 0xff})
	gc.SetFillColor(color.RGBA{})
	gc.SetLineWidth(5) // 线框宽度
	gc.BeginPath()
	gc.MoveTo(0, 0)
	gc.LineTo(float64(transparentImg.Bounds().Dx()), 0)
	gc.LineTo(float64(transparentImg.Bounds().Dx()), float64(transparentImg.Bounds().Dy()))
	gc.LineTo(0, float64(transparentImg.Bounds().Dy()))
	gc.LineTo(0, 0)
	gc.Close()
	gc.FillStroke()
}

func writeWord2Pic(newTemplateImage *image.RGBA) {
	// 在写入之前有一些准备工作
	content := freetype.NewContext()
	content.SetClip(newTemplateImage.Bounds())
	content.SetDst(newTemplateImage)
	content.SetSrc(image.Black) // 设置字体颜色
	content.SetDPI(72)          // 设置字体分辨率

	content.SetFontSize(40)  // 设置字体大小
	content.SetFont(fontKai) // 设置字体样式,就是我们上面加载的字体

	// 	正式写入文字
	// 参数1:要写入的文字
	// 参数2:文字坐标
	content.DrawString("yida同志:", freetype.Pt(160, 375))
	content.DrawString("您在2022年度中表现突出,忠诚奉献、认真负责,", freetype.Pt(230, 450))
	content.DrawString("被评为", freetype.Pt(160, 520))

	content.DrawString("特发此证,以资鼓励。", freetype.Pt(645, 520))
	// 设置字体大小
	content.SetFontSize(48)
	// 设置字体颜色
	content.SetSrc(image.NewUniform(color.RGBA{R: 237, G: 39, B: 90, A: 255}))
	content.DrawString("“最佳奉献奖”,", freetype.Pt(280, 520))

	content.SetFont(fontTtf) // 设置字体样式
	// 设置字体大小
	content.SetFontSize(32)
	// 设置字体颜色
	content.SetSrc(image.Black)
	content.DrawString("东风战略导弹部队", freetype.Pt(898, 660))
	content.DrawString("二零二二年五月", freetype.Pt(898, 726))
}

// 根据路径加载字体文件
// 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("C:\\Users\\yida\\GolandProjects\\GoProjectDemo\\A-go-study\\dst.png")
	if err != nil {
		fmt.Println(err)
	}
	defer dstFile.Close()
	png.Encode(dstFile, pic)
}

// 根据地址获取图片内容
func getDataByUrl(url string) (img image.Image, err error) {
	res, err := http.Get(url)
	if err != nil {
		err = fmt.Errorf("[%s]通过url获取数据失败,err:%s", url, err.Error())
		return
	}
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(res.Body)

	// 读取获取的[]byte数据
	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		err = fmt.Errorf("读取数据失败,err:%s", err.Error())
		return
	}

	if !strings.HasSuffix(url, ".jpg") &&
		!strings.HasSuffix(url, ".jpeg") &&
		!strings.HasSuffix(url, ".png") {
		err = fmt.Errorf("[%s]不支持的图片类型,暂只支持.jpg、.png文件类型", url)
		return
	}

	// []byte 转 io.Reader
	reader := bytes.NewReader(data)
	if strings.HasSuffix(url, ".jpg") || strings.HasSuffix(url, ".jpeg") {
		// 此处jgeg.decode 有坑,明明是.jpg的图片但 会报 invalid JPEG format: missing SOI marker 错误
		// 所以当报错时我们再用 png.decode 试试
		img, err = jpeg.Decode(reader)
		if err != nil {
			fmt.Printf("jpeg.Decode err:%s", err.Error())
			reader2 := bytes.NewReader(data)
			img, err = png.Decode(reader2)
			if err != nil {
				err = fmt.Errorf("===>png.Decode err:%s", err.Error())
				return
			}
		}
	}

	if strings.HasSuffix(url, ".png") {
		img, err = png.Decode(reader)
		if err != nil {
			err = fmt.Errorf("png.Decode err:%s", err.Error())
			return
		}
	}

	return
}


最终的效果图

在这里插入图片描述

来源 https://blog.csdn.net/qq_40585384/article/details/124762939

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang中设置北京图片的方法取决于你具体是想要实现什么功能。如果你想在Golang程序中加载和显示图片,你可以使用Golang的`image`和`image/draw`包来实现。你可以按照以下步骤进行操作: 1. 导入必要的包: ```go import ( "image" "image/draw" "os" ) ``` 2. 打开图片文件: ```go file, err := os.Open("path/to/image.jpg") if err != nil { fmt.Println("打开图片失败:", err) return } defer file.Close() ``` 3. 使用`image.Decode`函数解码图片文件为`image.Image`对象: ```go img, _, err := image.Decode(file) if err != nil { fmt.Println("解码图片失败:", err) return } ``` 4. 创建一个可以绘制的`RGBA`图像: ```go rgba := image.NewRGBA(img.Bounds()) draw.Draw(rgba, img.Bounds(), img, image.Point{}, draw.Src) ``` 5. 在图像上进行你想要的操作,比如添加文字、绘制形状等: ```go // 在图像上添加文字 draw.Draw(rgba, text.Bounds().Add(image.Point{X: 10, Y: 10}), text, image.Point{}, draw.Over) // 在图像上绘制一个矩形 rect := image.Rect(50, 50, 200, 200) draw.Draw(rgba, rect, &image.Uniform{color.RGBA{0, 255, 0, 255}}, image.Point{}, draw.Over) ``` 6. 将修改后的图像保存到文件: ```go output, err := os.Create("path/to/output.jpg") if err != nil { fmt.Println("创建输出文件失败:", err) return } defer output.Close() jpeg.Encode(output, rgba, nil) ``` 这些步骤可以帮助你在Golang中加载和处理图片。根据你的具体需求,你可以根据这个框架进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值