🎉 Initial commit

This commit is contained in:
2025-04-16 16:12:33 +08:00
commit af9b26fa6c
28 changed files with 6318 additions and 0 deletions

184
document/body.go Normal file
View File

@@ -0,0 +1,184 @@
package document
import "fmt"
// Body 表示Word文档的主体部分
type Body struct {
Content []interface{} // 可以是段落、表格等元素
SectionProperties *SectionProperties
}
// SectionProperties 表示节属性
type SectionProperties struct {
PageSize *PageSize
PageMargin *PageMargin
Columns *Columns
DocGrid *DocGrid
HeaderReference []*HeaderFooterReference
FooterReference []*HeaderFooterReference
}
// PageSize 表示页面大小
type PageSize struct {
Width int // 页面宽度单位为twip
Height int // 页面高度单位为twip
Orientation string // 页面方向portrait, landscape
}
// PageMargin 表示页面边距
type PageMargin struct {
Top int // 上边距单位为twip
Right int // 右边距单位为twip
Bottom int // 下边距单位为twip
Left int // 左边距单位为twip
Header int // 页眉边距单位为twip
Footer int // 页脚边距单位为twip
Gutter int // 装订线单位为twip
}
// Columns 表示分栏
type Columns struct {
Num int // 栏数
Space int // 栏间距单位为twip
}
// DocGrid 表示文档网格
type DocGrid struct {
LinePitch int // 行距单位为twip
}
// HeaderFooterReference 表示页眉页脚引用
type HeaderFooterReference struct {
Type string // 类型default, first, even
ID string // 引用ID
}
// NewBody 创建一个新的文档主体
func NewBody() *Body {
return &Body{
Content: make([]interface{}, 0),
SectionProperties: &SectionProperties{
PageSize: &PageSize{
Width: 12240, // 8.5英寸 = 12240 twip
Height: 15840, // 11英寸 = 15840 twip
Orientation: "portrait",
},
PageMargin: &PageMargin{
Top: 1440, // 1英寸 = 1440 twip
Right: 1440,
Bottom: 1440,
Left: 1440,
Header: 720,
Footer: 720,
Gutter: 0,
},
Columns: &Columns{
Num: 1,
Space: 720,
},
DocGrid: &DocGrid{
LinePitch: 360,
},
HeaderReference: make([]*HeaderFooterReference, 0),
FooterReference: make([]*HeaderFooterReference, 0),
},
}
}
// AddParagraph 向文档主体添加一个段落并返回它
func (b *Body) AddParagraph() *Paragraph {
p := NewParagraph()
b.Content = append(b.Content, p)
return p
}
// AddTable 向文档主体添加一个表格并返回它
func (b *Body) AddTable(rows, cols int) *Table {
t := NewTable(rows, cols)
b.Content = append(b.Content, t)
return t
}
// AddPageBreak 向文档主体添加一个分页符
func (b *Body) AddPageBreak() *Paragraph {
p := NewParagraph()
p.AddRun().AddBreak(BreakTypePage)
b.Content = append(b.Content, p)
return p
}
// AddSectionBreak 向文档主体添加一个分节符
func (b *Body) AddSectionBreak() *Paragraph {
p := NewParagraph()
p.AddRun().AddBreak(BreakTypeSection)
b.Content = append(b.Content, p)
return p
}
// ToXML 将Body转换为XML
func (b *Body) ToXML() string {
xml := "<w:body>"
// 添加所有内容元素的XML
for _, content := range b.Content {
switch v := content.(type) {
case *Paragraph:
xml += v.ToXML()
case *Table:
xml += v.ToXML()
}
}
// 添加节属性
xml += "<w:sectPr>"
// 页面大小
if b.SectionProperties.PageSize != nil {
xml += fmt.Sprintf("<w:pgSz w:w=\"%d\" w:h=\"%d\" w:orient=\"%s\" />",
b.SectionProperties.PageSize.Width,
b.SectionProperties.PageSize.Height,
b.SectionProperties.PageSize.Orientation)
}
// 页面边距
if b.SectionProperties.PageMargin != nil {
xml += fmt.Sprintf("<w:pgMar w:top=\"%d\" w:right=\"%d\" w:bottom=\"%d\" w:left=\"%d\" w:header=\"%d\" w:footer=\"%d\" w:gutter=\"%d\" />",
b.SectionProperties.PageMargin.Top,
b.SectionProperties.PageMargin.Right,
b.SectionProperties.PageMargin.Bottom,
b.SectionProperties.PageMargin.Left,
b.SectionProperties.PageMargin.Header,
b.SectionProperties.PageMargin.Footer,
b.SectionProperties.PageMargin.Gutter)
}
// 分栏
if b.SectionProperties.Columns != nil {
xml += fmt.Sprintf("<w:cols w:num=\"%d\" w:space=\"%d\" />",
b.SectionProperties.Columns.Num,
b.SectionProperties.Columns.Space)
}
// 文档网格
if b.SectionProperties.DocGrid != nil {
xml += fmt.Sprintf("<w:docGrid w:linePitch=\"%d\" />",
b.SectionProperties.DocGrid.LinePitch)
}
// 页眉引用
for _, headerRef := range b.SectionProperties.HeaderReference {
xml += fmt.Sprintf("<w:headerReference w:type=\"%s\" r:id=\"%s\" />",
headerRef.Type, headerRef.ID)
}
// 页脚引用
for _, footerRef := range b.SectionProperties.FooterReference {
xml += fmt.Sprintf("<w:footerReference w:type=\"%s\" r:id=\"%s\" />",
footerRef.Type, footerRef.ID)
}
xml += "</w:sectPr>"
xml += "</w:body>"
return xml
}

112
document/content_types.go Normal file
View File

@@ -0,0 +1,112 @@
package document
import (
"fmt"
)
// ContentTypes 表示Word文档中的内容类型集合
type ContentTypes struct {
Defaults []*Default
Overrides []*Override
}
// Default 表示默认的内容类型
type Default struct {
Extension string
ContentType string
}
// Override 表示覆盖的内容类型
type Override struct {
PartName string
ContentType string
}
// NewContentTypes 创建一个新的内容类型集合
func NewContentTypes() *ContentTypes {
ct := &ContentTypes{
Defaults: make([]*Default, 0),
Overrides: make([]*Override, 0),
}
// 添加默认的内容类型
ct.AddDefault("xml", "application/xml")
ct.AddDefault("rels", "application/vnd.openxmlformats-package.relationships+xml")
ct.AddDefault("png", "image/png")
ct.AddDefault("jpeg", "image/jpeg")
ct.AddDefault("jpg", "image/jpeg")
ct.AddDefault("gif", "image/gif")
ct.AddDefault("bmp", "image/bmp")
ct.AddDefault("tiff", "image/tiff")
ct.AddDefault("tif", "image/tiff")
ct.AddDefault("wmf", "image/x-wmf")
ct.AddDefault("emf", "image/x-emf")
// 添加覆盖的内容类型
ct.AddOverride("/document/document.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
ct.AddOverride("/document/styles.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml")
ct.AddOverride("/document/numbering.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml")
ct.AddOverride("/document/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml")
ct.AddOverride("/document/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml")
ct.AddOverride("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml")
ct.AddOverride("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml")
return ct
}
// AddDefault 添加一个默认的内容类型
func (c *ContentTypes) AddDefault(extension, contentType string) *Default {
def := &Default{
Extension: extension,
ContentType: contentType,
}
c.Defaults = append(c.Defaults, def)
return def
}
// AddOverride 添加一个覆盖的内容类型
func (c *ContentTypes) AddOverride(partName, contentType string) *Override {
override := &Override{
PartName: partName,
ContentType: contentType,
}
c.Overrides = append(c.Overrides, override)
return override
}
// AddHeaderOverride 添加一个页眉的内容类型
func (c *ContentTypes) AddHeaderOverride(index int) *Override {
return c.AddOverride(
"/document/header"+fmt.Sprintf("%d", index)+".xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
)
}
// AddFooterOverride 添加一个页脚的内容类型
func (c *ContentTypes) AddFooterOverride(index int) *Override {
return c.AddOverride(
"/document/footer"+fmt.Sprintf("%d", index)+".xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
)
}
// ToXML 将内容类型集合转换为XML
func (c *ContentTypes) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"
// 添加所有默认的内容类型
for _, def := range c.Defaults {
xml += "<Default Extension=\"" + def.Extension + "\""
xml += " ContentType=\"" + def.ContentType + "\" />"
}
// 添加所有覆盖的内容类型
for _, override := range c.Overrides {
xml += "<Override PartName=\"" + override.PartName + "\""
xml += " ContentType=\"" + override.ContentType + "\" />"
}
xml += "</Types>"
return xml
}

685
document/document.go Normal file
View File

@@ -0,0 +1,685 @@
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 := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
appXML += "<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\">\n"
appXML += "<Application>go-flowdoc</Application>\n"
appXML += "<AppVersion>1.0.0</AppVersion>\n"
appXML += "</Properties>\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 := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
coreXML += "<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" "
coreXML += "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
coreXML += "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
coreXML += "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
coreXML += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
if d.Properties.Title != "" {
coreXML += "<dc:title>" + d.Properties.Title + "</dc:title>\n"
}
if d.Properties.Subject != "" {
coreXML += "<dc:subject>" + d.Properties.Subject + "</dc:subject>\n"
}
if d.Properties.Creator != "" {
coreXML += "<dc:creator>" + d.Properties.Creator + "</dc:creator>\n"
}
if d.Properties.Keywords != "" {
coreXML += "<cp:keywords>" + d.Properties.Keywords + "</cp:keywords>\n"
}
if d.Properties.Description != "" {
coreXML += "<dc:description>" + d.Properties.Description + "</dc:description>\n"
}
if d.Properties.LastModifiedBy != "" {
coreXML += "<cp:lastModifiedBy>" + d.Properties.LastModifiedBy + "</cp:lastModifiedBy>\n"
}
if d.Properties.Revision > 0 {
coreXML += "<cp:revision>" + fmt.Sprintf("%d", d.Properties.Revision) + "</cp:revision>\n"
}
// 格式化时间
createdTime := d.Properties.Created.Format("2006-01-02T15:04:05Z")
modifiedTime := d.Properties.Modified.Format("2006-01-02T15:04:05Z")
coreXML += "<dcterms:created xsi:type=\"dcterms:W3CDTF\">" + createdTime + "</dcterms:created>\n"
coreXML += "<dcterms:modified xsi:type=\"dcterms:W3CDTF\">" + modifiedTime + "</dcterms:modified>\n"
coreXML += "</cp:coreProperties>\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 := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
docXML += "<w:document xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" "
docXML += "xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" "
docXML += "xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\">\n"
// 添加文档主体
docXML += d.Body.ToXML()
docXML += "</w:document>"
_, 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
}

336
document/drawing.go Normal file
View File

