From ecb03c1fb7ce8d6eb33314aa6d2aa0ed36195835 Mon Sep 17 00:00:00 2001 From: sainw Date: Wed, 8 Apr 2026 15:31:23 +0630 Subject: [PATCH] fix html to png --- .gitignore | 3 +- .vscode/launch.json | 15 +++ canvas.go | 141 +++++++++++++++++++++ cmd/main.go | 109 ++++++++++++++++ escpos_printer.go | 2 +- go.mod | 40 +++++- go.sum | 134 ++++++++++++++++++++ img.go | 293 +++++++++++++++++++++++++++++--------------- main.go | 51 -------- printer.go | 65 +++++++++- rabbit.go | 2 +- vo.go | 2 +- 12 files changed, 694 insertions(+), 163 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 canvas.go create mode 100644 cmd/main.go delete mode 100644 main.go diff --git a/.gitignore b/.gitignore index 3acef89..6561e4f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ go.work.sum .env build -assets \ No newline at end of file +assets +libgofunc diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..adb394e --- /dev/null +++ b/.vscode/launch.json @@ -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", + }, + ] +} \ No newline at end of file diff --git a/canvas.go b/canvas.go new file mode 100644 index 0000000..85f3a39 --- /dev/null +++ b/canvas.go @@ -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 +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..13e367b --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,109 @@ +package main + +import ( + /* + #include + */ + "C" + "fmt" + _ "image/png" + + "github.com/google/gousb" +) +import ( + "github.com/google/gousb/usbid" + "gt.mokkon.com/sainw/libgofunc" +) + +//export Sum +func Sum(a, b int) int { + return a + b +} + +func main() { + payload := `{"Name":"Ko Myo","Amount":3000}` + const temp = ` + +

New Day Energy

+ +

Address: မင်္ဂလာပါ {{.Name}}

+

Receipt: RCPT001

+

Phone: 0977777777

+

Date: 4 Jan 2026 15:38:38

+

Car No.: 3J/3883

+

Casher: မနှင်းနှင်း

+

MOP: B2B

+

Tier: 1

+

Deal No.: RR

+
+ + + + + + + + + + + + + + + + + + +
ItemPumpPriceLiterGallonAmount
95 RONP82,0003.250.7156,500
+ +

+စက်သုံးဆီ အရေအတွက် နှင့် အရည်အသွေးစျေးနှုန်းများ အားသံသယရှိပါက ph 09450539099, 09765421033, 09765421029 သို့တိုင်ကြား နိုင်ပါသည်။

