LLKennedy/imagetemplate

View on GitHub
render/barcode.go

Summary

Maintainability
A
2 hrs
Test Coverage
package render

import (
    "errors"
    "image"
    "image/color"
    "image/draw"

    "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/code93"
    "github.com/boombuler/barcode/datamatrix"
    "github.com/boombuler/barcode/ean"
    "github.com/boombuler/barcode/pdf417"
    "github.com/boombuler/barcode/qr"
    "github.com/boombuler/barcode/twooffive"
)

// BarcodeType wraps the barcode types into a single enum.
type BarcodeType string

const (
    // BarcodeTypeAztec           is an alias for an imported barcode type
    BarcodeTypeAztec BarcodeType = barcode.TypeAztec
    // BarcodeTypeCodabar         is an alias for an imported barcode type
    BarcodeTypeCodabar BarcodeType = barcode.TypeCodabar
    // BarcodeTypeCode128         is an alias for an imported barcode type
    BarcodeTypeCode128 BarcodeType = barcode.TypeCode128
    // BarcodeTypeCode39          is an alias for an imported barcode type
    BarcodeTypeCode39 BarcodeType = barcode.TypeCode39
    // BarcodeTypeCode93          is an alias for an imported barcode type
    BarcodeTypeCode93 BarcodeType = barcode.TypeCode93
    // BarcodeTypeDataMatrix      is an alias for an imported barcode type
    BarcodeTypeDataMatrix BarcodeType = barcode.TypeDataMatrix
    // BarcodeTypeEAN8            is an alias for an imported barcode type
    BarcodeTypeEAN8 BarcodeType = barcode.TypeEAN8
    // BarcodeTypeEAN13           is an alias for an imported barcode type
    BarcodeTypeEAN13 BarcodeType = barcode.TypeEAN13
    // BarcodeTypePDF             is an alias for an imported barcode type
    BarcodeTypePDF BarcodeType = barcode.TypePDF
    // BarcodeTypeQR              is an alias for an imported barcode type
    BarcodeTypeQR BarcodeType = barcode.TypeQR
    // BarcodeType2of5            is an alias for an imported barcode type
    BarcodeType2of5 BarcodeType = barcode.Type2of5
    // BarcodeType2of5Interleaved is an alias for an imported barcode type
    BarcodeType2of5Interleaved BarcodeType = barcode.Type2of5Interleaved
)

// ToBarcodeType attempts to convert a barcode type string to a defined BarcodeType constant.
func ToBarcodeType(raw string) (BarcodeType, error) {
    switch raw {
    case string(BarcodeTypeAztec):
        return BarcodeTypeAztec, nil
    case string(BarcodeTypeCodabar):
        return BarcodeTypeCodabar, nil
    case string(BarcodeTypeCode128):
        return BarcodeTypeCode128, nil
    case string(BarcodeTypeCode39):
        return BarcodeTypeCode39, nil
    case string(BarcodeTypeCode93):
        return BarcodeTypeCode93, nil
    case string(BarcodeTypeDataMatrix):
        return BarcodeTypeDataMatrix, nil
    case string(BarcodeTypeEAN8):
        return BarcodeTypeEAN8, nil
    case string(BarcodeTypeEAN13):
        return BarcodeTypeEAN13, nil
    case string(BarcodeTypePDF):
        return BarcodeTypePDF, nil
    case string(BarcodeTypeQR):
        return BarcodeTypeQR, nil
    case string(BarcodeType2of5):
        return BarcodeType2of5, nil
    case string(BarcodeType2of5Interleaved):
        return BarcodeType2of5Interleaved, nil
    default:
        return BarcodeType(""), errors.New("barcode type does not match defined constants")
    }
}

// BarcodeExtraData contains additional data required for some barcode formats, leave any fields not named for the type in use alone.
type BarcodeExtraData struct {
    // AztecMinECCPercent is required for aztec barcodes
    AztecMinECCPercent int
    // AztecUserSpecifiedLayers is required for aztec barcodes
    AztecUserSpecifiedLayers int
    // Code39IncludeChecksum is required for code39 barcodes
    Code39IncludeChecksum bool
    // Code39FullASCIIMode is required for code39 barcodes
    Code39FullASCIIMode bool
    // Code93IncludeChecksum is required for code93 barcodes
    Code93IncludeChecksum bool
    // Code93FullASCIIMode is required for code93 barcodes
    Code93FullASCIIMode bool
    // PDFSecurityLevel is required for pdf417 barcodes
    PDFSecurityLevel byte
    // QRLevel is required for qr barcodes
    QRLevel qr.ErrorCorrectionLevel
    // QRMode is required for qr barcodes
    QRMode qr.Encoding
}

