examples: add Word template example

Add an example that shows how to use a Word document
as a template and pull its styles for use in a new document.
This commit is contained in:
Todd 2017-08-30 16:49:57 -05:00
parent 9237920cf3
commit 8e630657bf
9 changed files with 163 additions and 1 deletions

View File

@ -0,0 +1,56 @@
package main
import (
"fmt"
"log"
"baliance.com/gooxml/document"
)
var lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin lobortis, lectus dictum feugiat tempus, sem neque finibus enim, sed eleifend sem nunc ac diam. Vestibulum tempus sagittis elementum`
func main() {
// When Word saves a document, it removes all unused styles. This means to
// copy the styles from an existing document, you must first create a
// document that contains text in each style of interest. As an example,
// see the template.docx in this directory. It contains a paragraph set in
// each style that Word supports by default.
doc, err := document.OpenTemplate("template.docx")
if err != nil {
log.Fatalf("error opening Windows Word 2016 document: %s", err)
}
// We can now print out all styles in the document, verifying that they
// exist.
for _, s := range doc.Styles.ParagraphStyles() {
fmt.Println("style", s.Name(), "has ID of", s.StyleID())
}
// And create documents setting their style to the style ID (not style name).
para := doc.AddParagraph()
para.SetStyle("Title")
para.AddRun().AddText("My Document Title")
para = doc.AddParagraph()
para.SetStyle("Subtitle")
para.AddRun().AddText("Document Subtitle")
para = doc.AddParagraph()
para.SetStyle("Heading1")
para.AddRun().AddText("Major Section")
para = doc.AddParagraph()
para = doc.AddParagraph()
for i := 0; i < 4; i++ {
para.AddRun().AddText(lorem)
}
para = doc.AddParagraph()
para.SetStyle("Heading2")
para.AddRun().AddText("Minor Section")
para = doc.AddParagraph()
for i := 0; i < 4; i++ {
para.AddRun().AddText(lorem)
}
doc.SaveToFile("use-template.docx")
}

Binary file not shown.

Binary file not shown.

View File

@ -272,6 +272,20 @@ func Open(filename string) (*Document, error) {
return Read(f, fi.Size())
}
// OpenTemplate opens a document, removing all content so it can be used as a
// template. Since Word removes unused styles from a document upon save, to
// create a template in Word add a paragraph with every style of interest. When
// opened with OpenTemplate the documents styles will be available but the
// content will be gone.
func OpenTemplate(filename string) (*Document, error) {
d, err := Open(filename)
if err != nil {
return nil, err
}
d.x.Body = wml.NewCT_Body()
return d, nil
}
// Read reads a document from an io.Reader.
func Read(r io.ReaderAt, size int64) (*Document, error) {
doc := New()

View File

@ -21,6 +21,11 @@ func (s Style) X() *wml.CT_Style {
return s.x
}
// Type returns the type of the style.
func (s Style) Type() wml.ST_StyleType {
return s.x.TypeAttr
}
// StyleID returns the style ID.
func (s Style) StyleID() string {
if s.x.StyleIdAttr == nil {

View File

@ -206,3 +206,24 @@ func (s Styles) initializeDocDefaults() {
s.x.DocDefaults.PPrDefault.PPr.Spacing.LineAttr.Int32 = gooxml.Int32(259)
s.x.DocDefaults.PPrDefault.PPr.Spacing.LineRuleAttr = wml.ST_LineSpacingRuleAuto
}
// Styles returns all styles.
func (s Styles) Styles() []Style {
ret := []Style{}
for _, s := range s.x.Style {
ret = append(ret, Style{s})
}
return ret
}
// Styles returns only paragraph styles.
func (s Styles) ParagraphStyles() []Style {
ret := []Style{}
for _, s := range s.x.Style {
if s.TypeAttr != wml.ST_StyleTypeParagraph {
continue
}
ret = append(ret, Style{s})
}
return ret
}

64
document/styles_test.go Normal file
View File

@ -0,0 +1,64 @@
package document_test
import (
"bytes"
"encoding/xml"
"fmt"
"os"
"testing"
"baliance.com/gooxml/document"
"baliance.com/gooxml/testhelper"
"baliance.com/gooxml/zippkg"
)
func TestStylesUnmarshal(t *testing.T) {
f, err := os.Open("testdata/styles.xml")
if err != nil {
t.Fatalf("error reading content types file")
}
defer f.Close()
dec := xml.NewDecoder(f)
r := document.NewStyles()
if err := dec.Decode(r.X()); err != nil {
t.Errorf("error decoding content types: %s", err)
}
got := &bytes.Buffer{}
fmt.Fprintf(got, zippkg.XMLHeader)
enc := xml.NewEncoder(zippkg.SelfClosingWriter{W: got})
if err := enc.Encode(r.X()); err != nil {
t.Errorf("error encoding content types: %s", err)
}
testhelper.CompareGoldenXML(t, "styles.xml", got.Bytes())
}
func TestStylesList(t *testing.T) {
f, err := os.Open("testdata/styles.xml")
if err != nil {
t.Fatalf("error reading content types file")
}
defer f.Close()
dec := xml.NewDecoder(f)
r := document.NewStyles()
if err := dec.Decode(r.X()); err != nil {
t.Errorf("error decoding content types: %s", err)
}
expStyleCnt := 26
if got := len(r.Styles()); got != expStyleCnt {
t.Errorf("expected %d total styles, got %d", expStyleCnt, got)
}
expParaStyleCnt := 9
if got := len(r.ParagraphStyles()); got != expParaStyleCnt {
t.Errorf("expected %d total paragraph styles, got %d", expStyleCnt, got)
}
for _, ps := range r.ParagraphStyles() {
switch ps.StyleID() {
case "Normal", "Heading1", "Heading2", "Heading3",
"Title", "Subtitle", "Quote", "IntenseQuote", "ListParagraph":
default:
t.Errorf("unexpected paragraph style: %s", ps.StyleID())
}
}
}

2
document/testdata/styles.xml vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,7 @@ import (
"baliance.com/gooxml/zippkg"
)
func TestWtyleSheetUnmarshal(t *testing.T) {
func TestStyleSheetUnmarshal(t *testing.T) {
f, err := os.Open("testdata/styles.xml")
if err != nil {
t.Fatalf("error reading content types file")