+` + result := libgofunc.GenImg(550, "./out.png", payload, temp) + fmt.Println("Result:", result) + // PrintImg(C.CString("usb:/dev/usb/lp1"), C.CString("build/out.png")) + // libgofunc.Print("int:/dev/bus/usb/001/046", "./out.png") + libgofunc.Print("tcp:192.168.100.151:9100", "./out.png") + // printer := "tcp:192.168.100.151:9100" + // ListUSB() +} + +func ListUSB() { + ctx := gousb.NewContext() + defer ctx.Close() + + // OpenDevices returns all devices that return 'true' in the filter + devices, _ := ctx.OpenDevices(func(desc *gousb.DeviceDesc) bool { + // fmt.Println(usbid.Describe(desc)) + + // path := fmt.Sprintf("/dev/bus/usb/%03d/%03d", desc.Bus, desc.Address) + // fmt.Printf("Device Path: %s\n", path) + + // switch desc.Class { + // case 7: + // fmt.Println("Found a standard printer") + // case 0xFF: + // fmt.Println("Found a vendor-specific device (likely your printer)") + // } + + return true // Open every device to read its name + }) + defer func() { + for _, d := range devices { + + d.Close() + } + }() + + for _, d := range devices { + fmt.Println(usbid.Describe(d.Desc)) + fmt.Printf("Bus: %d, Address: %d\n", d.Desc.Bus, d.Desc.Address) + // Read human-readable names from the device strings + manufacturer, _ := d.Manufacturer() + product, _ := d.Product() + serial, _ := d.SerialNumber() + // d.Desc.Path + fmt.Printf("ID: %s:%s | Manufacturer: %s | Product: %s | Serial: %s\n", + d.Desc.Vendor, d.Desc.Product, manufacturer, product, serial) + } +} diff --git a/escpos_printer.go b/escpos_printer.go index 46c323a..85d39a7 100644 --- a/escpos_printer.go +++ b/escpos_printer.go @@ -1,4 +1,4 @@ -package main +package libgofunc import ( "image" diff --git a/go.mod b/go.mod index dee8d4a..7010b5a 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,46 @@ -module libgofunc +module gt.mokkon.com/sainw/libgofunc -go 1.22.0 +go 1.25.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 ( + codeberg.org/go-latex/latex v0.2.0 // indirect + codeberg.org/go-pdf/fpdf v0.11.1 // indirect + github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 // indirect + github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 // indirect + github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect + github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect + github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f // indirect + github.com/Kagami/go-avif v0.1.0 // indirect + github.com/andybalholm/brotli v1.2.1 // indirect + github.com/benoitkugler/textlayout v0.3.2 // indirect + github.com/benoitkugler/textprocessing v0.0.6 // indirect + github.com/go-fonts/latin-modern v0.3.3 // indirect + github.com/go-text/typesetting v0.3.4 // indirect + github.com/kolesa-team/go-webp v1.0.5 // indirect + github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect + github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388 // indirect + github.com/tdewolff/font v0.0.0-20260314002930-9f995dac393e // indirect + github.com/tdewolff/minify/v2 v2.24.11 // indirect + github.com/tdewolff/parse/v2 v2.8.11 // indirect + github.com/wcharczuk/go-chart/v2 v2.1.2 // indirect + github.com/yuin/goldmark v1.8.2 // indirect + gonum.org/v1/plot v0.16.0 // indirect + modernc.org/knuth v0.5.5 // indirect + modernc.org/token v1.1.0 // indirect + star-tex.org/x/tex v0.7.1 // indirect ) require ( github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - golang.org/x/text v0.22.0 // indirect + github.com/google/gousb v1.1.3 + github.com/tdewolff/canvas v0.0.0-20260406091912-5d4f7059846e + golang.org/x/text v0.35.0 // indirect ) diff --git a/go.sum b/go.sum index c121f9c..5b1156d 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,148 @@ +codeberg.org/go-latex/latex v0.2.0 h1:Ol/a6VHY06N+5gPfewswymoRb5ZcKDXWVaVegcx4hbI= +codeberg.org/go-latex/latex v0.2.0/go.mod h1:VJAwQir7/T8LZxj7xAPivISKiVOwkMpQ8bTuPQ31X0Y= +codeberg.org/go-pdf/fpdf v0.11.1 h1:U8+coOTDVLxHIXZgGvkfQEi/q0hYHYvEHFuGNX2GzGs= +codeberg.org/go-pdf/fpdf v0.11.1/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= +github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk= +github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF7V5IcmiE2sMFV2q3J47BEirxbXJAdzA= +github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= +github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f h1:l7moT9o/v/9acCWA64Yz/HDLqjcRTvc0noQACi4MsJw= +github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f/go.mod h1:vIOkSdX3NDCPwgu8FIuTat2zDF0FPXXQ0RYFRy+oQic= +github.com/Kagami/go-avif v0.1.0 h1:8GHAGLxCdFfhpd4Zg8j1EqO7rtcQNenxIDerC/uu68w= +github.com/Kagami/go-avif v0.1.0/go.mod h1:OPmPqzNdQq3+sXm0HqaUJQ9W/4k+Elbc3RSfJUemDKA= +github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= +github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= +github.com/benoitkugler/textlayout v0.3.2 h1:Y/LTkwpQ9G0Fia9yhPmZA9IR5AnE8Cq30j3C+Gx5/IE= +github.com/benoitkugler/textlayout v0.3.2/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= +github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= +github.com/benoitkugler/textprocessing v0.0.6 h1:obkMyj62GEPg3xUVYqROlCN22z1OleuZm6ULqX9Om1g= +github.com/benoitkugler/textprocessing v0.0.6/go.mod h1:Io0gN08/PXEzrSOWFa88xHx2Xv3VjvLMY7H76YoI23A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/go-fonts/latin-modern v0.3.3 h1:g2xNgI8yzdNzIVm+qvbMryB6yGPe0pSMss8QT3QwlJ0= +github.com/go-fonts/latin-modern v0.3.3/go.mod h1:tHaiWDGze4EPB0Go4cLT5M3QzRY3peya09Z/8KSCrpY= +github.com/go-text/typesetting v0.3.4 h1:YYurUOtEb9kGSOz4uE3k4OpBGsp1dDL8+fjCeaFamAU= +github.com/go-text/typesetting v0.3.4/go.mod h1:4qZCQphq4KSgGTAeI0uMEkVbROgfah8BuyF5LRYr7XY= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gousb v1.1.3 h1:xt6M5TDsGSZ+rlomz5Si5Hmd/Fvbmo2YCJHN+yGaK4o= +github.com/google/gousb v1.1.3/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg= 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= +github.com/kolesa-team/go-webp v1.0.5 h1:GZQHJBaE8dsNKZltfwqsL0qVJ7vqHXsfA+4AHrQW3pE= +github.com/kolesa-team/go-webp v1.0.5/go.mod h1:QmJu0YHXT3ex+4SgUvs+a+1SFCDcCqyZg+LbIuNNTnE= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= +github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388 h1:ZdkidVdpLW13BQ9a+/3uerT2ezy9J7KQWH18JCfhDmI= +github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388/go.mod h1:C/WY5lmWfMtPFYYBTd3Lzdn4FTLr+RxlIeiBNye+/os= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tdewolff/canvas v0.0.0-20260406091912-5d4f7059846e h1:A8iHpv0I/IDv+i5YQ/km/UlqSmonRz4UNIrckFxYc+A= +github.com/tdewolff/canvas v0.0.0-20260406091912-5d4f7059846e/go.mod h1:xCHrGFuVb9eeblIAffMuLzKoRnYHQj8xZq8yZ7YQfEw= +github.com/tdewolff/font v0.0.0-20260314002930-9f995dac393e h1:20EEwnJWwKApfX5KttlWbIjgXrXa+HUvkiVUow1hdJ0= +github.com/tdewolff/font v0.0.0-20260314002930-9f995dac393e/go.mod h1:EFuulYdR5cCTF2zKMW6NxchFgd5xtEPSrjkD1MFWDpA= +github.com/tdewolff/minify/v2 v2.24.11 h1:JlANsiWaRBXedoYtsiZgY3YFkdr42oF32vp2SLgQKi4= +github.com/tdewolff/minify/v2 v2.24.11/go.mod h1:exq1pjdrh9uAICdfVKQwqz6MsJmWmQahZuTC6pTO6ro= +github.com/tdewolff/parse/v2 v2.8.11 h1:SGyjEy3xEqd+W9WVzTlTQ5GkP/en4a1AZNZVJ1cvgm0= +github.com/tdewolff/parse/v2 v2.8.11/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E= +github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= +github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 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/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/plot v0.16.0 h1:dK28Qx/Ky4VmPUN/2zeW0ELyM6ucDnBAj5yun7M9n1g= +gonum.org/v1/plot v0.16.0/go.mod h1:Xz6U1yDMi6Ni6aaXILqmVIb6Vro8E+K7Q/GeeH+Pn0c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/knuth v0.5.5 h1:6lap2U/ISm8aC/4NU58ALFCRllNPaK0EZcIGY/oDgUg= +modernc.org/knuth v0.5.5/go.mod h1:e5SBb35HQBj2aFwbBO3ClPcViLY3Wi0LzaOd7c/3qMk= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +star-tex.org/x/tex v0.7.1 h1:4qGAByRyY0WQsOjtcHlxz+FgrYxz8fzxIds2Gjepp5U= +star-tex.org/x/tex v0.7.1/go.mod h1:Y3y0U7sZTltTh/CDZIx0oAtMjG7eMaTuTtvDZGdyhJo= diff --git a/img.go b/img.go index ff785a0..7bc9ae8 100644 --- a/img.go +++ b/img.go @@ -1,4 +1,4 @@ -package main +package libgofunc import ( /* @@ -11,7 +11,6 @@ import ( "image" _ "image/png" "os" - "strconv" "strings" "embed" @@ -25,18 +24,24 @@ import ( "github.com/kenshaw/escpos" ) +import "log" 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) *C.char { + canvasWidth := int(width) goPath := C.GoString(outputPath) goPayload := C.GoString(payload) goTmpl := C.GoString(tmpl) @@ -58,42 +63,29 @@ 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 + renderNode(dc, canvasWidth, body, &y, *face) err = dc.SavePNG(goPath) if err != nil { @@ -104,6 +96,12 @@ func GenPNG(outputPath *C.char, payload *C.char, tmpl *C.char) *C.char { return NewOk(nil) } +func GenImg(width int, outputPath, payload, tmpl string) string { + result := GenPNG(C.int(width), C.CString(outputPath), C.CString(payload), C.CString(tmpl)) + r := C.GoString(result) + return r +} + func renderTemplate(tmp string, data map[string]interface{}) (string, error) { tmpl := template.Must(template.New("mytemplate").Parse(tmp)) var buf bytes.Buffer @@ -113,34 +111,48 @@ 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) { + 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": + drawTextBlock(dc, n, canvasWidth, elePSize, y, face) + case "hr": + renderLine(dc, n, canvasWidth, y) + case "img": + drawImage(dc, n, y) + 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) } } -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 +176,101 @@ 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") +func drawImage(dc *gg.Context, n *Node, y *int) { + src := n.getSrc() file, err := os.Open(src) if err != nil { return } defer file.Close() img, _, _ := image.Decode(file) - widthStr := getAttr(n, "width") - if widthStr != "" { - w, _ := strconv.Atoi(widthStr) - scale := float64(w) / float64(img.Bounds().Dx()) + 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 } -func renderTable(dc *gg.Context, table *html.Node, y *int, face font.Face) { +func renderTable(dc *gg.Context, canvasWidth int, table *Node, y *int, face font.Face) { 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) + 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)) + dc.DrawStringAnchored(cell, float64(x+8), float64(*y+20), 0, 0) 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) [][]string { + var rows [][]string + 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 len(row) > 0 { + rows = append(rows, row) + } + } + for _, c := range n.Children { + traverse(c) + } + } + traverse(table) + return rows +} + +func extractNodeRows(table *html.Node) [][]string { var rows [][]string var traverse func(*html.Node) traverse = func(n *html.Node) { @@ -247,15 +307,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 +319,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 { @@ -318,3 +339,73 @@ func printImg(prt *escpos.Escpos, imgPath string) error { _, 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 +} diff --git a/main.go b/main.go deleted file mode 100644 index 16d1b77..0000000 --- a/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - /* - #include - */ - "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 = ` - - - - - -

