package document import ( "archive/zip" "fmt" "os" "path/filepath" "time" ) // Document 表示一个Word文档 type Document struct { Body *Body Properties *DocumentProperties Relationships *Relationships Styles *Styles Numbering *Numbering Footers []*Footer Headers []*Header Theme *Theme Settings *Settings ContentTypes *ContentTypes Rels *DocumentRels } // DocumentProperties 包含文档的元数据 type DocumentProperties struct { Title string Subject string Creator string Keywords string Description string LastModifiedBy string Revision int Created time.Time Modified time.Time } // NewDocument 创建一个新的Word文档 func NewDocument() *Document { return &Document{ Body: NewBody(), Properties: &DocumentProperties{ Created: time.Now(), Modified: time.Now(), Revision: 1, }, Relationships: NewRelationships(), Styles: NewStyles(), Numbering: NewNumbering(), Footers: make([]*Footer, 0), Headers: make([]*Header, 0), Theme: NewTheme(), Settings: NewSettings(), ContentTypes: NewContentTypes(), Rels: NewDocumentRels(), } } // Save 将文档保存到指定路径 func (d *Document) Save(path string) error { // 创建一个新的zip文件 zipFile, err := os.Create(path) if err != nil { return err } defer zipFile.Close() // 创建一个zip writer zipWriter := zip.NewWriter(zipFile) defer zipWriter.Close() // 添加[Content_Types].xml if err := d.addContentTypes(zipWriter); err != nil { return err } // 添加_rels/.rels if err := d.addRels(zipWriter); err != nil { return err } // 添加docProps/app.xml和docProps/core.xml if err := d.addDocProps(zipWriter); err != nil { return err } // 添加word/document.xml if err := d.addDocument(zipWriter); err != nil { return err } // 添加word/styles.xml if err := d.addStyles(zipWriter); err != nil { return err } // 添加word/numbering.xml if err := d.addNumbering(zipWriter); err != nil { return err } // 添加word/_rels/document.xml.rels if err := d.addDocumentRels(zipWriter); err != nil { return err } // 添加word/theme/theme1.xml if err := d.addTheme(zipWriter); err != nil { return err } // 添加word/settings.xml if err := d.addSettings(zipWriter); err != nil { return err } // 添加页眉 for i, header := range d.Headers { if err := d.addHeader(zipWriter, header, i+1); err != nil { return err } } // 添加页脚 for i, footer := range d.Footers { if err := d.addFooter(zipWriter, footer, i+1); err != nil { return err } } // 添加图片 for _, rel := range d.Rels.Relationships.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") { if err := d.addImage(zipWriter, rel); err != nil { return err } } return nil } // 以下是内部方法,用于将各个部分添加到zip文件中 func (d *Document) addContentTypes(zipWriter *zip.Writer) error { // 添加[Content_Types].xml w, err := zipWriter.Create("[Content_Types].xml") if err != nil { return err } _, err = w.Write([]byte(d.ContentTypes.ToXML())) return err } func (d *Document) addRels(zipWriter *zip.Writer) error { // 添加_rels/.rels w, err := zipWriter.Create("_rels/.rels") if err != nil { return err } // 创建基本关系 rels := NewRelationships() rels.AddRelationship("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", "document/document.xml") rels.AddRelationship("rId2", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", "docProps/core.xml") rels.AddRelationship("rId3", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", "docProps/app.xml") _, err = w.Write([]byte(rels.ToXML())) return err } func (d *Document) addDocProps(zipWriter *zip.Writer) error { // 添加docProps/app.xml w1, err := zipWriter.Create("docProps/app.xml") if err != nil { return err } // 创建app.xml内容 appXML := "\n" appXML += "\n" appXML += "go-flowdoc\n" appXML += "1.0.0\n" appXML += "\n" _, err = w1.Write([]byte(appXML)) if err != nil { return err } // 添加docProps/core.xml w2, err := zipWriter.Create("docProps/core.xml") if err != nil { return err } // 创建core.xml内容 coreXML := "\n" coreXML += "\n" if d.Properties.Title != "" { coreXML += "" + d.Properties.Title + "\n" } if d.Properties.Subject != "" { coreXML += "" + d.Properties.Subject + "\n" } if d.Properties.Creator != "" { coreXML += "" + d.Properties.Creator + "\n" } if d.Properties.Keywords != "" { coreXML += "" + d.Properties.Keywords + "\n" } if d.Properties.Description != "" { coreXML += "" + d.Properties.Description + "\n" } if d.Properties.LastModifiedBy != "" { coreXML += "" + d.Properties.LastModifiedBy + "\n" } if d.Properties.Revision > 0 { coreXML += "" + fmt.Sprintf("%d", d.Properties.Revision) + "\n" } // 格式化时间 createdTime := d.Properties.Created.Format("2006-01-02T15:04:05Z") modifiedTime := d.Properties.Modified.Format("2006-01-02T15:04:05Z") coreXML += "" + createdTime + "\n" coreXML += "" + modifiedTime + "\n" coreXML += "\n" _, err = w2.Write([]byte(coreXML)) return err } func (d *Document) addDocument(zipWriter *zip.Writer) error { // 添加word/document.xml w, err := zipWriter.Create("document/document.xml") if err != nil { return err } // 创建document.xml内容 docXML := "\n" docXML += "\n" // 添加文档主体 docXML += d.Body.ToXML() docXML += "" _, err = w.Write([]byte(docXML)) return err } func (d *Document) addStyles(zipWriter *zip.Writer) error { // 添加word/styles.xml w, err := zipWriter.Create("document/styles.xml") if err != nil { return err } _, err = w.Write([]byte(d.Styles.ToXML())) return err } func (d *Document) addNumbering(zipWriter *zip.Writer) error { // 添加word/numbering.xml w, err := zipWriter.Create("document/numbering.xml") if err != nil { return err } _, err = w.Write([]byte(d.Numbering.ToXML())) return err } func (d *Document) addDocumentRels(zipWriter *zip.Writer) error { // 添加word/_rels/document.xml.rels w, err := zipWriter.Create("document/_rels/document.xml.rels") if err != nil { return err } _, err = w.Write([]byte(d.Rels.ToXML())) return err } func (d *Document) addTheme(zipWriter *zip.Writer) error { // 添加word/theme/theme1.xml w, err := zipWriter.Create("document/theme/theme1.xml") if err != nil { return err } _, err = w.Write([]byte(d.Theme.ToXML())) return err } func (d *Document) addSettings(zipWriter *zip.Writer) error { // 添加word/settings.xml w, err := zipWriter.Create("document/settings.xml") if err != nil { return err } _, err = w.Write([]byte(d.Settings.ToXML())) return err } func (d *Document) addHeader(zipWriter *zip.Writer, header *Header, index int) error { // 添加word/header{index}.xml headerPath := fmt.Sprintf("document/header%d.xml", index) w, err := zipWriter.Create(headerPath) if err != nil { return err } _, err = w.Write([]byte(header.ToXML())) return err } func (d *Document) addFooter(zipWriter *zip.Writer, footer *Footer, index int) error { // 添加word/footer{index}.xml footerPath := fmt.Sprintf("document/footer%d.xml", index) w, err := zipWriter.Create(footerPath) if err != nil { return err } _, err = w.Write([]byte(footer.ToXML())) return err } func (d *Document) addImage(zipWriter *zip.Writer, rel *Relationship) error { // 从关系中提取图片ID和路径 imageID := rel.ID imagePath := rel.Target // 查找对应的Drawing对象 var imageData []byte // 在文档主体中查找 for _, para := range d.Body.Content { if p, ok := para.(*Paragraph); ok { for _, run := range p.Runs { if run.Drawing != nil && run.Drawing.ID == imageID { imageData = run.Drawing.ImageData break } } } } // 在页眉中查找 if len(imageData) == 0 { for _, header := range d.Headers { for _, content := range header.Content { if p, ok := content.(*Paragraph); ok { for _, run := range p.Runs { if run.Drawing != nil && run.Drawing.ID == imageID { imageData = run.Drawing.ImageData break } } } } } } // 在页脚中查找 if len(imageData) == 0 { for _, footer := range d.Footers { for _, content := range footer.Content { if p, ok := content.(*Paragraph); ok { for _, run := range p.Runs { if run.Drawing != nil && run.Drawing.ID == imageID { imageData = run.Drawing.ImageData break } } } } } } if len(imageData) == 0 { return fmt.Errorf("未找到图片数据: %s", imageID) } // 添加图片文件 w, err := zipWriter.Create("document/" + imagePath) if err != nil { return err } _, err = w.Write(imageData) return err } // AddParagraph 向文档添加一个段落 func (d *Document) AddParagraph() *Paragraph { return d.Body.AddParagraph() } // AddTable 向文档添加一个表格 func (d *Document) AddTable(rows, cols int) *Table { return d.Body.AddTable(rows, cols) } // AddPageBreak 向文档添加一个分页符 func (d *Document) AddPageBreak() *Paragraph { return d.Body.AddPageBreak() } // AddSectionBreak 向文档添加一个分节符 func (d *Document) AddSectionBreak() *Paragraph { return d.Body.AddSectionBreak() } // AddHeader 向文档添加一个页眉并返回它 func (d *Document) AddHeader() *Header { header := NewHeader() d.Headers = append(d.Headers, header) // 添加页眉关系 headerID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) headerPath := fmt.Sprintf("header%d.xml", len(d.Headers)) d.Rels.AddHeader(headerID, headerPath) // 添加页眉内容类型 d.ContentTypes.AddHeaderOverride(len(d.Headers)) return header } // AddFooter 向文档添加一个页脚并返回它 func (d *Document) AddFooter() *Footer { footer := NewFooter() d.Footers = append(d.Footers, footer) // 添加页脚关系 footerID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) footerPath := fmt.Sprintf("footer%d.xml", len(d.Footers)) d.Rels.AddFooter(footerID, footerPath) // 添加页脚内容类型 d.ContentTypes.AddFooterOverride(len(d.Footers)) return footer } // AddImage 向文档添加一个图片 func (d *Document) AddImage(path string, width, height int) (*Run, error) { // 创建一个新段落和运行 para := d.AddParagraph() run := para.AddRun() // 创建图片 drawing := NewDrawing() err := drawing.SetImagePath(path) if err != nil { return nil, err } // 设置图片大小 drawing.SetSize(width, height) // 添加图片关系 imageID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) imageName := filepath.Base(path) imagePath := fmt.Sprintf("media/%s", imageName) d.Rels.AddImage(imageID, imagePath) // 设置图片ID drawing.ID = imageID // 添加图片到运行 run.AddDrawing(drawing) return run, nil } // AddImageBytes 通过字节数据添加图片 func (d *Document) AddImageBytes(data []byte, format, name string, width, height int) (*Run, error) { // 创建一个新段落和运行 para := d.AddParagraph() run := para.AddRun() // 创建图片 drawing := NewDrawing() drawing.SetImageData(data) drawing.SetName(name) // 设置图片大小 drawing.SetSize(width, height) // 添加图片关系 imageID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) imagePath := fmt.Sprintf("media/%s.%s", name, format) d.Rels.AddImage(imageID, imagePath) // 设置图片ID drawing.ID = imageID // 添加图片到运行 run.AddDrawing(drawing) return run, nil } // SetTitle 设置文档标题 func (d *Document) SetTitle(title string) *Document { d.Properties.Title = title return d } // SetSubject 设置文档主题 func (d *Document) SetSubject(subject string) *Document { d.Properties.Subject = subject return d } // SetCreator 设置文档创建者 func (d *Document) SetCreator(creator string) *Document { d.Properties.Creator = creator d.Properties.LastModifiedBy = creator return d } // SetKeywords 设置文档关键词 func (d *Document) SetKeywords(keywords string) *Document { d.Properties.Keywords = keywords return d } // SetDescription 设置文档描述 func (d *Document) SetDescription(description string) *Document { d.Properties.Description = description return d } // SetLastModifiedBy 设置文档最后修改者 func (d *Document) SetLastModifiedBy(lastModifiedBy string) *Document { d.Properties.LastModifiedBy = lastModifiedBy return d } // SetRevision 设置文档修订版本 func (d *Document) SetRevision(revision int) *Document { d.Properties.Revision = revision return d } // SetCreated 设置文档创建时间 func (d *Document) SetCreated(created time.Time) *Document { d.Properties.Created = created return d } // SetModified 设置文档修改时间 func (d *Document) SetModified(modified time.Time) *Document { d.Properties.Modified = modified return d } // SetPageSize 设置页面大小 func (d *Document) SetPageSize(width, height int, orientation string) *Document { d.Body.SectionProperties.PageSize.Width = width d.Body.SectionProperties.PageSize.Height = height d.Body.SectionProperties.PageSize.Orientation = orientation return d } // SetPageSizeA4 设置页面大小为A4 func (d *Document) SetPageSizeA4(landscape bool) *Document { if landscape { return d.SetPageSize(16838, 11906, "landscape") } return d.SetPageSize(11906, 16838, "portrait") } // SetPageSizeA5 设置页面大小为A5 func (d *Document) SetPageSizeA5(landscape bool) *Document { if landscape { return d.SetPageSize(11906, 8419, "landscape") } return d.SetPageSize(8419, 11906, "portrait") } // SetPageSizeLetter 设置页面大小为Letter func (d *Document) SetPageSizeLetter(landscape bool) *Document { if landscape { return d.SetPageSize(15840, 12240, "landscape") } return d.SetPageSize(12240, 15840, "portrait") } // SetPageMargin 设置页面边距 func (d *Document) SetPageMargin(top, right, bottom, left, header, footer, gutter int) *Document { d.Body.SectionProperties.PageMargin.Top = top d.Body.SectionProperties.PageMargin.Right = right d.Body.SectionProperties.PageMargin.Bottom = bottom d.Body.SectionProperties.PageMargin.Left = left d.Body.SectionProperties.PageMargin.Header = header d.Body.SectionProperties.PageMargin.Footer = footer d.Body.SectionProperties.PageMargin.Gutter = gutter return d } // SetColumns 设置分栏 func (d *Document) SetColumns(num, space int) *Document { d.Body.SectionProperties.Columns.Num = num d.Body.SectionProperties.Columns.Space = space return d } // AddHeaderReference 添加页眉引用 func (d *Document) AddHeaderReference(headerType, id string) *Document { headerRef := &HeaderFooterReference{ Type: headerType, ID: id, } d.Body.SectionProperties.HeaderReference = append(d.Body.SectionProperties.HeaderReference, headerRef) return d } // AddFooterReference 添加页脚引用 func (d *Document) AddFooterReference(footerType, id string) *Document { footerRef := &HeaderFooterReference{ Type: footerType, ID: id, } d.Body.SectionProperties.FooterReference = append(d.Body.SectionProperties.FooterReference, footerRef) return d } // AddHeaderWithReference 添加页眉并同时添加页眉引用 func (d *Document) AddHeaderWithReference(headerType string) *Header { header := NewHeader() d.Headers = append(d.Headers, header) // 添加页眉关系 headerID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) headerPath := fmt.Sprintf("header%d.xml", len(d.Headers)) d.Rels.AddHeader(headerID, headerPath) // 添加页眉内容类型 d.ContentTypes.AddHeaderOverride(len(d.Headers)) // 添加页眉引用 d.AddHeaderReference(headerType, headerID) return header } // AddFooterWithReference 添加页脚并同时添加页脚引用 func (d *Document) AddFooterWithReference(footerType string) *Footer { footer := NewFooter() d.Footers = append(d.Footers, footer) // 添加页脚关系 footerID := fmt.Sprintf("rId%d", len(d.Rels.Relationships.Relationships)+1) footerPath := fmt.Sprintf("footer%d.xml", len(d.Footers)) d.Rels.AddFooter(footerID, footerPath) // 添加页脚内容类型 d.ContentTypes.AddFooterOverride(len(d.Footers)) // 添加页脚引用 d.AddFooterReference(footerType, footerID) return footer }