// Barcode draws a barcode on the canvas.
func (canvas ImageCanvas) Barcode(codeType BarcodeType, content []byte, extra BarcodeExtraData, start image.Point, width, height int, dataColour color.Color, backgroundColour color.Color) (Canvas, error) {
    c := canvas
    if c.Image == nil {
        return canvas, errors.New("no image set for canvas to draw on")
    }
    var encodedBarcode barcode.Barcode
    var err error
    switch codeType {
    case BarcodeTypeAztec:
        encodedBarcode, err = aztec.Encode(content, extra.AztecMinECCPercent, extra.AztecUserSpecifiedLayers)
    case BarcodeTypeCodabar:
        encodedBarcode, err = codabar.Encode(string(content))
    case BarcodeTypeCode128:
        encodedBarcode, err = code128.Encode(string(content))
    case BarcodeTypeCode39:
        encodedBarcode, err = code39.Encode(string(content), extra.Code39IncludeChecksum, extra.Code39FullASCIIMode)
    case BarcodeTypeCode93:
        encodedBarcode, err = code93.Encode(string(content), extra.Code93IncludeChecksum, extra.Code93FullASCIIMode)
        if err != nil && err.Error() == "invalid data!" {
            err = errors.New("invalid data")
        }
    case BarcodeTypeDataMatrix:
        encodedBarcode, err = datamatrix.Encode(string(content))
    case BarcodeTypeEAN8:
        if len(content) != 8 {
            err = errors.New("EAN8 Barcode requires 8 characters")
        } else {
            encodedBarcode, err = ean.Encode(string(content))
        }
    case BarcodeTypeEAN13:
        if len(content) != 13 {
            err = errors.New("EAN13 Barcode requires 13 characters")
        } else {
            encodedBarcode, err = ean.Encode(string(content))
        }
    case BarcodeTypePDF:
        encodedBarcode, err = pdf417.Encode(string(content), extra.PDFSecurityLevel)
    case BarcodeTypeQR:
        encodedBarcode, err = qr.Encode(string(content), extra.QRLevel, extra.QRMode)
    case BarcodeType2of5:
        encodedBarcode, err = twooffive.Encode(string(content), false)
    case BarcodeType2of5Interleaved:
        encodedBarcode, err = twooffive.Encode(string(content), true)
    }
    if err != nil {
        return canvas, err
    }
    encodedBarcode, err = barcode.Scale(encodedBarcode, width, height)
    if err != nil {
        return canvas, err
    }

    if dataColour == nil {
        dataColour = color.Black
    }
    if backgroundColour == nil {
        backgroundColour = color.White
    }
    boundRect := encodedBarcode.Bounds()
    draw.DrawMask(c.Image, image.Rect(start.X, start.Y, start.X+width, start.Y+height), image.NewUniform(backgroundColour), image.ZP, blackAndWhiteMask{bw: encodedBarcode, bColour: color.Transparent, wColour: color.Opaque}, boundRect.Min, draw.Over)
    draw.DrawMask(c.Image, image.Rect(start.X, start.Y, start.X+width, start.Y+height), image.NewUniform(dataColour), image.ZP, blackAndWhiteMask{bw: encodedBarcode, bColour: color.Opaque, wColour: color.Transparent}, boundRect.Min, draw.Over)
    return c, nil
}

type blackAndWhiteMask struct {
    bw      image.Image
    bColour color.Alpha16
    wColour color.Alpha16
}

func (m blackAndWhiteMask) ColorModel() color.Model {
    return color.AlphaModel
}

func (m blackAndWhiteMask) Bounds() image.Rectangle {
    return m.bw.Bounds()
}

func (m blackAndWhiteMask) At(x, y int) color.Color {
    if m.bw.At(x, y) == color.Black {
        return m.bColour
    }
    return m.wColour
}