မြန်မာစာသည်တို့စာ (Invoice)

- -

မင်္ဂလာပါ {{.Name}}, သင်၏ အိုင်ဗွိုင်းစိန်း အချက်အလက်များပါသည်။

-

အထက်ပါ အကွက်နှစ်ကွက်ကတော့ အချိန်နဲ့ တပြေးညီ ဖောင့်ပြောင်းပေးတဲ့ မြန်မာဖောင့် ကွန်ဗာတာပဲ ဖြစ်ပါတယ်။ စာရိုက်ထည့်တာနဲ့ဖြစ်ဖြစ် ဒါမှမဟုတ် ကူးထည့်တာနဲ့ဖြစ်ဖြစ် မြန်မာဖောင့် တစ်ခုကနေ တစ်ခုကို ပြောင်းပေးပါတယ်။ မြန်မာ ယူနီကုဒ်ကနေ ပြောင်းချင်တယ်ဆို မြန်မာ ယူနီကုဒ်ဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်လိုက်တာနဲ့ ဇော်ဂျီဝမ်းဘက်မှာ ဇော်ဂျီဖောင့်ကိုပြောင်းပြီးသား တိုက်ရိုက်ထွက်လာပါမယ်။ အပြန်အလှန်ပါပဲ၊ ဇော်ဂျီကနေပြောင်းချင်တယ်ဆိုရင် ဇော်ဂျီဝမ်းဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်တာနဲ့ မြန်မာ ယူနီကုဒ်ဖောင့်ကို ပြောင်းပြီးသားက မြန်မာ ယူနီကုဒ်အကွက်ထဲမှာ ပေါ်လာမှာဖြစ်ပါတယ်။

