From 0e18f8a1630da6762b87f18692503ec4df9d2181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sun, 26 Feb 2023 15:01:17 +0800 Subject: [PATCH] feat: add shade effect --- apirun.go | 10 +++++- apitable.go | 10 ++++++ cmd/main/main.go | 18 ++++++++--- structdrawing.go | 15 ++------- structdrawing_test.go | 2 +- structeffects.go | 74 +++++++++++++++++++++++++++++++++++++++++++ structpara.go | 12 +++++-- structrun.go | 56 ++++++++------------------------ structtable.go | 30 +++++++++++------- structtable_test.go | 2 +- unpack.go | 5 +-- 11 files changed, 154 insertions(+), 80 deletions(-) create mode 100644 structeffects.go diff --git a/apirun.go b/apirun.go index 8a1014e..9843003 100644 --- a/apirun.go +++ b/apirun.go @@ -25,7 +25,6 @@ func (r *Run) Color(color string) *Run { r.RunProperties.Color = &Color{ Val: color, } - return r } @@ -34,7 +33,16 @@ func (r *Run) Size(size string) *Run { r.RunProperties.Size = &Size{ Val: size, } + return r +} +// Shade allows to set run shade +func (r *Run) Shade(val, color, fill string) *Run { + r.RunProperties.Shade = &Shade{ + Val: val, + Color: color, + Fill: fill, + } return r } diff --git a/apitable.go b/apitable.go index 3dfa362..ce3aec9 100644 --- a/apitable.go +++ b/apitable.go @@ -158,3 +158,13 @@ func (w *WTableRow) Justification(val string) *WTableRow { w.TableRowProperties.Justification.Val = val return w } + +// Shade allows to set cell's shade +func (c *WTableCell) Shade(val, color, fill string) *WTableCell { + c.TableCellProperties.Shade = &Shade{ + Val: val, + Color: color, + Fill: fill, + } + return c +} diff --git a/cmd/main/main.go b/cmd/main/main.go index 7756b6b..f165c18 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -51,9 +51,10 @@ func main() { r.Children[0].(*docx.Drawing).Anchor.PositionH.PosOffset = r.Children[0].(*docx.Drawing).Anchor.Extent.CX r.Children[0].(*docx.Drawing).Anchor.Graphic.GraphicData.Pic.BlipFill.Blip.AlphaModFix = &docx.AAlphaModFix{Amount: 50000} // add text - para1.AddText("test") - para1.AddText("test font size").Size("44") - para1.AddText("test color").Color("808080") + para1.AddText("test").AddTab() + para1.AddText("test font size").Size("44").AddTab() + para1.AddText("test color").Color("808080").AddTab() + para1.AddText("test shade").Shade("clear", "auto", "E7E6E6").AddTab() para2 := w.AddParagraph().Justification("end") para2.AddText("test font size and color").Size("44").Color("ff0000") @@ -88,10 +89,16 @@ func main() { panic(err) } - tbl1 := w.AddTable(3, 2) + w.AddParagraph() + + tbl1 := w.AddTable(9, 9) for x, r := range tbl1.TableRows { + red := (x + 1) * 28 for y, c := range r.TableCells { - c.AddParagraph().AddText(fmt.Sprintf("(%d, %d)", x, y)) + green := ((y + 1) / 3) * 85 + blue := (y%3 + 1) * 85 + v := fmt.Sprintf("%02X%02X%02X", red, green, blue) + c.Shade("clear", "auto", v).AddParagraph().AddText(v).Size("18") } } @@ -105,6 +112,7 @@ func main() { c.AddParagraph().Justification("center").AddText(fmt.Sprintf("(%d, %d)", x, y)) } } + tbl2.TableRows[0].TableCells[0].Shade("clear", "auto", "E7E6E6") f, err := os.Create(*fileLocation) if err != nil { diff --git a/structdrawing.go b/structdrawing.go index b23ce3f..87651b3 100644 --- a/structdrawing.go +++ b/structdrawing.go @@ -236,10 +236,7 @@ func (r *WPExtent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } // Consume the end element _, err = d.Token() - if err != nil { - return err - } - return nil + return err } // WPEffectExtent represents the effect extent of a drawing in a Word document. @@ -280,10 +277,7 @@ func (r *WPEffectExtent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er } // Consume the end element _, err = d.Token() - if err != nil { - return err - } - return nil + return err } // WPDocPr represents the document properties of a drawing in a Word document. @@ -312,10 +306,7 @@ func (r *WPDocPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } // Consume the end element _, err := d.Token() - if err != nil { - return err - } - return nil + return err } // WPCNvGraphicFramePr represents the non-visual properties of a graphic frame. diff --git a/structdrawing_test.go b/structdrawing_test.go index 3769efc..074a7cf 100644 --- a/structdrawing_test.go +++ b/structdrawing_test.go @@ -33,7 +33,7 @@ func TestDrawingStructure(t *testing.T) { // add new paragraph para1 := w.AddParagraph() // add text - para1.AddText("直接粘贴 inline").AddTab() + para1.AddText("直接粘贴 inline").Shade("clear", "auto", "E7E6E6").AddTab() r, err := para1.AddAnchorDrawingFrom("testdata/fumiama.JPG") if err != nil { t.Fatal(err) diff --git a/structeffects.go b/structeffects.go new file mode 100644 index 0000000..6734e83 --- /dev/null +++ b/structeffects.go @@ -0,0 +1,74 @@ +package docx + +import "encoding/xml" + +// RunStyle contains styling for a run +type RunStyle struct { + XMLName xml.Name `xml:"w:rStyle,omitempty"` + Val string `xml:"w:val,attr"` +} + +// Style contains styling for a paragraph +type Style struct { + XMLName xml.Name `xml:"w:pStyle,omitempty"` + Val string `xml:"w:val,attr"` +} + +// Color contains the sound of music. :D +// I'm kidding. It contains the color +type Color struct { + XMLName xml.Name `xml:"w:color,omitempty"` + Val string `xml:"w:val,attr"` +} + +// Size contains the font size +type Size struct { + XMLName xml.Name `xml:"w:sz,omitempty"` + 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,omitempty"` + Val string `xml:"w:val,attr"` +} + +// Shade is an element that represents a shading pattern applied to a document element. +type Shade struct { + XMLName xml.Name `xml:"w:shd,omitempty"` + Val string `xml:"w:val,attr,omitempty"` + Color string `xml:"w:color,attr,omitempty"` + Fill string `xml:"w:fill,attr,omitempty"` + ThemeFill string `xml:"w:themeFill,attr,omitempty"` + ThemeFillTint string `xml:"w:themeFillTint,attr,omitempty"` +} + +// UnmarshalXML ... +func (s *Shade) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "val": + s.Val = attr.Value + case "color": + s.Color = attr.Value + case "fill": + s.Fill = attr.Value + case "themeFill": + s.ThemeFill = attr.Value + case "themeFillTint": + s.ThemeFillTint = attr.Value + default: + // ignore other attributes + } + } + // Consume the end element + _, err := d.Token() + return err +} diff --git a/structpara.go b/structpara.go index a72077f..b43e6a7 100644 --- a/structpara.go +++ b/structpara.go @@ -30,8 +30,9 @@ import ( // ParagraphProperties type ParagraphProperties struct { - XMLName xml.Name `xml:"w:pPr,omitempty"` - Justification *Justification `xml:"w:jc,omitempty"` + XMLName xml.Name `xml:"w:pPr,omitempty"` + Justification *Justification + Shade *Shade } // UnmarshalXML ... @@ -48,6 +49,13 @@ func (p *ParagraphProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen switch tt.Name.Local { case "jc": p.Justification = &Justification{Val: getAtt(tt.Attr, "val")} + case "shd": + var value Shade + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + p.Shade = &value default: err = d.Skip() // skip unsupported tags if err != nil { diff --git a/structrun.go b/structrun.go index 4873610..78ad7bc 100644 --- a/structrun.go +++ b/structrun.go @@ -106,11 +106,12 @@ type WTab struct { // 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 + Size *Size + RunStyle *RunStyle + Style *Style + Shade *Shade } // UnmarshalXML ... @@ -142,6 +143,13 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err var value Style value.Val = getAtt(tt.Attr, "val") r.Style = &value + case "shd": + var value Shade + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + r.Shade = &value default: err = d.Skip() // skip unsupported tags if err != nil { @@ -154,41 +162,3 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err return nil } - -// RunStyle contains styling for a run -type RunStyle struct { - XMLName xml.Name `xml:"w:rStyle,omitempty"` - Val string `xml:"w:val,attr"` -} - -// Style contains styling for a paragraph -type Style struct { - XMLName xml.Name `xml:"w:pStyle,omitempty"` - Val string `xml:"w:val,attr"` -} - -// Color contains the sound of music. :D -// I'm kidding. It contains the color -type Color struct { - XMLName xml.Name `xml:"w:color"` - Val string `xml:"w:val,attr"` -} - -// Size contains the font size -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"` -} diff --git a/structtable.go b/structtable.go index 57c4fee..8401dc2 100644 --- a/structtable.go +++ b/structtable.go @@ -307,10 +307,7 @@ func (t *WTableLook) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error } // Consume the end element _, err := d.Token() - if err != nil { - return err - } - return nil + return err } // WTableGrid is a structure that represents the table grid of a Word document. @@ -382,6 +379,7 @@ func (g *WGridCol) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err type WTableRow struct { XMLName xml.Name `xml:"w:tr,omitempty"` RsidR string `xml:"w:rsidR,attr,omitempty"` + RsidRPr string `xml:"w:rsidRPr,attr,omitempty"` RsidTr string `xml:"w:rsidTr,attr,omitempty"` TableRowProperties *WTableRowProperties TableCells []*WTableCell @@ -395,6 +393,8 @@ func (w *WTableRow) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch attr.Name.Local { case "rsidR": w.RsidR = attr.Value + case "rsidRPr": + w.RsidRPr = attr.Value case "rsidTr": w.RsidTr = attr.Value default: @@ -443,7 +443,7 @@ func (w *WTableRow) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type WTableRowProperties struct { XMLName xml.Name `xml:"w:trPr,omitempty"` TableRowHeight *WTableRowHeight - Justification *Justification `xml:"w:jc,omitempty"` + Justification *Justification } // UnmarshalXML ... @@ -462,12 +462,14 @@ func (t *WTableRowProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen case "trHeight": th := new(WTableRowHeight) for _, attr := range tt.Attr { - if attr.Name.Local == "val" { + switch attr.Name.Local { + case "val": th.Val, err = strconv.ParseInt(attr.Value, 10, 64) if err != nil { return err } - break + case "hRule": + th.Rule = attr.Value } } t.TableRowHeight = th @@ -502,6 +504,7 @@ func (t *WTableRowProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen // WTableRowHeight represents the height of a row within a table. type WTableRowHeight struct { XMLName xml.Name `xml:"w:trHeight,omitempty"` + Rule string `xml:"w:hRule,omitempty"` Val int64 `xml:"w:val,attr"` } @@ -563,6 +566,7 @@ type WTableCellProperties struct { GridSpan *WGridSpan VAlign *WVerticalAlignment TableBorders *WTableBorders `xml:"w:tcBorders"` + Shade *Shade } // UnmarshalXML ... @@ -608,6 +612,13 @@ func (r *WTableCellProperties) UnmarshalXML(d *xml.Decoder, start xml.StartEleme if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } + case "shd": + var value Shade + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + r.Shade = &value default: err = d.Skip() // skip unsupported tags if err != nil { @@ -743,10 +754,7 @@ func (t *WTableBorder) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro } // Consume the end element _, err := d.Token() - if err != nil { - return err - } - return nil + return err } // WGridSpan represents the number of grid columns this cell should span. diff --git a/structtable_test.go b/structtable_test.go index 1b61e48..f835aa3 100644 --- a/structtable_test.go +++ b/structtable_test.go @@ -36,7 +36,7 @@ func TestTableStructure(t *testing.T) { para1.AddText("table") tab1 := w.AddTable(4, 3).Justification("center") tab1.TableProperties.Position = &WTablePositioningProperties{LeftFromText: 2333} - para2 := tab1.TableRows[3].Justification("center").TableCells[2].AddParagraph() + para2 := tab1.TableRows[3].Justification("center").TableCells[2].Shade("clear", "auto", "E7E6E6").AddParagraph() r, err := para2.AddAnchorDrawingFrom("testdata/fumiama.JPG") if err != nil { t.Fatal(err) diff --git a/unpack.go b/unpack.go index e705f6f..2ee83e9 100644 --- a/unpack.go +++ b/unpack.go @@ -87,10 +87,7 @@ func (f *Docx) parseDocument(file *zip.File) error { f.Document.Body.file = f err = xml.NewDecoder(zf).Decode(&f.Document) - if err != nil { - return err - } - return nil + return err } // parseDocRelation processes one of the relevant files, the one with the relationships