1package main
2
3import (
4 "fmt"
5 "image"
6 "os"
7 "strconv"
8 "strings"
9
10 "github.com/signintech/gopdf"
11)
12
13const (
14 quantityColumnOffset = 360
15 rateColumnOffset = 405
16 amountColumnOffset = 480
17)
18
19const (
20 subtotalLabel = "Subtotal"
21 discountLabel = "Discount"
22 taxLabel = "Tax"
23 totalLabel = "Total"
24)
25
26func writeLogo(pdf *gopdf.GoPdf, logo string, from string) {
27 if logo != "" {
28 width, height := getImageDimension(logo)
29 scaledWidth := 100.0
30 scaledHeight := float64(height) * scaledWidth / float64(width)
31 _ = pdf.Image(logo, pdf.GetX(), pdf.GetY(), &gopdf.Rect{W: scaledWidth, H: scaledHeight})
32 pdf.Br(scaledHeight + 24)
33 }
34 pdf.SetTextColor(55, 55, 55)
35
36 formattedFrom := strings.ReplaceAll(from, `\n`, "\n")
37 fromLines := strings.Split(formattedFrom, "\n")
38
39 for i := 0; i < len(fromLines); i++ {
40 if i == 0 {
41 _ = pdf.SetFont("Inter", "", 12)
42 _ = pdf.Cell(nil, fromLines[i])
43 pdf.Br(18)
44 } else {
45 _ = pdf.SetFont("Inter", "", 10)
46 _ = pdf.Cell(nil, fromLines[i])
47 pdf.Br(15)
48 }
49 }
50 pdf.Br(21)
51 pdf.SetStrokeColor(225, 225, 225)
52 pdf.Line(pdf.GetX(), pdf.GetY(), 260, pdf.GetY())
53 pdf.Br(36)
54}
55
56func writeTitle(pdf *gopdf.GoPdf, title, id, date string) {
57 _ = pdf.SetFont("Inter-Bold", "", 24)
58 pdf.SetTextColor(0, 0, 0)
59 _ = pdf.Cell(nil, title)
60 pdf.Br(36)
61 _ = pdf.SetFont("Inter", "", 12)
62 pdf.SetTextColor(100, 100, 100)
63 _ = pdf.Cell(nil, "#")
64 _ = pdf.Cell(nil, id)
65 pdf.SetTextColor(150, 150, 150)
66 _ = pdf.Cell(nil, " · ")
67 pdf.SetTextColor(100, 100, 100)
68 _ = pdf.Cell(nil, date)
69 pdf.Br(48)
70}
71
72func writeDueDate(pdf *gopdf.GoPdf, due string) {
73 _ = pdf.SetFont("Inter", "", 9)
74 pdf.SetTextColor(75, 75, 75)
75 pdf.SetX(rateColumnOffset)
76 _ = pdf.Cell(nil, "Due Date")
77 pdf.SetTextColor(0, 0, 0)
78 _ = pdf.SetFontSize(11)
79 pdf.SetX(amountColumnOffset - 15)
80 _ = pdf.Cell(nil, due)
81 pdf.Br(12)
82}
83
84func writeBillTo(pdf *gopdf.GoPdf, to string) {
85 pdf.SetTextColor(75, 75, 75)
86 _ = pdf.SetFont("Inter", "", 9)
87 _ = pdf.Cell(nil, "BILL TO")
88 pdf.Br(18)
89 pdf.SetTextColor(75, 75, 75)
90
91 formattedTo := strings.ReplaceAll(to, `\n`, "\n")
92 toLines := strings.Split(formattedTo, "\n")
93
94 for i := 0; i < len(toLines); i++ {
95 if i == 0 {
96 _ = pdf.SetFont("Inter", "", 15)
97 _ = pdf.Cell(nil, toLines[i])
98 pdf.Br(20)
99 } else {
100 _ = pdf.SetFont("Inter", "", 10)
101 _ = pdf.Cell(nil, toLines[i])
102 pdf.Br(15)
103 }
104 }
105 pdf.Br(64)
106}
107
108func writeHeaderRow(pdf *gopdf.GoPdf, showLineItemDetails bool) {
109 _ = pdf.SetFont("Inter", "", 9)
110 pdf.SetTextColor(55, 55, 55)
111 _ = pdf.Cell(nil, "ITEM")
112 if showLineItemDetails {
113 pdf.SetX(quantityColumnOffset)
114 _ = pdf.Cell(nil, "QTY")
115 pdf.SetX(rateColumnOffset)
116 _ = pdf.Cell(nil, "RATE")
117 }
118 pdf.SetX(amountColumnOffset)
119 _ = pdf.Cell(nil, "AMOUNT")
120 pdf.Br(24)
121}
122
123func writeNotes(pdf *gopdf.GoPdf, notes string) {
124 pdf.SetY(600)
125
126 _ = pdf.SetFont("Inter", "", 9)
127 pdf.SetTextColor(55, 55, 55)
128 _ = pdf.Cell(nil, "NOTES")
129 pdf.Br(18)
130 _ = pdf.SetFont("Inter", "", 9)
131 pdf.SetTextColor(0, 0, 0)
132
133 formattedNotes := strings.ReplaceAll(notes, `\n`, "\n")
134 notesLines := strings.Split(formattedNotes, "\n")
135
136 for i := 0; i < len(notesLines); i++ {
137 _ = pdf.Cell(nil, notesLines[i])
138 pdf.Br(15)
139 }
140
141 pdf.Br(48)
142}
143func writeFooter(pdf *gopdf.GoPdf, id string) {
144 pdf.SetY(800)
145
146 _ = pdf.SetFont("Inter", "", 10)
147 pdf.SetTextColor(55, 55, 55)
148 _ = pdf.Cell(nil, id)
149 pdf.SetStrokeColor(225, 225, 225)
150 pdf.Line(pdf.GetX()+10, pdf.GetY()+6, 550, pdf.GetY()+6)
151 pdf.Br(48)
152}
153
154func writeRow(pdf *gopdf.GoPdf, item string, quantity int, rate float64) {
155 _ = pdf.SetFont("Inter", "", 11)
156 pdf.SetTextColor(0, 0, 0)
157
158 total := float64(quantity) * rate
159 amount := strconv.FormatFloat(total, 'f', 2, 64)
160
161 _ = pdf.Cell(nil, item)
162 if quantity != 1 {
163 pdf.SetX(quantityColumnOffset)
164 _ = pdf.Cell(nil, strconv.Itoa(quantity))
165 pdf.SetX(rateColumnOffset)
166 _ = pdf.Cell(nil, currencySymbols[file.Currency]+strconv.FormatFloat(rate, 'f', 2, 64))
167 }
168 pdf.SetX(amountColumnOffset)
169 _ = pdf.Cell(nil, currencySymbols[file.Currency]+amount)
170 pdf.Br(24)
171}
172
173func writeTotals(pdf *gopdf.GoPdf, subtotal float64, tax float64, discount float64) {
174 pdf.SetY(600)
175
176 writeTotal(pdf, subtotalLabel, subtotal)
177 if tax > 0 {
178 writeTotal(pdf, taxLabel, tax)
179 }
180 if discount > 0 {
181 writeTotal(pdf, discountLabel, discount)
182 }
183 writeTotal(pdf, totalLabel, subtotal+tax-discount)
184}
185
186func writeTotal(pdf *gopdf.GoPdf, label string, total float64) {
187 _ = pdf.SetFont("Inter", "", 9)
188 pdf.SetTextColor(75, 75, 75)
189 pdf.SetX(rateColumnOffset)
190 _ = pdf.Cell(nil, label)
191 pdf.SetTextColor(0, 0, 0)
192 _ = pdf.SetFontSize(12)
193 pdf.SetX(amountColumnOffset - 15)
194 if label == totalLabel {
195 _ = pdf.SetFont("Inter-Bold", "", 11.5)
196 }
197 _ = pdf.Cell(nil, currencySymbols[file.Currency]+strconv.FormatFloat(total, 'f', 2, 64))
198 pdf.Br(24)
199}
200
201func getImageDimension(imagePath string) (int, int) {
202 file, err := os.Open(imagePath)
203 if err != nil {
204 fmt.Fprintf(os.Stderr, "%v\n", err)
205 }
206 defer file.Close()
207
208 image, _, err := image.DecodeConfig(file)
209 if err != nil {
210 fmt.Fprintf(os.Stderr, "%s: %v\n", imagePath, err)
211 }
212 return image.Width, image.Height
213}