You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
2.6 KiB
130 lines
2.6 KiB
package goqc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"embed"
|
|
_ "embed"
|
|
"github.com/fogleman/gg"
|
|
"github.com/golang/freetype/truetype"
|
|
"github.com/nfnt/resize"
|
|
"github.com/skip2/go-qrcode"
|
|
"image/png"
|
|
"io"
|
|
"path/filepath"
|
|
)
|
|
|
|
//go:embed simhei.ttf
|
|
var fontContent embed.FS
|
|
var fontPath = "simhei.ttf"
|
|
var fontSize = 16.0
|
|
|
|
func loadFontPath(dc *gg.Context) error {
|
|
// 设置字体和文本属性
|
|
err := dc.LoadFontFace(fontPath, fontSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadFontEmbed(dc *gg.Context) error {
|
|
// 读取嵌入的字体文件
|
|
fontBytes, err := fontContent.ReadFile(fontPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 设置字体
|
|
f, err := truetype.Parse(fontBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 文本属性
|
|
face := truetype.NewFace(f, &truetype.Options{
|
|
Size: fontSize,
|
|
})
|
|
dc.SetFontFace(face)
|
|
return nil
|
|
}
|
|
|
|
// QrCodeOperation 二维码操作
|
|
type QrCodeOperation struct {
|
|
ctx context.Context
|
|
dc *gg.Context
|
|
}
|
|
|
|
// QrCodeText 生成二维码带文本
|
|
func QrCodeText(ctx context.Context, textContent string, qcContent string, qcLevel qrcode.RecoveryLevel) (*QrCodeOperation, error) {
|
|
|
|
// 生成二维码
|
|
qr, err := qrcode.New(qcContent, qcLevel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 设置二维码图片的大小
|
|
qrSize := 256
|
|
|
|
// 生成二维码图片
|
|
qrImage := qr.Image(qrSize)
|
|
|
|
// 调整二维码图片大小
|
|
qrImage = resize.Resize(uint(qrSize), uint(qrSize), qrImage, resize.Lanczos3)
|
|
|
|
// 使用 github.com/fogleman/gg 绘制文本
|
|
dc := gg.NewContext(qrSize, qrSize)
|
|
dc.DrawImage(qrImage, 0, 0)
|
|
|
|
// 设置字体和文本属性
|
|
err = loadFontEmbed(dc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 设置文本颜色
|
|
dc.SetRGB(0, 0, 0)
|
|
|
|
// 计算文本尺寸
|
|
textWidth, textHeight := dc.MeasureString(textContent)
|
|
|
|
// 计算文本位置
|
|
x := (float64(qrSize) - textWidth) / 2
|
|
y := (float64(qrSize+qrSize) + textHeight - textHeight) / 2
|
|
|
|
// 绘制文本
|
|
dc.DrawStringAnchored(textContent, x, y-6, 0, 0)
|
|
|
|
return &QrCodeOperation{ctx: ctx, dc: dc}, err
|
|
}
|
|
|
|
// SavePNG 保存图片
|
|
func (o *QrCodeOperation) SavePNG(filePath, fileName string) error {
|
|
if fileName == "" {
|
|
return o.dc.SavePNG(filePath)
|
|
} else {
|
|
path := filepath.Join(filePath, "/", fileName)
|
|
return o.dc.SavePNG(path)
|
|
}
|
|
}
|
|
|
|
// Encode 返回图片字节
|
|
func (o *QrCodeOperation) Encode() ([]byte, error) {
|
|
img := o.dc.Image()
|
|
|
|
encoder := png.Encoder{CompressionLevel: png.BestCompression}
|
|
|
|
var b bytes.Buffer
|
|
err := encoder.Encode(&b, img)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// EncodePNG 返回图片编码
|
|
func (o *QrCodeOperation) EncodePNG(w io.Writer) error {
|
|
return o.dc.EncodePNG(w)
|
|
}
|