1
0
mirror of https://github.com/fumiama/go-docx.git synced 2026-06-06 00:00:24 +08:00

First commit

This commit is contained in:
Gonzalo Fernandez-Victorio
2021-04-23 16:58:31 +01:00
parent bd1f5d3e9b
commit eae5f90385
16 changed files with 957 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
docxlib

23
LICENSE Normal file
View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2020 gingfrederik
Copyright (c) 2021 Gonzalo Fernandez-Victorio
Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

61
README.md Normal file
View File

@@ -0,0 +1,61 @@
# Docx library
Yet another library to manipulate .docx (Microsoft Word) files in Go.
## Introduction
As part of my work for [Basement Crowd](https://www.basementcrowd.com) y [FromCounsel](https://www.fromcounsel.com), we were in need of a basic library to manipulate (both read and write)
The difference with other projects is the following:
- [UniOffice](https://github.com/unidoc/unioffice) is probably the most complete but it is also commercial (you need to pay). It also very complete, but too much for my needs.
- [gingfrederik/docx](https://github.com/gingfrederik/docx) only allows to write.
There are also a couple of other projects [kingzbauer/docx](https://github.com/kingzbauer/docx) and [nguyenthenguyen/docx](https://github.com/nguyenthenguyen/docx)
[gingfrederik/docx](https://github.com/gingfrederik/docx) was a heavy influence (the original structures and the main method come from that project).
However, the structures didn't handle reading and extending them was particularly difficult due to Go xml parser being limited and [6 year old bug](https://github.com/golang/go/issues/9519).
Additionally, my requirements go beyond the original structure and a hard fork seemed more sensible.
The plan is to evolve the library, so the API is likely to change according to my company's needs. But please do feel free to send patches, reports and PRs or fork.
In the mean time, shared as an example.
## Getting Started
### Install
Go modules supported
```sh
go get github.com/gonfva/docxlib
```
### Usage
See [main](main/main.go) for an example
```
$ ./docxlib
Preparing new document to write at /tmp/new-file.docx
Document writen.
Now trying to read it
We've found a new run with the text ->test
We've found a new run with the text ->test font size
We've found a new run with the text ->test color
We've found a new run with the text ->test font size and color
End of main
```
### Build
```
$ go build -o docxlib ./main
```
## License
MIT. See [LICENSE](LICENSE)

389
constants.go Normal file
View File

@@ -0,0 +1,389 @@
package docxlib
const (
TEMP_REL = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`
TEMP_DOCPROPS_APP = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>Go DOCX</Application></Properties>`
TEMP_DOCPROPS_CORE = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></cp:coreProperties>`
TEMP_CONTENT = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
<Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>`
TEMP_WORD_STYLE = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid"
xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid">
<w:docDefaults>
<w:rPrDefault>
<w:rPr>
<w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorEastAsia" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
<w:kern w:val="2"/>
<w:sz w:val="24"/>
<w:szCs w:val="24"/>
<w:lang w:val="en-US" w:eastAsia="zh-TW" w:bidi="ar-SA"/>
</w:rPr>
</w:rPrDefault>
<w:pPrDefault/>
</w:docDefaults>
<w:style w:type="paragraph" w:default="1" w:styleId="a">
<w:name w:val="Normal"/>
<w:qFormat/>
<w:rsid w:val="00A955DD"/>
<w:pPr>
<w:widowControl w:val="0"/>
</w:pPr>
<w:rPr>
<w:szCs w:val="22"/>
</w:rPr>
</w:style>
<w:style w:type="character" w:default="1" w:styleId="a0">
<w:name w:val="Default Paragraph Font"/>
<w:uiPriority w:val="1"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
</w:style>
<w:style w:type="character" w:styleId="a1">
<w:name w:val="Hyperlink"/>
<w:basedOn w:val="a0"/>
<w:uiPriority w:val="99"/>
<w:unhideWhenUsed/>
<w:rsid w:val="003B745A"/>
<w:rPr>
<w:color w:val="0563C1" w:themeColor="hyperlink"/>
<w:u w:val="single"/>
</w:rPr>
</w:style>
</w:styles>`
TEMP_WORD_THEME_THEME = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office-Design">
<a:themeElements>
<a:clrScheme name="Office">
<a:dk1>
<a:sysClr val="windowText" lastClr="000000"/>
</a:dk1>
<a:lt1>
<a:sysClr val="window" lastClr="FFFFFF"/>
</a:lt1>
<a:dk2>
<a:srgbClr val="1F497D"/>
</a:dk2>
<a:lt2>
<a:srgbClr val="EEECE1"/>
</a:lt2>
<a:accent1>
<a:srgbClr val="4F81BD"/>
</a:accent1>
<a:accent2>
<a:srgbClr val="C0504D"/>
</a:accent2>
<a:accent3>
<a:srgbClr val="9BBB59"/>
</a:accent3>
<a:accent4>
<a:srgbClr val="8064A2"/>
</a:accent4>
<a:accent5>
<a:srgbClr val="4BACC6"/>
</a:accent5>
<a:accent6>
<a:srgbClr val="F79646"/>
</a:accent6>
<a:hlink>
<a:srgbClr val="0000FF"/>
</a:hlink>
<a:folHlink>
<a:srgbClr val="800080"/>
</a:folHlink>
</a:clrScheme>
<a:fontScheme name="Office">
<a:majorFont>
<a:latin typeface="Cambria"/>
<a:ea typeface=""/>
<a:cs typeface=""/>
<a:font script="Jpan" typeface=" Pゴシック"/>
<a:font script="Hang" typeface="맑은 고딕"/>
<a:font script="Hans" typeface="宋体"/>
<a:font script="Hant" typeface="新細明體"/>
<a:font script="Arab" typeface="Times New Roman"/>
<a:font script="Hebr" typeface="Times New Roman"/>
<a:font script="Thai" typeface="Tahoma"/>
<a:font script="Ethi" typeface="Nyala"/>
<a:font script="Beng" typeface="Vrinda"/>
<a:font script="Gujr" typeface="Shruti"/>
<a:font script="Khmr" typeface="MoolBoran"/>
<a:font script="Knda" typeface="Tunga"/>
<a:font script="Guru" typeface="Raavi"/>
<a:font script="Cans" typeface="Euphemia"/>
<a:font script="Cher" typeface="Plantagenet Cherokee"/>
<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
<a:font script="Tibt" typeface="Microsoft Himalaya"/>
<a:font script="Thaa" typeface="MV Boli"/>
<a:font script="Deva" typeface="Mangal"/>
<a:font script="Telu" typeface="Gautami"/>
<a:font script="Taml" typeface="Latha"/>
<a:font script="Syrc" typeface="Estrangelo Edessa"/>
<a:font script="Orya" typeface="Kalinga"/>
<a:font script="Mlym" typeface="Kartika"/>
<a:font script="Laoo" typeface="DokChampa"/>
<a:font script="Sinh" typeface="Iskoola Pota"/>
<a:font script="Mong" typeface="Mongolian Baiti"/>
<a:font script="Viet" typeface="Times New Roman"/>
<a:font script="Uigh" typeface="Microsoft Uighur"/>
<a:font script="Geor" typeface="Sylfaen"/>
</a:majorFont>
<a:minorFont>
<a:latin typeface="Arial"/>
<a:ea typeface=""/>
<a:cs typeface=""/>
<a:font script="Jpan" typeface=" Pゴシック"/>
<a:font script="Hang" typeface="맑은 고딕"/>
<a:font script="Hans" typeface="宋体"/>
<a:font script="Hant" typeface="新細明體"/>
<a:font script="Arab" typeface="Arial"/>
<a:font script="Hebr" typeface="Arial"/>
<a:font script="Thai" typeface="Tahoma"/>
<a:font script="Ethi" typeface="Nyala"/>
<a:font script="Beng" typeface="Vrinda"/>
<a:font script="Gujr" typeface="Shruti"/>
<a:font script="Khmr" typeface="DaunPenh"/>
<a:font script="Knda" typeface="Tunga"/>
<a:font script="Guru" typeface="Raavi"/>
<a:font script="Cans" typeface="Euphemia"/>
<a:font script="Cher" typeface="Plantagenet Cherokee"/>
<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
<a:font script="Tibt" typeface="Microsoft Himalaya"/>
<a:font script="Thaa" typeface="MV Boli"/>
<a:font script="Deva" typeface="Mangal"/>
<a:font script="Telu" typeface="Gautami"/>
<a:font script="Taml" typeface="Latha"/>
<a:font script="Syrc" typeface="Estrangelo Edessa"/>
<a:font script="Orya" typeface="Kalinga"/>
<a:font script="Mlym" typeface="Kartika"/>
<a:font script="Laoo" typeface="DokChampa"/>
<a:font script="Sinh" typeface="Iskoola Pota"/>
<a:font script="Mong" typeface="Mongolian Baiti"/>
<a:font script="Viet" typeface="Arial"/>
<a:font script="Uigh" typeface="Microsoft Uighur"/>
<a:font script="Geor" typeface="Sylfaen"/>
</a:minorFont>
</a:fontScheme>
<a:fmtScheme name="Office">
<a:fillStyleLst>
<a:solidFill>
<a:schemeClr val="phClr"/>
</a:solidFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0">
<a:schemeClr val="phClr">
<a:tint val="50000"/>
<a:satMod val="300000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="35000">
<a:schemeClr val="phClr">
<a:tint val="37000"/>
<a:satMod val="300000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="100000">
<a:schemeClr val="phClr">
<a:tint val="15000"/>
<a:satMod val="350000"/>
</a:schemeClr>
</a:gs>
</a:gsLst>
<a:lin ang="16200000" scaled="1"/>
</a:gradFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0">
<a:schemeClr val="phClr">
<a:tint val="100000"/>
<a:shade val="100000"/>
<a:satMod val="130000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="100000">
<a:schemeClr val="phClr">
<a:tint val="50000"/>
<a:shade val="100000"/>
<a:satMod val="350000"/>
</a:schemeClr>
</a:gs>
</a:gsLst>
<a:lin ang="16200000" scaled="0"/>
</a:gradFill>
</a:fillStyleLst>
<a:lnStyleLst>
<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
<a:solidFill>
<a:schemeClr val="phClr">
<a:shade val="95000"/>
<a:satMod val="105000"/>
</a:schemeClr>
</a:solidFill>
<a:prstDash val="solid"/>
</a:ln>
<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
<a:solidFill>
<a:schemeClr val="phClr"/>
</a:solidFill>
<a:prstDash val="solid"/>
</a:ln>
<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr">
<a:solidFill>
<a:schemeClr val="phClr"/>
</a:solidFill>
<a:prstDash val="solid"/>
</a:ln>
</a:lnStyleLst>
<a:effectStyleLst>
<a:effectStyle>
<a:effectLst>
<a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0">
<a:srgbClr val="000000">
<a:alpha val="38000"/>
</a:srgbClr>
</a:outerShdw>
</a:effectLst>
</a:effectStyle>
<a:effectStyle>
<a:effectLst>
<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
<a:srgbClr val="000000">
<a:alpha val="35000"/>
</a:srgbClr>
</a:outerShdw>
</a:effectLst>
</a:effectStyle>
<a:effectStyle>
<a:effectLst>
<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
<a:srgbClr val="000000">
<a:alpha val="35000"/>
</a:srgbClr>
</a:outerShdw>
</a:effectLst>
<a:scene3d>
<a:camera prst="orthographicFront">
<a:rot lat="0" lon="0" rev="0"/>
</a:camera>
<a:lightRig rig="threePt" dir="t">
<a:rot lat="0" lon="0" rev="1200000"/>
</a:lightRig>
</a:scene3d>
<a:sp3d>
<a:bevelT w="63500" h="25400"/>
</a:sp3d>
</a:effectStyle>
</a:effectStyleLst>
<a:bgFillStyleLst>
<a:solidFill>
<a:schemeClr val="phClr"/>
</a:solidFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0">
<a:schemeClr val="phClr">
<a:tint val="40000"/>
<a:satMod val="350000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="40000">
<a:schemeClr val="phClr">
<a:tint val="45000"/>
<a:shade val="99000"/>
<a:satMod val="350000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="100000">
<a:schemeClr val="phClr">
<a:shade val="20000"/>
<a:satMod val="255000"/>
</a:schemeClr>
</a:gs>
</a:gsLst>
<a:path path="circle">
<a:fillToRect l="50000" t="-80000" r="50000" b="180000"/>
</a:path>
</a:gradFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0">
<a:schemeClr val="phClr">
<a:tint val="80000"/>
<a:satMod val="300000"/>
</a:schemeClr>
</a:gs>
<a:gs pos="100000">
<a:schemeClr val="phClr">
<a:shade val="30000"/>
<a:satMod val="200000"/>
</a:schemeClr>
</a:gs>
</a:gsLst>
<a:path path="circle">
<a:fillToRect l="50000" t="50000" r="50000" b="50000"/>
</a:path>
</a:gradFill>
</a:bgFillStyleLst>
</a:fmtScheme>
</a:themeElements>
<a:objectDefaults>
<a:spDef>
<a:spPr/>
<a:bodyPr/>
<a:lstStyle/>
<a:style>
<a:lnRef idx="1">
<a:schemeClr val="accent1"/>
</a:lnRef>
<a:fillRef idx="3">
<a:schemeClr val="accent1"/>
</a:fillRef>
<a:effectRef idx="2">
<a:schemeClr val="accent1"/>
</a:effectRef>
<a:fontRef idx="minor">
<a:schemeClr val="lt1"/>
</a:fontRef>
</a:style>
</a:spDef>
<a:lnDef>
<a:spPr/>
<a:bodyPr/>
<a:lstStyle/>
<a:style>
<a:lnRef idx="2">
<a:schemeClr val="accent1"/>
</a:lnRef>
<a:fillRef idx="0">
<a:schemeClr val="accent1"/>
</a:fillRef>
<a:effectRef idx="1">
<a:schemeClr val="accent1"/>
</a:effectRef>
<a:fontRef idx="minor">
<a:schemeClr val="tx1"/>
</a:fontRef>
</a:style>
</a:lnDef>
</a:objectDefaults>
<a:extraClrSchemeLst/>
</a:theme>`
)

20
document.go Normal file
View File

@@ -0,0 +1,20 @@
package docxlib
import "encoding/xml"
const (
XMLNS_W = `http://schemas.openxmlformats.org/wordprocessingml/2006/main`
XMLNS_R = `http://schemas.openxmlformats.org/officeDocument/2006/relationships`
)
type Body struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main body"`
Paragraphs []*Paragraph `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"`
}
type Document struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main document"`
XMLW string `xml:"xmlns:w,attr"`
XMLR string `xml:"xmlns:r,attr"`
Body *Body
}

37
docx.go Normal file
View File

@@ -0,0 +1,37 @@
package docxlib
import (
"archive/zip"
"io"
)
type Docx struct {
Document Document
DocRelation Relationships
rId int
}
// New generates a new empty docx file that we can manipulate and
// later on, save
func New() *Docx {
return emptyFile()
}
// Parse generates a new docx file in memory from a reader
func Parse(reader io.ReaderAt, size int64) (doc *Docx, err error) {
zipReader, err := zip.NewReader(reader, size)
if err != nil {
return nil, err
}
doc, err = unpack(zipReader)
return
}
// Write allows to save a docx to a writer
func (f *Docx) Write(writer io.Writer) (err error) {
zipWriter := zip.NewWriter(writer)
defer zipWriter.Close()
return f.pack(zipWriter)
}

48
empty.go Normal file
View File

@@ -0,0 +1,48 @@
package docxlib
import "encoding/xml"
func emptyRelationships() []*Relationship {
defaultRel := []*Relationship{
{
ID: "rId1",
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles`,
Target: "styles.xml",
},
{
ID: "rId2",
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme`,
Target: "theme/theme1.xml",
},
{
ID: "rId3",
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable`,
Target: "fontTable.xml",
},
}
return defaultRel
}
func emptyFile() *Docx {
docx := &Docx{
Document: Document{
XMLName: xml.Name{
Space: "w",
},
XMLW: XMLNS_W,
XMLR: XMLNS_R,
Body: &Body{
XMLName: xml.Name{
Space: "w",
},
Paragraphs: make([]*Paragraph, 0),
},
},
DocRelation: Relationships{
Xmlns: XMLNS,
Relationships: emptyRelationships(),
},
rId: 4,
}
return docx
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/gonfva/docxlib
go 1.16

38
link.go Normal file
View File

@@ -0,0 +1,38 @@
package docxlib
import "strconv"
func (f *Docx) addLinkRelation(link string) string {
rel := &Relationship{
ID: "rId" + strconv.Itoa(f.rId),
Type: REL_HYPERLINK,
Target: link,
TargetMode: REL_TARGETMODE,
}
f.rId += 1
f.DocRelation.Relationships = append(f.DocRelation.Relationships, rel)
return rel.ID
}
// AddLink add hyperlink to paragraph
func (p *Paragraph) AddLink(text string, link string) *Hyperlink {
rId := p.file.addLinkRelation(link)
hyperlink := &Hyperlink{
ID: rId,
Run: Run{
RunProperties: &RunProperties{
RunStyle: &RunStyle{
Val: HYPERLINK_STYLE,
},
},
InstrText: text,
},
}
p.Data = append(p.Data, ParagraphChild{Link: hyperlink})
return hyperlink
}

56
main/main.go Normal file
View File

@@ -0,0 +1,56 @@
package main
import (
"fmt"
"os"
"github.com/gonfva/docxlib"
)
const FILE_PATH = "/tmp/new-file.docx"
func main() {
fmt.Printf("Preparing new document to write at %s\n", FILE_PATH)
w := docxlib.New()
// add new paragraph
para1 := w.AddParagraph()
// add text
para1.AddText("test")
para1.AddText("test font size").Size(22)
para1.AddText("test color").Color("808080")
para2 := w.AddParagraph()
para2.AddText("test font size and color").Size(22).Color("ff0000")
nextPara := w.AddParagraph()
nextPara.AddLink("google", `http://google.com`)
f, err := os.Create(FILE_PATH)
if err != nil {
panic(err)
}
defer f.Close()
w.Write(f)
fmt.Println("Document writen. \nNow trying to read it")
// Now let's try to read the file
readFile, err := os.Open(FILE_PATH)
if err != nil {
panic(err)
}
fileinfo, err := readFile.Stat()
if err != nil {
panic(err)
}
size := fileinfo.Size()
doc, err := docxlib.Parse(readFile, int64(size))
if err != nil {
panic(err)
}
for _, para := range doc.Paragraphs() {
for _, run := range para.Runs() {
fmt.Printf("\tWe've found a new run with the text ->%s\n", run.Text.Text)
}
}
fmt.Println("End of main")
}

51
pack.go Normal file
View File

@@ -0,0 +1,51 @@
package docxlib
import (
"archive/zip"
"encoding/xml"
"fmt"
)
func (f *Docx) pack(zipWriter *zip.Writer) (err error) {
files := map[string]string{}
files["_rels/.rels"] = TEMP_REL
files["docProps/app.xml"] = TEMP_DOCPROPS_APP
files["docProps/core.xml"] = TEMP_DOCPROPS_CORE
files["word/theme/theme1.xml"] = TEMP_WORD_THEME_THEME
files["word/styles.xml"] = TEMP_WORD_STYLE
files["[Content_Types].xml"] = TEMP_CONTENT
files["word/_rels/document.xml.rels"], err = marshal(f.DocRelation)
if err != nil {
return err
}
files["word/document.xml"], err = marshal(f.Document)
if err != nil {
return err
}
for path, data := range files {
w, err := zipWriter.Create(path)
if err != nil {
return err
}
_, err = w.Write([]byte(data))
if err != nil {
return err
}
}
return
}
func marshal(data interface{}) (out string, err error) {
body, err := xml.Marshal(data)
if err != nil {
fmt.Println(err)
return
}
out = xml.Header + string(body)
return
}

42
paragraph.go Normal file
View File

@@ -0,0 +1,42 @@
package docxlib
import (
"encoding/xml"
)
type ParagraphChild struct {
Link *Hyperlink `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink"`
Run *Run `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r"`
}
type Paragraph struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"`
Data []ParagraphChild
file *Docx
}
// AddParagraph adds a new paragraph
func (f *Docx) AddParagraph() *Paragraph {
p := &Paragraph{
Data: make([]ParagraphChild, 0),
file: f,
}
f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, p)
return p
}
func (f *Docx) Paragraphs() []*Paragraph {
return f.Document.Body.Paragraphs
}
func (p *Paragraph) Runs() (ret []*Run) {
data := p.Data
for _, d := range data {
if d.Run != nil {
ret = append(ret, d.Run)
}
}
return
}

24
relationship.go Normal file
View File

@@ -0,0 +1,24 @@
package docxlib
import "encoding/xml"
const (
XMLNS = `http://schemas.openxmlformats.org/package/2006/relationships`
REL_HYPERLINK = `http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink`
REL_TARGETMODE = "External"
)
type Relationships struct {
XMLName xml.Name `xml:"Relationships"`
Xmlns string `xml:"xmlns,attr"`
Relationships []*Relationship `xml:"Relationship"`
}
type Relationship struct {
XMLName xml.Name `xml:"Relationship"`
ID string `xml:"Id,attr"`
Type string `xml:"Type,attr"`
Target string `xml:"Target,attr"`
TargetMode string `xml:"TargetMode,attr,omitempty"`
}

58
run.go Normal file
View File

@@ -0,0 +1,58 @@
package docxlib
import "encoding/xml"
// A Run is part of a paragraph that has its own style. It could be
// a piece of text in bold, or a link
type Run struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r"`
RunProperties *RunProperties `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"`
InstrText string `xml:"w:instrText,omitempty"`
Text *Text
}
// The Text object contains the actual text
type Text struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main t"`
XMLSpace string `xml:"xml:space,attr,omitempty"`
Text string `xml:",chardata"`
}
type Hyperlink struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink"`
ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr"`
Run Run
}
// Color allows to set run color
func (r *Run) Color(color string) *Run {
r.RunProperties.Color = &Color{
Val: color,
}
return r
}
// Size allows to set run size
func (r *Run) Size(size int) *Run {
r.RunProperties.Size = &Size{
Val: size * 2,
}
return r
}
// AddText add text to paragraph
func (p *Paragraph) AddText(text string) *Run {
t := &Text{
Text: text,
}
run := &Run{
Text: t,
RunProperties: &RunProperties{},
}
p.Data = append(p.Data, ParagraphChild{Run: run})
return run
}

