diff --git a/.gitignore b/.gitignore
index 5b90e79..71ea9f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ go.work.sum
# env file
.env
+build
\ No newline at end of file
diff --git a/build_lib.sh b/build_lib.sh
new file mode 100755
index 0000000..ab0e716
--- /dev/null
+++ b/build_lib.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+export PATH=$HOME/apps/go1.22.12/bin:$PATH
+go version
+
+# need Android NDK
+NDK_HOME="$HOME/Android/Sdk/ndk/28.2.13676358" # <--- CHECK YOUR VERSION
+API=21
+
+TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
+
+echo "Building for Android x86_64..."
+CC="$TOOLCHAIN/x86_64-linux-android$API-clang" \
+CGO_ENABLED=1 GOOS=android GOARCH=amd64 \
+go build -buildmode=c-shared -o ./build/libgofunc_x64.so .
+
+echo "Building for Android ARM64..."
+CC="$TOOLCHAIN/aarch64-linux-android$API-clang" \
+CGO_ENABLED=1 GOOS=android GOARCH=arm64 \
+go build -buildmode=c-shared -o ./build/libgofunc_arm64.so .
+
+echo "Building for Android ARMv7..."
+CC="$TOOLCHAIN/armv7a-linux-androideabi$API-clang" \
+CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \
+go build -buildmode=c-shared -o ./build/libgofunc_armv7a.so .
+
+
+# echo "Building for iOS ARM64..."
+# CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
+# go build -buildmode=c-archive -o ./libgofunc_arm64.a .
+
+# cp ./libgofunc_x64.so /home/sainw/ws/forward_pos/android/app/src/main/jniLibs/x86_64/libgofuncso
+
+cp ./build/libgofunc_x64.so $HOME/ws/forward_pos/native/android/x86_64/libgofunc.so
+cp ./build/libgofunc_arm64.so $HOME/ws/forward_pos/native/android/arm64-v8a/libgofunc.so
+cp ./build/libgofunc_armv7a.so $HOME/ws/forward_pos/native/android/armeabi-v7a/libgofunc.so
+
+
+echo "Done!"
\ No newline at end of file
diff --git a/escpos_printer.go b/escpos_printer.go
new file mode 100644
index 0000000..46c323a
--- /dev/null
+++ b/escpos_printer.go
@@ -0,0 +1,192 @@
+package main
+
+import (
+ "image"
+ "image/color"
+ "io"
+)
+
+// Printer wraps an io.Writer (USB file, TCP conn, etc.)
+type Printer struct {
+ w io.Writer
+}
+
+func NewPrinter(w io.Writer) *Printer {
+ return &Printer{w: w}
+}
+
+func (p *Printer) Write(data []byte) error {
+ _, err := p.w.Write(data)
+ return err
+}
+
+// Print plain text
+func (p *Printer) PrintText(text string) error {
+ return p.Write([]byte(text))
+}
+
+// New line
+func (p *Printer) LF(num int) error {
+ for range num {
+ err := p.PrintText("\n")
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Bold text ON
+func (p *Printer) BoldOn() error {
+ return p.Write([]byte{0x1b, 0x45, 0x01})
+}
+
+// Bold text OFF
+func (p *Printer) BoldOff() error {
+ return p.Write([]byte{0x1b, 0x45, 0x00})
+}
+
+// Alignment: 0=left, 1=center, 2=right
+func (p *Printer) Align(n int) error {
+ return p.Write([]byte{0x1b, 0x61, byte(n)})
+}
+
+// Cut paper
+func (p *Printer) Cut() error {
+ return p.Write([]byte{0x1d, 0x56, 0x00})
+}
+
+// SelectFont chooses printer's built-in font (0=A, 1=B, 2=C if supported)
+func (p *Printer) SelectFont(n int) error {
+ return p.Write([]byte{0x1b, 0x4d, byte(n)})
+}
+
+// SetFontSize scales text (x, y from 1–8)
+func (p *Printer) SetFontSize(x, y int) error {
+ n := byte(((y - 1) << 4) | (x - 1))
+ return p.Write([]byte{0x1d, 0x21, n})
+}
+
+// Underline: 0=off, 1=thin, 2=thick
+func (p *Printer) Underline(n int) error {
+ return p.Write([]byte{0x1b, 0x2d, byte(n)})
+}
+
+// UpsideDown: 0=off, 1=on
+func (p *Printer) UpsideDown(n int) error {
+ return p.Write([]byte{0x1b, 0x7b, byte(n)})
+}
+
+// SetCharSpacing sets extra spacing between characters
+func (p *Printer) SetCharSpacing(n int) error {
+ return p.Write([]byte{0x1b, 0x20, byte(n)})
+}
+
+// ---------------- IMAGE PRINTING ----------------
+
+// convert to monochrome
+func toMonochrome(img image.Image) *image.Gray {
+ bounds := img.Bounds()
+ gray := image.NewGray(bounds)
+ 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)
+ if grayValue > 128 {
+ gray.Set(x, y, color.White)
+ } else {
+ gray.Set(x, y, color.Black)
+ }
+ }
+ }
+ return gray
+}
+
+// ESC/POS: GS v 0
+func escposRaster(img *image.Gray) []byte {
+ bounds := img.Bounds()
+ w := bounds.Dx()
+ h := bounds.Dy()
+
+ // Width must be in bytes (8 pixels per byte)
+ wBytes := (w + 7) / 8
+ data := []byte{}
+
+ // ESC/POS raster format: GS v 0 m xL xH yL yH [data]
+ // GS v 0
+ data = append(data, 0x1d, 0x76, 0x30, 0x00)
+
+ // xL, xH
+ data = append(data, byte(wBytes%256), byte(wBytes/256))
+ // yL, yH
+ data = append(data, byte(h%256), byte(h/256))
+
+ // Image data
+ for y := 0; y < h; y++ {
+ for xByte := 0; xByte < wBytes; xByte++ {
+ var b byte
+ for bit := 0; bit < 8; bit++ {
+ x := xByte*8 + bit
+ if x < w {
+ grayColor := img.GrayAt(x, y).Y
+ if grayColor < 128 {
+ // Black pixel = 1
+ b |= (1 << (7 - bit))
+ }
+ }
+ }
+ data = append(data, b)
+ }
+ }
+
+ return data
+}
+
+// PrintImage sends an image using GS v 0 (raster bit image)
+func (p *Printer) PrintImage(img image.Image) error {
+ gray := toMonochrome(img)
+ data := escposRaster(gray)
+ return p.Write(data)
+}
+
+// PrintBarcode prints a CODE128 barcode (most supported)
+func (p *Printer) PrintBarcode(data string) error {
+ // Select barcode height
+ p.Write([]byte{0x1d, 0x68, 100}) // height in dots
+ // Select barcode width
+ p.Write([]byte{0x1d, 0x77, 2}) // module width (2 = medium)
+ // HRI (human readable interpretation) below barcode
+ p.Write([]byte{0x1d, 0x48, 2})
+
+ // GS k m d1...dk NUL
+ cmd := []byte{0x1d, 0x6b, 73, byte(len(data))} // 73 = CODE128 auto
+ cmd = append(cmd, []byte(data)...)
+ return p.Write(cmd)
+}
+
+// ---------------- QR CODE ----------------
+
+// PrintQRCode prints a QR code
+func (p *Printer) PrintQRCode(data string, size int, ecLevel byte) error {
+ // size: module size (1–16)
+ // ecLevel: error correction (48=L, 49=M, 50=Q, 51=H)
+
+ // [1] Store QR data
+ storeLen := len(data) + 3
+ cmdStore := []byte{0x1d, 0x28, 0x6b, byte(storeLen % 256), byte(storeLen / 256), 49, 80, 48}
+ cmdStore = append(cmdStore, []byte(data)...)
+
+ // [2] Set module size
+ cmdSize := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 67, byte(size)}
+
+ // [3] Set error correction level
+ cmdErr := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 69, ecLevel}
+
+ // [4] Print
+ cmdPrint := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 81, 48}
+
+ p.Write(cmdSize)
+ p.Write(cmdErr)
+ p.Write(cmdStore)
+ return p.Write(cmdPrint)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..dee8d4a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,16 @@
+module libgofunc
+
+go 1.22.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
+)
+
+require (
+ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
+ golang.org/x/text v0.22.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..c121f9c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,14 @@
+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/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/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=
diff --git a/img.go b/img.go
new file mode 100644
index 0000000..a006c8f
--- /dev/null
+++ b/img.go
@@ -0,0 +1,317 @@
+package main
+
+import (
+ "C"
+ "bytes"
+ "fmt"
+ "html/template"
+ "image"
+ _ "image/png"
+ "os"
+ "strconv"
+ "strings"
+
+ "embed"
+
+ "github.com/fogleman/gg"
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/opentype"
+ "golang.org/x/net/html"
+
+ "encoding/json"
+
+ "github.com/kenshaw/escpos"
+)
+
+const (
+ canvasWidth = 550
+ padding = 5
+ lineGap = 12
+)
+
+//go:embed static/*
+var fontFs embed.FS
+
+//export GenPNG
+func GenPNG(outputPath *C.char, payload *C.char, tmpl *C.char) *C.char {
+ goPath := C.GoString(outputPath)
+ goPayload := C.GoString(payload)
+ goTmpl := C.GoString(tmpl)
+
+ data := make(map[string]interface{})
+ err := json.Unmarshal([]byte(goPayload), &data)
+ if err != nil {
+ return NewErr(err)
+ }
+
+ htmlStr, err := renderTemplate(goTmpl, data)
+ if err != nil {
+ return NewErr(err)
+ }
+
+ htmlStr = uni2zg(htmlStr)
+ root, err := html.Parse(strings.NewReader(htmlStr))
+ if err != nil {
+ 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,
+ })
+ if err != nil {
+ return NewErr(err)
+ }
+
+ // First pass: compute required height
+ dummyDC := gg.NewContext(canvasWidth, 150)
+ dummyDC.SetFontFace(face)
+ bodyNode := findNode(root, "body")
+ if bodyNode == nil {
+ bodyNode = root
+ }
+
+ computedHeight := computeHeight(bodyNode, padding, dummyDC, face)
+
+ // Second pass: actual rendering
+ dc := gg.NewContext(canvasWidth, computedHeight+50)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetFontFace(face)
+
+ y := padding
+ renderNode(dc, bodyNode, &y, face)
+
+ err = dc.SavePNG(goPath)
+ if err != nil {
+ return NewErr(err)
+ }
+
+ // PrintReceipt(goPath)
+ return NewOk(nil)
+}
+
+func renderTemplate(tmp string, data map[string]interface{}) (string, error) {
+ tmpl := template.Must(template.New("mytemplate").Parse(tmp))
+ var buf bytes.Buffer
+ if err := tmpl.Execute(&buf, data); err != nil {
+ return "", err
+ }
+ 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)
+ }
+ }
+
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ renderNode(dc, c, y, face)
+ }
+}
+
+func drawTextBlock(dc *gg.Context, text string, size float64, y *int, face font.Face) {
+ dc.SetFontFace(face)
+ lines := wordWrap(dc, text, canvasWidth-padding*2)
+ for _, line := range lines {
+ dc.SetRGB(0, 0, 0)
+ dc.DrawString(line, padding, float64(*y))
+ *y += int(size) + lineGap
+ }
+ *y += 5
+}
+
+func wordWrap(dc *gg.Context, text string, maxWidth int) []string {
+ words := strings.Fields(text)
+ if len(words) == 0 {
+ return []string{}
+ }
+ var lines []string
+ current := words[0]
+ for _, w := range words[1:] {
+ test := current + " " + w
+ wWidth, _ := dc.MeasureString(test)
+ if int(wWidth) > maxWidth {
+ lines = append(lines, current)
+ current = w
+ } else {
+ current = test
+ }
+ }
+ lines = append(lines, current)
+ return lines
+}
+
+func drawImage(dc *gg.Context, n *html.Node, y *int) {
+ src := getAttr(n, "src")
+ 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())
+ dc.Push()
+ dc.Scale(scale, scale)
+ dc.DrawImage(img, padding, *y)
+ dc.Pop()
+ *y += int(float64(img.Bounds().Dy()) * scale)
+ } else {
+ dc.DrawImage(img, padding, *y)
+ *y += img.Bounds().Dy()
+ }
+ *y += 10
+}
+
+func renderTable(dc *gg.Context, table *html.Node, y *int, face font.Face) {
+ rows := extractRows(table)
+ if len(rows) == 0 {
+ return
+ }
+ colCount := len(rows[0])
+ cellWidth := (canvasWidth - padding*2) / colCount
+ dc.SetFontFace(face)
+ for _, row := range rows {
+ x := padding
+ for _, cell := range row {
+ dc.DrawRectangle(float64(x), float64(*y), float64(cellWidth), 30)
+ dc.Stroke()
+ dc.SetRGB(0, 0, 0)
+ dc.DrawString(cell, float64(x+8), float64(*y+20))
+ x += cellWidth
+ }
+ *y += 30
+ }
+ *y += 10
+}
+
+func extractRows(table *html.Node) [][]string {
+ var rows [][]string
+ var traverse func(*html.Node)
+ traverse = func(n *html.Node) {
+ if n.Type == html.ElementNode && n.Data == "tr" {
+ var row []string
+ for td := n.FirstChild; td != nil; td = td.NextSibling {
+ if td.Type == html.ElementNode && (td.Data == "td" || td.Data == "th") {
+ row = append(row, getText(td))
+ }
+ }
+ if len(row) > 0 {
+ rows = append(rows, row)
+ }
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ traverse(c)
+ }
+ }
+ traverse(table)
+ return rows
+}
+
+func getText(n *html.Node) string {
+ if n == nil {
+ return ""
+ }
+ if n.Type == html.TextNode {
+ return strings.TrimSpace(n.Data)
+ }
+ var buf strings.Builder
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ buf.WriteString(getText(c))
+ }
+ 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
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if res := findNode(c, tag); res != nil {
+ return res
+ }
+ }
+ 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 {
+ fmt.Println(err)
+ return err
+ }
+
+ img, _, err := image.Decode(imgFile)
+ defer imgFile.Close()
+ if err != nil {
+ fmt.Println(err)
+ return err
+ }
+
+ gray := toMonochrome(img)
+ data := escposRaster(gray)
+
+ _, err = prt.WriteRaw(data)
+ return err
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..150ab27
--- /dev/null
+++ b/main.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "C"
+ "fmt"
+ _ "image/png"
+)
+
+func main() {
+ payload := `{"Name":"Ko Myo","Amount":3000}`
+ const temp = `
+
+
+ |
+မြန်မာစာသည်တို့စာ (Invoice) |
+
+
+
+မင်္ဂလာပါ {{.Name}}, သင်၏ အိုင်ဗွိုင်းစိန်း အချက်အလက်များပါသည်။
+အထက်ပါ အကွက်နှစ်ကွက်ကတော့ အချိန်နဲ့ တပြေးညီ ဖောင့်ပြောင်းပေးတဲ့ မြန်မာဖောင့် ကွန်ဗာတာပဲ ဖြစ်ပါတယ်။ စာရိုက်ထည့်တာနဲ့ဖြစ်ဖြစ် ဒါမှမဟုတ် ကူးထည့်တာနဲ့ဖြစ်ဖြစ် မြန်မာဖောင့် တစ်ခုကနေ တစ်ခုကို ပြောင်းပေးပါတယ်။ မြန်မာ ယူနီကုဒ်ကနေ ပြောင်းချင်တယ်ဆို မြန်မာ ယူနီကုဒ်ဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်လိုက်တာနဲ့ ဇော်ဂျီဝမ်းဘက်မှာ ဇော်ဂျီဖောင့်ကိုပြောင်းပြီးသား တိုက်ရိုက်ထွက်လာပါမယ်။ အပြန်အလှန်ပါပဲ၊ ဇော်ဂျီကနေပြောင်းချင်တယ်ဆိုရင် ဇော်ဂျီဝမ်းဘက်မှာ ရိုက်ထည့်၊ ကူးထည့်တာနဲ့ မြန်မာ ယူနီကုဒ်ဖောင့်ကို ပြောင်းပြီးသားက မြန်မာ ယူနီကုဒ်အကွက်ထဲမှာ ပေါ်လာမှာဖြစ်ပါတယ်။
+
+
+
+| ပစ္စည်း |
+အရေအတွက် |
+ဈေးနှုန်း |
+
+
+| Hosting |
+1 |
+{{.Amount}} |
+
+
+| Domain registration |
+1 |
+$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
new file mode 100644
index 0000000..4c915e5
--- /dev/null
+++ b/printer.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "C"
+ _ "image/png"
+ "os"
+ "strings"
+
+ "bufio"
+ "log"
+ "net"
+
+ "time"
+
+ "github.com/kenshaw/escpos"
+)
+
+//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
+ if strings.HasPrefix(goPrinter, "tcp:") {
+ location := strings.TrimLeft(goPrinter, "tcp:")
+ conn, err := net.Dial("tcp", location)
+ if err != nil {
+ log.Println("error dialing:", err.Error())
+ return NewErr(err)
+ }
+ defer conn.Close()
+ w = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
+ } else if strings.HasPrefix(goPrinter, "usb:") {
+ location := strings.TrimLeft(goPrinter, "usb:")
+ f, err := os.OpenFile(location, os.O_RDWR, os.ModeDevice)
+ if err != nil {
+ log.Println("error OpenFile usb:", err.Error())
+ return NewErr(err)
+ }
+ defer f.Close()
+ w = bufio.NewReadWriter(bufio.NewReader(f), bufio.NewWriter(f))
+ }
+ prt := escpos.New(w)
+
+ prt.Init()
+ prt.SetSmooth(1)
+ err := printImg(prt, goImagePath)
+ if err != nil {
+ return NewErr(err)
+ }
+ prt.Cut()
+ prt.End()
+ w.Flush()
+ time.Sleep(100 * time.Millisecond)
+ return NewOk(nil)
+}
diff --git a/rabbit.go b/rabbit.go
new file mode 100644
index 0000000..bc83a20
--- /dev/null
+++ b/rabbit.go
@@ -0,0 +1,833 @@
+package main
+
+import (
+ "encoding/json"
+
+ "github.com/dlclark/regexp2"
+)
+
+type RuleStruct struct {
+ From string
+ To string
+}
+
+func uni2zg(str string) string {
+ unizgRule := `[
+ {
+ "from": "\u1004\u103a\u1039",
+ "to": "\u1064"
+ },
+ {
+ "from": "\u1039\u1010\u103d",
+ "to": "\u1096"
+ },
+ {
+ "from": "\u102b\u103a",
+ "to": "\u105a"
+ },
+ {
+ "from": "\u102d\u1036",
+ "to": "\u108e"
+ },
+ {
+ "from": "\u104e\u1004\u103a\u1038",
+ "to": "\u104e"
+ },
+ {
+ "from": "[\u1025\u1009](?=\u1039)",
+ "to": "\u106a"
+ },
+ {
+ "from": "\u1009(?=[\u102f\u1030])",
+ "to": "\u1025"
+ },
+ {
+ "from": "[\u1025\u1009](?=[\u1037]?[\u103a])",
+ "to": "\u1025"
+ },
+ {
+ "from": "\u100a(?=[\u1039\u103d])",
+ "to": "\u106b"
+ },
+ {
+ "from": "(\u1039[\u1000-\u1021])(\u102D){0,1}\u102f",
+ "to": "$1$2\u1033"
+ },
+ {
+ "from": "(\u1039[\u1000-\u1021])\u1030",
+ "to": "$1\u1034"
+ },
+ {
+ "from": "\u1014(?=[\u102d\u102e\u102f\u103A]?[\u1030\u103d\u103e\u102f\u1039])",
+ "to": "\u108f"
+ },
+ {
+ "from": "\u1014(?=\u103A\u102F )",
+ "to": "\u108f"
+ },
+ {
+ "from" : "\u1014\u103c",
+ "to" : "\u108f\u103c"
+ },
+ {
+ "from": "\u1039\u1000",
+ "to": "\u1060"
+ },
+ {
+ "from": "\u1039\u1001",
+ "to": "\u1061"
+ },
+ {
+ "from": "\u1039\u1002",
+ "to": "\u1062"
+ },
+ {
+ "from": "\u1039\u1003",
+ "to": "\u1063"
+ },
+ {
+ "from": "\u1039\u1005",
+ "to": "\u1065"
+ },
+ {
+ "from": "\u1039\u1006",
+ "to": "\u1066"
+ },
+ {
+ "from": "\u1039\u1007",
+ "to": "\u1068"
+ },
+ {
+ "from": "\u1039\u1008",
+ "to": "\u1069"
+ },
+ {
+ "from": "\u1039\u100b",
+ "to": "\u106c"
+ },
+ {
+ "from": "\u100b\u1039\u100c",
+ "to": "\u1092"
+ },
+ {
+ "from": "\u1039\u100c",
+ "to": "\u106d"
+ },
+ {
+ "from": "\u100d\u1039\u100d",
+ "to": "\u106e"
+ },
+ {
+ "from": "\u100d\u1039\u100e",
+ "to": "\u106f"
+ },
+ {
+ "from": "\u1039\u100f",
+ "to": "\u1070"
+ },
+ {
+ "from": "\u1039\u1010",
+ "to": "\u1071"
+ },
+ {
+ "from": "\u1039\u1011",
+ "to": "\u1073"
+ },
+ {
+ "from": "\u1039\u1012",
+ "to": "\u1075"
+ },
+ {
+ "from": "\u1039\u1013",
+ "to": "\u1076"
+ },
+ {
+ "from": "\u1039[\u1014\u108f]",
+ "to": "\u1077"
+ },
+ {
+ "from": "\u1039\u1015",
+ "to": "\u1078"
+ },
+ {
+ "from": "\u1039\u1016",
+ "to": "\u1079"
+ },
+ {
+ "from": "\u1039\u1017",
+ "to": "\u107a"
+ },
+ {
+ "from": "\u1039\u1018",
+ "to": "\u107b"
+ },
+ {
+ "from": "\u1039\u1019",
+ "to": "\u107c"
+ },
+ {
+ "from": "\u1039\u101c",
+ "to": "\u1085"
+ },
+ {
+ "from": "\u103f",
+ "to": "\u1086"
+ },
+ {
+ "from": "\u103d\u103e",
+ "to": "\u108a"
+ },
+ {
+ "from": "(\u1064)([\u1000-\u1021])([\u103b\u103c]?)\u102d",
+ "to": "$2$3\u108b"
+ },
+ {
+ "from": "(\u1064)([\u1000-\u1021])([\u103b\u103c]?)\u102e",
+ "to": "$2$3\u108c"
+ },
+ {
+ "from": "(\u1064)([\u1000-\u1021])([\u103b\u103c]?)\u1036",
+ "to": "$2$3\u108d"
+ },
+ {
+ "from": "(\u1064)([\u1000-\u1021\u1040-\u1049])([\u103b\u103c]?)([\u1031]?)",
+ "to": "$2$3$4$1"
+ },
+ {
+ "from": "\u101b(?=([\u102d\u102e]?)[\u102f\u1030\u103d\u108a])",
+ "to": "\u1090"
+ },
+ {
+ "from": "\u100f\u1039\u100d",
+ "to": "\u1091"
+ },
+ {
+ "from": "\u100b\u1039\u100b",
+ "to": "\u1097"
+ },
+ {
+ "from": "([\u1000-\u1021\u108f\u1029\u106e\u106f\u1086\u1090\u1091\u1092\u1097])([\u1060-\u1069\u106c\u106d\u1070-\u107c\u1085\u108a])?([\u103b-\u103e]*)?\u1031",
+ "to": "\u1031$1$2$3"
+ },
+ {
+ "from": "\u103c\u103e",
+ "to": "\u103c\u1087"
+ },
+ {
+ "from": "([\u1000-\u1021\u108f\u1029])([\u1060-\u1069\u106c\u106d\u1070-\u107c\u1085])?(\u103c)",
+ "to": "$3$1$2"
+ },
+ {
+ "from": "\u103a",
+ "to": "\u1039"
+ },
+ {
+ "from": "\u103b",
+ "to": "\u103a"
+ },
+ {
+ "from": "\u103c",
+ "to": "\u103b"
+ },
+ {
+ "from": "\u103d",
+ "to": "\u103c"
+ },
+ {
+ "from": "\u103e",
+ "to": "\u103d"
+ },
+ {
+ "from": "([^\u103a\u100a])\u103d([\u102d\u102e]?)\u102f",
+ "to": "$1\u1088$2"
+ },
+ {
+ "from": "([\u101b\u103a\u103c\u108a\u1088\u1090])([\u1030\u103d])?([\u1032\u1036\u1039\u102d\u102e\u108b\u108c\u108d\u108e]?)(\u102f)?\u1037",
+ "to": "$1$2$3$4\u1095"
+ },
+ {
+ "from": "([\u102f\u1014\u1030\u103d])([\u1032\u1036\u1039\u102d\u102e\u108b\u108c\u108d\u108e]?)\u1037",
+ "to": "$1$2\u1094"
+ },
+ {
+ "from": "([\u103b])([\u1000-\u1021])([\u1087]?)([\u1036\u102d\u102e\u108b\u108c\u108d\u108e]?)\u102f",
+ "to": "$1$2$3$4\u1033"
+ },
+ {
+ "from": "([\u103b])([\u1000-\u1021])([\u1087]?)([\u1036\u102d\u102e\u108b\u108c\u108d\u108e]?)\u1030",
+ "to": "$1$2$3$4\u1034"
+ },
+ {
+ "from": "([\u103a\u103c\u100a\u1008\u100b\u100c\u100d\u1020\u1025])([\u103d]?)([\u1036\u102d\u102e\u108b\u108c\u108d\u108e]?)\u102f",
+ "to": "$1$2$3\u1033"
+ },
+ {
+ "from": "([\u103a\u103c\u100a\u1008\u100b\u100c\u100d\u1020\u1025])(\u103d?)([\u1036\u102d\u102e\u108b\u108c\u108d\u108e]?)\u1030",
+ "to": "$1$2$3\u1034"
+ },
+ {
+ "from": "([\u100a\u1020\u1009])\u103d",
+ "to": "$1\u1087"
+ },
+ {
+ "from": "\u103d\u1030",
+ "to": "\u1089"
+ },
+ {
+ "from": "\u103b([\u1000\u1003\u1006\u100f\u1010\u1011\u1018\u101a\u101c\u101a\u101e\u101f])",
+ "to": "\u107e$1"
+ },
+ {
+ "from": "\u107e([\u1000\u1003\u1006\u100f\u1010\u1011\u1018\u101a\u101c\u101a\u101e\u101f])([\u103c\u108a])([\u1032\u1036\u102d\u102e\u108b\u108c\u108d\u108e])",
+ "to": "\u1084$1$2$3"
+ },
+ {
+ "from": "\u107e([\u1000\u1003\u1006\u100f\u1010\u1011\u1018\u101a\u101c\u101a\u101e\u101f])([\u103c\u108a])",
+ "to": "\u1082$1$2"
+ },
+ {
+ "from": "\u107e([\u1000\u1003\u1006\u100f\u1010\u1011\u1018\u101a\u101c\u101a\u101e\u101f])([\u1033\u1034]?)([\u1032\u1036\u102d\u102e\u108b\u108c\u108d\u108e])",
+ "to": "\u1080$1$2$3"
+ },
+ {
+ "from": "\u103b([\u1000-\u1021])([\u103c\u108a])([\u1032\u1036\u102d\u102e\u108b\u108c\u108d\u108e])",
+ "to": "\u1083$1$2$3"
+ },
+ {
+ "from": "\u103b([\u1000-\u1021])([\u103c\u108a])",
+ "to": "\u1081$1$2"
+ },
+ {
+ "from": "\u103b([\u1000-\u1021])([\u1033\u1034]?)([\u1032\u1036\u102d\u102e\u108b\u108c\u108d\u108e])",
+ "to": "\u107f$1$2$3"
+ },
+ {
+ "from": "\u103a\u103d",
+ "to": "\u103d\u103a"
+ },
+ {
+ "from": "\u103a([\u103c\u108a])",
+ "to": "$1\u107d"
+ },
+ {
+ "from": "([\u1033\u1034])(\u1036?)\u1094",
+ "to": "$1$2\u1095"
+ },
+ {
+ "from": "\u108F\u1071",
+ "to" : "\u108F\u1072"
+ },
+ {
+ "from": "([\u1000-\u1021])([\u107B\u1066])\u102C",
+ "to": "$1\u102C$2"
+ },
+ {
+ "from": "\u102C([\u107B\u1066])\u1037",
+ "to": "\u102C$1\u1094"
+ },
+ {
+ "from": "\u1047((?=[\u1000-\u1021]\u1039)|(?=[\u102c-\u1030\u1032\u1036-\u1038\u103c\u103d]))",
+ "to": "\u101b"
+ }
+]
+`
+
+ return replaceRule(unizgRule, str)
+}
+
+func Zg2uni(str string) string {
+ zguniRule := `[
+ {
+ "from" : "([\u102D\u102E\u103D\u102F\u1037\u1095])\\1+",
+ "to" : "$1"
+ },
+ {
+ "from": "\u200B",
+ "to": ""
+ },
+ {
+ "from" : "\u103d\u103c",
+ "to" : "\u108a"
+ },
+ {
+ "from": "(\u103d|\u1087)",
+ "to": "\u103e"
+ },
+ {
+ "from": "\u103c",
+ "to": "\u103d"
+ },
+ {
+ "from": "(\u103b|\u107e|\u107f|\u1080|\u1081|\u1082|\u1083|\u1084)",
+ "to": "\u103c"
+ },
+ {
+ "from": "(\u103a|\u107d)",
+ "to": "\u103b"
+ },
+ {
+ "from": "\u1039",
+ "to": "\u103a"
+ },
+ {
+ "from": "(\u1066|\u1067)",
+ "to": "\u1039\u1006"
+ },
+ {
+ "from": "\u106a",
+ "to": "\u1009"
+ },
+ {
+ "from": "\u106b",
+ "to": "\u100a"
+ },
+ {
+ "from": "\u106c",
+ "to": "\u1039\u100b"
+ },
+ {
+ "from": "\u106d",
+ "to": "\u1039\u100c"
+ },
+ {
+ "from": "\u106e",
+ "to": "\u100d\u1039\u100d"
+ },
+ {
+ "from": "\u106f",
+ "to": "\u100d\u1039\u100e"
+ },
+ {
+ "from": "\u1070",
+ "to": "\u1039\u100f"
+ },
+ {
+ "from": "(\u1071|\u1072)",
+ "to": "\u1039\u1010"
+ },
+ {
+ "from": "\u1060",
+ "to": "\u1039\u1000"
+ },
+ {
+ "from": "\u1061",
+ "to": "\u1039\u1001"
+ },
+ {
+ "from": "\u1062",
+ "to": "\u1039\u1002"
+ },
+ {
+ "from": "\u1063",
+ "to": "\u1039\u1003"
+ },
+ {
+ "from": "\u1065",
+ "to": "\u1039\u1005"
+ },
+ {
+ "from": "\u1068",
+ "to": "\u1039\u1007"
+ },
+ {
+ "from": "\u1069",
+ "to": "\u1039\u1008"
+ },
+ {
+ "from": "(\u1073|\u1074)",
+ "to": "\u1039\u1011"
+ },
+ {
+ "from": "\u1075",
+ "to": "\u1039\u1012"
+ },
+ {
+ "from": "\u1076",
+ "to": "\u1039\u1013"
+ },
+ {
+ "from": "\u1077",
+ "to": "\u1039\u1014"
+ },
+ {
+ "from": "\u1078",
+ "to": "\u1039\u1015"
+ },
+ {
+ "from": "\u1079",
+ "to": "\u1039\u1016"
+ },
+ {
+ "from": "\u107a",
+ "to": "\u1039\u1017"
+ },
+ {
+ "from": "\u107c",
+ "to": "\u1039\u1019"
+ },
+ {
+ "from": "\u1085",
+ "to": "\u1039\u101c"
+ },
+ {
+ "from": "\u1033",
+ "to": "\u102f"
+ },
+ {
+ "from": "\u1034",
+ "to": "\u1030"
+ },
+ {
+ "from": "\u103f",
+ "to": "\u1030"
+ },
+ {
+ "from": "\u1086",
+ "to": "\u103f"
+ },
+ {
+ "from": "\u1036\u1088",
+ "to": "\u1088\u1036"
+ },
+ {
+ "from": "\u1088",
+ "to": "\u103e\u102f"
+ },
+ {
+ "from": "\u1089",
+ "to": "\u103e\u1030"
+ },
+ {
+ "from": "\u108a",
+ "to": "\u103d\u103e"
+ },
+ {
+ "from": "\u103B\u1064",
+ "to": "\u1064\u103B"
+ },
+ {
+ "from": "\u103c([\u1000-\u1021])(\u1064|\u108b)",
+ "to": "$1\u103c$2"
+ },
+ {
+ "from": "(\u1031)?([\u1000-\u1021\u1040-\u1049])(\u103c)?\u1064",
+ "to": "\u1004\u103a\u1039$1$2$3"
+ },
+ {
+ "from": "(\u1031)?([\u1000-\u1021])(\u103b|\u103c)?\u108b",
+ "to": "\u1004\u103a\u1039$1$2$3\u102d"
+ },
+ {
+ "from": "(\u1031)?([\u1000-\u1021])(\u103b)?\u108c",
+ "to": "\u1004\u103a\u1039$1$2$3\u102e"
+ },
+ {
+ "from": "(\u1031)?([\u1000-\u1021])(\u103b)?\u108d",
+ "to": "\u1004\u103a\u1039$1$2$3\u1036"
+ },
+ {
+ "from": "\u108e",
+ "to": "\u102d\u1036"
+ },
+ {
+ "from": "\u108f",
+ "to": "\u1014"
+ },
+ {
+ "from": "\u1090",
+ "to": "\u101b"
+ },
+ {
+ "from": "\u1091",
+ "to": "\u100f\u1039\u100d"
+ },
+ {
+ "from": "\u1092",
+ "to": "\u100b\u1039\u100c"
+ },
+ {
+ "from": "\u1019\u102c(\u107b|\u1093)",
+ "to": "\u1019\u1039\u1018\u102c"
+ },
+ {
+ "from": "(\u107b|\u1093)",
+ "to": "\u1039\u1018"
+ },
+ {
+ "from": "(\u1094|\u1095)",
+ "to": "\u1037"
+ },
+ {
+ "from": "([\u1000-\u1021])\u1037\u1032",
+ "to": "$1\u1032\u1037"
+ },
+ {
+ "from": "\u1096",
+ "to": "\u1039\u1010\u103d"
+ },
+ {
+ "from": "\u1097",
+ "to": "\u100b\u1039\u100b"
+ },
+ {
+ "from": "\u103c([\u1000-\u1021])([\u1000-\u1021])?",
+ "to": "$1\u103c$2"
+ },
+ {
+ "from": "([\u1000-\u1021])\u103c\u103a",
+ "to": "\u103c$1\u103a"
+ },
+ {
+ "from": "\u1047(?=[\u102c-\u1030\u1032\u1036-\u1038\u103d\u1038])",
+ "to": "\u101b"
+ },
+ {
+ "from": "\u1031\u1047",
+ "to": "\u1031\u101b"
+ },
+ {
+ "from": "\u1040(\u102e|\u102f|\u102d\u102f|\u1030|\u1036|\u103d|\u103e)",
+ "to": "\u101d$1"
+ },
+ {
+ "from": "([^\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049])\u1040\u102b",
+ "to": "$1\u101d\u102b"
+ },
+ {
+ "from": "([\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049])\u1040\u102b(?!\u1038)",
+ "to": "$1\u101d\u102b"
+ },
+ {
+ "from": "^\u1040(?=\u102b)",
+ "to": "\u101d"
+ },
+ {
+ "from": "\u1040\u102d(?!\u0020?/)",
+ "to": "\u101d\u102d"
+ },
+ {
+ "from": "([^\u1040-\u1049])\u1040([^\u1040-\u1049\u0020]|[\u104a\u104b])",
+ "to": "$1\u101d$2"
+ },
+ {
+ "from": "([^\u1040-\u1049])\u1040(?=[\\f\\n\\r])",
+ "to": "$1\u101d"
+ },
+ {
+ "from": "([^\u1040-\u1049])\u1040$",
+ "to": "$1\u101d"
+ },
+ {
+ "from": "\u1031([\u1000-\u1021\u103f])(\u103e)?(\u103b)?",
+ "to": "$1$2$3\u1031"
+ },
+ {
+ "from": "([\u1000-\u1021])\u1031([\u103b\u103c\u103d\u103e]+)",
+ "to": "$1$2\u1031"
+ },
+ {
+ "from": "\u1032\u103d",
+ "to": "\u103d\u1032"
+ },
+ {
+ "from": "([\u102d\u102e])\u103b",
+ "to": "\u103b$1"
+ },
+ {
+ "from": "\u103d\u103b",
+ "to": "\u103b\u103d"
+ },
+ {
+ "from": "\u103a\u1037",
+ "to": "\u1037\u103a"
+ },
+ {
+ "from": "\u102f(\u102d|\u102e|\u1036|\u1037)\u102f",
+ "to": "\u102f$1"
+ },
+ {
+ "from": "(\u102f|\u1030)(\u102d|\u102e)",
+ "to": "$2$1"
+ },
+ {
+ "from": "(\u103e)(\u103b|\u103c)",
+ "to": "$2$1"
+ },
+ {
+ "from": "\u1025(?=[\u1037]?[\u103a\u102c])",
+ "to": "\u1009"
+ },
+ {
+ "from": "\u1025\u102e",
+ "to": "\u1026"
+ },
+ {
+ "from": "\u1005\u103b",
+ "to": "\u1008"
+ },
+ {
+ "from": "\u1036(\u102f|\u1030)",
+ "to": "$1\u1036"
+ },
+ {
+ "from": "\u1031\u1037\u103e",
+ "to": "\u103e\u1031\u1037"
+ },
+ {
+ "from": "\u1031\u103e\u102c",
+ "to": "\u103e\u1031\u102c"
+ },
+ {
+ "from": "\u105a",
+ "to": "\u102b\u103a"
+ },
+ {
+ "from": "\u1031\u103b\u103e",
+ "to": "\u103b\u103e\u1031"
+ },
+ {
+ "from": "(\u102d|\u102e)(\u103d|\u103e)",
+ "to": "$2$1"
+ },
+ {
+ "from": "\u102c\u1039([\u1000-\u1021])",
+ "to": "\u1039$1\u102c"
+ },
+ {
+ "from": "\u1039\u103c\u103a\u1039([\u1000-\u1021])",
+ "to": "\u103a\u1039$1\u103c"
+ },
+ {
+ "from": "\u103c\u1039([\u1000-\u1021])",
+ "to": "\u1039$1\u103c"
+ },
+ {
+ "from": "\u1036\u1039([\u1000-\u1021])",
+ "to": "\u1039$1\u1036"
+ },
+ {
+ "from": "\u104e",
+ "to": "\u104e\u1004\u103a\u1038"
+ },
+ {
+ "from": "\u1040(\u102b|\u102c|\u1036)",
+ "to": "\u101d$1"
+ },
+ {
+ "from": "\u1025\u1039",
+ "to": "\u1009\u1039"
+ },
+ {
+ "from": "([\u1000-\u1021])\u103c\u1031\u103d",
+ "to": "$1\u103c\u103d\u1031"
+ },
+ {
+ "from": "([\u1000-\u1021])\u103b\u1031\u103d(\u103e)?",
+ "to": "$1\u103b\u103d$2\u1031"
+ },
+ {
+ "from": "([\u1000-\u1021])\u103d\u1031\u103b",
+ "to": "$1\u103b\u103d\u1031"
+ },
+ {
+ "from": "([\u1000-\u1021])\u1031(\u1039[\u1000-\u1021])",
+ "to": "$1$2\u1031"
+ },
+ {
+ "from": "\u1038\u103a",
+ "to": "\u103a\u1038"
+ },
+ {
+ "from": "\u102d\u103a|\u103a\u102d",
+ "to": "\u102d"
+ },
+ {
+ "from": "\u102d\u102f\u103a",
+ "to": "\u102d\u102f"
+ },
+ {
+ "from": "\u0020\u1037",
+ "to": "\u1037"
+ },
+ {
+ "from": "\u1037\u1036",
+ "to": "\u1036\u1037"
+ },
+ {
+ "from": "[\u102d]+",
+ "to": "\u102d"
+ },
+ {
+ "from": "[\u103a]+",
+ "to": "\u103a"
+ },
+ {
+ "from": "[\u103d]+",
+ "to": "\u103d"
+ },
+ {
+ "from": "[\u1037]+",
+ "to": "\u1037"
+ },
+ {
+ "from": "[\u102e]+",
+ "to": "\u102e"
+ },
+ {
+ "from": "\u102d\u102e|\u102e\u102d",
+ "to": "\u102e"
+ },
+ {
+ "from": "\u102f\u102d",
+ "to": "\u102d\u102f"
+ },
+ {
+ "from": "\u1037\u1037",
+ "to": "\u1037"
+ },
+ {
+ "from": "\u1032\u1032",
+ "to": "\u1032"
+ },
+ {
+ "from": "\u1044\u1004\u103a\u1038",
+ "to": "\u104E\u1004\u103a\u1038"
+ },
+ {
+ "from": "([\u102d\u102e])\u1039([\u1000-\u1021])",
+ "to": "\u1039$2$1"
+ },
+ {
+ "from": "(\u103c\u1031)\u1039([\u1000-\u1021])",
+ "to": "\u1039$2$1"
+ },
+ {
+ "from": "\u1036\u103d",
+ "to": "\u103d\u1036"
+ },
+ {
+ "from": "\u1047((?=[\u1000-\u1021]\u103a)|(?=[\u102c-\u1030\u1032\u1036-\u1038\u103d\u103e]))",
+ "to": "\u101b"
+ }
+]`
+
+ return replaceRule(zguniRule, str)
+}
+
+func replaceRule(ruleJson string, output string) string {
+ var rules []RuleStruct
+ json.Unmarshal([]byte(ruleJson), &rules)
+ text := output
+ for _, rule := range rules {
+ re := regexp2.MustCompile(rule.From, 1)
+ res, err := re.Replace(text, rule.To, 0, -1)
+ text = res
+ if err != nil {
+ //nothing to do
+ continue
+ }
+ }
+
+ return text
+
+}
diff --git a/static/Zawgyi-One.ttf b/static/Zawgyi-One.ttf
new file mode 100644
index 0000000..a1cd173
Binary files /dev/null and b/static/Zawgyi-One.ttf differ
diff --git a/vo.go b/vo.go
new file mode 100644
index 0000000..def2806
--- /dev/null
+++ b/vo.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "C"
+ "encoding/json"
+ "log"
+)
+
+type Reply struct {
+ Status int `json:"status"`
+ Err string `json:"err"`
+ Result interface{} `json:"result"`
+}
+
+func NewErr(err error) *C.char {
+ e := Reply{Status: 1, Err: err.Error()}
+ b, err := json.Marshal(e)
+ if err != nil {
+ log.Println("Error json.Marshal:", err.Error())
+ }
+ return C.CString(string(b))
+}
+
+func NewOk(data interface{}) *C.char {
+ e := Reply{Status: 0, Result: data}
+ b, err := json.Marshal(e)
+ if err != nil {
+ log.Println("Error json.Marshal:", err.Error())
+ }
+ return C.CString(string(b))
+}