Browse Source

add style to Col
add default style to Col
add Col.SetType(cellType CellType)
add sheet.Col(idx int)
remove default style from Cell
Cell will be follow Col's style if don't have any style

blackss2 10 years ago
parent
commit
8678f3a787
6 changed files with 232 additions and 69 deletions
  1. 88 10
      cell.go
  2. 31 1
      col.go
  3. 93 51
      sheet.go
  4. 6 1
      style.go
  5. 8 0
      xmlStyle.go
  6. 6 6
      xmlWorksheet.go

+ 88 - 10
cell.go

@@ -5,6 +5,7 @@ import (
 	"math"
 	"strconv"
 	"strings"
+	"time"
 )
 
 // CellType is an int type for storing metadata about the data type in the cell.
@@ -18,6 +19,8 @@ const (
 	CellTypeBool
 	CellTypeInline
 	CellTypeError
+	CellTypeDate
+	CellTypeGeneral
 )
 
 // Cell is a high level structure intended to provide user access to
@@ -43,7 +46,7 @@ type CellInterface interface {
 
 // NewCell creates a cell and adds it to a row.
 func NewCell(r *Row) *Cell {
-	return &Cell{style: NewStyle(), Row: r}
+	return &Cell{Row: r}
 }
 
 // Merge with other cells, horizontally and/or vertically.
@@ -71,7 +74,7 @@ func (c *Cell) String() string {
 
 // SetFloat sets the value of a cell to a float.
 func (c *Cell) SetFloat(n float64) {
-	c.SetFloatWithFormat(n, "general")
+	c.SetFloatWithFormat(n, builtInNumFmt[builtInNumFmtIndex_FLOAT])
 }
 
 /*
@@ -100,6 +103,36 @@ func (c *Cell) SetFloatWithFormat(n float64, format string) {
 	c.cellType = CellTypeNumeric
 }
 
+var timeLocationUTC *time.Location
+
+func init() {
+	timeLocationUTC, _ = time.LoadLocation("UTC")
+}
+
+func timeToUTCTime(t time.Time) time.Time {
+	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
+}
+
+func timeToExcelTime(t time.Time) float64 {
+	return float64(t.Unix())/86400.0 + 25569.0
+}
+
+// SetDate sets the value of a cell to a float.
+func (c *Cell) SetDate(t time.Time) {
+	c.SetDateTimeWithFormat(float64(int64(timeToExcelTime(timeToUTCTime(t)))), builtInNumFmt[14])
+}
+
+func (c *Cell) SetDateTime(t time.Time) {
+	c.SetDateTimeWithFormat(timeToExcelTime(timeToUTCTime(t)), builtInNumFmt[14])
+}
+
+func (c *Cell) SetDateTimeWithFormat(n float64, format string) {
+	c.Value = strconv.FormatFloat(n, 'f', -1, 64)
+	c.numFmt = format
+	c.formula = ""
+	c.cellType = CellTypeDate
+}
+
 // Float returns the value of cell as a number.
 func (c *Cell) Float() (float64, error) {
 	f, err := strconv.ParseFloat(c.Value, 64)
@@ -112,7 +145,7 @@ func (c *Cell) Float() (float64, error) {
 // SetInt64 sets a cell's value to a 64-bit integer.
 func (c *Cell) SetInt64(n int64) {
 	c.Value = fmt.Sprintf("%d", n)
-	c.numFmt = "0"
+	c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
 	c.formula = ""
 	c.cellType = CellTypeNumeric
 }
@@ -129,11 +162,53 @@ func (c *Cell) Int64() (int64, error) {
 // SetInt sets a cell's value to an integer.
 func (c *Cell) SetInt(n int) {
 	c.Value = fmt.Sprintf("%d", n)
-	c.numFmt = "0"
+	c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
 	c.formula = ""
 	c.cellType = CellTypeNumeric
 }
 
+// SetInt sets a cell's value to an integer.
+func (c *Cell) SetValue(n interface{}) {
+	var s string
+	switch n.(type) {
+	case time.Time:
+		c.SetDateTime(n.(time.Time))
+		return
+	case int:
+		c.setGeneral(fmt.Sprintf("%v", n))
+		return
+	case int32:
+		c.setGeneral(fmt.Sprintf("%v", n))
+		return
+	case int64:
+		c.setGeneral(fmt.Sprintf("%v", n))
+		return
+	case float32:
+		c.setGeneral(fmt.Sprintf("%v", n))
+		return
+	case float64:
+		c.setGeneral(fmt.Sprintf("%v", n))
+		return
+	case string:
+		s = n.(string)
+	case []byte:
+		s = string(n.([]byte))
+	case nil:
+		s = ""
+	default:
+		s = fmt.Sprintf("%v", n)
+	}
+	c.SetString(s)
+}
+
+// SetInt sets a cell's value to an integer.
+func (c *Cell) setGeneral(s string) {
+	c.Value = s
+	c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
+	c.formula = ""
+	c.cellType = CellTypeGeneral
+}
+
 // Int returns the value of cell as integer.
 // Has max 53 bits of precision
 // See: float64(int64(math.MaxInt))
@@ -184,6 +259,9 @@ func (c *Cell) Formula() string {
 
 // GetStyle returns the Style associated with a Cell
 func (c *Cell) GetStyle() *Style {
+	if c.style == nil {
+		c.style = NewStyle()
+	}
 	return c.style
 }
 
@@ -222,11 +300,11 @@ func (c *Cell) FormattedValue() string {
 		return parseTime(c)
 	}
 	switch numberFormat {
-	case "general", "@":
+	case builtInNumFmt[builtInNumFmtIndex_GENERAL], builtInNumFmt[builtInNumFmtIndex_STRING]:
 		return c.Value
-	case "0", "#,##0":
+	case builtInNumFmt[builtInNumFmtIndex_INT], "#,##0":
 		return c.formatToInt("%d")
-	case "0.00", "#,##0.00":
+	case builtInNumFmt[builtInNumFmtIndex_FLOAT], "#,##0.00":
 		return c.formatToFloat("%.2f")
 	case "#,##0 ;(#,##0)", "#,##0 ;[red](#,##0)":
 		f, err := strconv.ParseFloat(c.Value, 64)
@@ -278,9 +356,9 @@ func parseTime(c *Cell) string {
 	format := c.GetNumberFormat()
 	// Replace Excel placeholders with Go time placeholders.
 	// For example, replace yyyy with 2006. These are in a specific order,
-    // due to the fact that m is used in month, minute, and am/pm. It would
-    // be easier to fix that with regular expressions, but if it's possible
-    // to keep this simple it would be easier to maintain.
+	// due to the fact that m is used in month, minute, and am/pm. It would
+	// be easier to fix that with regular expressions, but if it's possible
+	// to keep this simple it would be easier to maintain.
 	replacements := []struct{ xltime, gotime string }{
 		{"yyyy", "2006"},
 		{"yy", "06"},

+ 31 - 1
col.go

@@ -9,5 +9,35 @@ type Col struct {
 	Hidden    bool
 	Width     float64
 	Collapsed bool
-	//	Style     int
+	numFmt    string
+	style     *Style
+}
+
+func (c *Col) SetType(cellType CellType) {
+	switch cellType {
+	case CellTypeString:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
+	case CellTypeBool:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
+	case CellTypeNumeric:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
+	case CellTypeDate:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_DATE]
+	case CellTypeFormula:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
+	case CellTypeError:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
+	case CellTypeGeneral:
+		c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
+	}
+}
+
+// GetStyle returns the Style associated with a Col
+func (c *Col) GetStyle() *Style {
+	return c.style
+}
+
+// SetStyle sets the style of a Col
+func (c *Col) SetStyle(style *Style) {
+	c.style = style
 }

+ 93 - 51
sheet.go

@@ -50,17 +50,23 @@ func (s *Sheet) AddRow() *Row {
 func (s *Sheet) maybeAddCol(cellCount int) {
 	if cellCount > s.MaxCol {
 		col := &Col{
+			style:     NewStyle(),
 			Min:       cellCount,
 			Max:       cellCount,
 			Hidden:    false,
 			Collapsed: false,
-			// Style:     0,
-			Width: ColWidth}
+			Width:     ColWidth}
 		s.Cols = append(s.Cols, col)
 		s.MaxCol = cellCount
 	}
 }
 
+// Make sure we always have as many Cols as we do cells.
+func (s *Sheet) Col(idx int) *Col {
+	s.maybeAddCol(idx + 1)
+	return s.Cols[idx]
+}
+
 // Get a Cell by passing it's cartesian coordinates (zero based) as
 // row and column integer indexes.
 //
@@ -84,12 +90,12 @@ func (s *Sheet) SetColWidth(startcol, endcol int, width float64) error {
 		return fmt.Errorf("Could not set width for range %d-%d: startcol must be less than endcol.", startcol, endcol)
 	}
 	col := &Col{
+		style:     NewStyle(),
 		Min:       startcol + 1,
 		Max:       endcol + 1,
 		Hidden:    false,
 		Collapsed: false,
-		// Style:     0,
-		Width: width}
+		Width:     width}
 	s.Cols = append(s.Cols, col)
 	if endcol+1 > s.MaxCol {
 		s.MaxCol = endcol + 1
@@ -103,7 +109,33 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 	xSheet := xlsxSheetData{}
 	maxRow := 0
 	maxCell := 0
-	XfId := 0
+
+	colsXfIdList := make([]int, len(s.Cols))
+	worksheet.Cols = xlsxCols{Col: []xlsxCol{}}
+	for c, col := range s.Cols {
+		XfId := 0
+
+		style := col.GetStyle()
+		//col's style always not nil
+		if style != nil {
+			xNumFmt := styles.newNumFmt(col.numFmt)
+			XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
+		}
+		colsXfIdList[c] = XfId
+
+		if col.Width == 0 {
+			col.Width = ColWidth
+		}
+		worksheet.Cols.Col = append(worksheet.Cols.Col,
+			xlsxCol{Min: col.Min,
+				Max:       col.Max,
+				Hidden:    col.Hidden,
+				Width:     col.Width,
+				Collapsed: col.Collapsed,
+				Style:     XfId,
+			})
+	}
+
 	for r, row := range s.Rows {
 		if r > maxRow {
 			maxRow = r
@@ -115,41 +147,18 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 			xRow.Ht = fmt.Sprintf("%g", row.Height)
 		}
 		for c, cell := range row.Cells {
-			style := cell.GetStyle()
-			if style != nil {
-				xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
-				fontId := styles.addFont(xFont)
-				fillId := styles.addFill(xFill)
-				// generate NumFmtId and add new NumFmt
-				xNumFmt := styles.newNumFmt(cell.numFmt)
-
-				// HACK - adding light grey fill, as in OO and Google
-				greyfill := xlsxFill{}
-				greyfill.PatternFill.PatternType = "lightGrey"
-				styles.addFill(greyfill)
-
-				borderId := styles.addBorder(xBorder)
-				xCellStyleXf.FontId = fontId
-				xCellStyleXf.FillId = fillId
-				xCellStyleXf.BorderId = borderId
-				xCellStyleXf.NumFmtId = 0 // General
-				xCellXf.FontId = fontId
-				xCellXf.FillId = fillId
-				xCellXf.BorderId = borderId
-				xCellXf.NumFmtId = xNumFmt.NumFmtId
-				// apply the numFmtId when it is not the default cellxf
-				if xCellXf.NumFmtId > 0 {
-					xCellXf.ApplyNumberFormat = true
-				}
+			XfId := colsXfIdList[c]
 
-				xCellStyleXf.Alignment.Horizontal = style.Alignment.Horizontal
-				xCellStyleXf.Alignment.Vertical = style.Alignment.Vertical
-				xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
-				xCellXf.Alignment.Vertical = style.Alignment.Vertical
+			// generate NumFmtId and add new NumFmt
+			xNumFmt := styles.newNumFmt(cell.numFmt)
 
-				styles.addCellStyleXf(xCellStyleXf)
-				XfId = styles.addCellXf(xCellXf)
+			style := cell.style
+			if style != nil {
+				XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
+			} else if len(cell.numFmt) > 0 && s.Cols[c].numFmt != cell.numFmt {
+				XfId = handleNumFmtIdForXLSX(xNumFmt.NumFmtId, styles)
 			}
+
 			if c > maxCell {
 				maxCell = c
 			}
@@ -167,6 +176,9 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 			case CellTypeNumeric:
 				xC.V = cell.Value
 				xC.S = XfId
+			case CellTypeDate:
+				xC.V = cell.Value
+				xC.S = XfId
 			case CellTypeFormula:
 				xC.V = cell.Value
 				xC.F = &xlsxF{Content: cell.formula}
@@ -176,6 +188,9 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 				xC.F = &xlsxF{Content: cell.formula}
 				xC.T = "e"
 				xC.S = XfId
+			case CellTypeGeneral:
+				xC.V = cell.Value
+				xC.S = XfId
 			}
 			xRow.C = append(xRow.C, xC)
 
@@ -200,20 +215,6 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 		worksheet.MergeCells.Count = len(worksheet.MergeCells.Cells)
 	}
 
-	worksheet.Cols = xlsxCols{Col: []xlsxCol{}}
-	for _, col := range s.Cols {
-		if col.Width == 0 {
-			col.Width = ColWidth
-		}
-		worksheet.Cols.Col = append(worksheet.Cols.Col,
-			xlsxCol{Min: col.Min,
-				Max:       col.Max,
-				Hidden:    col.Hidden,
-				Width:     col.Width,
-				Collapsed: col.Collapsed,
-				// Style:     col.Style
-			})
-	}
 	worksheet.SheetData = xSheet
 	dimension := xlsxDimension{}
 	dimension.Ref = fmt.Sprintf("A1:%s%d",
@@ -224,3 +225,44 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 	worksheet.Dimension = dimension
 	return worksheet
 }
+
+func handleStyleForXLSX(style *Style, NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
+	xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
+	fontId := styles.addFont(xFont)
+	fillId := styles.addFill(xFill)
+
+	// HACK - adding light grey fill, as in OO and Google
+	greyfill := xlsxFill{}
+	greyfill.PatternFill.PatternType = "lightGrey"
+	styles.addFill(greyfill)
+
+	borderId := styles.addBorder(xBorder)
+	xCellStyleXf.FontId = fontId
+	xCellStyleXf.FillId = fillId
+	xCellStyleXf.BorderId = borderId
+	xCellStyleXf.NumFmtId = builtInNumFmtIndex_GENERAL
+	xCellXf.FontId = fontId
+	xCellXf.FillId = fillId
+	xCellXf.BorderId = borderId
+	xCellXf.NumFmtId = NumFmtId
+	// apply the numFmtId when it is not the default cellxf
+	if xCellXf.NumFmtId > 0 {
+		xCellXf.ApplyNumberFormat = true
+	}
+
+	xCellStyleXf.Alignment.Horizontal = style.Alignment.Horizontal
+	xCellStyleXf.Alignment.Vertical = style.Alignment.Vertical
+	xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
+	xCellXf.Alignment.Vertical = style.Alignment.Vertical
+
+	styles.addCellStyleXf(xCellStyleXf)
+	XfId = styles.addCellXf(xCellXf)
+	return
+}
+
+func handleNumFmtIdForXLSX(NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
+	xCellXf := makeXLSXCellElement()
+	xCellXf.NumFmtId = NumFmtId
+	XfId = styles.addCellXf(xCellXf)
+	return
+}

+ 6 - 1
style.go

@@ -60,11 +60,11 @@ func (style *Style) makeXLSXStyleElements() (xFont xlsxFont, xFill xlsxFill, xBo
 	xBorder.Right = xlsxLine{Style: style.Border.Right}
 	xBorder.Top = xlsxLine{Style: style.Border.Top}
 	xBorder.Bottom = xlsxLine{Style: style.Border.Bottom}
+	xCellXf = makeXLSXCellElement()
 	xCellXf.ApplyBorder = style.ApplyBorder
 	xCellXf.ApplyFill = style.ApplyFill
 	xCellXf.ApplyFont = style.ApplyFont
 	xCellXf.ApplyAlignment = style.ApplyAlignment
-	xCellXf.NumFmtId = 0
 	xCellStyleXf.ApplyBorder = style.ApplyBorder
 	xCellStyleXf.ApplyFill = style.ApplyFill
 	xCellStyleXf.ApplyFont = style.ApplyFont
@@ -75,6 +75,11 @@ func (style *Style) makeXLSXStyleElements() (xFont xlsxFont, xFill xlsxFill, xBo
 	return
 }
 
+func makeXLSXCellElement() (xCellXf xlsxXf) {
+	xCellXf.NumFmtId = 0
+	return
+}
+
 // Border is a high level structure intended to provide user access to
 // the contents of Border Style within an Sheet.
 type Border struct {

+ 8 - 0
xmlStyle.go

@@ -57,6 +57,14 @@ var builtInNumFmt = map[int]string{
 	49: "@",
 }
 
+const (
+	builtInNumFmtIndex_GENERAL = int(0)
+	builtInNumFmtIndex_INT = int(1)
+	builtInNumFmtIndex_FLOAT = int(2)
+	builtInNumFmtIndex_DATE = int(14)
+	builtInNumFmtIndex_STRING = int(49)
+)
+
 // xlsxStyle directly maps the styleSheet element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much

+ 6 - 6
xmlWorksheet.go

@@ -192,12 +192,12 @@ type xlsxCols struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxCol struct {
-	Collapsed bool `xml:"collapsed,attr"`
-	Hidden    bool `xml:"hidden,attr"`
-	Max       int  `xml:"max,attr"`
-	Min       int  `xml:"min,attr"`
-	// Style     int     `xml:"style,attr"`
-	Width float64 `xml:"width,attr"`
+	Collapsed bool    `xml:"collapsed,attr"`
+	Hidden    bool    `xml:"hidden,attr"`
+	Max       int     `xml:"max,attr"`
+	Min       int     `xml:"min,attr"`
+	Style     int     `xml:"style,attr"`
+	Width     float64 `xml:"width,attr"`
 }
 
 // xlsxDimension directly maps the dimension element in the namespace