🎉 Initial commit
This commit is contained in:
184
document/body.go
Normal file
184
document/body.go
Normal 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
112
document/content_types.go
Normal 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
685
document/document.go
Normal 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
336
document/drawing.go
Normal 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)
|
||||
}
|
||||
150
document/examples/simple/main.go
Normal file
150
document/examples/simple/main.go
Normal 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
95
document/header_footer.go
Normal 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
375
document/numbering.go
Normal 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
299
document/paragraph.go
Normal 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
121
document/relationships.go
Normal 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
345
document/run.go
Normal 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
202
document/settings.go
Normal 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
399
document/styles.go
Normal 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
632
document/table.go
Normal 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
164
document/theme.go
Normal 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
63
document/utils.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user