Skip to content

Commit

Permalink
Merge pull request #43 from gomutex/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
gomutex authored Jul 2, 2024
2 parents 33f9713 + 6b6ce69 commit 258a3f9
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 75 deletions.
5 changes: 2 additions & 3 deletions docx/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ func (body *Body) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err erro
switch elem.Name.Local {
case "p":
para := newParagraph(body.root)
if err := d.DecodeElement(&para.ct, &elem); err != nil {
if err := para.unmarshalXML(d, elem); err != nil {
return err
}
body.Children = append(body.Children, DocumentChild{Para: para})
case "tbl":
tbl := NewTable(body.root)
if err := d.DecodeElement(&tbl.ct, &elem); err != nil {
if err := tbl.unmarshalXML(d, elem); err != nil {
return err
}
body.Children = append(body.Children, DocumentChild{Table: tbl})
Expand All @@ -101,5 +101,4 @@ func (body *Body) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err erro
return nil
}
}

}
24 changes: 24 additions & 0 deletions docx/docx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package docx

import (
"log"
"testing"

"github.com/gomutex/godocx/wml/ctypes"
)

func setupRootDoc(t *testing.T) *RootDoc {
log.Println("setting root doc")

return &RootDoc{
Path: "/tmp/test",
RootRels: Relationships{},
ContentType: ContentTypes{},
Document: &Document{
Body: &Body{},
},
DocStyles: &ctypes.Styles{},
rID: 1,
ImageCount: 1,
}
}
90 changes: 59 additions & 31 deletions docx/paragraph.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package docx

