Browse Source

Merge branch 'master' into dev

Colin Fox 10 years ago
parent
commit
37c9921f5e
15 changed files with 340 additions and 151 deletions
  1. 14 5
      cell.go
  2. 36 0
      cell_test.go
  3. 9 0
      file.go
  4. 13 4
      file_test.go
  5. 20 11
      lib.go
  6. 13 2
      lib_test.go
  7. 23 11
      sheet.go
  8. 49 2
      sheet_test.go
  9. 3 3
      style.go
  10. BIN
      testdocs/testchartsheet.xlsx
  11. 98 83
      xmlStyle.go
  12. 36 14
      xmlStyle_test.go
  13. 16 9
      xmlWorkbook.go
  14. 7 6
      xmlWorksheet.go
  15. 3 1
      xmlWorksheet_test.go

+ 14 - 5
cell.go

@@ -88,10 +88,8 @@ func (c *Cell) SetFloat(n float64) {
 // SetFloatWithFormat sets the value of a cell to a float and applies
 // formatting to the cell.
 func (c *Cell) SetFloatWithFormat(n float64, format string) {
-	// tmp value. final value is formatted by FormattedValue() method
-	c.Value = fmt.Sprintf("%e", n)
+	c.Value = strconv.FormatFloat(n, 'e', -1, 64)
 	c.numFmt = format
-	c.Value = c.FormattedValue()
 	c.formula = ""
 	c.cellType = CellTypeNumeric
 }
@@ -155,7 +153,16 @@ func (c *Cell) SetBool(b bool) {
 // TODO: Determine if the current return value is
 // appropriate for types other than CellTypeBool.
 func (c *Cell) Bool() bool {
-	return c.Value == "1"
+	// If bool, just return the value.
+	if c.cellType == CellTypeBool {
+		return c.Value == "1"
+	}
+	// If numeric, base it on a non-zero.
+	if c.cellType == CellTypeNumeric {
+		return c.Value != "0"
+	}
+	// Return whether there's an empty string.
+	return c.Value != ""
 }
 
 // SetFormula sets the format string for a cell.
@@ -294,7 +301,7 @@ func (c *Cell) FormattedValue() string {
 		t := TimeFromExcelTime(f, c.date1904)
 		return fmt.Sprintf("%0d%0d.%d", t.Minute(), t.Second(), t.Nanosecond()/1000)
 
-	case "yyyy\\-mm\\-dd":
+	case "yyyy\\-mm\\-dd", "yyyy\\-mm\\-dd;@":
 		return c.formatToTime("2006\\-01\\-02")
 	case "dd/mm/yy":
 		return c.formatToTime("02/01/06")
@@ -304,6 +311,8 @@ func (c *Cell) FormattedValue() string {
 		return c.formatToTime("02/01/06\\ 15:04")
 	case "dd/mm/yyyy hh:mm:ss":
 		return c.formatToTime("02/01/2006 15:04:05")
+	case "yyyy/mm/dd":
+		return c.formatToTime("2006/01/02")
 	case "yy-mm-dd":
 		return c.formatToTime("06-01-02")
 	case "d-mmm-yyyy":

+ 36 - 0
cell_test.go

@@ -93,6 +93,15 @@ func (s *CellSuite) TestGetStyleWithBorders(c *C) {
 	c.Assert(xBorder.Bottom.Style, Equals, "thin")
 }
 
+// We can return a string representation of the formatted data
+func (l *CellSuite) TestSetFloatWithFormat(c *C) {
+	cell := Cell{}
+	cell.SetFloatWithFormat(37947.75334343, "yyyy/mm/dd")
+	c.Assert(cell.Value, Equals, "3.794775334343e+04")
+	c.Assert(cell.numFmt, Equals, "yyyy/mm/dd")
+	c.Assert(cell.Type(), Equals, CellTypeNumeric)
+}
+
 // We can return a string representation of the formatted data
 func (l *CellSuite) TestFormattedValue(c *C) {
 	cell := Cell{Value: "37947.7500001"}
@@ -221,6 +230,9 @@ func (l *CellSuite) TestFormattedValue(c *C) {
 	cell.numFmt = "dd/mm/yy\\ hh:mm"
 	c.Assert(cell.FormattedValue(), Equals, "22/11/03\\ 18:00")
 
+	cell.numFmt = "yyyy/mm/dd"
+	c.Assert(cell.FormattedValue(), Equals, "2003/11/22")
+
 	cell.numFmt = "yy-mm-dd"
 	c.Assert(cell.FormattedValue(), Equals, "03-11-22")
 
@@ -295,3 +307,27 @@ func (s *CellSuite) TestOddInput(c *C) {
 	cell.numFmt = "@"
 	c.Assert(cell.String(), Equals, odd)
 }
+
+// TestBool tests basic Bool getting and setting booleans.
+func (s *CellSuite) TestBool(c *C) {
+	cell := Cell{}
+	cell.SetBool(true)
+	c.Assert(cell.Value, Equals, "1")
+	c.Assert(cell.Bool(), Equals, true)
+	cell.SetBool(false)
+	c.Assert(cell.Value, Equals, "0")
+	c.Assert(cell.Bool(), Equals, false)
+}
+
+// TestStringBool tests calling Bool on a non CellTypeBool value.
+func (s *CellSuite) TestStringBool(c *C) {
+	cell := Cell{}
+	cell.SetInt(0)
+	c.Assert(cell.Bool(), Equals, false)
+	cell.SetInt(1)
+	c.Assert(cell.Bool(), Equals, true)
+	cell.SetString("")
+	c.Assert(cell.Bool(), Equals, false)
+	cell.SetString("0")
+	c.Assert(cell.Bool(), Equals, true)
+}

+ 9 - 0
file.go

@@ -7,6 +7,7 @@ import (
 	"io"
 	"os"
 	"strconv"
+	"strings"
 )
 
 // File is a high level structure providing a slice of Sheet structs
@@ -203,6 +204,14 @@ func (f *File) MarshallParts() (map[string]string, error) {
 		return parts, err
 	}
 
+	// Make it work with Mac Numbers.
+	// Dirty hack to fix issues #63 and #91; encoding/xml currently
+	// "doesn't allow for additional namespaces to be defined in the root element of the document,"
+	// as described by @tealeg in the comments for #63.
+	oldXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
+	newXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">`
+	parts["xl/workbook.xml"] = strings.Replace(parts["xl/workbook.xml"], oldXmlns, newXmlns, 1)
+
 	parts["_rels/.rels"] = TEMPLATE__RELS_DOT_RELS
 	parts["docProps/app.xml"] = TEMPLATE_DOCPROPS_APP
 	// TODO - do this properly, modification and revision information

+ 13 - 4
file_test.go

@@ -31,6 +31,12 @@ func (l *FileSuite) TestOpenFileWithoutStyleAndSharedStrings(c *C) {
 	c.Assert(xlsxFile, NotNil)
 }
 
+func (l *FileSuite) TestOpenFileWithChartsheet(c *C) {
+	xlsxFile, error := OpenFile("./testdocs/testchartsheet.xlsx")
+	c.Assert(error, IsNil)
+	c.Assert(xlsxFile, NotNil)
+}
+
 // Test that we can correctly extract a reference table from the
 // sharedStrings.xml file embedded in the XLSX file and return a
 // reference table of string values from it.
@@ -275,11 +281,11 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 
 	// sheets
 	expectedSheet1 := `<?xml version="1.0" encoding="UTF-8"?>
-<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" s="0" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
 	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"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" s="0" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
 	c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet2)
 
 	// .rels.xml
@@ -636,8 +642,11 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
 
 	// workbook.xml
+	// Note that the following XML snippet is just pasted in here to correspond to the hack
+	// added in file.go to support Apple Numbers so the test passes.
+	// `xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"`
 	expectedWorkbook := `<?xml version="1.0" encoding="UTF-8"?>
-<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fileVersion appName="Go XLSX"></fileVersion><workbookPr showObjects="all" date1904="false"></workbookPr><workbookProtection></workbookProtection><bookViews><workbookView showHorizontalScroll="true" showVerticalScroll="true" showSheetTabs="true" tabRatio="204" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"></workbookView></bookViews><sheets><sheet name="MySheet" sheetId="1" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId1" state="visible"></sheet><sheet name="AnotherSheet" sheetId="2" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId2" state="visible"></sheet></sheets><definedNames></definedNames><calcPr iterateCount="100" refMode="A1" iterateDelta="0.001"></calcPr></workbook>`
+<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><fileVersion appName="Go XLSX"></fileVersion><workbookPr showObjects="all" date1904="false"></workbookPr><workbookProtection></workbookProtection><bookViews><workbookView showHorizontalScroll="true" showVerticalScroll="true" showSheetTabs="true" tabRatio="204" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"></workbookView></bookViews><sheets><sheet name="MySheet" sheetId="1" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId1" state="visible"></sheet><sheet name="AnotherSheet" sheetId="2" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId2" state="visible"></sheet></sheets><definedNames></definedNames><calcPr iterateCount="100" refMode="A1" iterateDelta="0.001"></calcPr></workbook>`
 	c.Assert(parts["xl/workbook.xml"], Equals, expectedWorkbook)
 
 	// [Content_Types].xml
@@ -650,7 +659,7 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	// For now we only allow simple string data in the
 	// spreadsheet.  Style support will follow.
 	expectedStyles := `<?xml version="1.0" encoding="UTF-8"?>
-<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="1"><font><sz val="12"/><name val="Verdana"/><family val="0"/><charset val="0"/></font></fonts><fills count="2"><fill><patternFill patternType="none"><fgColor rgb="FFFFFFFF"/><bgColor rgb="00000000"/></patternFill></fill><fill><patternFill patternType="lightGrey"/></fill></fills><borders count="1"><border><left style="none"/><right style="none"/><top style="none"/><bottom style="none"/></border></borders><cellStyleXfs count="1"><xf applyAlignment="0" applyBorder="0" applyFont="0" applyFill="0" applyProtection="0" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="" indent="0" shrinkToFit="0" textRotation="0" vertical="" wrapText="0"/></xf></cellStyleXfs><cellXfs count="1"><xf applyAlignment="0" applyBorder="0" applyFont="0" applyFill="0" applyProtection="0" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="" indent="0" shrinkToFit="0" textRotation="0" vertical="" wrapText="0"/></xf></cellXfs></styleSheet>`
+<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="1"><font><sz val="12"/><name val="Verdana"/><family val="0"/><charset val="0"/></font></fonts><fills count="2"><fill><patternFill patternType="none"><fgColor rgb="FFFFFFFF"/><bgColor rgb="00000000"/></patternFill></fill><fill><patternFill patternType="lightGrey"/></fill></fills><borders count="1"><border><left style="none"/><right style="none"/><top style="none"/><bottom style="none"/></border></borders><cellStyleXfs count="1"><xf applyAlignment="0" applyBorder="0" applyFont="0" applyFill="0" applyNumberFormat="0" applyProtection="0" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="" indent="0" shrinkToFit="0" textRotation="0" vertical="" wrapText="0"/></xf></cellStyleXfs><cellXfs count="1"><xf applyAlignment="0" applyBorder="0" applyFont="0" applyFill="0" applyNumberFormat="0" applyProtection="0" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="" indent="0" shrinkToFit="0" textRotation="0" vertical="" wrapText="0"/></xf></cellXfs></styleSheet>`
 	c.Assert(parts["xl/styles.xml"], Equals, expectedStyles)
 }
 

+ 20 - 11
lib.go

@@ -320,6 +320,9 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
 	var res string
 
 	f := rawcell.F
+	if f == nil {
+		return ""
+	}
 	if f.T == "shared" {
 		x, y, err := getCoordsFromCellIDString(rawcell.R)
 		if err != nil {
@@ -494,7 +497,7 @@ func readRowsFromSheet(Worksheet *xlsxWorksheet, file *File) ([]*Row, []*Col, in
 			// from the data.
 			for x > insertColIndex {
 				// Put an empty Cell into the array
-				row.Cells[insertColIndex-minCol] = new(Cell)
+				row.Cells[insertColIndex] = new(Cell)
 				insertColIndex++
 			}
 			cellX := insertColIndex
@@ -561,6 +564,10 @@ func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *F
 	sheet.Rows, sheet.Cols, sheet.MaxCol, sheet.MaxRow = readRowsFromSheet(worksheet, fi)
 	sheet.Hidden = rsheet.State == sheetStateHidden || rsheet.State == sheetStateVeryHidden
 	sheet.SheetViews = readSheetViews(worksheet.SheetViews)
+
+	sheet.SheetFormat.DefaultColWidth = worksheet.SheetFormatPr.DefaultColWidth
+	sheet.SheetFormat.DefaultRowHeight = worksheet.SheetFormatPr.DefaultRowHeight
+
 	result.Sheet = sheet
 	sc <- result
 }
@@ -585,22 +592,24 @@ func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]strin
 		return nil, nil, err
 	}
 	file.Date1904 = workbook.WorkbookPr.Date1904
-	sheetCount = len(workbook.Sheets.Sheet)
+
+	// Only try and read sheets that have corresponding files.
+	// Notably this excludes chartsheets don't right now
+	var workbookSheets []xlsxSheet
+	for _, sheet := range workbook.Sheets.Sheet {
+		if f := worksheetFileForSheet(sheet, file.worksheets, sheetXMLMap); f != nil {
+			workbookSheets = append(workbookSheets, sheet)
+		}
+	}
+	sheetCount = len(workbookSheets)
 	sheetsByName := make(map[string]*Sheet, sheetCount)
 	sheets := make([]*Sheet, sheetCount)
 	sheetChan := make(chan *indexedSheet, sheetCount)
 	defer close(sheetChan)
 
 	go func() {
-		defer func() {
-			if e := recover(); e != nil {
-				err = fmt.Errorf("%v", e)
-				result := &indexedSheet{Index: -1, Sheet: nil, Error: err}
-				sheetChan <- result
-			}
-		}()
 		err = nil
-		for i, rawsheet := range workbook.Sheets.Sheet {
+		for i, rawsheet := range workbookSheets {
 			readSheetFromFile(sheetChan, i, rawsheet, file, sheetXMLMap)
 		}
 	}()
@@ -610,7 +619,7 @@ func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]strin
 		if sheet.Error != nil {
 			return nil, nil, sheet.Error
 		}
-		sheetName := workbook.Sheets.Sheet[sheet.Index].Name
+		sheetName := workbookSheets[sheet.Index].Name
 		sheetsByName[sheetName] = sheet.Sheet
 		sheet.Sheet.Name = sheetName
 		sheets[sheet.Index] = sheet.Sheet

+ 13 - 2
lib_test.go

@@ -317,8 +317,8 @@ func (l *LibSuite) TestReadRowsFromSheet(c *C) {
 	sheetView := worksheet.SheetViews.SheetView[0]
 	c.Assert(sheetView.Pane, NotNil)
 	pane := sheetView.Pane
-	c.Assert(pane.XSplit, Equals, 0)
-	c.Assert(pane.YSplit, Equals, 1)
+	c.Assert(pane.XSplit, Equals, 0.0)
+	c.Assert(pane.YSplit, Equals, 1.0)
 }
 
 func (l *LibSuite) TestReadRowsFromSheetWithLeadingEmptyRows(c *C) {
@@ -894,3 +894,14 @@ func (l *LibSuite) TestSharedFormulas(c *C) {
 	c.Assert(row.Cells[1].Formula(), Equals, "2*B1")
 	c.Assert(row.Cells[2].Formula(), Equals, "2*C1")
 }
+
+// Avoid panic when cell.F.T is "e" (for error)
+func (l *LibSuite) TestFormulaForCellPanic(c *C) {
+	cell := xlsxC{R: "A1"}
+	// This line would panic before the fix.
+	sharedFormulas := make(map[int]sharedFormula)
+
+	// Not really an important test; getting here without a
+	// panic is the real win.
+	c.Assert(formulaForCell(cell, sharedFormulas), Equals, "")
+}

+ 23 - 11
sheet.go

@@ -8,14 +8,15 @@ import (
 // Sheet is a high level structure intended to provide user access to
 // the contents of a particular sheet within an XLSX file.
 type Sheet struct {
-	Name       string
-	File       *File
-	Rows       []*Row
-	Cols       []*Col
-	MaxRow     int
-	MaxCol     int
-	Hidden     bool
-	SheetViews []SheetView
+	Name        string
+	File        *File
+	Rows        []*Row
+	Cols        []*Col
+	MaxRow      int
+	MaxCol      int
+	Hidden      bool
+	SheetViews  []SheetView
+	SheetFormat SheetFormat
 }
 
 type SheetView struct {
@@ -23,13 +24,18 @@ type SheetView struct {
 }
 
 type Pane struct {
-	XSplit      int
-	YSplit      int
+	XSplit      float64
+	YSplit      float64
 	TopLeftCell string
 	ActivePane  string
 	State       string // Either "split" or "frozen"
 }
 
+type SheetFormat struct {
+	DefaultColWidth  float64
+	DefaultRowHeight float64
+}
+
 // Add a new Row to a Sheet
 func (s *Sheet) AddRow() *Row {
 	row := &Row{Sheet: s}
@@ -110,6 +116,8 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 				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{}
@@ -124,7 +132,11 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 				xCellXf.FontId = fontId
 				xCellXf.FillId = fillId
 				xCellXf.BorderId = borderId
-				xCellXf.NumFmtId = 0 // General
+				xCellXf.NumFmtId = xNumFmt.NumFmtId
+				// apply the numFmtId when it is not the default cellxf
+				if xCellXf.NumFmtId > 0 {
+					xCellXf.ApplyNumberFormat = true
+				}
 				styles.addCellStyleXf(xCellStyleXf)
 				XfId = styles.addCellXf(xCellXf)
 			}

+ 49 - 2
sheet_test.go

@@ -49,6 +49,53 @@ func (s *SheetSuite) TestMakeXLSXSheetFromRows(c *C) {
 	c.Assert(xSI.T, Equals, "A cell!")
 }
 
+// Test if the NumFmts assigned properly according the FormatCode in cell.
+func (s *SheetSuite) TestMakeXLSXSheetWithNumFormats(c *C) {
+	file := NewFile()
+	sheet := file.AddSheet("Sheet1")
+	row := sheet.AddRow()
+
+	cell1 := row.AddCell()
+	cell1.Value = "A cell!"
+	cell1.numFmt = "general"
+
+	cell2 := row.AddCell()
+	cell2.Value = "37947.7500001"
+	cell2.numFmt = "0"
+
+	cell3 := row.AddCell()
+	cell3.Value = "37947.7500001"
+	cell3.numFmt = "mm-dd-yy"
+
+	cell4 := row.AddCell()
+	cell4.Value = "37947.7500001"
+	cell4.numFmt = "hh:mm:ss"
+
+	refTable := NewSharedStringRefTable()
+	styles := newXlsxStyleSheet(nil)
+	worksheet := sheet.makeXLSXSheet(refTable, styles)
+
+	c.Assert(styles.CellStyleXfs.Count, Equals, 1)
+	// The 0th CellStyleXf could just be getting the zero values by default
+	c.Assert(styles.CellStyleXfs.Xf[0].FontId, Equals, 0)
+	c.Assert(styles.CellStyleXfs.Xf[0].FillId, Equals, 0)
+	c.Assert(styles.CellStyleXfs.Xf[0].BorderId, Equals, 0)
+
+	c.Assert(styles.CellXfs.Count, Equals, 4)
+	c.Assert(styles.CellXfs.Xf[0].NumFmtId, Equals, 0)
+	c.Assert(styles.CellXfs.Xf[1].NumFmtId, Equals, 1)
+	c.Assert(styles.CellXfs.Xf[2].NumFmtId, Equals, 14)
+	c.Assert(styles.CellXfs.Xf[3].NumFmtId, Equals, 164)
+	c.Assert(styles.NumFmts.Count, Equals, 1)
+	c.Assert(styles.NumFmts.NumFmt[0].NumFmtId, Equals, 164)
+	c.Assert(styles.NumFmts.NumFmt[0].FormatCode, Equals, "hh:mm:ss")
+
+	// 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)
+}
+
 // When we create the xlsxSheet we also populate the xlsxStyles struct
 // with style information.
 func (s *SheetSuite) TestMakeXLSXSheetAlsoPopulatesXLSXSTyles(c *C) {
@@ -126,7 +173,7 @@ func (s *SheetSuite) TestMarshalSheet(c *C) {
 	_, err = output.Write(body)
 	c.Assert(err, IsNil)
 	expectedXLSXSheet := `<?xml version="1.0" encoding="UTF-8"?>
-<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" s="0" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" t="s"><v>0</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
 	c.Assert(output.String(), Equals, expectedXLSXSheet)
 }
 
@@ -149,7 +196,7 @@ func (s *SheetSuite) TestMarshalSheetWithMultipleCells(c *C) {
 	_, err = output.Write(body)
 	c.Assert(err, IsNil)
 	expectedXLSXSheet := `<?xml version="1.0" encoding="UTF-8"?>
-<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1:B1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col><col collapsed="false" hidden="false" max="2" min="2" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" s="0" t="s"><v>0</v></c><c r="B1" s="0" t="s"><v>1</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetPr filterMode="false"><pageSetUpPr fitToPage="false"></pageSetUpPr></sheetPr><dimension ref="A1:B1"></dimension><sheetViews><sheetView windowProtection="false" showFormulas="false" showGridLines="true" showRowColHeaders="true" showZeros="true" rightToLeft="false" tabSelected="true" showOutlineSymbols="true" defaultGridColor="true" view="normal" topLeftCell="A1" colorId="64" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100" workbookViewId="0"><selection pane="topLeft" activeCell="A1" activeCellId="0" sqref="A1"></selection></sheetView></sheetViews><sheetFormatPr defaultRowHeight="12.85"></sheetFormatPr><cols><col collapsed="false" hidden="false" max="1" min="1" width="9.5"></col><col collapsed="false" hidden="false" max="2" min="2" width="9.5"></col></cols><sheetData><row r="1"><c r="A1" t="s"><v>0</v></c><c r="B1" t="s"><v>1</v></c></row></sheetData><printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"></printOptions><pageMargins left="0.7875" right="0.7875" top="1.05277777777778" bottom="1.05277777777778" header="0.7875" footer="0.7875"></pageMargins><pageSetup paperSize="9" scale="100" firstPageNumber="1" fitToWidth="1" fitToHeight="1" pageOrder="downThenOver" orientation="portrait" usePrinterDefaults="false" blackAndWhite="false" draft="false" cellComments="none" useFirstPageNumber="true" horizontalDpi="300" verticalDpi="300" copies="1"></pageSetup><headerFooter differentFirst="false" differentOddEven="false"><oddHeader>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12&amp;A</oddHeader><oddFooter>&amp;C&amp;&#34;Times New Roman,Regular&#34;&amp;12Page &amp;P</oddFooter></headerFooter></worksheet>`
 	c.Assert(output.String(), Equals, expectedXLSXSheet)
 }
 

+ 3 - 3
style.go

@@ -36,17 +36,17 @@ func (style *Style) makeXLSXStyleElements() (xFont xlsxFont, xFill xlsxFill, xBo
 	xFont.Charset.Val = strconv.Itoa(style.Font.Charset)
 	xFont.Color.RGB = style.Font.Color
 	if style.Font.Bold {
-		xFont.B = &struct{}{}
+		xFont.B = &xlsxVal{}
 	} else {
 		xFont.B = nil
 	}
 	if style.Font.Italic {
-		xFont.I = &struct{}{}
+		xFont.I = &xlsxVal{}
 	} else {
 		xFont.I = nil
 	}
 	if style.Font.Underline {
-		xFont.U = &struct{}{}
+		xFont.U = &xlsxVal{}
 	} else {
 		xFont.U = nil
 	}

BIN
testdocs/testchartsheet.xlsx


+ 98 - 83
xmlStyle.go

@@ -15,6 +15,48 @@ import (
 	"sync"
 )
 
+// Excel styles can reference number formats that are built-in, all of which
+// have an id less than 164.
+const builtinNumFmtsCount = 163
+
+// Excel styles can reference number formats that are built-in, all of which
+// have an id less than 164. This is a possibly incomplete list comprised of as
+// many of them as I could find.
+var builtInNumFmt = map[int]string{
+	0:  "general",
+	1:  "0",
+	2:  "0.00",
+	3:  "#,##0",
+	4:  "#,##0.00",
+	9:  "0%",
+	10: "0.00%",
+	11: "0.00E+00",
+	12: "# ?/?",
+	13: "# ??/??",
+	14: "mm-dd-yy",
+	15: "d-mmm-yy",
+	16: "d-mmm",
+	17: "mmm-yy",
+	18: "h:mm AM/PM",
+	19: "h:mm:ss AM/PM",
+	20: "h:mm",
+	21: "h:mm:ss",
+	22: "m/d/yy h:mm",
+	37: "#,##0 ;(#,##0)",
+	38: "#,##0 ;[Red](#,##0)",
+	39: "#,##0.00;(#,##0.00)",
+	40: "#,##0.00;[Red](#,##0.00)",
+	41: `_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)`,
+	42: `_("$"* #,##0_);_("$* \(#,##0\);_("$"* "-"_);_(@_)`,
+	43: `_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)`,
+	44: `_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)`,
+	45: "mm:ss",
+	46: "[h]:mm:ss",
+	47: "mmss.0",
+	48: "##0.0E+0",
+	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
@@ -48,7 +90,8 @@ func (styles *xlsxStyleSheet) reset() {
 	styles.Fills = xlsxFills{}
 	styles.Borders = xlsxBorders{}
 	styles.CellStyleXfs = xlsxCellStyleXfs{}
-	styles.CellXfs = xlsxCellXfs{}
+	// add default xf
+	styles.CellXfs = xlsxCellXfs{Count: 1, Xf: []xlsxXf{xlsxXf{}}}
 	styles.NumFmts = xlsxNumFmts{}
 }
 
@@ -105,13 +148,13 @@ func (styles *xlsxStyleSheet) getStyle(styleIndex int) (style *Style) {
 			style.Font.Charset, _ = strconv.Atoi(xfont.Charset.Val)
 			style.Font.Color = styles.argbValue(xfont.Color)
 
-			if xfont.B != nil {
+			if bold := xfont.B; bold != nil && bold.Val != "0" {
 				style.Font.Bold = true
 			}
-			if xfont.I != nil {
+			if italic := xfont.I; italic != nil && italic.Val != "0" {
 				style.Font.Italic = true
 			}
-			if xfont.U != nil {
+			if underline := xfont.U; underline != nil && underline.Val != "0"  {
 				style.Font.Underline = true
 			}
 		}
@@ -137,71 +180,7 @@ func (styles *xlsxStyleSheet) argbValue(color xlsxColor) string {
 // have an id less than 164. This is a possibly incomplete list comprised of as
 // many of them as I could find.
 func getBuiltinNumberFormat(numFmtId int) string {
-	switch numFmtId {
-	case 1:
-		return "0"
-	case 2:
-		return "0.00"
-	case 3:
-		return "#,##0"
-	case 4:
-		return "#,##0.00"
-	case 9:
-		return "0%"
-	case 10:
-		return "0.00%"
-	case 11:
-		return "0.00E+00"
-	case 12:
-		return "# ?/?"
-	case 13:
-		return "# ??/??"
-	case 14:
-		return "mm-dd-yy"
-	case 15:
-		return "d-mmm-yy"
-	case 16:
-		return "d-mmm"
-	case 17:
-		return "mmm-yy"
-	case 18:
-		return "h:mm AM/PM"
-	case 19:
-		return "h:mm:ss AM/PM"
-	case 20:
-		return "h:mm"
-	case 21:
-		return "h:mm:ss"
-	case 22:
-		return "m/d/yy h:mm"
-	case 37:
-		return "#,##0 ;(#,##0)"
-	case 38:
-		return "#,##0 ;[Red](#,##0)"
-	case 39:
-		return "#,##0.00;(#,##0.00)"
-	case 40:
-		return "#,##0.00;[Red](#,##0.00)"
-	case 41:
-		return `_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)`
-	case 42:
-		return `_("$"* #,##0_);_("$* \(#,##0\);_("$"* "-"_);_(@_)`
-	case 43:
-		return `_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)`
-	case 44:
-		return `_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)`
-	case 45:
-		return "mm:ss"
-	case 46:
-		return "[h]:mm:ss"
-	case 47:
-		return "mmss.0"
-	case 48:
-		return "##0.0E+0"
-	case 49:
-		return "@"
-	}
-	return ""
+	return builtInNumFmt[numFmtId]
 }
 
 func (styles *xlsxStyleSheet) getNumberFormat(styleIndex int) string {
@@ -291,7 +270,42 @@ func (styles *xlsxStyleSheet) addCellXf(xCellXf xlsxXf) (index int) {
 	return
 }
 
+// newNumFmt generate a xlsxNumFmt according the format code. When the FormatCode is built in, it will return a xlsxNumFmt with the NumFmtId defined in ECMA document, otherwise it will generate a new NumFmtId greater than 164.
+func (styles *xlsxStyleSheet) newNumFmt(formatCode string) xlsxNumFmt {
+	if formatCode == "" {
+		return xlsxNumFmt{NumFmtId: 0, FormatCode: "general"}
+	}
+	// built in NumFmts in xmlStyle.go, traverse from the const.
+	numFmts := make(map[string]int)
+	for k, v := range builtInNumFmt {
+		numFmts[v] = k
+	}
+	numFmtId, ok := numFmts[formatCode]
+	if ok {
+		return xlsxNumFmt{NumFmtId: numFmtId, FormatCode: formatCode}
+	}
+
+	// The user define NumFmtId. The one less than 164 in built in.
+	numFmtId = builtinNumFmtsCount + 1
+	styles.lock.Lock()
+	defer styles.lock.Unlock()
+	for {
+		// get a unused NumFmtId
+		if _, ok = styles.numFmtRefTable[numFmtId]; ok {
+			numFmtId += 1
+		} else {
+			styles.addNumFmt(xlsxNumFmt{NumFmtId: numFmtId, FormatCode: formatCode})
+			break
+		}
+	}
+	return xlsxNumFmt{NumFmtId: numFmtId, FormatCode: formatCode}
+}
+
 func (styles *xlsxStyleSheet) addNumFmt(xNumFmt xlsxNumFmt) (index int) {
+	// don't add built in NumFmt
+	if xNumFmt.NumFmtId <= builtinNumFmtsCount {
+		return -1
+	}
 	numFmt, ok := styles.numFmtRefTable[xNumFmt.NumFmtId]
 	if !ok {
 		if styles.numFmtRefTable == nil {
@@ -445,9 +459,9 @@ type xlsxFont struct {
 	Family  xlsxVal   `xml:"family,omitempty"`
 	Charset xlsxVal   `xml:"charset,omitempty"`
 	Color   xlsxColor `xml:"color,omitempty"`
-	B       *struct{} `xml:"b,omitempty"`
-	I       *struct{} `xml:"i,omitempty"`
-	U       *struct{} `xml:"u,omitempty"`
+	B       *xlsxVal  `xml:"b,omitempty"`
+	I       *xlsxVal  `xml:"i,omitempty"`
+	U       *xlsxVal  `xml:"u,omitempty"`
 }
 
 func (font *xlsxFont) Equals(other xlsxFont) bool {
@@ -756,16 +770,17 @@ func (cellXfs *xlsxCellXfs) Marshal(outputBorderMap, outputFillMap, outputFontMa
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxXf struct {
-	ApplyAlignment  bool          `xml:"applyAlignment,attr"`
-	ApplyBorder     bool          `xml:"applyBorder,attr"`
-	ApplyFont       bool          `xml:"applyFont,attr"`
-	ApplyFill       bool          `xml:"applyFill,attr"`
-	ApplyProtection bool          `xml:"applyProtection,attr"`
-	BorderId        int           `xml:"borderId,attr"`
-	FillId          int           `xml:"fillId,attr"`
-	FontId          int           `xml:"fontId,attr"`
-	NumFmtId        int           `xml:"numFmtId,attr"`
-	Alignment       xlsxAlignment `xml:"alignment"`
+	ApplyAlignment    bool          `xml:"applyAlignment,attr"`
+	ApplyBorder       bool          `xml:"applyBorder,attr"`
+	ApplyFont         bool          `xml:"applyFont,attr"`
+	ApplyFill         bool          `xml:"applyFill,attr"`
+	ApplyNumberFormat bool          `xml:"applyNumberFormat,attr"`
+	ApplyProtection   bool          `xml:"applyProtection,attr"`
+	BorderId          int           `xml:"borderId,attr"`
+	FillId            int           `xml:"fillId,attr"`
+	FontId            int           `xml:"fontId,attr"`
+	NumFmtId          int           `xml:"numFmtId,attr"`
+	Alignment         xlsxAlignment `xml:"alignment"`
 }
 
 func (xf *xlsxXf) Equals(other xlsxXf) bool {
@@ -783,7 +798,7 @@ func (xf *xlsxXf) Equals(other xlsxXf) bool {
 
 func (xf *xlsxXf) Marshal(outputBorderMap, outputFillMap, outputFontMap map[int]int) (result string, err error) {
 	var xAlignment string
-	result = fmt.Sprintf(`<xf applyAlignment="%b" applyBorder="%b" applyFont="%b" applyFill="%b" applyProtection="%b" borderId="%d" fillId="%d" fontId="%d" numFmtId="%d">`, bool2Int(xf.ApplyAlignment), bool2Int(xf.ApplyBorder), bool2Int(xf.ApplyFont), bool2Int(xf.ApplyFill), bool2Int(xf.ApplyProtection), outputBorderMap[xf.BorderId], outputFillMap[xf.FillId], outputFontMap[xf.FontId], xf.NumFmtId)
+	result = fmt.Sprintf(`<xf applyAlignment="%b" applyBorder="%b" applyFont="%b" applyFill="%b" applyNumberFormat="%b" applyProtection="%b" borderId="%d" fillId="%d" fontId="%d" numFmtId="%d">`, bool2Int(xf.ApplyAlignment), bool2Int(xf.ApplyBorder), bool2Int(xf.ApplyFont), bool2Int(xf.ApplyFill), bool2Int(xf.ApplyNumberFormat), bool2Int(xf.ApplyProtection), outputBorderMap[xf.BorderId], outputFillMap[xf.FillId], outputFontMap[xf.FontId], xf.NumFmtId)
 	xAlignment, err = xf.Alignment.Marshal()
 	if err != nil {
 		return

+ 36 - 14
xmlStyle_test.go

@@ -26,9 +26,9 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithAFont(c *C) {
 	font := xlsxFont{}
 	font.Sz.Val = "10"
 	font.Name.Val = "Andale Mono"
-	font.B = &struct{}{}
-	font.I = &struct{}{}
-	font.U = &struct{}{}
+	font.B = &xlsxVal{}
+	font.I = &xlsxVal{}
+	font.U = &xlsxVal{}
 	styles.Fonts.Font[0] = font
 
 	expected := `<?xml version="1.0" encoding="UTF-8"?>
@@ -103,7 +103,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellStyleXf(c *C) {
 	styles.CellStyleXfs.Xf[0] = xf
 
 	expected := `<?xml version="1.0" encoding="UTF-8"?>
-<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cellStyleXfs count="1"><xf applyAlignment="1" applyBorder="1" applyFont="1" applyFill="1" applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="left" indent="1" shrinkToFit="1" textRotation="0" vertical="middle" wrapText="0"/></xf></cellStyleXfs></styleSheet>`
+<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cellStyleXfs count="1"><xf applyAlignment="1" applyBorder="1" applyFont="1" applyFill="1" applyNumberFormat="0" applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="left" indent="1" shrinkToFit="1" textRotation="0" vertical="middle" wrapText="0"/></xf></cellStyleXfs></styleSheet>`
 	result, err := styles.Marshal()
 	c.Assert(err, IsNil)
 	c.Assert(string(result), Equals, expected)
@@ -121,6 +121,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellXf(c *C) {
 	xf.ApplyBorder = true
 	xf.ApplyFont = true
 	xf.ApplyFill = true
+	xf.ApplyNumberFormat = true
 	xf.ApplyProtection = true
 	xf.BorderId = 0
 	xf.FillId = 0
@@ -136,7 +137,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellXf(c *C) {
 	styles.CellXfs.Xf[0] = xf
 
 	expected := `<?xml version="1.0" encoding="UTF-8"?>
-<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cellXfs count="1"><xf applyAlignment="1" applyBorder="1" applyFont="1" applyFill="1" applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="left" indent="1" shrinkToFit="1" textRotation="0" vertical="middle" wrapText="0"/></xf></cellXfs></styleSheet>`
+<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cellXfs count="1"><xf applyAlignment="1" applyBorder="1" applyFont="1" applyFill="1" applyNumberFormat="1" applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0"><alignment horizontal="left" indent="1" shrinkToFit="1" textRotation="0" vertical="middle" wrapText="0"/></xf></cellXfs></styleSheet>`
 	result, err := styles.Marshal()
 	c.Assert(err, IsNil)
 	c.Assert(string(result), Equals, expected)
@@ -163,16 +164,16 @@ func (x *XMLStyleSuite) TestFontEquals(c *C) {
 		Color:  xlsxColor{RGB: "FFFF0000"},
 		Name:   xlsxVal{Val: "Calibri"},
 		Family: xlsxVal{Val: "2"},
-		B:      &struct{}{},
-		I:      &struct{}{},
-		U:      &struct{}{}}
+		B:      &xlsxVal{},
+		I:      &xlsxVal{},
+		U:      &xlsxVal{}}
 	fontB := xlsxFont{Sz: xlsxVal{Val: "11"},
 		Color:  xlsxColor{RGB: "FFFF0000"},
 		Name:   xlsxVal{Val: "Calibri"},
 		Family: xlsxVal{Val: "2"},
-		B:      &struct{}{},
-		I:      &struct{}{},
-		U:      &struct{}{}}
+		B:      &xlsxVal{},
+		I:      &xlsxVal{},
+		U:      &xlsxVal{}}
 
 	c.Assert(fontA.Equals(fontB), Equals, true)
 	fontB.Sz.Val = "12"
@@ -189,13 +190,13 @@ func (x *XMLStyleSuite) TestFontEquals(c *C) {
 	fontB.Family.Val = "2"
 	fontB.B = nil
 	c.Assert(fontA.Equals(fontB), Equals, false)
-	fontB.B = &struct{}{}
+	fontB.B = &xlsxVal{}
 	fontB.I = nil
 	c.Assert(fontA.Equals(fontB), Equals, false)
-	fontB.I = &struct{}{}
+	fontB.I = &xlsxVal{}
 	fontB.U = nil
 	c.Assert(fontA.Equals(fontB), Equals, false)
-	fontB.U = &struct{}{}
+	fontB.U = &xlsxVal{}
 	// For sanity
 	c.Assert(fontA.Equals(fontB), Equals, true)
 }
@@ -301,3 +302,24 @@ func (x *XMLStyleSuite) TestXfEquals(c *C) {
 	// for sanity
 	c.Assert(xfA.Equals(xfB), Equals, true)
 }
+
+func (s *CellSuite) TestNewNumFmt(c *C) {
+	styles := newXlsxStyleSheet(nil)
+	styles.NumFmts = xlsxNumFmts{}
+	styles.NumFmts.NumFmt = make([]xlsxNumFmt, 0)
+
+	c.Assert(styles.newNumFmt("0"), DeepEquals, xlsxNumFmt{1, "0"})
+	c.Assert(styles.newNumFmt("mm-dd-yy"), DeepEquals, xlsxNumFmt{14, "mm-dd-yy"})
+	c.Assert(styles.newNumFmt("hh:mm:ss"), DeepEquals, xlsxNumFmt{164, "hh:mm:ss"})
+}
+
+func (s *CellSuite) TestAddNumFmt(c *C) {
+	styles := &xlsxStyleSheet{}
+	styles.NumFmts = xlsxNumFmts{}
+	styles.NumFmts.NumFmt = make([]xlsxNumFmt, 0)
+
+	c.Assert(styles.addNumFmt(xlsxNumFmt{1, "0"}), Equals, -1)
+	c.Assert(styles.addNumFmt(xlsxNumFmt{14, "mm-dd-yy"}), Equals, -1)
+	c.Assert(styles.addNumFmt(xlsxNumFmt{164, "hh:mm:ss"}), DeepEquals, 0)
+	c.Assert(styles.addNumFmt(xlsxNumFmt{165, "yyyy/mm/dd"}), DeepEquals, 1)
+}

+ 16 - 9
xmlWorkbook.go

@@ -148,6 +148,19 @@ type xlsxCalcPr struct {
 	IterateDelta float64 `xml:"iterateDelta,attr,omitempty"`
 }
 
+// Helper function to lookup the file corresponding to a xlsxSheet object in the worksheets map
+func worksheetFileForSheet(sheet xlsxSheet, worksheets map[string]*zip.File, sheetXMLMap map[string]string) *zip.File {
+	sheetName, ok := sheetXMLMap[sheet.Id]
+	if !ok {
+		if sheet.SheetId != "" {
+			sheetName = fmt.Sprintf("sheet%s", sheet.SheetId)
+		} else {
+			sheetName = fmt.Sprintf("sheet%s", sheet.Id)
+		}
+	}
+	return worksheets[sheetName]
+}
+
 // getWorksheetFromSheet() is an internal helper function to open a
 // sheetN.xml file, refered to by an xlsx.xlsxSheet struct, from the XLSX
 // file and unmarshal it an xlsx.xlsxWorksheet struct
@@ -156,18 +169,12 @@ func getWorksheetFromSheet(sheet xlsxSheet, worksheets map[string]*zip.File, she
 	var decoder *xml.Decoder
 	var worksheet *xlsxWorksheet
 	var error error
-	var sheetName string
 	worksheet = new(xlsxWorksheet)
 
-	sheetName, ok := sheetXMLMap[sheet.Id]
-	if !ok {
-		if sheet.SheetId != "" {
-			sheetName = fmt.Sprintf("sheet%s", sheet.SheetId)
-		} else {
-			sheetName = fmt.Sprintf("sheet%s", sheet.Id)
-		}
+	f := worksheetFileForSheet(sheet, worksheets, sheetXMLMap)
+	if f == nil {
+		return nil, fmt.Errorf("Unable to find sheet '%s'", sheet)
 	}
-	f := worksheets[sheetName]
 	rc, error = f.Open()
 	if error != nil {
 		return nil, error

+ 7 - 6
xmlWorksheet.go

@@ -102,6 +102,7 @@ type xlsxPageMargins struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxSheetFormatPr struct {
+	DefaultColWidth  float64 `xml:"defaultColWidth,attr,omitempty"`
 	DefaultRowHeight float64 `xml:"defaultRowHeight,attr"`
 }
 
@@ -154,11 +155,11 @@ type xlsxSelection struct {
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxPane struct {
-	XSplit      int    `xml:"xSplit,attr"`
-	YSplit      int    `xml:"ySplit,attr"`
-	TopLeftCell string `xml:"topLeftCell,attr"`
-	ActivePane  string `xml:"activePane,attr"`
-	State       string `xml:"state,attr"` // Either "split" or "frozen"
+	XSplit      float64 `xml:"xSplit,attr"`
+	YSplit      float64 `xml:"ySplit,attr"`
+	TopLeftCell string  `xml:"topLeftCell,attr"`
+	ActivePane  string  `xml:"activePane,attr"`
+	State       string  `xml:"state,attr"` // Either "split" or "frozen"
 }
 
 // xlsxSheetPr directly maps the sheetPr element in the namespace
@@ -243,7 +244,7 @@ type xlsxMergeCells struct {
 // as I need.
 type xlsxC struct {
 	R string `xml:"r,attr"`           // Cell ID, e.g. A1
-	S int    `xml:"s,attr"`           // Style reference.
+	S int    `xml:"s,attr,omitempty"` // Style reference.
 	T string `xml:"t,attr,omitempty"` // Type.
 	V string `xml:"v"`                // Value
 	F *xlsxF `xml:"f,omitempty"`      // Formula

+ 3 - 1
xmlWorksheet_test.go

@@ -44,7 +44,7 @@ func (w *WorksheetSuite) TestUnmarshallWorksheet(c *C) {
                          sqref="B2"/>
             </sheetView>
           </sheetViews>
-          <sheetFormatPr defaultRowHeight="15">
+          <sheetFormatPr defaultRowHeight="15" defaultColWidth="8">
           </sheetFormatPr>
           <cols>
             <col collapsed="false"
@@ -131,6 +131,8 @@ func (w *WorksheetSuite) TestUnmarshallWorksheet(c *C) {
 	c.Assert(err, IsNil)
 	c.Assert(worksheet.Dimension.Ref, Equals, "A1:B2")
 	c.Assert(worksheet.SheetData.Row, HasLen, 2)
+	c.Assert(worksheet.SheetFormatPr.DefaultRowHeight, Equals, 15.0)
+	c.Assert(worksheet.SheetFormatPr.DefaultColWidth, Equals, 8.0)
 	row := worksheet.SheetData.Row[0]
 	c.Assert(row.R, Equals, 1)
 	c.Assert(row.C, HasLen, 2)