Mike Schwörer
b78a468632
Some checks failed
Build Docker and Deploy / Run goext test-suite (push) Has been cancelled
368 lines
8.9 KiB
Go
368 lines
8.9 KiB
Go
package wpdf
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/jung-kurt/gofpdf"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/imageext"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
"image"
|
|
"image/color"
|
|
"image/draw"
|
|
"net/http"
|
|
)
|
|
|
|
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 := ""
|
|
if len(bin) > 512 {
|
|
ct = http.DetectContentType(bin[:512])
|
|
} else {
|
|
ct = http.DetectContentType(bin)
|
|
}
|
|
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
|
|
alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
|
debug *bool
|
|
}
|
|
|
|
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) Debug(v bool) *PDFImageOpt {
|
|
opt.debug = &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 (opt *PDFImageOpt) Alpha(alpha float64, blendMode PDFBlendMode) *PDFImageOpt {
|
|
opt.alphaOverride = &dataext.Tuple[float64, PDFBlendMode]{V1: alpha, V2: blendMode}
|
|
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
|
|
compression := imageext.CompressionPNGSpeed
|
|
debug := false
|
|
var crop *imageext.ImageCrop = nil
|
|
var alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
|
|
|
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.Coalesce(opt.compression, compression)
|
|
reEncodePixelPerMM = langext.Coalesce(opt.reEncodePixelPerMM, reEncodePixelPerMM)
|
|
crop = langext.CoalesceOpt(opt.crop, crop)
|
|
debug = langext.Coalesce(opt.debug, debug)
|
|
alphaOverride = langext.CoalesceOpt(opt.alphaOverride, alphaOverride)
|
|
}
|
|
|
|
regName := img.Name
|
|
|
|
var subImageBounds *imageext.PercentageRectangle = nil
|
|
|
|
if imageFit != nil || fillColor != nil || crop != 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
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
var dataImgRect imageext.PercentageRectangle
|
|
dataimg, dataImgRect, err = imageext.ObjectFitImage(dataimg, pxw, pxh, *imageFit, fillColor)
|
|
if err != nil {
|
|
b.b.SetError(err)
|
|
return
|
|
}
|
|
|
|
subImageBounds = &dataImgRect
|
|
}
|
|
|
|
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)
|
|
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)
|
|
|
|
}
|
|
|
|
if alphaOverride != nil {
|
|
oldA, oldBMS := b.b.GetAlpha()
|
|
b.b.SetAlpha(alphaOverride.V1, string(alphaOverride.V2))
|
|
defer func() { b.b.SetAlpha(oldA, oldBMS) }()
|
|
}
|
|
|
|
fpdfOpt := gofpdf.ImageOptions{
|
|
ImageType: imageType,
|
|
ReadDpi: readDpi,
|
|
AllowNegativePosition: allowNegativePosition,
|
|
}
|
|
|
|
b.b.ImageOptions(regName, x, y, w, h, flow, fpdfOpt, link, linkStr)
|
|
|
|
if debug {
|
|
b.Rect(w, h, RectOutline, NewPDFRectOpt().X(x).Y(y).LineWidth(2).DrawColor(255, 0, 0))
|
|
|
|
if subImageBounds != nil {
|
|
r := subImageBounds.Of(imageext.Rectangle{X: x, Y: y, W: w, H: h})
|
|
b.Rect(r.W, r.H, RectFill, NewPDFRectOpt().X(r.X).Y(r.Y).FillColor(255, 0, 0).Alpha(0.2, BlendNormal))
|
|
b.Line(r.X, r.Y, r.X+r.W, r.Y+r.H, NewPDFLineOpt().LineWidth(2).DrawColor(255, 0, 0))
|
|
b.Line(r.X+r.W, r.Y, r.X, r.Y+r.H, NewPDFLineOpt().LineWidth(2).DrawColor(255, 0, 0))
|
|
}
|
|
}
|
|
}
|