goext/wpdf/wpdfImage.go

329 lines
7.5 KiB
Go
Raw Normal View History

2024-05-14 11:52:56 +02:00
package wpdf
import (
"bytes"
"github.com/jung-kurt/gofpdf"
2024-05-14 12:46:49 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/imageext"
2024-05-14 11:52:56 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
2024-05-14 12:46:49 +02:00
"image"
"image/color"
2024-07-22 15:16:28 +02:00
"image/draw"
2024-05-14 11:52:56 +02:00
"net/http"
)
type PDFImageRef struct {
2024-05-14 12:46:49 +02:00
Info *gofpdf.ImageInfoType
Name string
Bin []byte
Image *image.Image
Mime string
2024-05-14 11:52:56 +02:00
}
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
2024-05-14 12:46:49 +02:00
mime := "application/octet-stream"
2024-05-14 11:52:56 +02:00
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 := ""
if len(bin) > 512 {
ct = http.DetectContentType(bin[:512])
} else {
ct = http.DetectContentType(bin)
}
2024-05-14 11:52:56 +02:00
switch ct {
case "image/jpg":
imageType = "JPG"
2024-05-14 12:46:49 +02:00
mime = ct
2024-05-14 11:52:56 +02:00
case "image/jpeg":
imageType = "JPEG"
2024-05-14 12:46:49 +02:00
mime = ct
2024-05-14 11:52:56 +02:00
case "image/png":
imageType = "PNG"
2024-05-14 12:46:49 +02:00
mime = ct
2024-05-14 11:52:56 +02:00
case "image/gif":
imageType = "GIF"
2024-05-14 12:46:49 +02:00
mime = ct
}
} else {
switch imageType {
case "JPG":
case "JPEG":
mime = "image/jpeg"
case "PNG":
mime = "image/png"
case "GIF":
mime = "image/gif"
2024-05-14 11:52:56 +02:00
}
}
options := gofpdf.ImageOptions{
ImageType: imageType,
ReadDpi: readDpi,
AllowNegativePosition: allowNegativePosition,
}
info := b.b.RegisterImageOptionsReader(imgName, options, bytes.NewReader(bin))
return &PDFImageRef{
2024-05-14 12:46:49 +02:00
Name: imgName,
Info: info,
Bin: bin,
Image: nil,
Mime: mime,
2024-05-14 11:52:56 +02:00
}
}
type PDFImageOpt struct {
x *float64
y *float64
width *float64
height *float64
flow *bool
link *int
linkStr *string
imageType *string
readDpi *bool
allowNegativePosition *bool
2024-05-14 12:46:49 +02:00
imageFit *imageext.ImageFit
fillColor *color.Color
compression *imageext.ImageCompresson
reEncodePixelPerMM *float64
crop *imageext.ImageCrop
2024-05-14 11:52:56 +02:00
}
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
}
2024-05-14 12:46:49 +02:00
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
}
2024-05-14 11:52:56 +02:00
func (b *WPDFBuilder) Image(img *PDFImageRef, opts ...*PDFImageOpt) {
2024-05-14 12:46:49 +02:00
var err error
2024-05-14 11:52:56 +02:00
x := b.GetX()
y := b.GetY()
w := img.Info.Width()
h := img.Info.Height()
flow := true
link := 0
linkStr := ""
imageType := ""
readDpi := false
allowNegativePosition := false
2024-05-14 12:46:49 +02:00
reEncodePixelPerMM := 15.0
var imageFit *imageext.ImageFit = nil
var fillColor color.Color = color.Transparent
compression := imageext.CompressionPNGSpeed
2024-05-14 12:46:49 +02:00
var crop *imageext.ImageCrop = nil
2024-05-14 11:52:56 +02:00
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)
2024-05-14 12:46:49 +02:00
imageFit = langext.CoalesceOpt(opt.imageFit, imageFit)
fillColor = langext.Coalesce(opt.fillColor, fillColor)
compression = langext.Coalesce(opt.compression, compression)
2024-05-14 12:46:49 +02:00
reEncodePixelPerMM = langext.Coalesce(opt.reEncodePixelPerMM, reEncodePixelPerMM)
crop = langext.CoalesceOpt(opt.crop, crop)
}
regName := img.Name
if imageFit != nil || fillColor != nil || crop != nil {
2024-05-14 12:46:49 +02:00
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
}
}
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
}
}
2024-07-22 15:16:28 +02:00
if dataimg.ColorModel() != color.RGBAModel && dataimg.ColorModel() != color.NRGBAModel {
// the image cannto be 16bpp or similar - otherwise fpdf errors out
dataImgRGBA := image.NewNRGBA(image.Rect(0, 0, dataimg.Bounds().Dx(), dataimg.Bounds().Dy()))
draw.Draw(dataImgRGBA, dataImgRGBA.Bounds(), dataimg, dataimg.Bounds().Min, draw.Src)
dataimg = dataImgRGBA
}
bfr, imgMime, err := imageext.EncodeImage(dataimg, compression)
2024-05-14 12:46:49 +02:00
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"
}
b.b.RegisterImageOptionsReader(regName, gofpdf.ImageOptions{ImageType: imageType}, &bfr)
2024-05-14 11:52:56 +02:00
}
fpdfOpt := gofpdf.ImageOptions{
ImageType: imageType,
ReadDpi: readDpi,
AllowNegativePosition: allowNegativePosition,
}
2024-05-14 12:46:49 +02:00
b.b.ImageOptions(regName, x, y, w, h, flow, fpdfOpt, link, linkStr)
2024-05-14 11:52:56 +02:00
}