@@ -0,0 +1,336 @@
package document
import (
"encoding/base64"
"fmt"
"os"
"path/filepath"
"strings"
)
// Drawing 表示Word文档中的图形
type Drawing struct {
ID string
Name string
Description string
ImagePath string
ImageData []byte
Width int // 单位为EMU (English Metric Unit)
Height int // 单位为EMU (1厘米 = 360000 EMU)
WrapType string // 文字环绕方式inline, square, tight, through, topAndBottom, behind, inFront
PositionH *DrawingPosition
PositionV *DrawingPosition
}
// DrawingPosition 表示图形的位置
type DrawingPosition struct {
RelativeFrom string // 相对位置page, margin, column, paragraph, line, character
Align string // 对齐方式left, center, right, inside, outside
Offset int // 偏移量单位为EMU
}
// NewDrawing 创建一个新的图形
func NewDrawing() *Drawing {
return &Drawing{
ID: generateUniqueID(),
WrapType: "inline",
}
}
// SetImagePath 设置图片路径
func (d *Drawing) SetImagePath(path string) *Drawing {
d.ImagePath = path
// 设置图片名称
if d.Name == "" {
d.Name = filepath.Base(path)
}
// 读取图片数据
data, err := os.ReadFile(path)
if err == nil {
d.ImageData = data
}
return d
}
// SetImageData 设置图片数据
func (d *Drawing) SetImageData(data []byte) *Drawing {
d.ImageData = data
return d
}
// SetSize 设置图片大小
func (d *Drawing) SetSize(width, height int) *Drawing {
d.Width = width
d.Height = height
return d
}
// SetName 设置图片名称
func (d *Drawing) SetName(name string) *Drawing {
d.Name = name
return d
}
// SetDescription 设置图片描述
func (d *Drawing) SetDescription(description string) *Drawing {
d.Description = description
return d
}
// SetWrapType 设置文字环绕方式
func (d *Drawing) SetWrapType(wrapType string) *Drawing {
d.WrapType = wrapType
return d
}
// SetPositionH 设置水平位置
func (d *Drawing) SetPositionH(relativeFrom, align string, offset int) *Drawing {
d.PositionH = &DrawingPosition{
RelativeFrom: relativeFrom,
Align: align,
Offset: offset,
}
return d
}
// SetPositionV 设置垂直位置
func (d *Drawing) SetPositionV(relativeFrom, align string, offset int) *Drawing {
d.PositionV = &DrawingPosition{
RelativeFrom: relativeFrom,
Align: align,
Offset: offset,
}
return d
}
// ToXML 将图形转换为XML
func (d *Drawing) ToXML() string {
xml := "<w:drawing>"
// 内联图片
if d.WrapType == "inline" {
xml += "<wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\">"
// 图片大小
xml += "<wp:extent cx=\"" + fmt.Sprintf("%d", d.Width) + "\" cy=\"" + fmt.Sprintf("%d", d.Height) + "\" />"
// 图片效果
xml += "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\" />"
// 文档中的图片
xml += "<wp:docPr id=\"" + d.ID + "\" name=\"" + d.Name + "\" descr=\"" + d.Description + "\" />"
// 图片属性
xml += "<wp:cNvGraphicFramePr>"
xml += "<a:graphicFrameLocks xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" noChangeAspect=\"1\" />"
xml += "</wp:cNvGraphicFramePr>"
// 图片
xml += "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
xml += "<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
xml += "<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
// 图片信息
xml += "<pic:nvPicPr>"
xml += "<pic:cNvPr id=\"0\" name=\"" + d.Name + "\" descr=\"" + d.Description + "\" />"
xml += "<pic:cNvPicPr>"
xml += "<a:picLocks noChangeAspect=\"1\" noChangeArrowheads=\"1\" />"
xml += "</pic:cNvPicPr>"
xml += "</pic:nvPicPr>"
// 图片填充
xml += "<pic:blipFill>"
xml += "<a:blip r:embed=\"rId" + d.ID + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" />"
xml += "<a:stretch>"
xml += "<a:fillRect />"
xml += "</a:stretch>"
xml += "</pic:blipFill>"
// 图片形状
xml += "<pic:spPr>"
xml += "<a:xfrm>"
xml += "<a:off x=\"0\" y=\"0\" />"
xml += "<a:ext cx=\"" + fmt.Sprintf("%d", d.Width) + "\" cy=\"" + fmt.Sprintf("%d", d.Height) + "\" />"
xml += "</a:xfrm>"
xml += "<a:prstGeom prst=\"rect\">"
xml += "<a:avLst />"
xml += "</a:prstGeom>"
xml += "</pic:spPr>"
xml += "</pic:pic>"
xml += "</a:graphicData>"
xml += "</a:graphic>"
xml += "</wp:inline>"
} else {
// 浮动图片
xml += "<wp:anchor distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\" simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + boolToString(d.WrapType == "behind") + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
// 简单位置
xml += "<wp:simplePos x=\"0\" y=\"0\" />"
// 水平位置
if d.PositionH != nil {
xml += "<wp:positionH relativeFrom=\"" + d.PositionH.RelativeFrom + "\">"
if d.PositionH.Align != "" {
xml += "<wp:align>" + d.PositionH.Align + "</wp:align>"
} else {
xml += "<wp:posOffset>" + fmt.Sprintf("%d", d.PositionH.Offset) + "</wp:posOffset>"
}
xml += "</wp:positionH>"
} else {
xml += "<wp:positionH relativeFrom=\"column\">"
xml += "<wp:align>left</wp:align>"
xml += "</wp:positionH>"
}
// 垂直位置
if d.PositionV != nil {
xml += "<wp:positionV relativeFrom=\"" + d.PositionV.RelativeFrom + "\">"
if d.PositionV.Align != "" {
xml += "<wp:align>" + d.PositionV.Align + "</wp:align>"
} else {
xml += "<wp:posOffset>" + fmt.Sprintf("%d", d.PositionV.Offset) + "</wp:posOffset>"
}
xml += "</wp:positionV>"
} else {
xml += "<wp:positionV relativeFrom=\"paragraph\">"
xml += "<wp:align>top</wp:align>"
xml += "</wp:positionV>"
}
// 图片大小
xml += "<wp:extent cx=\"" + fmt.Sprintf("%d", d.Width) + "\" cy=\"" + fmt.Sprintf("%d", d.Height) + "\" />"
// 图片效果
xml += "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\" />"
// 文字环绕方式
switch d.WrapType {
case "square":
xml += "<wp:wrapSquare wrapText=\"bothSides\" />"
case "tight":
xml += "<wp:wrapTight wrapText=\"bothSides\" />"
case "through":
xml += "<wp:wrapThrough wrapText=\"bothSides\" />"
case "topAndBottom":
xml += "<wp:wrapTopAndBottom />"
case "behind":
xml += "<wp:wrapNone />"
case "inFront":
xml += "<wp:wrapNone />"
default:
xml += "<wp:wrapSquare wrapText=\"bothSides\" />"
}
// 文档中的图片
xml += "<wp:docPr id=\"" + d.ID + "\" name=\"" + d.Name + "\" descr=\"" + d.Description + "\" />"
// 图片属性
xml += "<wp:cNvGraphicFramePr>"
xml += "<a:graphicFrameLocks xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" noChangeAspect=\"1\" />"
xml += "</wp:cNvGraphicFramePr>"
// 图片
xml += "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
xml += "<a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
xml += "<pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
// 图片信息
xml += "<pic:nvPicPr>"
xml += "<pic:cNvPr id=\"0\" name=\"" + d.Name + "\" descr=\"" + d.Description + "\" />"
xml += "<pic:cNvPicPr>"
xml += "<a:picLocks noChangeAspect=\"1\" noChangeArrowheads=\"1\" />"
xml += "</pic:cNvPicPr>"
xml += "</pic:nvPicPr>"
// 图片填充
xml += "<pic:blipFill>"
xml += "<a:blip r:embed=\"rId" + d.ID + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" />"
xml += "<a:stretch>"
xml += "<a:fillRect />"
xml += "</a:stretch>"
xml += "</pic:blipFill>"
// 图片形状
xml += "<pic:spPr>"
xml += "<a:xfrm>"
xml += "<a:off x=\"0\" y=\"0\" />"
xml += "<a:ext cx=\"" + fmt.Sprintf("%d", d.Width) + "\" cy=\"" + fmt.Sprintf("%d", d.Height) + "\" />"
xml += "</a:xfrm>"
xml += "<a:prstGeom prst=\"rect\">"
xml += "<a:avLst />"
xml += "</a:prstGeom>"
xml += "</pic:spPr>"
xml += "</pic:pic>"
xml += "</a:graphicData>"
xml += "</a:graphic>"
xml += "</wp:anchor>"
}
xml += "</w:drawing>"
return xml
}
// GetImageData 获取图片数据的Base64编码
func (d *Drawing) GetImageData() string {
return base64.StdEncoding.EncodeToString(d.ImageData)
}
// GetImageType 获取图片类型
func (d *Drawing) GetImageType() string {
if d.ImagePath != "" {
ext := strings.ToLower(filepath.Ext(d.ImagePath))
switch ext {
case ".jpg", ".jpeg":
return "jpeg"
case ".png":
return "png"
case ".gif":
return "gif"
case ".bmp":
return "bmp"
case ".tif", ".tiff":
return "tiff"
case ".wmf":
return "x-wmf"
case ".emf":
return "x-emf"
default:
return "jpeg"
}
}
return "jpeg"
}
// GetContentType 获取图片的Content-Type
func (d *Drawing) GetContentType() string {
switch d.GetImageType() {
case "jpeg":
return "image/jpeg"
case "png":
return "image/png"
case "gif":
return "image/gif"
case "bmp":
return "image/bmp"
case "tiff":
return "image/tiff"
case "x-wmf":
return "image/x-wmf"
case "x-emf":
return "image/x-emf"
default:
return "image/jpeg"
}
}
// Error 实现error接口
func (d *Drawing) Error() string {
return fmt.Sprintf("Drawing error: %s", d.Description)
}

View File

@@ -0,0 +1,150 @@
package main
import (
"fmt"
"github.com/landaiqing/go-dockit/document"
"log"
)
func main() {
// 创建一个新的Word文档
doc := document.NewDocument()
// 设置文档属性
doc.SetTitle("示例文档")
doc.SetCreator("FlowDoc库")
doc.SetDescription("这是一个使用go-dockit库创建的示例文档")
// 添加一个标题段落
titlePara := doc.AddParagraph()
titlePara.SetAlignment("center")
titleRun := titlePara.AddRun()
titleRun.AddText("FlowDoc示例文档")
titleRun.SetBold(true)
titleRun.SetFontSize(32) // 16磅
titleRun.SetFontFamily("黑体")
// 添加一个普通段落
para1 := doc.AddParagraph()
para1.SetAlignment("left")
para1.SetIndentFirstLine(420) // 首行缩进0.3厘米
run1 := para1.AddRun()
run1.AddText("这是一个使用go-dockit库创建的示例文档。该库提供了一种简单的方式来生成Word文档支持段落、表格、列表、图片等元素。")
// 添加一个带样式的段落
para2 := doc.AddParagraph()
para2.SetAlignment("left")
para2.SetIndentFirstLine(420)
para2.SetSpacingAfter(200) // 段后间距
run2 := para2.AddRun()
run2.AddText("这个段落演示了不同的文本样式:")
// 添加不同样式的文本
para2.AddRun().AddText("粗体").SetBold(true)
para2.AddRun().AddText("、")
para2.AddRun().AddText("斜体").SetItalic(true)
para2.AddRun().AddText("、")
para2.AddRun().AddText("下划线").SetUnderline("single")
para2.AddRun().AddText("、")
para2.AddRun().AddText("红色文本").SetColor("FF0000")
para2.AddRun().AddText("、")
para2.AddRun().AddText("黄色高亮").SetHighlight("yellow")
// 添加一个标题
headingPara := doc.AddParagraph()
headingPara.SetSpacingBefore(400)
headingPara.SetSpacingAfter(200)
headingRun := headingPara.AddRun()
headingRun.AddText("表格示例")
headingRun.SetBold(true)
headingRun.SetFontSize(28) // 14磅
// 添加一个表格
table := doc.AddTable(3, 3)
table.SetWidth(8000, "dxa") // 约14厘米宽
table.SetAlignment("center")
// 设置表头
headerRow := table.Rows[0]
headerRow.SetIsHeader(true)
// 填充表头单元格
headerRow.Cells[0].AddParagraph().AddRun().AddText("产品名称").SetBold(true)
headerRow.Cells[1].AddParagraph().AddRun().AddText("数量").SetBold(true)
headerRow.Cells[2].AddParagraph().AddRun().AddText("单价").SetBold(true)
// 填充表格数据
table.Rows[1].Cells[0].AddParagraph().AddRun().AddText("产品A")
table.Rows[1].Cells[1].AddParagraph().AddRun().AddText("10")
table.Rows[1].Cells[2].AddParagraph().AddRun().AddText("¥100.00")
table.Rows[2].Cells[0].AddParagraph().AddRun().AddText("产品B")
table.Rows[2].Cells[1].AddParagraph().AddRun().AddText("5")
table.Rows[2].Cells[2].AddParagraph().AddRun().AddText("¥200.00")
// 添加一个分页符
doc.Body.AddPageBreak()
// 添加一个标题
listHeadingPara := doc.AddParagraph()
listHeadingPara.SetSpacingBefore(400)
listHeadingPara.SetSpacingAfter(200)
listHeadingRun := listHeadingPara.AddRun()
listHeadingRun.AddText("列表示例")
listHeadingRun.SetBold(true)
listHeadingRun.SetFontSize(28) // 14磅
// 创建一个项目符号列表
bulletListId := doc.Numbering.CreateBulletList()
// 添加列表项
listItem1 := doc.AddParagraph()
listItem1.SetNumbering(bulletListId, 0)
listItem1.AddRun().AddText("这是第一个列表项")
listItem2 := doc.AddParagraph()
listItem2.SetNumbering(bulletListId, 0)
listItem2.AddRun().AddText("这是第二个列表项")
listItem3 := doc.AddParagraph()
listItem3.SetNumbering(bulletListId, 0)
listItem3.AddRun().AddText("这是第三个列表项")
// 创建一个数字列表
numberListId := doc.Numbering.CreateNumberList()
// 添加列表项
numListItem1 := doc.AddParagraph()
numListItem1.SetNumbering(numberListId, 0)
numListItem1.AddRun().AddText("这是第一个数字列表项")
numListItem2 := doc.AddParagraph()
numListItem2.SetNumbering(numberListId, 0)
numListItem2.AddRun().AddText("这是第二个数字列表项")
numListItem3 := doc.AddParagraph()
numListItem3.SetNumbering(numberListId, 0)
numListItem3.AddRun().AddText("这是第三个数字列表项")
// 添加页眉并同时添加页眉引用
header := doc.AddHeaderWithReference("default")
headerPara := header.AddParagraph()
headerPara.SetAlignment("right")
headerPara.AddRun().AddText("FlowDoc示例文档 - 页眉")
// 添加页脚并同时添加页脚引用
footer := doc.AddFooterWithReference("default")
footerPara := footer.AddParagraph()
footerPara.SetAlignment("center")
footerPara.AddRun().AddText("第 ")
footerPara.AddRun().AddPageNumber()
footerPara.AddRun().AddText(" 页")
// 保存文档
err := doc.Save("./document/examples/simple/example.docx")
if err != nil {
log.Fatalf("保存文档时出错: %v", err)
}
fmt.Println("文档已成功保存为 example.docx")
}

95
document/header_footer.go Normal file
View File

