mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-27 13:48:54 +08:00
document: add more documentation and examples
This commit is contained in:
parent
3820294523
commit
caebe001a8
@ -16,7 +16,7 @@ func main() {
|
|||||||
|
|
||||||
// FindAllFields is a helper function that traverses the document
|
// FindAllFields is a helper function that traverses the document
|
||||||
// identifying fields
|
// identifying fields
|
||||||
fields := document.FindAllFields(doc)
|
fields := doc.FormFields()
|
||||||
fmt.Println("found", len(fields), "fields")
|
fmt.Println("found", len(fields), "fields")
|
||||||
|
|
||||||
for _, fld := range fields {
|
for _, fld := range fields {
|
||||||
|
3
doc.go
3
doc.go
@ -22,5 +22,8 @@ spreadsheet.Workbook and presentation.Presentation), the other wrapper types are
|
|||||||
value types with non-pointer methods. They exist solely to modify and return
|
value types with non-pointer methods. They exist solely to modify and return
|
||||||
data from one or more XML types.
|
data from one or more XML types.
|
||||||
|
|
||||||
|
The packages of interest are baliance.com/gooxml/document,
|
||||||
|
baliance/gooxml/spreadsheet and baliance.com/gooxml/presentation.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package gooxml
|
package gooxml
|
||||||
|
@ -27,7 +27,9 @@ import (
|
|||||||
"baliance.com/gooxml/zippkg"
|
"baliance.com/gooxml/zippkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Document is a text document that can be written out in the OOXML .docx format.
|
// Document is a text document that can be written out in the OOXML .docx
|
||||||
|
// format. It can be opened from a file on disk and modified, or created from
|
||||||
|
// scratch.
|
||||||
type Document struct {
|
type Document struct {
|
||||||
common.DocBase
|
common.DocBase
|
||||||
x *wml.Document
|
x *wml.Document
|
||||||
@ -89,7 +91,8 @@ func (d *Document) X() *wml.Document {
|
|||||||
return d.x
|
return d.x
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHeader creates a header, but doesn't add it to the document for display.
|
// AddHeader creates a header associated with the document, but doesn't add it
|
||||||
|
// to the document for display.
|
||||||
func (d *Document) AddHeader() Header {
|
func (d *Document) AddHeader() Header {
|
||||||
hdr := wml.NewHdr()
|
hdr := wml.NewHdr()
|
||||||
d.headers = append(d.headers, hdr)
|
d.headers = append(d.headers, hdr)
|
||||||
@ -100,7 +103,8 @@ func (d *Document) AddHeader() Header {
|
|||||||
return Header{d, hdr}
|
return Header{d, hdr}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFooter creates a Footer, but doesn't add it to the document for display.
|
// AddFooter creates a Footer associated with the document, but doesn't add it
|
||||||
|
// to the document for display.
|
||||||
func (d *Document) AddFooter() Footer {
|
func (d *Document) AddFooter() Footer {
|
||||||
ftr := wml.NewFtr()
|
ftr := wml.NewFtr()
|
||||||
d.footers = append(d.footers, ftr)
|
d.footers = append(d.footers, ftr)
|
||||||
@ -111,8 +115,8 @@ func (d *Document) AddFooter() Footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BodySection returns the default body section used for all preceeding
|
// BodySection returns the default body section used for all preceeding
|
||||||
// paragraphs until the previous Section. If there is no previous section, it
|
// paragraphs until the previous Section. If there is no previous sections, the
|
||||||
// applies to the entire document.
|
// body section applies to the entire document.
|
||||||
func (d *Document) BodySection() Section {
|
func (d *Document) BodySection() Section {
|
||||||
if d.x.Body.SectPr == nil {
|
if d.x.Body.SectPr == nil {
|
||||||
d.x.Body.SectPr = wml.NewCT_SectPr()
|
d.x.Body.SectPr = wml.NewCT_SectPr()
|
||||||
@ -285,7 +289,7 @@ func Open(filename string) (*Document, error) {
|
|||||||
// OpenTemplate opens a document, removing all content so it can be used as a
|
// 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
|
// 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
|
// 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
|
// opened with OpenTemplate the document's styles will be available but the
|
||||||
// content will be gone.
|
// content will be gone.
|
||||||
func OpenTemplate(filename string) (*Document, error) {
|
func OpenTemplate(filename string) (*Document, error) {
|
||||||
d, err := Open(filename)
|
d, err := Open(filename)
|
||||||
@ -449,7 +453,9 @@ func Read(r io.ReaderAt, size int64) (*Document, error) {
|
|||||||
return doc, nil
|
return doc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate attempts to validate the structure of a document.
|
// Validate validates the structure and in cases where it't possible, the ranges
|
||||||
|
// of elements within a document. A validation error dones't mean that the
|
||||||
|
// document won't work in MS Word or LibreOffice, but it's worth checking into.
|
||||||
func (d *Document) Validate() error {
|
func (d *Document) Validate() error {
|
||||||
if d == nil || d.x == nil {
|
if d == nil || d.x == nil {
|
||||||
return errors.New("document not initialized correctly, nil base")
|
return errors.New("document not initialized correctly, nil base")
|
||||||
@ -518,3 +524,54 @@ func (d *Document) Images() []ImageRef {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormFields extracts all of the fields from a document. They can then be
|
||||||
|
// manipulated via the methods on the field and the document saved.
|
||||||
|
func (d *Document) FormFields() []FormField {
|
||||||
|
ret := []FormField{}
|
||||||
|
for _, p := range d.Paragraphs() {
|
||||||
|
runs := p.Runs()
|
||||||
|
for i, r := range runs {
|
||||||
|
for _, ic := range r.x.EG_RunInnerContent {
|
||||||
|
// skip non form fields
|
||||||
|
if ic.FldChar == nil || ic.FldChar.FfData == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// found a begin form field
|
||||||
|
if ic.FldChar.FldCharTypeAttr == wml.ST_FldCharTypeBegin {
|
||||||
|
// ensure it has a name
|
||||||
|
if len(ic.FldChar.FfData.Name) == 0 || ic.FldChar.FfData.Name[0].ValAttr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
field := FormField{x: ic.FldChar.FfData}
|
||||||
|
// for text input boxes, we need a pointer to where to set
|
||||||
|
// the text as well
|
||||||
|
if ic.FldChar.FfData.TextInput != nil {
|
||||||
|
|
||||||
|
// ensure we always have at lest two IC's
|
||||||
|
for j := i + 1; j < len(runs)-1; j++ {
|
||||||
|
if len(runs[j].x.EG_RunInnerContent) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ic := runs[j].x.EG_RunInnerContent[0]
|
||||||
|
// look for the 'separate' field
|
||||||
|
if ic.FldChar != nil && ic.FldChar.FldCharTypeAttr == wml.ST_FldCharTypeSeparate {
|
||||||
|
if len(runs[j+1].x.EG_RunInnerContent) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// the value should be the text in the next inner content that is not a field char
|
||||||
|
if runs[j+1].x.EG_RunInnerContent[0].FldChar == nil {
|
||||||
|
field.textIC = runs[j+1].x.EG_RunInnerContent[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = append(ret, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
66
document/document_examples_test.go
Normal file
66
document/document_examples_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package document_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"baliance.com/gooxml/document"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNew() {
|
||||||
|
doc := document.New()
|
||||||
|
doc.AddParagraph().AddRun().AddText("Hello World!")
|
||||||
|
doc.SaveToFile("document.docx")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOpen() {
|
||||||
|
doc, err := document.Open("existing.docx")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error opening document: %s", err)
|
||||||
|
}
|
||||||
|
for _, para := range doc.Paragraphs() {
|
||||||
|
for _, run := range para.Runs() {
|
||||||
|
fmt.Print(run.Text())
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOpenTemplate() {
|
||||||
|
doc, err := document.OpenTemplate("existing.docx")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error opening document template: %s", err)
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
doc.SaveToFile("ouput.docx")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDocument_FormFields() {
|
||||||
|
doc, err := document.Open("invitation.docx")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error opening document form: %s", err)
|
||||||
|
}
|
||||||
|
for _, field := range doc.FormFields() {
|
||||||
|
switch field.Name() {
|
||||||
|
case "attendingEvent":
|
||||||
|
if field.Type() == document.FormFieldTypeCheckBox {
|
||||||
|
field.SetChecked(true)
|
||||||
|
}
|
||||||
|
case "name":
|
||||||
|
if field.Type() == document.FormFieldTypeText {
|
||||||
|
field.SetValue("John Smith")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doc.SaveToFile("invitation-respoonse.docx")
|
||||||
|
}
|
@ -23,8 +23,8 @@ const (
|
|||||||
FormFieldTypeDropDown
|
FormFieldTypeDropDown
|
||||||
)
|
)
|
||||||
|
|
||||||
// FormField is a form within a document. It references the document, so
|
// FormField is a form within a document. It references the document, so changes
|
||||||
// changes to the form field wil be reflected in the document if it is saved.
|
// to the form field wil be reflected in the document if it is saved.
|
||||||
type FormField struct {
|
type FormField struct {
|
||||||
x *wml.CT_FFData
|
x *wml.CT_FFData
|
||||||
textIC *wml.EG_RunInnerContent
|
textIC *wml.EG_RunInnerContent
|
||||||
@ -121,54 +121,3 @@ func (f FormField) SetChecked(b bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindAllFields extracts all of the fields from a document. They can then be
|
|
||||||
// manipulated via the methods on the field and the document can be saved.
|
|
||||||
func FindAllFields(d *Document) []FormField {
|
|
||||||
ret := []FormField{}
|
|
||||||
for _, p := range d.Paragraphs() {
|
|
||||||
runs := p.Runs()
|
|
||||||
for i, r := range runs {
|
|
||||||
for _, ic := range r.x.EG_RunInnerContent {
|
|
||||||
// skip non form fields
|
|
||||||
if ic.FldChar == nil || ic.FldChar.FfData == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// found a begin form field
|
|
||||||
if ic.FldChar.FldCharTypeAttr == wml.ST_FldCharTypeBegin {
|
|
||||||
// ensure it has a name
|
|
||||||
if len(ic.FldChar.FfData.Name) == 0 || ic.FldChar.FfData.Name[0].ValAttr == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
field := FormField{x: ic.FldChar.FfData}
|
|
||||||
// for text input boxes, we need a pointer to where to set
|
|
||||||
// the text as well
|
|
||||||
if ic.FldChar.FfData.TextInput != nil {
|
|
||||||
|
|
||||||
// ensure we always have at lest two IC's
|
|
||||||
for j := i + 1; j < len(runs)-1; j++ {
|
|
||||||
if len(runs[j].x.EG_RunInnerContent) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ic := runs[j].x.EG_RunInnerContent[0]
|
|
||||||
// look for the 'separate' field
|
|
||||||
if ic.FldChar != nil && ic.FldChar.FldCharTypeAttr == wml.ST_FldCharTypeSeparate {
|
|
||||||
if len(runs[j+1].x.EG_RunInnerContent) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// the value should be the text in the next inner content that is not a field char
|
|
||||||
if runs[j+1].x.EG_RunInnerContent[0].FldChar == nil {
|
|
||||||
field.textIC = runs[j+1].x.EG_RunInnerContent[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = append(ret, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
11
example_test.go
Normal file
11
example_test.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package gooxml_test
|
||||||
|
|
||||||
|
import "baliance.com/gooxml/document"
|
||||||
|
|
||||||
|
func Example_document() {
|
||||||
|
// see the baliance.com/gooxml/document documentation or _examples/document
|
||||||
|
// for more examples
|
||||||
|
doc := document.New()
|
||||||
|
doc.AddParagraph().AddRun().AddText("Hello World!")
|
||||||
|
doc.SaveToFile("document.docx")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user