diff --git a/apipara.go b/apipara.go index 12bee30..4151631 100644 --- a/apipara.go +++ b/apipara.go @@ -11,3 +11,19 @@ func (f *Docx) AddParagraph() *Paragraph { return p } + +// Justification allows to set para's horizonal alignment +// +// w:jc 属性的取值可以是以下之一: +// start:左对齐。 +// center:居中对齐。 +// end:右对齐。 +// both:两端对齐。 +// distribute:分散对齐。 +func (p *Paragraph) Justification(val string) *Paragraph { + if p.Properties == nil { + p.Properties = &ParagraphProperties{} + } + p.Properties.Justification = &Justification{Val: val} + return p +} diff --git a/apirun.go b/apirun.go index 182db8f..067fc4e 100644 --- a/apirun.go +++ b/apirun.go @@ -1,5 +1,7 @@ package docxlib +import "encoding/xml" + // Color allows to set run color func (r *Run) Color(color string) *Run { r.RunProperties.Color = &Color{ @@ -17,3 +19,35 @@ func (r *Run) Size(size string) *Run { return r } + +// Justification allows to set run's horizonal alignment +// +// w:jc 属性的取值可以是以下之一: +// start:左对齐。 +// center:居中对齐。 +// end:右对齐。 +// both:两端对齐。 +// distribute:分散对齐。 +func (r *Run) Justification(val string) *Run { + r.RunProperties.Justification = &Justification{ + Val: val, + } + + return r +} + +// AddTab add a tab in front of the run +func (r *Run) AddTab() *Run { + r.FrontTab = append(r.FrontTab, struct { + XMLName xml.Name "xml:\"w:tab,omitempty\"" + }{}) + return r +} + +// AppendTab add a tab after the run +func (r *Run) AppendTab() *Run { + r.RearTab = append(r.RearTab, struct { + XMLName xml.Name "xml:\"w:tab,omitempty\"" + }{}) + return r +} diff --git a/apitext.go b/apitext.go index c556aa6..3442f6d 100644 --- a/apitext.go +++ b/apitext.go @@ -1,7 +1,25 @@ package docxlib +import "encoding/xml" + +// AddTab adds tab to para +func (p *Paragraph) AddTab() *Run { + run := &Run{ + RunProperties: &RunProperties{}, + FrontTab: []struct { + XMLName xml.Name "xml:\"w:tab,omitempty\"" + }{{}}, + } + p.Children = append(p.Children, ParagraphChild{Run: run}) + return run +} + // AddText adds text to paragraph func (p *Paragraph) AddText(text string) *Run { + if text == "\t" { + return p.AddTab() + } + t := &Text{ Text: text, } diff --git a/cmd/main/main.go b/cmd/main/main.go index 32843b1..b1541ac 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -19,36 +19,41 @@ func main() { w := docxlib.NewA4() // add new paragraph - para1 := w.AddParagraph() + para1 := w.AddParagraph().Justification("distribute") // add text - para1.AddText("test") + para1.AddText("test").Justification("distribute") + para1.AddText("test font size").Size("44").Justification("distribute") + para1.AddText("test color").Color("808080").Justification("distribute") - para1.AddText("test font size").Size("44") - para1.AddText("test color").Color("808080") - para2 := w.AddParagraph() - para2.AddText("test font size and color").Size("44").Color("ff0000") + para2 := w.AddParagraph().Justification("end") + para2.AddText("test font size and color").Size("44").Color("ff0000").Justification("end") nextPara := w.AddParagraph() nextPara.AddLink("google", `http://google.com`) - para3 := w.AddParagraph() + para3 := w.AddParagraph().Justification("center") // add text - para3.AddText("直接粘贴 inline") + para3.AddText("一行2个 inline").Size("44").Justification("center") - para4 := w.AddParagraph() + para4 := w.AddParagraph().Justification("center") r, err := para4.AddInlineDrawingFrom("testdata/fumiama.JPG") if err != nil { panic(err) } - r.Drawing.Inline.Size(r.Drawing.Inline.Extent.CX/2, r.Drawing.Inline.Extent.CY/2) + r.Drawing.Inline.Size(r.Drawing.Inline.Extent.CX*4/5, r.Drawing.Inline.Extent.CY*4/5) + para4.AddTab().AddTab() r, err = para4.AddInlineDrawingFrom("testdata/fumiama2x.webp") if err != nil { panic(err) } - r.Drawing.Inline.Size(r.Drawing.Inline.Extent.CX/2, r.Drawing.Inline.Extent.CY/2) + r.Drawing.Inline.Size(r.Drawing.Inline.Extent.CX*4/5, r.Drawing.Inline.Extent.CY*4/5) - para5 := w.AddParagraph() - _, err = para5.AddInlineDrawingFrom("testdata/fumiamayoko.png") + para5 := w.AddParagraph().Justification("center") + // add text + para5.AddText("一行1个 横向 inline").Size("44").Justification("center") + + para6 := w.AddParagraph() + _, err = para6.AddInlineDrawingFrom("testdata/fumiamayoko.png") if err != nil { panic(err) } diff --git a/structdoc_test.go b/structdoc_test.go index f9fa4fa..7386045 100644 --- a/structdoc_test.go +++ b/structdoc_test.go @@ -40,9 +40,6 @@ func TestUnmarshalPlainStructure(t *testing.T) { if child.Link == nil && child.Properties == nil && child.Run == nil { t.Fatalf("There are Paragraph children with all fields nil") } - if child.Run != nil && child.Run.Text == nil && child.Run.InstrText == "" { - t.Fatalf("We have a run with no text") - } if child.Link != nil && child.Link.ID == "" { t.Fatalf("We have a link without ID") } @@ -572,10 +569,11 @@ func TestMarshalDrawingStructure(t *testing.T) { // add new paragraph para1 := w.AddParagraph() // add text - para1.AddText("直接粘贴 inline") + para1.AddText("直接粘贴 inline").AddTab().Justification("center") - para2 := w.AddParagraph() + para2 := w.AddParagraph().Justification("center") para2.AddInlineDrawingFrom("testdata/fumiama.JPG") + para2.AddTab().AddTab().AppendTab().AppendTab() para2.AddInlineDrawingFrom("testdata/fumiama2x.webp") para3 := w.AddParagraph() diff --git a/structpara.go b/structpara.go index 0ee3bef..6b5887a 100644 --- a/structpara.go +++ b/structpara.go @@ -6,6 +6,35 @@ import ( "strings" ) +type ParagraphProperties struct { + XMLName xml.Name `xml:"w:pPr,omitempty"` + Justification *Justification `xml:"w:jc,omitempty"` +} + +func (p *ParagraphProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "jc": + p.Justification = &Justification{Val: getAtt(tt.Attr, "val")} + default: + continue + } + } + + } + return nil + +} + type ParagraphChild struct { Link *Hyperlink `xml:"w:hyperlink,omitempty"` Run *Run `xml:"w:r,omitempty"` @@ -13,8 +42,9 @@ type ParagraphChild struct { } type Paragraph struct { - XMLName xml.Name `xml:"w:p,omitempty"` - Children []ParagraphChild // Children will generate an unnecessary tag ... and we skip it by a self-defined xml.Marshaler + XMLName xml.Name `xml:"w:p,omitempty"` + Properties *ParagraphProperties + Children []ParagraphChild // Children will generate an unnecessary tag ... and we skip it by a self-defined xml.Marshaler file *Docx } @@ -51,6 +81,12 @@ func (p *Paragraph) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if err != nil { return err } + if p.Properties != nil { + err = e.Encode(p.Properties) + if err != nil { + return err + } + } for _, c := range p.Children { switch { case c.Link != nil: @@ -99,13 +135,15 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { var value Run d.DecodeElement(&value, &start) elem.Run = &value - if value.InstrText == "" && value.Text == nil && value.Drawing == nil { - continue - } case "rPr": var value RunProperties d.DecodeElement(&value, &start) elem.Properties = &value + case "pPr": + var value ParagraphProperties + d.DecodeElement(&value, &start) + p.Properties = &value + continue default: continue } diff --git a/structrun.go b/structrun.go index f02a82c..6f50ec3 100644 --- a/structrun.go +++ b/structrun.go @@ -10,9 +10,15 @@ import ( type Run struct { XMLName xml.Name `xml:"w:r,omitempty"` RunProperties *RunProperties `xml:"w:rPr,omitempty"` - InstrText string `xml:"w:instrText,omitempty"` - Text *Text - Drawing *Drawing + FrontTab []struct { + XMLName xml.Name `xml:"w:tab,omitempty"` + } + InstrText string `xml:"w:instrText,omitempty"` + Text *Text + Drawing *Drawing + RearTab []struct { + XMLName xml.Name `xml:"w:tab,omitempty"` + } } func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { @@ -44,6 +50,16 @@ func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { var value Drawing d.DecodeElement(&value, &start) r.Drawing = &value + case "tab": + if r.InstrText == "" && r.Text == nil && r.Drawing == nil { + r.FrontTab = append(r.FrontTab, struct { + XMLName xml.Name "xml:\"w:tab,omitempty\"" + }{}) + } else { + r.RearTab = append(r.RearTab, struct { + XMLName xml.Name "xml:\"w:tab,omitempty\"" + }{}) + } default: continue } @@ -57,11 +73,12 @@ func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // RunProperties encapsulates visual properties of a run type RunProperties struct { - XMLName xml.Name `xml:"w:rPr,omitempty"` - Color *Color `xml:"w:color,omitempty"` - Size *Size `xml:"w:sz,omitempty"` - RunStyle *RunStyle `xml:"w:rStyle,omitempty"` - Style *Style `xml:"w:pStyle,omitempty"` + XMLName xml.Name `xml:"w:rPr,omitempty"` + Color *Color `xml:"w:color,omitempty"` + Size *Size `xml:"w:sz,omitempty"` + RunStyle *RunStyle `xml:"w:rStyle,omitempty"` + Style *Style `xml:"w:pStyle,omitempty"` + Justification *Justification `xml:"w:jc,omitempty"` } func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { @@ -93,6 +110,10 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err var value Style value.Val = getAtt(tt.Attr, "val") r.Style = &value + case "jc": + var value Justification + value.Val = getAtt(tt.Attr, "val") + r.Justification = &value default: continue } @@ -128,3 +149,16 @@ type Size struct { XMLName xml.Name `xml:"w:sz"` Val string `xml:"w:val,attr"` } + +// Justification contains the way of the horizonal alignment +// +// w:jc 属性的取值可以是以下之一: +// start:左对齐。 +// center:居中对齐。 +// end:右对齐。 +// both:两端对齐。 +// distribute:分散对齐。 +type Justification struct { + XMLName xml.Name `xml:"w:jc"` + Val string `xml:"w:val,attr"` +}