Browse Source

Merge pull request #118 from klniu/master

Added support for writing float with NumFmt style.

Looks good to me.
Shawn Milochik 10 years ago
parent
commit
8fbad487b8
8 changed files with 189 additions and 92 deletions
  1. 1 3
      cell.go
  2. 9 0
      cell_test.go
  3. 6 6
      file_test.go
  4. 7 1
      sheet.go
  5. 49 2
      sheet_test.go
  6. 92 77
      xmlStyle.go
  7. 24 2
      xmlStyle_test.go
  8. 1 1
      xmlWorksheet.go

+ 1 - 3
cell.go

@@ -88,10 +88,8 @@ func (c *Cell) SetFloat(n float64) {
 // SetFloatWithFormat sets the value of a cell to a float and applies
 // SetFloatWithFormat sets the value of a cell to a float and applies
 // formatting to the cell.
 // formatting to the cell.
 func (c *Cell) SetFloatWithFormat(n float64, format string) {
 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.numFmt = format
-	c.Value = c.FormattedValue()
 	c.formula = ""
 	c.formula = ""
 	c.cellType = CellTypeNumeric
 	c.cellType = CellTypeNumeric
 }
 }

+ 9 - 0
cell_test.go

@@ -93,6 +93,15 @@ func (s *CellSuite) TestGetStyleWithBorders(c *C) {
 	c.Assert(xBorder.Bottom.Style, Equals, "thin")
 	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
 // We can return a string representation of the formatted data
 func (l *CellSuite) TestFormattedValue(c *C) {
 func (l *CellSuite) TestFormattedValue(c *C) {
 	cell := Cell{Value: "37947.7500001"}
 	cell := Cell{Value: "37947.7500001"}

+ 6 - 6
file_test.go

@@ -281,11 +281,11 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 
 
 	// sheets
 	// sheets
 	expectedSheet1 := `<?xml version="1.0" encoding="UTF-8"?>
 	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)
 	c.Assert(parts["xl/worksheets/sheet1.xml"], Equals, expectedSheet1)
 
 
 	expectedSheet2 := `<?xml version="1.0" encoding="UTF-8"?>
 	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)
 	c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet2)
 
 
 	// .rels.xml
 	// .rels.xml
@@ -642,9 +642,9 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
 	c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
 
 
 	// workbook.xml
 	// 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"?>
 	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>`
 <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)
 	c.Assert(parts["xl/workbook.xml"], Equals, expectedWorkbook)
@@ -659,7 +659,7 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	// For now we only allow simple string data in the
 	// For now we only allow simple string data in the
 	// spreadsheet.  Style support will follow.
 	// spreadsheet.  Style support will follow.
 	expectedStyles := `<?xml version="1.0" encoding="UTF-8"?>
 	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)
 	c.Assert(parts["xl/styles.xml"], Equals, expectedStyles)
 }
 }
 
 

+ 7 - 1
sheet.go

@@ -116,6 +116,8 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 				xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
 				xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
 				fontId := styles.addFont(xFont)
 				fontId := styles.addFont(xFont)
 				fillId := styles.addFill(xFill)
 				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
 				// HACK - adding light grey fill, as in OO and Google
 				greyfill := xlsxFill{}
 				greyfill := xlsxFill{}
@@ -130,7 +132,11 @@ func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxW
 				xCellXf.FontId = fontId
 				xCellXf.FontId = fontId
 				xCellXf.FillId = fillId
 				xCellXf.FillId = fillId
 				xCellXf.BorderId = borderId
 				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)
 				styles.addCellStyleXf(xCellStyleXf)
 				XfId = styles.addCellXf(xCellXf)
 				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!")
 	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
 // When we create the xlsxSheet we also populate the xlsxStyles struct
 // with style information.
 // with style information.
 func (s *SheetSuite) TestMakeXLSXSheetAlsoPopulatesXLSXSTyles(c *C) {
 func (s *SheetSuite) TestMakeXLSXSheetAlsoPopulatesXLSXSTyles(c *C) {
@@ -126,7 +173,7 @@ func (s *SheetSuite) TestMarshalSheet(c *C) {
 	_, err = output.Write(body)
 	_, err = output.Write(body)
 	c.Assert(err, IsNil)
 	c.Assert(err, IsNil)
 	expectedXLSXSheet := `<?xml version="1.0" encoding="UTF-8"?>
 	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)
 	c.Assert(output.String(), Equals, expectedXLSXSheet)
 }
 }
 
 
@@ -149,7 +196,7 @@ func (s *SheetSuite) TestMarshalSheetWithMultipleCells(c *C) {
 	_, err = output.Write(body)
 	_, err = output.Write(body)
 	c.Assert(err, IsNil)
 	c.Assert(err, IsNil)
 	expectedXLSXSheet := `<?xml version="1.0" encoding="UTF-8"?>
 	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)
 	c.Assert(output.String(), Equals, expectedXLSXSheet)
 }
 }
 
 

