Commit 1b51eed04dcd7aaa90727f3586530ec57f1d2843
1 parent
b80ee4b2bc
Exists in
master
pdf helper
Showing
7 changed files
with
352 additions
and
2 deletions
Show diff stats
date_util.go
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 |
float_util.go
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 |
guid.go
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 | } |
http.go
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 |
int_util.go
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 |
nullables.go
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 |
pdfhelper/pdf.go
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 |