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) 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) }