155 lines
4.1 KiB
Go
155 lines
4.1 KiB
Go
package zitie
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"toolbox/pkg/base"
|
|
"toolbox/pkg/zitie/logic"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"golang.org/x/image/font/sfnt"
|
|
)
|
|
|
|
//go:embed data/all.json
|
|
var allDataContent []byte
|
|
|
|
//go:embed data/*.ttf
|
|
var fontFS embed.FS
|
|
|
|
type zitieTool struct {
|
|
allChars map[string]logic.HanziData
|
|
}
|
|
|
|
func init() {
|
|
base.Register(&zitieTool{})
|
|
}
|
|
|
|
func (t *zitieTool) ID() string { return "zitie" }
|
|
func (t *zitieTool) Name() string { return "汉字字帖生成" }
|
|
func (t *zitieTool) Description() string { return "提供智能缺字处理和古风排版的专业字帖工具" }
|
|
func (t *zitieTool) Emoji() string { return "🎨" }
|
|
|
|
func (t *zitieTool) Init() error {
|
|
fmt.Println("Initializing Zitie tool with font check...")
|
|
t.allChars = make(map[string]logic.HanziData)
|
|
if err := json.Unmarshal(allDataContent, &t.allChars); err != nil {
|
|
return fmt.Errorf("failed to unmarshal all.json: %v", err)
|
|
}
|
|
|
|
fontBytes, _ := fontFS.ReadFile("data/font.ttf")
|
|
logic.SetFontData(fontBytes)
|
|
|
|
fonts := map[string]string{
|
|
"kaiti": "data/kaiti.ttf",
|
|
"lishu": "data/lishu.ttf",
|
|
"xingshu": "data/xingshu.ttf",
|
|
"songti": "data/songti.ttf",
|
|
}
|
|
|
|
for id, src := range fonts {
|
|
bytes, err := fontFS.ReadFile(src)
|
|
if err != nil { continue }
|
|
|
|
// 1. 同步到磁盘用于 gopdf
|
|
tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("own_tools_%s.ttf", id))
|
|
_ = os.WriteFile(tmpPath, bytes, 0644)
|
|
logic.RegisterFontPath(id, tmpPath)
|
|
|
|
// 2. 解析 Cmap 用于缺字检查
|
|
f, err := sfnt.Parse(bytes)
|
|
if err == nil {
|
|
logic.RegisterFontSFNT(id, f)
|
|
fmt.Printf("Font [%s] analyzed and registered\n", id)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *zitieTool) RegisterRoutes(r *gin.RouterGroup) {
|
|
r.POST("/teaching", t.handleTeaching)
|
|
r.POST("/step", t.handleStep)
|
|
r.POST("/manuscript", t.handleManuscript)
|
|
}
|
|
|
|
type ZitieRequest struct {
|
|
Chars string `json:"chars" binding:"required"`
|
|
PaperSize string `json:"paper_size"`
|
|
FontType string `json:"font_type"`
|
|
}
|
|
|
|
func (t *zitieTool) handleTeaching(c *gin.Context) {
|
|
var req ZitieRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
data := t.filterChars(req.Chars, false)
|
|
t.generateAndResponse(c, data, "teaching", req.PaperSize, "kaiti")
|
|
}
|
|
|
|
func (t *zitieTool) handleStep(c *gin.Context) {
|
|
var req ZitieRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
data := t.filterChars(req.Chars, false)
|
|
t.generateAndResponse(c, data, "step", req.PaperSize, "kaiti")
|
|
}
|
|
|
|
func (t *zitieTool) handleManuscript(c *gin.Context) {
|
|
var req ZitieRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if req.FontType == "" { req.FontType = "kaiti" }
|
|
data := t.filterChars(req.Chars, true)
|
|
t.generateAndResponse(c, data, "manuscript", req.PaperSize, req.FontType)
|
|
}
|
|
|
|
func (t *zitieTool) filterChars(input string, keepNewline bool) []logic.HanziData {
|
|
var res []logic.HanziData
|
|
// 扩充标点符号列表
|
|
puncs := ",。!?;:、“”()《》〈〉…·.?!,:;\"'()<> 「」【】『』〔〕"
|
|
|
|
for _, r := range input {
|
|
charStr := string(r)
|
|
if r == '\n' {
|
|
if keepNewline { res = append(res, logic.HanziData{Character: "\n"}) }
|
|
continue
|
|
}
|
|
if r == ' ' || r == '\r' || r == '\t' { continue }
|
|
|
|
isPunc := false
|
|
for _, p := range puncs { if r == p { isPunc = true; break } }
|
|
if isPunc { continue }
|
|
|
|
if hd, ok := t.allChars[charStr]; ok {
|
|
hd.Character = charStr
|
|
res = append(res, hd)
|
|
} else {
|
|
res = append(res, logic.HanziData{Character: charStr})
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (t *zitieTool) generateAndResponse(c *gin.Context, data []logic.HanziData, mode, paper, font string) {
|
|
if len(data) == 0 {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "no valid characters found"})
|
|
return
|
|
}
|
|
pdfBytes, err := logic.GeneratePDFExtended(data, mode, paper, font)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Data(http.StatusOK, "application/pdf", pdfBytes)
|
|
}
|