@@ -0,0 +1,95 @@
package document
// Header 表示Word文档中的页眉
type Header struct {
ID string
Content []interface{} // 可以是段落、表格等元素
}
// Footer 表示Word文档中的页脚
type Footer struct {
ID string
Content []interface{} // 可以是段落、表格等元素
}
// NewHeader 创建一个新的页眉
func NewHeader() *Header {
return &Header{
ID: generateUniqueID(),
Content: make([]interface{}, 0),
}
}
// NewFooter 创建一个新的页脚
func NewFooter() *Footer {
return &Footer{
ID: generateUniqueID(),
Content: make([]interface{}, 0),
}
}
// AddParagraph 向页眉添加一个段落并返回它
func (h *Header) AddParagraph() *Paragraph {
p := NewParagraph()
h.Content = append(h.Content, p)
return p
}
// AddTable 向页眉添加一个表格并返回它
func (h *Header) AddTable(rows, cols int) *Table {
t := NewTable(rows, cols)
h.Content = append(h.Content, t)
return t
}
// AddParagraph 向页脚添加一个段落并返回它
func (f *Footer) AddParagraph() *Paragraph {
p := NewParagraph()
f.Content = append(f.Content, p)
return p
}
// AddTable 向页脚添加一个表格并返回它
func (f *Footer) AddTable(rows, cols int) *Table {
t := NewTable(rows, cols)
f.Content = append(f.Content, t)
return t
}
// ToXML 将页眉转换为XML
func (h *Header) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<w:hdr xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
// 添加所有内容元素的XML
for _, content := range h.Content {
switch v := content.(type) {
case *Paragraph:
xml += v.ToXML()
case *Table:
xml += v.ToXML()
}
}
xml += "</w:hdr>"
return xml
}
// ToXML 将页脚转换为XML
func (f *Footer) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<w:ftr xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
// 添加所有内容元素的XML
for _, content := range f.Content {
switch v := content.(type) {
case *Paragraph:
xml += v.ToXML()
case *Table:
xml += v.ToXML()
}
}
xml += "</w:ftr>"
return xml
}

375
document/numbering.go Normal file
View File

@@ -0,0 +1,375 @@
package document
import (
"fmt"
)
// Numbering 表示Word文档中的编号集合
type Numbering struct {
AbstractNums []*AbstractNum
Nums []*Num
}
// AbstractNum 表示抽象编号
type AbstractNum struct {
ID int
Levels []*NumberingLevel
}
// Num 表示具体编号
type Num struct {
ID int
AbstractNumID int
LevelOverrides []*LevelOverride
}
// LevelOverride 表示级别覆盖
type LevelOverride struct {
Level int
StartAt int
NumberingLevel *NumberingLevel
}
// NumberingLevel 表示编号级别
type NumberingLevel struct {
Level int
Start int
NumberingFormat string // decimal, upperRoman, lowerRoman, upperLetter, lowerLetter, bullet, etc.
Text string // 编号文本,如 "%1."
Justification string // left, center, right
ParagraphStyle string // 段落样式ID
Font string // 字体
Indent int // 缩进
HangingIndent int // 悬挂缩进
TabStop int // 制表位
Suffix string // tab, space, nothing
}
// NewNumbering 创建一个新的编号集合
func NewNumbering() *Numbering {
return &Numbering{
AbstractNums: make([]*AbstractNum, 0),
Nums: make([]*Num, 0),
}
}
// AddAbstractNum 添加一个抽象编号
func (n *Numbering) AddAbstractNum() *AbstractNum {
abstractNum := &AbstractNum{
ID: len(n.AbstractNums) + 1,
Levels: make([]*NumberingLevel, 0),
}
n.AbstractNums = append(n.AbstractNums, abstractNum)
return abstractNum
}
// AddNum 添加一个具体编号
func (n *Numbering) AddNum(abstractNumID int) *Num {
num := &Num{
ID: len(n.Nums) + 1,
AbstractNumID: abstractNumID,
LevelOverrides: make([]*LevelOverride, 0),
}
n.Nums = append(n.Nums, num)
return num
}
// AddLevel 向抽象编号添加一个级别
func (a *AbstractNum) AddLevel(level int) *NumberingLevel {
numberingLevel := &NumberingLevel{
Level: level,
Start: 1,
NumberingFormat: "decimal",
Text: "%" + fmt.Sprintf("%d", level+1) + ".",
Justification: "left",
Indent: 720 * (level + 1), // 720 twip = 0.5 inch
HangingIndent: 360, // 360 twip = 0.25 inch
TabStop: 720 * (level + 1),
Suffix: "tab",
}
a.Levels = append(a.Levels, numberingLevel)
return numberingLevel
}
// AddLevelOverride 向具体编号添加一个级别覆盖
func (n *Num) AddLevelOverride(level int) *LevelOverride {
levelOverride := &LevelOverride{
Level: level,
StartAt: 1,
}
n.LevelOverrides = append(n.LevelOverrides, levelOverride)
return levelOverride
}
// SetStart 设置编号级别的起始值
func (l *NumberingLevel) SetStart(start int) *NumberingLevel {
l.Start = start
return l
}
// SetNumberingFormat 设置编号级别的格式
func (l *NumberingLevel) SetNumberingFormat(format string) *NumberingLevel {
l.NumberingFormat = format
return l
}
// SetText 设置编号级别的文本
func (l *NumberingLevel) SetText(text string) *NumberingLevel {
l.Text = text
return l
}
// SetJustification 设置编号级别的对齐方式
func (l *NumberingLevel) SetJustification(justification string) *NumberingLevel {
l.Justification = justification
return l
}
// SetParagraphStyle 设置编号级别的段落样式
func (l *NumberingLevel) SetParagraphStyle(style string) *NumberingLevel {
l.ParagraphStyle = style
return l
}
// SetFont 设置编号级别的字体
func (l *NumberingLevel) SetFont(font string) *NumberingLevel {
l.Font = font
return l
}
// SetIndent 设置编号级别的缩进
func (l *NumberingLevel) SetIndent(indent int) *NumberingLevel {
l.Indent = indent
return l
}
// SetHangingIndent 设置编号级别的悬挂缩进
func (l *NumberingLevel) SetHangingIndent(hangingIndent int) *NumberingLevel {
l.HangingIndent = hangingIndent
return l
}
// SetTabStop 设置编号级别的制表位
func (l *NumberingLevel) SetTabStop(tabStop int) *NumberingLevel {
l.TabStop = tabStop
return l
}
// SetSuffix 设置编号级别的后缀
func (l *NumberingLevel) SetSuffix(suffix string) *NumberingLevel {
l.Suffix = suffix
return l
}
// SetStartAt 设置级别覆盖的起始值
func (l *LevelOverride) SetStartAt(startAt int) *LevelOverride {
l.StartAt = startAt
return l
}
// SetNumberingLevel 设置级别覆盖的编号级别
func (l *LevelOverride) SetNumberingLevel(level *NumberingLevel) *LevelOverride {
l.NumberingLevel = level
return l
}
// CreateBulletList 创建一个项目符号列表
func (n *Numbering) CreateBulletList() int {
// 创建抽象编号
abstractNum := n.AddAbstractNum()
// 添加9个级别
for i := 0; i < 9; i++ {
level := abstractNum.AddLevel(i)
level.SetNumberingFormat("bullet")
// 根据级别设置不同的项目符号
switch i % 3 {
case 0:
level.SetText("•")
level.SetFont("Symbol")
case 1:
level.SetText("○")
level.SetFont("Courier New")
case 2:
level.SetText("▪")
level.SetFont("Wingdings")
}
}
// 创建具体编号
num := n.AddNum(abstractNum.ID)
return num.ID
}
// CreateNumberList 创建一个数字列表
func (n *Numbering) CreateNumberList() int {
// 创建抽象编号
abstractNum := n.AddAbstractNum()
// 添加9个级别
for i := 0; i < 9; i++ {
level := abstractNum.AddLevel(i)
// 根据级别设置不同的编号格式
switch i % 3 {
case 0:
level.SetNumberingFormat("decimal")
level.SetText("%" + fmt.Sprintf("%d", i+1) + ".")
case 1:
level.SetNumberingFormat("lowerLetter")
level.SetText("%" + fmt.Sprintf("%d", i+1) + ").")
case 2:
level.SetNumberingFormat("lowerRoman")
level.SetText("%" + fmt.Sprintf("%d", i+1) + ").")
}
}
// 创建具体编号
num := n.AddNum(abstractNum.ID)
return num.ID
}
// ToXML 将编号集合转换为XML
func (n *Numbering) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<w:numbering xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
// 添加所有抽象编号
for _, abstractNum := range n.AbstractNums {
xml += "<w:abstractNum w:abstractNumId=\"" + fmt.Sprintf("%d", abstractNum.ID) + "\">"
// 添加所有级别
for _, level := range abstractNum.Levels {
xml += "<w:lvl w:ilvl=\"" + fmt.Sprintf("%d", level.Level) + "\">"
// 起始值
xml += "<w:start w:val=\"" + fmt.Sprintf("%d", level.Start) + "\" />"
// 编号格式
xml += "<w:numFmt w:val=\"" + level.NumberingFormat + "\" />"
// 编号文本
xml += "<w:lvlText w:val=\"" + level.Text + "\" />"
// 对齐方式
xml += "<w:lvlJc w:val=\"" + level.Justification + "\" />"
// 段落属性
xml += "<w:pPr>"
// 缩进
xml += "<w:ind w:left=\"" + fmt.Sprintf("%d", level.Indent) + "\""
xml += " w:hanging=\"" + fmt.Sprintf("%d", level.HangingIndent) + "\" />"
// 制表位
if level.TabStop > 0 {
xml += "<w:tabs>"
xml += "<w:tab w:val=\"num\" w:pos=\"" + fmt.Sprintf("%d", level.TabStop) + "\" />"
xml += "</w:tabs>"
}
xml += "</w:pPr>"
// 文本运行属性
xml += "<w:rPr>"
// 字体
if level.Font != "" {
xml += "<w:rFonts w:ascii=\"" + level.Font + "\""
xml += " w:hAnsi=\"" + level.Font + "\""
xml += " w:hint=\"default\" />"
}
xml += "</w:rPr>"
// 后缀
if level.Suffix != "" {
xml += "<w:suff w:val=\"" + level.Suffix + "\" />"
}
xml += "</w:lvl>"
}
xml += "</w:abstractNum>"
}
// 添加所有具体编号
for _, num := range n.Nums {
xml += "<w:num w:numId=\"" + fmt.Sprintf("%d", num.ID) + "\">"
// 抽象编号ID
xml += "<w:abstractNumId w:val=\"" + fmt.Sprintf("%d", num.AbstractNumID) + "\" />"
// 添加所有级别覆盖
for _, levelOverride := range num.LevelOverrides {
xml += "<w:lvlOverride w:ilvl=\"" + fmt.Sprintf("%d", levelOverride.Level) + "\">"
// 起始值
if levelOverride.StartAt > 0 {
xml += "<w:startOverride w:val=\"" + fmt.Sprintf("%d", levelOverride.StartAt) + "\" />"
}
// 编号级别
if levelOverride.NumberingLevel != nil {
xml += "<w:lvl w:ilvl=\"" + fmt.Sprintf("%d", levelOverride.NumberingLevel.Level) + "\">"
// 起始值
xml += "<w:start w:val=\"" + fmt.Sprintf("%d", levelOverride.NumberingLevel.Start) + "\" />"
// 编号格式
xml += "<w:numFmt w:val=\"" + levelOverride.NumberingLevel.NumberingFormat + "\" />"
// 编号文本
xml += "<w:lvlText w:val=\"" + levelOverride.NumberingLevel.Text + "\" />"
// 对齐方式
xml += "<w:lvlJc w:val=\"" + levelOverride.NumberingLevel.Justification + "\" />"
// 段落属性
xml += "<w:pPr>"
// 缩进
xml += "<w:ind w:left=\"" + fmt.Sprintf("%d", levelOverride.NumberingLevel.Indent) + "\""
xml += " w:hanging=\"" + fmt.Sprintf("%d", levelOverride.NumberingLevel.HangingIndent) + "\" />"
// 制表位
if levelOverride.NumberingLevel.TabStop > 0 {
xml += "<w:tabs>"
xml += "<w:tab w:val=\"num\" w:pos=\"" + fmt.Sprintf("%d", levelOverride.NumberingLevel.TabStop) + "\" />"
xml += "</w:tabs>"
}
xml += "</w:pPr>"
// 文本运行属性
xml += "<w:rPr>"
// 字体
if levelOverride.NumberingLevel.Font != "" {
xml += "<w:rFonts w:ascii=\"" + levelOverride.NumberingLevel.Font + "\""
xml += " w:hAnsi=\"" + levelOverride.NumberingLevel.Font + "\""
xml += " w:hint=\"default\" />"
}
xml += "</w:rPr>"
// 后缀
if levelOverride.NumberingLevel.Suffix != "" {
xml += "<w:suff w:val=\"" + levelOverride.NumberingLevel.Suffix + "\" />"
}
xml += "</w:lvl>"
}
xml += "</w:lvlOverride>"
}
xml += "</w:num>"
}
xml += "</w:numbering>"
return xml
}

299
document/paragraph.go Normal file
View File

