diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e1f1b28
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+.idea/
+.vscode/
+.DS_Store
+output/
+
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
\ No newline at end of file
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 0000000..a0c415f
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 gingfrederik
+
+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.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a0e774f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# docx
+
+## Introduction
+docx is a simple library to creating DOCX file in Go.
\ No newline at end of file
diff --git a/docx.go b/docx.go
new file mode 100644
index 0000000..e1eaa0c
--- /dev/null
+++ b/docx.go
@@ -0,0 +1,20 @@
+package docx
+
+import "encoding/xml"
+
+const (
+ XMLNS_W = `http://schemas.openxmlformats.org/wordprocessingml/2006/main`
+ XMLNS_R = `http://schemas.openxmlformats.org/officeDocument/2006/relationships`
+)
+
+type Document struct {
+ XMLName xml.Name `xml:"w:document"`
+ XMLW string `xml:"xmlns:w,attr"`
+ XMLR string `xml:"xmlns:r,attr"`
+ Body *Body
+}
+
+type Body struct {
+ XMLName xml.Name `xml:"w:body"`
+ Paragraph []*Paragraph
+}
diff --git a/example/main.go b/example/main.go
new file mode 100644
index 0000000..c4e64bf
--- /dev/null
+++ b/example/main.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/gingfrederik/docx"
+)
+
+func main() {
+ f := docx.NewFile()
+ // add new paragraph
+ para := f.AddParagraph()
+ // add text
+ para.AddText("test")
+
+ para.AddText("test font size").Size(22)
+ para.AddText("test color").Color("808080")
+ para.AddText("test font size and color").Size(22).Color("121212")
+
+ nextPara := f.AddParagraph()
+ nextPara.AddLink("google", `http://google.com`)
+
+ f.Save("./test.docx")
+}
diff --git a/file.go b/file.go
new file mode 100644
index 0000000..efadef0
--- /dev/null
+++ b/file.go
@@ -0,0 +1,146 @@
+package docx
+
+import (
+ "archive/zip"
+ "encoding/xml"
+ "io"
+ "os"
+ "strconv"
+)
+
+type File struct {
+ Document Document
+ DocRelation DocRelation
+
+ rId int
+}
+
+func NewFile() *File {
+ 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",
+ },
+ }
+
+ f := &File{
+ Document: Document{
+ XMLName: xml.Name{
+ Space: "w",
+ },
+ XMLW: XMLNS_W,
+ XMLR: XMLNS_R,
+ Body: &Body{
+ XMLName: xml.Name{
+ Space: "w",
+ },
+ Paragraph: make([]*Paragraph, 0),
+ },
+ },
+ DocRelation: DocRelation{
+ Xmlns: XMLNS,
+ Relationship: defaultRel,
+ },
+ rId: 4,
+ }
+
+ return f
+}
+
+// Save save file to path
+func (f *File) Save(path string) (err error) {
+ fzip, _ := os.Create(path)
+ defer fzip.Close()
+
+ zipWriter := zip.NewWriter(fzip)
+ defer zipWriter.Close()
+
+ return f.pack(zipWriter)
+}
+
+func (f *File) Write(writer io.Writer) (err error) {
+ zipWriter := zip.NewWriter(writer)
+ defer zipWriter.Close()
+
+ return f.pack(zipWriter)
+}
+
+// AddParagraph add new paragraph
+func (f *File) AddParagraph() *Paragraph {
+ p := &Paragraph{
+ Data: make([]interface{}, 0),
+ file: f,
+ }
+
+ f.Document.Body.Paragraph = append(f.Document.Body.Paragraph, p)
+ return p
+}
+
+func (f *File) 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.Relationship = append(f.DocRelation.Relationship, rel)
+
+ return rel.ID
+}
+
+func (f *File) 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 {
+ return
+ }
+
+ out = xml.Header + string(body)
+ return
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..b637fc0
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/gingfrederik/docx
+
+go 1.13
diff --git a/paragraph.go b/paragraph.go
new file mode 100644
index 0000000..ebe04ae
--- /dev/null
+++ b/paragraph.go
@@ -0,0 +1,46 @@
+package docx
+
+import "encoding/xml"
+
+type Paragraph struct {
+ XMLName xml.Name `xml:"w:p"`
+ Data []interface{}
+
+ file *File
+}
+
+// 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, run)
+
+ return run
+}
+
+// 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, hyperlink)
+
+ return hyperlink
+}
diff --git a/relationship.go b/relationship.go
new file mode 100644
index 0000000..887e515
--- /dev/null
+++ b/relationship.go
@@ -0,0 +1,24 @@
+package docx
+
+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 DocRelation struct {
+ XMLName xml.Name `xml:"Relationships"`
+ Xmlns string `xml:"xmlns,attr"`
+ Relationship []*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"`
+}
diff --git a/run.go b/run.go
new file mode 100644
index 0000000..77b6183
--- /dev/null
+++ b/run.go
@@ -0,0 +1,39 @@
+package docx
+
+import "encoding/xml"
+
+type Run struct {
+ XMLName xml.Name `xml:"w:r"`
+ RunProperties *RunProperties `xml:"w:rPr,omitempty"`
+ InstrText string `xml:"w:instrText,omitempty"`
+ Text *Text
+}
+
+type Text struct {
+ XMLName xml.Name `xml:"w:t"`
+ XMLSpace string `xml:"xml:space,attr,omitempty"`
+ Text string `xml:",chardata"`
+}
+
+// Color set run color
+func (r *Run) Color(color string) *Run {
+ r.RunProperties.Color = &Color{
+ Val: color,
+ }
+
+ return r
+}
+
+// Size set run size
+func (r *Run) Size(size int) *Run {
+ r.RunProperties.Size = &Size{
+ Val: size * 2,
+ }
+ return r
+}
+
+type Hyperlink struct {
+ XMLName xml.Name `xml:"w:hyperlink"`
+ ID string `xml:"r:id,attr"`
+ Run Run
+}
diff --git a/run_properties.go b/run_properties.go
new file mode 100644
index 0000000..d647d18
--- /dev/null
+++ b/run_properties.go
@@ -0,0 +1,29 @@
+package docx
+
+import "encoding/xml"
+
+const (
+ HYPERLINK_STYLE = "a1"
+)
+
+type RunProperties struct {
+ XMLName xml.Name `xml:"w:rPr"`
+ Color *Color `xml:"w:color,omitempty"`
+ Size *Size `xml:"w:sz,omitempty"`
+ RunStyle *RunStyle `xml:"w:rStyle,omitempty"`
+}
+
+type RunStyle struct {
+ XMLName xml.Name `xml:"w:rStyle"`
+ Val string `xml:"w:val,attr"`
+}
+
+type Color struct {
+ XMLName xml.Name `xml:"w:color"`
+ Val string `xml:"w:val,attr"`
+}
+
+type Size struct {
+ XMLName xml.Name `xml:"w:sz"`
+ Val int `xml:"w:val,attr"`
+}
diff --git a/template.go b/template.go
new file mode 100644
index 0000000..0dd2ab6
--- /dev/null
+++ b/template.go
@@ -0,0 +1,389 @@
+package docx
+
+const (
+ TEMP_REL = `
+
+
+
+
+ `
+ TEMP_DOCPROPS_APP = `Go DOCX`
+ TEMP_DOCPROPS_CORE = ``
+ TEMP_CONTENT = `
+
+
+
+
+
+
+
+
+ `
+ TEMP_WORD_STYLE = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ TEMP_WORD_THEME_THEME = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+)