Files
cutThink_lite/src-ocr-plugin/main.go
Claude e2ea309ee6 feat: CutThenThink v3.0 初始版本
完整实现 Tauri + Vanilla JS 轻量级截图工具

Phase 1 - 项目搭建
- Tauri 2.x 项目初始化
- Vite 前端项目搭建
- 基础 UI 框架(CSS 变量、组件库)
- 构建配置优化

Phase 2 - 核心截图功能
- 全屏/区域/窗口截图
- 截图预览和管理
- 文件命名和缩略图
- 全局快捷键集成

Phase 3 - 上传与存储
- 多图床上传(GitHub/Imgur/自定义)
- 配置管理系统
- SQLite 数据库

Phase 4 - OCR 集成
- 云端 OCR(百度/腾讯云)
- 插件管理系统
- 本地 OCR 插件(Go)
- OCR 结果处理

Phase 5 - AI 分类系统
- Claude/OpenAI API 集成
- Prompt 模板引擎
- 模板管理界面
- 自动分类流程

Phase 6 - 历史记录与管理
- 图库视图(网格/列表)
- 搜索与筛选
- 批量操作
- 导出功能(JSON/CSV/ZIP)

Phase 7 - 打包与发布
- 多平台构建配置
- CI/CD 工作流
- 图标和资源
- 安装包配置

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:59:26 +08:00

186 lines
4.3 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"github.com/otiai10/gosseract/v2"
)
const (
Version = "1.0.0"
)
func main() {
// 命令行参数
command := flag.String("command", "", "Command to execute (recognize, version)")
imagePath := flag.String("image", "", "Path to image file")
language := flag.String("lang", "eng+chi_sim", "OCR language (default: eng+chi_sim)")
flag.Parse()
if *command == "" || len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
switch *command {
case "version":
printVersion()
case "recognize":
if *imagePath == "" {
fmt.Fprintln(os.Stderr, "Error: image path is required for recognize command")
os.Exit(1)
}
recognize(*imagePath, *language)
default:
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", *command)
printUsage()
os.Exit(1)
}
}
func printUsage() {
fmt.Println("CutThenThink OCR Plugin v" + Version)
fmt.Println("\nUsage:")
fmt.Println(" ocr-plugin version - Print version information")
fmt.Println(" ocr-plugin recognize -image <path> - Recognize text from image")
fmt.Println("\nOptions:")
fmt.Println(" -lang <language> - OCR language (default: eng+chi_sim)")
fmt.Println("\nSupported languages:")
fmt.Println(" eng - English")
fmt.Println(" chi_sim - Simplified Chinese")
fmt.Println(" chi_tra - Traditional Chinese")
fmt.Println(" jpn - Japanese")
fmt.Println(" kor - Korean")
fmt.Println(" (combine with + for multiple languages)")
}
func printVersion() {
fmt.Println(Version)
}
// OCRBlock represents a single text block with bounding box
type OCRBlock struct {
Text string `json:"text"`
Confidence float32 `json:"confidence"`
BBoxX uint32 `json:"bbox_x"`
BBoxY uint32 `json:"bbox_y"`
BBoxWidth uint32 `json:"bbox_width"`
BBoxHeight uint32 `json:"bbox_height"`
BlockType string `json:"block_type"`
}
// OCRResponse represents the JSON response from the plugin
type OCRResponse struct {
Success bool `json:"success"`
Error *string `json:"error,omitempty"`
Engine string `json:"engine,omitempty"`
Language string `json:"language,omitempty"`
Blocks []OCRBlock `json:"blocks"`
}
func recognize(imagePath, language string) {
// Check if file exists
if _, err := os.Stat(imagePath); os.IsNotExist(err) {
response := OCRResponse{
Success: false,
Error: stringPtr("Image file not found: " + imagePath),
}
printJSON(response)
os.Exit(1)
}
// Create Tesseract client
client := gosseract.NewClient()
defer client.Close()
// Set language
client.SetLanguage(language)
// Set image
client.SetImage(imagePath)
// Get text
text, err := client.Text()
if err != nil {
response := OCRResponse{
Success: false,
Error: stringPtr("OCR failed: " + err.Error()),
}
printJSON(response)
os.Exit(1)
}
// Get detailed text boxes
boxes, err := client.GetBoundingBoxes(gosseract.RIL_TEXT_LINE)
if err != nil {
log.Printf("Warning: Failed to get bounding boxes: %v", err)
// Continue without bounding boxes
}
// Convert to our format
var blocks []OCRBlock
for i, box := range boxes {
if box.Box != nil {
blocks = append(blocks, OCRBlock{
Text: box.Word,
Confidence: float32(box.Confidence),
BBoxX: uint32(box.Box.Left),
BBoxY: uint32(box.Box.Top),
BBoxWidth: uint32(box.Box.Width),
BBoxHeight: uint32(box.Box.Height),
BlockType: "text",
})
} else {
// Fallback for lines without bounding boxes
blocks = append(blocks, OCRBlock{
Text: box.Word,
Confidence: float32(box.Confidence),
BBoxX: 0,
BBoxY: uint32(i * 20),
BBoxWidth: 100,
BBoxHeight: 20,
BlockType: "text",
})
}
}
// If no boxes found, create a single block with the full text
if len(blocks) == 0 && text != "" {
blocks = append(blocks, OCRBlock{
Text: text,
Confidence: 80.0, // Default confidence
BBoxX: 0,
BBoxY: 0,
BBoxWidth: 100,
BBoxHeight: 100,
BlockType: "text",
})
}
response := OCRResponse{
Success: true,
Engine: "tesseract",
Language: language,
Blocks: blocks,
}
printJSON(response)
}
func printJSON(v interface{}) {
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if err := encoder.Encode(v); err != nil {
log.Fatalf("Failed to encode JSON: %v", err)
}
}
func stringPtr(s string) *string {
return &s
}