@@ -0,0 +1,299 @@
package document
import "fmt"
// Paragraph 表示Word文档中的段落
type Paragraph struct {
Runs []*Run
Properties *ParagraphProperties
}
// ParagraphProperties 表示段落的属性
type ParagraphProperties struct {
Alignment string // left, center, right, justified
IndentLeft int // 左缩进单位为twip (1/20 point)
IndentRight int // 右缩进
IndentFirstLine int // 首行缩进
SpacingBefore int // 段前间距
SpacingAfter int // 段后间距
SpacingLine int // 行间距
SpacingLineRule string // auto, exact, atLeast
KeepNext bool // 与下段同页
KeepLines bool // 段中不分页
PageBreakBefore bool // 段前分页
WidowControl bool // 孤行控制
OutlineLevel int // 大纲级别
StyleID string // 样式ID
NumID int // 编号ID
NumLevel int // 编号级别
BorderTop *Border
BorderBottom *Border
BorderLeft *Border
BorderRight *Border
Shading *Shading
}
// Border 表示边框
type Border struct {
Style string // single, double, dotted, dashed, etc.
Size int // 边框宽度单位为1/8点
Color string // 边框颜色格式为RRGGBB
Space int // 边框与文本的距离
}
// Shading 表示底纹
type Shading struct {
Fill string // 填充颜色
Color string // 文本颜色
Pattern string // 底纹样式
}
// NewParagraph 创建一个新的段落
func NewParagraph() *Paragraph {
return &Paragraph{
Runs: make([]*Run, 0),
Properties: &ParagraphProperties{
Alignment: "left",
WidowControl: true,
SpacingLineRule: "auto",
},
}
}
// AddRun 向段落添加一个文本运行并返回它
func (p *Paragraph) AddRun() *Run {
r := NewRun()
p.Runs = append(p.Runs, r)
return r
}
// AddText 向段落添加文本
func (p *Paragraph) AddText(text string) *Run {
return p.AddRun().AddText(text)
}
// SetAlignment 设置段落对齐方式
func (p *Paragraph) SetAlignment(alignment string) *Paragraph {
p.Properties.Alignment = alignment
return p
}
// SetIndentLeft 设置左缩进
func (p *Paragraph) SetIndentLeft(indent int) *Paragraph {
p.Properties.IndentLeft = indent
return p
}
// SetIndentRight 设置右缩进
func (p *Paragraph) SetIndentRight(indent int) *Paragraph {
p.Properties.IndentRight = indent
return p
}
// SetIndentFirstLine 设置首行缩进
func (p *Paragraph) SetIndentFirstLine(indent int) *Paragraph {
p.Properties.IndentFirstLine = indent
return p
}
// SetSpacingBefore 设置段前间距
func (p *Paragraph) SetSpacingBefore(spacing int) *Paragraph {
p.Properties.SpacingBefore = spacing
return p
}
// SetSpacingAfter 设置段后间距
func (p *Paragraph) SetSpacingAfter(spacing int) *Paragraph {
p.Properties.SpacingAfter = spacing
return p
}
// SetSpacingLine 设置行间距
func (p *Paragraph) SetSpacingLine(spacing int, rule string) *Paragraph {
p.Properties.SpacingLine = spacing
p.Properties.SpacingLineRule = rule
return p
}
// SetKeepNext 设置与下段同页
func (p *Paragraph) SetKeepNext(keepNext bool) *Paragraph {
p.Properties.KeepNext = keepNext
return p
}
// SetKeepLines 设置段中不分页
func (p *Paragraph) SetKeepLines(keepLines bool) *Paragraph {
p.Properties.KeepLines = keepLines
return p
}
// SetPageBreakBefore 设置段前分页
func (p *Paragraph) SetPageBreakBefore(pageBreakBefore bool) *Paragraph {
p.Properties.PageBreakBefore = pageBreakBefore
return p
}
// SetStyleID 设置样式ID
func (p *Paragraph) SetStyleID(styleID string) *Paragraph {
p.Properties.StyleID = styleID
return p
}
// SetNumbering 设置编号
func (p *Paragraph) SetNumbering(numID, numLevel int) *Paragraph {
p.Properties.NumID = numID
p.Properties.NumLevel = numLevel
return p
}
// SetBorder 设置边框
func (p *Paragraph) SetBorder(position string, style string, size int, color string, space int) *Paragraph {
border := &Border{
Style: style,
Size: size,
Color: color,
Space: space,
}
switch position {
case "top":
p.Properties.BorderTop = border
case "bottom":
p.Properties.BorderBottom = border
case "left":
p.Properties.BorderLeft = border
case "right":
p.Properties.BorderRight = border
}
return p
}
// SetShading 设置底纹
func (p *Paragraph) SetShading(fill, color, pattern string) *Paragraph {
p.Properties.Shading = &Shading{
Fill: fill,
Color: color,
Pattern: pattern,
}
return p
}
// ToXML 将段落转换为XML
func (p *Paragraph) ToXML() string {
xml := "<w:p>"
// 添加段落属性
xml += "<w:pPr>"
// 对齐方式
if p.Properties.Alignment != "" {
xml += "<w:jc w:val=\"" + p.Properties.Alignment + "\" />"
}
// 缩进
if p.Properties.IndentLeft > 0 || p.Properties.IndentRight > 0 || p.Properties.IndentFirstLine > 0 {
xml += "<w:ind"
if p.Properties.IndentLeft > 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 += "<w:spacing"
if p.Properties.SpacingBefore > 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 += "<w:keepNext />"
}
if p.Properties.KeepLines {
xml += "<w:keepLines />"
}
if p.Properties.PageBreakBefore {
xml += "<w:pageBreakBefore />"
}
if p.Properties.WidowControl {
xml += "<w:widowControl />"
}
// 样式
if p.Properties.StyleID != "" {
xml += "<w:pStyle w:val=\"" + p.Properties.StyleID + "\" />"
}
// 编号
if p.Properties.NumID > 0 {
xml += "<w:numPr>"
xml += "<w:numId w:val=\"" + fmt.Sprintf("%d", p.Properties.NumID) + "\" />"
xml += "<w:ilvl w:val=\"" + fmt.Sprintf("%d", p.Properties.NumLevel) + "\" />"
xml += "</w:numPr>"
}
// 边框
if p.Properties.BorderTop != nil || p.Properties.BorderBottom != nil ||
p.Properties.BorderLeft != nil || p.Properties.BorderRight != nil {
xml += "<w:pBdr>"
if p.Properties.BorderTop != nil {
xml += "<w:top w:val=\"" + p.Properties.BorderTop.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", p.Properties.BorderTop.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", p.Properties.BorderTop.Space) + "\""
xml += " w:color=\"" + p.Properties.BorderTop.Color + "\" />"
}
if p.Properties.BorderBottom != nil {
xml += "<w:bottom w:val=\"" + p.Properties.BorderBottom.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", p.Properties.BorderBottom.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", p.Properties.BorderBottom.Space) + "\""
xml += " w:color=\"" + p.Properties.BorderBottom.Color + "\" />"
}
if p.Properties.BorderLeft != nil {
xml += "<w:left w:val=\"" + p.Properties.BorderLeft.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", p.Properties.BorderLeft.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", p.Properties.BorderLeft.Space) + "\""
xml += " w:color=\"" + p.Properties.BorderLeft.Color + "\" />"
}
if p.Properties.BorderRight != nil {
xml += "<w:right w:val=\"" + p.Properties.BorderRight.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", p.Properties.BorderRight.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", p.Properties.BorderRight.Space) + "\""
xml += " w:color=\"" + p.Properties.BorderRight.Color + "\" />"
}
xml += "</w:pBdr>"
}
// 底纹
if p.Properties.Shading != nil {
xml += "<w:shd w:val=\"" + p.Properties.Shading.Pattern + "\""
xml += " w:fill=\"" + p.Properties.Shading.Fill + "\""
xml += " w:color=\"" + p.Properties.Shading.Color + "\" />"
}
xml += "</w:pPr>"
// 添加所有Run的XML
for _, run := range p.Runs {
xml += run.ToXML()
}
xml += "</w:p>"
return xml
}

121
document/relationships.go Normal file
View File

