123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // Copyright (c) 2015 Jelmer Snoeck (Gmail: jelmer.snoeck)
- //
- // Permission to use, copy, modify, and distribute this software for any purpose
- // with or without fee is hereby granted, provided that the above copyright notice
- // and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- // Package barcode provides helper methods for adding barcodes of different
- // types to your pdf document. It relies on the github.com/boombuler/barcode
- // package for the barcode creation.
- package barcode
- import (
- "bytes"
- "errors"
- "image/jpeg"
- "io"
- "strconv"
- "sync"
- "github.com/boombuler/barcode"
- "github.com/boombuler/barcode/aztec"
- "github.com/boombuler/barcode/codabar"
- "github.com/boombuler/barcode/code128"
- "github.com/boombuler/barcode/code39"
- "github.com/boombuler/barcode/datamatrix"
- "github.com/boombuler/barcode/ean"
- "github.com/boombuler/barcode/qr"
- "github.com/boombuler/barcode/twooffive"
- "github.com/jung-kurt/gofpdf"
- "github.com/ruudk/golang-pdf417"
- )
- // barcodes represents the barcodes that have been registered through this
- // package. They will later be used to be scaled and put on the page.
- // RubenN: made this a struct with a mutex to prevent race condition
- var barcodes struct {
- sync.Mutex
- cache map[string]barcode.Barcode
- }
- // barcodePdf is a partial PDF implementation that only implements a subset of
- // functions that are required to add the barcode to the PDF.
- type barcodePdf interface {
- GetConversionRatio() float64
- GetImageInfo(imageStr string) *gofpdf.ImageInfoType
- Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string)
- RegisterImageReader(imgName, tp string, r io.Reader) *gofpdf.ImageInfoType
- SetError(err error)
- }
- // printBarcode internally prints the scaled or unscaled barcode to the PDF. Used by both
- // Barcode() and BarcodeUnscalable().
- func printBarcode(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) {
- barcodes.Lock()
- unscaled, ok := barcodes.cache[code]
- barcodes.Unlock()
- if !ok {
- err := errors.New("Barcode not found")
- pdf.SetError(err)
- return
- }
- bname := uniqueBarcodeName(code, x, y)
- info := pdf.GetImageInfo(bname)
- scaleToWidth := unscaled.Bounds().Dx()
- scaleToHeight := unscaled.Bounds().Dy()
- if info == nil {
- bcode, err := barcode.Scale(
- unscaled,
- scaleToWidth,
- scaleToHeight,
- )
- if err != nil {
- pdf.SetError(err)
- return
- }
- err = registerScaledBarcode(pdf, bname, bcode)
- if err != nil {
- pdf.SetError(err)
- return
- }
- }
- scaleToWidthF := float64(scaleToWidth)
- scaleToHeightF := float64(scaleToHeight)
- if w != nil {
- scaleToWidthF = *w
- }
- if h != nil {
- scaleToHeightF = *h
- }
- pdf.Image(bname, x, y, scaleToWidthF, scaleToHeightF, flow, "jpg", 0, "")
- }
- // BarcodeUnscalable puts a registered barcode in the current page.
- //
- // Its arguments work in the same way as that of Barcode(). However, it allows for an unscaled
- // barcode in the width and/or height dimensions. This can be useful if you want to prevent
- // side effects of upscaling.
- func BarcodeUnscalable(pdf barcodePdf, code string, x, y float64, w, h *float64, flow bool) {
- printBarcode(pdf, code, x, y, w, h, flow)
- }
- // Barcode puts a registered barcode in the current page.
- //
- // The size should be specified in the units used to create the PDF document.
- // If width or height are left unspecfied, the barcode is not scaled in the unspecified dimensions.
- //
- // Positioning with x, y and flow is inherited from Fpdf.Image().
- func Barcode(pdf barcodePdf, code string, x, y, w, h float64, flow bool) {
- printBarcode(pdf, code, x, y, &w, &h, flow)
- }
- // GetUnscaledBarcodeDimensions returns the width and height of the
- // unscaled barcode associated with the given code.
- func GetUnscaledBarcodeDimensions(pdf barcodePdf, code string) (w, h float64) {
- barcodes.Lock()
- unscaled, ok := barcodes.cache[code]
- barcodes.Unlock()
- if !ok {
- err := errors.New("Barcode not found")
- pdf.SetError(err)
- return
- }
- return convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dx())),
- convertFrom96Dpi(pdf, float64(unscaled.Bounds().Dy()))
- }
- // Register registers a barcode but does not put it on the page. Use Barcode()
- // with the same code to put the barcode on the PDF page.
- func Register(bcode barcode.Barcode) string {
- barcodes.Lock()
- if len(barcodes.cache) == 0 {
- barcodes.cache = make(map[string]barcode.Barcode)
- }
- key := barcodeKey(bcode)
- barcodes.cache[key] = bcode
- barcodes.Unlock()
- return key
- }
- // RegisterAztec registers a barcode of type Aztec to the PDF, but not to
- // the page. Use Barcode() with the return value to put the barcode on the page.
- // code is the string to be encoded. minECCPercent is the error correction percentage. 33 is the default.
- // userSpecifiedLayers can be a value between -4 and 32 inclusive.
- func RegisterAztec(pdf barcodePdf, code string, minECCPercent int, userSpecifiedLayers int) string {
- bcode, err := aztec.Encode([]byte(code), minECCPercent, userSpecifiedLayers)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterCodabar registers a barcode of type Codabar to the PDF, but not to
- // the page. Use Barcode() with the return value to put the barcode on the page.
- func RegisterCodabar(pdf barcodePdf, code string) string {
- bcode, err := codabar.Encode(code)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterCode128 registers a barcode of type Code128 to the PDF, but not to
- // the page. Use Barcode() with the return value to put the barcode on the page.
- func RegisterCode128(pdf barcodePdf, code string) string {
- bcode, err := code128.Encode(code)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterCode39 registers a barcode of type Code39 to the PDF, but not to
- // the page. Use Barcode() with the return value to put the barcode on the page.
- //
- // includeChecksum and fullASCIIMode are inherited from code39.Encode().
- func RegisterCode39(pdf barcodePdf, code string, includeChecksum, fullASCIIMode bool) string {
- bcode, err := code39.Encode(code, includeChecksum, fullASCIIMode)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterDataMatrix registers a barcode of type DataMatrix to the PDF, but not
- // to the page. Use Barcode() with the return value to put the barcode on the
- // page.
- func RegisterDataMatrix(pdf barcodePdf, code string) string {
- bcode, err := datamatrix.Encode(code)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterPdf417 registers a barcode of type Pdf417 to the PDF, but not to the
- // page. code is the string to be encoded. columns specifies the number of
- // barcode columns; this should be a value between 1 and 30 inclusive.
- // securityLevel specifies an error correction level between zero and 8
- // inclusive. Barcodes for use with FedEx must set columns to 10 and
- // securityLevel to 5. Use Barcode() with the return value to put the barcode
- // on the page.
- func RegisterPdf417(pdf barcodePdf, code string, columns int, securityLevel int) string {
- bcode := pdf417.Encode(code, columns, securityLevel)
- return registerBarcode(pdf, bcode, nil)
- }
- // RegisterEAN registers a barcode of type EAN to the PDF, but not to the page.
- // It will automatically detect if the barcode is EAN8 or EAN13. Use Barcode()
- // with the return value to put the barcode on the page.
- func RegisterEAN(pdf barcodePdf, code string) string {
- bcode, err := ean.Encode(code)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterQR registers a barcode of type QR to the PDF, but not to the page.
- // Use Barcode() with the return value to put the barcode on the page.
- //
- // The ErrorCorrectionLevel and Encoding mode are inherited from qr.Encode().
- func RegisterQR(pdf barcodePdf, code string, ecl qr.ErrorCorrectionLevel, mode qr.Encoding) string {
- bcode, err := qr.Encode(code, ecl, mode)
- return registerBarcode(pdf, bcode, err)
- }
- // RegisterTwoOfFive registers a barcode of type TwoOfFive to the PDF, but not
- // to the page. Use Barcode() with the return value to put the barcode on the
- // page.
- //
- // The interleaved bool is inherited from twooffive.Encode().
- func RegisterTwoOfFive(pdf barcodePdf, code string, interleaved bool) string {
- bcode, err := twooffive.Encode(code, interleaved)
- return registerBarcode(pdf, bcode, err)
- }
- // registerBarcode registers a barcode internally using the Register() function.
- // In case of an error generating the barcode it will not be registered and will
- // set an error on the PDF. It will return a unique key for the barcode type and
- // content that can be used to put the barcode on the page.
- func registerBarcode(pdf barcodePdf, bcode barcode.Barcode, err error) string {
- if err != nil {
- pdf.SetError(err)
- return ""
- }
- return Register(bcode)
- }
- // uniqueBarcodeName makes sure every barcode has a unique name for its
- // dimensions. Scaling a barcode image results in quality loss, which could be
- // a problem for barcode readers.
- func uniqueBarcodeName(code string, x, y float64) string {
- xStr := strconv.FormatFloat(x, 'E', -1, 64)
- yStr := strconv.FormatFloat(y, 'E', -1, 64)
- return "barcode-" + code + "-" + xStr + yStr
- }
- // barcodeKey combines the code type and code value into a unique identifier for
- // a barcode type. This is so that we can store several barcodes with the same
- // code but different type in the barcodes map.
- func barcodeKey(bcode barcode.Barcode) string {
- return bcode.Metadata().CodeKind + bcode.Content()
- }
- // registerScaledBarcode registers a barcode with its exact dimensions to the
- // PDF but does not put it on the page. Use Fpdf.Image() with the same code to
- // add the barcode to the page.
- func registerScaledBarcode(pdf barcodePdf, code string, bcode barcode.Barcode) error {
- buf := new(bytes.Buffer)
- err := jpeg.Encode(buf, bcode, nil)
- if err != nil {
- return err
- }
- reader := bytes.NewReader(buf.Bytes())
- pdf.RegisterImageReader(code, "jpg", reader)
- return nil
- }
- // convertTo96DPI converts the given value, which is based on a 72 DPI value
- // like the rest of the PDF document, to a 96 DPI value that is required for
- // an Image.
- //
- // Doing this through the Fpdf.Image() function would mean that it uses a 72 DPI
- // value and stretches it to a 96 DPI value. This results in quality loss which
- // could be problematic for barcode scanners.
- func convertTo96Dpi(pdf barcodePdf, value float64) float64 {
- return value * pdf.GetConversionRatio() / 72 * 96
- }
- // convertFrom96Dpi converts the given value, which is based on a 96 DPI value
- // required for an Image, to a 72 DPI value like the rest of the PDF document.
- func convertFrom96Dpi(pdf barcodePdf, value float64) float64 {
- return value / pdf.GetConversionRatio() * 72 / 96
- }
|