pdf.go 6.71 KB
package pdfhelper

import (
	"fmt"
	"strings"

	"git.to-net.rs/marko.tikvic/gofpdf"
)

// Block ...
const (
	CENTER   = "C"
	LEFT     = "L"
	RIGHT    = "R"
	TOP      = "T"
	BOTTOM   = "B"
	FULL     = "1"
	NOBORDER = ""
)

const (
	CONTINUE = 0
	NEWLINE  = 1
	BELLOW   = 2
)

const (
	cyrillicEncoding = "cp1251"
	latinEncoding    = "cp1250"
)

// Helper ...
type Helper struct {
	*gofpdf.Fpdf
	translators map[string]func(string) string
}

// New ...
func New(ori, unit, size string) *Helper {
	helper := &Helper{
		Fpdf: gofpdf.New(ori, unit, size, ""),
	}

	return helper
}

func (pdf *Helper) LoadTranslators() {
	pdf.translators = make(map[string]func(string) string)
	pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding)
	pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding)
}

// InsertTab ...
func (pdf *Helper) InsertTab(count int, w, h float64) {
	for i := 0; i < count; i++ {
		pdf.Cell(w, h, "")
	}
}

// CellWithBox ...
func (pdf *Helper) CellWithBox(w, h float64, text, align string) {
	//pdf.drawCellMargins(w, h)
	//pdf.Cell(w, h, text)
	pdf.CellFormat(w, h, pdf.ToUTF8(text), FULL, CONTINUE, align, false, 0, "")
}

func (pdf *Helper) CellWithMargins(w, h float64, text, align string, index, of int) {
	len := of - 1
	border := ""

	// if top cell
	if index == 0 {
		border += "T"
	}

	border += "LR" // always draw these

	// if bottom cell
	if index == len {
		border += "B"
	}

	pdf.CellFormat(w, h, pdf.ToUTF8(text), border, CONTINUE, align, false, 0, "")
}

// MultiRowCellWithBox ...
func (pdf *Helper) MultiRowCellWithBox(w, h float64, text []string, align string) {
	pdf.drawCellMargins(w, h*float64(len(text)))
	for i := range text {
		pdf.CellFormat(w, h, pdf.ToUTF8(text[i]), "", BELLOW, align, false, 0, "")
	}
}

// CellFormat(w, h, text, borders, newLine, fill)

type FormatedCell struct {
	W, H            float64
	Text            string
	Font, FontStyle string
	FontSize        float64
	Border          string
	Alignment       string
}

func (pdf *Helper) FCell(x, y float64, c FormatedCell) {
	pdf.SetXY(x, y)
	pdf.SetFont(c.Font, c.FontStyle, c.FontSize)
	pdf.CellFormat(c.W, c.H, pdf.ToUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "")
}

func (pdf *Helper) Column(x, y float64, cells []FormatedCell) {
	pdf.SetXY(x, y)
	for _, c := range cells {
		pdf.SetFont(c.Font, c.FontStyle, c.FontSize)
		pdf.CellFormat(c.W, c.H, pdf.ToUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "")
		//pdf.CellFormat(c.w, c.h, c.text, c.border, BELLOW, align, false, 0, "")
	}
}

// NewLine ...
func (pdf *Helper) NewLine(h float64) {
	pdf.Ln(h)
}

// WriteColumns ...
func (pdf *Helper) WriteColumns(align string, cols []PDFCell) {
	for _, c := range cols {
		pdf.CellFormat(c.width, c.height, pdf.ToUTF8(c.data), "", CONTINUE, align, false, 0, "")
	}
}

func (pdf *Helper) Row(x, y float64, cells []FormatedCell) {
	pdf.SetXY(x, y)
	for _, c := range cells {
		pdf.SetFont(c.Font, c.FontStyle, c.FontSize)
		pdf.CellFormat(c.W, c.H, pdf.ToUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "")
	}
}

const threeDots = "\u2056\u2056\u2056"

// WriteColumnsWithAlignment ...
func (pdf *Helper) WriteColumnsWithAlignment(cols []PDFCellAligned) {
	for _, c := range cols {
		lines := pdf.SplitText(c.data, c.width)
		if len(lines) == 1 {
			pdf.CellFormat(c.width, c.height, pdf.ToUTF8(lines[0]), "", CONTINUE, c.alignment, false, 0, "")
		} else {
			pdf.CellFormat(c.width, c.height, pdf.ToUTF8(lines[0]+threeDots), "", CONTINUE, c.alignment, false, 0, "")
		}
	}

}

