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
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
View as plain text