package xibao import ( "log" "os" "strings" "git.lxtend.com/qqbot/message" "git.lxtend.com/qqbot/util" "github.com/fogleman/gg" "golang.org/x/image/font/opentype" ) func GenerateCongratulationImage(text string, inputFile, outputFile string, isGood bool) { // 加载喜报背景图片 im, err := gg.LoadImage(inputFile) // 需要提前准备的背景图片 if err != nil { log.Print("无法加载喜报图片:", err) return } // 创建与背景图片大小相同的画布 width := im.Bounds().Dx() height := im.Bounds().Dy() dc := gg.NewContext(width, height) // 将背景图片绘制到画布上 dc.DrawImage(im, 0, 0) // 判断是否为图片 if imgUrl, ok := isImageCQ(text); ok { filePath, err := util.DownloadFile(imgUrl, "/tmp/qqbot", false) if err != nil { log.Print("无法下载图片:", err) return } imC, err := gg.LoadImage(filePath) if err != nil { log.Print("无法加载图片:", err) return } widthC := imC.Bounds().Dx() heightC := imC.Bounds().Dy() dcC := gg.NewContext(widthC, heightC) dcC.DrawImage(imC, 0, 0) // 保存原始状态 dcC.Push() scaleFactor := 1.0 // 根据高度比例进行缩放 if float64(heightC) > float64(height)/1.8 { scaleFactor = (float64(height) / 1.8) / float64(heightC) } // 根据宽度比例进行缩放 if float64(widthC) > float64(width)/1.1 { scaleFactor = (float64(width) / 1.1) / float64(widthC) } // 针对宽高较小的情况进行缩放 if heightC < height/5 && widthC < width/4 { if float64(widthC)*(float64(height)/5)/float64(heightC) > float64(width/4) { scaleFactor = (float64(height) / 5) / float64(heightC) // log.Print(fmt.Sprintf("图片高度过小,已缩放至高度的 %f 倍", scaleFactor)) } else { scaleFactor = (float64(width) / 4) / float64(widthC) // log.Print(fmt.Sprintf("图片宽度过小,已缩放至宽度的 %f 倍", scaleFactor)) } } // 应用缩放比例 // 计算缩放后的图像尺寸 newWidth := int(float64(widthC) * scaleFactor) newHeight := int(float64(heightC) * scaleFactor) // 创建一个新的上下文,使用缩放后的尺寸 dcScaledC := gg.NewContext(newWidth, newHeight) // 将原始图像缩放并绘制到新的上下文中 dcScaledC.Scale(scaleFactor, scaleFactor) dcScaledC.DrawImage(imC, 0, 0) // 在最终画布上绘制缩放后的图像 dc.DrawImageAnchored(dcScaledC.Image(), width/2, height/2, 0.5, 0.5) dc.SavePNG(outputFile) return } // 设置字体和大小,字体文件需要自备,放在合适的路径 // if err := dc.LoadFontFace("./resource/emoji.ttf", 96); err != nil { // log.Print("无法加载字体:", err) // return // } fontBytes, err := os.ReadFile("./resource/emoji.ttf") if err != nil { log.Printf("failed to load font: %v", err) } // 解析字体 font, err := opentype.Parse(fontBytes) if err != nil { log.Printf("failed to parse font: %v", err) } // 设置字体大小和 DPI const fontSize = 48 const dpi = 72 // 创建 font.Face,用于渲染文本 face, err := opentype.NewFace(font, &opentype.FaceOptions{ Size: fontSize, DPI: dpi, Hinting: 0, }) if err != nil { log.Printf("failed to create font face: %v", err) return } dc.SetFontFace(face) if err := dc.LoadFontFace("./resource/font.ttf", 96); err != nil { log.Print("无法加载字体:", err) return } if isGood { // 设置文本颜色为红色 dc.SetRGB(0.8, 0, 0) } else { dc.SetRGB(0, 0, 0.8) } // 将文本按 \n 分割为多行 lines := strings.Split(text, "\n") // 设置初始绘制的 y 位置,可以根据需要调整 startY := float64(height) / 2 lineHeight := 120.0 // 行高,可以根据字体大小调整 // 居中绘制每一行 for i, line := range lines { i = i - len(lines)/2 x := float64(width) / 2 y := startY + float64(i)*lineHeight dc.DrawStringAnchored(line, x, y, 0.5, 0.5) } // 将生成的图片保存为输出文件 err = dc.SavePNG(outputFile) if err != nil { log.Print("无法保存生成的图片:", err) } } func isImageCQ(text string) (string, bool) { imgMsg := message.ImageMessage{} if err := imgMsg.ParseMessage(text); err == nil { return imgMsg.Data.URL, true } return "", false }