Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Docx library

[![Go](https://github.com/gonfva/docxlib/actions/workflows/build.yml/badge.svg)](https://github.com/gonfva/docxlib/actions/workflows/build.yml) [![CodeQL](https://github.com/gonfva/docxlib/actions/workflows/codeql-analysis.yml/badge.svg?branch=master)](https://github.com/gonfva/docxlib/actions/workflows/codeql-analysis.yml)

Yet another library to read and write .docx (a.k.a. Microsoft Word documents or ECMA-376 Office Open XML) files in Go.

## Introduction
Expand Down Expand Up @@ -51,9 +53,12 @@ Now trying to read it
We've found a new hyperlink with ref http://google.com and the text google
End of main
```
You can also increase the log level (-logtostderr=true -v=0) and just dump a specific file(-file /tmp/new-file.docx). See [getstructure/main](getstructure/main.go)
You can also increase the log level (-logtostderr=true -v=0).

And you can just dump a specific file(-file /tmp/new-file.docx -ro)
```
$ go build -o docxlib ./getstructure/ && ./docxlib -logtostderr=true -v=0 -file /tmp/new-file.docx
$ go build -o docxlib ./main
$ ./docxlib -logtostderr=true -v=0 -file /tmp/new-file.docx -ro
I0511 12:37:40.898493 18466 unpack.go:69] Relations: [...]
I0511 12:37:40.898787 18466 unpack.go:47] Doc: [...]
I0511 12:37:40.899330 18466 unpack.go:58] Paragraph [0xc000026d40 0xc000027d00 0xc000172340]
Expand Down
46 changes: 21 additions & 25 deletions docxlib.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package docxlib

import (
"archive/zip"
"errors"
"io"

"github.com/gonfva/docxlib/xml"
)

// DocxLib is the structure that allow to access the internal represntation
// in memory of the doc (either read or about to be written)
type DocxLib struct {
Document Document
DocRelation Relationships

rId int
lib *xml.LibXML
}

// New generates a new empty docx file that we can manipulate and
// later on, save
func New() *DocxLib {
return emptyFile()
lib := xml.New()
return &DocxLib{lib: lib}
}

