Browse Source

Added support for built-in and user-defined NumFmt style. Now it can write float into cell with number format but not the formatted value which sometimes cause the broken file warning in Microsoft Excel.

Hugh Gao 10 years ago
parent
commit
d0daf3ac53
7 changed files with 179 additions and 89 deletions
  1. 0 1
      cell.go
  2. 6 6
      file_test.go
  3. 11 1
      sheet.go
  4. 49 2
      sheet_test.go
  5. 88 76
      xmlStyle.go
  6. 24 2
      xmlStyle_test.go
  7. 1 1
      xmlWorksheet.go

+ 0 - 1
cell.go

@@ -91,7 +91,6 @@ func (c *Cell) SetFloatWithFormat(n float64, format string) {
 	// tmp value. final value is formatted by FormattedValue() method
 	c.Value = fmt.Sprintf("%e", n)
 	c.numFmt = format
-	c.Value = c.FormattedValue()
 	c.formula = ""
 	c.cellType = CellTypeNumeric
 }

+ 6 - 6
file_test.go

@@ -275,11 +275,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,9 +636,9 @@ 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"`
+	// 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" 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)
@@ -653,7 +653,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)
 }
 

+ 11 - 1
sheet.go

@@ -110,6 +110,12 @@ 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
+				xNumFmt := styles.newNumFmt(cell.numFmt)
+				// the NumFmtId of NumFmt user defined is greater than 164
+				if xNumFmt.NumFmtId >= 164 {
+					styles.addNumFmt(xNumFmt)
+				}
 
 				// HACK - adding light grey fill, as in OO and Google
 				greyfill := xlsxFill{}
@@ -124,7 +130,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)
 }
 

+ 88 - 76
xmlStyle.go

@@ -15,6 +15,44 @@ import (
 	"sync"
 )
 
+// 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 = 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
@@ -137,71 +175,12 @@ 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 ""
+	formatCode, ok := builtInNumFmt[numFmtId]
+	if ok {
+		return formatCode
+	} else {
+		return ""
+	}
 }
 
 func (styles *xlsxStyleSheet) getNumberFormat(styleIndex int) string {
@@ -291,7 +270,39 @@ 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 = 164
+	for {
+		// get a unused NumFmtId
+		if _, ok = styles.numFmtRefTable[numFmtId]; ok {
+			numFmtId += 1
+		} else {
+			break
+		}
+	}
+	return xlsxNumFmt{NumFmtId: numFmtId, FormatCode: formatCode}
+}
+
 func (styles *xlsxStyleSheet) addNumFmt(xNumFmt xlsxNumFmt) (index int) {
+	// don't add built in NumFmt
+	if xNumFmt.NumFmtId < 164 {
+		return -1
+	}
 	numFmt, ok := styles.numFmtRefTable[xNumFmt.NumFmtId]
 	if !ok {
 		if styles.numFmtRefTable == nil {
@@ -756,16 +767,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 +795,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

+ 24 - 2
xmlStyle_test.go

@@ -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)
@@ -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 := &xlsxStyleSheet{}
+	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)
+}

+ 1 - 1
xmlWorksheet.go

@@ -243,7 +243,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