From bb0b7372a6e097a5a0efcbb7ca6cf6177813989e Mon Sep 17 00:00:00 2001 From: Jelmer Snoeck Date: Sat, 29 Aug 2015 14:41:39 +0100 Subject: Add Barcode contribution package. This package adds the ability to easily add a barcode to the PDF. This barcode is generated with the github.com/boombuler/barcode package. It adds wrappers for all the available barcode types through the `Register()` and `Register()` methods. These register methods put the barcode on the PDF, but not on the page. They will return a unique key that should be used later on with the `Barcode()` method scale the barcode and put it on the page. --- contrib/barcode/barcode.go | 204 ++++++++++++++++++++++++++++++++++++++++ contrib/barcode/barcode_test.go | 126 +++++++++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 contrib/barcode/barcode.go create mode 100644 contrib/barcode/barcode_test.go (limited to 'contrib') diff --git a/contrib/barcode/barcode.go b/contrib/barcode/barcode.go new file mode 100644 index 0000000..e49ac53 --- /dev/null +++ b/contrib/barcode/barcode.go @@ -0,0 +1,204 @@ +// 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" + "github.com/boombuler/barcode" + "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" + "image/jpeg" + "strconv" +) + +// barcodes represents the barcodes that have been registered through this +// package. They will later be used to be scaled and put on the page. +var barcodes map[string]barcode.Barcode + +// Barcode puts a registered barcode in the current page. +// +// The size should be specified in the units used to create the PDF document. +// +// Positioning with x, y and flow is inherited from Fpdf.Image(). +func Barcode(pdf *gofpdf.Fpdf, code string, x, y, w, h float64, flow bool) { + unscaled, ok := barcodes[code] + + if !ok { + err := errors.New("Barcode not found") + pdf.SetError(err) + return + } + + bname := uniqueBarcodeName(code, x, y) + info := pdf.GetImageInfo(bname) + + if info == nil { + bcode, err := barcode.Scale( + unscaled, + int(convertTo96Dpi(pdf, w)), + int(convertTo96Dpi(pdf, h)), + ) + + if err != nil { + pdf.SetError(err) + return + } + + err = registerScaledBarcode(pdf, bname, bcode) + if err != nil { + pdf.SetError(err) + return + } + } + + pdf.Image(bname, x, y, 0, 0, flow, "jpg", 0, "") +} + +// 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 { + if len(barcodes) == 0 { + barcodes = make(map[string]barcode.Barcode) + } + + key := barcodeKey(bcode) + barcodes[key] = bcode + return key +} + +// 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, code string) string { + bcode, err := datamatrix.Encode(code) + return registerBarcode(pdf, bcode, err) +} + +// 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, bcode barcode.Barcode, err error) string { + if err != nil { + pdf.SetError(err) + } + + 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 *gofpdf.Fpdf, 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 *gofpdf.Fpdf, value float64) float64 { + return value * pdf.GetConversionRatio() / 72 * 96 +} diff --git a/contrib/barcode/barcode_test.go b/contrib/barcode/barcode_test.go new file mode 100644 index 0000000..c12ff12 --- /dev/null +++ b/contrib/barcode/barcode_test.go @@ -0,0 +1,126 @@ +package barcode_test + +import ( + "github.com/boombuler/barcode/code128" + "github.com/boombuler/barcode/qr" + "github.com/jung-kurt/gofpdf" + "github.com/jung-kurt/gofpdf/contrib/barcode" + "github.com/jung-kurt/gofpdf/internal/example" +) + +func createPdf() (pdf *gofpdf.Fpdf) { + pdf = gofpdf.New("L", "mm", "A4", "") + pdf.SetFont("Helvetica", "", 12) + pdf.SetFillColor(200, 200, 220) + pdf.AddPage() + return +} + +func ExampleRegister() { + pdf := createPdf() + + fileStr := example.Filename("contrib_barcode_Register") + + bcode, err := code128.Encode("gofpdf") + + if err == nil { + key := barcode.Register(bcode) + barcode.Barcode(pdf, key, 15, 15, 100, 10, false) + } + + err = pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_Register.pdf +} + +func ExampleRegisterCodabar() { + pdf := createPdf() + + key := barcode.RegisterCode128(pdf, "codabar") + barcode.Barcode(pdf, key, 15, 15, 100, 10, false) + + fileStr := example.Filename("contrib_barcode_RegisterCodabar") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterCodabar.pdf +} + +func ExampleRegisterCode128() { + pdf := createPdf() + + key := barcode.RegisterCode128(pdf, "code128") + barcode.Barcode(pdf, key, 15, 15, 100, 10, false) + + fileStr := example.Filename("contrib_barcode_RegisterCode128") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterCode128.pdf +} + +func ExampleRegisterCode39() { + pdf := createPdf() + + key := barcode.RegisterCode39(pdf, "CODE39", false, true) + barcode.Barcode(pdf, key, 15, 15, 100, 10, false) + + fileStr := example.Filename("contrib_barcode_RegisterCode39") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterCode39.pdf +} + +func ExampleRegisterDataMatrix() { + pdf := createPdf() + + key := barcode.RegisterDataMatrix(pdf, "datamatrix") + barcode.Barcode(pdf, key, 15, 15, 20, 20, false) + + fileStr := example.Filename("contrib_barcode_RegisterDataMatrix") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterDataMatrix.pdf +} + +func ExampleRegisterEAN() { + pdf := createPdf() + + key := barcode.RegisterEAN(pdf, "96385074") + barcode.Barcode(pdf, key, 15, 15, 100, 10, false) + + fileStr := example.Filename("contrib_barcode_RegisterEAN") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterEAN.pdf +} + +func ExampleRegisterQR() { + pdf := createPdf() + + key := barcode.RegisterQR(pdf, "qrcode", qr.H, qr.Unicode) + barcode.Barcode(pdf, key, 15, 15, 20, 20, false) + + fileStr := example.Filename("contrib_barcode_RegisterQR") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterQR.pdf +} + +func ExampleRegisterTwoOfFive() { + pdf := createPdf() + + key := barcode.RegisterTwoOfFive(pdf, "1234567895", true) + barcode.Barcode(pdf, key, 15, 15, 100, 20, false) + + fileStr := example.Filename("contrib_barcode_RegisterTwoOfFive") + err := pdf.OutputFileAndClose(fileStr) + example.Summary(err, fileStr) + // Output: + // Successfully generated ../../pdf/contrib_barcode_RegisterTwoOfFive.pdf +} -- cgit v1.2.1-24-ge1ad