Files
file-tool-wails/services/word_service.go
2026-06-17 08:05:20 +08:00

225 lines
4.4 KiB
Go

package services
import (
"archive/zip"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/signintech/gopdf"
)
type WordService struct{}
func NewWordService() *WordService {
return &WordService{}
}
type WordInfo struct {
Title string `json:"title"`
Author string `json:"author"`
Pages int `json:"pages"`
Words int `json:"words"`
FilePath string `json:"filePath"`
Size int64 `json:"size"`
}
func (s *WordService) GetWordInfo(filePath string) (*WordInfo, error) {
fileInfo, err := os.Stat(filePath)
if err != nil {
return nil, fmt.Errorf("获取文件信息失败: %v", err)
}
text, _ := s.ExtractText(filePath)
words := len(strings.Fields(text))
return &WordInfo{
Title: filepath.Base(filePath),
Author: "",
Pages: 1,
Words: words,
FilePath: filePath,
Size: fileInfo.Size(),
}, nil
}
func (s *WordService) ConvertToPDF(inputPath, outputPath string) error {
if outputPath == "" {
outputPath = ChangeExtension(inputPath, ".pdf")
}
text, err := s.ExtractText(inputPath)
if err != nil {
return fmt.Errorf("提取Word文本失败: %v", err)
}
if strings.TrimSpace(text) == "" {
text = "(空文档)"
}
return createCJKPDF(outputPath, text)
}
func (s *WordService) ExtractText(inputPath string) (string, error) {
r, err := zip.OpenReader(inputPath)
if err != nil {
return extractPlainText(inputPath)
}
defer r.Close()
var docBody []byte
for _, f := range r.File {
if f.Name == "word/document.xml" {
rc, err := f.Open()
if err != nil {
continue
}
docBody, err = readAll(rc)
rc.Close()
if err != nil {
continue
}
break
}
}
if docBody == nil {
return extractPlainText(inputPath)
}
text := extractXMLText(string(docBody))
return text, nil
}
func readAll(r interface{ Read([]byte) (int, error) }) ([]byte, error) {
buf := make([]byte, 0, 4096)
tmp := make([]byte, 1024)
for {
n, err := r.Read(tmp)
buf = append(buf, tmp[:n]...)
if err != nil {
break
}
}
return buf, nil
}
func extractPlainText(filePath string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(data), nil
}
func extractXMLText(xml string) string {
var result strings.Builder
inTag := false
for i := 0; i < len(xml); i++ {
if xml[i] == '<' {
inTag = true
if i+1 < len(xml) && xml[i+1] == '/' {
if result.Len() > 0 {
ch := result.String()
if !strings.HasSuffix(ch, "\n") && !strings.HasSuffix(ch, " ") {
result.WriteString(" ")
}
}
}
continue
}
if xml[i] == '>' {
inTag = false
continue
}
if !inTag {
result.WriteByte(xml[i])
}
}
text := result.String()
text = strings.ReplaceAll(text, "&#xA;", "\n")
text = strings.ReplaceAll(text, "&amp;", "&")
text = strings.ReplaceAll(text, "&lt;", "<")
text = strings.ReplaceAll(text, "&gt;", ">")
text = strings.ReplaceAll(text, "&quot;", "\"")
text = strings.ReplaceAll(text, "&#39;", "'")
text = strings.ReplaceAll(text, "\n ", "\n")
text = strings.TrimSpace(text)
var lines []string
for _, line := range strings.Split(text, "\n") {
line = strings.TrimSpace(line)
if line != "" {
lines = append(lines, line)
}
}
return strings.Join(lines, "\n")
}
func createCJKPDF(outputPath, text string) error {
pdf := gopdf.GoPdf{}
pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
fontPaths := []string{
"C:\\Windows\\Fonts\\msyh.ttc",
"C:\\Windows\\Fonts\\simhei.ttf",
"C:\\Windows\\Fonts\\simsun.ttc",
"C:\\Windows\\Fonts\\msyhbd.ttc",
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
}
var fontLoaded bool
for _, fp := range fontPaths {
if _, err := os.Stat(fp); err == nil {
err = pdf.AddTTFFont("cjk", fp)
if err == nil {
fontLoaded = true
break
}
}
}
pdf.AddPage()
lines := strings.Split(text, "\n")
y := 50.0
pageHeight := 800.0
lineHeight := 16.0
marginLeft := 50.0
for _, line := range lines {
if y > pageHeight-30 {
pdf.AddPage()
y = 50.0
}
if fontLoaded {
pdf.SetFont("cjk", "", 11)
} else {
pdf.SetFont("helvetica", "", 11)
}
wrappedLines, err := pdf.SplitTextWithWordWrap(line, 500)
if err != nil {
wrappedLines = []string{line}
}
for _, wl := range wrappedLines {
if y > pageHeight-30 {
pdf.AddPage()
y = 50.0
}
pdf.SetXY(marginLeft, y)
pdf.Cell(nil, wl)
y += lineHeight
}
}
return pdf.WritePdf(outputPath)
}