go 实现海报

package main

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

func main() {
    // 根据路径打开模板文件
    dir, _ := os.Getwd()
    fmt.Println(dir)
    dir, err := os.Getwd()
    templateFile, err := os.Open(dir + "/test/top_bg2@3x.png")
    if err != nil {
        panic(err)
    }
    defer func(templateFile *os.File) {
        _ = templateFile.Close()
    }(templateFile)
    // 解码
    templateFileImage, err := png.Decode(templateFile)
    if err != nil {
        panic(err)
    }
    // 新建一张和模板文件一样大小的画布
    newTemplateImage := image.NewRGBA(image.Rect(0, 0, 1125, 2000))

    for x := 0; x < newTemplateImage.Bounds().Dx(); x++ { // 将背景图涂白
        for y := 0; y < newTemplateImage.Bounds().Dy(); y++ {
            newTemplateImage.Set(x, y, color.White)
        }
    }

    // 加载字体文件
    font, err := loadFont(dir + "/test/simsun.ttc")
    if err != nil {
        log.Panicln(err.Error())
        return
    }
    content := freetype.NewContext()
    content.SetFont(font)                      // 设置字体
    content.SetClip(newTemplateImage.Bounds()) //设置可以操作的区域
    content.SetDst(newTemplateImage)           //设置操作的目标文件
    content.SetDPI(72)                         // 设置字体分辨率

    // top
    if true {
        // 将模板图片画到新建的画布上
        draw.Draw(newTemplateImage, templateFileImage.Bounds(), templateFileImage, templateFileImage.Bounds().Min, draw.Over)
        content.SetFontSize(40) // 设置字体大小
        content.SetSrc(image.White)
        _, _ = content.DrawString("D222222222222222", freetype.Pt(255, 208))
        _, _ = content.DrawString("2022年06月16日 11:54:03", freetype.Pt(255, 280))

        iconNoteFile, _ := os.Open(dir + "/test/icon1.png")
        iconNoteImg, _, _ := image.Decode(iconNoteFile)
        draw.Draw(newTemplateImage, iconNoteImg.Bounds().Add(image.Pt(900, 100)), iconNoteImg, image.Point{}, draw.Over)
        _, _ = content.DrawString("品 质 保 证             值 得 信 赖", freetype.Pt(200, 415))
    }

    content.SetSrc(image.Black)
    lineFile, _ := os.Open(dir + "/test/line.png")
    defer func(lineFile *os.File) {
        err = lineFile.Close()
    }(lineFile)
    lineImg, _, _ := image.Decode(lineFile)

    // 收货人
    if true {
        //60-100
        consigneeFile, _ := os.Open(dir + "/test/consignee.png")
        defer func(consigneeFile *os.File) {
            err = consigneeFile.Close()
        }(consigneeFile)
        consigneeImage, _, _ := image.Decode(consigneeFile)
        draw.Draw(newTemplateImage, consigneeImage.Bounds().Add(image.Pt(60, 485)), consigneeImage, image.Point{}, draw.Over)
        _, _ = content.DrawString("张三  18888888888", freetype.Pt(230, 520))
        //_, _ = content.DrawString("收货人: 张三  18888888888", freetype.Pt(60, 520))

        addressFile, _ := os.Open(dir + "/test/address.png")
        defer func(addressFile *os.File) {
            err = addressFile.Close()
        }(addressFile)
        addressImage, _, _ := image.Decode(addressFile)
        draw.Draw(newTemplateImage, addressImage.Bounds().Add(image.Pt(60, 565)), addressImage, image.Point{}, draw.Over)
        _, _ = content.DrawString(" 广东省深圳市宝安区****", freetype.Pt(180, 600))

        draw.Draw(newTemplateImage, lineImg.Bounds().Add(image.Pt(25, 660)), lineImg, image.Point{}, draw.Over)
    }

    goodsDivHeight := 100 //商品块的总高度
    dx := newTemplateImage.Bounds().Dx()

    //商品
    if true {

        minHeight := 252

        //商品图片
        goodsImage, _ := getRemotelyImage("https://dd.jzbull.com/dsx/2010011258/pc/20211129165907c6i9cmvbeupft5d9hrbg.jpg", minHeight, minHeight)
        draw.Draw(newTemplateImage, goodsImage.Bounds().Add(image.Pt(60, 700)), goodsImage, image.Point{}, draw.Over)

        //商品标题
        //字符串截取
        _, _ = content.DrawString("多件任选test-重量1111多件任选test-重量1111多件任选test-重量1111多件任选test-重量1111", freetype.Pt(350, 740))

        //商品规格
        content.SetFontSize(30)
        content.SetSrc(image.NewUniform(color.RGBA{R: 142, G: 145, B: 152, A: 255}))
        _, _ = content.DrawString("第一件:颜色:黑色aaaaaaaa,尺码:sbbbbbbbbbbbbbbbbbbbbbbbbbbbbb x1", freetype.Pt(350, 750+50))
        _, _ = content.DrawString("第二件:颜色:白色,尺码:s x1", freetype.Pt(350, 750+50*2))
        _, _ = content.DrawString("第三件:颜色:红色,尺码:s x1", freetype.Pt(350, 750+50*3))
        _, _ = content.DrawString("第四件:颜色:蓝色,尺码:s x1", freetype.Pt(350, 750+50*4))
        _, _ = content.DrawString("第五件:颜色:黄色,尺码:s x1", freetype.Pt(350, 750+50*5))

        //如果规格过多 则 minHeight 增加
        minHeight += 100

        //价格
        content.SetFontSize(40)
        content.SetSrc(image.Black)
        _, _ = content.DrawString("¥ 59.00", freetype.Pt(350, 750+50*6))

        //印章
        if true {
            authFile, _ := os.Open(dir + "/test/img_2.png")
            defer func(authFile *os.File) {
                err = authFile.Close()
            }(authFile)
            authImg, _, _ := image.Decode(authFile)
            draw.Draw(newTemplateImage, authImg.Bounds().Add(image.Pt(dx-278, 660)), authImg, image.Point{}, draw.Over)
        }

        goodsDivHeight += minHeight

        draw.Draw(newTemplateImage, lineImg.Bounds().Add(image.Pt(25, 750+50*6+50)), lineImg, image.Point{}, draw.Over)

    }

    //订单信息
    if true {
        pointToFixed := content.PointToFixed(40).Round()
        startY := 660 + goodsDivHeight + 60

        goodsNumStr := "x 114"
        startGoodsNumX := getStartX(dx, goodsNumStr, pointToFixed)
        //fmt.Println(startGoodsNumX)

        goodsTotalFile, _ := os.Open(dir + "/test/goods_total.png")
        defer func(goodsTotalFile *os.File) {
            err = goodsTotalFile.Close()
        }(goodsTotalFile)
        goodsTotalImage, _, _ := image.Decode(goodsTotalFile)
        draw.Draw(newTemplateImage, goodsTotalImage.Bounds().Add(image.Pt(60, startY-40)), goodsTotalImage, image.Point{}, draw.Over)
        //_, _ = content.DrawString("商品数量", freetype.Pt(60, startY))
        _, _ = content.DrawString(goodsNumStr, freetype.Pt(startGoodsNumX, startY))

        startGoodsAmountStr := "¥ 59.00"
        startGoodsAmountX := getStartX(dx, startGoodsAmountStr, pointToFixed) + 20 //微调20个像素
        //fmt.Println(startGoodsAmountX)
        goodsAmountFile, _ := os.Open(dir + "/test/goods_amount.png")
        defer func(goodsAmountFile *os.File) {
            err = goodsAmountFile.Close()
        }(goodsAmountFile)
        goodsAmountImage, _, _ := image.Decode(goodsAmountFile)
        draw.Draw(newTemplateImage, goodsAmountImage.Bounds().Add(image.Pt(60, startY+40)), goodsAmountImage, image.Point{}, draw.Over)
        //_, _ = content.DrawString("商品总额", freetype.Pt(60, startY+80))
        _, _ = content.DrawString(startGoodsAmountStr, freetype.Pt(startGoodsAmountX, startY+80))

        startShippingFeeStr := "¥ 0.00"
        startShippingFeeX := getStartX(dx, startShippingFeeStr, pointToFixed) + 20
        //fmt.Println(startShippingFeeX)
        shippingFeeFile, _ := os.Open(dir + "/test/shipping_fee.png")
        defer func(shippingFeeFile *os.File) {
            err = shippingFeeFile.Close()
        }(shippingFeeFile)
        shippingFeeImage, _, _ := image.Decode(shippingFeeFile)
        draw.Draw(newTemplateImage, shippingFeeImage.Bounds().Add(image.Pt(60, startY+120)), shippingFeeImage, image.Point{}, draw.Over)
        //_, _ = content.DrawString("运费", freetype.Pt(60, startY+160))
        _, _ = content.DrawString(startShippingFeeStr, freetype.Pt(startShippingFeeX, startY+160))

        draw.Draw(newTemplateImage, lineImg.Bounds().Add(image.Pt(25, startY+240)), lineImg, image.Point{}, draw.Over)

        startTotalStr := "¥ 59.00"
        startTotalX := getStartX(dx, startTotalStr, pointToFixed) + 20
        //fmt.Println(startTotalX)

        totalFile, _ := os.Open(dir + "/test/total.png")
        defer func(totalFile *os.File) {
            err = totalFile.Close()
        }(totalFile)
        totalImage, _, _ := image.Decode(totalFile)
        draw.Draw(newTemplateImage, totalImage.Bounds().Add(image.Pt(60, startY+280)), totalImage, image.Point{}, draw.Over)
        //_, _ = content.DrawString("合计", freetype.Pt(60, startY+320))
        content.SetSrc(image.NewUniform(color.RGBA{R: 237, G: 39, B: 90, A: 255}))
        _, _ = content.DrawString(startTotalStr, freetype.Pt(startTotalX, startY+320))
    }

    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("dst.png")
    if err != nil {
        fmt.Println(err)
    }
    defer dstFile.Close()
    png.Encode(dstFile, pic)
}

//总宽度 - 自身宽度 - 右侧宽度
func getStartX(dx int, str string, pointToFixed int) int {
    return dx - (len(str)+2)*(pointToFixed/2) - 40
}

func getRemotelyImage(filePath string, width int, height int) (*image.NRGBA, error) {
    resp, err := http.Get(filePath)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    imgDecode, _, err := image.Decode(resp.Body)
    //转成缩略图
    imgDecode = imaging.Resize(imgDecode, width, height, imaging.Lanczos)
    if err != nil {
        return nil, err
    }
    rgba := image.NewNRGBA(image.Rect(0, 0, width, height))
    draw.Draw(rgba, rgba.Bounds(), imgDecode, image.Point{}, draw.Src)
    return rgba, nil
}
作者:admin  创建时间:2022-06-20 11:09
最后编辑:admin  更新时间:2023-04-10 15:46