spreadsheet: add support for merged cells

This commit is contained in:
Todd 2017-09-07 17:13:03 -05:00
parent 8957cf73e3
commit 924140c87d
7 changed files with 183 additions and 0 deletions

View File

@ -41,6 +41,7 @@ and .pptx).
- [Bar Chart](https://github.com/baliance/gooxml/tree/master/_examples/spreadsheet/bar-chart) Bar Charts
- [Mutiple Charts](https://github.com/baliance/gooxml/tree/master/_examples/spreadsheet/multiple-charts) Multiple charts on a single sheet
- [Named Cell Ranges](https://github.com/baliance/gooxml/tree/master/_examples/spreadsheet/named-ranges) Naming cell ranges
- [Merged Cells](https://github.com/baliance/gooxml/tree/master/_examples/spreadsheet/merged) Merge and unmerge cells.
## Raw Types ##

View File

@ -0,0 +1,35 @@
// Copyright 2017 Baliance. All rights reserved.
package main
import (
"fmt"
"log"
"baliance.com/gooxml/spreadsheet"
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
)
func main() {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetString("Hello World!")
sheet.Cell("B1").SetString("will not be visible") // as it's not the first cell within a merged range Excel warns you when you do this through the UI
sheet.AddMergedCells("A1", "C2")
centered := ss.StyleSheet.AddCellStyle()
centered.SetHorizontalAlignment(sml.ST_HorizontalAlignmentCenter)
centered.SetVerticalAlignment(sml.ST_VerticalAlignmentCenter)
sheet.Cell("A1").SetStyle(centered)
for _, m := range sheet.MergedCells() {
fmt.Println("merged region", m.Reference(), "has contents", m.Cell().GetString())
}
if err := ss.Validate(); err != nil {
log.Fatalf("error validating sheet: %s", err)
}
ss.SaveToFile("merged.xlsx")
}

Binary file not shown.

View File

@ -245,6 +245,31 @@ func (c Cell) SetStyleIndex(idx uint32) {
c.x.SAttr = gooxml.Uint32(idx)
}
// GetString returns the string in a cell if it's an inline or string table
// string. Otherwise it returns an empty string.
func (c Cell) GetString() string {
switch c.x.TAttr {
case sml.ST_CellTypeInlineStr:
if c.x.Is == nil || c.x.Is.T == nil {
return ""
}
case sml.ST_CellTypeS:
if c.x.V == nil {
return ""
}
id, err := strconv.Atoi(*c.x.V)
if err != nil {
return ""
}
s, err := c.w.SharedStrings.GetString(id)
if err != nil {
return ""
}
return s
}
return ""
}
func (c Cell) GetValue() (string, error) {
switch c.x.TAttr {
case sml.ST_CellTypeInlineStr:

48
spreadsheet/mergedcell.go Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2017 Baliance. All rights reserved.
//
// Use of this source code is governed by the terms of the Affero GNU General
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting sales@baliance.com.
package spreadsheet
import (
"fmt"
"strings"
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
)
type MergedCell struct {
wb *Workbook
ws *sml.Worksheet
x *sml.CT_MergeCell
}
// X returns the inner wrapped XML type.
func (s MergedCell) X() *sml.CT_MergeCell {
return s.x
}
// SetReference sets the regin of cells that the merged cell applies to.
func (s MergedCell) SetReference(ref string) {
s.x.RefAttr = ref
}
// Reference returns the region of cells that are merged.
func (s MergedCell) Reference() string {
return s.x.RefAttr
}
// Cell returns the actual cell behind the merged region
func (s MergedCell) Cell() Cell {
ref := s.Reference()
if idx := strings.Index(s.Reference(), ":"); idx != -1 {
ref = ref[0:idx]
return Sheet{w: s.wb, x: s.ws}.Cell(ref)
}
fmt.Println("DIE")
// couldn't find it, log an error?
return Cell{}
}

View File

@ -257,3 +257,46 @@ func (s Sheet) SetAutoFilter(rangeRef string) {
}
}
}
// AddMergedCells merges cells within a sheet.
func (s Sheet) AddMergedCells(fromRef, toRef string) MergedCell {
// TODO: we might need to actually create the merged cells if they don't
// exist, but it appears to work fine on both Excel and LibreOffice just
// creating the merged region
if s.x.MergeCells == nil {
s.x.MergeCells = sml.NewCT_MergeCells()
}
merge := sml.NewCT_MergeCell()
merge.RefAttr = fmt.Sprintf("%s:%s", fromRef, toRef)
s.x.MergeCells.MergeCell = append(s.x.MergeCells.MergeCell, merge)
s.x.MergeCells.CountAttr = gooxml.Uint32(uint32(len(s.x.MergeCells.MergeCell)))
return MergedCell{s.w, s.x, merge}
}
// MergedCells returns the merged cell regions within the sheet.
func (s Sheet) MergedCells() []MergedCell {
if s.x.MergeCells == nil {
return nil
}
ret := []MergedCell{}
for _, c := range s.x.MergeCells.MergeCell {
ret = append(ret, MergedCell{s.w, s.x, c})
}
return ret
}
// RemoveMergedCell removes merging from a cell range within a sheet. The cells
// that made up the merged cell remain, but are no lon merged.
func (s Sheet) RemoveMergedCell(mc MergedCell) {
for i, c := range s.x.MergeCells.MergeCell {
if c == mc.X() {
copy(s.x.MergeCells.MergeCell[i:], s.x.MergeCells.MergeCell[i+1:])
s.x.MergeCells.MergeCell[len(s.x.MergeCells.MergeCell)-1] = nil
s.x.MergeCells.MergeCell = s.x.MergeCells.MergeCell[:len(s.x.MergeCells.MergeCell)-1]
}
}
}

View File

@ -132,3 +132,34 @@ func TestSheetNameLength(t *testing.T) {
t.Errorf("expected validation error with sheet name too long")
}
}
func TestMergedCell(t *testing.T) {
wb := spreadsheet.New()
sheet := wb.AddSheet()
expContent := "testing 123"
sheet.Cell("A1").SetString(expContent)
sheet.Cell("B1").SetString("in range, but not visible")
if len(sheet.MergedCells()) != 0 {
t.Errorf("new sheet should have no merged cells")
}
sheet.AddMergedCells("A1", "C2")
if len(sheet.MergedCells()) != 1 {
t.Errorf("sheet should have a single merged cells")
}
mc := sheet.MergedCells()[0]
expRef := "A1:C2"
if mc.Reference() != expRef {
t.Errorf("expected merged cell reference %s, got %s", expRef, mc.Reference())
}
if mc.Cell().GetString() != expContent {
t.Errorf("expected merged cell content to be '%s', got '%s'", expContent, mc.Cell().GetString())
}
sheet.RemoveMergedCell(mc)
if len(sheet.MergedCells()) != 0 {
t.Errorf("after removal, sheet should have no merged cells")
}
}