-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support WASM
- Loading branch information
Showing
10 changed files
with
417 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
## WebAssembly support | ||
|
||
This part provide support to compile and run WebAssembly code. | ||
|
||
```javascript | ||
let option = { | ||
encodeVersion: 0, // 0 - 40 | ||
encodeMode: 2, // 0 - 3 | ||
encodeECLevel: "Q", // L, M, Q, H | ||
|
||
outputBgColor: "#123123", // #000000 - #ffffff | ||
outputBgTransparent: false, // true - false | ||
outputQrColor: "#666666", // #000000 - #ffffff | ||
outputQrWidth: 20, // 0 - 256 | ||
outputCircleShape: true, // true - false | ||
outputImageEncoder: "png", // png, jpeg, jpg | ||
outputMargin: 20, // 0 - 256 | ||
} | ||
let r = generateQRCode("content", option) | ||
// output: | ||
// { | ||
"success": true, | ||
"error": "", | ||
"base64EncodedImage": "iVBORw0KGgoAAAANSUhEUgAAAmwAAAJ... more" | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/sh | ||
|
||
GOOS=js GOARCH=wasm go build -o com.github.yeqown.goqrcode.wasm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module wasm | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/pkg/errors v0.9.1 | ||
github.com/yeqown/go-qrcode/v2 v2.0.2 | ||
github.com/yeqown/go-qrcode/writer/standard v1.1.1 | ||
) | ||
|
||
require ( | ||
github.com/fogleman/gg v1.3.0 // indirect | ||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect | ||
github.com/yeqown/reedsolomon v1.0.0 // indirect | ||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//go:build js && wasm | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"syscall/js" | ||
|
||
"github.com/pkg/errors" | ||
qrcode "github.com/yeqown/go-qrcode/v2" | ||
stdw "github.com/yeqown/go-qrcode/writer/standard" | ||
) | ||
|
||
func main() { | ||
js.Global().Set("generateQRCode", js.FuncOf(genqrcode)) | ||
fmt.Println("com.github.yeqown.goqrcode.wasm loaded") | ||
|
||
select {} | ||
} | ||
|
||
// genqrcode generates a qrcode image and returns the base64 encoded string. | ||
// args should be a string array with length of 2 at most. the first one is the | ||
// content string which will be encoded to qrcode, the second one is the encoding | ||
// option, which is optional. | ||
// | ||
// let result = generateQRCode("content", { | ||
// qrWidth: 200, | ||
// qrMargin: 10, | ||
// qrColor: "#000000", | ||
// qrBackColor: "#ffffff", | ||
// encLevel: "H", | ||
// encVersion: 7, | ||
// }) | ||
// more options refer to the `genOption` struct | ||
func genqrcode(_ js.Value, args []js.Value) (v interface{}) { | ||
var ( | ||
srcContent = "" | ||
r = new(genResult) | ||
opt = new(genOption) | ||
) | ||
|
||
defer func() { | ||
v = r.JSValue() | ||
}() | ||
|
||
switch len(args) { | ||
case 0: | ||
r.setError(errors.New("no args")) | ||
return | ||
case 1: | ||
srcContent = args[0].String() | ||
case 2: | ||
fallthrough | ||
default: | ||
srcContent = args[0].String() | ||
opt = optionFromJSValue(args[1]) | ||
} | ||
|
||
//if err := opt.validate(); err != nil { | ||
// r.setError(errors.Wrap(err, "invalid option")) | ||
// return | ||
//} | ||
|
||
qrc, err := qrcode.NewWith(srcContent, opt.encodeOptions()...) | ||
if err != nil { | ||
r.setError(errors.Wrap(err, "genqrcode qrcode")) | ||
return | ||
} | ||
|
||
buf := bytes.NewBuffer(nil) | ||
wr := nopCloser{Writer: buf} | ||
w := stdw.NewWithWriter(wr, opt.outputOptions()...) | ||
|
||
err = qrc.Save(w) | ||
if err != nil { | ||
r.setError(errors.Wrap(err, "apply output option")) | ||
return | ||
} | ||
|
||
// apply image to result | ||
r.setImage(buf) | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
//go:build js && wasm | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"encoding/base64" | ||
"io" | ||
"syscall/js" | ||
|
||
"github.com/yeqown/go-qrcode/v2" | ||
stdw "github.com/yeqown/go-qrcode/writer/standard" | ||
) | ||
|
||
// encodeOption refers to github.com/yeqown/go-qrcode/v2.encodingOption | ||
type encodeOption struct { | ||
version int // encodeVersion | ||
mode uint8 // encodeMode specifies which encMode to use (0/1/2/3). | ||
ecLevel string // encodeECLevel specifies which ecLevel to use (L/M/Q/H) | ||
} | ||
|
||
// outputOption refers to github.com/yeqown/go-qrcode/writer/standard.outputImageOptions | ||
type outputOption struct { | ||
bgColor string // outputBgColor is the background color of the QR code image. | ||
bgTransparent bool // outputBgTransparent indicates whether the background color is transparent. | ||
qrColor string // outputQrColor is the foreground color of the QR code. | ||
qrWidth uint8 // outputQrWidth is the width of the QR code. | ||
circleShape bool // outputCircleShape indicates whether to draw the qr block in circle shape. | ||
imageEncoder string // outputImageEncoder specifies file format would be encoded the QR image. (jpg/jpeg/png) | ||
margin int // outputMargin is the border width of the output image. | ||
} | ||
|
||
// genOption is a type of option for generating code. | ||
type genOption struct { | ||
encodeOption | ||
outputOption | ||
} | ||
|
||
// optionFromJSValue converts js.Value to genOption. | ||
func optionFromJSValue(option js.Value) *genOption { | ||
//fmt.Println(option.Type().String()) | ||
if option.IsNull() || option.IsUndefined() || option.Type().String() != "object" { | ||
// option must be an object, otherwise return default empty option. | ||
return nil | ||
} | ||
|
||
return &genOption{ | ||
encodeOption: encodeOption{ | ||
version: option.Get("encodeVersion").Int(), | ||
mode: uint8(option.Get("encodeMode").Int()), | ||
ecLevel: option.Get("encodeECLevel").String(), | ||
}, | ||
outputOption: outputOption{ | ||
bgColor: option.Get("outputBgColor").String(), | ||
bgTransparent: option.Get("outputBgTransparent").Bool(), | ||
qrColor: option.Get("outputQrColor").String(), | ||
qrWidth: uint8(option.Get("outputQrWidth").Int()), | ||
circleShape: option.Get("outputCircleShape").Bool(), | ||
imageEncoder: option.Get("outputImageEncoder").String(), | ||
margin: option.Get("outputMargin").Int(), | ||
}, | ||
} | ||
} | ||
|
||
// validate genOption. | ||
func (o *genOption) validate() error { | ||
if o == nil { | ||
return nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (o *genOption) encodeOptions() []qrcode.EncodeOption { | ||
if o == nil { | ||
return nil | ||
} | ||
|
||
out := make([]qrcode.EncodeOption, 0, 4) | ||
|
||
if o.encodeOption.version != 0 { | ||
out = append(out, qrcode.WithVersion(o.encodeOption.version)) | ||
} | ||
|
||
switch o.encodeOption.mode { | ||
case uint8(qrcode.EncModeAlphanumeric): | ||
out = append(out, qrcode.WithEncodingMode(qrcode.EncModeAlphanumeric)) | ||
case uint8(qrcode.EncModeNumeric): | ||
out = append(out, qrcode.WithEncodingMode(qrcode.EncModeNumeric)) | ||
case uint8(qrcode.EncModeByte): | ||
out = append(out, qrcode.WithEncodingMode(qrcode.EncModeByte)) | ||
case uint8(qrcode.EncModeJP): | ||
out = append(out, qrcode.WithEncodingMode(qrcode.EncModeJP)) | ||
} | ||
|
||
switch o.encodeOption.ecLevel { | ||
case "L": | ||
out = append(out, qrcode.WithErrorCorrectionLevel(qrcode.ErrorCorrectionLow)) | ||
case "M": | ||
out = append(out, qrcode.WithErrorCorrectionLevel(qrcode.ErrorCorrectionMedium)) | ||
case "Q": | ||
out = append(out, qrcode.WithErrorCorrectionLevel(qrcode.ErrorCorrectionQuart)) | ||
case "H": | ||
out = append(out, qrcode.WithErrorCorrectionLevel(qrcode.ErrorCorrectionHighest)) | ||
} | ||
|
||
return out | ||
} | ||
|
||
func (o *genOption) outputOptions() []stdw.ImageOption { | ||
if o == nil { | ||
return nil | ||
} | ||
|
||
out := make([]stdw.ImageOption, 0, 8) | ||
|
||
if o.outputOption.bgColor != "" { | ||
out = append(out, stdw.WithBgColorRGBHex(o.outputOption.bgColor)) | ||
} | ||
if o.outputOption.bgTransparent { | ||
out = append(out, stdw.WithBgTransparent()) | ||
} | ||
if o.outputOption.qrColor != "" { | ||
out = append(out, stdw.WithFgColorRGBHex(o.outputOption.qrColor)) | ||
} | ||
if o.outputOption.qrWidth != 0 { | ||
out = append(out, stdw.WithQRWidth(o.outputOption.qrWidth)) | ||
} | ||
if o.outputOption.circleShape { | ||
out = append(out, stdw.WithCircleShape()) | ||
} | ||
|
||
switch o.outputOption.imageEncoder { | ||
case "jpg", "jpeg": | ||
out = append(out, stdw.WithBuiltinImageEncoder(stdw.JPEG_FORMAT)) | ||
case "png": | ||
fallthrough | ||
default: | ||
out = append(out, stdw.WithBuiltinImageEncoder(stdw.PNG_FORMAT)) | ||
} | ||
|
||
if o.outputOption.margin != 0 { | ||
out = append(out, stdw.WithBorderWidth(o.outputOption.margin)) | ||
} | ||
|
||
return out | ||
} | ||
|
||
// genResult is result contains generated code image or error message. | ||
type genResult struct { | ||
Success bool `json:"success"` | ||
Error string `json:"error"` | ||
Base64EncodedImage string `json:"base64EncodedImage"` | ||
} | ||
|
||
func (r *genResult) setError(err error) { | ||
r.Success = false | ||
r.Error = err.Error() | ||
} | ||
|
||
func (r *genResult) setImage(buf *bytes.Buffer) { | ||
r.Success = true | ||
r.Base64EncodedImage = base64.StdEncoding.EncodeToString(buf.Bytes()) | ||
} | ||
|
||
func (r *genResult) JSValue() js.Value { | ||
return js.ValueOf(map[string]any{ | ||
"success": r.Success, | ||
"error": r.Error, | ||
"base64EncodedImage": r.Base64EncodedImage, | ||
}) | ||
} | ||
|
||
type nopCloser struct { | ||
io.Writer | ||
} | ||
|
||
func (nopCloser) Close() error { return nil } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
) | ||
|
||
func Test_genOption(t *testing.T) { | ||
o := genOption{ | ||
encodeOption: encodeOption{ | ||
version: 0, | ||
mode: 0, | ||
ecLevel: "", | ||
}, | ||
outputOption: outputOption{ | ||
bgColor: "", | ||
bgTransparent: false, | ||
qrColor: "", | ||
qrWidth: 0, | ||
circleShape: false, | ||
imageEncoder: "", | ||
margin: 0, | ||
}, | ||
} | ||
|
||
byts, err := json.MarshalIndent(o, "", " ") | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
t.Log(string(byts)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
com.github.yeqown.goqrcode.wasm | ||
wasm_exec.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
## WebAssembly Example | ||
|
||
This example demonstrates how to use the `go-qrcode/wasm` pre-compiled `wasm` to generate | ||
QRCode image in Web browser. | ||
|
||
```bash | ||
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" . | ||
cp "$PATH/go-qrcode/wasm/com.github.yeqown.goqrcode.wasm" . | ||
# then serve the index.html in browser | ||
python3 -m http.server | ||
# it serves the index.html on http://localhost:8000, you can use another way too. | ||
``` | ||
|
||
after that, you can visit http://localhost:8000 to see the demo. |
Oops, something went wrong.