29
run_properties.go Normal file
View File

@@ -0,0 +1,29 @@
package docxlib
import "encoding/xml"
const (
HYPERLINK_STYLE = "a1"
)
type RunProperties struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr"`
Color *Color `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main color,omitempty"`
Size *Size `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main sz,omitempty"`
RunStyle *RunStyle `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rStyle,omitempty"`
}
type RunStyle struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rStyle"`
Val string `xml:"w:val,attr"`
}
type Color struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main color"`
Val string `xml:"w:val,attr"`
}
type Size struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main sz"`
Val int `xml:"w:val,attr"`
}

77
unpack.go Normal file
View File

@@ -0,0 +1,77 @@
package docxlib
import (
"archive/zip"
"encoding/xml"
"fmt"
"io/ioutil"
)
func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
var doc *Document
var relations *Relationships
for _, f := range zipReader.File {
if f.Name == "word/_rels/document.xml.rels" {
relations, err = processRelations(f)
if err != nil {
return nil, err
}
}
if f.Name == "word/document.xml" {
doc, err = processDoc(f)
if err != nil {
return nil, err
}
}
}
docx = &Docx{
Document: *doc,
DocRelation: *relations,
}
return docx, nil
}
func processDoc(file *zip.File) (*Document, error) {
filebytes, err := readZipFile(file)
if err != nil {
fmt.Println("Error reading from internal zip file")
return nil, err
}
doc := Document{
XMLW: XMLNS_W,
XMLR: XMLNS_R,
XMLName: xml.Name{Space: XMLNS_W, Local: "document"}}
err = xml.Unmarshal(filebytes, &doc)
//r := bytes.NewReader(filebytes)
//err = decode(r)
if err != nil {
fmt.Println("Error unmarshalling doc")
fmt.Println(string(filebytes))
return nil, err
}
return &doc, nil
}
func processRelations(file *zip.File) (*Relationships, error) {
filebytes, err := readZipFile(file)
if err != nil {
fmt.Println("Error reading from internal zip file")
return nil, err
}
rels := Relationships{Xmlns: "none"}
err = xml.Unmarshal(filebytes, &rels)
if err != nil {
fmt.Println("Error unmarshalling relationships")
return nil, err
}
return &rels, nil
}
func readZipFile(zf *zip.File) ([]byte, error) {
f, err := zf.Open()
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}