Commit 1b51eed04dcd7aaa90727f3586530ec57f1d2843

Authored by Marko Tikvić
1 parent b80ee4b2bc
Exists in master

pdf helper

1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 "strings"
5 "time" 6 "time"
6 ) 7 )
7 8
8 const ( 9 const (
9 YYYYMMDD_sl = "2006/01/02" 10 YYYYMMDD_sl = "2006/01/02"
10 YYYYMMDD_ds = "2006-01-02" 11 YYYYMMDD_ds = "2006-01-02"
11 YYYYMMDD_dt = "2006.01.02." 12 YYYYMMDD_dt = "2006.01.02."
12 13
13 DDMMYYYY_sl = "02/01/2006" 14 DDMMYYYY_sl = "02/01/2006"
14 DDMMYYYY_ds = "02-01-2006" 15 DDMMYYYY_ds = "02-01-2006"
15 DDMMYYYY_dt = "02.01.2006." 16 DDMMYYYY_dt = "02.01.2006."
16 17
17 YYYYMMDD_HHMMSS_sl = "2006/01/02 15:04:05" 18 YYYYMMDD_HHMMSS_sl = "2006/01/02 15:04:05"
18 YYYYMMDD_HHMMSS_ds = "2006-01-02 15:04:05" 19 YYYYMMDD_HHMMSS_ds = "2006-01-02 15:04:05"
19 YYYYMMDD_HHMMSS_dt = "2006.01.02. 15:04:05" 20 YYYYMMDD_HHMMSS_dt = "2006.01.02. 15:04:05"
20 21
21 DDMMYYYY_HHMMSS_sl = "02/01/2006 15:04:05" 22 DDMMYYYY_HHMMSS_sl = "02/01/2006 15:04:05"
22 DDMMYYYY_HHMMSS_ds = "02-01-2006 15:04:05" 23 DDMMYYYY_HHMMSS_ds = "02-01-2006 15:04:05"
23 DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05" 24 DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05"
24 ) 25 )
25 26
27 var (
28 regularYear = [12]int64{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
29 leapYear = [12]int64{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
30 )
31
26 func Systime() int64 { 32 func Systime() int64 {
27 return time.Now().Unix() 33 return time.Now().Unix()
28 } 34 }
29 35
30 func DateToEpoch(date, format string) int64 { 36 func DateToEpoch(date, format string) int64 {
31 t, err := time.Parse(format, date) 37 t, err := time.Parse(format, date)
32 if err != nil { 38 if err != nil {
33 fmt.Println(err.Error()) 39 fmt.Println(err.Error())
34 return 0 40 return 0
35 } 41 }
36 return t.Unix() 42 return t.Unix()
37 } 43 }
38 44
39 func EpochToDate(e int64, format string) string { 45 func EpochToDate(e int64, format string) string {
40 return time.Unix(e, 0).Format(format) 46 return time.Unix(e, 0).Format(format)
41 } 47 }
48
49 func EpochToDayMonthYear(timestamp int64) (d, m, y int64) {
50 datestring := EpochToDate(timestamp, DDMMYYYY_sl)
51 parts := strings.Split(datestring, "/")
52 d = StringToInt64(parts[0])
53 m = StringToInt64(parts[1])
54 y = StringToInt64(parts[2])
55 return d, m, y
56 }
57
58 func DaysInMonth(year, month int64) int64 {
59 if month < 1 || month > 12 {
60 return 0
61 }
62 if IsLeapYear(year) {
63 return leapYear[month-1]
64 }
65 return regularYear[month-1]
66 }
67
68 func IsLeapYear(year int64) bool {
69 return year%4 == 0 && !((year%100 == 0) && (year%400 != 0))
70 }
42 71
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 "math" 5 "math"
6 "math/big" 6 "math/big"
7 ) 7 )
8 8
9 func RoundFloat64(f float64, dec int) float64 { 9 func RoundFloat64(f float64, dec int) float64 {
10 p := math.Pow(10, float64(dec)) 10 p := math.Pow(10, float64(dec))
11 return math.Round(f*p) / p 11 return math.Round(f*p) / p
12 } 12 }
13 13
14 func NewBF(f float64, prec uint) *big.Float { 14 func NewBF(f float64, prec uint) *big.Float {
15 x := big.NewFloat(f) 15 x := big.NewFloat(f)
16 x.SetPrec(prec) 16 x.SetPrec(prec)
17 return x 17 return x
18 } 18 }
19 19
20 func AddBF(x, y *big.Float) *big.Float { 20 func AddBF(x, y *big.Float) *big.Float {
21 z := big.NewFloat(0.0) 21 z := big.NewFloat(0.0)
22 z.SetPrec(x.Prec()) 22 z.SetPrec(x.Prec())
23 z.Add(x, y) 23 z.Add(x, y)
24 return z 24 return z
25 } 25 }
26 26
27 func SubBF(x, y *big.Float) *big.Float { 27 func SubBF(x, y *big.Float) *big.Float {
28 z := big.NewFloat(0.0) 28 z := big.NewFloat(0.0)
29 z.SetPrec(x.Prec()) 29 z.SetPrec(x.Prec())
30 30
31 yneg := big.NewFloat(0.0) 31 yneg := big.NewFloat(0.0)
32 yneg.Neg(y) 32 yneg.Neg(y)
33 33
34 z.Add(x, yneg) 34 z.Add(x, yneg)
35 return z 35 return z
36 } 36 }
37 37
38 func MulBF(x, y *big.Float) *big.Float { 38 func MulBF(x, y *big.Float) *big.Float {
39 z := big.NewFloat(0.0) 39 z := big.NewFloat(0.0)
40 z.SetPrec(x.Prec()) 40 z.SetPrec(x.Prec())
41 z.Mul(x, y) 41 z.Mul(x, y)
42 return z 42 return z
43 } 43 }
44 44
45 func DivBF(x, y *big.Float) *big.Float { 45 func DivBF(x, y *big.Float) *big.Float {
46 z := big.NewFloat(0.0) 46 z := big.NewFloat(0.0)
47 z.SetPrec(x.Prec()) 47 z.SetPrec(x.Prec())
48 z.Quo(x, y) 48 z.Quo(x, y)
49 return z 49 return z
50 } 50 }
51 51
52 func BFtoFloat(f *big.Float) float64 { 52 func BFtoFloat(f *big.Float) float64 {
53 v, _ := f.Float64() 53 v, _ := f.Float64()
54 return v 54 return v
55 } 55 }
56 56
57 func Float64ToString(f float64) string { 57 func Float64ToString(f float64) string {
58 return fmt.Sprintf("%.2f", f) 58 return fmt.Sprintf("%.2f", f)
59 } 59 }
60
61 func Float64PtrToString(f *float64) string {
62 if f == nil {
63 return ""
64 }
65 return fmt.Sprintf("%.2f", *f)
66 }
60 67
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "crypto/rand"
5 "fmt" 4 "fmt"
5 "math/rand"
6 ) 6 )
7 7
8 func SeedGUID(seed int64) {
9 rand.Seed(seed)
10 }
11
8 // GUID ... 12 // GUID ...
9 func GUID() (string, error) { 13 func GUID() (string, error) {
10 b := make([]byte, 16) 14 b := make([]byte, 16)
11 _, err := rand.Read(b) 15 _, err := rand.Read(b)
12 if err != nil { 16 if err != nil {
13 return "", err 17 return "", err
14 } 18 }
15 id := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 19 id := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
16 return id, nil 20 return id, nil
17 } 21 }
22
23 func NewGUID() string {
24 id, _ := GUID()
25 return id
26 }
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "fmt" 5 "fmt"
6 "net/http" 6 "net/http"
7 ) 7 )
8 8
9 // StatusRecorder ... 9 // StatusRecorder ...
10 type StatusRecorder struct { 10 type StatusRecorder struct {
11 writer http.ResponseWriter 11 writer http.ResponseWriter
12 status int 12 status int
13 size int 13 size int
14 } 14 }
15 15
16 // NewStatusRecorder ... 16 // NewStatusRecorder ...
17 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder { 17 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder {
18 return &StatusRecorder{ 18 return &StatusRecorder{
19 writer: w, 19 writer: w,
20 status: 0, 20 status: 0,
21 size: 0, 21 size: 0,
22 } 22 }
23 } 23 }
24 24
25 // WriteHeader is a wrapper http.ResponseWriter interface 25 // WriteHeader is a wrapper http.ResponseWriter interface
26 func (r *StatusRecorder) WriteHeader(code int) { 26 func (r *StatusRecorder) WriteHeader(code int) {
27 r.status = code 27 r.status = code
28 r.writer.WriteHeader(code) 28 r.writer.WriteHeader(code)
29 } 29 }
30 30
31 // Write is a wrapper for http.ResponseWriter interface 31 // Write is a wrapper for http.ResponseWriter interface
32 func (r *StatusRecorder) Write(in []byte) (int, error) { 32 func (r *StatusRecorder) Write(in []byte) (int, error) {
33 r.size = len(in) 33 r.size = len(in)
34 return r.writer.Write(in) 34 return r.writer.Write(in)
35 } 35 }
36 36
37 // Header is a wrapper for http.ResponseWriter interface 37 // Header is a wrapper for http.ResponseWriter interface
38 func (r *StatusRecorder) Header() http.Header { 38 func (r *StatusRecorder) Header() http.Header {
39 return r.writer.Header() 39 return r.writer.Header()
40 } 40 }
41 41
42 // Status ... 42 // Status ...
43 func (r *StatusRecorder) Status() int { 43 func (r *StatusRecorder) Status() int {
44 return r.status 44 return r.status
45 } 45 }
46 46
47 // Size ... 47 // Size ...
48 func (r *StatusRecorder) Size() int { 48 func (r *StatusRecorder) Size() int {
49 return r.size 49 return r.size
50 } 50 }
51 51
52 // NotFoundHandlerFunc writes HTTP error 404 to w. 52 // NotFoundHandlerFunc writes HTTP error 404 to w.
53 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) { 53 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) {
54 SetAccessControlHeaders(w) 54 SetAccessControlHeaders(w)
55 SetContentType(w, "application/json") 55 SetContentType(w, "application/json")
56 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String())) 56 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String()))
57 } 57 }
58 58
59 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?) 59 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?)
60 func SetContentType(w http.ResponseWriter, ctype string) { 60 func SetContentType(w http.ResponseWriter, ctype string) {
61 w.Header().Set("Content-Type", ctype) 61 w.Header().Set("Content-Type", ctype)
62 } 62 }
63 63
64 // SetResponseStatus ... 64 // SetResponseStatus ...
65 func SetResponseStatus(w http.ResponseWriter, status int) { 65 func SetResponseStatus(w http.ResponseWriter, status int) {
66 w.WriteHeader(status) 66 w.WriteHeader(status)
67 } 67 }
68 68
69 // WriteResponse ... 69 // WriteResponse ...
70 func WriteResponse(w http.ResponseWriter, content []byte) { 70 func WriteResponse(w http.ResponseWriter, content []byte) {
71 w.Write(content) 71 w.Write(content)
72 } 72 }
73 73
74 // SetAccessControlHeaders set's default headers for an HTTP response. 74 // SetAccessControlHeaders set's default headers for an HTTP response.
75 func SetAccessControlHeaders(w http.ResponseWriter) { 75 func SetAccessControlHeaders(w http.ResponseWriter) {
76 w.Header().Set("Access-Control-Allow-Origin", "*") 76 w.Header().Set("Access-Control-Allow-Origin", "*")
77 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") 77 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
78 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") 78 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
79 } 79 }
80 80
81 // GetLocale ... 81 // GetLocale ...
82 func GetLocale(req *http.Request, dflt string) string { 82 func GetLocale(req *http.Request, dflt string) string {
83 loc := req.FormValue("locale") 83 loc := req.FormValue("locale")
84 if loc == "" { 84 if loc == "" {
85 return dflt 85 return dflt
86 } 86 }
87 return loc 87 return loc
88 } 88 }
89 89
90 // Success ... 90 // Success ...
91 func Success(w http.ResponseWriter, payload interface{}, code int) { 91 func Success(w http.ResponseWriter, payload interface{}, code int) {
92 w.WriteHeader(code) 92 w.WriteHeader(code)
93 if payload != nil { 93 if payload != nil {
94 json.NewEncoder(w).Encode(payload) 94 json.NewEncoder(w).Encode(payload)
95 } 95 }
96 } 96 }
97 97
98 // OK ... 98 // OK ...
99 func OK(w http.ResponseWriter, payload interface{}) { 99 func OK(w http.ResponseWriter, payload interface{}) {
100 SetContentType(w, "application/json") 100 SetContentType(w, "application/json")
101 Success(w, payload, http.StatusOK) 101 Success(w, payload, http.StatusOK)
102 } 102 }
103 103
104 // Created ... 104 // Created ...
105 func Created(w http.ResponseWriter, payload interface{}) { 105 func Created(w http.ResponseWriter, payload interface{}) {
106 SetContentType(w, "application/json") 106 SetContentType(w, "application/json")
107 Success(w, payload, http.StatusCreated) 107 Success(w, payload, http.StatusCreated)
108 } 108 }
109 109
110 type weberror struct { 110 type weberror struct {
111 Request string `json:"request"` 111 Request string `json:"request"`
112 Error string `json:"error"` 112 Error string `json:"error"`
113 //Code int64 `json:"code"` TODO 113 //Code int64 `json:"code"` TODO
114 } 114 }
115 115
116 // Error ... 116 // Error ...
117 func Error(w http.ResponseWriter, r *http.Request, code int, err string) { 117 func Error(w http.ResponseWriter, r *http.Request, code int, err string) {
118 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI} 118 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI}
119 w.WriteHeader(code) 119 w.WriteHeader(code)
120 json.NewEncoder(w).Encode(werr) 120 json.NewEncoder(w).Encode(werr)
121 } 121 }
122 122
123 // BadRequest ... 123 // BadRequest ...
124 func BadRequest(w http.ResponseWriter, r *http.Request, err string) { 124 func BadRequest(w http.ResponseWriter, r *http.Request, err string) {
125 SetContentType(w, "application/json") 125 SetContentType(w, "application/json")
126 Error(w, r, http.StatusBadRequest, err) 126 Error(w, r, http.StatusBadRequest, err)
127 } 127 }
128 128
129 // Unauthorized ... 129 // Unauthorized ...
130 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) { 130 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) {
131 SetContentType(w, "application/json") 131 SetContentType(w, "application/json")
132 Error(w, r, http.StatusUnauthorized, err) 132 Error(w, r, http.StatusUnauthorized, err)
133 } 133 }
134 134
135 // Forbidden ... 135 // Forbidden ...
136 func Forbidden(w http.ResponseWriter, r *http.Request, err string) { 136 func Forbidden(w http.ResponseWriter, r *http.Request, err string) {
137 SetContentType(w, "application/json") 137 SetContentType(w, "application/json")
138 Error(w, r, http.StatusForbidden, err) 138 Error(w, r, http.StatusForbidden, err)
139 } 139 }
140 140
141 // NotFound ... 141 // NotFound ...
142 func NotFound(w http.ResponseWriter, r *http.Request, err string) { 142 func NotFound(w http.ResponseWriter, r *http.Request, err string) {
143 SetContentType(w, "application/json") 143 SetContentType(w, "application/json")
144 Error(w, r, http.StatusNotFound, err) 144 Error(w, r, http.StatusNotFound, err)
145 } 145 }
146 146
147 // Conflict ... 147 // Conflict ...
148 func Conflict(w http.ResponseWriter, r *http.Request, err string) { 148 func Conflict(w http.ResponseWriter, r *http.Request, err string) {
149 SetContentType(w, "application/json") 149 SetContentType(w, "application/json")
150 Error(w, r, http.StatusConflict, err) 150 Error(w, r, http.StatusConflict, err)
151 } 151 }
152 152
153 // InternalServerError ... 153 // InternalServerError ...
154 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) { 154 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) {
155 SetContentType(w, "application/json") 155 SetContentType(w, "application/json")
156 Error(w, r, http.StatusInternalServerError, err) 156 Error(w, r, http.StatusInternalServerError, err)
157 } 157 }
158 158
159 func SetHeader(r *http.Request, key, val string) { 159 func SetHeader(r *http.Request, key, val string) {
160 r.Header.Set(key, val) 160 r.Header.Set(key, val)
161 } 161 }
162 162
163 func AddHeader(r *http.Request, key, val string) { 163 func AddHeader(r *http.Request, key, val string) {
164 r.Header.Add(key, val) 164 r.Header.Add(key, val)
165 } 165 }
166 166
167 func GetHeader(r *http.Request, key string) string { 167 func GetHeader(r *http.Request, key string) string {
168 return r.Header.Get(key) 168 return r.Header.Get(key)
169 } 169 }
170 170
171 func GetClientAgentUTCOffset(req *http.Request) int64 { 171 func ClientUTCOffset(req *http.Request) int64 {
172 return StringToInt64(GetHeader(req, "X-Timezone-Offset")) 172 return StringToInt64(GetHeader(req, "X-Timezone-Offset"))
173 } 173 }
174 174
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
5 ) 5 )
6 6
7 // ClampInt64 ... 7 // ClampInt64 ...
8 func ClampInt64(v, min, max int64) int64 { 8 func ClampInt64(v, min, max int64) int64 {
9 if v < min { 9 if v < min {
10 return min 10 return min
11 } else if v > max { 11 } else if v > max {
12 return max 12 return max
13 } 13 }
14 14
15 return v 15 return v
16 } 16 }
17 17
18 // InRangeInt64 ... 18 // InRangeInt64 ...
19 func InRangeInt64(v, min, max int64) bool { 19 func InRangeInt64(v, min, max int64) bool {
20 return (v >= min && v <= max) 20 return (v >= min && v <= max)
21 } 21 }
22 22
23 // Int64ToString ... 23 // Int64ToString ...
24 func Int64ToString(i int64) string { 24 func Int64ToString(i int64) string {
25 return fmt.Sprintf("%d", i) 25 return fmt.Sprintf("%d", i)
26 } 26 }
27 27
28 // Int64PtrToString ...
29 func Int64PtrToString(i *int64) string {
30 if i == nil {
31 return ""
32 }
33 return fmt.Sprintf("%d", *i)
34 }
35
28 // BoolToInt64 ... 36 // BoolToInt64 ...
29 func BoolToInt64(b bool) int64 { 37 func BoolToInt64(b bool) int64 {
30 if b { 38 if b {
31 return 1 39 return 1
32 } 40 }
33 return 0 41 return 0
34 } 42 }
35 43
36 // Int64ToBool ... 44 // Int64ToBool ...
37 func Int64ToBool(i int64) bool { 45 func Int64ToBool(i int64) bool {
38 return i != 0 46 return i != 0
39 } 47 }
40 48
41 func MaxInt(vars ...int) (max int) { 49 func MaxInt(vars ...int) (max int) {
50 max = vars[0]
42 for _, v := range vars { 51 for _, v := range vars {
43 if v > max { 52 if v > max {
44 max = v 53 max = v
45 } 54 }
46 } 55 }
47 return max 56 return max
48 } 57 }
58
59 func MinInt64(vars ...int64) (min int64) {
60 min = vars[0]
61 for _, v := range vars {
62 if v < min {
63 min = v
64 }
65 }
66 return min
67 }
49 68
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "database/sql" 4 "database/sql"
5 "database/sql/driver" 5 "database/sql/driver"
6 "encoding/json" 6 "encoding/json"
7 "fmt" 7 "fmt"
8 "time" 8 "time"
9 ) 9 )
10 10
11 // NullBool is a wrapper for sql.NullBool with added JSON (un)marshalling 11 // NullBool is a wrapper for sql.NullBool with added JSON (un)marshalling
12 type NullBool sql.NullBool 12 type NullBool sql.NullBool
13 13
14 // Scan ... 14 // Scan ...
15 func (nb *NullBool) Scan(value interface{}) error { 15 func (nb *NullBool) Scan(value interface{}) error {
16 var b sql.NullBool 16 var b sql.NullBool
17 if err := b.Scan(value); err != nil { 17 if err := b.Scan(value); err != nil {
18 nb.Bool, nb.Valid = false, false 18 nb.Bool, nb.Valid = false, false
19 return err 19 return err
20 } 20 }
21 nb.Bool, nb.Valid = b.Bool, b.Valid 21 nb.Bool, nb.Valid = b.Bool, b.Valid
22 return nil 22 return nil
23 } 23 }
24 24
25 // Value ... 25 // Value ...
26 func (nb *NullBool) Value() (driver.Value, error) { 26 func (nb *NullBool) Value() (driver.Value, error) {
27 if !nb.Valid { 27 if !nb.Valid {
28 return nil, nil 28 return nil, nil
29 } 29 }
30 return nb.Bool, nil 30 return nb.Bool, nil
31 } 31 }
32 32
33 // MarshalJSON ... 33 // MarshalJSON ...
34 func (nb NullBool) MarshalJSON() ([]byte, error) { 34 func (nb NullBool) MarshalJSON() ([]byte, error) {
35 if nb.Valid { 35 if nb.Valid {
36 return json.Marshal(nb.Bool) 36 return json.Marshal(nb.Bool)
37 } 37 }
38 38
39 return json.Marshal(nil) 39 return json.Marshal(nil)
40 } 40 }
41 41
42 // UnmarshalJSON ... 42 // UnmarshalJSON ...
43 func (nb *NullBool) UnmarshalJSON(b []byte) error { 43 func (nb *NullBool) UnmarshalJSON(b []byte) error {
44 var temp *bool 44 var temp *bool
45 if err := json.Unmarshal(b, &temp); err != nil { 45 if err := json.Unmarshal(b, &temp); err != nil {
46 return err 46 return err
47 } 47 }
48 if temp != nil { 48 if temp != nil {
49 nb.Valid = true 49 nb.Valid = true
50 nb.Bool = *temp 50 nb.Bool = *temp
51 } else { 51 } else {
52 nb.Valid = false 52 nb.Valid = false
53 } 53 }
54 return nil 54 return nil
55 } 55 }
56 56
57 // SQLCast ... 57 // SQLCast ...
58 func (nb *NullBool) SQLCast() sql.NullBool { 58 func (nb *NullBool) SQLCast() sql.NullBool {
59 return sql.NullBool(*nb) 59 return sql.NullBool(*nb)
60 } 60 }
61 61
62 // NullString is a wrapper for sql.NullString with added JSON (un)marshalling 62 // NullString is a wrapper for sql.NullString with added JSON (un)marshalling
63 type NullString sql.NullString 63 type NullString sql.NullString
64 64
65 // Scan ... 65 // Scan ...
66 func (ns *NullString) Scan(value interface{}) error { 66 func (ns *NullString) Scan(value interface{}) error {
67 var s sql.NullString 67 var s sql.NullString
68 if err := s.Scan(value); err != nil { 68 if err := s.Scan(value); err != nil {
69 ns.String, ns.Valid = "", false 69 ns.String, ns.Valid = "", false
70 return err 70 return err
71 } 71 }
72 ns.String, ns.Valid = s.String, s.Valid 72 ns.String, ns.Valid = s.String, s.Valid
73 return nil 73 return nil
74 } 74 }
75 75
76 // Value ... 76 // Value ...
77 func (ns *NullString) Value() (driver.Value, error) { 77 func (ns *NullString) Value() (driver.Value, error) {
78 if !ns.Valid { 78 if !ns.Valid {
79 return nil, nil 79 return nil, nil
80 } 80 }
81 return ns.String, nil 81 return ns.String, nil
82 } 82 }
83 83
84 // MarshalJSON ... 84 // MarshalJSON ...
85 func (ns NullString) MarshalJSON() ([]byte, error) { 85 func (ns NullString) MarshalJSON() ([]byte, error) {
86 if ns.Valid { 86 if ns.Valid {
87 return json.Marshal(ns.String) 87 return json.Marshal(ns.String)
88 } 88 }
89 return json.Marshal(nil) 89 return json.Marshal(nil)
90 } 90 }
91 91
92 // UnmarshalJSON ... 92 // UnmarshalJSON ...
93 func (ns *NullString) UnmarshalJSON(b []byte) error { 93 func (ns *NullString) UnmarshalJSON(b []byte) error {
94 var temp *string 94 var temp *string
95 if err := json.Unmarshal(b, &temp); err != nil { 95 if err := json.Unmarshal(b, &temp); err != nil {
96 return err 96 return err
97 } 97 }
98 if temp != nil { 98 if temp != nil {
99 ns.Valid = true 99 ns.Valid = true
100 ns.String = *temp 100 ns.String = *temp
101 } else { 101 } else {
102 ns.Valid = false 102 ns.Valid = false
103 } 103 }
104 return nil 104 return nil
105 } 105 }
106 106
107 // SQLCast ... 107 // SQLCast ...
108 func (ns *NullString) SQLCast() sql.NullString { 108 func (ns *NullString) SQLCast() sql.NullString {
109 return sql.NullString(*ns) 109 return sql.NullString(*ns)
110 } 110 }
111 111
112 // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling 112 // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling
113 type NullInt64 sql.NullInt64 113 type NullInt64 sql.NullInt64
114 114
115 // Scan ... 115 // Scan ...
116 func (ni *NullInt64) Scan(value interface{}) error { 116 func (ni *NullInt64) Scan(value interface{}) error {
117 var i sql.NullInt64 117 var i sql.NullInt64
118 if err := i.Scan(value); err != nil { 118 if err := i.Scan(value); err != nil {
119 ni.Int64, ni.Valid = 0, false 119 ni.Int64, ni.Valid = 0, false
120 return err 120 return err
121 } 121 }
122 ni.Int64, ni.Valid = i.Int64, i.Valid 122 ni.Int64, ni.Valid = i.Int64, i.Valid
123 return nil 123 return nil
124 } 124 }
125 125
126 // ScanPtr ... 126 // ScanPtr ...
127 func (ni *NullInt64) ScanPtr(v interface{}) error { 127 func (ni *NullInt64) ScanPtr(v interface{}) error {
128 if ip, ok := v.(*int64); ok && ip != nil { 128 if ip, ok := v.(*int64); ok && ip != nil {
129 return ni.Scan(*ip) 129 return ni.Scan(*ip)
130 } 130 }
131 return nil 131 return nil
132 } 132 }
133 133
134 // Value ... 134 // Value ...
135 func (ni *NullInt64) Value() (driver.Value, error) { 135 func (ni *NullInt64) Value() (driver.Value, error) {
136 if !ni.Valid { 136 if !ni.Valid {
137 return nil, nil 137 return nil, nil
138 } 138 }
139 return ni.Int64, nil 139 return ni.Int64, nil
140 } 140 }
141 141
142 func (ni *NullInt64) Val() int64 {
143 return ni.Int64
144 }
145
146 // Add
147 func (ni *NullInt64) Add(i NullInt64) {
148 ni.Valid = true
149 ni.Int64 += i.Int64
150 }
151
152 func (ni *NullInt64) Set(i int64) {
153 ni.Valid = true
154 ni.Int64 = i
155 }
156
142 // MarshalJSON ... 157 // MarshalJSON ...
143 func (ni NullInt64) MarshalJSON() ([]byte, error) { 158 func (ni NullInt64) MarshalJSON() ([]byte, error) {
144 if ni.Valid { 159 if ni.Valid {
145 return json.Marshal(ni.Int64) 160 return json.Marshal(ni.Int64)
146 } 161 }
147 return json.Marshal(nil) 162 return json.Marshal(nil)
148 } 163 }
149 164
150 // UnmarshalJSON ... 165 // UnmarshalJSON ...
151 func (ni *NullInt64) UnmarshalJSON(b []byte) error { 166 func (ni *NullInt64) UnmarshalJSON(b []byte) error {
152 var temp *int64 167 var temp *int64
153 if err := json.Unmarshal(b, &temp); err != nil { 168 if err := json.Unmarshal(b, &temp); err != nil {
154 return err 169 return err
155 } 170 }
156 if temp != nil { 171 if temp != nil {
157 ni.Valid = true 172 ni.Valid = true
158 ni.Int64 = *temp 173 ni.Int64 = *temp
159 } else { 174 } else {
160 ni.Valid = false 175 ni.Valid = false
161 } 176 }
162 return nil 177 return nil
163 } 178 }
164 179
165 // SQLCast ... 180 // SQLCast ...
166 func (ni *NullInt64) SQLCast() sql.NullInt64 { 181 func (ni *NullInt64) SQLCast() sql.NullInt64 {
167 return sql.NullInt64(*ni) 182 return sql.NullInt64(*ni)
168 } 183 }
169 184
170 // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling 185 // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling
171 type NullFloat64 sql.NullFloat64 186 type NullFloat64 sql.NullFloat64
172 187
173 // Scan ... 188 // Scan ...
174 func (nf *NullFloat64) Scan(value interface{}) error { 189 func (nf *NullFloat64) Scan(value interface{}) error {
175 var f sql.NullFloat64 190 var f sql.NullFloat64
176 if err := f.Scan(value); err != nil { 191 if err := f.Scan(value); err != nil {
177 nf.Float64, nf.Valid = 0.0, false 192 nf.Float64, nf.Valid = 0.0, false
178 return err 193 return err
179 } 194 }
180 nf.Float64, nf.Valid = f.Float64, f.Valid 195 nf.Float64, nf.Valid = f.Float64, f.Valid
181 return nil 196 return nil
182 } 197 }
183 198
184 // ScanPtr ... 199 // ScanPtr ...
185 func (nf *NullFloat64) ScanPtr(v interface{}) error { 200 func (nf *NullFloat64) ScanPtr(v interface{}) error {
186 if fp, ok := v.(*float64); ok && fp != nil { 201 if fp, ok := v.(*float64); ok && fp != nil {
187 return nf.Scan(*fp) 202 return nf.Scan(*fp)
188 } 203 }
189 return nil 204 return nil
190 } 205 }
191 206
192 // Value ... 207 // Value ...
193 func (nf *NullFloat64) Value() (driver.Value, error) { 208 func (nf *NullFloat64) Value() (driver.Value, error) {
194 if !nf.Valid { 209 if !nf.Valid {
195 return nil, nil 210 return nil, nil
196 } 211 }
197 return nf.Float64, nil 212 return nf.Float64, nil
198 } 213 }
199 214
215 // Val ...
216 func (nf *NullFloat64) Val() float64 {
217 return nf.Float64
218 }
219
220 // Add ...
221 func (nf *NullFloat64) Add(f NullFloat64) {
222 nf.Valid = true
223 nf.Float64 += f.Float64
224 }
225
226 func (nf *NullFloat64) Set(f float64) {
227 nf.Valid = true
228 nf.Float64 = f
229 }
230
200 // MarshalJSON ... 231 // MarshalJSON ...
201 func (nf NullFloat64) MarshalJSON() ([]byte, error) { 232 func (nf NullFloat64) MarshalJSON() ([]byte, error) {
202 if nf.Valid { 233 if nf.Valid {
203 return json.Marshal(nf.Float64) 234 return json.Marshal(nf.Float64)
204 } 235 }
205 return json.Marshal(nil) 236 return json.Marshal(nil)
206 } 237 }
207 238
208 // UnmarshalJSON ... 239 // UnmarshalJSON ...
209 func (nf *NullFloat64) UnmarshalJSON(b []byte) error { 240 func (nf *NullFloat64) UnmarshalJSON(b []byte) error {
210 var temp *float64 241 var temp *float64
211 if err := json.Unmarshal(b, &temp); err != nil { 242 if err := json.Unmarshal(b, &temp); err != nil {
212 return err 243 return err
213 } 244 }
214 if temp != nil { 245 if temp != nil {
215 nf.Valid = true 246 nf.Valid = true
216 nf.Float64 = *temp 247 nf.Float64 = *temp
217 } else { 248 } else {
218 nf.Valid = false 249 nf.Valid = false
219 } 250 }
220 return nil 251 return nil
221 } 252 }
222 253
223 // SQLCast ... 254 // SQLCast ...
224 func (nf *NullFloat64) SQLCast() sql.NullFloat64 { 255 func (nf *NullFloat64) SQLCast() sql.NullFloat64 {
225 return sql.NullFloat64(*nf) 256 return sql.NullFloat64(*nf)
226 } 257 }
227 258
228 // NullTime ... 259 // NullTime ...
229 type NullTime struct { 260 type NullTime struct {
230 Time time.Time 261 Time time.Time
231 Valid bool // Valid is true if Time is not NULL 262 Valid bool // Valid is true if Time is not NULL
232 } 263 }
233 264
234 // Scan ... 265 // Scan ...
235 func (nt *NullTime) Scan(value interface{}) (err error) { 266 func (nt *NullTime) Scan(value interface{}) (err error) {
236 if value == nil { 267 if value == nil {
237 nt.Time, nt.Valid = time.Time{}, false 268 nt.Time, nt.Valid = time.Time{}, false
238 return 269 return
239 } 270 }
240 271
241 switch v := value.(type) { 272 switch v := value.(type) {
242 case time.Time: 273 case time.Time:
243 nt.Time, nt.Valid = v, true 274 nt.Time, nt.Valid = v, true
244 return 275 return
245 case []byte: 276 case []byte:
246 nt.Time, err = parseDateTime(string(v), time.UTC) 277 nt.Time, err = parseDateTime(string(v), time.UTC)
247 nt.Valid = (err == nil) 278 nt.Valid = (err == nil)
248 return 279 return
249 case string: 280 case string:
250 nt.Time, err = parseDateTime(v, time.UTC) 281 nt.Time, err = parseDateTime(v, time.UTC)
251 nt.Valid = (err == nil) 282 nt.Valid = (err == nil)
252 return 283 return
253 } 284 }
254 285
255 nt.Valid = false 286 nt.Valid = false
256 return fmt.Errorf("Can't convert %T to time.Time", value) 287 return fmt.Errorf("Can't convert %T to time.Time", value)
257 } 288 }
258 289
259 // Value implements the driver Valuer interface. 290 // Value implements the driver Valuer interface.
260 func (nt NullTime) Value() (driver.Value, error) { 291 func (nt NullTime) Value() (driver.Value, error) {
261 if !nt.Valid { 292 if !nt.Valid {
262 return nil, nil 293 return nil, nil
263 } 294 }
264 return nt.Time, nil 295 return nt.Time, nil
265 } 296 }
266 297
267 // MarshalJSON ... 298 // MarshalJSON ...
268 func (nt NullTime) MarshalJSON() ([]byte, error) { 299 func (nt NullTime) MarshalJSON() ([]byte, error) {
269 if nt.Valid { 300 if nt.Valid {
270 format := nt.Time.Format("2006-01-02 15:04:05") 301 format := nt.Time.Format("2006-01-02 15:04:05")
271 return json.Marshal(format) 302 return json.Marshal(format)
272 } 303 }
273 return json.Marshal(nil) 304 return json.Marshal(nil)
274 } 305 }
275 306
276 // UnmarshalJSON ... 307 // UnmarshalJSON ...
277 func (nt *NullTime) UnmarshalJSON(b []byte) error { 308 func (nt *NullTime) UnmarshalJSON(b []byte) error {
278 var temp *time.Time 309 var temp *time.Time
279 var t1 time.Time 310 var t1 time.Time
280 var err error 311 var err error
281 312
282 s1 := string(b) 313 s1 := string(b)
283 s2 := s1[1 : len(s1)-1] 314 s2 := s1[1 : len(s1)-1]
284 if s1 == "null" { 315 if s1 == "null" {
285 temp = nil 316 temp = nil
286 } else { 317 } else {
287 t1, err = time.Parse("2006-01-02 15:04:05", s2) 318 t1, err = time.Parse("2006-01-02 15:04:05", s2)
288 if err != nil { 319 if err != nil {
289 return err 320 return err
290 } 321 }
291 temp = &t1 322 temp = &t1
292 } 323 }
293 324
294 if temp != nil { 325 if temp != nil {
295 nt.Valid = true 326 nt.Valid = true
296 nt.Time = *temp 327 nt.Time = *temp
297 } else { 328 } else {
298 nt.Valid = false 329 nt.Valid = false
299 } 330 }
300 return nil 331 return nil
301 } 332 }
302 333
303 func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { 334 func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
304 base := "0000-00-00 00:00:00.0000000" 335 base := "0000-00-00 00:00:00.0000000"
305 timeFormat := "2006-01-02 15:04:05.999999" 336 timeFormat := "2006-01-02 15:04:05.999999"
306 switch len(str) { 337 switch len(str) {
307 case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" 338 case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
308 if str == base[:len(str)] { 339 if str == base[:len(str)] {
309 return 340 return
310 } 341 }
311 t, err = time.Parse(timeFormat[:len(str)], str) 342 t, err = time.Parse(timeFormat[:len(str)], str)
312 default: 343 default:
313 err = fmt.Errorf("invalid time string: %s", str) 344 err = fmt.Errorf("invalid time string: %s", str)
314 return 345 return
315 } 346 }
316 347
317 // Adjust location 348 // Adjust location
318 if err == nil && loc != time.UTC { 349 if err == nil && loc != time.UTC {
319 y, mo, d := t.Date() 350 y, mo, d := t.Date()
320 h, mi, s := t.Clock() 351 h, mi, s := t.Clock()
321 t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil 352 t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
322 } 353 }
323 354
324 return 355 return
325 } 356 }
326 357
File was created 1 package pdfhelper
2
3 import (
4 "fmt"
5 "strings"
6
7 "git.to-net.rs/marko.tikvic/gofpdf"
8 )
9
10 // Block ...
11 const (
12 CENTER = "C"
13 LEFT = "L"
14 RIGHT = "R"
15 TOP = "T"
16 BOTTOM = "B"
17 FULL = "1"
18 NOBORDER = ""
19 )
20
21 const (
22 CONTINUE = 0
23 NEWLINE = 1
24 BELLOW = 2
25 )
26
27 const (
28 cyrillicEncoding = "cp1251"
29 latinEncoding = "cp1250"
30 )
31
32 // Helper ...
33 type Helper struct {
34 *gofpdf.Fpdf
35 translators map[string]func(string) string
36 }
37
38 // New ...
39 func New(ori, unit, size string) *Helper {
40 helper := &Helper{
41 Fpdf: gofpdf.New(ori, unit, size, ""),
42 }
43
44 return helper
45 }
46
47 func (pdf *Helper) LoadTranslators() {
48 pdf.translators = make(map[string]func(string) string)
49 pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding)
50 pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding)
51 }
52
53 // InsertTab ...
54 func (pdf *Helper) InsertTab(count int, w, h float64) {
55 for i := 0; i < count; i++ {
56 pdf.Cell(w, h, "")
57 }
58 }
59
60 // CellWithBox ...
61 func (pdf *Helper) CellWithBox(w, h float64, text, align string) {
62 //pdf.drawCellMargins(w, h)
63 //pdf.Cell(w, h, text)
64 pdf.CellFormat(w, h, pdf.ToUTF8(text), FULL, CONTINUE, align, false, 0, "")
65 }
66
67 func (pdf *Helper) CellWithMargins(w, h float64, text, align string, index, of int) {
68 len := of - 1
69 border := ""
70
71 // if top cell
72 if index == 0 {
73 border += "T"
74 }
75
76 border += "LR" // always draw these
77
78 // if bottom cell
79 if index == len {
80 border += "B"
81 }
82
83 pdf.CellFormat(w, h, pdf.ToUTF8(text), border, CONTINUE, align, false, 0, "")
84 }
85
86 // MultiRowCellWithBox ...
87 func (pdf *Helper) MultiRowCellWithBox(w, h float64, text []string, align string) {
88 pdf.drawCellMargins(w, h*float64(len(text)))
89 for i := range text {
90 pdf.CellFormat(w, h, pdf.ToUTF8(text[i]), "", BELLOW, align, false, 0, "")
91 }
92 }
93
94 // CellFormat(w, h, text, borders, newLine, fill)
95
96 type FormatedCell struct {
97 W, H float64
98 Text string
99 Font, FontStyle string
100 FontSize float64
101 Border string
102 Alignment string
103 }
104
105 func (pdf *Helper) Column(x, y float64, cells []FormatedCell) {
106 pdf.SetXY(x, y)
107 for _, c := range cells {
108 pdf.SetFont(c.Font, c.FontStyle, c.FontSize)
109 pdf.CellFormat(c.W, c.H, pdf.ToUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "")
110 //pdf.CellFormat(c.w, c.h, c.text, c.border, BELLOW, align, false, 0, "")
111 }
112 }
113
114 // NewLine ...
115 func (pdf *Helper) NewLine(h float64) {
116 pdf.Ln(h)
117 }
118
119 // WriteColumns ...
120 func (pdf *Helper) WriteColumns(align string, cols []PDFCell) {
121 for _, c := range cols {
122 pdf.CellFormat(c.width, c.height, pdf.ToUTF8(c.data), "", CONTINUE, align, false, 0, "")
123 }
124 }
125
126 func (pdf *Helper) Row(x, y float64, cells []FormatedCell) {
127 pdf.SetXY(x, y)
128 for _, c := range cells {
129 pdf.SetFont(c.Font, c.FontStyle, c.FontSize)
130 pdf.CellFormat(c.W, c.H, pdf.ToUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "")
131 }
132 }
133
134 const threeDots = "\u2056\u2056\u2056"
135
136 // WriteColumnsWithAlignment ...
137 func (pdf *Helper) WriteColumnsWithAlignment(cols []PDFCellAligned) {
138 for _, c := range cols {
139 lines := pdf.SplitText(c.data, c.width)
140 if len(lines) == 1 {
141 pdf.CellFormat(c.width, c.height, pdf.ToUTF8(lines[0]), "", CONTINUE, c.alignment, false, 0, "")
142 } else {
143 pdf.CellFormat(c.width, c.height, pdf.ToUTF8(lines[0]+threeDots), "", CONTINUE, c.alignment, false, 0, "")
144 }
145 }
146
147 }
148
149 func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string {
150 parts := pdf.Fpdf.SplitText(text, maxWidth)
151 if len(parts) > 1 {
152 return parts[0] + limiter
153 }
154
155 return text
156 }
157
158 // InsertImage ...
159 func (pdf *Helper) InsertImage(img string, x, y, w, h float64) {
160 imgType := ""
161 if parts := strings.Split(img, "."); len(parts) >= 2 {
162 imgType = parts[len(parts)-1]
163 }
164 opt := gofpdf.ImageOptions{
165 ImageType: imgType,
166 ReadDpi: false,
167 AllowNegativePosition: false,
168 }
169 autoBreak := false // if it's not false then you can't draw the image at an arbitrary height (y position)
170 pdf.ImageOptions(img, x, y, w, h, autoBreak, opt, 0, "")
171 }
172
173 // PDFCell ...
174 type PDFCell struct {
175 data string
176 height, width float64
177 }
178
179 // PDFCellAligned ...
180 type PDFCellAligned struct {
181 alignment string
182 data string
183 height, width float64
184 }
185
186 func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 {
187 family, _, _, _ = pdf.setCorrectFontFamily(txt)
188 return pdf.Fpdf.TextLength(txt, family, style, size)
189 }
190
191 // ToUTF8 ...
192 func (pdf *Helper) ToUTF8(s string) string {
193 encoding := latinEncoding
194 runes := []rune(s)
195 for _, r := range runes {
196 if uint64(r) >= 0x0402 && uint64(r) <= 0x044f {
197 encoding = cyrillicEncoding
198 break
199 }
200 }
201 pdf.setCorrectFontFamily(encoding)
202 translator, ok := pdf.translators[encoding]
203 if !ok {
204 return ""
205 }
206 return translator(s)
207 }
208
209 func (pdf *Helper) setCorrectFontFamily(enc string) (family, style string, ptSize, unitSize float64) {
210 family, style, ptSize, unitSize = pdf.GetFontInfo()
211 if enc == cyrillicEncoding {
212 if !strings.HasSuffix(family, "Cyrillic") {
213 family += "Cyrillic"
214 }
215 } else {
216 if strings.HasSuffix(family, "Cyrillic") {
217 family = strings.TrimSuffix(family, "Cyrillic")
218 }
219 }
220 pdf.SetFont(family, style, ptSize)
221 return family, style, ptSize, unitSize
222 }
223
224 func (pdf *Helper) PageHasSpace(requiredHeight float64) bool {
225 _, h := pdf.GetPageSize()
226 _, _, _, bot := pdf.GetMargins()
227 return (h - bot - pdf.GetY()) > requiredHeight
228 }
229
230 func (pdf *Helper) imageCenterOffset(w, h float64) (x, y float64) {
231 pageW, pageH := pdf.GetPageSize()
232 x = pageW/2.0 - w/2.0
233 y = pageH/2.0 - h/2.0
234 return x, y
235 }
236
237 // call before drawing the cell
238 func (pdf *Helper) drawCellMargins(cw, ch float64) {
239 x0, y0 := pdf.GetX(), pdf.GetY()
240 pdf.DrawBox(x0, y0, cw, ch)
241 }
242
243 // DrawBox ...
244 func (pdf Helper) DrawBox(x0, y0, w, h float64) {
245 pdf.Line(x0, y0, x0+w, y0)
246 pdf.Line(x0+w, y0, x0+w, y0+h)
247 pdf.Line(x0+w, y0+h, x0, y0+h)
248 pdf.Line(x0, y0+h, x0, y0)
249 }
250
251 // Strana %d/{TotalPages}
252 func (pdf *Helper) InsertPageNumber(x, y float64, format string) {
253 num := fmt.Sprintf(format, pdf.PageNo())
254 pdf.Column(x, y, []FormatedCell{{10, 1, num, "DejaVuSans", "", 8, NOBORDER, LEFT}})
255 }
256