func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string {
	parts := pdf.Fpdf.SplitText(text, maxWidth)
	if len(parts) > 1 {
		return parts[0] + limiter
	}

	return text
}

// InsertImage ...
func (pdf *Helper) InsertImage(img string, x, y, w, h float64) {
	imgType := ""
	if parts := strings.Split(img, "."); len(parts) >= 2 {
		imgType = parts[len(parts)-1]
	}
	opt := gofpdf.ImageOptions{
		ImageType:             imgType,
		ReadDpi:               false,
		AllowNegativePosition: false,
	}
	autoBreak := false // if it's not false then you can't draw the image at an arbitrary height (y position)
	pdf.ImageOptions(img, x, y, w, h, autoBreak, opt, 0, "")
}

// PDFCell ...
type PDFCell struct {
	data          string
	height, width float64
}

// PDFCellAligned ...
type PDFCellAligned struct {
	alignment     string
	data          string
	height, width float64
}

func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 {
	family, _, _, _ = pdf.setCorrectFontFamily(textEncoding(txt))
	return pdf.Fpdf.TextLength(txt, family, style, size)
}

func textEncoding(s string) string {
	encoding := latinEncoding
	runes := []rune(s)
	for _, r := range runes {
		if uint64(r) >= 0x0402 && uint64(r) <= 0x044f {
			encoding = cyrillicEncoding
			break
		}
	}
	return encoding
}

// ToUTF8 ...
func (pdf *Helper) ToUTF8(s string) string {
	encoding := textEncoding(s)
	pdf.setCorrectFontFamily(encoding)
	translator, ok := pdf.translators[encoding]
	if !ok {
		return ""
	}
	return translator(s)
}

func (pdf *Helper) setCorrectFontFamily(enc string) (family, style string, ptSize, unitSize float64) {
	family, style, ptSize, unitSize = pdf.GetFontInfo()
	if enc == cyrillicEncoding {
		if !strings.HasSuffix(family, "cyrillic") {
			family += "cyrillic"
		}
	} else {
		if strings.HasSuffix(family, "cyrillic") {
			family = strings.TrimSuffix(family, "cyrillic")
		}
	}
	pdf.SetFont(family, style, ptSize)
	return family, style, ptSize, unitSize
}

func (pdf *Helper) PageHasSpace(requiredHeight float64) bool {
	_, h := pdf.GetPageSize()
	_, _, _, bot := pdf.GetMargins()
	return (h - bot - pdf.GetY()) > requiredHeight
}

func (pdf *Helper) imageCenterOffset(w, h float64) (x, y float64) {
	pageW, pageH := pdf.GetPageSize()
	x = pageW/2.0 - w/2.0
	y = pageH/2.0 - h/2.0
	return x, y
}

// call before drawing the cell
func (pdf *Helper) drawCellMargins(cw, ch float64) {
	x0, y0 := pdf.GetX(), pdf.GetY()
	pdf.DrawBox(x0, y0, cw, ch)
}

// DrawBox ...
func (pdf Helper) DrawBox(x0, y0, w, h float64) {
	pdf.Line(x0, y0, x0+w, y0)
	pdf.Line(x0+w, y0, x0+w, y0+h)
	pdf.Line(x0+w, y0+h, x0, y0+h)
	pdf.Line(x0, y0+h, x0, y0)
}

// Strana %d/{TotalPages}
func (pdf *Helper) InsertPageNumber(x, y float64, format string) {
	num := fmt.Sprintf(format, pdf.PageNo())
	pdf.Column(x, y, []FormatedCell{{10, 1, num, "DejaVuSans", "", 8, NOBORDER, LEFT}})
}

func (pdf *Helper) SuperscriptText(x, y, cw, ch float64, text, script string) {
	family, style, size, sizeU := pdf.setCorrectFontFamily(textEncoding(text))

	pdf.FCell(x, y, FormatedCell{cw, ch, text, family, style, size, NOBORDER, LEFT})

	sx := x + pdf.TextLength(text, family, style, size)
	sy := y - sizeU*0.2
	pdf.FCell(sx, sy, FormatedCell{cw, ch, script, family, style, size - 2, NOBORDER, LEFT})
}