Browse Source

Merge branch 'output-styles'

Geoffrey J. Teale 11 years ago
parent
commit
989a8bf5ee
11 changed files with 392 additions and 265 deletions
  1. 11 86
      cell.go
  2. 95 160
      cell_test.go
  3. 2 1
      file.go
  4. 20 4
      file_test.go
  5. 4 3
      lib.go
  6. 18 1
      sheet.go
  7. 74 3
      sheet_test.go
  8. 42 3
      style.go
  9. 30 0
      style_test.go
  10. 92 0
      xmlStyle.go
  11. 4 4
      xmlWorksheet.go

+ 11 - 86
cell.go

@@ -4,18 +4,16 @@ import (
 	"fmt"
 	"math"
 	"strconv"
-	"strings"
 )
 
 // Cell is a high level structure intended to provide user access to
 // the contents of Cell within an xlsx.Row.
 type Cell struct {
-	Value          string
-	styleIndex     int
-	styles         *xlsxStyles
-	numFmtRefTable map[int]xlsxNumFmt
-	date1904       bool
-	Hidden         bool
+	Value    string
+	style    Style
+	numFmt   string
+	date1904 bool
+	Hidden   bool
 }
 
 // CellInterface defines the public API of the Cell.
@@ -31,63 +29,17 @@ func (c *Cell) String() string {
 
 // GetStyle returns the Style associated with a Cell
 func (c *Cell) GetStyle() Style {
-	var err error
-	style := Style{}
-	style.Border = Border{}
-	style.Fill = Fill{}
-	style.Font = Font{}
-
-	if c.styleIndex > -1 && c.styleIndex <= len(c.styles.CellXfs) {
-		xf := c.styles.CellXfs[c.styleIndex]
-
-		if xf.ApplyBorder {
-			style.Border.Left = c.styles.Borders[xf.BorderId].Left.Style
-			style.Border.Right = c.styles.Borders[xf.BorderId].Right.Style
-			style.Border.Top = c.styles.Borders[xf.BorderId].Top.Style
-			style.Border.Bottom = c.styles.Borders[xf.BorderId].Bottom.Style
-		}
-		// TODO - consider how to handle the fact that
-		// ApplyFill can be missing.  At the moment the XML
-		// unmarshaller simply sets it to false, which creates
-		// a bug.
+	return c.style
+}
 
-		// if xf.ApplyFill {
-		if xf.FillId > -1 && xf.FillId < len(c.styles.Fills) {
-			xFill := c.styles.Fills[xf.FillId]
-			style.Fill.PatternType = xFill.PatternFill.PatternType
-			style.Fill.FgColor = xFill.PatternFill.FgColor.RGB
-			style.Fill.BgColor = xFill.PatternFill.BgColor.RGB
-		}
-		// }
-		if xf.ApplyFont {
-			xfont := c.styles.Fonts[xf.FontId]
-			style.Font.Size, err = strconv.Atoi(xfont.Sz.Val)
-			if err != nil {
-				panic(err.Error())
-			}
-			style.Font.Name = xfont.Name.Val
-			style.Font.Family, _ = strconv.Atoi(xfont.Family.Val)
-			style.Font.Charset, _ = strconv.Atoi(xfont.Charset.Val)
-		}
-	}
-	return style
+// SetStyle sets the style of a cell.
+func (c *Cell) SetStyle(style Style) {
+	c.style = style
 }
 
 // The number format string is returnable from a cell.
 func (c *Cell) GetNumberFormat() string {
-	if c.styles == nil {
-		return ""
-	}
-	if c.styles.CellXfs == nil {
-		return ""
-	}
-	var numberFormat string = ""
-	if c.styleIndex > -1 && c.styleIndex <= len(c.styles.CellXfs) {
-		xf := c.styles.CellXfs[c.styleIndex]
-		numFmt := c.numFmtRefTable[xf.NumFmtId]
-		numberFormat = numFmt.FormatCode
-	}
-	return strings.ToLower(numberFormat)
+	return c.numFmt
 }
 
 func (c *Cell) formatToTime(format string) string {
@@ -229,30 +181,3 @@ func (c *Cell) FormattedValue() string {
 	}
 	return c.Value
 }
-
-func (c *Cell) SetStyle(style *Style) int {
-	if c.styles == nil {
-		c.styles = &xlsxStyles{}
-	}
-	index := len(c.styles.Fonts)
-	xFont := xlsxFont{}
-	xFill := xlsxFill{}
-	xBorder := xlsxBorder{}
-	xCellStyleXf := xlsxXf{}
-	xCellXf := xlsxXf{}
-	xFont.Sz.Val = strconv.Itoa(style.Font.Size)
-	xFont.Name.Val = style.Font.Name
-	xFont.Family.Val = strconv.Itoa(style.Font.Family)
-	xFont.Charset.Val = strconv.Itoa(style.Font.Charset)
-	xPatternFill := xlsxPatternFill{}
-	xPatternFill.PatternType = style.Fill.PatternType
-	xPatternFill.FgColor.RGB = style.Fill.FgColor
-	xPatternFill.BgColor.RGB = style.Fill.BgColor
-	xFill.PatternFill = xPatternFill
-	c.styles.Fonts = append(c.styles.Fonts, xFont)
-	c.styles.Fills = append(c.styles.Fills, xFill)
-	c.styles.Borders = append(c.styles.Borders, xBorder)
-	c.styles.CellStyleXfs = append(c.styles.CellStyleXfs, xCellStyleXf)
-	c.styles.CellXfs = append(c.styles.CellXfs, xCellXf)
-	return index
-}

+ 95 - 160
cell_test.go

@@ -21,71 +21,44 @@ func (s *CellSuite) TestValueSet(c *C) {
 
 // Test that GetStyle correctly converts the xlsxStyle.Fonts.
 func (s *CellSuite) TestGetStyleWithFonts(c *C) {
-	var cell *Cell
-	var style Style
-	var xStyles *xlsxStyles
-	var fonts []xlsxFont
-	var cellXfs []xlsxXf
-
-	fonts = make([]xlsxFont, 1)
-	fonts[0] = xlsxFont{
-		Sz:   xlsxVal{Val: "10"},
-		Name: xlsxVal{Val: "Calibra"}}
-
-	cellXfs = make([]xlsxXf, 1)
-	cellXfs[0] = xlsxXf{ApplyFont: true, FontId: 0}
-
-	xStyles = &xlsxStyles{Fonts: fonts, CellXfs: cellXfs}
+	font := NewFont(10, "Calibra")
+	style := *NewStyle()
+	style.Font = *font
 
-	cell = &Cell{Value: "123", styleIndex: 0, styles: xStyles}
+	cell := &Cell{Value: "123", style: style}
 	style = cell.GetStyle()
 	c.Assert(style, NotNil)
 	c.Assert(style.Font.Size, Equals, 10)
 	c.Assert(style.Font.Name, Equals, "Calibra")
 }
 
-// Test that SetStyle correctly updates the xlsxStyle.Fonts.
+// Test that SetStyle correctly translates into a xlsxFont element
 func (s *CellSuite) TestSetStyleWithFonts(c *C) {
 	file := NewFile()
 	sheet := file.AddSheet("Test")
 	row := sheet.AddRow()
 	cell := row.AddCell()
 	font := NewFont(12, "Calibra")
-	style := NewStyle()
+	style := *NewStyle()
 	style.Font = *font
 	cell.SetStyle(style)
-	c.Assert(cell.styleIndex, Equals, 0)
-	c.Assert(cell.styles.Fonts, HasLen, 1)
-	xFont := cell.styles.Fonts[0]
+	style = cell.GetStyle()
+	xFont, _, _, _, _ := style.makeXLSXStyleElements()
 	c.Assert(xFont.Sz.Val, Equals, "12")
 	c.Assert(xFont.Name.Val, Equals, "Calibra")
 }
 
 // Test that GetStyle correctly converts the xlsxStyle.Fills.
 func (s *CellSuite) TestGetStyleWithFills(c *C) {
-	var cell *Cell
-	var style Style
-	var xStyles *xlsxStyles
-	var fills []xlsxFill
-	var cellXfs []xlsxXf
-
-	fills = make([]xlsxFill, 1)
-	fills[0] = xlsxFill{
-		PatternFill: xlsxPatternFill{
-			PatternType: "solid",
-			FgColor:     xlsxColor{RGB: "FF000000"},
-			BgColor:     xlsxColor{RGB: "00FF0000"}}}
-	cellXfs = make([]xlsxXf, 1)
-	cellXfs[0] = xlsxXf{ApplyFill: true, FillId: 0}
-
-	xStyles = &xlsxStyles{Fills: fills, CellXfs: cellXfs}
-
-	cell = &Cell{Value: "123", styleIndex: 0, styles: xStyles}
+	fill := *NewFill("solid", "FF000000", "00FF0000")
+	style := *NewStyle()
+	style.Fill = fill
+	cell := &Cell{Value: "123", style: style}
 	style = cell.GetStyle()
-	fill := style.Fill
-	c.Assert(fill.PatternType, Equals, "solid")
-	c.Assert(fill.BgColor, Equals, "00FF0000")
-	c.Assert(fill.FgColor, Equals, "FF000000")
+	_, xFill, _, _, _ := style.makeXLSXStyleElements()
+	c.Assert(xFill.PatternFill.PatternType, Equals, "solid")
+	c.Assert(xFill.PatternFill.BgColor.RGB, Equals, "00FF0000")
+	c.Assert(xFill.PatternFill.FgColor.RGB, Equals, "FF000000")
 }
 
 // Test that SetStyle correctly updates xlsxStyle.Fills.
@@ -95,12 +68,11 @@ func (s *CellSuite) TestSetStyleWithFills(c *C) {
 	row := sheet.AddRow()
 	cell := row.AddCell()
 	fill := NewFill("solid", "00FF0000", "FF000000")
-	style := NewStyle()
+	style := *NewStyle()
 	style.Fill = *fill
 	cell.SetStyle(style)
-	c.Assert(cell.styleIndex, Equals, 0)
-	c.Assert(cell.styles.Fills, HasLen, 1)
-	xFill := cell.styles.Fills[0]
+	style = cell.GetStyle()
+	_, xFill, _, _, _ := style.makeXLSXStyleElements()
 	xPatternFill := xFill.PatternFill
 	c.Assert(xPatternFill.PatternType, Equals, "solid")
 	c.Assert(xPatternFill.FgColor.RGB, Equals, "00FF0000")
@@ -109,214 +81,177 @@ func (s *CellSuite) TestSetStyleWithFills(c *C) {
 
 // Test that GetStyle correctly converts the xlsxStyle.Borders.
 func (s *CellSuite) TestGetStyleWithBorders(c *C) {
-	var cell *Cell
-	var style Style
-	var xStyles *xlsxStyles
-	var borders []xlsxBorder
-	var cellXfs []xlsxXf
-
-	borders = make([]xlsxBorder, 1)
-	borders[0] = xlsxBorder{
-		Left:   xlsxLine{Style: "thin"},
-		Right:  xlsxLine{Style: "thin"},
-		Top:    xlsxLine{Style: "thin"},
-		Bottom: xlsxLine{Style: "thin"}}
-
-	cellXfs = make([]xlsxXf, 1)
-	cellXfs[0] = xlsxXf{ApplyBorder: true, BorderId: 0}
-
-	xStyles = &xlsxStyles{Borders: borders, CellXfs: cellXfs}
-
-	cell = &Cell{Value: "123", styleIndex: 0, styles: xStyles}
+	border := *NewBorder("thin", "thin", "thin", "thin")
+	style := *NewStyle()
+	style.Border = border
+	cell := Cell{Value: "123", style: style}
 	style = cell.GetStyle()
-	border := style.Border
-	c.Assert(border.Left, Equals, "thin")
-	c.Assert(border.Right, Equals, "thin")
-	c.Assert(border.Top, Equals, "thin")
-	c.Assert(border.Bottom, Equals, "thin")
-}
-
-func (s *CellSuite) TestGetNumberFormat(c *C) {
-	var cell *Cell
-	var cellXfs []xlsxXf
-	var numFmt xlsxNumFmt
-	var numFmts []xlsxNumFmt
-	var xStyles *xlsxStyles
-	var numFmtRefTable map[int]xlsxNumFmt
-
-	cellXfs = make([]xlsxXf, 1)
-	cellXfs[0] = xlsxXf{NumFmtId: 0}
-
-	numFmts = make([]xlsxNumFmt, 1)
-	numFmtRefTable = make(map[int]xlsxNumFmt)
-
-	xStyles = &xlsxStyles{NumFmts: numFmts, CellXfs: cellXfs}
-
-	cell = &Cell{Value: "123.123", numFmtRefTable: numFmtRefTable, styleIndex: 0, styles: xStyles}
-
-	numFmt = xlsxNumFmt{NumFmtId: 0, FormatCode: "dd/mm/yy"}
-	numFmts[0] = numFmt
-	numFmtRefTable[0] = numFmt
-	c.Assert(cell.GetNumberFormat(), Equals, "dd/mm/yy")
+	_, _, xBorder, _, _ := style.makeXLSXStyleElements()
+	c.Assert(xBorder.Left.Style, Equals, "thin")
+	c.Assert(xBorder.Right.Style, Equals, "thin")
+	c.Assert(xBorder.Top.Style, Equals, "thin")
+	c.Assert(xBorder.Bottom.Style, Equals, "thin")
 }
 
 // We can return a string representation of the formatted data
 func (l *CellSuite) TestFormattedValue(c *C) {
-	var cell, earlyCell, negativeCell, smallCell *Cell
-	var cellXfs []xlsxXf
-	var numFmt xlsxNumFmt
-	var numFmts []xlsxNumFmt
-	var xStyles *xlsxStyles
-	var numFmtRefTable map[int]xlsxNumFmt
-
-	cellXfs = make([]xlsxXf, 1)
-	cellXfs[0] = xlsxXf{NumFmtId: 1}
-
-	numFmts = make([]xlsxNumFmt, 1)
-	numFmtRefTable = make(map[int]xlsxNumFmt)
-
-	xStyles = &xlsxStyles{NumFmts: numFmts, CellXfs: cellXfs}
-	cell = &Cell{Value: "37947.7500001", numFmtRefTable: numFmtRefTable, styleIndex: 0, styles: xStyles}
-	negativeCell = &Cell{Value: "-37947.7500001", numFmtRefTable: numFmtRefTable, styleIndex: 0, styles: xStyles}
-	smallCell = &Cell{Value: "0.007", numFmtRefTable: numFmtRefTable, styleIndex: 0, styles: xStyles}
-	earlyCell = &Cell{Value: "2.1", numFmtRefTable: numFmtRefTable, styleIndex: 0, styles: xStyles}
-	setCode := func(code string) {
-		numFmt = xlsxNumFmt{NumFmtId: 1, FormatCode: code}
-		numFmts[0] = numFmt
-		numFmtRefTable[1] = numFmt
-	}
-
-	setCode("general")
+	cell := Cell{Value: "37947.7500001"}
+	negativeCell := Cell{Value: "-37947.7500001"}
+	smallCell := Cell{Value: "0.007"}
+	earlyCell := Cell{Value: "2.1"}
+
+	cell.numFmt = "general"
 	c.Assert(cell.FormattedValue(), Equals, "37947.7500001")
+	negativeCell.numFmt = "general"
 	c.Assert(negativeCell.FormattedValue(), Equals, "-37947.7500001")
 
-	setCode("0")
+	cell.numFmt = "0"
 	c.Assert(cell.FormattedValue(), Equals, "37947")
 
-	setCode("#,##0") // For the time being we're not doing this
-	// comma formatting, so it'll fall back to
-	// the related non-comma form.
+	cell.numFmt = "#,##0" // For the time being we're not doing
+	// this comma formatting, so it'll fall back to the related
+	// non-comma form.
 	c.Assert(cell.FormattedValue(), Equals, "37947")
 
-	setCode("0.00")
+	cell.numFmt = "0.00"
 	c.Assert(cell.FormattedValue(), Equals, "37947.75")
 
-	setCode("#,##0.00") // For the time being we're not doing this
-	// comma formatting, so it'll fall back to
-	// the related non-comma form.
+	cell.numFmt = "#,##0.00" // For the time being we're not doing
+	// this comma formatting, so it'll fall back to the related
+	// non-comma form.
 	c.Assert(cell.FormattedValue(), Equals, "37947.75")
 
-	setCode("#,##0 ;(#,##0)")
+	cell.numFmt = "#,##0 ;(#,##0)"
 	c.Assert(cell.FormattedValue(), Equals, "37947")
+	negativeCell.numFmt = "#,##0 ;(#,##0)"
 	c.Assert(negativeCell.FormattedValue(), Equals, "(37947)")
 
-	setCode("#,##0 ;[red](#,##0)")
+	cell.numFmt = "#,##0 ;[red](#,##0)"
 	c.Assert(cell.FormattedValue(), Equals, "37947")
+	negativeCell.numFmt = "#,##0 ;[red](#,##0)"
 	c.Assert(negativeCell.FormattedValue(), Equals, "(37947)")
 
-	setCode("0%")
+	cell.numFmt = "0%"
 	c.Assert(cell.FormattedValue(), Equals, "3794775%")
 
-	setCode("0.00%")
+	cell.numFmt = "0.00%"
 	c.Assert(cell.FormattedValue(), Equals, "3794775.00%")
 
-	setCode("0.00e+00")
+	cell.numFmt = "0.00e+00"
 	c.Assert(cell.FormattedValue(), Equals, "3.794775e+04")
 
-	setCode("##0.0e+0") // This is wrong, but we'll use it for now.
+	cell.numFmt = "##0.0e+0" // This is wrong, but we'll use it for now.
 	c.Assert(cell.FormattedValue(), Equals, "3.794775e+04")
 
-	setCode("mm-dd-yy")
+	cell.numFmt = "mm-dd-yy"
 	c.Assert(cell.FormattedValue(), Equals, "11-22-03")
 
-	setCode("d-mmm-yy")
+	cell.numFmt = "d-mmm-yy"
 	c.Assert(cell.FormattedValue(), Equals, "22-Nov-03")
+	earlyCell.numFmt = "d-mmm-yy"
 	c.Assert(earlyCell.FormattedValue(), Equals, "1-Jan-00")
 
-	setCode("d-mmm")
+	cell.numFmt = "d-mmm"
 	c.Assert(cell.FormattedValue(), Equals, "22-Nov")
+	earlyCell.numFmt = "d-mmm"
 	c.Assert(earlyCell.FormattedValue(), Equals, "1-Jan")
 
-	setCode("mmm-yy")
+	cell.numFmt = "mmm-yy"
 	c.Assert(cell.FormattedValue(), Equals, "Nov-03")
 
-	setCode("h:mm am/pm")
+	cell.numFmt = "h:mm am/pm"
 	c.Assert(cell.FormattedValue(), Equals, "6:00 pm")
+	smallCell.numFmt = "h:mm am/pm"
 	c.Assert(smallCell.FormattedValue(), Equals, "12:14 am")
 
-	setCode("h:mm:ss am/pm")
+	cell.numFmt = "h:mm:ss am/pm"
 	c.Assert(cell.FormattedValue(), Equals, "6:00:00 pm")
+	smallCell.numFmt = "h:mm:ss am/pm"
 	c.Assert(smallCell.FormattedValue(), Equals, "12:14:47 am")
 
-	setCode("h:mm")
+	cell.numFmt = "h:mm"
 	c.Assert(cell.FormattedValue(), Equals, "18:00")
+	smallCell.numFmt = "h:mm"
 	c.Assert(smallCell.FormattedValue(), Equals, "00:14")
 
-	setCode("h:mm:ss")
+	cell.numFmt = "h:mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "18:00:00")
 	// This is wrong, but there's no eary way aroud it in Go right now, AFAICT.
+	smallCell.numFmt = "h:mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "00:14:47")
 
-	setCode("m/d/yy h:mm")
+	cell.numFmt = "m/d/yy h:mm"
 	c.Assert(cell.FormattedValue(), Equals, "11/22/03 18:00")
+	smallCell.numFmt = "m/d/yy h:mm"
 	c.Assert(smallCell.FormattedValue(), Equals, "12/30/99 00:14") // Note, that's 1899
-	c.Assert(earlyCell.FormattedValue(), Equals, "1/1/00 02:24")   // and 1900
+	earlyCell.numFmt = "m/d/yy h:mm"
+	c.Assert(earlyCell.FormattedValue(), Equals, "1/1/00 02:24") // and 1900
 
-	setCode("mm:ss")
+	cell.numFmt = "mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "00:00")
+	smallCell.numFmt = "mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "14:47")
 
-	setCode("[h]:mm:ss")
+	cell.numFmt = "[h]:mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "18:00:00")
+	smallCell.numFmt = "[h]:mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "14:47")
 
-	setCode("mmss.0") // I'm not sure about these.
+	cell.numFmt = "mmss.0" // I'm not sure about these.
 	c.Assert(cell.FormattedValue(), Equals, "00.8640")
+	smallCell.numFmt = "mmss.0"
 	c.Assert(smallCell.FormattedValue(), Equals, "1447.999997")
 
-	setCode("yyyy\\-mm\\-dd")
+	cell.numFmt = "yyyy\\-mm\\-dd"
 	c.Assert(cell.FormattedValue(), Equals, "2003\\-11\\-22")
 
-	setCode("dd/mm/yy")
+	cell.numFmt = "dd/mm/yy"
 	c.Assert(cell.FormattedValue(), Equals, "22/11/03")
+	earlyCell.numFmt = "dd/mm/yy"
 	c.Assert(earlyCell.FormattedValue(), Equals, "01/01/00")
 
-	setCode("hh:mm:ss")
+	cell.numFmt = "hh:mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "18:00:00")
+	smallCell.numFmt = "hh:mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "00:14:47")
 
-	setCode("dd/mm/yy\\ hh:mm")
+	cell.numFmt = "dd/mm/yy\\ hh:mm"
 	c.Assert(cell.FormattedValue(), Equals, "22/11/03\\ 18:00")
 
-	setCode("yy-mm-dd")
+	cell.numFmt = "yy-mm-dd"
 	c.Assert(cell.FormattedValue(), Equals, "03-11-22")
 
-	setCode("d-mmm-yyyy")
+	cell.numFmt = "d-mmm-yyyy"
 	c.Assert(cell.FormattedValue(), Equals, "22-Nov-2003")
+	earlyCell.numFmt = "d-mmm-yyyy"
 	c.Assert(earlyCell.FormattedValue(), Equals, "1-Jan-1900")
 
-	setCode("m/d/yy")
+	cell.numFmt = "m/d/yy"
 	c.Assert(cell.FormattedValue(), Equals, "11/22/03")
+	earlyCell.numFmt = "m/d/yy"
 	c.Assert(earlyCell.FormattedValue(), Equals, "1/1/00")
 
-	setCode("m/d/yyyy")
+	cell.numFmt = "m/d/yyyy"
 	c.Assert(cell.FormattedValue(), Equals, "11/22/2003")
+	earlyCell.numFmt = "m/d/yyyy"
 	c.Assert(earlyCell.FormattedValue(), Equals, "1/1/1900")
 
-	setCode("dd-mmm-yyyy")
+	cell.numFmt = "dd-mmm-yyyy"
 	c.Assert(cell.FormattedValue(), Equals, "22-Nov-2003")
 
-	setCode("dd/mm/yyyy")
+	cell.numFmt = "dd/mm/yyyy"
 	c.Assert(cell.FormattedValue(), Equals, "22/11/2003")
 
-	setCode("mm/dd/yy hh:mm am/pm")
+	cell.numFmt = "mm/dd/yy hh:mm am/pm"
 	c.Assert(cell.FormattedValue(), Equals, "11/22/03 06:00 pm")
 
-	setCode("mm/dd/yyyy hh:mm:ss")
+	cell.numFmt = "mm/dd/yyyy hh:mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "11/22/2003 18:00:00")
+	smallCell.numFmt = "mm/dd/yyyy hh:mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "12/30/1899 00:14:47")
 
-	setCode("yyyy-mm-dd hh:mm:ss")
+	cell.numFmt = "yyyy-mm-dd hh:mm:ss"
 	c.Assert(cell.FormattedValue(), Equals, "2003-11-22 18:00:00")
+	smallCell.numFmt = "yyyy-mm-dd hh:mm:ss"
 	c.Assert(smallCell.FormattedValue(), Equals, "1899-12-30 00:14:47")
 }

+ 2 - 1
file.go

@@ -144,8 +144,9 @@ func (f *File) MarshallParts() (map[string]string, error) {
 	workbook = f.makeWorkbook()
 	sheetIndex := 1
 
+	styles := &xlsxStyles{}
 	for _, sheet := range f.Sheets {
-		xSheet := sheet.makeXLSXSheet(refTable)
+		xSheet := sheet.makeXLSXSheet(refTable, styles)
 		rId := fmt.Sprintf("rId%d", sheetIndex)
 		sheetId := strconv.Itoa(sheetIndex)
 		sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)

+ 20 - 4
file_test.go

@@ -158,12 +158,15 @@ func (l *FileSuite) TestGetStyleFromZipFile(c *C) {
 	style = cellFoo.GetStyle()
 	c.Assert(cellFoo.String(), Equals, "Foo")
 	c.Assert(style.Fill.BgColor, Equals, "FF33CCCC")
+	c.Assert(style.ApplyFill, Equals, false)
+	c.Assert(style.ApplyFont, Equals, true)
 
 	row1 := tabelle1.Rows[1]
 	cellQuuk := row1.Cells[1]
 	style = cellQuuk.GetStyle()
 	c.Assert(cellQuuk.String(), Equals, "Quuk")
 	c.Assert(style.Border.Left, Equals, "thin")
+	c.Assert(style.ApplyBorder, Equals, true)
 
 	cellBar := row0.Cells[1]
 	c.Assert(cellBar.String(), Equals, "Bar")
@@ -281,7 +284,7 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	c.Assert(len(parts), Equals, 10)
 
 	// sheets
-	expectedSheet := `<?xml version="1.0" encoding="UTF-8"?>
+	expectedSheet1 := `<?xml version="1.0" encoding="UTF-8"?>
   <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
     <dimension ref="A1:A1"></dimension>
     <cols>
@@ -289,14 +292,27 @@ func (l *FileSuite) TestMarshalFile(c *C) {
     </cols>
     <sheetData>
       <row r="1">
-        <c r="A1" t="s">
+        <c r="A1" s="0" t="s">
           <v>0</v>
         </c>
       </row>
     </sheetData>
   </worksheet>`
-	c.Assert(parts["xl/worksheets/sheet1.xml"], Equals, expectedSheet)
-	c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet)
+	c.Assert(parts["xl/worksheets/sheet1.xml"], Equals, expectedSheet1)
+
+	expectedSheet2 := `<?xml version="1.0" encoding="UTF-8"?>
+  <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+    <dimension ref="A1:A1"></dimension>
+    <cols></cols>
+    <sheetData>
+      <row r="1">
+        <c r="A1" s="1" t="s">
+          <v>0</v>
+        </c>
+      </row>
+    </sheetData>
+  </worksheet>`
+	c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet2)
 
 	// .rels.xml
 	expectedRels := `<?xml version="1.0" encoding="UTF-8"?>

+ 4 - 3
lib.go

@@ -404,9 +404,10 @@ func readRowsFromSheet(Worksheet *xlsxWorksheet, file *File) ([]*Row, []*Col, in
 			}
 			cellX := insertColIndex - minCol
 			row.Cells[cellX].Value = getValueFromCellData(rawcell, reftable)
-			row.Cells[cellX].styleIndex = rawcell.S
-			row.Cells[cellX].styles = file.styles
-			row.Cells[cellX].numFmtRefTable = file.numFmtRefTable
+			if file.styles != nil {
+				row.Cells[cellX].style = file.styles.getStyle(rawcell.S)
+				row.Cells[cellX].numFmt = file.styles.getNumberFormat(rawcell.S, file.numFmtRefTable)
+			}
 			row.Cells[cellX].date1904 = file.Date1904
 			row.Cells[cellX].Hidden = rawrow.Hidden || cols[cellX].Hidden
 			insertColIndex++

+ 18 - 1
sheet.go

@@ -51,7 +51,7 @@ func (sh *Sheet) Cell(row, col int) *Cell {
 }
 
 // Dump sheet to it's XML representation, intended for internal use only
-func (s *Sheet) makeXLSXSheet(refTable *RefTable) *xlsxWorksheet {
+func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyles) *xlsxWorksheet {
 	worksheet := &xlsxWorksheet{}
 	xSheet := xlsxSheetData{}
 	maxRow := 0
@@ -63,6 +63,22 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable) *xlsxWorksheet {
 		xRow := xlsxRow{}
 		xRow.R = r + 1
 		for c, cell := range row.Cells {
+			style := cell.GetStyle()
+			xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
+			fontId := styles.addFont(xFont)
+			fillId := styles.addFill(xFill)
+			borderId := styles.addBorder(xBorder)
+			xCellStyleXf.FontId = fontId
+			xCellStyleXf.FillId = fillId
+			xCellStyleXf.BorderId = borderId
+			xCellXf.FontId = fontId
+			xCellXf.FillId = fillId
+			xCellXf.BorderId = borderId
+			styleXfId := styles.addCellStyleXf(xCellStyleXf)
+			XfId := styles.addCellXf(xCellXf)
+			if styleXfId != XfId {
+				panic("StyleXFId != XfId, this should never happen.")
+			}
 			if c > maxCell {
 				maxCell = c
 			}
@@ -70,6 +86,7 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable) *xlsxWorksheet {
 			xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r+1)
 			xC.V = strconv.Itoa(refTable.AddString(cell.Value))
 			xC.T = "s" // Hardcode string type, for now.
+			xC.S = XfId
 			xRow.C = append(xRow.C, xC)
 		}
 		xSheet.Row = append(xSheet.Row, xRow)

+ 74 - 3
sheet_test.go

@@ -28,7 +28,8 @@ func (s *SheetSuite) TestMakeXLSXSheetFromRows(c *C) {
 	cell := row.AddCell()
 	cell.Value = "A cell!"
 	refTable := NewSharedStringRefTable()
-	xSheet := sheet.makeXLSXSheet(refTable)
+	styles := &xlsxStyles{}
+	xSheet := sheet.makeXLSXSheet(refTable, styles)
 	c.Assert(xSheet.Dimension.Ref, Equals, "A1:A1")
 	c.Assert(xSheet.SheetData.Row, HasLen, 1)
 	xRow := xSheet.SheetData.Row[0]
@@ -48,6 +49,75 @@ func (s *SheetSuite) TestMakeXLSXSheetFromRows(c *C) {
 	c.Assert(xSI.T, Equals, "A cell!")
 }
 
+// When we create the xlsxSheet we also populate the xlsxStyles struct
+// with style information.
+func (s *SheetSuite) TestMakeXLSXSheetAlsoPopulatesXLSXSTyles(c *C) {
+	file := NewFile()
+	sheet := file.AddSheet("Sheet1")
+	row := sheet.AddRow()
+
+	cell1 := row.AddCell()
+	cell1.Value = "A cell!"
+	style1 := *NewStyle()
+	style1.Font = *NewFont(10, "Verdana")
+	style1.Fill = *NewFill("solid", "FFFFFFFF", "00000000")
+	style1.Border = *NewBorder("none", "thin", "none", "thin")
+	cell1.SetStyle(style1)
+
+	// We need a second style to check that Xfs are populated correctly.
+	cell2 := row.AddCell()
+	cell2.Value = "Another cell!"
+	style2 := *NewStyle()
+	style2.Font = *NewFont(10, "Verdana")
+	style2.Fill = *NewFill("solid", "FFFFFFFF", "00000000")
+	style2.Border = *NewBorder("none", "thin", "none", "thin")
+	cell2.SetStyle(style2)
+
+	refTable := NewSharedStringRefTable()
+	styles := &xlsxStyles{}
+	worksheet := sheet.makeXLSXSheet(refTable, styles)
+
+	c.Assert(len(styles.Fonts), Equals, 2)
+	c.Assert(styles.Fonts[0].Sz.Val, Equals, "10")
+	c.Assert(styles.Fonts[0].Name.Val, Equals, "Verdana")
+
+	c.Assert(len(styles.Fills), Equals, 2)
+	c.Assert(styles.Fills[0].PatternFill.PatternType, Equals, "solid")
+	c.Assert(styles.Fills[0].PatternFill.FgColor.RGB, Equals, "FFFFFFFF")
+	c.Assert(styles.Fills[0].PatternFill.BgColor.RGB, Equals, "00000000")
+
+	c.Assert(len(styles.Borders), Equals, 2)
+	c.Assert(styles.Borders[0].Left.Style, Equals, "none")
+	c.Assert(styles.Borders[0].Right.Style, Equals, "thin")
+	c.Assert(styles.Borders[0].Top.Style, Equals, "none")
+	c.Assert(styles.Borders[0].Bottom.Style, Equals, "thin")
+
+	c.Assert(len(styles.CellStyleXfs), Equals, 2)
+	// The 0th CellStyleXf could just be getting the zero values by default
+	c.Assert(styles.CellStyleXfs[0].FontId, Equals, 0)
+	c.Assert(styles.CellStyleXfs[0].FillId, Equals, 0)
+	c.Assert(styles.CellStyleXfs[0].BorderId, Equals, 0)
+	// The 1st element cannot get initialised this way by accident.
+	c.Assert(styles.CellStyleXfs[1].FontId, Equals, 1)
+	c.Assert(styles.CellStyleXfs[1].FillId, Equals, 1)
+	c.Assert(styles.CellStyleXfs[1].BorderId, Equals, 1)
+
+	c.Assert(len(styles.CellXfs), Equals, 2)
+	c.Assert(styles.CellXfs[0].FontId, Equals, 0)
+	c.Assert(styles.CellXfs[0].FillId, Equals, 0)
+	c.Assert(styles.CellXfs[0].BorderId, Equals, 0)
+	// As above, we need the 1st element to make the test fail
+	// when it should.
+	c.Assert(styles.CellXfs[1].FontId, Equals, 1)
+	c.Assert(styles.CellXfs[1].FillId, Equals, 1)
+	c.Assert(styles.CellXfs[1].BorderId, Equals, 1)
+
+	// Finally we check that the cell points to the right CellXf /
+	// CellStyleXf.
+	c.Assert(worksheet.SheetData.Row[0].C[0].S, Equals, 0)
+	c.Assert(worksheet.SheetData.Row[0].C[1].S, Equals, 1)
+}
+
 func (s *SheetSuite) TestMarshalSheet(c *C) {
 	file := NewFile()
 	sheet := file.AddSheet("Sheet1")
@@ -55,7 +125,8 @@ func (s *SheetSuite) TestMarshalSheet(c *C) {
 	cell := row.AddCell()
 	cell.Value = "A cell!"
 	refTable := NewSharedStringRefTable()
-	xSheet := sheet.makeXLSXSheet(refTable)
+	styles := &xlsxStyles{}
+	xSheet := sheet.makeXLSXSheet(refTable, styles)
 
 	output := bytes.NewBufferString(xml.Header)
 	body, err := xml.MarshalIndent(xSheet, "  ", "  ")
@@ -71,7 +142,7 @@ func (s *SheetSuite) TestMarshalSheet(c *C) {
     </cols>
     <sheetData>
       <row r="1">
-        <c r="A1" t="s">
+        <c r="A1" s="0" t="s">
           <v>0</v>
         </c>
       </row>

+ 42 - 3
style.go

@@ -1,17 +1,52 @@
 package xlsx
 
+import "strconv"
+
 // Style is a high level structure intended to provide user access to
 // the contents of Style within an XLSX file.
 type Style struct {
-	Border Border
-	Fill   Fill
-	Font   Font
+	Border      Border
+	Fill        Fill
+	Font        Font
+	ApplyBorder bool
+	ApplyFill   bool
+	ApplyFont   bool
 }
 
+// Return a new Style structure initialised with the default values.
 func NewStyle() *Style {
 	return &Style{}
 }
 
+// Generate the underlying XLSX style elements that correspond to the Style.
+func (style *Style) makeXLSXStyleElements() (xFont xlsxFont, xFill xlsxFill, xBorder xlsxBorder, xCellStyleXf xlsxXf, xCellXf xlsxXf) {
+	xFont = xlsxFont{}
+	xFill = xlsxFill{}
+	xBorder = xlsxBorder{}
+	xCellStyleXf = xlsxXf{}
+	xCellXf = xlsxXf{}
+	xFont.Sz.Val = strconv.Itoa(style.Font.Size)
+	xFont.Name.Val = style.Font.Name
+	xFont.Family.Val = strconv.Itoa(style.Font.Family)
+	xFont.Charset.Val = strconv.Itoa(style.Font.Charset)
+	xPatternFill := xlsxPatternFill{}
+	xPatternFill.PatternType = style.Fill.PatternType
+	xPatternFill.FgColor.RGB = style.Fill.FgColor
+	xPatternFill.BgColor.RGB = style.Fill.BgColor
+	xFill.PatternFill = xPatternFill
+	xBorder.Left = xlsxLine{Style: style.Border.Left}
+	xBorder.Right = xlsxLine{Style: style.Border.Right}
+	xBorder.Top = xlsxLine{Style: style.Border.Top}
+	xBorder.Bottom = xlsxLine{Style: style.Border.Bottom}
+	xCellXf.ApplyBorder = style.ApplyBorder
+	xCellXf.ApplyFill = style.ApplyFill
+	xCellXf.ApplyFont = style.ApplyFont
+	xCellStyleXf.ApplyBorder = style.ApplyBorder
+	xCellStyleXf.ApplyFill = style.ApplyFill
+	xCellStyleXf.ApplyFont = style.ApplyFont
+	return
+}
+
 // Border is a high level structure intended to provide user access to
 // the contents of Border Style within an Sheet.
 type Border struct {
@@ -21,6 +56,10 @@ type Border struct {
 	Bottom string
 }
 
+func NewBorder(left, right, top, bottom string) *Border {
+	return &Border{Left: left, Right: right, Top: top, Bottom: bottom}
+}
+
 // Fill is a high level structure intended to provide user access to
 // the contents of background and foreground color index within an Sheet.
 type Fill struct {

+ 30 - 0
style_test.go

@@ -13,6 +13,36 @@ func (s *StyleSuite) TestNewStyle(c *C) {
 	c.Assert(style, NotNil)
 }
 
+func (s *StyleSuite) TestMakeXLSXStyleElements(c *C) {
+	style := NewStyle()
+	font := *NewFont(12, "Verdana")
+	style.Font = font
+	fill := *NewFill("solid", "00FF0000", "FF000000")
+	style.Fill = fill
+	border := *NewBorder("thin", "thin", "thin", "thin")
+	style.Border = border
+	style.ApplyBorder = true
+	style.ApplyFill = true
+	style.ApplyFont = true
+	xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
+	c.Assert(xFont.Sz.Val, Equals, "12")
+	c.Assert(xFont.Name.Val, Equals, "Verdana")
+	c.Assert(xFill.PatternFill.PatternType, Equals, "solid")
+	c.Assert(xFill.PatternFill.FgColor.RGB, Equals, "00FF0000")
+	c.Assert(xFill.PatternFill.BgColor.RGB, Equals, "FF000000")
+	c.Assert(xBorder.Left.Style, Equals, "thin")
+	c.Assert(xBorder.Right.Style, Equals, "thin")
+	c.Assert(xBorder.Top.Style, Equals, "thin")
+	c.Assert(xBorder.Bottom.Style, Equals, "thin")
+	c.Assert(xCellStyleXf.ApplyBorder, Equals, true)
+	c.Assert(xCellStyleXf.ApplyFill, Equals, true)
+	c.Assert(xCellStyleXf.ApplyFont, Equals, true)
+	c.Assert(xCellXf.ApplyBorder, Equals, true)
+	c.Assert(xCellXf.ApplyFill, Equals, true)
+	c.Assert(xCellXf.ApplyFont, Equals, true)
+
+}
+
 type FontSuite struct{}
 
 var _ = Suite(&FontSuite{})

+ 92 - 0
xmlStyle.go

@@ -7,6 +7,11 @@
 
 package xlsx
 
+import (
+	"strconv"
+	"strings"
+)
+
 // xlsxStyle directly maps the style element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
@@ -119,3 +124,90 @@ type xlsxAlignment struct {
 	Vertical     string `xml:"vertical,attr"`
 	WrapText     bool   `xml:"wrapText,attr"`
 }
+
+func (styles *xlsxStyles) getStyle(styleIndex int) (style Style) {
+	var styleXf xlsxXf
+	style = Style{}
+	style.Border = Border{}
+	style.Fill = Fill{}
+	style.Font = Font{}
+
+	xfCount := len(styles.CellXfs)
+	if styleIndex > -1 && xfCount > 0 && styleIndex <= xfCount {
+		xf := styles.CellXfs[styleIndex]
+
+		// Google docs can produce output that has fewer
+		// CellStyleXfs than CellXfs - this copes with that.
+		if styleIndex < len(styles.CellStyleXfs) {
+			styleXf = styles.CellStyleXfs[styleIndex]
+		} else {
+			styleXf = xlsxXf{}
+		}
+
+		style.ApplyBorder = xf.ApplyBorder || styleXf.ApplyBorder
+		style.ApplyFill = xf.ApplyFill || styleXf.ApplyFill
+		style.ApplyFont = xf.ApplyFont || styleXf.ApplyFont
+
+		if xf.BorderId > -1 && xf.BorderId < len(styles.Borders) {
+			style.Border.Left = styles.Borders[xf.BorderId].Left.Style
+			style.Border.Right = styles.Borders[xf.BorderId].Right.Style
+			style.Border.Top = styles.Borders[xf.BorderId].Top.Style
+			style.Border.Bottom = styles.Borders[xf.BorderId].Bottom.Style
+		}
+
+		if xf.FillId > -1 && xf.FillId < len(styles.Fills) {
+			xFill := styles.Fills[xf.FillId]
+			style.Fill.PatternType = xFill.PatternFill.PatternType
+			style.Fill.FgColor = xFill.PatternFill.FgColor.RGB
+			style.Fill.BgColor = xFill.PatternFill.BgColor.RGB
+		}
+
+		if xf.FontId > -1 && xf.FontId < len(styles.Fonts) {
+			xfont := styles.Fonts[xf.FontId]
+			style.Font.Size, _ = strconv.Atoi(xfont.Sz.Val)
+			style.Font.Name = xfont.Name.Val
+			style.Font.Family, _ = strconv.Atoi(xfont.Family.Val)
+			style.Font.Charset, _ = strconv.Atoi(xfont.Charset.Val)
+		}
+	}
+	return style
+
+}
+
+func (styles *xlsxStyles) getNumberFormat(styleIndex int, numFmtRefTable map[int]xlsxNumFmt) string {
+	if styles.CellXfs == nil {
+		return ""
+	}
+	var numberFormat string = ""
+	if styleIndex > -1 && styleIndex <= len(styles.CellXfs) {
+		xf := styles.CellXfs[styleIndex]
+		numFmt := numFmtRefTable[xf.NumFmtId]
+		numberFormat = numFmt.FormatCode
+	}
+	return strings.ToLower(numberFormat)
+}
+
+func (styles *xlsxStyles) addFont(xFont xlsxFont) int {
+	styles.Fonts = append(styles.Fonts, xFont)
+	return len(styles.Fonts) - 1
+}
+
+func (styles *xlsxStyles) addFill(xFill xlsxFill) int {
+	styles.Fills = append(styles.Fills, xFill)
+	return len(styles.Fills) - 1
+}
+
+func (styles *xlsxStyles) addBorder(xBorder xlsxBorder) int {
+	styles.Borders = append(styles.Borders, xBorder)
+	return len(styles.Borders) - 1
+}
+
+func (styles *xlsxStyles) addCellStyleXf(xCellStyleXf xlsxXf) int {
+	styles.CellStyleXfs = append(styles.CellStyleXfs, xCellStyleXf)
+	return len(styles.CellStyleXfs) - 1
+}
+
+func (styles *xlsxStyles) addCellXf(xCellXf xlsxXf) int {
+	styles.CellXfs = append(styles.CellXfs, xCellXf)
+	return len(styles.CellXfs) - 1
+}

+ 4 - 4
xmlWorksheet.go

@@ -66,8 +66,8 @@ type xlsxRow struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxC struct {
-	R string `xml:"r,attr"`           // Cell ID, e.g. A1
-	S int    `xml:"s,attr,omitempty"` // Style reference.
-	T string `xml:"t,attr"`           // Type.
-	V string `xml:"v"`                // Value
+	R string `xml:"r,attr"` // Cell ID, e.g. A1
+	S int    `xml:"s,attr"` // Style reference.
+	T string `xml:"t,attr"` // Type.
+	V string `xml:"v"`      // Value
 }