- - - - - - - - - - - - - - - - - -
ပစ္စည်းအရေအတွက်ဈေးနှုန်း
Hosting1{{.Amount}}
Domain registration1$15
` - 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")) -} diff --git a/printer.go b/printer.go index a449bc3..d8e2396 100644 --- a/printer.go +++ b/printer.go @@ -1,4 +1,4 @@ -package main +package libgofunc import ( /* @@ -17,12 +17,33 @@ import ( "github.com/kenshaw/escpos" ) +import ( + "fmt" + + "github.com/google/gousb" +) + +type USBReadWriter struct { + out *gousb.OutEndpoint + in *gousb.InEndpoint // Optional, can be nil if you only write +} + +func (urw *USBReadWriter) Write(p []byte) (n int, err error) { + return urw.out.Write(p) +} + +func (urw *USBReadWriter) Read(p []byte) (n int, err error) { + if urw.in == nil { + return 0, fmt.Errorf("read not supported") + } + return urw.in.Read(p) +} //export PrintImg func PrintImg(printer *C.char, imagePath *C.char) *C.char { goPrinter := C.GoString(printer) goImagePath := C.GoString(imagePath) - + var out *gousb.OutEndpoint // printer := "tcp:192.168.100.151:9100" // printer := "usb:/dev/usb/lp1" var w *bufio.ReadWriter @@ -44,6 +65,40 @@ 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) } prt := escpos.New(w) @@ -59,3 +114,9 @@ func PrintImg(printer *C.char, imagePath *C.char) *C.char { time.Sleep(100 * time.Millisecond) return NewOk(nil) } + +func Print(printer, imagePath string) string { + result := PrintImg(C.CString(printer), C.CString(imagePath)) + r := C.GoString(result) + return r +} diff --git a/rabbit.go b/rabbit.go index bc83a20..b0e2d8b 100644 --- a/rabbit.go +++ b/rabbit.go @@ -1,4 +1,4 @@ -package main +package libgofunc import ( "encoding/json" diff --git a/vo.go b/vo.go index d996cf1..360fcd2 100644 --- a/vo.go +++ b/vo.go @@ -1,4 +1,4 @@ -package main +package libgofunc import ( /*