diff --git a/README.md b/README.md index cdc722e..e34718d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ -# WordZero - Golang Word Document Library +
+ WordZero Logo + +

WordZero - Golang Word Document Library

+
+
+ [![Go Version](https://img.shields.io/badge/Go-1.19+-00ADD8?style=flat&logo=go)](https://golang.org) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Tests](https://img.shields.io/badge/Tests-Passing-green.svg)](#testing) @@ -7,6 +13,8 @@ [![Performance](https://img.shields.io/badge/Performance-Golang%20Winner-brightgreen.svg)](https://github.com/ZeroHawkeye/wordZero/wiki/en-Performance-Benchmarks) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ZeroHawkeye/wordZero) +
+ **English** | [中文](README_zh.md) ## Project Introduction @@ -288,11 +296,29 @@ wordZero/ ├── examples/ # Usage examples ├── test/ # Integration tests ├── benchmark/ # Performance benchmarks +├── docs/ # Documentation and assets +│ ├── logo.svg # Main logo with performance indicators +│ ├── logo-banner.svg # Banner version for README headers +│ └── logo-simple.svg # Simplified icon version └── wordZero.wiki/ # Complete documentation ``` 👉 **View detailed structure description**: [Project Structure](https://github.com/ZeroHawkeye/wordZero/wiki/en-Project-Structure) +### Logo and Branding + +The project includes multiple logo variations for different use cases: + +
+ +| Logo Type | Usage | Preview | +|-----------|-------|---------| +| **Banner** | README headers, documentation | Banner Logo | +| **Main** | General branding | Main Logo | +| **Simple** | Icons, favicons | Simple Logo | + +
+ ## Contributing Issues and Pull Requests are welcome! Please ensure before submitting code: diff --git a/README_zh.md b/README_zh.md index 9e257d5..3b74b45 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,5 +1,11 @@ -# WordZero - Golang Word操作库 +
+ WordZero Logo + +

WordZero - Golang Word操作库

+
+
+ [![Go Version](https://img.shields.io/badge/Go-1.19+-00ADD8?style=flat&logo=go)](https://golang.org) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Tests](https://img.shields.io/badge/Tests-Passing-green.svg)](#测试) @@ -7,6 +13,8 @@ [![Performance](https://img.shields.io/badge/Performance-Golang%20优胜-brightgreen.svg)](https://github.com/ZeroHawkeye/wordZero/wiki/13-%E6%80%A7%E8%83%BD%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ZeroHawkeye/wordZero) +
+ [English](README.md) | **中文** ## 项目介绍 @@ -288,11 +296,29 @@ wordZero/ ├── examples/ # 使用示例 ├── test/ # 集成测试 ├── benchmark/ # 性能基准测试 +├── docs/ # 文档和资源文件 +│ ├── logo.svg # 主Logo带性能指标 +│ ├── logo-banner.svg # 横幅版本用于README标题 +│ └── logo-simple.svg # 简化图标版本 └── wordZero.wiki/ # 完整文档 ``` 👉 **查看详细结构说明**: [项目结构详解](https://github.com/ZeroHawkeye/wordZero/wiki/15-项目结构详解) +### Logo设计 + +项目包含多种Logo变体,适用于不同使用场景: + +
+ +| Logo类型 | 使用场景 | 预览 | +|----------|----------|------| +| **横幅版** | README标题、文档头部 | 横幅Logo | +| **主版本** | 通用品牌展示 | 主Logo | +| **简化版** | 图标、网站标识 | 简化Logo | + +
+ ## 贡献指南 欢迎提交 Issue 和 Pull Request!在提交代码前请确保: diff --git a/docs/logo-banner.svg b/docs/logo-banner.svg new file mode 100644 index 0000000..6132a6d --- /dev/null +++ b/docs/logo-banner.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Word + Zero + + + + 高性能 Golang Word 文档操作库 + + + + + + + ⚡ 2.62ms + + + + + + + 🚀 零依赖 + + + + + + + 📄 OOXML + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/logo-simple.svg b/docs/logo-simple.svg new file mode 100644 index 0000000..d4a08df --- /dev/null +++ b/docs/logo-simple.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + W + Z + + \ No newline at end of file diff --git a/docs/logo.svg b/docs/logo.svg new file mode 100644 index 0000000..734a46d --- /dev/null +++ b/docs/logo.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Word + + + Zero + + + + GOLANG WORD LIBRARY + + + + + + 2.62ms + + + + + ZERO + + + + + DEPS + + + + + + + + + + \ No newline at end of file diff --git a/pkg/document/doc.go b/pkg/document/doc.go index 44ef440..da5ccdb 100644 --- a/pkg/document/doc.go +++ b/pkg/document/doc.go @@ -183,6 +183,7 @@ WordZero 专注于现代的 Office Open XML (OOXML) 格式(.docx 文件), if err != nil { var docErr *document.DocumentError if errors.As(err, &docErr) { + Errorf("文档操作失败 - 操作: %s, 错误: %v", docErr.Operation, docErr.Cause) fmt.Printf("操作: %s, 错误: %v\n", docErr.Operation, docErr.Cause) } } diff --git a/pkg/document/document.go b/pkg/document/document.go index 451ba25..d6abb25 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -101,11 +101,11 @@ type ParagraphProperties struct { XMLName xml.Name `xml:"w:pPr"` ParagraphStyle *ParagraphStyle `xml:"w:pStyle,omitempty"` NumberingProperties *NumberingProperties `xml:"w:numPr,omitempty"` - Spacing *Spacing `xml:"w:spacing,omitempty"` - Justification *Justification `xml:"w:jc,omitempty"` - Indentation *Indentation `xml:"w:ind,omitempty"` - Tabs *Tabs `xml:"w:tabs,omitempty"` ParagraphBorder *ParagraphBorder `xml:"w:pBdr,omitempty"` + Tabs *Tabs `xml:"w:tabs,omitempty"` + Spacing *Spacing `xml:"w:spacing,omitempty"` + Indentation *Indentation `xml:"w:ind,omitempty"` + Justification *Justification `xml:"w:jc,omitempty"` } // ParagraphBorder 段落边框 @@ -151,19 +151,20 @@ type Run struct { } // RunProperties 文本属性 +// 注意:字段顺序必须符合OpenXML标准,w:rFonts必须在w:color之前 type RunProperties struct { XMLName xml.Name `xml:"w:rPr"` + FontFamily *FontFamily `xml:"w:rFonts,omitempty"` Bold *Bold `xml:"w:b,omitempty"` BoldCs *BoldCs `xml:"w:bCs,omitempty"` Italic *Italic `xml:"w:i,omitempty"` ItalicCs *ItalicCs `xml:"w:iCs,omitempty"` Underline *Underline `xml:"w:u,omitempty"` Strike *Strike `xml:"w:strike,omitempty"` + Color *Color `xml:"w:color,omitempty"` FontSize *FontSize `xml:"w:sz,omitempty"` FontSizeCs *FontSizeCs `xml:"w:szCs,omitempty"` - Color *Color `xml:"w:color,omitempty"` Highlight *Highlight `xml:"w:highlight,omitempty"` - FontFamily *FontFamily `xml:"w:rFonts,omitempty"` } // Bold 粗体 @@ -600,6 +601,10 @@ func (d *Document) AddFormattedParagraph(text string, format *TextFormat) *Parag runProps := &RunProperties{} if format != nil { + if format.FontFamily != "" { + runProps.FontFamily = &FontFamily{ASCII: format.FontFamily} + } + if format.Bold { runProps.Bold = &Bold{} } @@ -608,19 +613,15 @@ func (d *Document) AddFormattedParagraph(text string, format *TextFormat) *Parag runProps.Italic = &Italic{} } - if format.FontSize > 0 { - // Word中字体大小是半磅为单位,所以需要乘以2 - runProps.FontSize = &FontSize{Val: strconv.Itoa(format.FontSize * 2)} - } - if format.FontColor != "" { // 确保颜色格式正确(移除#前缀) color := strings.TrimPrefix(format.FontColor, "#") runProps.Color = &Color{Val: color} } - if format.FontFamily != "" { - runProps.FontFamily = &FontFamily{ASCII: format.FontFamily} + if format.FontSize > 0 { + // Word中字体大小是半磅为单位,所以需要乘以2 + runProps.FontSize = &FontSize{Val: strconv.Itoa(format.FontSize * 2)} } } @@ -762,6 +763,10 @@ func (p *Paragraph) AddFormattedText(text string, format *TextFormat) { runProps := &RunProperties{} if format != nil { + if format.FontFamily != "" { + runProps.FontFamily = &FontFamily{ASCII: format.FontFamily} + } + if format.Bold { runProps.Bold = &Bold{} } @@ -770,17 +775,13 @@ func (p *Paragraph) AddFormattedText(text string, format *TextFormat) { runProps.Italic = &Italic{} } - if format.FontSize > 0 { - runProps.FontSize = &FontSize{Val: strconv.Itoa(format.FontSize * 2)} - } - if format.FontColor != "" { color := strings.TrimPrefix(format.FontColor, "#") runProps.Color = &Color{Val: color} } - if format.FontFamily != "" { - runProps.FontFamily = &FontFamily{ASCII: format.FontFamily} + if format.FontSize > 0 { + runProps.FontSize = &FontSize{Val: strconv.Itoa(format.FontSize * 2)} } } diff --git a/pkg/document/footnotes.go b/pkg/document/footnotes.go index 1d36d30..2a8f868 100644 --- a/pkg/document/footnotes.go +++ b/pkg/document/footnotes.go @@ -788,7 +788,7 @@ func (d *Document) addSettingsRelationship() { relationship := Relationship{ ID: relationshipID, Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", - Target: "settings.xml", + Target: "word/settings.xml", } d.relationships.Relationships = append(d.relationships.Relationships, relationship) } diff --git a/pkg/document/image.go b/pkg/document/image.go index bd8e289..73f0c1c 100644 --- a/pkg/document/image.go +++ b/pkg/document/image.go @@ -296,8 +296,9 @@ type PicElement struct { // NvPicPr 非可视图片属性 type NvPicPr struct { - XMLName xml.Name `xml:"pic:nvPicPr"` - CNvPr *CNvPr `xml:"pic:cNvPr"` + XMLName xml.Name `xml:"pic:nvPicPr"` + CNvPr *CNvPr `xml:"pic:cNvPr"` + CNvPicPr *CNvPicPr `xml:"pic:cNvPicPr"` } // CNvPr 通用非可视属性 @@ -309,6 +310,19 @@ type CNvPr struct { Title string `xml:"title,attr,omitempty"` } +// CNvPicPr 图片特定非可视属性 +type CNvPicPr struct { + XMLName xml.Name `xml:"pic:cNvPicPr"` + PicLocks *PicLocks `xml:"a:picLocks,omitempty"` +} + +// PicLocks 图片锁定属性 +type PicLocks struct { + XMLName xml.Name `xml:"a:picLocks"` + NoChangeAspect string `xml:"noChangeAspect,attr,omitempty"` + NoChangeArrowheads string `xml:"noChangeArrowheads,attr,omitempty"` +} + // BlipFill 图片填充 type BlipFill struct { XMLName xml.Name `xml:"pic:blipFill"` @@ -375,25 +389,31 @@ type AvLst struct { // AddImageFromFile 从文件添加图片到文档 func (d *Document) AddImageFromFile(filePath string, config *ImageConfig) (*ImageInfo, error) { + Debugf("开始添加图片文件: %s", filePath) + // 读取图片文件 imageData, err := os.ReadFile(filePath) if err != nil { + Errorf("读取图片文件失败 %s: %v", filePath, err) return nil, fmt.Errorf("读取图片文件失败: %v", err) } // 检测图片格式 format, err := detectImageFormat(imageData) if err != nil { + Errorf("检测图片格式失败 %s: %v", filePath, err) return nil, fmt.Errorf("检测图片格式失败: %v", err) } // 获取图片尺寸 width, height, err := getImageDimensions(imageData, format) if err != nil { + Errorf("获取图片尺寸失败 %s: %v", filePath, err) return nil, fmt.Errorf("获取图片尺寸失败: %v", err) } fileName := filepath.Base(filePath) + Infof("成功读取图片: %s (格式: %s, 尺寸: %dx%d, 大小: %d字节)", fileName, format, width, height, len(imageData)) return d.AddImageFromData(imageData, fileName, format, width, height, config) } @@ -723,6 +743,11 @@ func (d *Document) createImageGraphic(imageInfo *ImageInfo, displayWidth, displa Descr: altText, Title: title, }, + CNvPicPr: &CNvPicPr{ + PicLocks: &PicLocks{ + NoChangeAspect: "1", + }, + }, }, BlipFill: &BlipFill{ Blip: &Blip{ @@ -979,6 +1004,28 @@ func (d *Document) SetImageAlignment(imageInfo *ImageInfo, alignment AlignmentTy imageInfo.Config = &ImageConfig{} } + // 更新配置 imageInfo.Config.Alignment = alignment - return nil + + // 查找包含此图片的段落并更新其对齐方式 + for _, element := range d.Body.Elements { + if paragraph, ok := element.(*Paragraph); ok { + // 检查段落中是否包含指定的图片 + for _, run := range paragraph.Runs { + if run.Drawing != nil && run.Drawing.Inline != nil { + // 检查docPr ID是否匹配 + if run.Drawing.Inline.DocPr != nil && run.Drawing.Inline.DocPr.ID == imageInfo.ID { + // 更新段落对齐方式 + if paragraph.Properties == nil { + paragraph.Properties = &ParagraphProperties{} + } + paragraph.Properties.Justification = &Justification{Val: string(alignment)} + return nil + } + } + } + } + } + + return fmt.Errorf("找不到包含图片ID %s的段落", imageInfo.ID) } diff --git a/pkg/document/table.go b/pkg/document/table.go index 271bdb2..9abf28b 100644 --- a/pkg/document/table.go +++ b/pkg/document/table.go @@ -846,6 +846,12 @@ func (t *Table) SetCellFormattedText(row, col int, text string, format *TextForm if format != nil { run.Properties = &RunProperties{} + if format.FontFamily != "" { + run.Properties.FontFamily = &FontFamily{ + ASCII: format.FontFamily, + } + } + if format.Bold { run.Properties.Bold = &Bold{} } @@ -854,21 +860,15 @@ func (t *Table) SetCellFormattedText(row, col int, text string, format *TextForm run.Properties.Italic = &Italic{} } - if format.FontSize > 0 { - run.Properties.FontSize = &FontSize{ - Val: fmt.Sprintf("%d", format.FontSize*2), - } - } - if format.FontColor != "" { run.Properties.Color = &Color{ Val: format.FontColor, } } - if format.FontFamily != "" { - run.Properties.FontFamily = &FontFamily{ - ASCII: format.FontFamily, + if format.FontSize > 0 { + run.Properties.FontSize = &FontSize{ + Val: fmt.Sprintf("%d", format.FontSize*2), } } } @@ -904,6 +904,12 @@ func (t *Table) AddCellFormattedText(row, col int, text string, format *TextForm if format != nil { run.Properties = &RunProperties{} + if format.FontFamily != "" { + run.Properties.FontFamily = &FontFamily{ + ASCII: format.FontFamily, + } + } + if format.Bold { run.Properties.Bold = &Bold{} } @@ -912,21 +918,15 @@ func (t *Table) AddCellFormattedText(row, col int, text string, format *TextForm run.Properties.Italic = &Italic{} } - if format.FontSize > 0 { - run.Properties.FontSize = &FontSize{ - Val: fmt.Sprintf("%d", format.FontSize*2), - } - } - if format.FontColor != "" { run.Properties.Color = &Color{ Val: format.FontColor, } } - if format.FontFamily != "" { - run.Properties.FontFamily = &FontFamily{ - ASCII: format.FontFamily, + if format.FontSize > 0 { + run.Properties.FontSize = &FontSize{ + Val: fmt.Sprintf("%d", format.FontSize*2), } } } diff --git a/pkg/document/template_engine.go b/pkg/document/template_engine.go index d92e38a..cf432ef 100644 --- a/pkg/document/template_engine.go +++ b/pkg/document/template_engine.go @@ -2,7 +2,6 @@ package document import ( "fmt" - "log" "regexp" ) @@ -33,14 +32,14 @@ func (tr *TemplateRenderer) SetLogging(enabled bool) { // logInfo 记录信息日志 func (tr *TemplateRenderer) logInfo(format string, args ...interface{}) { if tr.logger.enabled { - log.Printf("[模板引擎] "+format, args...) + Infof("[模板引擎] "+format, args...) } } // logError 记录错误日志 func (tr *TemplateRenderer) logError(format string, args ...interface{}) { if tr.logger.enabled { - log.Printf("[模板引擎-错误] "+format, args...) + Errorf("[模板引擎] "+format, args...) } } diff --git a/pkg/style/style.go b/pkg/style/style.go index ecd5c48..975a6d5 100644 --- a/pkg/style/style.go +++ b/pkg/style/style.go @@ -56,17 +56,18 @@ type Next struct { } // ParagraphProperties 段落样式属性 +// 注意:字段顺序必须符合OpenXML标准 type ParagraphProperties struct { XMLName xml.Name `xml:"w:pPr"` - Spacing *Spacing `xml:"w:spacing,omitempty"` - Justification *Justification `xml:"w:jc,omitempty"` - Indentation *Indentation `xml:"w:ind,omitempty"` KeepNext *KeepNext `xml:"w:keepNext,omitempty"` KeepLines *KeepLines `xml:"w:keepLines,omitempty"` PageBreak *PageBreak `xml:"w:pageBreakBefore,omitempty"` - OutlineLevel *OutlineLevel `xml:"w:outlineLvl,omitempty"` ParagraphBorder *ParagraphBorder `xml:"w:pBdr,omitempty"` Shading *Shading `xml:"w:shd,omitempty"` + Spacing *Spacing `xml:"w:spacing,omitempty"` + Indentation *Indentation `xml:"w:ind,omitempty"` + Justification *Justification `xml:"w:jc,omitempty"` + OutlineLevel *OutlineLevel `xml:"w:outlineLvl,omitempty"` } // ParagraphBorder 段落边框 @@ -95,15 +96,16 @@ type Shading struct { } // RunProperties 字符样式属性 +// 注意:字段顺序必须符合OpenXML标准,w:rFonts必须在w:color之前 type RunProperties struct { XMLName xml.Name `xml:"w:rPr"` + FontFamily *FontFamily `xml:"w:rFonts,omitempty"` Bold *Bold `xml:"w:b,omitempty"` Italic *Italic `xml:"w:i,omitempty"` Underline *Underline `xml:"w:u,omitempty"` Strike *Strike `xml:"w:strike,omitempty"` - FontSize *FontSize `xml:"w:sz,omitempty"` Color *Color `xml:"w:color,omitempty"` - FontFamily *FontFamily `xml:"w:rFonts,omitempty"` + FontSize *FontSize `xml:"w:sz,omitempty"` Highlight *Highlight `xml:"w:highlight,omitempty"` }