Skip to content

Commit

Permalink
Merge pull request #35 from yeqown/issue-34
Browse files Browse the repository at this point in the history
feat(image): support border width as output image parameters
  • Loading branch information
yeqown authored Aug 11, 2021
2 parents 1decac2 + 3133e5f commit bb67d8f
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 7 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ QR code (abbreviated from Quick Response Code) is the trademark for a type of ma

- [x] `WithLogoImage`, `WithLogoImageFilePNG`, `WithLogoImageFileJPEG` help you add an icon at the central of QR Code.

- [x] `WithBorderWidth` allows to specify any width of 4 sides around the qrcode.

### Install

```sh
Expand Down Expand Up @@ -93,6 +95,12 @@ func WithBuiltinImageEncoder(format formatTyp) ImageOption
// WithCustomImageEncoder to use custom image encoder to encode image.Image into
// io.Writer
func WithCustomImageEncoder(encoder ImageEncoder) ImageOption

// WithBorderWidth specify the both 4 sides' border width. Notice that
// WithBorderWidth(a) means all border width use this variable `a`,
// WithBorderWidth(a, b) mean top/bottom equal to `a`, left/right equal to `b`.
// WithBorderWidth(a, b, c, d) mean top, right, bottom, left.
func WithBorderWidth(widths ...int) ImageOption
```

use options in `New` and `NewWithConfig`.
Expand Down
16 changes: 10 additions & 6 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ func drawAndSaveToFile(name string, m matrix.Matrix, opt *outputImageOptions) er
if err != nil {
return fmt.Errorf("could not create file: %v", err)
}
defer f.Close()

defer func(f *os.File) {
err = f.Close()
}(f)

return drawAndSave(f, m, opt)
}
Expand Down Expand Up @@ -128,15 +131,16 @@ func drawAndSave(w io.Writer, m matrix.Matrix, imgOpt *outputImageOptions) (err
// return rgba
//}

// draw deal QRCode's matrix to be a image.Image
// draw deal QRCode's matrix to be an image.Image
func draw(mat matrix.Matrix, opt *outputImageOptions) image.Image {
if _debug {
fmt.Printf("matrix.Width()=%d, matrix.Height()=%d\n", mat.Width(), mat.Height())
}

top, right, bottom, left := opt.borderWidths[0], opt.borderWidths[1], opt.borderWidths[2], opt.borderWidths[3]
// w as image width, h as image height
w := mat.Width()*opt.qrBlockWidth() + 2*_defaultPadding
h := w
w := mat.Width()*opt.qrBlockWidth() + left + right
h := mat.Width()*opt.qrBlockWidth() + top + bottom
// rgba := image.NewRGBA(image.Rect(0, 0, w, h))
dc := gg.NewContext(w, h)

Expand All @@ -159,8 +163,8 @@ func draw(mat matrix.Matrix, opt *outputImageOptions) image.Image {
mat.Iterate(matrix.ROW, func(x int, y int, v matrix.State) {
// Draw the block
ctx.upperLeft = image.Point{
X: x*opt.qrBlockWidth() + _defaultPadding,
Y: y*opt.qrBlockWidth() + _defaultPadding,
X: x*opt.qrBlockWidth() + left,
Y: y*opt.qrBlockWidth() + top,
}
ctx.color = opt.stateRGBA(v)
// DONE(@yeqown): make this abstract to Shapes
Expand Down
4 changes: 4 additions & 0 deletions image_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func defaultOutputImageOption() *outputImageOptions {
qrWidth: 20, //
shape: _shapeRectangle, //
imageEncoder: jpegEncoder{},
borderWidths: [4]int{_defaultPadding, _defaultPadding, _defaultPadding, _defaultPadding},
}
}

Expand All @@ -41,6 +42,9 @@ type outputImageOptions struct {
// imageEncoder specify which file format would be encoded the QR image.
imageEncoder ImageEncoder

// borderWidths indicates the border width of the output image. the order is
// top, right, bottom, left same as the WithBorder
borderWidths [4]int
}

func (oo *outputImageOptions) backgroundColor() color.Color {
Expand Down
29 changes: 28 additions & 1 deletion image_option_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func WithBuiltinImageEncoder(format formatTyp) ImageOption {
})
}

// WithBuiltinImageEncoder to use custom image encoder to encode image.Image into
// WithCustomImageEncoder to use custom image encoder to encode image.Image into
// io.Writer
func WithCustomImageEncoder(encoder ImageEncoder) ImageOption {
return newFuncDialOption(func(oo *outputImageOptions) {
Expand All @@ -164,3 +164,30 @@ func WithCustomImageEncoder(encoder ImageEncoder) ImageOption {
})
}

// WithBorderWidth specify the both 4 sides' border width. Notice that
// WithBorderWidth(a) means all border width use this variable `a`,
// WithBorderWidth(a, b) mean top/bottom equal to `a`, left/right equal to `b`.
// WithBorderWidth(a, b, c, d) mean top, right, bottom, left.
func WithBorderWidth(widths ...int) ImageOption {
apply := func(arr *[4]int, top, right, bottom, left int) {
arr[0] = top
arr[1] = right
arr[2] = bottom
arr[3] = left
}

return newFuncDialOption(func(oo *outputImageOptions) {
n := len(widths)
switch n {
case 0:
apply(&oo.borderWidths, _defaultPadding, _defaultPadding, _defaultPadding, _defaultPadding)
case 1:
apply(&oo.borderWidths, widths[0], widths[0], widths[0], widths[0])
case 2, 3:
apply(&oo.borderWidths, widths[0], widths[1], widths[0], widths[1])
default:
// 4+
apply(&oo.borderWidths, widths[0], widths[1], widths[2], widths[3])
}
})
}
25 changes: 25 additions & 0 deletions image_option_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/yeqown/go-qrcode/matrix"
)

Expand Down Expand Up @@ -69,3 +70,27 @@ func Test_defaultOutputOption(t *testing.T) {
oo2 := defaultOutputImageOption()
assert.NotEqual(t, oo2.bgColor, oo.bgColor)
}

func Test_WithBorderWidth(t *testing.T) {
oo := defaultOutputImageOption()

// zero parameter
WithBorderWidth().apply(oo)
assert.Equal(t, [4]int{_defaultPadding, _defaultPadding, _defaultPadding, _defaultPadding}, oo.borderWidths)

// one parameter
WithBorderWidth(1).apply(oo)
assert.Equal(t, [4]int{1, 1, 1, 1}, oo.borderWidths)

// two parameters
WithBorderWidth(1, 2).apply(oo)
assert.Equal(t, [4]int{1, 2, 1, 2}, oo.borderWidths)

// three parameters
WithBorderWidth(1, 2, 3).apply(oo)
assert.Equal(t, [4]int{1, 2, 1, 2}, oo.borderWidths)

// four parameters
WithBorderWidth(1, 2, 3, 4).apply(oo)
assert.Equal(t, [4]int{1, 2, 3, 4}, oo.borderWidths)
}
16 changes: 16 additions & 0 deletions qrcode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,19 @@ func Test_New_WithOutputOption_Shape(t *testing.T) {
t.Fail()
}
}

func Test_New_WithBorderWidth(t *testing.T) {
qrc, err := New("Test_New_WithOutputOption_Shape",
WithBorderWidth(10, 20, 30, 40),
)
if err != nil {
t.Errorf("could not generate QRCode: %v", err)
t.Fail()
}

// save file
if err = qrc.Save("./testdata/qrtest_border_width.jpeg"); err != nil {
t.Errorf("could not save image: %v", err)
t.Fail()
}
}

0 comments on commit bb67d8f

Please sign in to comment.