You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
2.8 KiB

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