@@ -0,0 +1,121 @@
package document
// Relationships 表示Word文档中的关系集合
type Relationships struct {
Relationships []*Relationship
}
// Relationship 表示Word文档中的关系
type Relationship struct {
ID string
Type string
Target string
TargetMode string // 目标模式Internal, External
}
// NewRelationships 创建一个新的关系集合
func NewRelationships() *Relationships {
return &Relationships{
Relationships: make([]*Relationship, 0),
}
}
// AddRelationship 添加一个关系
func (r *Relationships) AddRelationship(id, relType, target string) *Relationship {
rel := &Relationship{
ID: id,
Type: relType,
Target: target,
}
r.Relationships = append(r.Relationships, rel)
return rel
}
// AddExternalRelationship 添加一个外部关系
func (r *Relationships) AddExternalRelationship(id, relType, target string) *Relationship {
rel := &Relationship{
ID: id,
Type: relType,
Target: target,
TargetMode: "External",
}
r.Relationships = append(r.Relationships, rel)
return rel
}
// GetRelationshipByID 根据ID获取关系
func (r *Relationships) GetRelationshipByID(id string) *Relationship {
for _, rel := range r.Relationships {
if rel.ID == id {
return rel
}
}
return nil
}
// GetRelationshipsByType 根据类型获取关系
func (r *Relationships) GetRelationshipsByType(relType string) []*Relationship {
result := make([]*Relationship, 0)
for _, rel := range r.Relationships {
if rel.Type == relType {
result = append(result, rel)
}
}
return result
}
// ToXML 将关系集合转换为XML
func (r *Relationships) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
for _, rel := range r.Relationships {
xml += "<Relationship Id=\"" + rel.ID + "\""
xml += " Type=\"" + rel.Type + "\""
xml += " Target=\"" + rel.Target + "\""
if rel.TargetMode != "" {
xml += " TargetMode=\"" + rel.TargetMode + "\""
}
xml += " />"
}
xml += "</Relationships>"
return xml
}
// DocumentRels 表示Word文档中的文档关系
type DocumentRels struct {
Relationships *Relationships
}
// NewDocumentRels 创建一个新的文档关系
func NewDocumentRels() *DocumentRels {
return &DocumentRels{
Relationships: NewRelationships(),
}
}
// AddImage 添加一个图片关系
func (d *DocumentRels) AddImage(id, target string) *Relationship {
return d.Relationships.AddRelationship(id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", target)
}
// AddHyperlink 添加一个超链接关系
func (d *DocumentRels) AddHyperlink(id, target string) *Relationship {
return d.Relationships.AddExternalRelationship(id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", target)
}
// AddHeader 添加一个页眉关系
func (d *DocumentRels) AddHeader(id, target string) *Relationship {
return d.Relationships.AddRelationship(id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", target)
}
// AddFooter 添加一个页脚关系
func (d *DocumentRels) AddFooter(id, target string) *Relationship {
return d.Relationships.AddRelationship(id, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", target)
}
// ToXML 将文档关系转换为XML
func (d *DocumentRels) ToXML() string {
return d.Relationships.ToXML()
}

345
document/run.go Normal file
View File

@@ -0,0 +1,345 @@
package document
import (
"fmt"
)
// Run 表示Word文档中的文本运行
type Run struct {
Text string
Properties *RunProperties
BreakType string
Drawing *Drawing
Field *Field
}
// Field 表示Word文档中的域
type Field struct {
Type string // begin, separate, end
Code string // 域代码
}
// RunProperties 表示文本运行的属性
type RunProperties struct {
Bold bool // 粗体
Italic bool // 斜体
Underline string // 下划线类型single, double, thick, dotted, dash, etc.
Strike bool // 删除线
DoubleStrike bool // 双删除线
Superscript bool // 上标
Subscript bool // 下标
FontSize int // 字号,单位为半点
FontFamily string // 字体
Color string // 颜色格式为RRGGBB
Highlight string // 突出显示颜色
Caps bool // 全部大写
SmallCaps bool // 小型大写
CharacterSpacing int // 字符间距
Shading *Shading // 底纹
VertAlign string // 垂直对齐方式baseline, superscript, subscript
RTL bool // 从右到左文本方向
Language string // 语言
}
// BreakType 表示分隔符类型
const (
BreakTypePage = "page" // 分页符
BreakTypeColumn = "column" // 分栏符
BreakTypeSection = "section" // 分节符
BreakTypeLine = "textWrapping" // 换行符
)
// NewRun 创建一个新的文本运行
func NewRun() *Run {
return &Run{
Text: "",
Properties: &RunProperties{
FontSize: 22, // 默认11磅 (22半点)
FontFamily: "Calibri",
Color: "000000",
},
}
}
// AddText 向文本运行添加文本
func (r *Run) AddText(text string) *Run {
r.Text = text
return r
}
// AddBreak 向文本运行添加分隔符
func (r *Run) AddBreak(breakType string) *Run {
r.BreakType = breakType
return r
}
// AddDrawing 向文本运行添加图形
func (r *Run) AddDrawing(drawing *Drawing) *Run {
r.Drawing = drawing
return r
}
// SetBold 设置粗体
func (r *Run) SetBold(bold bool) *Run {
r.Properties.Bold = bold
return r
}
// SetItalic 设置斜体
func (r *Run) SetItalic(italic bool) *Run {
r.Properties.Italic = italic
return r
}
// SetUnderline 设置下划线
func (r *Run) SetUnderline(underline string) *Run {
r.Properties.Underline = underline
return r
}
// SetStrike 设置删除线
func (r *Run) SetStrike(strike bool) *Run {
r.Properties.Strike = strike
return r
}
// SetDoubleStrike 设置双删除线
func (r *Run) SetDoubleStrike(doubleStrike bool) *Run {
r.Properties.DoubleStrike = doubleStrike
return r
}
// SetSuperscript 设置上标
func (r *Run) SetSuperscript(superscript bool) *Run {
r.Properties.Superscript = superscript
return r
}
// SetSubscript 设置下标
func (r *Run) SetSubscript(subscript bool) *Run {
r.Properties.Subscript = subscript
return r
}
// SetFontSize 设置字号
func (r *Run) SetFontSize(fontSize int) *Run {
r.Properties.FontSize = fontSize
return r
}
// SetFontFamily 设置字体
func (r *Run) SetFontFamily(fontFamily string) *Run {
r.Properties.FontFamily = fontFamily
return r
}
// SetColor 设置颜色
func (r *Run) SetColor(color string) *Run {
r.Properties.Color = color
return r
}
// SetHighlight 设置突出显示颜色
func (r *Run) SetHighlight(highlight string) *Run {
r.Properties.Highlight = highlight
return r
}
// SetCaps 设置全部大写
func (r *Run) SetCaps(caps bool) *Run {
r.Properties.Caps = caps
return r
}
// SetSmallCaps 设置小型大写
func (r *Run) SetSmallCaps(smallCaps bool) *Run {
r.Properties.SmallCaps = smallCaps
return r
}
// SetCharacterSpacing 设置字符间距
func (r *Run) SetCharacterSpacing(spacing int) *Run {
r.Properties.CharacterSpacing = spacing
return r
}
// SetShading 设置底纹
func (r *Run) SetShading(fill, color, pattern string) *Run {
r.Properties.Shading = &Shading{
Fill: fill,
Color: color,
Pattern: pattern,
}
return r
}
// SetVertAlign 设置垂直对齐方式
func (r *Run) SetVertAlign(vertAlign string) *Run {
r.Properties.VertAlign = vertAlign
return r
}
// SetRTL 设置从右到左文本方向
func (r *Run) SetRTL(rtl bool) *Run {
r.Properties.RTL = rtl
return r
}
// SetLanguage 设置语言
func (r *Run) SetLanguage(language string) *Run {
r.Properties.Language = language
return r
}
// AddField 添加Word域
func (r *Run) AddField(fieldType string, fieldCode string) *Run {
r.Text = ""
r.Field = &Field{
Type: fieldType,
Code: fieldCode,
}
return r
}
// AddPageNumber 添加页码域
func (r *Run) AddPageNumber() *Run {
return r.AddField("begin", " PAGE ")
}
// ToXML 将文本运行转换为XML
func (r *Run) ToXML() string {
xml := "<w:r>"
// 添加文本运行属性
xml += "<w:rPr>"
// 字体
if r.Properties.FontFamily != "" {
xml += "<w:rFonts w:ascii=\"" + r.Properties.FontFamily + "\""
xml += " w:eastAsia=\"" + r.Properties.FontFamily + "\""
xml += " w:hAnsi=\"" + r.Properties.FontFamily + "\""
xml += " w:cs=\"" + r.Properties.FontFamily + "\" />"
}
// 字号
if r.Properties.FontSize > 0 {
xml += "<w:sz w:val=\"" + fmt.Sprintf("%d", r.Properties.FontSize) + "\" />"
xml += "<w:szCs w:val=\"" + fmt.Sprintf("%d", r.Properties.FontSize) + "\" />"
}
// 颜色
if r.Properties.Color != "" {
xml += "<w:color w:val=\"" + r.Properties.Color + "\" />"
}
// 粗体
if r.Properties.Bold {
xml += "<w:b />"
xml += "<w:bCs />"
}
// 斜体
if r.Properties.Italic {
xml += "<w:i />"
xml += "<w:iCs />"
}
// 下划线
if r.Properties.Underline != "" {
xml += "<w:u w:val=\"" + r.Properties.Underline + "\" />"
}
// 删除线
if r.Properties.Strike {
xml += "<w:strike />"
}
// 双删除线
if r.Properties.DoubleStrike {
xml += "<w:dstrike />"
}
// 突出显示颜色
if r.Properties.Highlight != "" {
xml += "<w:highlight w:val=\"" + r.Properties.Highlight + "\" />"
}
// 全部大写
if r.Properties.Caps {
xml += "<w:caps />"
}
// 小型大写
if r.Properties.SmallCaps {
xml += "<w:smallCaps />"
}
// 字符间距
if r.Properties.CharacterSpacing != 0 {
xml += "<w:spacing w:val=\"" + fmt.Sprintf("%d", r.Properties.CharacterSpacing) + "\" />"
}
// 底纹
if r.Properties.Shading != nil {
xml += "<w:shd w:val=\"" + r.Properties.Shading.Pattern + "\""
xml += " w:fill=\"" + r.Properties.Shading.Fill + "\""
xml += " w:color=\"" + r.Properties.Shading.Color + "\" />"
}
// 上标/下标
if r.Properties.Superscript {
xml += "<w:vertAlign w:val=\"superscript\" />"
} else if r.Properties.Subscript {
xml += "<w:vertAlign w:val=\"subscript\" />"
} else if r.Properties.VertAlign != "" {
xml += "<w:vertAlign w:val=\"" + r.Properties.VertAlign + "\" />"
}
// 从右到左文本方向
if r.Properties.RTL {
xml += "<w:rtl />"
}
// 语言
if r.Properties.Language != "" {
xml += "<w:lang w:val=\"" + r.Properties.Language + "\" />"
}
xml += "</w:rPr>"
// 添加分隔符
if r.BreakType != "" {
xml += "<w:br w:type=\"" + r.BreakType + "\" />"
}
// 添加文本
if r.Text != "" {
xml += "<w:t xml:space=\"preserve\">" + r.Text + "</w:t>"
}
// 添加图形
if r.Drawing != nil {
xml += r.Drawing.ToXML()
}
// 添加域
if r.Field != nil {
if r.Field.Type == "begin" {
xml += "<w:fldChar w:fldCharType=\"begin\" />"
} else if r.Field.Type == "separate" {
xml += "<w:fldChar w:fldCharType=\"separate\" />"
} else if r.Field.Type == "end" {
xml += "<w:fldChar w:fldCharType=\"end\" />"
}
if r.Field.Code != "" && r.Field.Type == "begin" {
// 添加域代码
xml += "</w:r><w:r><w:instrText xml:space=\"preserve\">" + r.Field.Code + "</w:instrText></w:r><w:r><w:fldChar w:fldCharType=\"separate\" />"
// 添加域结束标记
xml += "</w:r><w:r><w:fldChar w:fldCharType=\"end\" />"
}
}
xml += "</w:r>"
return xml
}

202
document/settings.go Normal file
View File

@@ -0,0 +1,202 @@
package document
import (
"fmt"
)
// Settings 表示Word文档中的设置
type Settings struct {
UpdateFields bool // 更新域
Zoom int // 缩放比例
DefaultTabStop int // 默认制表位
CharacterSpacingControl string // 字符间距控制
Compatibility *Compatibility
}
// Compatibility 表示兼容性设置
type Compatibility struct {
CompatibilityMode string // 兼容模式
DoNotExpandShiftReturn bool // 不展开Shift+Enter
DoNotBreakWrappedTables bool // 不断开环绕表格
DoNotSnapToGridInCell bool // 单元格中不对齐网格
DoNotWrapTextWithPunct bool // 不使用标点符号换行
DoNotUseEastAsianBreakRules bool // 不使用东亚换行规则
DoNotUseIndentAsNumberingTabStop bool // 不使用缩进作为编号制表位
UseAnsiKerningPairs bool // 使用ANSI字距调整对
DoNotAutofitConstrainedTables bool // 不自动调整受限表格
SplitPgBreakAndParaMark bool // 分割分页符和段落标记
DoNotVertAlignCellWithSp bool // 不垂直对齐带有形状的单元格
DoNotBreakConstrainedForcedTable bool // 不断开受限强制表格
DoNotVertAlignInTxbx bool // 不在文本框中垂直对齐
UseAnsiSpaceForEnglishInEastAsia bool // 在东亚语言中为英文使用ANSI空格
AllowSpaceOfSameStyleInTable bool // 允许表格中相同样式的空格
DoNotSuppressIndentation bool // 不抑制缩进
DoNotAutospaceEastAsianText bool // 不自动调整东亚文本间距
DoNotUseHTMLParagraphAutoSpacing bool // 不使用HTML段落自动间距
}
// NewSettings 创建一个新的设置
func NewSettings() *Settings {
return &Settings{
UpdateFields: true,
Zoom: 100,
DefaultTabStop: 720, // 720 twip = 0.5 inch
CharacterSpacingControl: "doNotCompress",
Compatibility: NewCompatibility(),
}
}
// NewCompatibility 创建一个新的兼容性设置
func NewCompatibility() *Compatibility {
return &Compatibility{
CompatibilityMode: "15", // Word 2013
}
}
// SetUpdateFields 设置是否更新域
func (s *Settings) SetUpdateFields(updateFields bool) *Settings {
s.UpdateFields = updateFields
return s
}
// SetZoom 设置缩放比例
func (s *Settings) SetZoom(zoom int) *Settings {
s.Zoom = zoom
return s
}
// SetDefaultTabStop 设置默认制表位
func (s *Settings) SetDefaultTabStop(defaultTabStop int) *Settings {
s.DefaultTabStop = defaultTabStop
return s
}
// SetCharacterSpacingControl 设置字符间距控制
func (s *Settings) SetCharacterSpacingControl(characterSpacingControl string) *Settings {
s.CharacterSpacingControl = characterSpacingControl
return s
}
// SetCompatibilityMode 设置兼容模式
func (s *Settings) SetCompatibilityMode(compatibilityMode string) *Settings {
s.Compatibility.CompatibilityMode = compatibilityMode
return s
}
// ToXML 将设置转换为XML
func (s *Settings) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<w:settings xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
// 更新域
if s.UpdateFields {
xml += "<w:updateFields w:val=\"true\" />"
}
// 缩放比例
xml += "<w:zoom w:percent=\"" + fmt.Sprintf("%d", s.Zoom) + "\" />"
// 默认制表位
xml += "<w:defaultTabStop w:val=\"" + fmt.Sprintf("%d", s.DefaultTabStop) + "\" />"
// 字符间距控制
xml += "<w:characterSpacingControl w:val=\"" + s.CharacterSpacingControl + "\" />"
// 兼容性设置
xml += "<w:compat>"
// 兼容模式
if s.Compatibility.CompatibilityMode != "" {
xml += "<w:compatSetting w:name=\"compatibilityMode\" w:uri=\"http://schemas.microsoft.com/office/document\" w:val=\"" + s.Compatibility.CompatibilityMode + "\" />"
}
// 不展开Shift+Enter
if s.Compatibility.DoNotExpandShiftReturn {
xml += "<w:doNotExpandShiftReturn />"
}
// 不断开环绕表格
if s.Compatibility.DoNotBreakWrappedTables {
xml += "<w:doNotBreakWrappedTables />"
}
// 单元格中不对齐网格
if s.Compatibility.DoNotSnapToGridInCell {
xml += "<w:doNotSnapToGridInCell />"
}
// 不使用标点符号换行
if s.Compatibility.DoNotWrapTextWithPunct {
xml += "<w:doNotWrapTextWithPunct />"
}
// 不使用东亚换行规则
if s.Compatibility.DoNotUseEastAsianBreakRules {
xml += "<w:doNotUseEastAsianBreakRules />"
}
// 不使用缩进作为编号制表位
if s.Compatibility.DoNotUseIndentAsNumberingTabStop {
xml += "<w:doNotUseIndentAsNumberingTabStop />"
}
// 使用ANSI字距调整对
if s.Compatibility.UseAnsiKerningPairs {
xml += "<w:useAnsiKerningPairs />"
}
// 不自动调整受限表格
if s.Compatibility.DoNotAutofitConstrainedTables {
xml += "<w:doNotAutofitConstrainedTables />"
}
// 分割分页符和段落标记
if s.Compatibility.SplitPgBreakAndParaMark {
xml += "<w:splitPgBreakAndParaMark />"
}
// 不垂直对齐带有形状的单元格
if s.Compatibility.DoNotVertAlignCellWithSp {
xml += "<w:doNotVertAlignCellWithSp />"
}
// 不断开受限强制表格
if s.Compatibility.DoNotBreakConstrainedForcedTable {
xml += "<w:doNotBreakConstrainedForcedTable />"
}
// 不在文本框中垂直对齐
if s.Compatibility.DoNotVertAlignInTxbx {
xml += "<w:doNotVertAlignInTxbx />"
}
// 在东亚语言中为英文使用ANSI空格
if s.Compatibility.UseAnsiSpaceForEnglishInEastAsia {
xml += "<w:useAnsiSpaceForEnglishInEastAsia />"
}
// 允许表格中相同样式的空格
if s.Compatibility.AllowSpaceOfSameStyleInTable {
xml += "<w:allowSpaceOfSameStyleInTable />"
}
// 不抑制缩进
if s.Compatibility.DoNotSuppressIndentation {
xml += "<w:doNotSuppressIndentation />"
}
// 不自动调整东亚文本间距
if s.Compatibility.DoNotAutospaceEastAsianText {
xml += "<w:doNotAutospaceEastAsianText />"
}
// 不使用HTML段落自动间距
if s.Compatibility.DoNotUseHTMLParagraphAutoSpacing {
xml += "<w:doNotUseHTMLParagraphAutoSpacing />"
}
xml += "</w:compat>"
xml += "</w:settings>"
return xml
}

399
document/styles.go Normal file
View File

@@ -0,0 +1,399 @@
package document
import (
"fmt"
)
// Styles 表示Word文档中的样式集合
type Styles struct {
Styles []*Style
}
// Style 表示Word文档中的样式
type Style struct {
ID string
Type string // paragraph, character, table, numbering
Name string
BasedOn string
Next string
Link string
Default bool
CustomStyle bool
ParagraphProperties *ParagraphProperties
RunProperties *RunProperties
TableProperties *TableProperties
}
// NewStyles 创建一个新的样式集合
func NewStyles() *Styles {
return &Styles{
Styles: make([]*Style, 0),
}
}
// AddStyle 添加一个样式
func (s *Styles) AddStyle(id, name, styleType string) *Style {
style := &Style{
ID: id,
Type: styleType,
Name: name,
CustomStyle: true,
ParagraphProperties: &ParagraphProperties{},
RunProperties: &RunProperties{},
}
s.Styles = append(s.Styles, style)
return style
}
// GetStyle 获取指定ID的样式
func (s *Styles) GetStyle(id string) *Style {
for _, style := range s.Styles {
if style.ID == id {
return style
}
}
return nil
}
// SetBasedOn 设置样式的基础样式
func (s *Style) SetBasedOn(basedOn string) *Style {
s.BasedOn = basedOn
return s
}
// SetNext 设置样式的下一个样式
func (s *Style) SetNext(next string) *Style {
s.Next = next
return s
}
// SetLink 设置样式的链接
func (s *Style) SetLink(link string) *Style {
s.Link = link
return s
}
// SetDefault 设置样式是否为默认样式
func (s *Style) SetDefault(isDefault bool) *Style {
s.Default = isDefault
return s
}
// SetParagraphProperties 设置段落属性
func (s *Style) SetParagraphProperties(props *ParagraphProperties) *Style {
s.ParagraphProperties = props
return s
}
// SetRunProperties 设置文本运行属性
func (s *Style) SetRunProperties(props *RunProperties) *Style {
s.RunProperties = props
return s
}
// SetTableProperties 设置表格属性
func (s *Style) SetTableProperties(props *TableProperties) *Style {
s.TableProperties = props
return s
}
// ToXML 将样式集合转换为XML
func (s *Styles) ToXML() string {
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<w:styles xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">"
// 添加默认样式
xml += "<w:docDefaults>"
xml += "<w:rPrDefault>"
xml += "<w:rPr>"
xml += "<w:rFonts w:ascii=\"Calibri\" w:eastAsia=\"Calibri\" w:hAnsi=\"Calibri\" w:cs=\"Calibri\" />"
xml += "<w:sz w:val=\"22\" />"
xml += "<w:szCs w:val=\"22\" />"
xml += "<w:lang w:val=\"en-US\" w:eastAsia=\"en-US\" w:bidi=\"ar-SA\" />"
xml += "</w:rPr>"
xml += "</w:rPrDefault>"
xml += "<w:pPrDefault>"
xml += "<w:pPr>"
xml += "<w:spacing w:after=\"200\" w:line=\"276\" w:lineRule=\"auto\" />"
xml += "</w:pPr>"
xml += "</w:pPrDefault>"
xml += "</w:docDefaults>"
// 添加所有样式
for _, style := range s.Styles {
xml += "<w:style w:type=\"" + style.Type + "\" w:styleId=\"" + style.ID + "\">"
// 样式名称
xml += "<w:name w:val=\"" + style.Name + "\" />"
// 基础样式
if style.BasedOn != "" {
xml += "<w:basedOn w:val=\"" + style.BasedOn + "\" />"
}
// 下一个样式
if style.Next != "" {
xml += "<w:next w:val=\"" + style.Next + "\" />"
}
// 链接
if style.Link != "" {
xml += "<w:link w:val=\"" + style.Link + "\" />"
}
// 默认样式
if style.Default {
xml += "<w:qFormat />"
}
// 自定义样式
if style.CustomStyle {
xml += "<w:customStyle w:val=\"1\" />"
}
// 段落属性
if style.ParagraphProperties != nil && style.Type == "paragraph" {
xml += "<w:pPr>"
// 对齐方式
if style.ParagraphProperties.Alignment != "" {
xml += "<w:jc w:val=\"" + style.ParagraphProperties.Alignment + "\" />"
}
// 缩进
if style.ParagraphProperties.IndentLeft > 0 || style.ParagraphProperties.IndentRight > 0 || style.ParagraphProperties.IndentFirstLine > 0 {
xml += "<w:ind"
if style.ParagraphProperties.IndentLeft > 0 {
xml += " w:left=\"" + fmt.Sprintf("%d", style.ParagraphProperties.IndentLeft) + "\""
}
if style.ParagraphProperties.IndentRight > 0 {
xml += " w:right=\"" + fmt.Sprintf("%d", style.ParagraphProperties.IndentRight) + "\""
}
if style.ParagraphProperties.IndentFirstLine > 0 {
xml += " w:firstLine=\"" + fmt.Sprintf("%d", style.ParagraphProperties.IndentFirstLine) + "\""
}
xml += " />"
}
// 间距
if style.ParagraphProperties.SpacingBefore > 0 || style.ParagraphProperties.SpacingAfter > 0 || style.ParagraphProperties.SpacingLine > 0 {
xml += "<w:spacing"
if style.ParagraphProperties.SpacingBefore > 0 {
xml += " w:before=\"" + fmt.Sprintf("%d", style.ParagraphProperties.SpacingBefore) + "\""
}
if style.ParagraphProperties.SpacingAfter > 0 {
xml += " w:after=\"" + fmt.Sprintf("%d", style.ParagraphProperties.SpacingAfter) + "\""
}
if style.ParagraphProperties.SpacingLine > 0 {
xml += " w:line=\"" + fmt.Sprintf("%d", style.ParagraphProperties.SpacingLine) + "\""
xml += " w:lineRule=\"" + style.ParagraphProperties.SpacingLineRule + "\""
}
xml += " />"
}
// 分页控制
if style.ParagraphProperties.KeepNext {
xml += "<w:keepNext />"
}
if style.ParagraphProperties.KeepLines {
xml += "<w:keepLines />"
}
if style.ParagraphProperties.PageBreakBefore {
xml += "<w:pageBreakBefore />"
}
if style.ParagraphProperties.WidowControl {
xml += "<w:widowControl />"
}
// 边框
if style.ParagraphProperties.BorderTop != nil || style.ParagraphProperties.BorderBottom != nil ||
style.ParagraphProperties.BorderLeft != nil || style.ParagraphProperties.BorderRight != nil {
xml += "<w:pBdr>"
if style.ParagraphProperties.BorderTop != nil {
xml += "<w:top w:val=\"" + style.ParagraphProperties.BorderTop.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderTop.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderTop.Space) + "\""
xml += " w:color=\"" + style.ParagraphProperties.BorderTop.Color + "\" />"
}
if style.ParagraphProperties.BorderBottom != nil {
xml += "<w:bottom w:val=\"" + style.ParagraphProperties.BorderBottom.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderBottom.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderBottom.Space) + "\""
xml += " w:color=\"" + style.ParagraphProperties.BorderBottom.Color + "\" />"
}
if style.ParagraphProperties.BorderLeft != nil {
xml += "<w:left w:val=\"" + style.ParagraphProperties.BorderLeft.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderLeft.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderLeft.Space) + "\""
xml += " w:color=\"" + style.ParagraphProperties.BorderLeft.Color + "\" />"
}
if style.ParagraphProperties.BorderRight != nil {
xml += "<w:right w:val=\"" + style.ParagraphProperties.BorderRight.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderRight.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.ParagraphProperties.BorderRight.Space) + "\""
xml += " w:color=\"" + style.ParagraphProperties.BorderRight.Color + "\" />"
}
xml += "</w:pBdr>"
}
// 底纹
if style.ParagraphProperties.Shading != nil {
xml += "<w:shd w:val=\"" + style.ParagraphProperties.Shading.Pattern + "\""
xml += " w:fill=\"" + style.ParagraphProperties.Shading.Fill + "\""
xml += " w:color=\"" + style.ParagraphProperties.Shading.Color + "\" />"
}
xml += "</w:pPr>"
}
// 文本运行属性
if style.RunProperties != nil {
xml += "<w:rPr>"
// 字体
if style.RunProperties.FontFamily != "" {
xml += "<w:rFonts w:ascii=\"" + style.RunProperties.FontFamily + "\""
xml += " w:eastAsia=\"" + style.RunProperties.FontFamily + "\""
xml += " w:hAnsi=\"" + style.RunProperties.FontFamily + "\""
xml += " w:cs=\"" + style.RunProperties.FontFamily + "\" />"
}
// 字号
if style.RunProperties.FontSize > 0 {
xml += "<w:sz w:val=\"" + fmt.Sprintf("%d", style.RunProperties.FontSize) + "\" />"
xml += "<w:szCs w:val=\"" + fmt.Sprintf("%d", style.RunProperties.FontSize) + "\" />"
}
// 颜色
if style.RunProperties.Color != "" {
xml += "<w:color w:val=\"" + style.RunProperties.Color + "\" />"
}
// 粗体
if style.RunProperties.Bold {
xml += "<w:b />"
xml += "<w:bCs />"
}
// 斜体
if style.RunProperties.Italic {
xml += "<w:i />"
xml += "<w:iCs />"
}
// 下划线
if style.RunProperties.Underline != "" {
xml += "<w:u w:val=\"" + style.RunProperties.Underline + "\" />"
}
// 删除线
if style.RunProperties.Strike {
xml += "<w:strike />"
}
// 双删除线
if style.RunProperties.DoubleStrike {
xml += "<w:dstrike />"
}
// 突出显示颜色
if style.RunProperties.Highlight != "" {
xml += "<w:highlight w:val=\"" + style.RunProperties.Highlight + "\" />"
}
// 全部大写
if style.RunProperties.Caps {
xml += "<w:caps />"
}
// 小型大写
if style.RunProperties.SmallCaps {
xml += "<w:smallCaps />"
}
// 字符间距
if style.RunProperties.CharacterSpacing != 0 {
xml += "<w:spacing w:val=\"" + fmt.Sprintf("%d", style.RunProperties.CharacterSpacing) + "\" />"
}
// 底纹
if style.RunProperties.Shading != nil {
xml += "<w:shd w:val=\"" + style.RunProperties.Shading.Pattern + "\""
xml += " w:fill=\"" + style.RunProperties.Shading.Fill + "\""
xml += " w:color=\"" + style.RunProperties.Shading.Color + "\" />"
}
// 垂直对齐方式
if style.RunProperties.VertAlign != "" {
xml += "<w:vertAlign w:val=\"" + style.RunProperties.VertAlign + "\" />"
}
xml += "</w:rPr>"
}
// 表格属性
if style.TableProperties != nil && style.Type == "table" {
xml += "<w:tblPr>"
// 表格宽度
if style.TableProperties.Width > 0 {
xml += "<w:tblW w:w=\"" + fmt.Sprintf("%d", style.TableProperties.Width) + "\""
xml += " w:type=\"" + style.TableProperties.WidthType + "\" />"
}
// 表格对齐方式
if style.TableProperties.Alignment != "" {
xml += "<w:jc w:val=\"" + style.TableProperties.Alignment + "\" />"
}
// 表格边框
if style.TableProperties.Borders != nil {
xml += "<w:tblBorders>"
if style.TableProperties.Borders.Top != nil {
xml += "<w:top w:val=\"" + style.TableProperties.Borders.Top.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Top.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Top.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.Top.Color + "\" />"
}
if style.TableProperties.Borders.Bottom != nil {
xml += "<w:bottom w:val=\"" + style.TableProperties.Borders.Bottom.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Bottom.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Bottom.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.Bottom.Color + "\" />"
}
if style.TableProperties.Borders.Left != nil {
xml += "<w:left w:val=\"" + style.TableProperties.Borders.Left.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Left.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Left.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.Left.Color + "\" />"
}
if style.TableProperties.Borders.Right != nil {
xml += "<w:right w:val=\"" + style.TableProperties.Borders.Right.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Right.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.Right.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.Right.Color + "\" />"
}
if style.TableProperties.Borders.InsideH != nil {
xml += "<w:insideH w:val=\"" + style.TableProperties.Borders.InsideH.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.InsideH.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.InsideH.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.InsideH.Color + "\" />"
}
if style.TableProperties.Borders.InsideV != nil {
xml += "<w:insideV w:val=\"" + style.TableProperties.Borders.InsideV.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.InsideV.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", style.TableProperties.Borders.InsideV.Space) + "\""
xml += " w:color=\"" + style.TableProperties.Borders.InsideV.Color + "\" />"
}
xml += "</w:tblBorders>"
}
xml += "</w:tblPr>"
}
xml += "</w:style>"
}
xml += "</w:styles>"
return xml
}

