From f35ee904dc22321aafe22b40bd8d78886f459739 Mon Sep 17 00:00:00 2001 From: gomutex Date: Sat, 3 Feb 2024 00:08:54 +0530 Subject: [PATCH] Table Borders implementation --- oxml/elements/border_position.go | 39 ++++++ oxml/elements/border_type.go | 37 +++++ oxml/elements/table_borders.go | 204 ++++++++++++++++++++++++++++ oxml/elements/table_borders_test.go | 78 +++++++++++ oxml/elements/table_property.go | 15 +- 5 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 oxml/elements/border_position.go create mode 100644 oxml/elements/border_type.go create mode 100644 oxml/elements/table_borders.go create mode 100644 oxml/elements/table_borders_test.go diff --git a/oxml/elements/border_position.go b/oxml/elements/border_position.go new file mode 100644 index 0000000..a26cde0 --- /dev/null +++ b/oxml/elements/border_position.go @@ -0,0 +1,39 @@ +package elements + +// TableBorderPosition represents the position of a border in a table. +type TableBorderPosition string + +const ( + TableBorderPositionLeft TableBorderPosition = "w:left" + TableBorderPositionRight TableBorderPosition = "w:right" + TableBorderPositionTop TableBorderPosition = "w:top" + TableBorderPositionBottom TableBorderPosition = "w:bottom" + TableBorderPositionInsideH TableBorderPosition = "w:insideH" + TableBorderPositionInsideV TableBorderPosition = "w:insideV" +) + +// TableCellBorderPosition represents the position of a border in a table cell. +type TableCellBorderPosition int + +const ( + TableCellBorderPositionLeft TableCellBorderPosition = 1 + TableCellBorderPositionRight TableCellBorderPosition = 2 + TableCellBorderPositionTop TableCellBorderPosition = 3 + TableCellBorderPositionBottom TableCellBorderPosition = 4 + TableCellBorderPositionInsideH TableCellBorderPosition = 5 + TableCellBorderPositionInsideV TableCellBorderPosition = 6 + TableCellBorderPositionTl2br TableCellBorderPosition = 7 + TableCellBorderPositionTr2bl TableCellBorderPosition = 8 +) + +// ParagraphBorderPosition represents the position of a border in a paragraph. +type ParagraphBorderPosition int + +const ( + ParagraphBorderPositionLeft ParagraphBorderPosition = 1 + ParagraphBorderPositionRight ParagraphBorderPosition = 2 + ParagraphBorderPositionTop ParagraphBorderPosition = 3 + ParagraphBorderPositionBottom ParagraphBorderPosition = 4 + ParagraphBorderPositionBetween ParagraphBorderPosition = 5 + ParagraphBorderPositionBar ParagraphBorderPosition = 6 +) diff --git a/oxml/elements/border_type.go b/oxml/elements/border_type.go new file mode 100644 index 0000000..f56417f --- /dev/null +++ b/oxml/elements/border_type.go @@ -0,0 +1,37 @@ +package elements + +type BorderType string + +const ( + BorderTypeNil BorderType = "nil" + BorderTypeNone BorderType = "none" + BorderTypeSingle BorderType = "single" + BorderTypeThick BorderType = "thick" + BorderTypeDouble BorderType = "double" + BorderTypeDotted BorderType = "dotted" + BorderTypeDashed BorderType = "dashed" + BorderTypeDotDash BorderType = "dotDash" + BorderTypeDotDotDash BorderType = "dotDotDash" + BorderTypeTriple BorderType = "triple" + BorderTypeThinThickSmallGap BorderType = "thinThickSmallGap" + BorderTypeThickThinSmallGap BorderType = "thickThinSmallGap" + BorderTypeThinThickThinSmallGap BorderType = "thinThickThinSmallGap" + BorderTypeThinThickMediumGap BorderType = "thinThickMediumGap" + BorderTypeThickThinMediumGap BorderType = "thickThinMediumGap" + BorderTypeThinThickThinMediumGap BorderType = "thinThickThinMediumGap" + BorderTypeThinThickLargeGap BorderType = "thinThickLargeGap" + BorderTypeThickThinLargeGap BorderType = "thickThinLargeGap" + BorderTypeThinThickThinLargeGap BorderType = "thinThickThinLargeGap" + BorderTypeWave BorderType = "wave" + BorderTypeDoubleWave BorderType = "doubleWave" + BorderTypeDashSmallGap BorderType = "dashSmallGap" + BorderTypeDashDotStroked BorderType = "dashDotStroked" + BorderTypeThreeDEmboss BorderType = "threeDEmboss" + BorderTypeThreeDEngrave BorderType = "threeDEngrave" + BorderTypeOutset BorderType = "outset" + BorderTypeInset BorderType = "inset" + BorderTypeApples BorderType = "apples" + BorderTypeArchedScallops BorderType = "archedScallops" + BorderTypeBabyPacifier BorderType = "babyPacifier" + BorderTypeBabyRattle BorderType = "babyRattle" +) diff --git a/oxml/elements/table_borders.go b/oxml/elements/table_borders.go new file mode 100644 index 0000000..9feec80 --- /dev/null +++ b/oxml/elements/table_borders.go @@ -0,0 +1,204 @@ +package elements + +import ( + "encoding/xml" + "strconv" +) + +type TableBorderElement struct { + BorderType BorderType + Position TableBorderPosition + Size uint64 + Space uint64 + Color string +} + +type TableBorders struct { + Top *TableBorderElement + Bottom *TableBorderElement + Left *TableBorderElement + Right *TableBorderElement + InsideH *TableBorderElement + InsideV *TableBorderElement +} + +// DefaulTableBorderElement creates a new TableBorderElement instance with default values. +// It sets the default border type to single, size to 2, space to 0, color to "000000", +// and position to TableBorderPositionBottom. +func DefaulTableBorderElement() *TableBorderElement { + return &TableBorderElement{ + BorderType: BorderTypeSingle, + Size: 2, + Space: 0, + Color: "000000", + Position: TableBorderPositionBottom, + } +} + +// NewTableBorderElement returns a new table border with the given properties. +func NewTableBorderElement(borderType BorderType, size uint64, color string, position TableBorderPosition, space uint64) *TableBorderElement { + return &TableBorderElement{ + BorderType: borderType, + Size: size, + Color: color, + Position: position, + Space: space, + } +} + +func DefaultTableBorders() *TableBorders { + return &TableBorders{} +} + +// MarshalXML implements the xml.Marshaler interface for TableBorderElement. +func (t *TableBorderElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + start.Name.Local = string(t.Position) + + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "w:val"}, Value: string(t.BorderType)}) + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "w:sz"}, Value: strconv.FormatInt(int64(t.Size), 10)}) + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "w:space"}, Value: strconv.FormatInt(int64(t.Space), 10)}) + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "w:color"}, Value: t.Color}) + + return e.EncodeElement("", start) +} + +func (t *TableBorderElement) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + + switch start.Name.Local { + case "top": + t.Position = TableBorderPositionTop + case "bottom": + t.Position = TableBorderPositionBottom + case "right": + t.Position = TableBorderPositionRight + case "left": + t.Position = TableBorderPositionLeft + case "insideH": + t.Position = TableBorderPositionInsideH + case "insideV": + t.Position = TableBorderPositionInsideV + } + + for _, attr := range start.Attr { + switch attr.Name.Local { + case "sz": + valueStr := attr.Value + if valueStr != "" { + value, err := strconv.ParseUint(valueStr, 10, 0) + if err != nil { + return err + } + t.Size = value + } + case "space": + valueStr := attr.Value + if valueStr != "" { + value, err := strconv.ParseUint(valueStr, 10, 0) + if err != nil { + return err + } + t.Space = value + } + case "val": + t.BorderType = BorderType(attr.Value) + case "color": + t.Color = attr.Value + } + } + + return d.Skip() // Skipping the inner content +} + +func (t *TableBorders) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + start.Name.Local = "tblBorders" + + if err := e.EncodeToken(start); err != nil { + return err + } + // Marshal each border individually + if t.Top != nil { + if err := e.Encode(t.Top); err != nil { + return err + } + } + if t.Left != nil { + if err := e.Encode(t.Left); err != nil { + return err + } + } + if t.Bottom != nil { + if err := e.Encode(t.Bottom); err != nil { + return err + } + } + if t.Right != nil { + if err := e.Encode(t.Right); err != nil { + return err + } + } + if t.InsideH != nil { + if err := e.Encode(t.InsideH); err != nil { + return err + } + } + if t.InsideV != nil { + if err := e.Encode(t.InsideV); err != nil { + return err + } + } + + return e.EncodeToken(xml.EndElement{Name: start.Name}) + +} + +func (t *TableBorders) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + + // Loop through each element within tblBorders + for { + token, err := d.Token() + if err != nil { + return err + } + switch elem := token.(type) { + case xml.StartElement: + switch elem.Name.Local { + case "top": + + t.Top = &TableBorderElement{} + if err := d.DecodeElement(t.Top, &elem); err != nil { + return err + } + case "left": + t.Left = &TableBorderElement{} + if err := d.DecodeElement(t.Left, &elem); err != nil { + return err + } + case "bottom": + t.Bottom = &TableBorderElement{} + if err := d.DecodeElement(t.Bottom, &elem); err != nil { + return err + } + case "right": + t.Right = &TableBorderElement{} + if err := d.DecodeElement(t.Right, &elem); err != nil { + return err + } + case "insideH": + t.InsideH = &TableBorderElement{} + if err := d.DecodeElement(t.InsideH, &elem); err != nil { + return err + } + case "insideV": + t.InsideV = &TableBorderElement{} + if err := d.DecodeElement(t.InsideV, &elem); err != nil { + return err + } + } + + case xml.EndElement: + if elem == start.End() { + return nil + } + } + } +} diff --git a/oxml/elements/table_borders_test.go b/oxml/elements/table_borders_test.go new file mode 100644 index 0000000..297cb1b --- /dev/null +++ b/oxml/elements/table_borders_test.go @@ -0,0 +1,78 @@ +package elements + +import ( + "encoding/xml" + "testing" +) + +func TestTableBorderElementMarshaling(t *testing.T) { + tblBorder := DefaulTableBorderElement() + tblBorder.BorderType = BorderTypeSingle + tblBorder.Color = "000000" + tblBorder.Position = TableBorderPositionTop + tblBorder.Space = 0 + tblBorder.Size = 2 + xmlData, err := xml.Marshal(tblBorder) + if err != nil { + t.Fatalf("Error marshaling TableBorders to XML: %v", err) + } + + var unmarshalledTableBorder TableBorderElement + err = xml.Unmarshal(xmlData, &unmarshalledTableBorder) + if err != nil { + t.Fatalf("Error unmarshaling XML to TableBorder: %v", err) + } + + if unmarshalledTableBorder.Position != tblBorder.Position { + t.Errorf("Expected TableBorder Position value %s, got %s", tblBorder.Position, unmarshalledTableBorder.Position) + } + + if unmarshalledTableBorder.BorderType != tblBorder.BorderType { + t.Errorf("Expected TableBorder BorderType value %s, got %s", tblBorder.BorderType, unmarshalledTableBorder.BorderType) + } + + if unmarshalledTableBorder.Color != tblBorder.Color { + t.Errorf("Expected TableBorder Color value %s, got %s", tblBorder.Color, unmarshalledTableBorder.Color) + } + + if unmarshalledTableBorder.Size != tblBorder.Size { + t.Errorf("Expected TableBorder Size value %v, got %v", tblBorder.Size, unmarshalledTableBorder.Size) + } + +} + +func TestTableBordersMarshaling(t *testing.T) { + tblBorders := DefaultTableBorders() + tblBorders.Bottom = DefaulTableBorderElement() + xmlData, err := xml.Marshal(tblBorders) + if err != nil { + t.Fatalf("Error marshaling TableBorders to XML: %v", err) + } + + var unmarshalledTableBorders TableBorders + err = xml.Unmarshal(xmlData, &unmarshalledTableBorders) + if err != nil { + t.Fatalf("Error unmarshaling XML to TableBorders: %v", err) + } + + if unmarshalledTableBorders.Bottom == nil { + t.Fatalf("Error unmarshaling XML to TableBorders: Top got nil value") + } + + if unmarshalledTableBorders.Bottom.Position != tblBorders.Bottom.Position { + t.Errorf("Expected TableBorder Position value %s, got %s", tblBorders.Bottom.Position, unmarshalledTableBorders.Bottom.Position) + } + + if unmarshalledTableBorders.Bottom.BorderType != tblBorders.Bottom.BorderType { + t.Errorf("Expected TableBorder BorderType value %s, got %s", tblBorders.Bottom.BorderType, unmarshalledTableBorders.Bottom.BorderType) + } + + if unmarshalledTableBorders.Bottom.Color != tblBorders.Bottom.Color { + t.Errorf("Expected TableBorder Color value %s, got %s", tblBorders.Bottom.Color, unmarshalledTableBorders.Bottom.Color) + } + + if unmarshalledTableBorders.Bottom.Size != tblBorders.Bottom.Size { + t.Errorf("Expected TableBorder Size value %v, got %v", tblBorders.Bottom.Size, unmarshalledTableBorders.Bottom.Size) + } + +} diff --git a/oxml/elements/table_property.go b/oxml/elements/table_property.go index 0f9fd21..c46a457 100644 --- a/oxml/elements/table_property.go +++ b/oxml/elements/table_property.go @@ -13,8 +13,7 @@ type TableProperty struct { Style *TableStyle Indent *TableIndent Margins *TableCellMargins - - // Borders TableBorders // TODO: implement borders + Borders *TableBorders } func DefaultTableProperty() *TableProperty { @@ -65,6 +64,12 @@ func (t *TableProperty) MarshalXML(e *xml.Encoder, start xml.StartElement) (err } } + if t.Borders != nil { + if err = e.EncodeElement(t.Borders, xml.StartElement{Name: xml.Name{Local: "w:tblBorders"}}); err != nil { + return err + } + } + return e.EncodeToken(xml.EndElement{Name: start.Name}) } @@ -114,6 +119,12 @@ func (t *TableProperty) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err return err } t.Margins = &tblCellMargins + case xml.Name{Space: constants.WMLNamespace, Local: "tblBorders"}, xml.Name{Space: constants.AltWMLNamespace, Local: "tblBorders"}: + tblBorders := TableBorders{} + if err := d.DecodeElement(tblBorders, &elem); err != nil { + return err + } + t.Borders = &tblBorders default: if err = d.Skip(); err != nil { return err