github.com/phpdave11/gofpdf@v1.4.2/contrib/barcode/barcode.go (about) 1 // Copyright (c) 2015 Jelmer Snoeck (Gmail: jelmer.snoeck) 2 // 3 // Permission to use, copy, modify, and distribute this software for any purpose 4 // with or without fee is hereby granted, provided that the above copyright notice 5 // and this permission notice appear in all copies. 6 // 7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 // PERFORMANCE OF THIS SOFTWARE. 14 15 // Package barcode provides helper methods for adding barcodes of different 16 // types to your pdf document. It relies on the github.com/boombuler/barcode 17 // package for the barcode creation. 18 package barcode 19 20 import ( 21 "bytes" 22 "errors" 23 "image/jpeg" 24 "io" 25 "strconv" 26 "sync" 27 28 "github.com/boombuler/barcode" 29 "github.com/boombuler/barcode/aztec" 30 "github.com/boombuler/barcode/codabar" 31 "github.com/boombuler/barcode/code128" 32 "github.com/boombuler/barcode/code39" 33 "github.com/boombuler/barcode/datamatrix" 34 "github.com/boombuler/barcode/ean" 35 "github.com/boombuler/barcode/qr" 36 "github.com/boombuler/barcode/twooffive" 37 "github.com/phpdave11/gofpdf" 38 "github.com/ruudk/golang-pdf417" 39 ) 40 41 // barcodes represents the barcodes that have been registered through this 42 // package. They will later be used to be scaled and put on the page. 43 // RubenN: made this a struct with a mutex to prevent race condition 44 var barcodes struct { 45 sync.Mutex 46 cache map[string]barcode.Barcode 47 } 48 49 // barcodePdf is a partial PDF implementation that only implements a subset of 50 // functions that are required to add the barcode to the PDF. 51 type barcodePdf interface { 52 GetConversionRatio() float64 53 GetImageInfo(imageStr string) *gofpdf.ImageInfoType 54 Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) 55 RegisterImageReader(imgName, tp string, r io.Reader) *gofpdf.ImageInfoType 56 SetError(err error) 57 } 58 59 // printBarcode internally prints the scaled or unscaled barcode to the PDF. Used by both 60 // Barcode() and BarcodeUnscalable(). 61 func printBarcode(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) { 62 barcodes.Lock() 63 unscaled, ok := barcodes.cache[code] 64 barcodes.Unlock() 65 66 if !ok { 67 err := errors.New("Barcode not found") 68 pdf.SetError(err) 69 return 70 } 71 72 bname := uniqueBarcodeName(code, x, y) 73 info := pdf.GetImageInfo(bname) 74 scaleToWidth := unscaled.Bounds().Dx() 75 scaleToHeight := unscaled.Bounds().Dy() 76 77 if info == nil { 78 bcode, err := barcode.Scale( 79 unscaled, 80 scaleToWidth, 81 scaleToHeight, 82 ) 83 84 if err != nil { 85 pdf.SetError(err) 86 return 87 } 88 89 err = registerScaledBarcode(pdf, bname, bcode) 90 if err != nil { 91 pdf.SetError(err) 92 return 93 } 94 } 95 96 scaleToWidthF := float64(scaleToWidth) 97 scaleToHeightF := float64(scaleToHeight) 98 99 if w != nil { 100 scaleToWidthF = *w 101 } 102 if h != nil { 103 scaleToHeightF = *h 104 } 105 106 pdf.Image(bname, x, y, scaleToWidthF, scaleToHeightF, flow, "jpg", 0, "") 107 108 } 109 110 // BarcodeUnscalable puts a registered barcode in the current page. 111 // 112 // Its arguments work in the same way as that of Barcode(). However, it allows for an unscaled 113 // barcode in the width and/or height dimensions. This can be useful if you want to prevent 114 // side effects of upscaling. 115 func BarcodeUnscalable(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) { 116 printBarcode(pdf, code, x, y, w, h, flow) 117 } 118 119 // Barcode puts a registered barcode in the current page. 120 // 121 // The size should be specified in the units used to create the PDF document. 122 // If width or height are left unspecfied, the barcode is not scaled in the unspecified dimensions. 123 // 124 // Positioning with x, y and flow is inherited from Fpdf.Image(). 125 func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) { 126 printBarcode(pdf, code, x, y, &w, &h, flow) 127 } 128 129 // GetUnscaledBarcodeDimensions returns the width and height of the 130 // unscaled barcode associated with the given code. 131 func GetUnscaledBarcodeDimensions(pdf barcodePdf, code string) (w, h float64) { 132 barcodes.Lock() 133 unscaled, ok := barcodes.cache[code] 134 barcodes.Unlock() 135 136 if !ok { 137 err := errors.New("Barcode not found") 138 pdf.SetError(err) 139 return 140 } 141 142 return convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dx())), 143 convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dy())) 144 } 145 146 // Register registers a barcode but does not put it on the page. Use Barcode() 147 // with the same code to put the barcode on the PDF page. 148 func Register(bcode barcode.Barcode) string { 149 barcodes.Lock() 150 if len(barcodes.cache) == 0 { 151 barcodes.cache = make(map[string]barcode.Barcode) 152 } 153 154 key := barcodeKey(bcode) 155 barcodes.cache[key] = bcode 156 barcodes.Unlock() 157 158 return key 159 } 160 161 // RegisterAztec registers a barcode of type Aztec to the PDF, but not to 162 // the page. Use Barcode() with the return value to put the barcode on the page. 163 // code is the string to be encoded. minECCPercent is the error correction percentage. 33 is the default. 164 // userSpecifiedLayers can be a value between -4 and 32 inclusive. 165 func RegisterAztec(pdf barcodePdf, code string, minECCPercent int, userSpecifiedLayers int) string { 166 bcode, err := aztec.Encode([]byte(code), minECCPercent, userSpecifiedLayers) 167 return registerBarcode(pdf, bcode, err) 168 } 169 170 // RegisterCodabar registers a barcode of type Codabar to the PDF, but not to 171 // the page. Use Barcode() with the return value to put the barcode on the page. 172 func RegisterCodabar(pdf barcodePdf, code string) string { 173 bcode, err := codabar.Encode(code) 174 return registerBarcode(pdf, bcode, err) 175 } 176 177 // RegisterCode128 registers a barcode of type Code128 to the PDF, but not to 178 // the page. Use Barcode() with the return value to put the barcode on the page. 179 func RegisterCode128(pdf barcodePdf, code string) string { 180 bcode, err := code128.Encode(code) 181 return registerBarcode(pdf, bcode, err) 182 } 183 184 // RegisterCode39 registers a barcode of type Code39 to the PDF, but not to 185 // the page. Use Barcode() with the return value to put the barcode on the page. 186 // 187 // includeChecksum and fullASCIIMode are inherited from code39.Encode(). 188 func RegisterCode39(pdf barcodePdf, code string, includeChecksum, fullASCIIMode bool) string { 189 bcode, err := code39.Encode(code, includeChecksum, fullASCIIMode) 190 return registerBarcode(pdf, bcode, err) 191 } 192 193 // RegisterDataMatrix registers a barcode of type DataMatrix to the PDF, but not 194 // to the page. Use Barcode() with the return value to put the barcode on the 195 // page. 196 func RegisterDataMatrix(pdf barcodePdf, code string) string { 197 bcode, err := datamatrix.Encode(code) 198 return registerBarcode(pdf, bcode, err) 199 } 200 201 // RegisterPdf417 registers a barcode of type Pdf417 to the PDF, but not to the 202 // page. code is the string to be encoded. columns specifies the number of 203 // barcode columns; this should be a value between 1 and 30 inclusive. 204 // securityLevel specifies an error correction level between zero and 8 205 // inclusive. Barcodes for use with FedEx must set columns to 10 and 206 // securityLevel to 5. Use Barcode() with the return value to put the barcode 207 // on the page. 208 func RegisterPdf417(pdf barcodePdf, code string, columns int, securityLevel int) string { 209 bcode := pdf417.Encode(code, columns, securityLevel) 210 return registerBarcode(pdf, bcode, nil) 211 } 212 213 // RegisterEAN registers a barcode of type EAN to the PDF, but not to the page. 214 // It will automatically detect if the barcode is EAN8 or EAN13. Use Barcode() 215 // with the return value to put the barcode on the page. 216 func RegisterEAN(pdf barcodePdf, code string) string { 217 bcode, err := ean.Encode(code) 218 return registerBarcode(pdf, bcode, err) 219 } 220 221 // RegisterQR registers a barcode of type QR to the PDF, but not to the page. 222 // Use Barcode() with the return value to put the barcode on the page. 223 // 224 // The ErrorCorrectionLevel and Encoding mode are inherited from qr.Encode(). 225 func RegisterQR(pdf barcodePdf, code string, ecl qr.ErrorCorrectionLevel, mode qr.Encoding) string { 226 bcode, err := qr.Encode(code, ecl, mode) 227 return registerBarcode(pdf, bcode, err) 228 } 229 230 // RegisterTwoOfFive registers a barcode of type TwoOfFive to the PDF, but not 231 // to the page. Use Barcode() with the return value to put the barcode on the 232 // page. 233 // 234 // The interleaved bool is inherited from twooffive.Encode(). 235 func RegisterTwoOfFive(pdf barcodePdf, code string, interleaved bool) string { 236 bcode, err := twooffive.Encode(code, interleaved) 237 return registerBarcode(pdf, bcode, err) 238 } 239 240 // registerBarcode registers a barcode internally using the Register() function. 241 // In case of an error generating the barcode it will not be registered and will 242 // set an error on the PDF. It will return a unique key for the barcode type and 243 // content that can be used to put the barcode on the page. 244 func registerBarcode(pdf barcodePdf, bcode barcode.Barcode, err error) string { 245 if err != nil { 246 pdf.SetError(err) 247 return "" 248 } 249 250 return Register(bcode) 251 } 252 253 // uniqueBarcodeName makes sure every barcode has a unique name for its 254 // dimensions. Scaling a barcode image results in quality loss, which could be 255 // a problem for barcode readers. 256 func uniqueBarcodeName(code string, x, y float64) string { 257 xStr := strconv.FormatFloat(x, 'E', -1, 64) 258 yStr := strconv.FormatFloat(y, 'E', -1, 64) 259 260 return "barcode-" + code + "-" + xStr + yStr 261 } 262 263 // barcodeKey combines the code type and code value into a unique identifier for 264 // a barcode type. This is so that we can store several barcodes with the same 265 // code but different type in the barcodes map. 266 func barcodeKey(bcode barcode.Barcode) string { 267 return bcode.Metadata().CodeKind + bcode.Content() 268 } 269 270 // registerScaledBarcode registers a barcode with its exact dimensions to the 271 // PDF but does not put it on the page. Use Fpdf.Image() with the same code to 272 // add the barcode to the page. 273 func registerScaledBarcode(pdf barcodePdf, code string, bcode barcode.Barcode) error { 274 buf := new(bytes.Buffer) 275 err := jpeg.Encode(buf, bcode, nil) 276 277 if err != nil { 278 return err 279 } 280 281 reader := bytes.NewReader(buf.Bytes()) 282 pdf.RegisterImageReader(code, "jpg", reader) 283 284 return nil 285 } 286 287 // convertTo96DPI converts the given value, which is based on a 72 DPI value 288 // like the rest of the PDF document, to a 96 DPI value that is required for 289 // an Image. 290 // 291 // Doing this through the Fpdf.Image() function would mean that it uses a 72 DPI 292 // value and stretches it to a 96 DPI value. This results in quality loss which 293 // could be problematic for barcode scanners. 294 func convertTo96Dpi(pdf barcodePdf, value float64) float64 { 295 return value * pdf.GetConversionRatio() / 72 * 96 296 } 297 298 // convertFrom96Dpi converts the given value, which is based on a 96 DPI value 299 // required for an Image, to a 72 DPI value like the rest of the PDF document. 300 func convertFrom96Dpi(pdf barcodePdf, value float64) float64 { 301 return value / pdf.GetConversionRatio() * 72 / 96 302 }