Compare View
Commits (2)
Showing
2 changed files
Show diff stats
document/document.go
File was created | 1 | package document | |
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "mime" | ||
8 | "net/http" | ||
9 | "os" | ||
10 | "strings" | ||
11 | "time" | ||
12 | |||
13 | web "git.to-net.rs/marko.tikvic/webutility" | ||
14 | ) | ||
15 | |||
16 | // Document ... | ||
17 | type Document struct { | ||
18 | ID int64 `json:"id"` | ||
19 | FileName string `json:"fileName"` | ||
20 | Extension string `json:"extension"` | ||
21 | ContentType string `json:"contentType"` | ||
22 | Size int64 `json:"fileSize"` | ||
23 | UploadedBy string `json:"uploadedBy"` | ||
24 | LastModifiedBy string `json:"lastModifiedBy"` | ||
25 | TimeUploaded int64 `json:"timeUploaded"` | ||
26 | TimeLastModified int64 `json:"timeLastModified"` | ||
27 | RoleAccessLevel int64 `json:"accessLevel"` | ||
28 | Description string `json:"description"` | ||
29 | Download *DownloadLink `json:"download"` | ||
30 | Path string `json:"-"` | ||
31 | directory string | ||
32 | data []byte | ||
33 | } | ||
34 | |||
35 | // OpenFileAsDocument ... | ||
36 | func OpenFileAsDocument(path string) (*Document, error) { | ||
37 | d := &Document{Path: path} | ||
38 | |||
39 | f, err := os.Open(d.Path) | ||
40 | if err != nil { | ||
41 | return nil, err | ||
42 | } | ||
43 | defer f.Close() | ||
44 | |||
45 | stats, err := f.Stat() | ||
46 | if err != nil { | ||
47 | return nil, err | ||
48 | } | ||
49 | |||
50 | d.FileName = stats.Name() | ||
51 | d.Size = stats.Size() | ||
52 | d.Extension = FileExtension(d.FileName) | ||
53 | |||
54 | d.data = make([]byte, d.Size) | ||
55 | if _, err = f.Read(d.data); err != nil { | ||
56 | return nil, err | ||
57 | } | ||
58 | |||
59 | return d, err | ||
60 | } | ||
61 | |||
62 | // DownloadLink ... | ||
63 | type DownloadLink struct { | ||
64 | Method string `json:"method"` | ||
65 | URL string `json:"url"` | ||
66 | } | ||
67 | |||
68 | // SetDownloadInfo ... | ||
69 | func (d *Document) SetDownloadInfo(method, url string) { | ||
70 | d.Download = &DownloadLink{ | ||
71 | Method: method, | ||
72 | URL: url, | ||
73 | } | ||
74 | } | ||
75 | |||
76 | // ServeDocument writes d's buffer to w and sets appropriate headers according to d's content type | ||
77 | // and downloadPrompt. | ||
78 | func ServeDocument(w http.ResponseWriter, d *Document, downloadPrompt bool) error { | ||
79 | f, err := os.Open(d.Path) | ||
80 | if err != nil { | ||
81 | return err | ||
82 | } | ||
83 | defer f.Close() | ||
84 | |||
85 | web.SetContentType(w, mime.TypeByExtension(d.Extension)) | ||
86 | web.SetResponseStatus(w, http.StatusOK) | ||
87 | if downloadPrompt { | ||
88 | w.Header().Set("Content-Disposition", "attachment; filename="+d.FileName) | ||
89 | } | ||
90 | |||
91 | buf := make([]byte, d.Size) | ||
92 | if _, err := f.Read(buf); err != nil { | ||
93 | return err | ||
94 | } | ||
95 | |||
96 | w.Header().Set("Content-Length", fmt.Sprintf("%d", d.Size)) | ||
97 | web.WriteResponse(w, buf) | ||
98 | |||
99 | return nil | ||
100 | } | ||
101 | |||
102 | // FileExists ... | ||
103 | func FileExists(path string) bool { | ||
104 | temp, err := os.Open(path) | ||
105 | defer temp.Close() | ||
106 | |||
107 | if err != nil { | ||
108 | return false | ||
109 | } | ||
110 | |||
111 | return true | ||
112 | } | ||
113 | |||
114 | // ParseDocument ... | ||
115 | func ParseDocument(req *http.Request) (doc *Document, err error) { | ||
116 | req.ParseMultipartForm(32 << 20) | ||
117 | file, fheader, err := req.FormFile("document") | ||
118 | if err != nil { | ||
119 | return doc, err | ||
120 | } | ||
121 | |||
122 | claims, _ := web.GetTokenClaims(req) | ||
123 | owner := claims.Username | ||
124 | |||
125 | fname := fheader.Filename | ||
126 | |||
127 | fsize := fheader.Size | ||
128 | ftype := fmt.Sprintf("%v", fheader.Header["Content-Type"][0]) | ||
129 | |||
130 | fextn := FileExtension(fname) | ||
131 | if fextn == "" { | ||
132 | return doc, errors.New("invalid extension") | ||
133 | } | ||
134 | |||
135 | doc = new(Document) | ||
136 | |||
137 | doc.FileName = fname | ||
138 | doc.Size = fsize | ||
139 | doc.ContentType = ftype | ||
140 | doc.Extension = "." + fextn | ||
141 | |||
142 | t := time.Now().Unix() | ||
143 | doc.TimeUploaded = t | ||
144 | doc.TimeLastModified = t | ||
145 | |||
146 | doc.UploadedBy = owner | ||
147 | doc.LastModifiedBy = owner | ||
148 | doc.RoleAccessLevel = 0 | ||
149 | |||
150 | doc.data = make([]byte, doc.Size) | ||
151 | if _, err = io.ReadFull(file, doc.data); err != nil { | ||
152 | return doc, err | ||
153 | } | ||
154 | |||
155 | return doc, nil | ||
156 | } | ||
157 | |||
158 | // DirectoryFromPath ... | ||
159 | func DirectoryFromPath(path string) (dir string) { | ||
160 | parts := strings.Split(path, "/") | ||
161 | if len(parts) == 1 { | ||
162 | return "" | ||
163 | } | ||
164 | |||
165 | dir = parts[0] | ||
166 | for _, p := range parts[1 : len(parts)-1] { | ||
167 | dir += "/" + p | ||
168 | } | ||
169 | |||
170 | return dir | ||
171 | } | ||
172 | |||
173 | // SaveToFile ... | ||
174 | func (d *Document) SaveToFile(path string) (f *os.File, err error) { | ||
175 | d.Path = path | ||
176 | |||
177 | if FileExists(path) { | ||
178 | err = fmt.Errorf("file %s alredy exists", path) | ||
179 | return nil, err | ||
180 | } | ||
181 | |||
182 | if parentDir := DirectoryFromPath(path); parentDir != "" { | ||
183 | if err = os.MkdirAll(parentDir, os.ModePerm); err != nil { | ||
184 | if !os.IsExist(err) { | ||
185 | return nil, err | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | if f, err = os.Create(path); err != nil { | ||
191 | return nil, err | ||
192 | } | ||
193 | |||
194 | if _, err = f.Write(d.data); err != nil { | ||
195 | f.Close() | ||
196 | d.DeleteFile() | ||
197 | return nil, err | ||
198 | } | ||
199 | f.Close() | ||
200 | |||
201 | return f, nil | ||
202 | } | ||
203 | |||
204 | func DeleteFile(path string) error { | ||
205 | return os.Remove(path) | ||
206 | } | ||
207 | |||
208 | // DeleteFile ... | ||
209 | func (d *Document) DeleteFile() error { | ||
210 | return os.Remove(d.Path) | ||
211 | } | ||
212 | |||
213 | func FileExtension(path string) string { | ||
214 | parts := strings.Split(path, ".") // because name can contain dots | ||
215 | if len(parts) < 2 { | ||
216 | return "" | ||
217 | } | ||
218 | return "." + parts[len(parts)-1] | ||
219 | } | ||
220 |
pdfhelper/pdf.go
1 | package pdfhelper | 1 | package pdfhelper |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strings" | 5 | "strings" |
6 | 6 | ||
7 | "git.to-net.rs/marko.tikvic/gofpdf" | 7 | "git.to-net.rs/marko.tikvic/gofpdf" |
8 | ) | 8 | ) |
9 | 9 | ||
10 | // Block ... | 10 | // Block ... |
11 | const ( | 11 | const ( |
12 | CENTER = "C" | 12 | CENTER = "C" |
13 | LEFT = "L" | 13 | LEFT = "L" |
14 | RIGHT = "R" | 14 | RIGHT = "R" |
15 | TOP = "T" | 15 | TOP = "T" |
16 | BOTTOM = "B" | 16 | BOTTOM = "B" |
17 | FULL = "1" | 17 | FULL = "1" |
18 | NOBORDER = "" | 18 | NOBORDER = "" |
19 | ) | 19 | ) |
20 | 20 | ||
21 | const ( | 21 | const ( |
22 | CONTINUE = 0 | 22 | CONTINUE = 0 |
23 | NEWLINE = 1 | 23 | NEWLINE = 1 |
24 | BELLOW = 2 | 24 | BELLOW = 2 |
25 | ) | 25 | ) |
26 | 26 | ||
27 | const ( | 27 | const ( |
28 | cyrillicEncoding = "cp1251" | 28 | cyrillicEncoding = "cp1251" |
29 | latinEncoding = "cp1250" | 29 | latinEncoding = "cp1250" |
30 | ) | 30 | ) |
31 | 31 | ||
32 | const threeDots = "\u2056\u2056\u2056" | ||
33 | |||
34 | type TableCell struct { | ||
35 | W, H float64 | ||
36 | Text string | ||
37 | Font, FontStyle string | ||
38 | FontSize float64 | ||
39 | Border string | ||
40 | Alignment string | ||
41 | } | ||
42 | |||
32 | const threeDots = "\u2056\u2056\u2056" | 43 | // Helper ... |
33 | 44 | type Helper struct { | |
34 | type TableCell struct { | 45 | *gofpdf.Fpdf |
35 | W, H float64 | 46 | translators map[string]func(string) string |
36 | Text string | 47 | } |
37 | Font, FontStyle string | 48 | |
38 | FontSize float64 | 49 | // New ... |
39 | Border string | 50 | func New(ori, unit, size string) *Helper { |
40 | Alignment string | 51 | helper := &Helper{ |
41 | } | 52 | Fpdf: gofpdf.New(ori, unit, size, ""), |
42 | 53 | } | |
43 | // Helper ... | 54 | |
44 | type Helper struct { | 55 | return helper |
45 | *gofpdf.Fpdf | 56 | } |
46 | translators map[string]func(string) string | 57 | |
47 | } | 58 | func (pdf *Helper) LoadTranslators() { |
48 | 59 | pdf.translators = make(map[string]func(string) string) | |
49 | // New ... | 60 | pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding) |
50 | func New(ori, unit, size string) *Helper { | 61 | pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding) |
51 | helper := &Helper{ | 62 | } |
52 | Fpdf: gofpdf.New(ori, unit, size, ""), | 63 | |
53 | } | 64 | func (pdf *Helper) DrawCell(x, y float64, c TableCell) { |
54 | |||
55 | return helper | ||
56 | } | ||
57 | |||
58 | func (pdf *Helper) LoadTranslators() { | ||
59 | pdf.translators = make(map[string]func(string) string) | ||
60 | pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding) | ||
61 | pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding) | ||
62 | } | ||
63 | |||
64 | func (pdf *Helper) DrawCell(x, y float64, c TableCell) { | ||
65 | pdf.SetXY(x, y) | ||
66 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | ||
67 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") | ||
68 | } | ||
69 | |||
70 | func (pdf *Helper) DrawColumn(x, y float64, cells []TableCell) { | ||
71 | pdf.SetXY(x, y) | ||
72 | for _, c := range cells { | ||
73 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | ||
74 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") | ||
75 | } | ||
76 | } | ||
77 | |||
78 | func (pdf *Helper) DrawRow(x, y float64, cells []TableCell) { | ||
79 | pdf.SetXY(x, y) | ||
80 | for _, c := range cells { | ||
81 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | ||
82 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "") | ||
83 | } | ||
84 | } | ||
85 | |||
86 | func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 { | ||
87 | family, _, _, _ = pdf.setCorrectFontFamily(textEncoding(txt)) | ||
88 | return pdf.Fpdf.TextLength(txt, family, style, size) | ||
89 | } | ||
90 | |||
91 | func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string { | ||
92 | parts := pdf.Fpdf.SplitText(text, maxWidth) | ||
93 | if len(parts) > 1 { | ||
94 | return parts[0] + limiter | ||
95 | } | ||
96 | |||
97 | return text | ||
98 | } | ||
99 | |||
100 | // InsertImage ... | ||
101 | func (pdf *Helper) InsertImage(img string, x, y, w, h float64) { | ||
102 | imgType := "" | ||
103 | if parts := strings.Split(img, "."); len(parts) >= 2 { | ||
104 | imgType = parts[len(parts)-1] | ||
105 | } | ||
106 | opt := gofpdf.ImageOptions{ | 65 | pdf.SetXY(x, y) |
107 | ImageType: imgType, | 66 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) |
108 | ReadDpi: false, | 67 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") |
109 | AllowNegativePosition: false, | 68 | } |
110 | } | 69 | |
111 | autoBreak := false // if it's not false then you can't draw the image at an arbitrary height (y position) | 70 | func (pdf *Helper) DrawColumn(x, y float64, cells []TableCell) { |
112 | pdf.ImageOptions(img, x, y, w, h, autoBreak, opt, 0, "") | 71 | pdf.SetXY(x, y) |
113 | } | 72 | for _, c := range cells { |
114 | 73 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | |
115 | func (pdf *Helper) PageHasSpace(requiredHeight float64) bool { | 74 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") |
116 | _, h := pdf.GetPageSize() | ||
117 | _, _, _, bot := pdf.GetMargins() | 75 | } |
118 | return (h - bot - pdf.GetY()) > requiredHeight | 76 | } |
119 | } | 77 | |
120 | 78 | func (pdf *Helper) DrawRow(x, y float64, cells []TableCell) { | |
121 | // DrawBox ... | ||
122 | func (pdf Helper) DrawBox(x0, y0, w, h float64) { | ||
123 | pdf.Line(x0, y0, x0+w, y0) | ||
124 | pdf.Line(x0+w, y0, x0+w, y0+h) | ||
125 | pdf.Line(x0+w, y0+h, x0, y0+h) | ||
126 | pdf.Line(x0, y0+h, x0, y0) | ||
127 | } | ||
128 | |||
129 | // Strana %d/{TotalPages} | ||
130 | func (pdf *Helper) InsertPageNumber(x, y float64, format string) { | ||
131 | num := fmt.Sprintf(format, pdf.PageNo()) | ||
132 | pdf.DrawColumn(x, y, []TableCell{{10, 1, num, "DejaVuSans", "", 8, NOBORDER, LEFT}}) | ||
133 | } | 79 | pdf.SetXY(x, y) |
134 | 80 | for _, c := range cells { | |
135 | func (pdf *Helper) SuperscriptText(x, y, cw, ch float64, text, script string) { | 81 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) |
136 | family, style, size, sizeU := pdf.setCorrectFontFamily(textEncoding(text)) | 82 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "") |
137 | 83 | } | |
138 | pdf.DrawCell(x, y, TableCell{cw, ch, text, family, style, size, NOBORDER, LEFT}) | 84 | } |
139 | 85 | ||
140 | sx := x + pdf.TextLength(text, family, style, size) | 86 | func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 { |
141 | sy := y - sizeU*0.2 | 87 | family, _, _, _ = pdf.setCorrectFontFamily(textEncoding(txt)) |
142 | pdf.DrawCell(sx, sy, TableCell{cw, ch, script, family, style, size - 2, NOBORDER, LEFT}) | 88 | return pdf.Fpdf.TextLength(txt, family, style, size) |
143 | } | ||
144 | |||
145 | // toUTF8 ... | ||
146 | func (pdf *Helper) toUTF8(s string) string { | ||
147 | encoding := textEncoding(s) | ||
148 | pdf.setCorrectFontFamily(encoding) | ||
149 | translator, ok := pdf.translators[encoding] | ||
150 | if !ok { | ||
151 | return "" | ||
152 | } | ||
153 | return translator(s) | 89 | } |
154 | } | 90 | |
155 | 91 | func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string { | |
156 | func textEncoding(s string) string { | 92 | parts := pdf.Fpdf.SplitText(text, maxWidth) |
157 | encoding := latinEncoding | 93 | if len(parts) > 1 { |
158 | runes := []rune(s) | 94 | return parts[0] + limiter |
159 | for _, r := range runes { | 95 | } |
160 | if uint64(r) >= 0x0402 && uint64(r) <= 0x044f { | 96 | |
161 | encoding = cyrillicEncoding | 97 | return text |
162 | break | 98 | } |
163 | } | 99 | |
164 | } | 100 | // InsertImage ... |
165 | return encoding | 101 | func (pdf *Helper) InsertImage(img string, x, y, w, h float64) { |
166 | } | 102 | imgType := "" |
167 | 103 | if parts := strings.Split(img, "."); len(parts) >= 2 { | |
168 | func (pdf *Helper) setCorrectFontFamily(enc string) (family, style string, ptSize, unitSize float64) { | 104 | imgType = parts[len(parts)-1] |
169 | family, style, ptSize, unitSize = pdf.GetFontInfo() | 105 | } |
170 | if enc == cyrillicEncoding { | 106 | opt := gofpdf.ImageOptions{ |
171 | if !strings.HasSuffix(family, "cyrillic") { | 107 | ImageType: imgType, |