632
document/table.go Normal file
View File

@@ -0,0 +1,632 @@
package document
import (
"fmt"
)
// Table 表示Word文档中的表格
type Table struct {
Rows []*TableRow
Properties *TableProperties
}
// TableProperties 表示表格的属性
type TableProperties struct {
Width int // 表格宽度单位为twip
WidthType string // 宽度类型auto, dxa, pct
Alignment string // 对齐方式left, center, right
Indent int // 缩进
Borders *TableBorders
CellMargin *TableCellMargin
Layout string // 布局方式fixed, autofit
Look string // 表格外观
Style string // 表格样式ID
FirstRow bool // 首行特殊格式
LastRow bool // 末行特殊格式
FirstColumn bool // 首列特殊格式
LastColumn bool // 末列特殊格式
NoHBand bool // 无水平带状格式
NoVBand bool // 无垂直带状格式
}
// TableBorders 表示表格的边框
type TableBorders struct {
Top *Border
Bottom *Border
Left *Border
Right *Border
InsideH *Border
InsideV *Border
}
// TableCellMargin 表示表格单元格的边距
type TableCellMargin struct {
Top int
Bottom int
Left int
Right int
}
// TableRow 表示表格的行
type TableRow struct {
Cells []*TableCell
Properties *TableRowProperties
}
// TableRowProperties 表示表格行的属性
type TableRowProperties struct {
Height int // 行高单位为twip
HeightRule string // 行高规则atLeast, exact, auto
CantSplit bool // 不允许跨页分割
IsHeader bool // 是否为表头行
}
// TableCell 表示表格的单元格
type TableCell struct {
Content []interface{} // 可以是段落、表格等元素
Properties *TableCellProperties
}
// TableCellProperties 表示表格单元格的属性
type TableCellProperties struct {
Width int // 单元格宽度单位为twip
WidthType string // 宽度类型auto, dxa, pct
VertAlign string // 垂直对齐方式top, center, bottom
Borders *TableBorders
Shading *Shading
GridSpan int // 跨列数
VMerge string // 垂直合并restart, continue
NoWrap bool // 不换行
FitText bool // 适应文本
}
// NewTable 创建一个新的表格
func NewTable(rows, cols int) *Table {
t := &Table{
Rows: make([]*TableRow, 0),
Properties: &TableProperties{
Width: 0,
WidthType: "auto",
Alignment: "left",
Layout: "autofit",
Borders: &TableBorders{
Top: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
Bottom: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
Left: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
Right: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
InsideH: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
InsideV: &Border{Style: "single", Size: 4, Color: "000000", Space: 0},
},
CellMargin: &TableCellMargin{
Top: 0,
Bottom: 0,
Left: 108, // 约0.15厘米
Right: 108,
},
},
}
// 创建行和单元格
for i := 0; i < rows; i++ {
row := t.AddRow()
for j := 0; j < cols; j++ {
row.AddCell()
}
}
return t
}
// AddRow 向表格添加一行并返回它
func (t *Table) AddRow() *TableRow {
r := &TableRow{
Cells: make([]*TableCell, 0),
Properties: &TableRowProperties{
Height: 0,
HeightRule: "auto",
CantSplit: false,
IsHeader: false,
},
}
t.Rows = append(t.Rows, r)
return r
}
// SetWidth 设置表格宽度
func (t *Table) SetWidth(width int, widthType string) *Table {
t.Properties.Width = width
t.Properties.WidthType = widthType
return t
}
// SetAlignment 设置表格对齐方式
func (t *Table) SetAlignment(alignment string) *Table {
t.Properties.Alignment = alignment
return t
}
// SetIndent 设置表格缩进
func (t *Table) SetIndent(indent int) *Table {
t.Properties.Indent = indent
return t
}
// SetLayout 设置表格布局方式
func (t *Table) SetLayout(layout string) *Table {
t.Properties.Layout = layout
return t
}
// SetBorders 设置表格边框
func (t *Table) SetBorders(position string, style string, size int, color string) *Table {
border := &Border{
Style: style,
Size: size,
Color: color,
Space: 0,
}
switch position {
case "top":
t.Properties.Borders.Top = border
case "bottom":
t.Properties.Borders.Bottom = border
case "left":
t.Properties.Borders.Left = border
case "right":
t.Properties.Borders.Right = border
case "insideH":
t.Properties.Borders.InsideH = border
case "insideV":
t.Properties.Borders.InsideV = border
case "all":
t.Properties.Borders.Top = border
t.Properties.Borders.Bottom = border
t.Properties.Borders.Left = border
t.Properties.Borders.Right = border
t.Properties.Borders.InsideH = border
t.Properties.Borders.InsideV = border
}
return t
}
// SetCellMargin 设置表格单元格边距
func (t *Table) SetCellMargin(position string, margin int) *Table {
switch position {
case "top":
t.Properties.CellMargin.Top = margin
case "bottom":
t.Properties.CellMargin.Bottom = margin
case "left":
t.Properties.CellMargin.Left = margin
case "right":
t.Properties.CellMargin.Right = margin
case "all":
t.Properties.CellMargin.Top = margin
t.Properties.CellMargin.Bottom = margin
t.Properties.CellMargin.Left = margin
t.Properties.CellMargin.Right = margin
}
return t
}
// SetStyle 设置表格样式
func (t *Table) SetStyle(style string) *Table {
t.Properties.Style = style
return t
}
// SetLook 设置表格外观
func (t *Table) SetLook(firstRow, lastRow, firstColumn, lastColumn, noHBand, noVBand bool) *Table {
t.Properties.FirstRow = firstRow
t.Properties.LastRow = lastRow
t.Properties.FirstColumn = firstColumn
t.Properties.LastColumn = lastColumn
t.Properties.NoHBand = noHBand
t.Properties.NoVBand = noVBand
// 计算Look值
look := 0
if firstRow {
look |= 0x0020
}
if lastRow {
look |= 0x0040
}
if firstColumn {
look |= 0x0080
}
if lastColumn {
look |= 0x0100
}
if noHBand {
look |= 0x0200
}
if noVBand {
look |= 0x0400
}
t.Properties.Look = fmt.Sprintf("%04X", look)
return t
}
// AddCell 向表格行添加一个单元格并返回它
func (r *TableRow) AddCell() *TableCell {
c := &TableCell{
Content: make([]interface{}, 0),
Properties: &TableCellProperties{
Width: 0,
WidthType: "auto",
VertAlign: "top",
GridSpan: 1,
},
}
r.Cells = append(r.Cells, c)
return c
}
// SetHeight 设置行高
func (r *TableRow) SetHeight(height int, rule string) *TableRow {
r.Properties.Height = height
r.Properties.HeightRule = rule
return r
}
// SetCantSplit 设置不允许跨页分割
func (r *TableRow) SetCantSplit(cantSplit bool) *TableRow {
r.Properties.CantSplit = cantSplit
return r
}
// SetIsHeader 设置是否为表头行
func (r *TableRow) SetIsHeader(isHeader bool) *TableRow {
r.Properties.IsHeader = isHeader
return r
}
// AddParagraph 向单元格添加一个段落并返回它
func (c *TableCell) AddParagraph() *Paragraph {
p := NewParagraph()
c.Content = append(c.Content, p)
return p
}
// AddTable 向单元格添加一个表格并返回它
func (c *TableCell) AddTable(rows, cols int) *Table {
t := NewTable(rows, cols)
c.Content = append(c.Content, t)
return t
}
// SetWidth 设置单元格宽度
func (c *TableCell) SetWidth(width int, widthType string) *TableCell {
c.Properties.Width = width
c.Properties.WidthType = widthType
return c
}
// SetVertAlign 设置单元格垂直对齐方式
func (c *TableCell) SetVertAlign(vertAlign string) *TableCell {
c.Properties.VertAlign = vertAlign
return c
}
// SetBorders 设置单元格边框
func (c *TableCell) SetBorders(position string, style string, size int, color string) *TableCell {
if c.Properties.Borders == nil {
c.Properties.Borders = &TableBorders{}
}
border := &Border{
Style: style,
Size: size,
Color: color,
Space: 0,
}
switch position {
case "top":
c.Properties.Borders.Top = border
case "bottom":
c.Properties.Borders.Bottom = border
case "left":
c.Properties.Borders.Left = border
case "right":
c.Properties.Borders.Right = border
case "all":
c.Properties.Borders.Top = border
c.Properties.Borders.Bottom = border
c.Properties.Borders.Left = border
c.Properties.Borders.Right = border
}
return c
}
// SetShading 设置单元格底纹
func (c *TableCell) SetShading(fill, color, pattern string) *TableCell {
c.Properties.Shading = &Shading{
Fill: fill,
Color: color,
Pattern: pattern,
}
return c
}
// SetGridSpan 设置单元格跨列数
func (c *TableCell) SetGridSpan(gridSpan int) *TableCell {
c.Properties.GridSpan = gridSpan
return c
}
// SetVMerge 设置单元格垂直合并
func (c *TableCell) SetVMerge(vMerge string) *TableCell {
c.Properties.VMerge = vMerge
return c
}
// SetNoWrap 设置单元格不换行
func (c *TableCell) SetNoWrap(noWrap bool) *TableCell {
c.Properties.NoWrap = noWrap
return c
}
// SetFitText 设置单元格适应文本
func (c *TableCell) SetFitText(fitText bool) *TableCell {
c.Properties.FitText = fitText
return c
}
// ToXML 将表格转换为XML
func (t *Table) ToXML() string {
xml := "<w:tbl>"
// 添加表格属性
xml += "<w:tblPr>"
// 表格宽度
if t.Properties.Width > 0 {
xml += "<w:tblW w:w=\"" + fmt.Sprintf("%d", t.Properties.Width) + "\""
xml += " w:type=\"" + t.Properties.WidthType + "\" />"
} else {
xml += "<w:tblW w:w=\"0\" w:type=\"auto\" />"
}
// 表格对齐方式
if t.Properties.Alignment != "" {
xml += "<w:jc w:val=\"" + t.Properties.Alignment + "\" />"
}
// 表格缩进
if t.Properties.Indent > 0 {
xml += "<w:tblInd w:w=\"" + fmt.Sprintf("%d", t.Properties.Indent) + "\""
xml += " w:type=\"dxa\" />"
}
// 表格边框
if t.Properties.Borders != nil {
xml += "<w:tblBorders>"
if t.Properties.Borders.Top != nil {
xml += "<w:top w:val=\"" + t.Properties.Borders.Top.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.Top.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.Top.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.Top.Color + "\" />"
}
if t.Properties.Borders.Bottom != nil {
xml += "<w:bottom w:val=\"" + t.Properties.Borders.Bottom.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.Bottom.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.Bottom.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.Bottom.Color + "\" />"
}
if t.Properties.Borders.Left != nil {
xml += "<w:left w:val=\"" + t.Properties.Borders.Left.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.Left.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.Left.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.Left.Color + "\" />"
}
if t.Properties.Borders.Right != nil {
xml += "<w:right w:val=\"" + t.Properties.Borders.Right.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.Right.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.Right.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.Right.Color + "\" />"
}
if t.Properties.Borders.InsideH != nil {
xml += "<w:insideH w:val=\"" + t.Properties.Borders.InsideH.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.InsideH.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.InsideH.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.InsideH.Color + "\" />"
}
if t.Properties.Borders.InsideV != nil {
xml += "<w:insideV w:val=\"" + t.Properties.Borders.InsideV.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", t.Properties.Borders.InsideV.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", t.Properties.Borders.InsideV.Space) + "\""
xml += " w:color=\"" + t.Properties.Borders.InsideV.Color + "\" />"
}
xml += "</w:tblBorders>"
}
// 表格单元格边距
if t.Properties.CellMargin != nil {
xml += "<w:tblCellMar>"
if t.Properties.CellMargin.Top > 0 {
xml += "<w:top w:w=\"" + fmt.Sprintf("%d", t.Properties.CellMargin.Top) + "\""
xml += " w:type=\"dxa\" />"
}
if t.Properties.CellMargin.Bottom > 0 {
xml += "<w:bottom w:w=\"" + fmt.Sprintf("%d", t.Properties.CellMargin.Bottom) + "\""
xml += " w:type=\"dxa\" />"
}
if t.Properties.CellMargin.Left > 0 {
xml += "<w:left w:w=\"" + fmt.Sprintf("%d", t.Properties.CellMargin.Left) + "\""
xml += " w:type=\"dxa\" />"
}
if t.Properties.CellMargin.Right > 0 {
xml += "<w:right w:w=\"" + fmt.Sprintf("%d", t.Properties.CellMargin.Right) + "\""
xml += " w:type=\"dxa\" />"
}
xml += "</w:tblCellMar>"
}
// 表格布局方式
if t.Properties.Layout != "" {
xml += "<w:tblLayout w:type=\"" + t.Properties.Layout + "\" />"
}
// 表格样式
if t.Properties.Style != "" {
xml += "<w:tblStyle w:val=\"" + t.Properties.Style + "\" />"
}
// 表格外观
if t.Properties.Look != "" {
xml += "<w:tblLook w:val=\"" + t.Properties.Look + "\""
xml += " w:firstRow=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.FirstRow)) + "\""
xml += " w:lastRow=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.LastRow)) + "\""
xml += " w:firstColumn=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.FirstColumn)) + "\""
xml += " w:lastColumn=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.LastColumn)) + "\""
xml += " w:noHBand=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.NoHBand)) + "\""
xml += " w:noVBand=\"" + fmt.Sprintf("%d", boolToInt(t.Properties.NoVBand)) + "\" />"
}
xml += "</w:tblPr>"
// 添加表格网格
xml += "<w:tblGrid>"
if len(t.Rows) > 0 && len(t.Rows[0].Cells) > 0 {
for i := 0; i < len(t.Rows[0].Cells); i++ {
xml += "<w:gridCol />"
}
}
xml += "</w:tblGrid>"
// 添加所有行的XML
for _, row := range t.Rows {
xml += "<w:tr>"
// 添加行属性
xml += "<w:trPr>"
// 行高
if row.Properties.Height > 0 {
xml += "<w:trHeight w:val=\"" + fmt.Sprintf("%d", row.Properties.Height) + "\""
xml += " w:hRule=\"" + row.Properties.HeightRule + "\" />"
}
// 不允许跨页分割
if row.Properties.CantSplit {
xml += "<w:cantSplit />"
}
// 表头行
if row.Properties.IsHeader {
xml += "<w:tblHeader />"
}
xml += "</w:trPr>"
// 添加所有单元格的XML
for _, cell := range row.Cells {
xml += "<w:tc>"
// 添加单元格属性
xml += "<w:tcPr>"
// 单元格宽度
if cell.Properties.Width > 0 {
xml += "<w:tcW w:w=\"" + fmt.Sprintf("%d", cell.Properties.Width) + "\""
xml += " w:type=\"" + cell.Properties.WidthType + "\" />"
} else {
xml += "<w:tcW w:w=\"0\" w:type=\"auto\" />"
}
// 垂直对齐方式
if cell.Properties.VertAlign != "" {
xml += "<w:vAlign w:val=\"" + cell.Properties.VertAlign + "\" />"
}
// 单元格边框
if cell.Properties.Borders != nil {
xml += "<w:tcBorders>"
if cell.Properties.Borders.Top != nil {
xml += "<w:top w:val=\"" + cell.Properties.Borders.Top.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Top.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Top.Space) + "\""
xml += " w:color=\"" + cell.Properties.Borders.Top.Color + "\" />"
}
if cell.Properties.Borders.Bottom != nil {
xml += "<w:bottom w:val=\"" + cell.Properties.Borders.Bottom.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Bottom.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Bottom.Space) + "\""
xml += " w:color=\"" + cell.Properties.Borders.Bottom.Color + "\" />"
}
if cell.Properties.Borders.Left != nil {
xml += "<w:left w:val=\"" + cell.Properties.Borders.Left.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Left.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Left.Space) + "\""
xml += " w:color=\"" + cell.Properties.Borders.Left.Color + "\" />"
}
if cell.Properties.Borders.Right != nil {
xml += "<w:right w:val=\"" + cell.Properties.Borders.Right.Style + "\""
xml += " w:sz=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Right.Size) + "\""
xml += " w:space=\"" + fmt.Sprintf("%d", cell.Properties.Borders.Right.Space) + "\""
xml += " w:color=\"" + cell.Properties.Borders.Right.Color + "\" />"
}
xml += "</w:tcBorders>"
}
// 底纹
if cell.Properties.Shading != nil {
xml += "<w:shd w:val=\"" + cell.Properties.Shading.Pattern + "\""
xml += " w:fill=\"" + cell.Properties.Shading.Fill + "\""
xml += " w:color=\"" + cell.Properties.Shading.Color + "\" />"
}
// 跨列数
if cell.Properties.GridSpan > 1 {
xml += "<w:gridSpan w:val=\"" + fmt.Sprintf("%d", cell.Properties.GridSpan) + "\" />"
}
// 垂直合并
if cell.Properties.VMerge != "" {
xml += "<w:vMerge w:val=\"" + cell.Properties.VMerge + "\" />"
}
// 不换行
if cell.Properties.NoWrap {
xml += "<w:noWrap />"
}
// 适应文本
if cell.Properties.FitText {
xml += "<w:fitText />"
}
xml += "</w:tcPr>"
// 添加所有内容元素的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 += "<w:p><w:pPr></w:pPr></w:p>"
}
xml += "</w:tc>"
}
xml += "</w:tr>"
}
xml += "</w:tbl>"
return xml
}