+ 92 - 77
xmlStyle.go

@@ -15,6 +15,48 @@ import (
 	"sync"
 	"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
 // xlsxStyle directly maps the styleSheet element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
 // currently I have not checked it for completeness - it does as much
@@ -48,7 +90,8 @@ func (styles *xlsxStyleSheet) reset() {
 	styles.Fills = xlsxFills{}
 	styles.Fills = xlsxFills{}
 	styles.Borders = xlsxBorders{}
 	styles.Borders = xlsxBorders{}
 	styles.CellStyleXfs = xlsxCellStyleXfs{}
 	styles.CellStyleXfs = xlsxCellStyleXfs{}
-	styles.CellXfs = xlsxCellXfs{}
+	// add default xf
+	styles.CellXfs = xlsxCellXfs{Count: 1, Xf: []xlsxXf{xlsxXf{}}}
 	styles.NumFmts = xlsxNumFmts{}
 	styles.NumFmts = xlsxNumFmts{}
 }
 }
 
 
@@ -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
 // have an id less than 164. This is a possibly incomplete list comprised of as
 // many of them as I could find.
 // many of them as I could find.
 func getBuiltinNumberFormat(numFmtId int) string {
 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 {
 func (styles *xlsxStyleSheet) getNumberFormat(styleIndex int) string {
@@ -291,7 +270,42 @@ func (styles *xlsxStyleSheet) addCellXf(xCellXf xlsxXf) (index int) {
 	return
 	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) {
 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]
 	numFmt, ok := styles.numFmtRefTable[xNumFmt.NumFmtId]
 	if !ok {
 	if !ok {
 		if styles.numFmtRefTable == nil {
 		if styles.numFmtRefTable == nil {
@@ -756,16 +770,17 @@ func (cellXfs *xlsxCellXfs) Marshal(outputBorderMap, outputFillMap, outputFontMa
 // currently I have not checked it for completeness - it does as much
 // currently I have not checked it for completeness - it does as much
 // as I need.
 // as I need.
 type xlsxXf struct {
 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 {
 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) {
 func (xf *xlsxXf) Marshal(outputBorderMap, outputFillMap, outputFontMap map[int]int) (result string, err error) {
 	var xAlignment string
 	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()
 	xAlignment, err = xf.Alignment.Marshal()
 	if err != nil {
 	if err != nil {
 		return
 		return

+ 24 - 2
xmlStyle_test.go

@@ -103,7 +103,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellStyleXf(c *C) {
 	styles.CellStyleXfs.Xf[0] = xf
 	styles.CellStyleXfs.Xf[0] = xf
 
 
 	expected := `<?xml version="1.0" encoding="UTF-8"?>
 	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()
 	result, err := styles.Marshal()
 	c.Assert(err, IsNil)
 	c.Assert(err, IsNil)
 	c.Assert(string(result), Equals, expected)
 	c.Assert(string(result), Equals, expected)
@@ -121,6 +121,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellXf(c *C) {
 	xf.ApplyBorder = true
 	xf.ApplyBorder = true
 	xf.ApplyFont = true
 	xf.ApplyFont = true
 	xf.ApplyFill = true
 	xf.ApplyFill = true
+	xf.ApplyNumberFormat = true
 	xf.ApplyProtection = true
 	xf.ApplyProtection = true
 	xf.BorderId = 0
 	xf.BorderId = 0
 	xf.FillId = 0
 	xf.FillId = 0
@@ -136,7 +137,7 @@ func (x *XMLStyleSuite) TestMarshalXlsxStyleSheetWithACellXf(c *C) {
 	styles.CellXfs.Xf[0] = xf
 	styles.CellXfs.Xf[0] = xf
 
 
 	expected := `<?xml version="1.0" encoding="UTF-8"?>
 	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()
 	result, err := styles.Marshal()
 	c.Assert(err, IsNil)
 	c.Assert(err, IsNil)
 	c.Assert(string(result), Equals, expected)
 	c.Assert(string(result), Equals, expected)
@@ -301,3 +302,24 @@ func (x *XMLStyleSuite) TestXfEquals(c *C) {
 	// for sanity
 	// for sanity
 	c.Assert(xfA.Equals(xfB), Equals, true)
 	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)
+}

+ 1 - 1
xmlWorksheet.go

@@ -244,7 +244,7 @@ type xlsxMergeCells struct {
 // as I need.
 // as I need.
 type xlsxC struct {
 type xlsxC struct {
 	R string `xml:"r,attr"`           // Cell ID, e.g. A1
 	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.
 	T string `xml:"t,attr,omitempty"` // Type.
 	V string `xml:"v"`                // Value
 	V string `xml:"v"`                // Value
 	F *xlsxF `xml:"f,omitempty"`      // Formula
 	F *xlsxF `xml:"f,omitempty"`      // Formula