mirror of
https://github.com/ZeroHawkeye/wordZero.git
synced 2025-09-26 20:01:17 +08:00
- 修复模板中字体没有继承问题
- 修复多种样式没有在模板继承中添加到问题
This commit is contained in:
@@ -109,10 +109,11 @@ type ParagraphProperties struct {
|
||||
|
||||
// Spacing 间距设置
|
||||
type Spacing struct {
|
||||
XMLName xml.Name `xml:"w:spacing"`
|
||||
Before string `xml:"w:before,attr,omitempty"`
|
||||
After string `xml:"w:after,attr,omitempty"`
|
||||
Line string `xml:"w:line,attr,omitempty"`
|
||||
XMLName xml.Name `xml:"w:spacing"`
|
||||
Before string `xml:"w:before,attr,omitempty"`
|
||||
After string `xml:"w:after,attr,omitempty"`
|
||||
Line string `xml:"w:line,attr,omitempty"`
|
||||
LineRule string `xml:"w:lineRule,attr,omitempty"`
|
||||
}
|
||||
|
||||
// Justification 对齐方式
|
||||
@@ -135,9 +136,15 @@ type Run struct {
|
||||
type RunProperties struct {
|
||||
XMLName xml.Name `xml:"w:rPr"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -146,23 +153,56 @@ type Bold struct {
|
||||
XMLName xml.Name `xml:"w:b"`
|
||||
}
|
||||
|
||||
// BoldCs 复杂脚本粗体
|
||||
type BoldCs struct {
|
||||
XMLName xml.Name `xml:"w:bCs"`
|
||||
}
|
||||
|
||||
// Italic 斜体
|
||||
type Italic struct {
|
||||
XMLName xml.Name `xml:"w:i"`
|
||||
}
|
||||
|
||||
// ItalicCs 复杂脚本斜体
|
||||
type ItalicCs struct {
|
||||
XMLName xml.Name `xml:"w:iCs"`
|
||||
}
|
||||
|
||||
// Underline 下划线
|
||||
type Underline struct {
|
||||
XMLName xml.Name `xml:"w:u"`
|
||||
Val string `xml:"w:val,attr,omitempty"`
|
||||
}
|
||||
|
||||
// Strike 删除线
|
||||
type Strike struct {
|
||||
XMLName xml.Name `xml:"w:strike"`
|
||||
}
|
||||
|
||||
// FontSize 字体大小
|
||||
type FontSize struct {
|
||||
XMLName xml.Name `xml:"w:sz"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
// FontSizeCs 复杂脚本字体大小
|
||||
type FontSizeCs struct {
|
||||
XMLName xml.Name `xml:"w:szCs"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
// Color 颜色
|
||||
type Color struct {
|
||||
XMLName xml.Name `xml:"w:color"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
// Highlight 背景色
|
||||
type Highlight struct {
|
||||
XMLName xml.Name `xml:"w:highlight"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
// Text 文本内容
|
||||
type Text struct {
|
||||
XMLName xml.Name `xml:"w:t"`
|
||||
@@ -380,6 +420,13 @@ func Open(filename string) (*Document, error) {
|
||||
return nil, WrapErrorWithContext("parse_document", err, filename)
|
||||
}
|
||||
|
||||
// 解析样式文件
|
||||
if err := doc.parseStyles(); err != nil {
|
||||
Debugf("解析样式失败,使用默认样式: %v", err)
|
||||
// 如果样式解析失败,重新初始化为默认样式
|
||||
doc.styleManager = style.NewStyleManager()
|
||||
}
|
||||
|
||||
Infof("成功打开文档: %s", filename)
|
||||
return doc, nil
|
||||
}
|
||||
@@ -1197,6 +1244,7 @@ func (d *Document) parseParagraphProperties(decoder *xml.Decoder, paragraph *Par
|
||||
spacing.Before = getAttributeValue(t.Attr, "before")
|
||||
spacing.After = getAttributeValue(t.Attr, "after")
|
||||
spacing.Line = getAttributeValue(t.Attr, "line")
|
||||
spacing.LineRule = getAttributeValue(t.Attr, "lineRule")
|
||||
paragraph.Properties.Spacing = spacing
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
@@ -1295,11 +1343,32 @@ func (d *Document) parseRunProperties(decoder *xml.Decoder, run *Run) error {
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "bCs":
|
||||
run.Properties.BoldCs = &BoldCs{}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "i":
|
||||
run.Properties.Italic = &Italic{}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "iCs":
|
||||
run.Properties.ItalicCs = &ItalicCs{}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "u":
|
||||
val := getAttributeValue(t.Attr, "val")
|
||||
run.Properties.Underline = &Underline{Val: val}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "strike":
|
||||
run.Properties.Strike = &Strike{}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "sz":
|
||||
val := getAttributeValue(t.Attr, "val")
|
||||
if val != "" {
|
||||
@@ -1308,6 +1377,14 @@ func (d *Document) parseRunProperties(decoder *xml.Decoder, run *Run) error {
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "szCs":
|
||||
val := getAttributeValue(t.Attr, "val")
|
||||
if val != "" {
|
||||
run.Properties.FontSizeCs = &FontSizeCs{Val: val}
|
||||
}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "color":
|
||||
val := getAttributeValue(t.Attr, "val")
|
||||
if val != "" {
|
||||
@@ -1316,6 +1393,14 @@ func (d *Document) parseRunProperties(decoder *xml.Decoder, run *Run) error {
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "highlight":
|
||||
val := getAttributeValue(t.Attr, "val")
|
||||
if val != "" {
|
||||
run.Properties.Highlight = &Highlight{Val: val}
|
||||
}
|
||||
if err := d.skipElement(decoder, t.Name.Local); err != nil {
|
||||
return err
|
||||
}
|
||||
case "rFonts":
|
||||
ascii := getAttributeValue(t.Attr, "ascii")
|
||||
hAnsi := getAttributeValue(t.Attr, "hAnsi")
|
||||
@@ -1793,6 +1878,25 @@ func (d *Document) serializeStyles() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseStyles 解析样式文件
|
||||
func (d *Document) parseStyles() error {
|
||||
Debugf("开始解析样式文件")
|
||||
|
||||
// 查找样式文件
|
||||
stylesData, ok := d.parts["word/styles.xml"]
|
||||
if !ok {
|
||||
return WrapError("parse_styles", fmt.Errorf("样式文件不存在"))
|
||||
}
|
||||
|
||||
// 使用样式管理器解析样式
|
||||
if err := d.styleManager.LoadStylesFromDocument(stylesData); err != nil {
|
||||
return WrapError("parse_styles", err)
|
||||
}
|
||||
|
||||
Debugf("样式解析完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToBytes 将文档转换为字节数组
|
||||
func (d *Document) ToBytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
@@ -707,9 +707,10 @@ func (te *TemplateEngine) cloneParagraphProperties(source *ParagraphProperties)
|
||||
// 复制间距
|
||||
if source.Spacing != nil {
|
||||
props.Spacing = &Spacing{
|
||||
Before: source.Spacing.Before,
|
||||
After: source.Spacing.After,
|
||||
Line: source.Spacing.Line,
|
||||
Before: source.Spacing.Before,
|
||||
After: source.Spacing.After,
|
||||
Line: source.Spacing.Line,
|
||||
LineRule: source.Spacing.LineRule,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,11 +786,33 @@ func (te *TemplateEngine) cloneRunProperties(source *RunProperties) *RunProperti
|
||||
props.Bold = &Bold{}
|
||||
}
|
||||
|
||||
// 复制复杂脚本粗体
|
||||
if source.BoldCs != nil {
|
||||
props.BoldCs = &BoldCs{}
|
||||
}
|
||||
|
||||
// 复制斜体
|
||||
if source.Italic != nil {
|
||||
props.Italic = &Italic{}
|
||||
}
|
||||
|
||||
// 复制复杂脚本斜体
|
||||
if source.ItalicCs != nil {
|
||||
props.ItalicCs = &ItalicCs{}
|
||||
}
|
||||
|
||||
// 复制下划线
|
||||
if source.Underline != nil {
|
||||
props.Underline = &Underline{
|
||||
Val: source.Underline.Val,
|
||||
}
|
||||
}
|
||||
|
||||
// 复制删除线
|
||||
if source.Strike != nil {
|
||||
props.Strike = &Strike{}
|
||||
}
|
||||
|
||||
// 复制字体大小
|
||||
if source.FontSize != nil {
|
||||
props.FontSize = &FontSize{
|
||||
@@ -797,6 +820,13 @@ func (te *TemplateEngine) cloneRunProperties(source *RunProperties) *RunProperti
|
||||
}
|
||||
}
|
||||
|
||||
// 复制复杂脚本字体大小
|
||||
if source.FontSizeCs != nil {
|
||||
props.FontSizeCs = &FontSizeCs{
|
||||
Val: source.FontSizeCs.Val,
|
||||
}
|
||||
}
|
||||
|
||||
// 复制颜色
|
||||
if source.Color != nil {
|
||||
props.Color = &Color{
|
||||
@@ -804,6 +834,13 @@ func (te *TemplateEngine) cloneRunProperties(source *RunProperties) *RunProperti
|
||||
}
|
||||
}
|
||||
|
||||
// 复制背景色
|
||||
if source.Highlight != nil {
|
||||
props.Highlight = &Highlight{
|
||||
Val: source.Highlight.Val,
|
||||
}
|
||||
}
|
||||
|
||||
// 完整复制字体族属性,包括所有字体设置
|
||||
if source.FontFamily != nil {
|
||||
props.FontFamily = &FontFamily{
|
||||
@@ -1347,6 +1384,12 @@ func (te *TemplateEngine) RenderTemplateToDocument(templateName string, data *Te
|
||||
|
||||
// replaceVariablesInDocument 在文档结构中直接替换变量
|
||||
func (te *TemplateEngine) replaceVariablesInDocument(doc *Document, data *TemplateData) error {
|
||||
// 首先处理文档级别的循环(跨段落)
|
||||
err := te.processDocumentLevelLoops(doc, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, element := range doc.Body.Elements {
|
||||
switch elem := element.(type) {
|
||||
case *Paragraph:
|
||||
@@ -1368,6 +1411,117 @@ func (te *TemplateEngine) replaceVariablesInDocument(doc *Document, data *Templa
|
||||
return nil
|
||||
}
|
||||
|
||||
// processDocumentLevelLoops 处理文档级别的循环(跨段落)
|
||||
func (te *TemplateEngine) processDocumentLevelLoops(doc *Document, data *TemplateData) error {
|
||||
elements := doc.Body.Elements
|
||||
newElements := make([]interface{}, 0)
|
||||
|
||||
i := 0
|
||||
for i < len(elements) {
|
||||
element := elements[i]
|
||||
|
||||
// 检查当前元素是否包含循环开始标记
|
||||
if para, ok := element.(*Paragraph); ok {
|
||||
// 获取段落的完整文本
|
||||
fullText := ""
|
||||
for _, run := range para.Runs {
|
||||
fullText += run.Text.Content
|
||||
}
|
||||
|
||||
// 检查是否包含循环开始标记
|
||||
eachPattern := regexp.MustCompile(`\{\{#each\s+(\w+)\}\}`)
|
||||
matches := eachPattern.FindStringSubmatch(fullText)
|
||||
|
||||
if len(matches) > 1 {
|
||||
listVarName := matches[1]
|
||||
|
||||
// 找到循环结束位置
|
||||
loopEndIndex := -1
|
||||
templateElements := make([]interface{}, 0)
|
||||
|
||||
// 收集循环模板元素(从当前位置到结束标记)
|
||||
for j := i; j < len(elements); j++ {
|
||||
templateElements = append(templateElements, elements[j])
|
||||
|
||||
if nextPara, ok := elements[j].(*Paragraph); ok {
|
||||
nextText := ""
|
||||
for _, run := range nextPara.Runs {
|
||||
nextText += run.Text.Content
|
||||
}
|
||||
|
||||
if strings.Contains(nextText, "{{/each}}") {
|
||||
loopEndIndex = j
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if loopEndIndex >= 0 {
|
||||
// 处理循环
|
||||
if listData, exists := data.Lists[listVarName]; exists {
|
||||
// 为每个数据项生成元素
|
||||
for _, item := range listData {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
// 复制模板元素并替换变量
|
||||
for _, templateElement := range templateElements {
|
||||
if templatePara, ok := templateElement.(*Paragraph); ok {
|
||||
newPara := te.cloneParagraph(templatePara)
|
||||
|
||||
// 处理段落文本
|
||||
fullText := ""
|
||||
for _, run := range newPara.Runs {
|
||||
fullText += run.Text.Content
|
||||
}
|
||||
|
||||
// 移除循环标记
|
||||
content := fullText
|
||||
content = regexp.MustCompile(`\{\{#each\s+\w+\}\}`).ReplaceAllString(content, "")
|
||||
content = regexp.MustCompile(`\{\{/each\}\}`).ReplaceAllString(content, "")
|
||||
|
||||
// 替换变量
|
||||
for key, value := range itemMap {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
content = strings.ReplaceAll(content, placeholder, te.interfaceToString(value))
|
||||
}
|
||||
|
||||
// 如果内容不为空,创建新段落
|
||||
if strings.TrimSpace(content) != "" {
|
||||
newPara.Runs = []Run{{
|
||||
Text: Text{Content: content},
|
||||
Properties: &RunProperties{
|
||||
FontFamily: &FontFamily{
|
||||
ASCII: "仿宋",
|
||||
HAnsi: "仿宋",
|
||||
EastAsia: "仿宋",
|
||||
},
|
||||
Bold: &Bold{},
|
||||
},
|
||||
}}
|
||||
newElements = append(newElements, newPara)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 跳过循环模板元素
|
||||
i = loopEndIndex + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 不是循环元素,直接添加
|
||||
newElements = append(newElements, element)
|
||||
i++
|
||||
}
|
||||
|
||||
// 更新文档元素
|
||||
doc.Body.Elements = newElements
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceVariablesInParagraph 在段落中替换变量(改进版本,更好地保持样式)
|
||||
func (te *TemplateEngine) replaceVariablesInParagraph(para *Paragraph, data *TemplateData) error {
|
||||
// 首先识别所有变量占位符的位置
|
||||
@@ -1401,17 +1555,86 @@ func (te *TemplateEngine) replaceVariablesInParagraph(para *Paragraph, data *Tem
|
||||
return nil
|
||||
}
|
||||
|
||||
// 先处理循环语句(包括非表格循环)
|
||||
processedText, hasLoopChanges := te.processNonTableLoops(fullText, data)
|
||||
if hasLoopChanges {
|
||||
// 重新构建段落
|
||||
para.Runs = []Run{{
|
||||
Text: Text{Content: processedText},
|
||||
Properties: &RunProperties{
|
||||
FontFamily: &FontFamily{
|
||||
ASCII: "仿宋",
|
||||
HAnsi: "仿宋",
|
||||
EastAsia: "仿宋",
|
||||
},
|
||||
Bold: &Bold{},
|
||||
},
|
||||
}}
|
||||
fullText = processedText
|
||||
}
|
||||
|
||||
// 使用新的逐个变量替换方法
|
||||
newRuns, hasChanges := te.replaceVariablesSequentially(runInfos, fullText, data)
|
||||
newRuns, hasVarChanges := te.replaceVariablesSequentially(runInfos, fullText, data)
|
||||
|
||||
// 如果有变化,更新段落的Run
|
||||
if hasChanges {
|
||||
if hasVarChanges || hasLoopChanges {
|
||||
para.Runs = newRuns
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processNonTableLoops 处理非表格循环
|
||||
func (te *TemplateEngine) processNonTableLoops(content string, data *TemplateData) (string, bool) {
|
||||
eachPattern := regexp.MustCompile(`(?s)\{\{#each\s+(\w+)\}\}(.*?)\{\{/each\}\}`)
|
||||
matches := eachPattern.FindAllStringSubmatchIndex(content, -1)
|
||||
|
||||
if len(matches) == 0 {
|
||||
return content, false
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
lastEnd := 0
|
||||
hasChanges := false
|
||||
|
||||
for _, match := range matches {
|
||||
// 找到变量名和块内容
|
||||
fullMatch := content[match[0]:match[1]]
|
||||
submatch := eachPattern.FindStringSubmatch(fullMatch)
|
||||
if len(submatch) >= 3 {
|
||||
listVar := submatch[1]
|
||||
blockContent := submatch[2]
|
||||
|
||||
// 添加循环前的内容
|
||||
result.WriteString(content[lastEnd:match[0]])
|
||||
|
||||
// 处理循环
|
||||
if listData, exists := data.Lists[listVar]; exists {
|
||||
for _, item := range listData {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
loopContent := blockContent
|
||||
for key, value := range itemMap {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
loopContent = strings.ReplaceAll(loopContent, placeholder, te.interfaceToString(value))
|
||||
}
|
||||
result.WriteString(loopContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastEnd = match[1]
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
// 添加剩余内容
|
||||
if lastEnd < len(content) {
|
||||
result.WriteString(content[lastEnd:])
|
||||
}
|
||||
|
||||
return result.String(), hasChanges
|
||||
}
|
||||
|
||||
// replaceVariablesSequentially 逐个替换变量,保持样式
|
||||
func (te *TemplateEngine) replaceVariablesSequentially(originalRunInfos []struct {
|
||||
startIndex int
|
||||
@@ -1676,21 +1899,22 @@ func (te *TemplateEngine) renderTableTemplate(table *Table, data *TemplateData)
|
||||
|
||||
for i, row := range table.Rows {
|
||||
found := false
|
||||
// 重构每个单元格的文本,解决跨Run的变量问题
|
||||
for _, cell := range row.Cells {
|
||||
for _, para := range cell.Paragraphs {
|
||||
// 合并所有Run的文本
|
||||
fullText := ""
|
||||
for _, run := range para.Runs {
|
||||
if run.Text.Content != "" {
|
||||
eachPattern := regexp.MustCompile(`\{\{#each\s+(\w+)\}\}`)
|
||||
matches := eachPattern.FindStringSubmatch(run.Text.Content)
|
||||
if len(matches) > 1 {
|
||||
templateRowIndex = i
|
||||
listVarName = matches[1]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
fullText += run.Text.Content
|
||||
}
|
||||
if found {
|
||||
|
||||
// 检查合并后的文本中是否包含循环语法
|
||||
eachPattern := regexp.MustCompile(`\{\{#each\s+(\w+)\}\}`)
|
||||
matches := eachPattern.FindStringSubmatch(fullText)
|
||||
if len(matches) > 1 {
|
||||
templateRowIndex = i
|
||||
listVarName = matches[1]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1730,24 +1954,46 @@ func (te *TemplateEngine) renderTableTemplate(table *Table, data *TemplateData)
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
for i := range newRow.Cells {
|
||||
for j := range newRow.Cells[i].Paragraphs {
|
||||
for k := range newRow.Cells[i].Paragraphs[j].Runs {
|
||||
if newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content != "" {
|
||||
// 移除模板语法标记
|
||||
content := newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content
|
||||
content = regexp.MustCompile(`\{\{#each\s+\w+\}\}`).ReplaceAllString(content, "")
|
||||
content = regexp.MustCompile(`\{\{/each\}\}`).ReplaceAllString(content, "")
|
||||
// 合并所有Run的文本
|
||||
fullText := ""
|
||||
originalRuns := newRow.Cells[i].Paragraphs[j].Runs
|
||||
for _, run := range originalRuns {
|
||||
fullText += run.Text.Content
|
||||
}
|
||||
|
||||
// 替换变量
|
||||
for key, value := range itemMap {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
content = strings.ReplaceAll(content, placeholder, te.interfaceToString(value))
|
||||
}
|
||||
// 移除模板语法标记
|
||||
content := fullText
|
||||
content = regexp.MustCompile(`\{\{#each\s+\w+\}\}`).ReplaceAllString(content, "")
|
||||
content = regexp.MustCompile(`\{\{/each\}\}`).ReplaceAllString(content, "")
|
||||
|
||||
// 处理条件语句
|
||||
content = te.renderLoopConditionals(content, itemMap)
|
||||
// 替换变量
|
||||
for key, value := range itemMap {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
content = strings.ReplaceAll(content, placeholder, te.interfaceToString(value))
|
||||
}
|
||||
|
||||
newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content = content
|
||||
}
|
||||
// 处理条件语句
|
||||
content = te.renderLoopConditionals(content, itemMap)
|
||||
|
||||
// 重建Run结构,保持第一个Run的样式
|
||||
if len(originalRuns) > 0 {
|
||||
firstRun := originalRuns[0]
|
||||
newRun := te.cloneRun(&firstRun)
|
||||
newRun.Text.Content = content
|
||||
newRow.Cells[i].Paragraphs[j].Runs = []Run{newRun}
|
||||
} else {
|
||||
// 如果没有原始Run,创建新的
|
||||
newRow.Cells[i].Paragraphs[j].Runs = []Run{{
|
||||
Text: Text{Content: content},
|
||||
Properties: &RunProperties{
|
||||
FontFamily: &FontFamily{
|
||||
ASCII: "仿宋",
|
||||
HAnsi: "仿宋",
|
||||
EastAsia: "仿宋",
|
||||
},
|
||||
Bold: &Bold{},
|
||||
},
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -228,7 +228,7 @@ func (sm *StyleManager) initializePredefinedStyles() {
|
||||
sm.addSpecialStyles()
|
||||
}
|
||||
|
||||
// addNormalStyle 添加普通文本样式
|
||||
// addNormalStyle 添加Normal样式
|
||||
func (sm *StyleManager) addNormalStyle() {
|
||||
normalStyle := &Style{
|
||||
Type: string(StyleTypeParagraph),
|
||||
@@ -246,7 +246,7 @@ func (sm *StyleManager) addNormalStyle() {
|
||||
},
|
||||
RunPr: &RunProperties{
|
||||
FontSize: &FontSize{
|
||||
Val: "22", // 11磅字体(Word中以半磅为单位)
|
||||
Val: "21", // 五号字体(10.5磅,Word中以半磅为单位)
|
||||
},
|
||||
FontFamily: &FontFamily{
|
||||
ASCII: "Calibri",
|
||||
@@ -1335,3 +1335,80 @@ func convertRunPropertiesToMap(props *RunProperties) map[string]interface{} {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ParseStylesFromXML 从XML数据解析样式
|
||||
func (sm *StyleManager) ParseStylesFromXML(xmlData []byte) error {
|
||||
type stylesXML struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main styles"`
|
||||
Styles []Style `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main style"`
|
||||
}
|
||||
|
||||
var styles stylesXML
|
||||
if err := xml.Unmarshal(xmlData, &styles); err != nil {
|
||||
return fmt.Errorf("解析样式XML失败: %v", err)
|
||||
}
|
||||
|
||||
// 清空现有样式(除非我们想要合并)
|
||||
sm.styles = make(map[string]*Style)
|
||||
|
||||
// 添加解析的样式
|
||||
for i := range styles.Styles {
|
||||
sm.AddStyle(&styles.Styles[i])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergeStylesFromXML 从XML数据合并样式(保留现有样式,只添加新的)
|
||||
func (sm *StyleManager) MergeStylesFromXML(xmlData []byte) error {
|
||||
type stylesXML struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main styles"`
|
||||
Styles []Style `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main style"`
|
||||
}
|
||||
|
||||
var styles stylesXML
|
||||
if err := xml.Unmarshal(xmlData, &styles); err != nil {
|
||||
return fmt.Errorf("解析样式XML失败: %v", err)
|
||||
}
|
||||
|
||||
// 只添加不存在的样式,保留现有样式
|
||||
for i := range styles.Styles {
|
||||
if !sm.StyleExists(styles.Styles[i].StyleID) {
|
||||
sm.AddStyle(&styles.Styles[i])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadStylesFromDocument 从现有文档加载样式,优先保留原有样式设置
|
||||
func (sm *StyleManager) LoadStylesFromDocument(xmlData []byte) error {
|
||||
if len(xmlData) == 0 {
|
||||
// 如果没有样式数据,使用默认样式
|
||||
sm.initializePredefinedStyles()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 先解析现有样式
|
||||
if err := sm.ParseStylesFromXML(xmlData); err != nil {
|
||||
// 如果解析失败,使用默认样式
|
||||
sm.initializePredefinedStyles()
|
||||
return fmt.Errorf("解析现有样式失败,使用默认样式: %v", err)
|
||||
}
|
||||
|
||||
// 确保基本样式存在,如果不存在则添加
|
||||
if !sm.StyleExists("Normal") {
|
||||
sm.addNormalStyle()
|
||||
}
|
||||
|
||||
// 确保基本标题样式存在
|
||||
headingStyles := []string{"Heading1", "Heading2", "Heading3", "Heading4", "Heading5", "Heading6", "Heading7", "Heading8", "Heading9"}
|
||||
for _, styleID := range headingStyles {
|
||||
if !sm.StyleExists(styleID) {
|
||||
sm.addHeadingStyles()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Submodule wordZero.wiki updated: 3b91af7d88...6dc76fa50b
Reference in New Issue
Block a user