164
document/theme.go Normal file
View File

@@ -0,0 +1,164 @@
package document
// Theme 表示Word文档中的主题
type Theme struct {
Name string
}
// NewTheme 创建一个新的主题
func NewTheme() *Theme {
return &Theme{
Name: "Office Theme",
}
}
// ToXML 将主题转换为XML
func (t *Theme) ToXML() string {
// 这里提供一个简化的Office主题XML
xml := "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
xml += "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"" + t.Name + "\">"
// 颜色方案
xml += "<a:themeElements>"
xml += "<a:clrScheme name=\"Office\">"
xml += "<a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1>"
xml += "<a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1>"
xml += "<a:dk2><a:srgbClr val=\"44546A\"/></a:dk2>"
xml += "<a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2>"
xml += "<a:accent1><a:srgbClr val=\"4472C4\"/></a:accent1>"
xml += "<a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2>"
xml += "<a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3>"
xml += "<a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4>"
xml += "<a:accent5><a:srgbClr val=\"5B9BD5\"/></a:accent5>"
xml += "<a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6>"
xml += "<a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink>"
xml += "<a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink>"
xml += "</a:clrScheme>"
// 字体方案
xml += "<a:fontScheme name=\"Office\">"
xml += "<a:majorFont>"
xml += "<a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/>"
xml += "<a:ea typeface=\"\"/>"
xml += "<a:cs typeface=\"\"/>"
xml += "<a:font script=\"Jpan\" typeface=\"游ゴシック Light\"/>"
xml += "<a:font script=\"Hang\" typeface=\"맑은 고딕\"/>"
xml += "<a:font script=\"Hans\" typeface=\"等线 Light\"/>"
xml += "<a:font script=\"Hant\" typeface=\"新細明體\"/>"
xml += "<a:font script=\"Arab\" typeface=\"Times New Roman\"/>"
xml += "<a:font script=\"Hebr\" typeface=\"Times New Roman\"/>"
xml += "<a:font script=\"Thai\" typeface=\"Angsana New\"/>"
xml += "<a:font script=\"Ethi\" typeface=\"Nyala\"/>"
xml += "<a:font script=\"Beng\" typeface=\"Vrinda\"/>"
xml += "<a:font script=\"Gujr\" typeface=\"Shruti\"/>"
xml += "<a:font script=\"Khmr\" typeface=\"MoolBoran\"/>"
xml += "<a:font script=\"Knda\" typeface=\"Tunga\"/>"
xml += "<a:font script=\"Guru\" typeface=\"Raavi\"/>"
xml += "<a:font script=\"Cans\" typeface=\"Euphemia\"/>"
xml += "<a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/>"
xml += "<a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/>"
xml += "<a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/>"
xml += "<a:font script=\"Thaa\" typeface=\"MV Boli\"/>"
xml += "<a:font script=\"Deva\" typeface=\"Mangal\"/>"
xml += "<a:font script=\"Telu\" typeface=\"Gautami\"/>"
xml += "<a:font script=\"Taml\" typeface=\"Latha\"/>"
xml += "<a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/>"
xml += "<a:font script=\"Orya\" typeface=\"Kalinga\"/>"
xml += "<a:font script=\"Mlym\" typeface=\"Kartika\"/>"
xml += "<a:font script=\"Laoo\" typeface=\"DokChampa\"/>"
xml += "<a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/>"
xml += "<a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/>"
xml += "<a:font script=\"Viet\" typeface=\"Times New Roman\"/>"
xml += "<a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/>"
xml += "<a:font script=\"Geor\" typeface=\"Sylfaen\"/>"
xml += "</a:majorFont>"
xml += "<a:minorFont>"
xml += "<a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/>"
xml += "<a:ea typeface=\"\"/>"
xml += "<a:cs typeface=\"\"/>"
xml += "<a:font script=\"Jpan\" typeface=\"游ゴシック\"/>"
xml += "<a:font script=\"Hang\" typeface=\"맑은 고딕\"/>"
xml += "<a:font script=\"Hans\" typeface=\"等线\"/>"
xml += "<a:font script=\"Hant\" typeface=\"新細明體\"/>"
xml += "<a:font script=\"Arab\" typeface=\"Arial\"/>"
xml += "<a:font script=\"Hebr\" typeface=\"Arial\"/>"
xml += "<a:font script=\"Thai\" typeface=\"Cordia New\"/>"
xml += "<a:font script=\"Ethi\" typeface=\"Nyala\"/>"
xml += "<a:font script=\"Beng\" typeface=\"Vrinda\"/>"
xml += "<a:font script=\"Gujr\" typeface=\"Shruti\"/>"
xml += "<a:font script=\"Khmr\" typeface=\"DaunPenh\"/>"
xml += "<a:font script=\"Knda\" typeface=\"Tunga\"/>"
xml += "<a:font script=\"Guru\" typeface=\"Raavi\"/>"
xml += "<a:font script=\"Cans\" typeface=\"Euphemia\"/>"
xml += "<a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/>"
xml += "<a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/>"
xml += "<a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/>"
xml += "<a:font script=\"Thaa\" typeface=\"MV Boli\"/>"
xml += "<a:font script=\"Deva\" typeface=\"Mangal\"/>"
xml += "<a:font script=\"Telu\" typeface=\"Gautami\"/>"
xml += "<a:font script=\"Taml\" typeface=\"Latha\"/>"
xml += "<a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/>"
xml += "<a:font script=\"Orya\" typeface=\"Kalinga\"/>"
xml += "<a:font script=\"Mlym\" typeface=\"Kartika\"/>"
xml += "<a:font script=\"Laoo\" typeface=\"DokChampa\"/>"
xml += "<a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/>"
xml += "<a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/>"
xml += "<a:font script=\"Viet\" typeface=\"Arial\"/>"
xml += "<a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/>"
xml += "<a:font script=\"Geor\" typeface=\"Sylfaen\"/>"
xml += "</a:minorFont>"
xml += "</a:fontScheme>"
// 格式方案
xml += "<a:fmtScheme name=\"Office\">"
xml += "<a:fillStyleLst>"
xml += "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>"
xml += "<a:gradFill rotWithShape=\"1\">"
xml += "<a:gsLst>"
xml += "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs>"
xml += "</a:gsLst>"
xml += "<a:lin ang=\"5400000\" scaled=\"0\"/>"
xml += "</a:gradFill>"
xml += "<a:gradFill rotWithShape=\"1\">"
xml += "<a:gsLst>"
xml += "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:satMod val=\"103000\"/><a:lumMod val=\"102000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"110000\"/><a:lumMod val=\"100000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs>"
xml += "</a:gsLst>"
xml += "<a:lin ang=\"5400000\" scaled=\"0\"/>"
xml += "</a:gradFill>"
xml += "</a:fillStyleLst>"
xml += "<a:lnStyleLst>"
xml += "<a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln>"
xml += "<a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln>"
xml += "<a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln>"
xml += "</a:lnStyleLst>"
xml += "<a:effectStyleLst>"
xml += "<a:effectStyle><a:effectLst/></a:effectStyle>"
xml += "<a:effectStyle><a:effectLst/></a:effectStyle>"
xml += "<a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle>"
xml += "</a:effectStyleLst>"
xml += "<a:bgFillStyleLst>"
xml += "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>"
xml += "<a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill>"
xml += "<a:gradFill rotWithShape=\"1\">"
xml += "<a:gsLst>"
xml += "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"93000\"/><a:satMod val=\"150000\"/><a:shade val=\"98000\"/><a:lumMod val=\"102000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:tint val=\"98000\"/><a:satMod val=\"130000\"/><a:shade val=\"90000\"/><a:lumMod val=\"103000\"/></a:schemeClr></a:gs>"
xml += "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"63000\"/><a:satMod val=\"120000\"/></a:schemeClr></a:gs>"
xml += "</a:gsLst>"
xml += "<a:lin ang=\"5400000\" scaled=\"0\"/>"
xml += "</a:gradFill>"
xml += "</a:bgFillStyleLst>"
xml += "</a:fmtScheme>"
xml += "</a:themeElements>"
// 其他主题元素
xml += "<a:objectDefaults/>"
xml += "<a:extraClrSchemeLst/>"
xml += "</a:theme>"
return xml
}