// Parse generates a new docx file in memory from a reader
Expand Down Expand Up @@ -49,30 +47,28 @@ func New() *DocxLib {
// docxlib.Parse(file, handler.Size)
// }
func Parse(reader io.ReaderAt, size int64) (doc *DocxLib, err error) {
zipReader, err := zip.NewReader(reader, size)
if err != nil {
return nil, err
}
doc, err = unpack(zipReader)
libxml, err := xml.Parse(reader, size)
doc = &DocxLib{lib: libxml}
return
}

// Write allows to save a docx to a writer
func (f *DocxLib) Write(writer io.Writer) (err error) {
zipWriter := zip.NewWriter(writer)
defer zipWriter.Close()

return f.pack(zipWriter)
return f.lib.Write(writer)
}

// References gets the url for a reference
func (f *DocxLib) References(id string) (href string, err error) {
for _, a := range f.DocRelation.Relationships {
if a.ID == id {
href = a.Target
return
}
func (f *DocxLib) Paragraphs() []*Paragraph {
pars := make([]*Paragraph, 0)
for _, p := range f.lib.Document.Body.Paragraphs {
pars = append(pars, &Paragraph{pxml: p, lib: f.lib})
}
err = errors.New("id not found")
return

return pars
}

// AddParagraph adds a new paragraph
func (f *DocxLib) AddParagraph() *Paragraph {
p := f.lib.AddParagraph()
para := &Paragraph{pxml: p, lib: f.lib}
return para
}
54 changes: 0 additions & 54 deletions getstructure/main.go

This file was deleted.

10 changes: 10 additions & 0 deletions inline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package docxlib

import "github.com/gonfva/docxlib/xml"

type Inline struct {
childXML *xml.ParagraphChild
Kind string
Link
Text
}
9 changes: 9 additions & 0 deletions link.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package docxlib

import "github.com/gonfva/docxlib/xml"

type Link struct {
Target string
Text string
pxml *xml.Hyperlink
}
57 changes: 28 additions & 29 deletions main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,39 @@ import (
)

var fileLocation *string
var readOnly *bool

func init() {
fileLocation = flag.String("file", "/tmp/new-file.docx", "file location")
readOnly = flag.Bool("ro", false, "Don't attempt to generate a new file, just read one")
flag.Parse()
}
func main() {
fmt.Printf("Preparing new document to write at %s\n", *fileLocation)
if !*readOnly {
fmt.Printf("Preparing new document to write at %s\n", *fileLocation)

w := docxlib.New()
// add new paragraph
para1 := w.AddParagraph()
// add text
para1.AddText("test")
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")
para1.AddText("test font size").SetSize(22)
para1.AddText("test color").SetColor("808080")
para2 := w.AddParagraph()
para2.AddText("test font size and color").SetSize(22).SetColor("ff0000")

nextPara := w.AddParagraph()
nextPara.AddLink("google", `http://google.com`)
nextPara := w.AddParagraph()
nextPara.AddLink("google", `http://google.com`)

f, err := os.Create(*fileLocation)
if err != nil {
panic(err)
f, err := os.Create(*fileLocation)
if err != nil {
panic(err)
}
defer f.Close()
w.Write(f)
fmt.Println("Document writen. \nNow trying to read it")
}
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(*fileLocation)
if err != nil {
Expand All @@ -54,18 +58,13 @@ func main() {
}
for _, para := range doc.Paragraphs() {
for _, child := range para.Children() {
if child.Run != nil {
fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Run.Text.Text)
if child.Kind == "Text" {
fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Text.Content)
}
if child.Link != nil {
id := child.Link.ID
text := child.Link.Run.InstrText
link, err := doc.References(id)
if err != nil {
fmt.Printf("\tWe found a link with id %s and text %s without target\n", id, text)
} else {
fmt.Printf("\tWe've found a new hyperlink with ref %s and the text %s\n", link, text)
}
if child.Kind == "Link" {
link := child.Link.Target
text := child.Link.Text
fmt.Printf("\tWe've found a new hyperlink with ref %s and the text %s\n", link, text)

}
}
Expand Down
44 changes: 44 additions & 0 deletions para.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package docxlib

import "github.com/gonfva/docxlib/xml"

type Paragraph struct {
lib *xml.LibXML
pxml *xml.Paragraph
}

func (p *Paragraph) Children() (ret []*Inline) {
var inline *Inline
ret = make([]*Inline, 0)
for _, child := range p.pxml.Data {
if child.Link != nil {
kind := "Link"
id := child.Link.ID
target, _ := p.lib.References(id)
link := Link{Target: target, Text: child.Link.Run.InstrText}
inline = &Inline{Kind: kind, childXML: &child, Link: link}
}
if child.Run != nil {
kind := "Text"
text := Text{Content: child.Run.Text.Text, pxml: child.Run}
inline = &Inline{Kind: kind, childXML: &child, Text: text}
}
ret = append(ret, inline)
}
return
}

// AddLink adds an hyperlink to paragraph
func (p *Paragraph) AddLink(text string, link string) *Link {
l := p.pxml.AddLink(text, link)
hyperlink := Link{Target: link, Text: link, pxml: l}

return &hyperlink
}

// AddText adds text to paragraph
func (p *Paragraph) AddText(text string) *Text {
t := p.pxml.AddText(text)
txt := Text{Content: text, pxml: t}
return &txt
}
21 changes: 21 additions & 0 deletions text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package docxlib

import "github.com/gonfva/docxlib/xml"

type Text struct {
Content string

pxml *xml.Run
}

// Color allows to set run color
func (r *Text) SetColor(color string) *Text {
r.pxml.Color(color)
return r
}

// Size allows to set run size
func (r *Text) SetSize(size int) *Text {
r.pxml.Size(size)
return r
}
4 changes: 2 additions & 2 deletions apilink.go → xml/apilink.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package docxlib
package xml

import "strconv"

// when adding an hyperlink we need to store a reference in the relationship field
func (f *DocxLib) addLinkRelation(link string) string {
func (f *LibXML) addLinkRelation(link string) string {
rel := &Relationship{
ID: "rId" + strconv.Itoa(f.rId),
Type: REL_HYPERLINK,
Expand Down
6 changes: 3 additions & 3 deletions apipara.go → xml/apipara.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package docxlib
package xml

// AddParagraph adds a new paragraph
func (f *DocxLib) AddParagraph() *Paragraph {
func (f *LibXML) AddParagraph() *Paragraph {
p := &Paragraph{
Data: make([]ParagraphChild, 0),
file: f,
Expand All @@ -11,7 +11,7 @@ func (f *DocxLib) AddParagraph() *Paragraph {
return p
}

func (f *DocxLib) Paragraphs() []*Paragraph {
func (f *LibXML) Paragraphs() []*Paragraph {
return f.Document.Body.Paragraphs
}

Expand Down
2 changes: 1 addition & 1 deletion apirun.go → xml/apirun.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package docxlib
package xml

// Color allows to set run color
func (r *Run) Color(color string) *Run {
Expand Down
6 changes: 3 additions & 3 deletions empty.go → xml/empty.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package docxlib
package xml

import "encoding/xml"

Expand All @@ -23,8 +23,8 @@ func emptyRelationships() []*Relationship {
return defaultRel
}

func emptyFile() *DocxLib {
docx := &DocxLib{
func emptyFile() *LibXML {
docx := &LibXML{
Document: Document{
XMLName: xml.Name{
Space: "w",
Expand Down
2 changes: 1 addition & 1 deletion empty_constants.go → xml/empty_constants.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package docxlib
package xml

const (
TEMP_REL = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
Expand Down
Loading