scaledbarcode.go raw

   1  package barcode
   2  
   3  import (
   4  	"errors"
   5  	"fmt"
   6  	"image"
   7  	"image/color"
   8  	"math"
   9  )
  10  
  11  type wrapFunc func(x, y int) color.Color
  12  
  13  type scaledBarcode struct {
  14  	wrapped     Barcode
  15  	wrapperFunc wrapFunc
  16  	rect        image.Rectangle
  17  }
  18  
  19  type intCSscaledBC struct {
  20  	scaledBarcode
  21  }
  22  
  23  func (bc *scaledBarcode) Content() string {
  24  	return bc.wrapped.Content()
  25  }
  26  
  27  func (bc *scaledBarcode) Metadata() Metadata {
  28  	return bc.wrapped.Metadata()
  29  }
  30  
  31  func (bc *scaledBarcode) ColorModel() color.Model {
  32  	return bc.wrapped.ColorModel()
  33  }
  34  
  35  func (bc *scaledBarcode) Bounds() image.Rectangle {
  36  	return bc.rect
  37  }
  38  
  39  func (bc *scaledBarcode) At(x, y int) color.Color {
  40  	return bc.wrapperFunc(x, y)
  41  }
  42  
  43  func (bc *intCSscaledBC) CheckSum() int {
  44  	if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
  45  		return cs.CheckSum()
  46  	}
  47  	return 0
  48  }
  49  
  50  // Scale returns a resized barcode with the given width and height.
  51  func Scale(bc Barcode, width, height int) (Barcode, error) {
  52  	switch bc.Metadata().Dimensions {
  53  	case 1:
  54  		return scale1DCode(bc, width, height)
  55  	case 2:
  56  		return scale2DCode(bc, width, height)
  57  	}
  58  
  59  	return nil, errors.New("unsupported barcode format")
  60  }
  61  
  62  func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
  63  	result := &scaledBarcode{
  64  		wrapped:     wrapped,
  65  		wrapperFunc: wrapperFunc,
  66  		rect:        rect,
  67  	}
  68  
  69  	if _, ok := wrapped.(BarcodeIntCS); ok {
  70  		return &intCSscaledBC{*result}
  71  	}
  72  	return result
  73  }
  74  
  75  func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
  76  	orgBounds := bc.Bounds()
  77  	orgWidth := orgBounds.Max.X - orgBounds.Min.X
  78  	orgHeight := orgBounds.Max.Y - orgBounds.Min.Y
  79  
  80  	factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
  81  	if factor <= 0 {
  82  		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
  83  	}
  84  
  85  	offsetX := (width - (orgWidth * factor)) / 2
  86  	offsetY := (height - (orgHeight * factor)) / 2
  87  
  88  	wrap := func(x, y int) color.Color {
  89  		if x < offsetX || y < offsetY {
  90  			return color.White
  91  		}
  92  		x = (x - offsetX) / factor
  93  		y = (y - offsetY) / factor
  94  		if x >= orgWidth || y >= orgHeight {
  95  			return color.White
  96  		}
  97  		return bc.At(x, y)
  98  	}
  99  
 100  	return newScaledBC(
 101  		bc,
 102  		wrap,
 103  		image.Rect(0, 0, width, height),
 104  	), nil
 105  }
 106  
 107  func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
 108  	orgBounds := bc.Bounds()
 109  	orgWidth := orgBounds.Max.X - orgBounds.Min.X
 110  	factor := int(float64(width) / float64(orgWidth))
 111  
 112  	if factor <= 0 {
 113  		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
 114  	}
 115  	offsetX := (width - (orgWidth * factor)) / 2
 116  
 117  	wrap := func(x, y int) color.Color {
 118  		if x < offsetX {
 119  			return color.White
 120  		}
 121  		x = (x - offsetX) / factor
 122  
 123  		if x >= orgWidth {
 124  			return color.White
 125  		}
 126  		return bc.At(x, 0)
 127  	}
 128  
 129  	return newScaledBC(
 130  		bc,
 131  		wrap,
 132  		image.Rect(0, 0, width, height),
 133  	), nil
 134  }
 135