Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99189e4b51 | |||
| d8781c3981 | |||
| d7ffc17d71 | |||
| ecb03c1fb7 | |||
| 016cc5b6fe | |||
| 9a0d1d098c |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -25,4 +25,8 @@ go.work.sum
|
||||
# env file
|
||||
.env
|
||||
|
||||
build
|
||||
build
|
||||
assets
|
||||
libgofunc
|
||||
libgo
|
||||
cmd/out.png
|
||||
|
||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Dev",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd",
|
||||
},
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
37
build_lib.sh
37
build_lib.sh
@@ -1,9 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
APP_NAME="libgofunc"
|
||||
VERSION="${1:-v0.1.1}"
|
||||
OUTPUT_DIR="assets"
|
||||
BUILD_DIR="build"
|
||||
VERSION="${1:-v0.1.7}"
|
||||
OUTPUT_DIR="../assets"
|
||||
BUILD_DIR="../build"
|
||||
|
||||
# need Android NDK
|
||||
NDK_HOME="$HOME/Android/Sdk/ndk/28.2.13676358" # <--- CHECK YOUR VERSION
|
||||
@@ -11,7 +11,7 @@ API=21
|
||||
|
||||
TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
OS="$(uname -s)"
|
||||
|
||||
cd cmd
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
export IOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
|
||||
export IOS_SIM_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)
|
||||
@@ -27,7 +27,7 @@ if [ "$OS" = "Darwin" ]; then
|
||||
CC="$(xcrun --sdk iphonesimulator --find clang)" \
|
||||
CGO_CFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \
|
||||
CGO_LDFLAGS="-isysroot $IOS_SIM_SDK -arch x86_64" \
|
||||
go build -buildmode=c-archive -o build/ios/sim/libgofunc_arm64_sim.a .
|
||||
go build ./cmd -buildmode=c-archive -o build/ios/sim/libgofunc_arm64_sim.a .
|
||||
|
||||
# xcodebuild -create-xcframework \
|
||||
# -library build/ios/device/libgofunc_arm64.a -headers build/ios/device/ \
|
||||
@@ -46,34 +46,35 @@ if [ "$OS" = "Darwin" ]; then
|
||||
elif [ "$OS" = "Linux" ]; then
|
||||
mkdir -p "${OUTPUT_DIR}/${VERSION}"
|
||||
|
||||
echo "Building for Android amd64..."
|
||||
ARCH="amd64"
|
||||
echo "Building for Android $ARCH..."
|
||||
CC="$TOOLCHAIN/x86_64-linux-android$API-clang" \
|
||||
CGO_ENABLED=1 GOOS=android GOARCH=amd64 \
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/libgofunc_amd64.so .
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/$VERSION/$ARCH/libgofunc.so .
|
||||
|
||||
ARCHIVE_NAME="${APP_NAME}-${VERSION}-amd64.tar.gz"
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" libgofunc_amd64.so
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" ./${VERSION}/${ARCH}
|
||||
|
||||
echo "Building for Android arm64..."
|
||||
ARCH="arm64"
|
||||
echo "Building for Android $ARCH..."
|
||||
CC="$TOOLCHAIN/aarch64-linux-android$API-clang" \
|
||||
CGO_ENABLED=1 GOOS=android GOARCH=arm64 \
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/libgofunc_arm64.so .
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/$VERSION/$ARCH/libgofunc.so .
|
||||
|
||||
ARCHIVE_NAME="${APP_NAME}-${VERSION}-arm64.tar.gz"
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" libgofunc_arm64.so
|
||||
ARCHIVE_NAME="${APP_NAME}-${VERSION}-${ARCH}.tar.gz"
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" ./${VERSION}/${ARCH}
|
||||
|
||||
ARCH="armv7a"
|
||||
echo "Building for Android armv7a..."
|
||||
CC="$TOOLCHAIN/armv7a-linux-androideabi$API-clang" \
|
||||
CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/libgofunc_armv7a.so .
|
||||
go build -buildmode=c-shared -o $BUILD_DIR/$VERSION/$ARCH/libgofunc.so .
|
||||
|
||||
ARCHIVE_NAME="${APP_NAME}-${VERSION}-armv7a.tar.gz"
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" libgofunc_armv7a.so
|
||||
|
||||
# cp ./assets/libgofunc_x64.so $HOME/ws/forward_pos/native/android/x86_64/libgofunc.so
|
||||
# cp ./assets/libgofunc_arm64.so $HOME/ws/forward_pos/native/android/arm64-v8a/libgofunc.so
|
||||
# cp ./assets/libgofunc_armv7a.so $HOME/ws/forward_pos/native/android/armeabi-v7a/libgofunc.so
|
||||
tar -czf "${OUTPUT_DIR}/${VERSION}/${ARCHIVE_NAME}" -C "${BUILD_DIR}" ./${VERSION}/${ARCH}
|
||||
|
||||
export HTTPS_PROXY="socks5://localhost:8080"
|
||||
rclone copy ../assets/${VERSION} s3:mokkon/libs/libgofunc/${VERSION}
|
||||
else
|
||||
echo "Unsupported OS: $OS"
|
||||
exit 1
|
||||
|
||||
141
canvas.go
Normal file
141
canvas.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// -------------------- STYLE --------------------
|
||||
type Style struct {
|
||||
PaddingLeft float64
|
||||
PaddingRight float64
|
||||
PaddingTop float64
|
||||
PaddingBottom float64
|
||||
FontSize float64
|
||||
Width float64
|
||||
Height float64
|
||||
TextAlign string
|
||||
VerticalAlign string
|
||||
Border float64
|
||||
}
|
||||
|
||||
func parseStyle(styleStr string) Style {
|
||||
style := Style{
|
||||
FontSize: 16,
|
||||
TextAlign: "left",
|
||||
VerticalAlign: "top",
|
||||
}
|
||||
|
||||
parts := strings.Split(styleStr, ";")
|
||||
for _, p := range parts {
|
||||
kv := strings.SplitN(strings.TrimSpace(p), ":", 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(kv[0])
|
||||
val := strings.TrimSpace(kv[1])
|
||||
|
||||
switch key {
|
||||
case "padding":
|
||||
v := parsePx(val)
|
||||
style.PaddingLeft, style.PaddingRight, style.PaddingTop, style.PaddingBottom = v, v, v, v
|
||||
case "padding-left":
|
||||
v := parsePx(val)
|
||||
style.PaddingLeft = v
|
||||
case "padding-right":
|
||||
v := parsePx(val)
|
||||
style.PaddingRight = v
|
||||
case "padding-top":
|
||||
v := parsePx(val)
|
||||
style.PaddingTop = v
|
||||
case "padding-bottom":
|
||||
v := parsePx(val)
|
||||
style.PaddingBottom = v
|
||||
case "font-size":
|
||||
style.FontSize = parsePx(val)
|
||||
case "width":
|
||||
style.Width = parsePx(val)
|
||||
case "height":
|
||||
style.Height = parsePx(val)
|
||||
case "text-align":
|
||||
style.TextAlign = val
|
||||
case "vertical-align":
|
||||
style.VerticalAlign = val
|
||||
case "border":
|
||||
style.Border = parsePx(val)
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
func parsePx(v string) float64 {
|
||||
v = strings.ReplaceAll(v, "px", "")
|
||||
f, _ := strconv.ParseFloat(v, 64)
|
||||
return f
|
||||
}
|
||||
|
||||
// -------------------- NODE --------------------
|
||||
type Node struct {
|
||||
Tag string
|
||||
Text string
|
||||
Attr map[string]string
|
||||
Style Style
|
||||
Children []*Node
|
||||
}
|
||||
|
||||
func (n Node) getSrc() string {
|
||||
for k, v := range n.Attr {
|
||||
if k == "src" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n Node) getHeight() int {
|
||||
return int(n.Style.Height)
|
||||
}
|
||||
|
||||
func BuildTree(n *html.Node, canvasWidth int, dc *gg.Context, face *font.Face) (*Node, int) {
|
||||
if n.Type != html.ElementNode {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
text := ""
|
||||
if n.Data == "h1" || n.Data == "h2" || n.Data == "h3" ||
|
||||
n.Data == "p" || n.Data == "td" || n.Data == "th" ||
|
||||
n.Type == html.TextNode {
|
||||
text = getText(n)
|
||||
}
|
||||
|
||||
node := &Node{
|
||||
Tag: n.Data,
|
||||
Text: text,
|
||||
Attr: map[string]string{},
|
||||
}
|
||||
|
||||
for _, a := range n.Attr {
|
||||
node.Attr[a.Key] = a.Val
|
||||
}
|
||||
|
||||
if s, ok := node.Attr["style"]; ok {
|
||||
node.Style = parseStyle(s)
|
||||
}
|
||||
xPadding := node.Style.PaddingLeft + node.Style.PaddingRight
|
||||
yPadding := node.Style.PaddingTop + node.Style.PaddingBottom
|
||||
fontSize := node.Style.FontSize
|
||||
h := nodeHeight(n, dc, canvasWidth, int(xPadding), int(yPadding),
|
||||
face, fontSize, node.getHeight())
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
child, ch := BuildTree(c, canvasWidth, dc, face)
|
||||
if child != nil {
|
||||
node.Children = append(node.Children, child)
|
||||
}
|
||||
h += ch
|
||||
}
|
||||
return node, h
|
||||
}
|
||||
69
cmd/main.go
Normal file
69
cmd/main.go
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
"image"
|
||||
@@ -91,7 +91,8 @@ func toMonochrome(img image.Image) *image.Gray {
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
grayValue := uint8((r + g + b) / 3 >> 8)
|
||||
// grayValue := uint8((r + g + b) / 3 >> 8)
|
||||
grayValue := uint8((0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)) / 256.0)
|
||||
if grayValue > 128 {
|
||||
gray.Set(x, y, color.White)
|
||||
} else {
|
||||
@@ -138,7 +139,6 @@ func escposRaster(img *image.Gray) []byte {
|
||||
data = append(data, b)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
10
go.mod
10
go.mod
@@ -1,16 +1,16 @@
|
||||
module libgofunc
|
||||
module gt.mokkon.com/sainw/libgofunc
|
||||
|
||||
go 1.22.0
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/dlclark/regexp2 v1.11.5
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc
|
||||
golang.org/x/image v0.24.0
|
||||
golang.org/x/net v0.34.0
|
||||
golang.org/x/image v0.38.0
|
||||
golang.org/x/net v0.52.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
)
|
||||
|
||||
12
go.sum
12
go.sum
@@ -6,9 +6,9 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc h1:4JwmN2Scz1vR+hfSxkdy2IE/DzxX2Cftm2lhWHyN0k0=
|
||||
github.com/kenshaw/escpos v0.0.0-20221114190919-df06b682a8fc/go.mod h1:M+GIBmg2MqaSWIJrXCZS+/wRFbr9fOguRz3SHn8DRPE=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
|
||||
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
|
||||
416
img.go
416
img.go
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
/*
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"image"
|
||||
_ "image/png"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"embed"
|
||||
@@ -25,21 +24,35 @@ import (
|
||||
|
||||
"github.com/kenshaw/escpos"
|
||||
)
|
||||
import (
|
||||
"log"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
const (
|
||||
canvasWidth = 550
|
||||
padding = 5
|
||||
lineGap = 12
|
||||
defalutFontSize = 18.0
|
||||
defalutTableBorder = 5
|
||||
lineGap = 12
|
||||
elePSize = 18
|
||||
eleH1Size = 32
|
||||
eleH2Size = 24
|
||||
eleH3Size = 19
|
||||
)
|
||||
|
||||
//go:embed static/*
|
||||
var fontFs embed.FS
|
||||
|
||||
//export GenPNG
|
||||
func GenPNG(outputPath *C.char, payload *C.char, tmpl *C.char) *C.char {
|
||||
func GenPNG(width C.int, outputPath *C.char, payload *C.char, tmpl *C.char, workingDirC *C.char) *C.char {
|
||||
canvasWidth := int(width)
|
||||
goPath := C.GoString(outputPath)
|
||||
goPayload := C.GoString(payload)
|
||||
goTmpl := C.GoString(tmpl)
|
||||
workingDir := C.GoString(workingDirC)
|
||||
|
||||
data := make(map[string]interface{})
|
||||
err := json.Unmarshal([]byte(goPayload), &data)
|
||||
@@ -58,42 +71,32 @@ func GenPNG(outputPath *C.char, payload *C.char, tmpl *C.char) *C.char {
|
||||
return NewErr(err)
|
||||
}
|
||||
|
||||
fontBytes, err := fontFs.ReadFile("static/Zawgyi-One.ttf")
|
||||
if err != nil {
|
||||
return NewErr(err)
|
||||
}
|
||||
ttfFont, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
return NewErr(err)
|
||||
}
|
||||
|
||||
face, err := opentype.NewFace(ttfFont, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
face, err := LoadFont(defalutFontSize)
|
||||
if err != nil {
|
||||
return NewErr(err)
|
||||
}
|
||||
|
||||
// First pass: compute required height
|
||||
dummyDC := gg.NewContext(canvasWidth, 150)
|
||||
dummyDC.SetFontFace(face)
|
||||
dummyDC.SetFontFace(*face)
|
||||
bodyNode := findNode(root, "body")
|
||||
if bodyNode == nil {
|
||||
bodyNode = root
|
||||
}
|
||||
|
||||
computedHeight := computeHeight(bodyNode, padding, dummyDC, face)
|
||||
body, height := BuildTree(bodyNode, canvasWidth, dummyDC, face)
|
||||
|
||||
// Second pass: actual rendering
|
||||
dc := gg.NewContext(canvasWidth, computedHeight+50)
|
||||
dc := gg.NewContext(canvasWidth, height)
|
||||
dc.SetRGB(1, 1, 1)
|
||||
dc.Clear()
|
||||
dc.SetFontFace(face)
|
||||
dc.SetFontFace(*face)
|
||||
|
||||
y := padding
|
||||
renderNode(dc, bodyNode, &y, face)
|
||||
y := 0
|
||||
err = renderNode(dc, canvasWidth, body, &y, *face, workingDir)
|
||||
if err != nil {
|
||||
return NewErr(err)
|
||||
}
|
||||
|
||||
err = dc.SavePNG(goPath)
|
||||
if err != nil {
|
||||
@@ -104,8 +107,22 @@ func GenPNG(outputPath *C.char, payload *C.char, tmpl *C.char) *C.char {
|
||||
return NewOk(nil)
|
||||
}
|
||||
|
||||
func GenImg(width int, outputPath, payload, tmpl, workingDir string) string {
|
||||
result := GenPNG(C.int(width), C.CString(outputPath), C.CString(payload), C.CString(tmpl), C.CString(workingDir))
|
||||
r := C.GoString(result)
|
||||
return r
|
||||
}
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"formatNumber": FormatNumber,
|
||||
"div": Div,
|
||||
}
|
||||
|
||||
func renderTemplate(tmp string, data map[string]interface{}) (string, error) {
|
||||
tmpl := template.Must(template.New("mytemplate").Parse(tmp))
|
||||
tmpl, err := template.New("mytemplate").Funcs(funcMap).Parse(tmp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := tmpl.Execute(&buf, data); err != nil {
|
||||
return "", err
|
||||
@@ -113,34 +130,55 @@ func renderTemplate(tmp string, data map[string]interface{}) (string, error) {
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func renderNode(dc *gg.Context, n *html.Node, y *int, face font.Face) {
|
||||
if n.Type == html.ElementNode {
|
||||
switch n.Data {
|
||||
case "h1":
|
||||
drawTextBlock(dc, getText(n), 28, y, face)
|
||||
case "p":
|
||||
drawTextBlock(dc, getText(n), 24, y, face)
|
||||
case "img":
|
||||
drawImage(dc, n, y)
|
||||
case "table":
|
||||
renderTable(dc, n, y, face)
|
||||
func renderNode(dc *gg.Context, canvasWidth int, n *Node, y *int, face font.Face, workingDir string) error {
|
||||
before := *y
|
||||
if n.Style.PaddingTop > 0 {
|
||||
*y += int(n.Style.PaddingTop)
|
||||
}
|
||||
switch n.Tag {
|
||||
case "h1":
|
||||
drawTextBlock(dc, n, canvasWidth, eleH1Size, y, face)
|
||||
case "h2":
|
||||
drawTextBlock(dc, n, canvasWidth, eleH2Size, y, face)
|
||||
case "h3":
|
||||
drawTextBlock(dc, n, canvasWidth, eleH3Size, y, face)
|
||||
case "p":
|
||||
size := float64(elePSize)
|
||||
if n.Style.FontSize > 0 {
|
||||
size = n.Style.FontSize
|
||||
}
|
||||
drawTextBlock(dc, n, canvasWidth, size, y, face)
|
||||
case "hr":
|
||||
renderLine(dc, n, canvasWidth, y)
|
||||
case "img":
|
||||
if err := drawImage(dc, n, y, workingDir); err != nil {
|
||||
return err
|
||||
}
|
||||
case "table":
|
||||
renderTable(dc, canvasWidth, n, y, face)
|
||||
}
|
||||
if n.Style.PaddingBottom > 0 {
|
||||
*y += int(n.Style.PaddingBottom)
|
||||
}
|
||||
log.Printf("render %s y, y', height: %d, %d, %d\n", n.Tag, before, *y, *y-before)
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
renderNode(dc, c, y, face)
|
||||
for _, c := range n.Children {
|
||||
renderNode(dc, canvasWidth, c, y, face, workingDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func drawTextBlock(dc *gg.Context, text string, size float64, y *int, face font.Face) {
|
||||
func drawTextBlock(dc *gg.Context, n *Node, canvasWidth int, size float64, y *int, face font.Face) {
|
||||
dc.SetFontFace(face)
|
||||
lines := wordWrap(dc, text, canvasWidth-padding*2)
|
||||
padding := n.Style.PaddingLeft + n.Style.PaddingRight
|
||||
lines := wordWrap(dc, n.Text, canvasWidth-int(padding))
|
||||
SetFontSize(dc, size)
|
||||
for _, line := range lines {
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.DrawString(line, padding, float64(*y))
|
||||
dc.DrawStringAnchored(line, padding, float64(*y), 0, 0.5)
|
||||
// dc.DrawString(line, padding, float64(*y))
|
||||
*y += int(size) + lineGap
|
||||
}
|
||||
*y += 5
|
||||
}
|
||||
|
||||
func wordWrap(dc *gg.Context, text string, maxWidth int) []string {
|
||||
@@ -164,53 +202,116 @@ func wordWrap(dc *gg.Context, text string, maxWidth int) []string {
|
||||
return lines
|
||||
}
|
||||
|
||||
func drawImage(dc *gg.Context, n *html.Node, y *int) {
|
||||
src := getAttr(n, "src")
|
||||
file, err := os.Open(src)
|
||||
func drawImage(dc *gg.Context, n *Node, y *int, workingDir string) error {
|
||||
src := n.getSrc()
|
||||
s := path.Join(workingDir, src)
|
||||
file, err := os.Open(s)
|
||||
if err != nil {
|
||||
return
|
||||
return fmt.Errorf("open file src: '%s', working directory: '%s', error : %s", src, workingDir, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, _ := image.Decode(file)
|
||||
widthStr := getAttr(n, "width")
|
||||
if widthStr != "" {
|
||||
w, _ := strconv.Atoi(widthStr)
|
||||
scale := float64(w) / float64(img.Bounds().Dx())
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
padding := n.Style.PaddingLeft
|
||||
h := n.Style.Height
|
||||
if n.Style.Width > 0 {
|
||||
ix := padding
|
||||
iy := float64(*y)
|
||||
scale := n.Style.Width / float64(img.Bounds().Dx())
|
||||
dc.Push()
|
||||
dc.Scale(scale, scale)
|
||||
dc.DrawImage(img, padding, *y)
|
||||
if scale > 0 {
|
||||
ix = ix / scale
|
||||
iy = iy / scale
|
||||
}
|
||||
dc.DrawImage(img, int(ix), int(iy))
|
||||
dc.Pop()
|
||||
*y += int(float64(img.Bounds().Dy()) * scale)
|
||||
if float64(img.Bounds().Dy())*scale > h {
|
||||
h = float64(img.Bounds().Dy()) * scale
|
||||
}
|
||||
*y += int(h)
|
||||
} else {
|
||||
dc.DrawImage(img, padding, *y)
|
||||
dc.DrawImage(img, int(padding), *y)
|
||||
*y += img.Bounds().Dy()
|
||||
}
|
||||
*y += 10
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderTable(dc *gg.Context, table *html.Node, y *int, face font.Face) {
|
||||
rows := extractRows(table)
|
||||
func renderTable(dc *gg.Context, canvasWidth int, table *Node, y *int, face font.Face) {
|
||||
headers, rows := extractRows(table)
|
||||
if len(rows) == 0 {
|
||||
return
|
||||
}
|
||||
fontSize := defalutFontSize
|
||||
|
||||
if table.Style.FontSize > 0 {
|
||||
fontSize = table.Style.FontSize
|
||||
}
|
||||
SetFontSize(dc, fontSize)
|
||||
padding := int(table.Style.PaddingLeft + table.Style.PaddingRight)
|
||||
colCount := len(rows[0])
|
||||
cellWidth := (canvasWidth - padding*2) / colCount
|
||||
dc.SetFontFace(face)
|
||||
cellWidth := (canvasWidth - padding) / colCount
|
||||
border := table.Style.Border
|
||||
|
||||
for _, row := range rows {
|
||||
x := padding
|
||||
for _, cell := range row {
|
||||
dc.DrawRectangle(float64(x), float64(*y), float64(cellWidth), 30)
|
||||
for i, cell := range row {
|
||||
header := headers[i]
|
||||
if border > 0 {
|
||||
dc.SetLineWidth(border)
|
||||
dc.DrawRectangle(float64(x), float64(*y), float64(cellWidth), fontSize+defalutTableBorder)
|
||||
}
|
||||
dc.Stroke()
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.DrawString(cell, float64(x+8), float64(*y+20))
|
||||
x += cellWidth
|
||||
dc.DrawStringAnchored(cell, float64(x+8), float64(*y+20), 0, 0)
|
||||
if w := header.Style.Width; w > 0 {
|
||||
x += int(w)
|
||||
} else {
|
||||
x += cellWidth
|
||||
}
|
||||
}
|
||||
*y += 30
|
||||
*y += int(fontSize) + defalutTableBorder
|
||||
}
|
||||
*y += 10
|
||||
}
|
||||
|
||||
func extractRows(table *html.Node) [][]string {
|
||||
func renderLine(dc *gg.Context, line *Node, canvasWidth int, y *int) {
|
||||
height := line.Style.Height
|
||||
xPadding := line.Style.PaddingLeft + line.Style.PaddingRight
|
||||
dc.SetLineWidth(height)
|
||||
dc.DrawLine(line.Style.PaddingLeft, float64(*y), float64(canvasWidth)-xPadding, float64(*y))
|
||||
*y += int(height)
|
||||
}
|
||||
|
||||
func extractRows(table *Node) ([]*Node, [][]string) {
|
||||
var rows [][]string
|
||||
var headers []*Node
|
||||
var traverse func(*Node)
|
||||
traverse = func(n *Node) {
|
||||
if n.Tag == "tr" {
|
||||
var row []string
|
||||
for _, td := range n.Children {
|
||||
if td.Tag == "td" || td.Tag == "th" {
|
||||
row = append(row, td.Text)
|
||||
}
|
||||
if td.Tag == "th" {
|
||||
headers = append(headers, td)
|
||||
}
|
||||
}
|
||||
if len(row) > 0 {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
}
|
||||
for _, c := range n.Children {
|
||||
traverse(c)
|
||||
}
|
||||
}
|
||||
traverse(table)
|
||||
return headers, rows
|
||||
}
|
||||
|
||||
func extractNodeRows(table *html.Node) [][]string {
|
||||
var rows [][]string
|
||||
var traverse func(*html.Node)
|
||||
traverse = func(n *html.Node) {
|
||||
@@ -247,15 +348,6 @@ func getText(n *html.Node) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func getAttr(n *html.Node, key string) string {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == key {
|
||||
return a.Val
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findNode(n *html.Node, tag string) *html.Node {
|
||||
if n.Type == html.ElementNode && n.Data == tag {
|
||||
return n
|
||||
@@ -268,36 +360,6 @@ func findNode(n *html.Node, tag string) *html.Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeHeight(n *html.Node, currentY int, dc *gg.Context, face font.Face) int {
|
||||
y := currentY
|
||||
if n.Type == html.ElementNode {
|
||||
switch n.Data {
|
||||
case "h1":
|
||||
dc.SetFontFace(face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-padding*2)
|
||||
y += len(lines)*28 + len(lines)*lineGap + 5
|
||||
case "p":
|
||||
dc.SetFontFace(face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-padding*2)
|
||||
y += len(lines)*18 + len(lines)*lineGap + 5
|
||||
case "table":
|
||||
rows := extractRows(n)
|
||||
y += len(rows)*30 + 10
|
||||
case "img":
|
||||
widthStr := getAttr(n, "width")
|
||||
h := 100
|
||||
if widthStr != "" {
|
||||
h = 100
|
||||
}
|
||||
y += h + 10
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
y = computeHeight(c, y, dc, face)
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func printImg(prt *escpos.Escpos, imgPath string) error {
|
||||
imgFile, err := os.Open(imgPath)
|
||||
if err != nil {
|
||||
@@ -311,10 +373,148 @@ func printImg(prt *escpos.Escpos, imgPath string) error {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
data := []byte{0x1D, 0x4C, 0x00, 0x00}
|
||||
_, err = prt.WriteRaw(data)
|
||||
if err != nil {
|
||||
fmt.Println("error 0x1D, 0x4C:", err.Error())
|
||||
}
|
||||
|
||||
gray := toMonochrome(img)
|
||||
data := escposRaster(gray)
|
||||
data = escposRaster(gray)
|
||||
|
||||
_, err = prt.WriteRaw(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func LoadFont(size float64) (*font.Face, error) {
|
||||
fontBytes, err := fontFs.ReadFile("static/Zawgyi-One.ttf")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ttfFont, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
face, err := opentype.NewFace(ttfFont, &opentype.FaceOptions{
|
||||
Size: size,
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &face, nil
|
||||
}
|
||||
|
||||
func SetFontSize(dc *gg.Context, size float64) {
|
||||
if size < 5 {
|
||||
return
|
||||
}
|
||||
face, err := LoadFont(size)
|
||||
if err != nil {
|
||||
log.Println("SetFontSize: ", err.Error())
|
||||
}
|
||||
dc.SetFontFace(*face)
|
||||
}
|
||||
|
||||
func nodeHeight(n *html.Node, dc *gg.Context, canvasWidth int, xPadding, yPadding int, face *font.Face, fontSize float64, implicit int) int {
|
||||
y := 0
|
||||
if n.Type == html.ElementNode {
|
||||
h := 0
|
||||
switch n.Data {
|
||||
case "h1":
|
||||
dc.SetFontFace(*face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-xPadding)
|
||||
h = (len(lines) * eleH1Size) + (len(lines) * lineGap) + yPadding
|
||||
case "h2":
|
||||
dc.SetFontFace(*face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-xPadding)
|
||||
h = (len(lines) * eleH2Size) + (len(lines) * lineGap) + yPadding
|
||||
case "h3":
|
||||
dc.SetFontFace(*face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-xPadding)
|
||||
h = (len(lines) * eleH3Size) + (len(lines) * lineGap) + yPadding
|
||||
case "p":
|
||||
dc.SetFontFace(*face)
|
||||
lines := wordWrap(dc, getText(n), canvasWidth-xPadding)
|
||||
h = (len(lines) * elePSize) + (len(lines) * lineGap) + yPadding
|
||||
case "hr":
|
||||
h = yPadding
|
||||
case "table":
|
||||
rows := extractNodeRows(n)
|
||||
h = (len(rows) * int(fontSize+defalutTableBorder)) + yPadding
|
||||
case "img":
|
||||
h = implicit + yPadding
|
||||
}
|
||||
if implicit > h {
|
||||
h = implicit
|
||||
}
|
||||
y += h
|
||||
log.Printf("nodeHeight %s height: %d, \n", n.Data, y)
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
type RealNumber interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
func FormatNumber(precision int, b any) string {
|
||||
return GenericFormatNumber(precision, convertToFloat64(b))
|
||||
}
|
||||
|
||||
func GenericFormatNumber[T RealNumber](precision int, v T) string {
|
||||
pEnglish := message.NewPrinter(language.English)
|
||||
|
||||
n := float64(v)
|
||||
s := fmt.Sprintf("%.*f", precision, n)
|
||||
|
||||
parts := strings.Split(s, ".")
|
||||
intPart := parts[0]
|
||||
i, err := strconv.Atoi(intPart)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
out := pEnglish.Sprintf("%d", i)
|
||||
|
||||
if len(parts) > 1 {
|
||||
return out + "." + parts[1]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func Div(a, b any) float64 {
|
||||
return GenericDiv(convertToFloat64(a), convertToFloat64(b))
|
||||
}
|
||||
|
||||
func GenericDiv[T RealNumber](a, b T) float64 {
|
||||
floatB := float64(b)
|
||||
if floatB == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(a) / floatB
|
||||
}
|
||||
|
||||
func convertToFloat64(v any) float64 {
|
||||
switch t := v.(type) {
|
||||
case float64:
|
||||
return t
|
||||
case float32:
|
||||
return float64(t)
|
||||
case int:
|
||||
return float64(t)
|
||||
case int64:
|
||||
return float64(t)
|
||||
case int32:
|
||||
return float64(t)
|
||||
case uint:
|
||||
return float64(t)
|
||||
case uint64:
|
||||
return float64(t)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
60
img_test.go
Normal file
60
img_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDiv(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a any
|
||||
b any
|
||||
want float64
|
||||
}{
|
||||
{"Float division", 10.5, 2.0, 5.25},
|
||||
{"Float division", 4975, 1000, 4.975},
|
||||
{"Integer division", 10, 4, 2.5},
|
||||
{"Mixed types", int64(100), float64(4.0), 25.0},
|
||||
{"Division by zero (float)", 10.0, 0.0, 0.0},
|
||||
{"Division by zero (int)", 5, 0, 0.0},
|
||||
{"Unsupported type defaults to zero", "string", 2, 0.0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Div(tt.a, tt.b)
|
||||
if got != tt.want {
|
||||
t.Errorf("TemplateDiv() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatNumber(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
precision int
|
||||
val any
|
||||
want string
|
||||
}{
|
||||
{"Standard float with commas", 2, 1234567.891, "1,234,567.89"},
|
||||
{"Standard float with commas", 3, 4.975, "4.975"},
|
||||
{"Integer input with commas", 0, 5000000, "5,000,000"},
|
||||
{"Integer input forced decimals", 2, 5000, "5,000.00"},
|
||||
{"Negative float commas", 2, -9876543.21, "-9,876,543.21"},
|
||||
{"Small float rounding up", 2, 0.128, "0.13"},
|
||||
{"Small float rounding down", 2, 0.123, "0.12"},
|
||||
{"Handling NaN", 2, math.NaN(), ""},
|
||||
{"Handling Inf", 2, math.Inf(1), ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := FormatNumber(tt.precision, tt.val)
|
||||
if got != tt.want {
|
||||
t.Errorf("TemplateFloat() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
51
main.go
51
main.go
@@ -1,51 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
/*
|
||||
#include <stdint.h>
|
||||
*/
|
||||
"C"
|
||||
"fmt"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
//export Sum
|
||||
func Sum(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
func main() {
|
||||
payload := `{"Name":"Ko Myo","Amount":3000}`
|
||||
const temp = `
|
||||
<table>
|
||||
<tr>
|
||||
<th><img src="static/logo.png" width="80"/></th>
|
||||
<th><h1 style="font-size:28">မြန်မာစာသည်တို့စာ (Invoice)</h1></th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>မင်္ဂလာပါ {{.Name}}, သင်၏ အိုင်ဗွိုင်းစိန်း အချက်အလက်များပါသည်။</p>
|
||||
<p>အထက်ပါ အကွက်နှစ်ကွက်ကတော့ အချိန်နဲ့ တပြေးညီ ဖောင့်ပြောင်းပေးတဲ့ မြန်မာဖောင့် ကွန်ဗာတာပဲ ဖြစ်ပါတယ်။ စာရိုက်ထည့်တာနဲ့ဖြစ်ဖြစ် ဒါမှမဟုတ် ကူးထည့်တာနဲ့ဖြစ်ဖြစ် မြန်မာဖောင့် တစ်ခုကနေ တစ်ခုကို ပြောင်းပေးပါတယ်။ မြန်မာ ယူနီကုဒ်ကနေ ပြောင်းချင်တယ်ဆို မြန်မာ ယူနီကုဒ်ဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်လိုက်တာနဲ့ ဇော်ဂျီဝမ်းဘက်မှာ ဇော်ဂျီဖောင့်ကိုပြောင်းပြီးသား တိုက်ရိုက်ထွက်လာပါမယ်။ အပြန်အလှန်ပါပဲ၊ ဇော်ဂျီကနေပြောင်းချင်တယ်ဆိုရင် ဇော်ဂျီဝမ်းဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်တာနဲ့ မြန်မာ ယူနီကုဒ်ဖောင့်ကို ပြောင်းပြီးသားက မြန်မာ ယူနီကုဒ်အကွက်ထဲမှာ ပေါ်လာမှာဖြစ်ပါတယ်။</p>
|
||||
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>ပစ္စည်း</th>
|
||||
<th>အရေအတွက်</th>
|
||||
<th>ဈေးနှုန်း</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hosting</td>
|
||||
<td>1</td>
|
||||
<td>{{.Amount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain registration</td>
|
||||
<td>1</td>
|
||||
<td>$15</td>
|
||||
</tr>
|
||||
</table>`
|
||||
result := GenPNG(C.CString("build/out.png"), C.CString(payload), C.CString(temp))
|
||||
goResult := C.GoString(result)
|
||||
fmt.Println("Result:", goResult)
|
||||
PrintImg(C.CString("usb:/dev/usb/lp1"), C.CString("build/out.png"))
|
||||
}
|
||||
51
printer.go
51
printer.go
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
/*
|
||||
@@ -17,12 +17,12 @@ import (
|
||||
|
||||
"github.com/kenshaw/escpos"
|
||||
)
|
||||
import "errors"
|
||||
|
||||
//export PrintImg
|
||||
func PrintImg(printer *C.char, imagePath *C.char) *C.char {
|
||||
goPrinter := C.GoString(printer)
|
||||
goImagePath := C.GoString(imagePath)
|
||||
|
||||
// printer := "tcp:192.168.100.151:9100"
|
||||
// printer := "usb:/dev/usb/lp1"
|
||||
var w *bufio.ReadWriter
|
||||
@@ -44,18 +44,63 @@ func PrintImg(printer *C.char, imagePath *C.char) *C.char {
|
||||
}
|
||||
defer f.Close()
|
||||
w = bufio.NewReadWriter(bufio.NewReader(f), bufio.NewWriter(f))
|
||||
// } else if strings.HasPrefix(goPrinter, "int:") {
|
||||
// ctx := gousb.NewContext()
|
||||
// // location := strings.TrimLeft(goPrinter, "int:")
|
||||
// targetBus := 1
|
||||
// targetAddr := 5
|
||||
// devs, err := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool {
|
||||
// return int(desc.Bus) == targetBus && int(desc.Address) == targetAddr
|
||||
// })
|
||||
// if err != nil || len(devs) == 0 {
|
||||
// log.Fatal("Could not find or open the device")
|
||||
// }
|
||||
// dev := devs[0]
|
||||
// defer dev.Close()
|
||||
// dev.SetAutoDetach(true)
|
||||
|
||||
// // 2. Claim the default interface (usually 0 for printers)
|
||||
// // Note: This may require detaching the kernel driver on Linux
|
||||
// intf, done, err := dev.DefaultInterface()
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to claim interface: %v", err)
|
||||
// }
|
||||
// defer done()
|
||||
|
||||
// // 3. Open the Bulk Output Endpoint (usually endpoint #1 or #2)
|
||||
// // You may need to inspect desc.Endpoints to find the correct Bulk Out ID
|
||||
// out, err = intf.OutEndpoint(1)
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to open OUT endpoint: %v", err)
|
||||
// }
|
||||
// // w = bufio.NewReadWriter(bufio.NewReader(outPort), bufio.NewWriter(f))
|
||||
// rw := &USBReadWriter{out: out}
|
||||
// reader := bufio.NewReader(rw)
|
||||
// writer := bufio.NewWriter(rw)
|
||||
// w = bufio.NewReadWriter(reader, writer)
|
||||
} else {
|
||||
return NewErr(errors.New("invalid printer"))
|
||||
}
|
||||
prt := escpos.New(w)
|
||||
|
||||
prt.Init()
|
||||
prt.SetSmooth(1)
|
||||
prt.SetAlign("left")
|
||||
|
||||
err := printImg(prt, goImagePath)
|
||||
if err != nil {
|
||||
return NewErr(err)
|
||||
}
|
||||
prt.WriteRaw([]byte{0x1B, 0x64, 0x03})
|
||||
prt.Cut()
|
||||
prt.End()
|
||||
w.Flush()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1 * time.Second)
|
||||
return NewOk(nil)
|
||||
}
|
||||
|
||||
func Print(printer, imagePath string) string {
|
||||
result := PrintImg(C.CString(printer), C.CString(imagePath))
|
||||
r := C.GoString(result)
|
||||
return r
|
||||
}
|
||||
|
||||
8
vo.go
8
vo.go
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package libgofunc
|
||||
|
||||
import (
|
||||
/*
|
||||
@@ -10,13 +10,13 @@ import (
|
||||
)
|
||||
|
||||
type Reply struct {
|
||||
Status int `json:"status"`
|
||||
Status string `json:"status"` // "ok", "error"
|
||||
Err string `json:"err"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
func NewErr(err error) *C.char {
|
||||
e := Reply{Status: 1, Err: err.Error()}
|
||||
e := Reply{Status: "error", Err: err.Error()}
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Println("Error json.Marshal:", err.Error())
|
||||
@@ -25,7 +25,7 @@ func NewErr(err error) *C.char {
|
||||
}
|
||||
|
||||
func NewOk(data interface{}) *C.char {
|
||||
e := Reply{Status: 0, Result: data}
|
||||
e := Reply{Status: "ok", Result: data}
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Println("Error json.Marshal:", err.Error())
|
||||
|
||||
Reference in New Issue
Block a user