diff --git a/date_util.go b/date_util.go index 285a059..dc20e1e 100644 --- a/date_util.go +++ b/date_util.go @@ -2,6 +2,7 @@ package webutility import ( "fmt" + "strings" "time" ) @@ -23,6 +24,11 @@ const ( DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05" ) +var ( + regularYear = [12]int64{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + leapYear = [12]int64{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +) + func Systime() int64 { return time.Now().Unix() } @@ -39,3 +45,26 @@ func DateToEpoch(date, format string) int64 { func EpochToDate(e int64, format string) string { return time.Unix(e, 0).Format(format) } + +func EpochToDayMonthYear(timestamp int64) (d, m, y int64) { + datestring := EpochToDate(timestamp, DDMMYYYY_sl) + parts := strings.Split(datestring, "/") + d = StringToInt64(parts[0]) + m = StringToInt64(parts[1]) + y = StringToInt64(parts[2]) + return d, m, y +} + +func DaysInMonth(year, month int64) int64 { + if month < 1 || month > 12 { + return 0 + } + if IsLeapYear(year) { + return leapYear[month-1] + } + return regularYear[month-1] +} + +func IsLeapYear(year int64) bool { + return year%4 == 0 && !((year%100 == 0) && (year%400 != 0)) +} diff --git a/float_util.go b/float_util.go index 851e210..c6e5be4 100644 --- a/float_util.go +++ b/float_util.go @@ -57,3 +57,10 @@ func BFtoFloat(f *big.Float) float64 { func Float64ToString(f float64) string { return fmt.Sprintf("%.2f", f) } + +func Float64PtrToString(f *float64) string { + if f == nil { + return "" + } + return fmt.Sprintf("%.2f", *f) +} diff --git a/guid.go b/guid.go index 33ab3ef..9de2913 100644 --- a/guid.go +++ b/guid.go @@ -1,10 +1,14 @@ package webutility import ( - "crypto/rand" "fmt" + "math/rand" ) +func SeedGUID(seed int64) { + rand.Seed(seed) +} + // GUID ... func GUID() (string, error) { b := make([]byte, 16) @@ -15,3 +19,8 @@ func GUID() (string, error) { id := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) return id, nil } + +func NewGUID() string { + id, _ := GUID() + return id +} diff --git a/http.go b/http.go index 2502856..cfe36d4 100644 --- a/http.go +++ b/http.go @@ -168,6 +168,6 @@ func GetHeader(r *http.Request, key string) string { return r.Header.Get(key) } -func GetClientAgentUTCOffset(req *http.Request) int64 { +func ClientUTCOffset(req *http.Request) int64 { return StringToInt64(GetHeader(req, "X-Timezone-Offset")) } diff --git a/int_util.go b/int_util.go index bf0d5bd..3c2146c 100644 --- a/int_util.go +++ b/int_util.go @@ -25,6 +25,14 @@ func Int64ToString(i int64) string { return fmt.Sprintf("%d", i) } +// Int64PtrToString ... +func Int64PtrToString(i *int64) string { + if i == nil { + return "" + } + return fmt.Sprintf("%d", *i) +} + // BoolToInt64 ... func BoolToInt64(b bool) int64 { if b { @@ -39,6 +47,7 @@ func Int64ToBool(i int64) bool { } func MaxInt(vars ...int) (max int) { + max = vars[0] for _, v := range vars { if v > max { max = v @@ -46,3 +55,13 @@ func MaxInt(vars ...int) (max int) { } return max } + +func MinInt64(vars ...int64) (min int64) { + min = vars[0] + for _, v := range vars { + if v < min { + min = v + } + } + return min +} diff --git a/nullables.go b/nullables.go index 681ff43..a580559 100644 --- a/nullables.go +++ b/nullables.go @@ -139,6 +139,21 @@ func (ni *NullInt64) Value() (driver.Value, error) { return ni.Int64, nil } +func (ni *NullInt64) Val() int64 { + return ni.Int64 +} + +// Add +func (ni *NullInt64) Add(i NullInt64) { + ni.Valid = true + ni.Int64 += i.Int64 +} + +func (ni *NullInt64) Set(i int64) { + ni.Valid = true + ni.Int64 = i +} + // MarshalJSON ... func (ni NullInt64) MarshalJSON() ([]byte, error) { if ni.Valid { @@ -197,6 +212,22 @@ func (nf *NullFloat64) Value() (driver.Value, error) { return nf.Float64, nil } +// Val ... +func (nf *NullFloat64) Val() float64 { + return nf.Float64 +} + +// Add ... +func (nf *NullFloat64) Add(f NullFloat64) { + nf.Valid = true + nf.Float64 += f.Float64 +} + +func (nf *NullFloat64) Set(f float64) { + nf.Valid = true + nf.Float64 = f +} + // MarshalJSON ... func (nf NullFloat64) MarshalJSON() ([]byte, error) { if nf.Valid { diff --git a/pdfhelper/pdf.go b/pdfhelper/pdf.go new file mode 100644 index 0000000..d1e2c2d --- /dev/null +++ b/pdfhelper/pdf.go @@ -0,0 +1,255 @@ +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) 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(txt) + return pdf.Fpdf.TextLength(txt, family, style, size) +} + +// ToUTF8 ... +func (pdf *Helper) ToUTF8(s string) string { + encoding := latinEncoding + runes := []rune(s) + for _, r := range runes { + if uint64(r) >= 0x0402 && uint64(r) <= 0x044f { + encoding = cyrillicEncoding + break + } + } + 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}}) +}