package wpdf import ( "bytes" "github.com/jung-kurt/gofpdf" "gogs.mikescher.com/BlackForestBytes/goext/imageext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "image" "image/color" "image/png" "net/http" "os" ) type PDFImageRef struct { Info *gofpdf.ImageInfoType Name string Bin []byte Image *image.Image Mime string } type PDFImageRegisterOpt struct { imageType *string readDpi *bool allowNegativePosition *bool name *string } func NewPDFImageRegisterOpt() *PDFImageRegisterOpt { return &PDFImageRegisterOpt{} } func (opt *PDFImageRegisterOpt) ImageType(v string) *PDFImageRegisterOpt { opt.imageType = &v return opt } func (opt *PDFImageRegisterOpt) ReadDpi(v bool) *PDFImageRegisterOpt { opt.readDpi = &v return opt } func (opt *PDFImageRegisterOpt) AllowNegativePosition(v bool) *PDFImageRegisterOpt { opt.allowNegativePosition = &v return opt } func (opt *PDFImageRegisterOpt) Name(v string) *PDFImageRegisterOpt { opt.name = &v return opt } func (b *WPDFBuilder) RegisterImage(bin []byte, opts ...*PDFImageRegisterOpt) *PDFImageRef { imgName := "fpdf_img_" + langext.MustRawHexUUID() imageType := "" readDpi := false allowNegativePosition := false mime := "application/octet-stream" for _, opt := range opts { imageType = langext.Coalesce(opt.imageType, imageType) readDpi = langext.Coalesce(opt.readDpi, readDpi) allowNegativePosition = langext.Coalesce(opt.allowNegativePosition, allowNegativePosition) imgName = langext.Coalesce(opt.name, imgName) } if imageType == "" { ct := http.DetectContentType(bin[:512]) switch ct { case "image/jpg": imageType = "JPG" mime = ct case "image/jpeg": imageType = "JPEG" mime = ct case "image/png": imageType = "PNG" mime = ct case "image/gif": imageType = "GIF" mime = ct } } else { switch imageType { case "JPG": case "JPEG": mime = "image/jpeg" case "PNG": mime = "image/png" case "GIF": mime = "image/gif" } } options := gofpdf.ImageOptions{ ImageType: imageType, ReadDpi: readDpi, AllowNegativePosition: allowNegativePosition, } info := b.b.RegisterImageOptionsReader(imgName, options, bytes.NewReader(bin)) return &PDFImageRef{ Name: imgName, Info: info, Bin: bin, Image: nil, Mime: mime, } } type PDFImageOpt struct { x *float64 y *float64 width *float64 height *float64 flow *bool link *int linkStr *string imageType *string readDpi *bool allowNegativePosition *bool imageFit *imageext.ImageFit fillColor *color.Color compression *imageext.ImageCompresson reEncodePixelPerMM *float64 crop *imageext.ImageCrop } func NewPDFImageOpt() *PDFImageOpt { return &PDFImageOpt{} } func (opt *PDFImageOpt) X(v float64) *PDFImageOpt { opt.x = &v return opt } func (opt *PDFImageOpt) Y(v float64) *PDFImageOpt { opt.y = &v return opt } func (opt *PDFImageOpt) Width(v float64) *PDFImageOpt { opt.width = &v return opt } func (opt *PDFImageOpt) Height(v float64) *PDFImageOpt { opt.height = &v return opt } func (opt *PDFImageOpt) Flow(v bool) *PDFImageOpt { opt.flow = &v return opt } func (opt *PDFImageOpt) Link(v int) *PDFImageOpt { opt.link = &v return opt } func (opt *PDFImageOpt) LinkStr(v string) *PDFImageOpt { opt.linkStr = &v return opt } func (opt *PDFImageOpt) ImageType(v string) *PDFImageOpt { opt.imageType = &v return opt } func (opt *PDFImageOpt) ReadDpi(v bool) *PDFImageOpt { opt.readDpi = &v return opt } func (opt *PDFImageOpt) AllowNegativePosition(v bool) *PDFImageOpt { opt.allowNegativePosition = &v return opt } func (opt *PDFImageOpt) ImageFit(v imageext.ImageFit) *PDFImageOpt { opt.imageFit = &v return opt } func (opt *PDFImageOpt) FillColor(v color.Color) *PDFImageOpt { opt.fillColor = &v return opt } func (opt *PDFImageOpt) Compression(v imageext.ImageCompresson) *PDFImageOpt { opt.compression = &v return opt } func (opt *PDFImageOpt) ReEncodePixelPerMM(v float64) *PDFImageOpt { opt.reEncodePixelPerMM = &v return opt } func (opt *PDFImageOpt) Crop(cropX float64, cropY float64, cropWidth float64, cropHeight float64) *PDFImageOpt { opt.crop = &imageext.ImageCrop{ CropX: cropX, CropY: cropY, CropWidth: cropWidth, CropHeight: cropHeight, } return opt } func (b *WPDFBuilder) Image(img *PDFImageRef, opts ...*PDFImageOpt) { var err error x := b.GetX() y := b.GetY() w := img.Info.Width() h := img.Info.Height() flow := true link := 0 linkStr := "" imageType := "" readDpi := false allowNegativePosition := false reEncodePixelPerMM := 15.0 var imageFit *imageext.ImageFit = nil var fillColor color.Color = color.Transparent var compression *imageext.ImageCompresson = nil var crop *imageext.ImageCrop = nil for _, opt := range opts { x = langext.Coalesce(opt.x, x) y = langext.Coalesce(opt.y, y) w = langext.Coalesce(opt.width, w) h = langext.Coalesce(opt.height, h) flow = langext.Coalesce(opt.flow, flow) link = langext.Coalesce(opt.link, link) linkStr = langext.Coalesce(opt.linkStr, linkStr) imageType = langext.Coalesce(opt.imageType, imageType) readDpi = langext.Coalesce(opt.readDpi, readDpi) allowNegativePosition = langext.Coalesce(opt.allowNegativePosition, allowNegativePosition) imageFit = langext.CoalesceOpt(opt.imageFit, imageFit) fillColor = langext.Coalesce(opt.fillColor, fillColor) compression = langext.CoalesceOpt(opt.compression, compression) reEncodePixelPerMM = langext.Coalesce(opt.reEncodePixelPerMM, reEncodePixelPerMM) crop = langext.CoalesceOpt(opt.crop, crop) } regName := img.Name if imageFit != nil || fillColor != nil || crop != nil || compression != nil { var dataimg image.Image if img.Image != nil { dataimg = *img.Image } else { dataimg, err = imageext.VerifyAndDecodeImage(bytes.NewReader(img.Bin), img.Mime) if err != nil { b.b.SetError(err) return } img.Image = langext.Ptr(dataimg) } _ = os.WriteFile("/tmp/a.png", img.Bin, 0755) { bfr := bytes.Buffer{} enc := &png.Encoder{CompressionLevel: png.NoCompression} _ = enc.Encode(&bfr, dataimg) _ = os.WriteFile("/tmp/b.png", img.Bin, 0755) } if crop != nil { dataimg, err = imageext.CropImage(dataimg, crop.CropX, crop.CropY, crop.CropWidth, crop.CropHeight) if err != nil { b.b.SetError(err) return } } if imageFit != nil { pdfPixelPerMillimeter := 15.0 pxw := w * pdfPixelPerMillimeter pxh := h * pdfPixelPerMillimeter dataimg, err = imageext.ObjectFitImage(dataimg, pxw, pxh, *imageFit, fillColor) if err != nil { b.b.SetError(err) return } } bfr, imgMime, err := imageext.EncodeImage(dataimg, langext.Coalesce(compression, imageext.CompressionPNGSpeed)) if err != nil { b.b.SetError(err) return } regName = regName + "_" + langext.MustRawHexUUID() switch imgMime { case "image/jpeg": imageType = "JPEG" case "image/png": imageType = "PNG" case "image/gif": imageType = "GIF" } _ = os.WriteFile("/tmp/c.png", bfr.Bytes(), 0755) b.b.RegisterImageOptionsReader(regName, gofpdf.ImageOptions{ImageType: imageType}, &bfr) } fpdfOpt := gofpdf.ImageOptions{ ImageType: imageType, ReadDpi: readDpi, AllowNegativePosition: allowNegativePosition, } b.b.ImageOptions(regName, x, y, w, h, flow, fpdfOpt, link, linkStr) }