Skip to content

Commit

Permalink
feat: ScaleBy: scale the barcode based on a factor
Browse files Browse the repository at this point in the history
Allows scaling both width and height, or only the height (using the
minimal width necessary)
  • Loading branch information
cvanloo committed Jan 12, 2024
1 parent f6def83 commit 66a86e1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 7 deletions.
32 changes: 28 additions & 4 deletions code128/code128.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,24 @@ import (
)

type Code128 struct {
*image.Gray16
*image.Gray16 // @todo: byte array?
}

func (c Code128) Scale(width, height int) (image.Image, error) {
oldWidth := c.Bounds().Dx()
if width < oldWidth {
return nil, errors.New("unable to shrink image, new width too small")
return nil, errors.New("unable to shrink image: new width too small")
}
if height <= 0 {
return nil, errors.New("unable to shrink image: new height too small")
}

scaledImage := image.NewGray16(image.Rectangle{image.Point{0, 0}, image.Point{width, height}})

// scale width
// @todo: don't scale QZ. Don't include QZ at all in Code128? (only add it to the scaled image)
scale := width / oldWidth
qz := float64(width % oldWidth) / 2
qz := float64(width%oldWidth) / 2
qzs := int(math.Floor(qz))
qze := int(math.Ceil(qz))
for x := 0; x < qzs; x++ { // extend quiet zone start
Expand All @@ -50,6 +54,26 @@ func (c Code128) Scale(width, height int) (image.Image, error) {
return scaledImage, nil
}

type ScaleMode int

const (
ScaleWidthAndHeight ScaleMode = iota
ScaleHeight
// no point in scaling only the width: what do you want to do with a 1px high barcode?
)

func (c Code128) ScaleBy(factor float64, mode ScaleMode) (image.Image, error) {
w, h := c.Bounds().Dx(), c.Bounds().Dy()
switch mode {
case ScaleWidthAndHeight:
return c.Scale(int(math.Floor(float64(w)*factor)), int(math.Floor(float64(h)*factor)))
case ScaleHeight:
return c.Scale(w, int(math.Floor(float64(h)*factor)))
default:
return nil, errors.New("invalid scaling mode")
}
}

type ASCII string

func NewASCII(text string) (ASCII, error) {
Expand All @@ -71,7 +95,7 @@ func isASCII(text string) bool {
func Encode(text ASCII) (Code128, error) {
indices := determineIndices(text)

c128 := &barcode{}
c128 := &barcode{}
c128.add(QuietSpace)

startSym := [...]int{START_A - SpecialOffset, START_B - SpecialOffset, START_C - SpecialOffset}[indices[0]]
Expand Down
60 changes: 57 additions & 3 deletions code128/code128_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

func TestDetermineIndices(t *testing.T) {
cases := []struct{
text string
cases := []struct {
text string
expected []TableIndex
}{
{"Abc12", []TableIndex{LookupB, LookupB, LookupB, LookupB, LookupB}},
Expand Down Expand Up @@ -198,8 +198,62 @@ func TestEncodeScale(t *testing.T) {
}
}

func TestEncodeScaleBy(t *testing.T) {
cases := []struct {
input string
scale float64
mode ScaleMode
success bool
}{
{"Hello, World!", 5, ScaleWidthAndHeight, true},
{"11223467", 2, ScaleWidthAndHeight, true},
{"\026\025", 3, ScaleHeight, true},
{"hello", 1.5, ScaleWidthAndHeight, true},
{"112269420", 1.7, ScaleHeight, true},
{"yoyoyoyo", 3.257, ScaleHeight, true},
{"439721-hello-WORLD", 0.8, ScaleWidthAndHeight, false},
{"hello\026world", 0.1, ScaleHeight, false},
{"\026\025h\006", 0.215, ScaleHeight, false},
{"\026\025H\006", 10, ScaleHeight, true},
{"eaou", 1, ScaleWidthAndHeight, true},
{"eaou", 1, ScaleHeight, true},
}

for i, c := range cases {
t.Logf("case %d: %+v\n", i, c)
text, err := NewASCII(c.input)
if err != nil {
t.Errorf("cannot convert %s to ASCII: %v", c.input, err)
continue
}
bc, err := Encode(text)
if err != nil {
t.Errorf("failed to encode `%s': %v", text, err)
continue
}
img, err := bc.ScaleBy(c.scale, c.mode)
if err != nil {
if c.success {
t.Error(err)
}
continue
}
if !c.success {
t.Errorf("should have failed, but didn't: `%s'", c.input)
}
bs, _, err := Decode(img)
if err != nil {
t.Errorf("failed to decode `%s': %v", c.input, err)
continue
}
if string(bs) != c.input {
t.Errorf("got: `%s', want: `%s'", string(bs), c.input)
}
}
}

func TestEncodeSyms(t *testing.T) {
cases := []struct{
cases := []struct {
text string
syms []int
}{
Expand Down

0 comments on commit 66a86e1

Please sign in to comment.