daShangDao_psiServer/service/barcode.go
2026-06-15 13:47:39 +08:00

186 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"bytes"
"encoding/base64"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"os"
systemRes "psi/models/response"
"strings"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/code128"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"golang.org/x/image/math/fixed"
)
type BarcodeService struct{}
// Config 条形码配置
type Config struct {
BarcodeHeight int
ModuleWidth int
Padding int
FontPath string
FontSize float64
BgColor color.Color
BarColor color.Color
}
// GenerateBarcode 根据货号生成条形码图片(Base64)
func (s *BarcodeService) GenerateBarcode(content string) (systemRes.BarcodeResponse, error) {
// 配置参数
config := &Config{
BarcodeHeight: 120,
ModuleWidth: 2,
Padding: 20,
FontPath: "fonts/youaimoshouheiti-regular.ttf",
FontSize: 50,
BgColor: color.White,
BarColor: color.Black,
}
// 若内容包含isbn则使用较小字号且不加粗
if strings.Contains(strings.ToLower(content), "9787") {
config.FontSize = 35
}
// 生成条形码图片的Base64
base64Image, err := generateBase64Barcode(content, config)
if err != nil {
return systemRes.BarcodeResponse{}, fmt.Errorf("生成条形码失败: %w", err)
}
return systemRes.BarcodeResponse{
ImageBase64: base64Image,
Content: content,
}, nil
}
func generateBase64Barcode(content string, config *Config) (string, error) {
// 1. 生成条形码图像
barcodeImg, err := generateBarcodeImage(content, config)
if err != nil {
return "", err
}
barcodeBounds := barcodeImg.Bounds()
barcodeWidth := barcodeBounds.Dx()
barcodeHeight := barcodeBounds.Dy()
// 2. 加载字体
face, err := loadFont(config)
if err != nil {
return "", err
}
defer face.Close()
// 3. 获取文字度量信息
metrics := face.Metrics()
ascent := metrics.Ascent.Ceil()
descent := metrics.Descent.Ceil()
textHeight := ascent + descent
// 计算文字宽度
drawer := &font.Drawer{Face: face}
advance := drawer.MeasureString(content)
textWidth := advance.Ceil()
// 4. 计算最终图片尺寸
gapBetween := 12
textBottomPadding := 12
maxContentWidth := barcodeWidth
if textWidth > maxContentWidth {
maxContentWidth = textWidth
}
finalWidth := maxContentWidth + config.Padding*2
finalHeight := barcodeHeight + gapBetween + textHeight + config.Padding*2 + textBottomPadding
// 5. 创建最终图片
finalImg := image.NewRGBA(image.Rect(0, 0, finalWidth, finalHeight))
draw.Draw(finalImg, finalImg.Bounds(), &image.Uniform{config.BgColor}, image.Point{}, draw.Src)
// 6. 绘制条形码(居中)
barcodeStartX := (finalWidth - barcodeWidth) / 2
barcodeStartY := config.Padding
draw.Draw(finalImg,
image.Rect(barcodeStartX, barcodeStartY, barcodeStartX+barcodeWidth, barcodeStartY+barcodeHeight),
barcodeImg,
image.Point{},
draw.Over)
// 7. 绘制货号文字(居中,加粗字体)
textAreaTop := barcodeStartY + barcodeHeight + gapBetween
textAreaHeight := textHeight + textBottomPadding
baselineY := textAreaTop + (textAreaHeight / 2) + (ascent / 2) - 2
textX := (finalWidth - textWidth) / 2
if textX < 0 {
textX = config.Padding
}
textDrawer := &font.Drawer{
Dst: finalImg,
Src: image.NewUniform(config.BarColor),
Face: face,
Dot: fixed.Point26_6{
X: fixed.I(textX),
Y: fixed.I(baselineY),
},
}
textDrawer.DrawString(content)
// 8. 转换为Base64
buf := new(bytes.Buffer)
if err := png.Encode(buf, finalImg); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(buf.Bytes()), nil
}
// generateBarcodeImage 生成条形码图像
func generateBarcodeImage(content string, config *Config) (image.Image, error) {
// 使用Code128编码
code128Barcode, err := code128.Encode(content)
if err != nil {
return nil, fmt.Errorf("编码失败: %w", err)
}
rawBounds := code128Barcode.Bounds()
rawWidth := rawBounds.Dx()
targetWidth := rawWidth * config.ModuleWidth
targetHeight := config.BarcodeHeight
return barcode.Scale(code128Barcode, targetWidth, targetHeight)
}
// loadFont 加载加粗TrueType字体
func loadFont(config *Config) (font.Face, error) {
fontBytes, err := os.ReadFile(config.FontPath)
if err != nil {
return nil, fmt.Errorf("读取字体文件失败: %w", err)
}
parsedFont, err := opentype.Parse(fontBytes)
if err != nil {
return nil, fmt.Errorf("解析字体失败: %w", err)
}
face, err := opentype.NewFace(parsedFont, &opentype.FaceOptions{
Size: config.FontSize,
DPI: 96,
Hinting: font.HintingFull,
})
if err != nil {
return nil, fmt.Errorf("创建字体失败: %w", err)
}
return face, nil
}