63
document/utils.go Normal file
View File

@@ -0,0 +1,63 @@
package document
import (
"fmt"
"math/rand"
"time"
)
// 初始化随机数生成器
func init() {
rand.New(rand.NewSource(time.Now().UnixNano()))
}
// generateUniqueID 生成一个唯一的ID
func generateUniqueID() string {
return fmt.Sprintf("%d", rand.Intn(1000000))
}
// boolToInt 将布尔值转换为整数
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
// boolToString 将布尔值转换为字符串
func boolToString(b bool) string {
if b {
return "1"
}
return "0"
}
// twipToCm 将twip转换为厘米
func twipToCm(twip int) float64 {
return float64(twip) / 1440.0
}
// cmToTwip 将厘米转换为twip
func cmToTwip(cm float64) int {
return int(cm * 1440.0)
}
// pointToTwip 将磅转换为twip
func pointToTwip(point float64) int {
return int(point * 20.0)
}
// twipToPoint 将twip转换为磅
func twipToPoint(twip int) float64 {
return float64(twip) / 20.0
}
// emuToPx 将EMU(English Metric Unit)转换为像素
func emuToPx(emu int) int {
return emu / 9525
}
// pxToEmu 将像素转换为EMU
func pxToEmu(px int) int {
return px * 9525
}