From 6b4be90900e49280b134c067a4a11f0361cc78a0 Mon Sep 17 00:00:00 2001 From: landaiqing Date: Wed, 16 Apr 2025 17:29:40 +0800 Subject: [PATCH] :art: Refactored XML generation logic and fixed content type override issues --- document/body.go | 26 +-- document/header_footer.go | 4 +- document/paragraph.go | 127 ++++++----- document/run.go | 127 ++++++----- document/table.go | 369 ++++++++++++++++--------------- workbook/content_types.go | 1 - workbook/examples/simple/main.go | 2 +- workbook/workbook.go | 5 + 8 files changed, 343 insertions(+), 318 deletions(-) diff --git a/document/body.go b/document/body.go index 4567818..36e3a2c 100644 --- a/document/body.go +++ b/document/body.go @@ -130,7 +130,19 @@ func (b *Body) ToXML() string { } // 添加节属性 - xml += "" + xml += "" + + // 页眉引用 + for _, headerRef := range b.SectionProperties.HeaderReference { + xml += fmt.Sprintf("", + headerRef.Type, headerRef.ID) + } + + // 页脚引用 + for _, footerRef := range b.SectionProperties.FooterReference { + xml += fmt.Sprintf("", + footerRef.Type, footerRef.ID) + } // 页面大小 if b.SectionProperties.PageSize != nil { @@ -165,18 +177,6 @@ func (b *Body) ToXML() string { b.SectionProperties.DocGrid.LinePitch) } - // 页眉引用 - for _, headerRef := range b.SectionProperties.HeaderReference { - xml += fmt.Sprintf("", - headerRef.Type, headerRef.ID) - } - - // 页脚引用 - for _, footerRef := range b.SectionProperties.FooterReference { - xml += fmt.Sprintf("", - footerRef.Type, footerRef.ID) - } - xml += "" xml += "" diff --git a/document/header_footer.go b/document/header_footer.go index 05fabf2..ae47b96 100644 --- a/document/header_footer.go +++ b/document/header_footer.go @@ -59,7 +59,7 @@ func (f *Footer) AddTable(rows, cols int) *Table { // ToXML 将页眉转换为XML func (h *Header) ToXML() string { xml := "" - xml += "" + xml += "" // 添加所有内容元素的XML for _, content := range h.Content { @@ -78,7 +78,7 @@ func (h *Header) ToXML() string { // ToXML 将页脚转换为XML func (f *Footer) ToXML() string { xml := "" - xml += "" + xml += "" // 添加所有内容元素的XML for _, content := range f.Content { diff --git a/document/paragraph.go b/document/paragraph.go index 8a6e0b7..548463b 100644 --- a/document/paragraph.go +++ b/document/paragraph.go @@ -186,43 +186,12 @@ func (p *Paragraph) ToXML() string { // 添加段落属性 xml += "" - // 对齐方式 - if p.Properties.Alignment != "" { - xml += "" + // 样式 (必须在最前面) + if p.Properties.StyleID != "" { + xml += fmt.Sprintf("", p.Properties.StyleID) } - // 缩进 - if p.Properties.IndentLeft > 0 || p.Properties.IndentRight > 0 || p.Properties.IndentFirstLine > 0 { - xml += " 0 { - xml += " w:left=\"" + fmt.Sprintf("%d", p.Properties.IndentLeft) + "\"" - } - if p.Properties.IndentRight > 0 { - xml += " w:right=\"" + fmt.Sprintf("%d", p.Properties.IndentRight) + "\"" - } - if p.Properties.IndentFirstLine > 0 { - xml += " w:firstLine=\"" + fmt.Sprintf("%d", p.Properties.IndentFirstLine) + "\"" - } - xml += " />" - } - - // 间距 - if p.Properties.SpacingBefore > 0 || p.Properties.SpacingAfter > 0 || p.Properties.SpacingLine > 0 { - xml += " 0 { - xml += " w:before=\"" + fmt.Sprintf("%d", p.Properties.SpacingBefore) + "\"" - } - if p.Properties.SpacingAfter > 0 { - xml += " w:after=\"" + fmt.Sprintf("%d", p.Properties.SpacingAfter) + "\"" - } - if p.Properties.SpacingLine > 0 { - xml += " w:line=\"" + fmt.Sprintf("%d", p.Properties.SpacingLine) + "\"" - xml += " w:lineRule=\"" + p.Properties.SpacingLineRule + "\"" - } - xml += " />" - } - - // 分页控制 + // 分页控制 (必须在编号前面) if p.Properties.KeepNext { xml += "" } @@ -236,16 +205,11 @@ func (p *Paragraph) ToXML() string { xml += "" } - // 样式 - if p.Properties.StyleID != "" { - xml += "" - } - // 编号 if p.Properties.NumID > 0 { xml += "" - xml += "" - xml += "" + xml += fmt.Sprintf("", p.Properties.NumLevel) + xml += fmt.Sprintf("", p.Properties.NumID) xml += "" } @@ -254,37 +218,78 @@ func (p *Paragraph) ToXML() string { p.Properties.BorderLeft != nil || p.Properties.BorderRight != nil { xml += "" if p.Properties.BorderTop != nil { - xml += "" + xml += fmt.Sprintf("", + p.Properties.BorderTop.Style, + p.Properties.BorderTop.Size, + p.Properties.BorderTop.Space, + p.Properties.BorderTop.Color) } if p.Properties.BorderBottom != nil { - xml += "" + xml += fmt.Sprintf("", + p.Properties.BorderBottom.Style, + p.Properties.BorderBottom.Size, + p.Properties.BorderBottom.Space, + p.Properties.BorderBottom.Color) } if p.Properties.BorderLeft != nil { - xml += "" + xml += fmt.Sprintf("", + p.Properties.BorderLeft.Style, + p.Properties.BorderLeft.Size, + p.Properties.BorderLeft.Space, + p.Properties.BorderLeft.Color) } if p.Properties.BorderRight != nil { - xml += "" + xml += fmt.Sprintf("", + p.Properties.BorderRight.Style, + p.Properties.BorderRight.Size, + p.Properties.BorderRight.Space, + p.Properties.BorderRight.Color) } xml += "" } // 底纹 if p.Properties.Shading != nil { - xml += "" + xml += fmt.Sprintf("", + p.Properties.Shading.Pattern, + p.Properties.Shading.Fill, + p.Properties.Shading.Color) + } + + // 间距 + if p.Properties.SpacingBefore > 0 || p.Properties.SpacingAfter > 0 || p.Properties.SpacingLine > 0 { + xml += " 0 { + xml += fmt.Sprintf(" w:before=\"%d\"", p.Properties.SpacingBefore) + } + if p.Properties.SpacingAfter > 0 { + xml += fmt.Sprintf(" w:after=\"%d\"", p.Properties.SpacingAfter) + } + if p.Properties.SpacingLine > 0 { + xml += fmt.Sprintf(" w:line=\"%d\"", p.Properties.SpacingLine) + xml += fmt.Sprintf(" w:lineRule=\"%s\"", p.Properties.SpacingLineRule) + } + xml += " />" + } + + // 缩进 + if p.Properties.IndentLeft > 0 || p.Properties.IndentRight > 0 || p.Properties.IndentFirstLine > 0 { + xml += " 0 { + xml += fmt.Sprintf(" w:left=\"%d\"", p.Properties.IndentLeft) + } + if p.Properties.IndentRight > 0 { + xml += fmt.Sprintf(" w:right=\"%d\"", p.Properties.IndentRight) + } + if p.Properties.IndentFirstLine > 0 { + xml += fmt.Sprintf(" w:firstLine=\"%d\"", p.Properties.IndentFirstLine) + } + xml += " />" + } + + // 对齐方式 + if p.Properties.Alignment != "" { + xml += fmt.Sprintf("", p.Properties.Alignment) } xml += "" diff --git a/document/run.go b/document/run.go index 08ec93a..a069b04 100644 --- a/document/run.go +++ b/document/run.go @@ -206,115 +206,130 @@ func (r *Run) AddPageNumber() *Run { return r.AddField("begin", " PAGE ") } -// ToXML 将文本运行转换为XML +// ToXML 将Run转换为XML func (r *Run) ToXML() string { xml := "" - // 添加文本运行属性 + // 添加运行属性 - 严格按照Word XML规范顺序 xml += "" - // 字体 + // 运行样式(缺失,预留位置) + + // 1. 字体 if r.Properties.FontFamily != "" { - xml += "" + xml += fmt.Sprintf("", + r.Properties.FontFamily, + r.Properties.FontFamily, + r.Properties.FontFamily, + r.Properties.FontFamily) } - // 字号 - if r.Properties.FontSize > 0 { - xml += "" - xml += "" - } - - // 颜色 - if r.Properties.Color != "" { - xml += "" - } - - // 粗体 + // 2. 加粗 if r.Properties.Bold { xml += "" + // 3. 复杂文本加粗 xml += "" } - // 斜体 + // 4. 斜体 if r.Properties.Italic { xml += "" + // 5. 复杂文本斜体 xml += "" } - // 下划线 - if r.Properties.Underline != "" { - xml += "" - } - - // 删除线 - if r.Properties.Strike { - xml += "" - } - - // 双删除线 - if r.Properties.DoubleStrike { - xml += "" - } - - // 突出显示颜色 - if r.Properties.Highlight != "" { - xml += "" - } - - // 全部大写 + // 6. 全部大写 if r.Properties.Caps { xml += "" } - // 小型大写 + // 7. 小型大写 if r.Properties.SmallCaps { xml += "" } - // 字符间距 + // 8. 删除线 + if r.Properties.Strike { + xml += "" + } + + // 9. 双删除线 + if r.Properties.DoubleStrike { + xml += "" + } + + // 10-18. 其他格式(outline, shadow, emboss等,缺失,预留位置) + + // 19. 颜色 + if r.Properties.Color != "" { + xml += fmt.Sprintf("", r.Properties.Color) + } + + // 20. 字符间距 if r.Properties.CharacterSpacing != 0 { - xml += "" + xml += fmt.Sprintf("", r.Properties.CharacterSpacing) } - // 底纹 - if r.Properties.Shading != nil { - xml += "" + // 21-23. 其他属性(缺失,预留位置) + + // 24. 字号 + if r.Properties.FontSize > 0 { + xml += fmt.Sprintf("", r.Properties.FontSize) + // 25. 复杂文本字号 + xml += fmt.Sprintf("", r.Properties.FontSize) } - // 上标/下标 + // 26. 突出显示 + if r.Properties.Highlight != "" { + xml += fmt.Sprintf("", r.Properties.Highlight) + } + + // 27. 下划线 + if r.Properties.Underline != "" { + xml += fmt.Sprintf("", r.Properties.Underline) + } + + // 28-31. 其他属性(缺失,预留位置) + + // 32. 上标/下标 if r.Properties.Superscript { xml += "" } else if r.Properties.Subscript { xml += "" } else if r.Properties.VertAlign != "" { - xml += "" + xml += fmt.Sprintf("", r.Properties.VertAlign) } - // 从右到左文本方向 + // 33. 从右到左文本方向 if r.Properties.RTL { xml += "" } - // 语言 + // 34-35. 其他属性(缺失,预留位置) + + // 36. 语言 if r.Properties.Language != "" { - xml += "" + xml += fmt.Sprintf("", r.Properties.Language) + } + + // 底纹(shd应该在位置30) + if r.Properties.Shading != nil { + xml += fmt.Sprintf("", + r.Properties.Shading.Pattern, + r.Properties.Shading.Fill, + r.Properties.Shading.Color) } xml += "" // 添加分隔符 if r.BreakType != "" { - xml += "" + xml += fmt.Sprintf("", r.BreakType) } // 添加文本 if r.Text != "" { - xml += "" + r.Text + "" + xml += fmt.Sprintf("%s", r.Text) } // 添加图形 diff --git a/document/table.go b/document/table.go index 13f060e..8e049a4 100644 --- a/document/table.go +++ b/document/table.go @@ -387,108 +387,105 @@ func (t *Table) ToXML() string { // 添加表格属性 xml += "" - // 表格宽度 - if t.Properties.Width > 0 { - xml += "" - } else { - xml += "" + // 表格样式ID + if t.Properties.Style != "" { + xml += fmt.Sprintf("", t.Properties.Style) } + // 表格宽度 + xml += fmt.Sprintf("", t.Properties.Width, t.Properties.WidthType) + // 表格对齐方式 if t.Properties.Alignment != "" { - xml += "" + xml += fmt.Sprintf("", t.Properties.Alignment) } // 表格缩进 if t.Properties.Indent > 0 { - xml += "" + xml += fmt.Sprintf("", t.Properties.Indent) } // 表格边框 if t.Properties.Borders != nil { xml += "" if t.Properties.Borders.Top != nil { - xml += "" - } - if t.Properties.Borders.Bottom != nil { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Borders.Top.Style, + t.Properties.Borders.Top.Size, + t.Properties.Borders.Top.Space, + t.Properties.Borders.Top.Color) } if t.Properties.Borders.Left != nil { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Borders.Left.Style, + t.Properties.Borders.Left.Size, + t.Properties.Borders.Left.Space, + t.Properties.Borders.Left.Color) + } + if t.Properties.Borders.Bottom != nil { + xml += fmt.Sprintf("", + t.Properties.Borders.Bottom.Style, + t.Properties.Borders.Bottom.Size, + t.Properties.Borders.Bottom.Space, + t.Properties.Borders.Bottom.Color) } if t.Properties.Borders.Right != nil { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Borders.Right.Style, + t.Properties.Borders.Right.Size, + t.Properties.Borders.Right.Space, + t.Properties.Borders.Right.Color) } if t.Properties.Borders.InsideH != nil { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Borders.InsideH.Style, + t.Properties.Borders.InsideH.Size, + t.Properties.Borders.InsideH.Space, + t.Properties.Borders.InsideH.Color) } if t.Properties.Borders.InsideV != nil { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Borders.InsideV.Style, + t.Properties.Borders.InsideV.Size, + t.Properties.Borders.InsideV.Space, + t.Properties.Borders.InsideV.Color) } xml += "" } - // 表格单元格边距 + // 表格布局 + if t.Properties.Layout != "" { + xml += fmt.Sprintf("", t.Properties.Layout) + } + + // 单元格边距 if t.Properties.CellMargin != nil { xml += "" if t.Properties.CellMargin.Top > 0 { - xml += "" - } - if t.Properties.CellMargin.Bottom > 0 { - xml += "" + xml += fmt.Sprintf("", t.Properties.CellMargin.Top) } if t.Properties.CellMargin.Left > 0 { - xml += "" + xml += fmt.Sprintf("", t.Properties.CellMargin.Left) + } + if t.Properties.CellMargin.Bottom > 0 { + xml += fmt.Sprintf("", t.Properties.CellMargin.Bottom) } if t.Properties.CellMargin.Right > 0 { - xml += "" + xml += fmt.Sprintf("", t.Properties.CellMargin.Right) } xml += "" } - // 表格布局方式 - if t.Properties.Layout != "" { - xml += "" - } - - // 表格样式 - if t.Properties.Style != "" { - xml += "" - } - // 表格外观 if t.Properties.Look != "" { - xml += "" + xml += fmt.Sprintf("", + t.Properties.Look, + formatBoolToWXml(t.Properties.FirstRow), + formatBoolToWXml(t.Properties.LastRow), + formatBoolToWXml(t.Properties.FirstColumn), + formatBoolToWXml(t.Properties.LastColumn), + formatBoolToWXml(t.Properties.NoHBand), + formatBoolToWXml(t.Properties.NoVBand)) } xml += "" @@ -504,129 +501,133 @@ func (t *Table) ToXML() string { // 添加所有行的XML for _, row := range t.Rows { - xml += "" - - // 添加行属性 - xml += "" - - // 行高 - if row.Properties.Height > 0 { - xml += "" - } - - // 不允许跨页分割 - if row.Properties.CantSplit { - xml += "" - } - - // 表头行 - if row.Properties.IsHeader { - xml += "" - } - - xml += "" - - // 添加所有单元格的XML - for _, cell := range row.Cells { - xml += "" - - // 添加单元格属性 - xml += "" - - // 单元格宽度 - if cell.Properties.Width > 0 { - xml += "" - } else { - xml += "" - } - - // 垂直对齐方式 - if cell.Properties.VertAlign != "" { - xml += "" - } - - // 单元格边框 - if cell.Properties.Borders != nil { - xml += "" - if cell.Properties.Borders.Top != nil { - xml += "" - } - if cell.Properties.Borders.Bottom != nil { - xml += "" - } - if cell.Properties.Borders.Left != nil { - xml += "" - } - if cell.Properties.Borders.Right != nil { - xml += "" - } - xml += "" - } - - // 底纹 - if cell.Properties.Shading != nil { - xml += "" - } - - // 跨列数 - if cell.Properties.GridSpan > 1 { - xml += "" - } - - // 垂直合并 - if cell.Properties.VMerge != "" { - xml += "" - } - - // 不换行 - if cell.Properties.NoWrap { - xml += "" - } - - // 适应文本 - if cell.Properties.FitText { - xml += "" - } - - xml += "" - - // 添加所有内容元素的XML - for _, content := range cell.Content { - switch v := content.(type) { - case *Paragraph: - xml += v.ToXML() - case *Table: - xml += v.ToXML() - } - } - - // 如果单元格没有内容,添加一个空段落 - if len(cell.Content) == 0 { - xml += "" - } - - xml += "" - } - - xml += "" + xml += row.ToXML() } xml += "" return xml } + +// formatBoolToWXml 将布尔值转换为Word XML中使用的字符串表示 +func formatBoolToWXml(value bool) string { + if value { + return "1" + } + return "0" +} + +// ToXML 将表格行转换为XML +func (r *TableRow) ToXML() string { + xml := "" + + // 添加行属性 + xml += "" + + // 行高 + if r.Properties.Height > 0 { + xml += "" + } + + // 不允许跨页分割 + if r.Properties.CantSplit { + xml += "" + } + + // 表头行 + if r.Properties.IsHeader { + xml += "" + } + + xml += "" + + // 添加所有单元格的XML + for _, cell := range r.Cells { + xml += cell.ToXML() + } + + xml += "" + return xml +} + +// ToXML 将表格单元格转换为XML +func (c *TableCell) ToXML() string { + xml := "" + + // 添加单元格属性 + xml += "" + + // 单元格宽度 + if c.Properties.Width > 0 { + xml += "" + } else { + xml += "" + } + + // 垂直对齐方式 + if c.Properties.VertAlign != "" { + xml += "" + } + + // 单元格边框 + if c.Properties.Borders != nil { + xml += "" + if c.Properties.Borders.Top != nil { + xml += "" + } + if c.Properties.Borders.Bottom != nil { + xml += "" + } + if c.Properties.Borders.Left != nil { + xml += "" + } + if c.Properties.Borders.Right != nil { + xml += "" + } + xml += "" + } + + // 底纹 + if c.Properties.Shading != nil { + xml += "" + } + + // 跨列数 + if c.Properties.GridSpan > 1 { + xml += "" + } + + // 垂直合并 + if c.Properties.VMerge != "" { + xml += "" + } + + // 不换行 + if c.Properties.NoWrap { + xml += "" + } + + // 适应文本 + if c.Properties.FitText { + xml += "" + } + + xml += "" + + // 添加所有内容元素的XML + for _, content := range c.Content { + switch v := content.(type) { + case *Paragraph: + xml += v.ToXML() + case *Table: + xml += v.ToXML() + } + } + + // 如果单元格没有内容,添加一个空段落 + if len(c.Content) == 0 { + xml += "" + } + + xml += "" + return xml +} diff --git a/workbook/content_types.go b/workbook/content_types.go index 2e15f7e..38ef0bf 100644 --- a/workbook/content_types.go +++ b/workbook/content_types.go @@ -40,7 +40,6 @@ func NewContentTypes() *ContentTypes { ct.AddOverride("/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml") ct.AddOverride("/xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml") ct.AddOverride("/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml") - ct.AddOverride("/xl/worksheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml") ct.AddOverride("/xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml") return ct diff --git a/workbook/examples/simple/main.go b/workbook/examples/simple/main.go index f824d7a..4c5abf8 100644 --- a/workbook/examples/simple/main.go +++ b/workbook/examples/simple/main.go @@ -259,7 +259,7 @@ func main() { }) // 保存Excel文件 - err := wb.Save("sales_report.xlsx") + err := wb.Save("./workbook/examples/simple/sales_report.xlsx") if err != nil { fmt.Println("保存Excel文件时出错:", err) return diff --git a/workbook/workbook.go b/workbook/workbook.go index add52d2..7993999 100644 --- a/workbook/workbook.go +++ b/workbook/workbook.go @@ -71,6 +71,11 @@ func (wb *Workbook) Save(filename string) error { zipWriter := zip.NewWriter(file) defer zipWriter.Close() + // 为每个工作表添加内容类型覆盖 + for i := range wb.Worksheets { + wb.ContentTypes.AddWorksheetOverride(i + 1) + } + // 添加[Content_Types].xml contentTypesWriter, err := zipWriter.Create("[Content_Types].xml") if err != nil {