import (
"encoding/xml"
"errors"
"fmt"

Expand All @@ -18,11 +19,43 @@ type Paragraph struct {
ct ctypes.Paragraph // ct holds the underlying Paragraph Complex Type.
}

// newParagraph creates and initializes a new Paragraph instance.
func newParagraph(root *RootDoc) *Paragraph {
return &Paragraph{
func (p *Paragraph) unmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return p.ct.UnmarshalXML(d, start)
}

type paraOpts struct {
text string
}

func newParaOpts() *paraOpts {
return &paraOpts{}
}

// paraOption defines a type for functions that can configure a Paragraph.
type paraOption func(*Paragraph)

// newParagraph creates and initializes a new Paragraph instance with given options.
func newParagraph(root *RootDoc, opts ...paraOption) *Paragraph {
p := &Paragraph{
root: root,
}
for _, opt := range opts {
opt(p)
}
return p
}

// paraWithText is an option for adding text to a Paragraph.
func paraWithText(text string) paraOption {
return func(p *Paragraph) {
p.AddText(text)
}
}

func (p *Paragraph) ensureProp() {
if p.ct.Property == nil {
p.ct.Property = ctypes.DefaultParaProperty()
}
}

// GetCT returns a pointer to the underlying Paragraph Complex Type.
Expand Down Expand Up @@ -59,9 +92,7 @@ func (rd *RootDoc) AddParagraph(text string) *Paragraph {
// p1 := document.AddParagraph("Example para")
// paragraph.Style("List Number")
func (p *Paragraph) Style(value string) {
if p.ct.Property == nil {
p.ct.Property = ctypes.DefaultParaProperty()
}
p.ensureProp()
p.ct.Property.Style = ctypes.NewParagraphStyle(value)
}

Expand All @@ -76,9 +107,7 @@ func (p *Paragraph) Style(value string) {
// p1 := document.AddParagraph("Example justified para")
// p1.Justification(stypes.JustificationCenter) // Center justification
func (p *Paragraph) Justification(value stypes.Justification) {
if p.ct.Property == nil {
p.ct.Property = ctypes.DefaultParaProperty()
}
p.ensureProp()

p.ct.Property.Justification = ctypes.NewGenSingleStrVal(value)
}
Expand All @@ -99,15 +128,14 @@ func (p *Paragraph) Justification(value stypes.Justification) {
//
// In this example, the paragraph p1 is assigned the numbering properties
// defined by numbering definition ID 1 and level 0.
func (p Paragraph) Numbering(id int, level int) {
func (p *Paragraph) Numbering(id int, level int) {

if p.ct.Property == nil {
p.ct.Property = ctypes.DefaultParaProperty()
}
p.ensureProp()

if p.ct.Property.NumProp == nil {
p.ct.Property.NumProp = &ctypes.NumProp{}
}

p.ct.Property.NumProp.NumID = ctypes.NewDecimalNum(id)
p.ct.Property.NumProp.ILvl = ctypes.NewDecimalNum(level)
}
Expand Down Expand Up @@ -164,6 +192,24 @@ func (p *Paragraph) AddRun() *Run {
return newRun(p.root, run)
}

// GetStyle retrieves the style information applied to the Paragraph.
//
// Returns:
// - *ctypes.Style: The style information of the Paragraph.
// - error: An error if the style information is not found.
func (p *Paragraph) GetStyle() (*ctypes.Style, error) {
if p.ct.Property == nil || p.ct.Property.Style == nil {
return nil, errors.New("No property for the style")
}

style := p.root.GetStyleByID(p.ct.Property.Style.Val, stypes.StyleTypeParagraph)
if style == nil {
return nil, errors.New("No style found for the paragraph")
}

return style, nil
}

// func (p *Paragraph) AddLink(text string, link string) *Hyperlink {
// rId := p.rootRef.addLinkRelation(link)

Expand Down Expand Up @@ -234,21 +280,3 @@ func (p *Paragraph) addDrawing(rID string, imgCount uint, width units.Inch, heig

return &inline
}

// GetStyle retrieves the style information applied to the Paragraph.
//
// Returns:
// - *ctypes.Style: The style information of the Paragraph.
// - error: An error if the style information is not found.
func (p *Paragraph) GetStyle() (*ctypes.Style, error) {
if p.ct.Property == nil || p.ct.Property.Style == nil {
return nil, errors.New("No property for the style")
}

style := p.root.GetStyleByID(p.ct.Property.Style.Val, stypes.StyleTypeParagraph)
if style == nil {
return nil, errors.New("No style found for the paragraph")
}

return style, nil
}
141 changes: 141 additions & 0 deletions docx/paragraph_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package docx

import (
"testing"

"github.com/gomutex/godocx/wml/ctypes"
"github.com/gomutex/godocx/wml/stypes"
"github.com/stretchr/testify/assert"
)

func assertParaText(t *testing.T, para *Paragraph, expected string) {
t.Helper()

assert.NotNil(t, para)
ct := para.GetCT()
assert.NotNil(t, ct)
assert.GreaterOrEqual(t, len(ct.Children), 1)
run := ct.Children[0].Run
assert.NotNil(t, run)
assert.GreaterOrEqual(t, len(run.Children), 1)
text := run.Children[0].Text
assert.NotNil(t, text)

assert.Equal(t, text.Text, expected)

}

func TestAddParagraph(t *testing.T) {
rd := setupRootDoc(t)
para := rd.AddParagraph("Test paragraph")
assertParaText(t, para, "Test paragraph")
}

func TestEmptyParagraph(t *testing.T) {
rd := setupRootDoc(t)
para := rd.AddEmptyParagraph()
para.AddText("Test paragraph")
assertParaText(t, para, "Test paragraph")
}

func TestParagraph_Style(t *testing.T) {
f := func(styleValue string, expectedStyleValue string) {
t.Helper()

p := &Paragraph{}

p.Style(styleValue)

assert.NotNil(t, p.ct.Property)
assert.NotNil(t, p.ct.Property.Style)
assert.Equal(t, p.ct.Property.Style.Val, expectedStyleValue)
}

f("Heading1", "Heading1")
f("Normal", "Normal")
}
func TestParagraph_Justification(t *testing.T) {
f := func(justificationValue, expectedJustificationValue stypes.Justification) {
t.Helper()

p := &Paragraph{}

p.Justification(justificationValue)

assert.NotNil(t, p.ct.Property, "Expected ct.Property to be non-nil")
assert.NotNil(t, p.ct.Property.Justification, "Expected ct.Property.Justification to be non-nil")
assert.Equal(t, p.ct.Property.Justification.Val, expectedJustificationValue, "Paragraph.Justification() value mismatch")
}

f(stypes.JustificationCenter, stypes.JustificationCenter)
f(stypes.JustificationLeft, stypes.JustificationLeft)
f(stypes.JustificationRight, stypes.JustificationRight)
f(stypes.JustificationBoth, stypes.JustificationBoth)
}

func TestParagraph_Numbering(t *testing.T) {
f := func(id int, level int, expectedNumID int, expectedILvl int) {
t.Helper()

p := &Paragraph{}

p.Numbering(id, level)

assert.NotNil(t, p.ct.Property, "Expected ct.Property to be non-nil")
assert.NotNil(t, p.ct.Property.NumProp, "Expected ct.Property.NumProp to be non-nil")
assert.Equal(t, expectedNumID, p.ct.Property.NumProp.NumID.Val, "Paragraph.Numbering() NumID value mismatch")
assert.Equal(t, expectedILvl, p.ct.Property.NumProp.ILvl.Val, "Paragraph.Numbering() ILvl value mismatch")
}

f(1, 0, 1, 0)
f(2, 1, 2, 1)
f(3, 2, 3, 2)
f(4, 3, 4, 3)
}

func TestParagraph_AddText(t *testing.T) {
f := func(text string, expectedText string) {
t.Helper()

p := &Paragraph{
ct: ctypes.Paragraph{
Children: []ctypes.ParagraphChild{},
},
}

run := p.AddText(text)

assert.NotNil(t, run, "Expected AddText() to return a non-nil Run")
assert.Equal(t, len(p.ct.Children), 1, "Expected one Run to be added to Paragraph")

assert.NotNil(t, p.ct.Children[0].Run, "Expected ct.Children[0].Run to be non-nil")
assert.GreaterOrEqual(t, len(p.ct.Children[0].Run.Children), 1, "Expected at least one Text element in Run")
assert.NotNil(t, p.ct.Children[0].Run.Children[0].Text, "Expected Text element in Run to be non-nil")
assert.Equal(t, p.ct.Children[0].Run.Children[0].Text.Text, expectedText, "Paragraph.AddText() Text value mismatch")
}

f("Hello, World!", "Hello, World!")
f("Another test", "Another test")
f("A third text", "A third text")
f("Sample text", "Sample text")
}

func TestParagraph_AddRun(t *testing.T) {
p := &Paragraph{
ct: ctypes.Paragraph{
Children: []ctypes.ParagraphChild{},
},
}

run := p.AddRun()

assert.NotNil(t, run, "Expected AddRun() to return a non-nil Run")

assert.Equal(t, 1, len(p.ct.Children), "Expected one Run to be added to Paragraph")
assert.NotNil(t, p.ct.Children[0].Run, "Expected ct.Children[0].Run to be non-nil")
assert.Equal(t, run.ct, p.ct.Children[0].Run, "Expected the Run returned by AddRun() to match the Run added to Paragraph")

assert.Empty(t, run.ct.Children, "Expected new Run to have no initial Children")

assert.Equal(t, 0, len(p.ct.Children[0].Run.Children), "Expected the new Run to have no initial Children")
}
Loading

0 comments on commit 258a3f9

Please sign in to comment.