Browse Source

Merge branch 'master' into master

xuri 7 years ago
parent
commit
204139739a
41 changed files with 1772 additions and 416 deletions
  1. 6 0
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 44 0
      .github/ISSUE_TEMPLATE/feature_request.md
  3. 11 0
      .travis.yml
  4. 45 0
      PULL_REQUEST_TEMPLATE.md
  5. 1 1
      README.md
  6. 1 1
      README_zh.md
  7. 41 32
      cell.go
  8. 52 41
      chart.go
  9. 23 14
      col.go
  10. 65 14
      comment.go
  11. 233 0
      datavalidation.go
  12. 57 0
      datavalidation_test.go
  13. 45 14
      date.go
  14. 42 0
      date_test.go
  15. 29 17
      excelize.go
  16. 55 6
      excelize_test.go
  17. 26 14
      file.go
  18. 139 0
      hsl.go
  19. 29 22
      lib.go
  20. 92 40
      picture.go
  21. 29 19
      rows.go
  22. 14 5
      shape.go
  23. 59 43
      sheet.go
  24. 11 2
      sheetpr.go
  25. 9 0
      sheetview.go
  26. 160 105
      styles.go
  27. 134 0
      styles_test.go
  28. 29 19
      table.go
  29. 9 0
      templates.go
  30. 9 0
      vmlDrawing.go
  31. 9 0
      xmlChart.go
  32. 17 0
      xmlComments.go
  33. 9 0
      xmlContentTypes.go
  34. 9 0
      xmlDecodeDrawing.go
  35. 9 0
      xmlDrawing.go
  36. 9 0
      xmlSharedStrings.go
  37. 9 0
      xmlStyles.go
  38. 9 0
      xmlTable.go
  39. 149 0
      xmlTheme.go
  40. 9 0
      xmlWorkbook.go
  41. 35 7
      xmlWorksheet.go

+ 6 - 0
.github/ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE/bug_report.md

@@ -1,3 +1,9 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
 <!--
 <!--
 If you are reporting a new issue, make sure that we do not have any duplicates
 If you are reporting a new issue, make sure that we do not have any duplicates
 already open. You can ensure this by searching the issue list for this
 already open. You can ensure this by searching the issue list for this

+ 44 - 0
.github/ISSUE_TEMPLATE/feature_request.md

@@ -0,0 +1,44 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+<!--
+If you are reporting a new issue, make sure that we do not have any duplicates
+already open. You can ensure this by searching the issue list for this
+repository. If there is a duplicate, please close your issue and add a comment
+to the existing issue instead.
+
+Use the commands below to provide key information from your environment:
+You do NOT have to include this information if this is a FEATURE REQUEST
+-->
+
+**Description**
+
+<!--
+Briefly describe the problem you are having in a few paragraphs.
+-->
+
+**Steps to reproduce the issue:**
+1.
+2.
+3.
+
+**Describe the results you received:**
+
+**Describe the results you expected:**
+
+**Output of `go version`:**
+
+```text
+(paste your output here)
+```
+
+**Excelize version or commit ID:**
+
+```text
+(paste here)
+```
+
+**Environment details (OS, Microsoft Excel™ version, physical, etc.):**

+ 11 - 0
.travis.yml

@@ -6,6 +6,17 @@ install:
 go:
 go:
   - 1.8.x
   - 1.8.x
   - 1.9.x
   - 1.9.x
+  - 1.10.x
+  - 1.11.x
+
+os:
+  - linux
+  - osx
+
+env:
+  matrix:
+    - GOARCH=amd64
+    - GOARCH=386
 
 
 script:
 script:
   - go vet ./...
   - go vet ./...

+ 45 - 0
PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,45 @@
+# PR Details
+
+<!--- Provide a general summary of your changes in the Title above -->
+
+## Description
+
+<!--- Describe your changes in detail -->
+
+## Related Issue
+
+<!--- This project only accepts pull requests related to open issues -->
+<!--- If suggesting a new feature or change, please discuss it in an issue first -->
+<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
+<!--- Please link to the issue here: -->
+
+## Motivation and Context
+
+<!--- Why is this change required? What problem does it solve? -->
+
+## How Has This Been Tested
+
+<!--- Please describe in detail how you tested your changes. -->
+<!--- Include details of your testing environment, and the tests you ran to -->
+<!--- see how your change affects other areas of the code, etc. -->
+
+## Types of changes
+
+<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
+
+- [ ] Docs change / refactoring / dependency upgrade
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to change)
+
+## Checklist
+
+<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
+<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
+
+- [ ] My code follows the code style of this project.
+- [ ] My change requires a change to the documentation.
+- [ ] I have updated the documentation accordingly.
+- [ ] I have read the **CONTRIBUTING** document.
+- [ ] I have added tests to cover my changes.
+- [ ] All new and existing tests passed.

+ 1 - 1
README.md

@@ -11,7 +11,7 @@
 
 
 ## Introduction
 ## Introduction
 
 
-Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel™ 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [Chinese translation](https://xuri.me/excelize/zh_cn).
+Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel™ 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
 
 
 ## Basic Usage
 ## Basic Usage
 
 

+ 1 - 1
README_zh.md

@@ -11,7 +11,7 @@
 
 
 ## 简介
 ## 简介
 
 
-Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。使用本类库要求使用的 Go 语言为 1.8 或更高版本,完整的 API 使用文档请访问 [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) 或查看 [中文翻译](https://xuri.me/excelize/zh_cn)。
+Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。使用本类库要求使用的 Go 语言为 1.8 或更高版本,完整的 API 使用文档请访问 [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) 或查看 [参考文档](https://xuri.me/excelize/)。
 
 
 ## 快速上手
 ## 快速上手
 
 

+ 41 - 32
cell.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -20,7 +29,7 @@ const (
 	STCellFormulaTypeShared = "shared"
 	STCellFormulaTypeShared = "shared"
 )
 )
 
 
-// mergeCellsParser provides function to check merged cells in worksheet by
+// mergeCellsParser provides a function to check merged cells in worksheet by
 // given axis.
 // given axis.
 func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
 func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
 	axis = strings.ToUpper(axis)
 	axis = strings.ToUpper(axis)
@@ -34,8 +43,8 @@ func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
 	return axis
 	return axis
 }
 }
 
 
-// SetCellValue provides function to set value of a cell. The following shows
-// the supported data types:
+// SetCellValue provides a function to set value of a cell. The following
+// shows the supported data types:
 //
 //
 //    int
 //    int
 //    int8
 //    int8
@@ -83,7 +92,7 @@ func (f *File) SetCellValue(sheet, axis string, value interface{}) {
 	}
 	}
 }
 }
 
 
-// setCellIntValue provides function to set int value of a cell.
+// setCellIntValue provides a function to set int value of a cell.
 func (f *File) setCellIntValue(sheet, axis string, value interface{}) {
 func (f *File) setCellIntValue(sheet, axis string, value interface{}) {
 	switch value.(type) {
 	switch value.(type) {
 	case int:
 	case int:
@@ -111,7 +120,7 @@ func (f *File) setCellIntValue(sheet, axis string, value interface{}) {
 	}
 	}
 }
 }
 
 
-// SetCellBool provides function to set bool type value of a cell by given
+// SetCellBool provides a function to set bool type value of a cell by given
 // worksheet name, cell coordinates and cell value.
 // worksheet name, cell coordinates and cell value.
 func (f *File) SetCellBool(sheet, axis string, value bool) {
 func (f *File) SetCellBool(sheet, axis string, value bool) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -139,10 +148,10 @@ func (f *File) SetCellBool(sheet, axis string, value bool) {
 	}
 	}
 }
 }
 
 
-// GetCellValue provides function to get formatted value from cell by given
-// worksheet name and axis in XLSX file. If it is possible to apply a format to
-// the cell value, it will do so, if not then an error will be returned, along
-// with the raw value of the cell.
+// GetCellValue provides a function to get formatted value from cell by given
+// worksheet name and axis in XLSX file. If it is possible to apply a format
+// to the cell value, it will do so, if not then an error will be returned,
+// along with the raw value of the cell.
 func (f *File) GetCellValue(sheet, axis string) string {
 func (f *File) GetCellValue(sheet, axis string) string {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
 	axis = f.mergeCellsParser(xlsx, axis)
 	axis = f.mergeCellsParser(xlsx, axis)
@@ -174,9 +183,9 @@ func (f *File) GetCellValue(sheet, axis string) string {
 	return ""
 	return ""
 }
 }
 
 
-// formattedValue provides function to returns a value after formatted. If it is
-// possible to apply a format to the cell value, it will do so, if not then an
-// error will be returned, along with the raw value of the cell.
+// formattedValue provides a function to returns a value after formatted. If
+// it is possible to apply a format to the cell value, it will do so, if not
+// then an error will be returned, along with the raw value of the cell.
 func (f *File) formattedValue(s int, v string) string {
 func (f *File) formattedValue(s int, v string) string {
 	if s == 0 {
 	if s == 0 {
 		return v
 		return v
@@ -189,7 +198,7 @@ func (f *File) formattedValue(s int, v string) string {
 	return v
 	return v
 }
 }
 
 
-// GetCellStyle provides function to get cell style index by given worksheet
+// GetCellStyle provides a function to get cell style index by given worksheet
 // name and cell coordinates.
 // name and cell coordinates.
 func (f *File) GetCellStyle(sheet, axis string) int {
 func (f *File) GetCellStyle(sheet, axis string) int {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -211,8 +220,8 @@ func (f *File) GetCellStyle(sheet, axis string) int {
 	return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
 	return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
 }
 }
 
 
-// GetCellFormula provides function to get formula from cell by given worksheet
-// name and axis in XLSX file.
+// GetCellFormula provides a function to get formula from cell by given
+// worksheet name and axis in XLSX file.
 func (f *File) GetCellFormula(sheet, axis string) string {
 func (f *File) GetCellFormula(sheet, axis string) string {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
 	axis = f.mergeCellsParser(xlsx, axis)
 	axis = f.mergeCellsParser(xlsx, axis)
@@ -276,7 +285,7 @@ func getSharedForumula(xlsx *xlsxWorksheet, si string) string {
 	return ""
 	return ""
 }
 }
 
 
-// SetCellFormula provides function to set cell formula by given string and
+// SetCellFormula provides a function to set cell formula by given string and
 // worksheet name.
 // worksheet name.
 func (f *File) SetCellFormula(sheet, axis, formula string) {
 func (f *File) SetCellFormula(sheet, axis, formula string) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -305,10 +314,10 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
 	}
 	}
 }
 }
 
 
-// SetCellHyperLink provides function to set cell hyperlink by given worksheet
-// name and link URL address. LinkType defines two types of hyperlink "External"
-// for web site or "Location" for moving to one of cell in this workbook. The
-// below is example for external link.
+// SetCellHyperLink provides a function to set cell hyperlink by given
+// worksheet name and link URL address. LinkType defines two types of
+// hyperlink "External" for web site or "Location" for moving to one of cell
+// in this workbook. The below is example for external link.
 //
 //
 //    xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
 //    xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
 //    // Set underline and font color style for the cell.
 //    // Set underline and font color style for the cell.
@@ -341,10 +350,10 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
 	xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
 	xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
 }
 }
 
 
-// GetCellHyperLink provides function to get cell hyperlink by given worksheet
-// name and axis. Boolean type value link will be ture if the cell has a
-// hyperlink and the target is the address of the hyperlink. Otherwise, the
-// value of link will be false and the value of the target will be a blank
+// GetCellHyperLink provides a function to get cell hyperlink by given
+// worksheet name and axis. Boolean type value link will be ture if the cell
+// has a hyperlink and the target is the address of the hyperlink. Otherwise,
+// the value of link will be false and the value of the target will be a blank
 // string. For example get hyperlink of Sheet1!H6:
 // string. For example get hyperlink of Sheet1!H6:
 //
 //
 //    link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
 //    link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
@@ -369,8 +378,8 @@ func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
 	return link, target
 	return link, target
 }
 }
 
 
-// MergeCell provides function to merge cells by given coordinate area and sheet
-// name. For example create a merged cell of D3:E9 on Sheet1:
+// MergeCell provides a function to merge cells by given coordinate area and
+// sheet name. For example create a merged cell of D3:E9 on Sheet1:
 //
 //
 //    xlsx.MergeCell("Sheet1", "D3", "E9")
 //    xlsx.MergeCell("Sheet1", "D3", "E9")
 //
 //
@@ -429,7 +438,7 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
 	}
 	}
 }
 }
 
 
-// SetCellInt provides function to set int type value of a cell by given
+// SetCellInt provides a function to set int type value of a cell by given
 // worksheet name, cell coordinates and cell value.
 // worksheet name, cell coordinates and cell value.
 func (f *File) SetCellInt(sheet, axis string, value int) {
 func (f *File) SetCellInt(sheet, axis string, value int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -453,7 +462,7 @@ func (f *File) SetCellInt(sheet, axis string, value int) {
 	xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
 	xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
 }
 }
 
 
-// prepareCellStyle provides function to prepare style index of cell in
+// prepareCellStyle provides a function to prepare style index of cell in
 // worksheet by given column index and style index.
 // worksheet by given column index and style index.
 func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
 func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
 	if xlsx.Cols != nil && style == 0 {
 	if xlsx.Cols != nil && style == 0 {
@@ -466,8 +475,8 @@ func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
 	return style
 	return style
 }
 }
 
 
-// SetCellStr provides function to set string type value of a cell. Total number
-// of characters that a cell can contain 32767 characters.
+// SetCellStr provides a function to set string type value of a cell. Total
+// number of characters that a cell can contain 32767 characters.
 func (f *File) SetCellStr(sheet, axis, value string) {
 func (f *File) SetCellStr(sheet, axis, value string) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
 	axis = f.mergeCellsParser(xlsx, axis)
 	axis = f.mergeCellsParser(xlsx, axis)
@@ -502,7 +511,7 @@ func (f *File) SetCellStr(sheet, axis, value string) {
 	xlsx.SheetData.Row[xAxis].C[yAxis].V = value
 	xlsx.SheetData.Row[xAxis].C[yAxis].V = value
 }
 }
 
 
-// SetCellDefault provides function to set string type value of a cell as
+// SetCellDefault provides a function to set string type value of a cell as
 // default format without escaping the cell.
 // default format without escaping the cell.
 func (f *File) SetCellDefault(sheet, axis, value string) {
 func (f *File) SetCellDefault(sheet, axis, value string) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -567,7 +576,7 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) {
 	}
 	}
 }
 }
 
 
-// checkCellInArea provides function to determine if a given coordinate is
+// checkCellInArea provides a function to determine if a given coordinate is
 // within an area.
 // within an area.
 func checkCellInArea(cell, area string) bool {
 func checkCellInArea(cell, area string) bool {
 	cell = strings.ToUpper(cell)
 	cell = strings.ToUpper(cell)

+ 52 - 41
chart.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -190,7 +199,7 @@ var (
 	}
 	}
 )
 )
 
 
-// parseFormatChartSet provides function to parse the format settings of the
+// parseFormatChartSet provides a function to parse the format settings of the
 // chart with default value.
 // chart with default value.
 func parseFormatChartSet(formatSet string) (*formatChart, error) {
 func parseFormatChartSet(formatSet string) (*formatChart, error) {
 	format := formatChart{
 	format := formatChart{
@@ -352,7 +361,9 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
 //    minimum
 //    minimum
 //
 //
 // reverse_order: Specifies that the categories or values on reverse order (orientation of the chart). The reverse_order property is optional. The default value is false.
 // reverse_order: Specifies that the categories or values on reverse order (orientation of the chart). The reverse_order property is optional. The default value is false.
+//
 // maximum: Specifies that the fixed maximum, 0 is auto. The maximum property is optional. The default value is auto.
 // maximum: Specifies that the fixed maximum, 0 is auto. The maximum property is optional. The default value is auto.
+//
 // minimum: Specifies that the fixed minimum, 0 is auto. The minimum property is optional. The default value is auto.
 // minimum: Specifies that the fixed minimum, 0 is auto. The minimum property is optional. The default value is auto.
 //
 //
 // Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
 // Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
@@ -377,7 +388,7 @@ func (f *File) AddChart(sheet, cell, format string) error {
 	return err
 	return err
 }
 }
 
 
-// countCharts provides function to get chart files count storage in the
+// countCharts provides a function to get chart files count storage in the
 // folder xl/charts.
 // folder xl/charts.
 func (f *File) countCharts() int {
 func (f *File) countCharts() int {
 	count := 0
 	count := 0
@@ -389,7 +400,7 @@ func (f *File) countCharts() int {
 	return count
 	return count
 }
 }
 
 
-// prepareDrawing provides function to prepare drawing ID and XML by given
+// prepareDrawing provides a function to prepare drawing ID and XML by given
 // drawingID, worksheet name and default drawingXML.
 // drawingID, worksheet name and default drawingXML.
 func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
 func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
 	sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
@@ -406,8 +417,8 @@ func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawing
 	return drawingID, drawingXML
 	return drawingID, drawingXML
 }
 }
 
 
-// addChart provides function to create chart as xl/charts/chart%d.xml by given
-// format sets.
+// addChart provides a function to create chart as xl/charts/chart%d.xml by
+// given format sets.
 func (f *File) addChart(formatSet *formatChart) {
 func (f *File) addChart(formatSet *formatChart) {
 	count := f.countCharts()
 	count := f.countCharts()
 	xlsxChartSpace := xlsxChartSpace{
 	xlsxChartSpace := xlsxChartSpace{
@@ -562,7 +573,7 @@ func (f *File) addChart(formatSet *formatChart) {
 	f.saveFileList(media, chart)
 	f.saveFileList(media, chart)
 }
 }
 
 
-// drawBaseChart provides function to draw the c:plotArea element for bar,
+// drawBaseChart provides a function to draw the c:plotArea element for bar,
 // and column series charts by given format sets.
 // and column series charts by given format sets.
 func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
 	c := cCharts{
 	c := cCharts{
@@ -659,7 +670,7 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
 	return charts[formatSet.Type]
 	return charts[formatSet.Type]
 }
 }
 
 
-// drawDoughnutChart provides function to draw the c:plotArea element for
+// drawDoughnutChart provides a function to draw the c:plotArea element for
 // doughnut chart by given format sets.
 // doughnut chart by given format sets.
 func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
@@ -673,8 +684,8 @@ func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawLineChart provides function to draw the c:plotArea element for line chart
-// by given format sets.
+// drawLineChart provides a function to draw the c:plotArea element for line
+// chart by given format sets.
 func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
 		LineChart: &cCharts{
 		LineChart: &cCharts{
@@ -699,8 +710,8 @@ func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawPieChart provides function to draw the c:plotArea element for pie chart
-// by given format sets.
+// drawPieChart provides a function to draw the c:plotArea element for pie
+// chart by given format sets.
 func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
 		PieChart: &cCharts{
 		PieChart: &cCharts{
@@ -712,8 +723,8 @@ func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawPie3DChart provides function to draw the c:plotArea element for 3D pie
-// chart by given format sets.
+// drawPie3DChart provides a function to draw the c:plotArea element for 3D
+// pie chart by given format sets.
 func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
 		Pie3DChart: &cCharts{
 		Pie3DChart: &cCharts{
@@ -725,7 +736,7 @@ func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawRadarChart provides function to draw the c:plotArea element for radar
+// drawRadarChart provides a function to draw the c:plotArea element for radar
 // chart by given format sets.
 // chart by given format sets.
 func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
@@ -748,8 +759,8 @@ func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawScatterChart provides function to draw the c:plotArea element for scatter
-// chart by given format sets.
+// drawScatterChart provides a function to draw the c:plotArea element for
+// scatter chart by given format sets.
 func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
 func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 	return &cPlotArea{
 		ScatterChart: &cCharts{
 		ScatterChart: &cCharts{
@@ -771,8 +782,8 @@ func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
 	}
 	}
 }
 }
 
 
-// drawChartSeries provides function to draw the c:ser element by given format
-// sets.
+// drawChartSeries provides a function to draw the c:ser element by given
+// format sets.
 func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
 func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
 	ser := []cSer{}
 	ser := []cSer{}
 	for k := range formatSet.Series {
 	for k := range formatSet.Series {
@@ -797,7 +808,7 @@ func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
 	return &ser
 	return &ser
 }
 }
 
 
-// drawChartSeriesSpPr provides function to draw the c:spPr element by given
+// drawChartSeriesSpPr provides a function to draw the c:spPr element by given
 // format sets.
 // format sets.
 func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
 func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
 	spPrScatter := &cSpPr{
 	spPrScatter := &cSpPr{
@@ -819,8 +830,8 @@ func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
 	return chartSeriesSpPr[formatSet.Type]
 	return chartSeriesSpPr[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesDPt provides function to draw the c:dPt element by given data
-// index and format sets.
+// drawChartSeriesDPt provides a function to draw the c:dPt element by given
+// data index and format sets.
 func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
 func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
 	dpt := []*cDPt{{
 	dpt := []*cDPt{{
 		IDx:      &attrValInt{Val: i},
 		IDx:      &attrValInt{Val: i},
@@ -848,8 +859,8 @@ func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
 	return chartSeriesDPt[formatSet.Type]
 	return chartSeriesDPt[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesCat provides function to draw the c:cat element by given chart
-// series and format sets.
+// drawChartSeriesCat provides a function to draw the c:cat element by given
+// chart series and format sets.
 func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat {
 func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat {
 	cat := &cCat{
 	cat := &cCat{
 		StrRef: &cStrRef{
 		StrRef: &cStrRef{
@@ -860,8 +871,8 @@ func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *
 	return chartSeriesCat[formatSet.Type]
 	return chartSeriesCat[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesVal provides function to draw the c:val element by given chart
-// series and format sets.
+// drawChartSeriesVal provides a function to draw the c:val element by given
+// chart series and format sets.
 func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal {
 func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal {
 	val := &cVal{
 	val := &cVal{
 		NumRef: &cNumRef{
 		NumRef: &cNumRef{
@@ -872,8 +883,8 @@ func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *
 	return chartSeriesVal[formatSet.Type]
 	return chartSeriesVal[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesMarker provides function to draw the c:marker element by given
-// data index and format sets.
+// drawChartSeriesMarker provides a function to draw the c:marker element by
+// given data index and format sets.
 func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
 func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
 	marker := &cMarker{
 	marker := &cMarker{
 		Symbol: &attrValString{Val: "circle"},
 		Symbol: &attrValString{Val: "circle"},
@@ -898,7 +909,7 @@ func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
 	return chartSeriesMarker[formatSet.Type]
 	return chartSeriesMarker[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesXVal provides function to draw the c:xVal element by given
+// drawChartSeriesXVal provides a function to draw the c:xVal element by given
 // chart series and format sets.
 // chart series and format sets.
 func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat {
 func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat {
 	cat := &cCat{
 	cat := &cCat{
@@ -910,7 +921,7 @@ func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart)
 	return chartSeriesXVal[formatSet.Type]
 	return chartSeriesXVal[formatSet.Type]
 }
 }
 
 
-// drawChartSeriesYVal provides function to draw the c:yVal element by given
+// drawChartSeriesYVal provides a function to draw the c:yVal element by given
 // chart series and format sets.
 // chart series and format sets.
 func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal {
 func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal {
 	val := &cVal{
 	val := &cVal{
@@ -922,8 +933,8 @@ func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart)
 	return chartSeriesYVal[formatSet.Type]
 	return chartSeriesYVal[formatSet.Type]
 }
 }
 
 
-// drawChartDLbls provides function to draw the c:dLbls element by given format
-// sets.
+// drawChartDLbls provides a function to draw the c:dLbls element by given
+// format sets.
 func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 	return &cDLbls{
 	return &cDLbls{
 		ShowLegendKey:   &attrValBool{Val: formatSet.Legend.ShowLegendKey},
 		ShowLegendKey:   &attrValBool{Val: formatSet.Legend.ShowLegendKey},
@@ -936,15 +947,15 @@ func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 	}
 	}
 }
 }
 
 
-// drawChartSeriesDLbls provides function to draw the c:dLbls element by given
-// format sets.
+// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
+// given format sets.
 func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
 func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
 	dLbls := f.drawChartDLbls(formatSet)
 	dLbls := f.drawChartDLbls(formatSet)
 	chartSeriesDLbls := map[string]*cDLbls{Bar: dLbls, BarStacked: dLbls, BarPercentStacked: dLbls, Bar3DClustered: dLbls, Bar3DStacked: dLbls, Bar3DPercentStacked: dLbls, Col: dLbls, ColStacked: dLbls, ColPercentStacked: dLbls, Col3DClustered: dLbls, Col3D: dLbls, Col3DStacked: dLbls, Col3DPercentStacked: dLbls, Doughnut: dLbls, Line: dLbls, Pie: dLbls, Pie3D: dLbls, Radar: dLbls, Scatter: nil}
 	chartSeriesDLbls := map[string]*cDLbls{Bar: dLbls, BarStacked: dLbls, BarPercentStacked: dLbls, Bar3DClustered: dLbls, Bar3DStacked: dLbls, Bar3DPercentStacked: dLbls, Col: dLbls, ColStacked: dLbls, ColPercentStacked: dLbls, Col3DClustered: dLbls, Col3D: dLbls, Col3DStacked: dLbls, Col3DPercentStacked: dLbls, Doughnut: dLbls, Line: dLbls, Pie: dLbls, Pie3D: dLbls, Radar: dLbls, Scatter: nil}
 	return chartSeriesDLbls[formatSet.Type]
 	return chartSeriesDLbls[formatSet.Type]
 }
 }
 
 
-// drawPlotAreaCatAx provides function to draw the c:catAx element.
+// drawPlotAreaCatAx provides a function to draw the c:catAx element.
 func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 	min := &attrValFloat{Val: formatSet.XAxis.Minimum}
 	min := &attrValFloat{Val: formatSet.XAxis.Minimum}
 	max := &attrValFloat{Val: formatSet.XAxis.Maximum}
 	max := &attrValFloat{Val: formatSet.XAxis.Maximum}
@@ -983,7 +994,7 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 	}
 	}
 }
 }
 
 
-// drawPlotAreaValAx provides function to draw the c:valAx element.
+// drawPlotAreaValAx provides a function to draw the c:valAx element.
 func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 	min := &attrValFloat{Val: formatSet.YAxis.Minimum}
 	min := &attrValFloat{Val: formatSet.YAxis.Minimum}
 	max := &attrValFloat{Val: formatSet.YAxis.Maximum}
 	max := &attrValFloat{Val: formatSet.YAxis.Maximum}
@@ -1019,7 +1030,7 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 	}
 	}
 }
 }
 
 
-// drawPlotAreaSpPr provides function to draw the c:spPr element.
+// drawPlotAreaSpPr provides a function to draw the c:spPr element.
 func (f *File) drawPlotAreaSpPr() *cSpPr {
 func (f *File) drawPlotAreaSpPr() *cSpPr {
 	return &cSpPr{
 	return &cSpPr{
 		Ln: &aLn{
 		Ln: &aLn{
@@ -1038,7 +1049,7 @@ func (f *File) drawPlotAreaSpPr() *cSpPr {
 	}
 	}
 }
 }
 
 
-// drawPlotAreaTxPr provides function to draw the c:txPr element.
+// drawPlotAreaTxPr provides a function to draw the c:txPr element.
 func (f *File) drawPlotAreaTxPr() *cTxPr {
 func (f *File) drawPlotAreaTxPr() *cTxPr {
 	return &cTxPr{
 	return &cTxPr{
 		BodyPr: aBodyPr{
 		BodyPr: aBodyPr{
@@ -1077,8 +1088,8 @@ func (f *File) drawPlotAreaTxPr() *cTxPr {
 	}
 	}
 }
 }
 
 
-// drawingParser provides function to parse drawingXML. In order to solve the
-// problem that the label structure is changed after serialization and
+// drawingParser provides a function to parse drawingXML. In order to solve
+// the problem that the label structure is changed after serialization and
 // deserialization, two different structures: decodeWsDr and encodeWsDr are
 // deserialization, two different structures: decodeWsDr and encodeWsDr are
 // defined.
 // defined.
 func (f *File) drawingParser(drawingXML string, content *xlsxWsDr) int {
 func (f *File) drawingParser(drawingXML string, content *xlsxWsDr) int {
@@ -1105,8 +1116,8 @@ func (f *File) drawingParser(drawingXML string, content *xlsxWsDr) int {
 	return cNvPrID
 	return cNvPrID
 }
 }
 
 
-// addDrawingChart provides function to add chart graphic frame by given sheet,
-// drawingXML, cell, width, height, relationship index and format sets.
+// addDrawingChart provides a function to add chart graphic frame by given
+// sheet, drawingXML, cell, width, height, relationship index and format sets.
 func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) {
 func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) {
 	cell = strings.ToUpper(cell)
 	cell = strings.ToUpper(cell)
 	fromCol := string(strings.Map(letterOnlyMapF, cell))
 	fromCol := string(strings.Map(letterOnlyMapF, cell))

+ 23 - 14
col.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -121,8 +130,8 @@ func (f *File) SetColOutlineLevel(sheet, column string, level uint8) {
 	xlsx.Cols.Col = append(xlsx.Cols.Col, col)
 	xlsx.Cols.Col = append(xlsx.Cols.Col, col)
 }
 }
 
 
-// SetColWidth provides function to set the width of a single column or multiple
-// columns. For example:
+// SetColWidth provides a function to set the width of a single column or
+// multiple columns. For example:
 //
 //
 //    xlsx := excelize.NewFile()
 //    xlsx := excelize.NewFile()
 //    xlsx.SetColWidth("Sheet1", "A", "H", 20)
 //    xlsx.SetColWidth("Sheet1", "A", "H", 20)
@@ -259,8 +268,8 @@ func (f *File) positionObjectPixels(sheet string, colStart, rowStart, x1, y1, wi
 	return colStart, rowStart, xAbs, yAbs, colEnd, rowEnd, x2, y2
 	return colStart, rowStart, xAbs, yAbs, colEnd, rowEnd, x2, y2
 }
 }
 
 
-// getColWidth provides function to get column width in pixels by given sheet
-// name and column index.
+// getColWidth provides a function to get column width in pixels by given
+// sheet name and column index.
 func (f *File) getColWidth(sheet string, col int) int {
 func (f *File) getColWidth(sheet string, col int) int {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
 	if xlsx.Cols != nil {
 	if xlsx.Cols != nil {
@@ -278,8 +287,8 @@ func (f *File) getColWidth(sheet string, col int) int {
 	return int(defaultColWidthPixels)
 	return int(defaultColWidthPixels)
 }
 }
 
 
-// GetColWidth provides function to get column width by given worksheet name and
-// column index.
+// GetColWidth provides a function to get column width by given worksheet name
+// and column index.
 func (f *File) GetColWidth(sheet, column string) float64 {
 func (f *File) GetColWidth(sheet, column string) float64 {
 	col := TitleToNumber(strings.ToUpper(column)) + 1
 	col := TitleToNumber(strings.ToUpper(column)) + 1
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -298,8 +307,8 @@ func (f *File) GetColWidth(sheet, column string) float64 {
 	return defaultColWidthPixels
 	return defaultColWidthPixels
 }
 }
 
 
-// InsertCol provides function to insert a new column before given column index.
-// For example, create a new column before column C in Sheet1:
+// InsertCol provides a function to insert a new column before given column
+// index. For example, create a new column before column C in Sheet1:
 //
 //
 //    xlsx.InsertCol("Sheet1", "C")
 //    xlsx.InsertCol("Sheet1", "C")
 //
 //
@@ -308,8 +317,8 @@ func (f *File) InsertCol(sheet, column string) {
 	f.adjustHelper(sheet, col, -1, 1)
 	f.adjustHelper(sheet, col, -1, 1)
 }
 }
 
 
-// RemoveCol provides function to remove single column by given worksheet name
-// and column index. For example, remove column C in Sheet1:
+// RemoveCol provides a function to remove single column by given worksheet
+// name and column index. For example, remove column C in Sheet1:
 //
 //
 //    xlsx.RemoveCol("Sheet1", "C")
 //    xlsx.RemoveCol("Sheet1", "C")
 //
 //
@@ -346,10 +355,10 @@ func completeCol(xlsx *xlsxWorksheet, row, cell int) {
 	}
 	}
 }
 }
 
 
-// convertColWidthToPixels provieds function to convert the width of a cell from
-// user's units to pixels. Excel rounds the column width to the nearest pixel.
-// If the width hasn't been set by the user we use the default value. If the
-// column is hidden it has a value of zero.
+// convertColWidthToPixels provieds function to convert the width of a cell
+// from user's units to pixels. Excel rounds the column width to the nearest
+// pixel. If the width hasn't been set by the user we use the default value.
+// If the column is hidden it has a value of zero.
 func convertColWidthToPixels(width float64) float64 {
 func convertColWidthToPixels(width float64) float64 {
 	var padding float64 = 5
 	var padding float64 = 5
 	var pixels float64
 	var pixels float64

+ 65 - 14
comment.go

@@ -1,14 +1,24 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"encoding/xml"
 	"encoding/xml"
+	"fmt"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
 
 
-// parseFormatCommentsSet provides function to parse the format settings of the
-// comment with default value.
+// parseFormatCommentsSet provides a function to parse the format settings of
+// the comment with default value.
 func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
 func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
 	format := formatComment{
 	format := formatComment{
 		Author: "Author:",
 		Author: "Author:",
@@ -18,6 +28,36 @@ func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
 	return &format, err
 	return &format, err
 }
 }
 
 
+// GetComments retrieves all comments and returns a map of worksheet name to
+// the worksheet comments.
+func (f *File) GetComments() (comments map[string][]Comment) {
+	comments = map[string][]Comment{}
+	for n := range f.sheetMap {
+		commentID := f.GetSheetIndex(n)
+		commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
+		c, ok := f.XLSX[commentsXML]
+		if ok {
+			d := xlsxComments{}
+			xml.Unmarshal([]byte(c), &d)
+			sheetComments := []Comment{}
+			for _, comment := range d.CommentList.Comment {
+				sheetComment := Comment{}
+				if comment.AuthorID < len(d.Authors) {
+					sheetComment.Author = d.Authors[comment.AuthorID].Author
+				}
+				sheetComment.Ref = comment.Ref
+				sheetComment.AuthorID = comment.AuthorID
+				for _, text := range comment.Text.R {
+					sheetComment.Text += text.T
+				}
+				sheetComments = append(sheetComments, sheetComment)
+			}
+			comments[n] = sheetComments
+		}
+	}
+	return
+}
+
 // AddComment provides the method to add comment in a sheet by given worksheet
 // AddComment provides the method to add comment in a sheet by given worksheet
 // index, cell and format set (such as author and text). Note that the max
 // index, cell and format set (such as author and text). Note that the max
 // author length is 255 and the max text length is 32512. For example, add a
 // author length is 255 and the max text length is 32512. For example, add a
@@ -49,14 +89,23 @@ func (f *File) AddComment(sheet, cell, format string) error {
 	}
 	}
 	commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
 	commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
 	f.addComment(commentsXML, cell, formatSet)
 	f.addComment(commentsXML, cell, formatSet)
-	f.addDrawingVML(commentID, drawingVML, cell)
+	var colCount int
+	for i, l := range strings.Split(formatSet.Text, "\n") {
+		if ll := len(l); ll > colCount {
+			if i == 0 {
+				ll += len(formatSet.Author)
+			}
+			colCount = ll
+		}
+	}
+	f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
 	f.addContentTypePart(commentID, "comments")
 	f.addContentTypePart(commentID, "comments")
 	return err
 	return err
 }
 }
 
 
-// addDrawingVML provides function to create comment as
+// addDrawingVML provides a function to create comment as
 // xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
 // xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
-func (f *File) addDrawingVML(commentID int, drawingVML, cell string) {
+func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) {
 	col := string(strings.Map(letterOnlyMapF, cell))
 	col := string(strings.Map(letterOnlyMapF, cell))
 	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
 	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
 	xAxis := row - 1
 	xAxis := row - 1
@@ -83,7 +132,7 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string) {
 			},
 			},
 			VPath: &vPath{
 			VPath: &vPath{
 				Gradientshapeok: "t",
 				Gradientshapeok: "t",
-				Connecttype:     "rect",
+				Connecttype:     "miter",
 			},
 			},
 		},
 		},
 	}
 	}
@@ -113,10 +162,12 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string) {
 		},
 		},
 		ClientData: &xClientData{
 		ClientData: &xClientData{
 			ObjectType: "Note",
 			ObjectType: "Note",
-			Anchor:     "3, 15, 8, 6, 4, 54, 13, 2",
-			AutoFill:   "False",
-			Row:        xAxis,
-			Column:     yAxis,
+			Anchor: fmt.Sprintf(
+				"%d, 23, %d, 0, %d, %d, %d, 5",
+				1+yAxis, 1+xAxis, 2+yAxis+lineCount, colCount+yAxis, 2+xAxis+lineCount),
+			AutoFill: "True",
+			Row:      xAxis,
+			Column:   yAxis,
 		},
 		},
 	}
 	}
 	s, _ := xml.Marshal(sp)
 	s, _ := xml.Marshal(sp)
@@ -149,8 +200,8 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string) {
 	f.XLSX[drawingVML] = v
 	f.XLSX[drawingVML] = v
 }
 }
 
 
-// addComment provides function to create chart as xl/comments%d.xml by given
-// cell and format sets.
+// addComment provides a function to create chart as xl/comments%d.xml by
+// given cell and format sets.
 func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
 func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
 	a := formatSet.Author
 	a := formatSet.Author
 	t := formatSet.Text
 	t := formatSet.Text
@@ -209,8 +260,8 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
 	f.saveFileList(commentsXML, v)
 	f.saveFileList(commentsXML, v)
 }
 }
 
 
-// countComments provides function to get comments files count storage in the
-// folder xl.
+// countComments provides a function to get comments files count storage in
+// the folder xl.
 func (f *File) countComments() int {
 func (f *File) countComments() int {
 	count := 0
 	count := 0
 	for k := range f.XLSX {
 	for k := range f.XLSX {

+ 233 - 0
datavalidation.go

@@ -0,0 +1,233 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
+package excelize
+
+import (
+	"fmt"
+	"strings"
+)
+
+// DataValidationType defined the type of data validation.
+type DataValidationType int
+
+// Data validation types.
+const (
+	_DataValidationType = iota
+	typeNone            // inline use
+	DataValidationTypeCustom
+	DataValidationTypeDate
+	DataValidationTypeDecimal
+	typeList // inline use
+	DataValidationTypeTextLeng
+	DataValidationTypeTime
+	// DataValidationTypeWhole Integer
+	DataValidationTypeWhole
+)
+
+const (
+	// dataValidationFormulaStrLen 255 characters+ 2 quotes
+	dataValidationFormulaStrLen = 257
+	// dataValidationFormulaStrLenErr
+	dataValidationFormulaStrLenErr = "data validation must be 0-255 characters"
+)
+
+// DataValidationErrorStyle defined the style of data validation error alert.
+type DataValidationErrorStyle int
+
+// Data validation error styles.
+const (
+	_ DataValidationErrorStyle = iota
+	DataValidationErrorStyleStop
+	DataValidationErrorStyleWarning
+	DataValidationErrorStyleInformation
+)
+
+// Data validation error styles.
+const (
+	styleStop        = "stop"
+	styleWarning     = "warning"
+	styleInformation = "information"
+)
+
+// DataValidationOperator operator enum.
+type DataValidationOperator int
+
+// Data validation operators.
+const (
+	_DataValidationOperator = iota
+	DataValidationOperatorBetween
+	DataValidationOperatorEqual
+	DataValidationOperatorGreaterThan
+	DataValidationOperatorGreaterThanOrEqual
+	DataValidationOperatorLessThan
+	DataValidationOperatorLessThanOrEqual
+	DataValidationOperatorNotBetween
+	DataValidationOperatorNotEqual
+)
+
+// NewDataValidation return data validation struct.
+func NewDataValidation(allowBlank bool) *DataValidation {
+	return &DataValidation{
+		AllowBlank:       allowBlank,
+		ShowErrorMessage: false,
+		ShowInputMessage: false,
+	}
+}
+
+// SetError set error notice.
+func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
+	dd.Error = &msg
+	dd.ErrorTitle = &title
+	strStyle := styleStop
+	switch style {
+	case DataValidationErrorStyleStop:
+		strStyle = styleStop
+	case DataValidationErrorStyleWarning:
+		strStyle = styleWarning
+	case DataValidationErrorStyleInformation:
+		strStyle = styleInformation
+
+	}
+	dd.ShowErrorMessage = true
+	dd.ErrorStyle = &strStyle
+}
+
+// SetInput set prompt notice.
+func (dd *DataValidation) SetInput(title, msg string) {
+	dd.ShowInputMessage = true
+	dd.PromptTitle = &title
+	dd.Prompt = &msg
+}
+
+// SetDropList data validation list.
+func (dd *DataValidation) SetDropList(keys []string) error {
+	dd.Formula1 = "\"" + strings.Join(keys, ",") + "\""
+	dd.Type = convDataValidationType(typeList)
+	return nil
+}
+
+// SetRange provides function to set data validation range in drop list.
+func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error {
+	formula1 := fmt.Sprintf("%d", f1)
+	formula2 := fmt.Sprintf("%d", f2)
+	if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) {
+		return fmt.Errorf(dataValidationFormulaStrLenErr)
+	}
+
+	dd.Formula1 = formula1
+	dd.Formula2 = formula2
+	dd.Type = convDataValidationType(t)
+	dd.Operator = convDataValidationOperatior(o)
+	return nil
+}
+
+// SetSqrefDropList provides set data validation on a range with source
+// reference range of the worksheet by given data validation object and
+// worksheet name. The data validation object can be created by
+// NewDataValidation function. For example, set data validation on
+// Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
+// in-cell dropdown by allowing list source:
+//
+//     dvRange := excelize.NewDataValidation(true)
+//     dvRange.Sqref = "A7:B8"
+//     dvRange.SetSqrefDropList("E1:E3", true)
+//     xlsx.AddDataValidation("Sheet1", dvRange)
+//
+func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error {
+	if isCurrentSheet {
+		dd.Formula1 = sqref
+		dd.Type = convDataValidationType(typeList)
+		return nil
+	}
+	return fmt.Errorf("cross-sheet sqref cell are not supported")
+}
+
+// SetSqref provides function to set data validation range in drop list.
+func (dd *DataValidation) SetSqref(sqref string) {
+	if dd.Sqref == "" {
+		dd.Sqref = sqref
+	} else {
+		dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref)
+	}
+}
+
+// convDataValidationType get excel data validation type.
+func convDataValidationType(t DataValidationType) string {
+	typeMap := map[DataValidationType]string{
+		typeNone:                   "none",
+		DataValidationTypeCustom:   "custom",
+		DataValidationTypeDate:     "date",
+		DataValidationTypeDecimal:  "decimal",
+		typeList:                   "list",
+		DataValidationTypeTextLeng: "textLength",
+		DataValidationTypeTime:     "time",
+		DataValidationTypeWhole:    "whole",
+	}
+
+	return typeMap[t]
+
+}
+
+// convDataValidationOperatior get excel data validation operator.
+func convDataValidationOperatior(o DataValidationOperator) string {
+	typeMap := map[DataValidationOperator]string{
+		DataValidationOperatorBetween:            "between",
+		DataValidationOperatorEqual:              "equal",
+		DataValidationOperatorGreaterThan:        "greaterThan",
+		DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
+		DataValidationOperatorLessThan:           "lessThan",
+		DataValidationOperatorLessThanOrEqual:    "lessThanOrEqual",
+		DataValidationOperatorNotBetween:         "notBetween",
+		DataValidationOperatorNotEqual:           "notEqual",
+	}
+
+	return typeMap[o]
+
+}
+
+// AddDataValidation provides set data validation on a range of the worksheet
+// by given data validation object and worksheet name. The data validation
+// object can be created by NewDataValidation function.
+//
+// Example 1, set data validation on Sheet1!A1:B2 with validation criteria
+// settings, show error alert after invalid data is entered with "Stop" style
+// and custom title "error body":
+//
+//     dvRange := excelize.NewDataValidation(true)
+//     dvRange.Sqref = "A1:B2"
+//     dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
+//     dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
+//     xlsx.AddDataValidation("Sheet1", dvRange)
+//
+// Example 2, set data validation on Sheet1!A3:B4 with validation criteria
+// settings, and show input message when cell is selected:
+//
+//     dvRange = excelize.NewDataValidation(true)
+//     dvRange.Sqref = "A3:B4"
+//     dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
+//     dvRange.SetInput("input title", "input body")
+//     xlsx.AddDataValidation("Sheet1", dvRange)
+//
+// Example 3, set data validation on Sheet1!A5:B6 with validation criteria
+// settings, create in-cell dropdown by allowing list source:
+//
+//     dvRange = excelize.NewDataValidation(true)
+//     dvRange.Sqref = "A5:B6"
+//     dvRange.SetDropList([]string{"1", "2", "3"})
+//     xlsx.AddDataValidation("Sheet1", dvRange)
+//
+func (f *File) AddDataValidation(sheet string, dv *DataValidation) {
+	xlsx := f.workSheetReader(sheet)
+	if nil == xlsx.DataValidations {
+		xlsx.DataValidations = new(xlsxDataValidations)
+	}
+	xlsx.DataValidations.DataValidation = append(xlsx.DataValidations.DataValidation, dv)
+	xlsx.DataValidations.Count = len(xlsx.DataValidations.DataValidation)
+}

+ 57 - 0
datavalidation_test.go

@@ -0,0 +1,57 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
+package excelize
+
+import "testing"
+
+func TestDataValidation(t *testing.T) {
+	xlsx := NewFile()
+
+	dvRange := NewDataValidation(true)
+	dvRange.Sqref = "A1:B2"
+	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween)
+	dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body")
+	dvRange.SetError(DataValidationErrorStyleWarning, "error title", "error body")
+	dvRange.SetError(DataValidationErrorStyleInformation, "error title", "error body")
+	xlsx.AddDataValidation("Sheet1", dvRange)
+
+	dvRange = NewDataValidation(true)
+	dvRange.Sqref = "A3:B4"
+	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
+	dvRange.SetInput("input title", "input body")
+	xlsx.AddDataValidation("Sheet1", dvRange)
+
+	dvRange = NewDataValidation(true)
+	dvRange.Sqref = "A5:B6"
+	dvRange.SetDropList([]string{"1", "2", "3"})
+	xlsx.AddDataValidation("Sheet1", dvRange)
+
+	xlsx.SetCellStr("Sheet1", "E1", "E1")
+	xlsx.SetCellStr("Sheet1", "E2", "E2")
+	xlsx.SetCellStr("Sheet1", "E3", "E3")
+	dvRange = NewDataValidation(true)
+	dvRange.SetSqref("A7:B8")
+	dvRange.SetSqref("A7:B8")
+	dvRange.SetSqrefDropList("$E$1:$E$3", true)
+	err := dvRange.SetSqrefDropList("$E$1:$E$3", false)
+	t.Log(err)
+	xlsx.AddDataValidation("Sheet1", dvRange)
+
+	dvRange = NewDataValidation(true)
+	dvRange.SetDropList(make([]string, 258))
+	err = dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
+	t.Log(err)
+
+	// Test write file to given path.
+	err = xlsx.SaveAs("./test/Book_data_validation.xlsx")
+	if err != nil {
+		t.Error(err)
+	}
+}

+ 45 - 14
date.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -8,17 +17,31 @@ import (
 // timeLocationUTC defined the UTC time location.
 // timeLocationUTC defined the UTC time location.
 var timeLocationUTC, _ = time.LoadLocation("UTC")
 var timeLocationUTC, _ = time.LoadLocation("UTC")
 
 
-// timeToUTCTime provides function to convert time to UTC time.
+// timeToUTCTime provides a function to convert time to UTC time.
 func timeToUTCTime(t time.Time) time.Time {
 func timeToUTCTime(t time.Time) time.Time {
 	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
 	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
 }
 }
 
 
-// timeToExcelTime provides function to convert time to Excel time.
+// timeToExcelTime provides a function to convert time to Excel time.
 func timeToExcelTime(t time.Time) float64 {
 func timeToExcelTime(t time.Time) float64 {
-	return float64(t.UnixNano())/8.64e13 + 25569.0
+	// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
+	var excelTime float64
+	var deltaDays int64
+	excelTime = 0
+	deltaDays = 290 * 364
+	// check if UnixNano would be out of int64 range
+	for t.Unix() > deltaDays*24*60*60 {
+		// reduce by aprox. 290 years, which is max for int64 nanoseconds
+		delta := time.Duration(deltaDays) * 24 * time.Hour
+		excelTime = excelTime + float64(deltaDays)
+		t = t.Add(-delta)
+	}
+	// finally add remainder of UnixNano to keep nano precision
+	// and 25569 which is days between 1900 and 1970
+	return excelTime + float64(t.UnixNano())/8.64e13 + 25569.0
 }
 }
 
 
-// shiftJulianToNoon provides function to process julian date to noon.
+// shiftJulianToNoon provides a function to process julian date to noon.
 func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
 func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
 	switch {
 	switch {
 	case -0.5 < julianFraction && julianFraction < 0.5:
 	case -0.5 < julianFraction && julianFraction < 0.5:
@@ -33,7 +56,7 @@ func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
 	return julianDays, julianFraction
 	return julianDays, julianFraction
 }
 }
 
 
-// fractionOfADay provides function to return the integer values for hour,
+// fractionOfADay provides a function to return the integer values for hour,
 // minutes, seconds and nanoseconds that comprised a given fraction of a day.
 // minutes, seconds and nanoseconds that comprised a given fraction of a day.
 // values would round to 1 us.
 // values would round to 1 us.
 func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
 func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
@@ -54,7 +77,7 @@ func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int)
 	return
 	return
 }
 }
 
 
-// julianDateToGregorianTime provides function to convert julian date to
+// julianDateToGregorianTime provides a function to convert julian date to
 // gregorian time.
 // gregorian time.
 func julianDateToGregorianTime(part1, part2 float64) time.Time {
 func julianDateToGregorianTime(part1, part2 float64) time.Time {
 	part1I, part1F := math.Modf(part1)
 	part1I, part1F := math.Modf(part1)
@@ -67,12 +90,12 @@ func julianDateToGregorianTime(part1, part2 float64) time.Time {
 	return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
 	return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
 }
 }
 
 
-// By this point generations of programmers have repeated the algorithm sent to
-// the editor of "Communications of the ACM" in 1968 (published in CACM, volume
-// 11, number 10, October 1968, p.657). None of those programmers seems to have
-// found it necessary to explain the constants or variable names set out by
-// Henry F. Fliegel and Thomas C. Van Flandern.  Maybe one day I'll buy that
-// jounal and expand an explanation here - that day is not today.
+// By this point generations of programmers have repeated the algorithm sent
+// to the editor of "Communications of the ACM" in 1968 (published in CACM,
+// volume 11, number 10, October 1968, p.657). None of those programmers seems
+// to have found it necessary to explain the constants or variable names set
+// out by Henry F. Fliegel and Thomas C. Van Flandern.  Maybe one day I'll buy
+// that jounal and expand an explanation here - that day is not today.
 func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
 func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
 	l := jd + 68569
 	l := jd + 68569
 	n := (4 * l) / 146097
 	n := (4 * l) / 146097
@@ -87,9 +110,10 @@ func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
 	return d, m, y
 	return d, m, y
 }
 }
 
 
-// timeFromExcelTime provides function to convert an excelTime representation
-// (stored as a floating point number) to a time.Time.
+// timeFromExcelTime provides a function to convert an excelTime
+// representation (stored as a floating point number) to a time.Time.
 func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
 func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
+	const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years
 	var date time.Time
 	var date time.Time
 	var intPart = int64(excelTime)
 	var intPart = int64(excelTime)
 	// Excel uses Julian dates prior to March 1st 1900, and Gregorian
 	// Excel uses Julian dates prior to March 1st 1900, and Gregorian
@@ -113,6 +137,13 @@ func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
 	} else {
 	} else {
 		date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
 		date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
 	}
 	}
+
+	// Duration is limited to aprox. 290 years
+	for intPart > MDD {
+		durationDays := time.Duration(MDD) * time.Hour * 24
+		date = date.Add(durationDays)
+		intPart = intPart - MDD
+	}
 	durationDays := time.Duration(intPart) * time.Hour * 24
 	durationDays := time.Duration(intPart) * time.Hour * 24
 	durationPart := time.Duration(dayNanoSeconds * floatPart)
 	durationPart := time.Duration(dayNanoSeconds * floatPart)
 	return date.Add(durationDays).Add(durationPart)
 	return date.Add(durationDays).Add(durationPart)

+ 42 - 0
date_test.go

@@ -0,0 +1,42 @@
+package excelize
+
+import (
+	"testing"
+	"time"
+)
+
+type dateTest struct {
+	ExcelValue float64
+	GoValue    time.Time
+}
+
+func TestTimeToExcelTime(t *testing.T) {
+	trueExpectedInputList := []dateTest{
+		{0.0, time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)},
+		{25569.0, time.Unix(0, 0)},
+		{43269.0, time.Date(2018, 6, 18, 0, 0, 0, 0, time.UTC)},
+		{401769.0, time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)},
+	}
+
+	for _, test := range trueExpectedInputList {
+		if test.ExcelValue != timeToExcelTime(test.GoValue) {
+			t.Fatalf("Expected %v from %v = true, got %v\n", test.ExcelValue, test.GoValue, timeToExcelTime(test.GoValue))
+		}
+	}
+}
+
+func TestTimeFromExcelTime(t *testing.T) {
+	trueExpectedInputList := []dateTest{
+		{0.0, time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)},
+		{60.0, time.Date(1900, 2, 28, 0, 0, 0, 0, time.UTC)},
+		{61.0, time.Date(1900, 3, 1, 0, 0, 0, 0, time.UTC)},
+		{41275.0, time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)},
+		{401769.0, time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)},
+	}
+
+	for _, test := range trueExpectedInputList {
+		if test.GoValue != timeFromExcelTime(test.ExcelValue, false) {
+			t.Fatalf("Expected %v from %v = true, got %v\n", test.GoValue, test.ExcelValue, timeFromExcelTime(test.ExcelValue, false))
+		}
+	}
+}

+ 29 - 17
excelize.go

@@ -1,3 +1,13 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+//
+// See https://xuri.me/excelize for more information about this package.
 package excelize
 package excelize
 
 
 import (
 import (
@@ -21,6 +31,7 @@ type File struct {
 	Sheet         map[string]*xlsxWorksheet
 	Sheet         map[string]*xlsxWorksheet
 	SheetCount    int
 	SheetCount    int
 	Styles        *xlsxStyleSheet
 	Styles        *xlsxStyleSheet
+	Theme         *xlsxTheme
 	WorkBook      *xlsxWorkbook
 	WorkBook      *xlsxWorkbook
 	WorkBookRels  *xlsxWorkbookRels
 	WorkBookRels  *xlsxWorkbookRels
 	XLSX          map[string][]byte
 	XLSX          map[string][]byte
@@ -66,10 +77,11 @@ func OpenReader(r io.Reader) (*File, error) {
 	}
 	}
 	f.sheetMap = f.getSheetMap()
 	f.sheetMap = f.getSheetMap()
 	f.Styles = f.stylesReader()
 	f.Styles = f.stylesReader()
+	f.Theme = f.themeReader()
 	return f, nil
 	return f, nil
 }
 }
 
 
-// setDefaultTimeStyle provides function to set default numbers format for
+// setDefaultTimeStyle provides a function to set default numbers format for
 // time.Time type cell value by given worksheet name, cell coordinates and
 // time.Time type cell value by given worksheet name, cell coordinates and
 // number format code.
 // number format code.
 func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
 func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
@@ -79,8 +91,8 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
 	}
 	}
 }
 }
 
 
-// workSheetReader provides function to get the pointer to the structure after
-// deserialization by given worksheet name.
+// workSheetReader provides a function to get the pointer to the structure
+// after deserialization by given worksheet name.
 func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
 func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
 	name, ok := f.sheetMap[trimSheetName(sheet)]
 	name, ok := f.sheetMap[trimSheetName(sheet)]
 	if !ok {
 	if !ok {
@@ -103,7 +115,7 @@ func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
 	return f.Sheet[name]
 	return f.Sheet[name]
 }
 }
 
 
-// checkSheet provides function to fill each row element and make that is
+// checkSheet provides a function to fill each row element and make that is
 // continuous in a worksheet of XML.
 // continuous in a worksheet of XML.
 func checkSheet(xlsx *xlsxWorksheet) {
 func checkSheet(xlsx *xlsxWorksheet) {
 	row := len(xlsx.SheetData.Row)
 	row := len(xlsx.SheetData.Row)
@@ -131,7 +143,7 @@ func checkSheet(xlsx *xlsxWorksheet) {
 	xlsx.SheetData = sheetData
 	xlsx.SheetData = sheetData
 }
 }
 
 
-// replaceWorkSheetsRelationshipsNameSpaceBytes provides function to replace
+// replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
 // xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
 // xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
 // Office Excel 2007.
 // Office Excel 2007.
 func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
 func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
@@ -180,7 +192,7 @@ func (f *File) UpdateLinkedValue() {
 	}
 	}
 }
 }
 
 
-// adjustHelper provides function to adjust rows and columns dimensions,
+// adjustHelper provides a function to adjust rows and columns dimensions,
 // hyperlinks, merged cells and auto filter when inserting or deleting rows or
 // hyperlinks, merged cells and auto filter when inserting or deleting rows or
 // columns.
 // columns.
 //
 //
@@ -202,7 +214,7 @@ func (f *File) adjustHelper(sheet string, column, row, offset int) {
 	checkRow(xlsx)
 	checkRow(xlsx)
 }
 }
 
 
-// adjustColDimensions provides function to update column dimensions when
+// adjustColDimensions provides a function to update column dimensions when
 // inserting or deleting rows or columns.
 // inserting or deleting rows or columns.
 func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
 func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
 	for i, r := range xlsx.SheetData.Row {
 	for i, r := range xlsx.SheetData.Row {
@@ -218,8 +230,8 @@ func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
 	}
 	}
 }
 }
 
 
-// adjustRowDimensions provides function to update row dimensions when inserting
-// or deleting rows or columns.
+// adjustRowDimensions provides a function to update row dimensions when
+// inserting or deleting rows or columns.
 func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
 func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
 	if rowIndex == -1 {
 	if rowIndex == -1 {
 		return
 		return
@@ -238,7 +250,7 @@ func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
 	}
 	}
 }
 }
 
 
-// adjustHyperlinks provides function to update hyperlinks when inserting or
+// adjustHyperlinks provides a function to update hyperlinks when inserting or
 // deleting rows or columns.
 // deleting rows or columns.
 func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
 func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -278,8 +290,8 @@ func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
 	}
 	}
 }
 }
 
 
-// adjustMergeCellsHelper provides function to update merged cells when inserting or
-// deleting rows or columns.
+// adjustMergeCellsHelper provides a function to update merged cells when
+// inserting or deleting rows or columns.
 func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 	if xlsx.MergeCells != nil {
 	if xlsx.MergeCells != nil {
 		for k, v := range xlsx.MergeCells.Cells {
 		for k, v := range xlsx.MergeCells.Cells {
@@ -319,8 +331,8 @@ func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, off
 	}
 	}
 }
 }
 
 
-// adjustMergeCells provides function to update merged cells when inserting or
-// deleting rows or columns.
+// adjustMergeCells provides a function to update merged cells when inserting
+// or deleting rows or columns.
 func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 	f.adjustMergeCellsHelper(xlsx, column, rowIndex, offset)
 	f.adjustMergeCellsHelper(xlsx, column, rowIndex, offset)
 
 
@@ -340,8 +352,8 @@ func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset in
 	}
 	}
 }
 }
 
 
-// adjustAutoFilter provides function to update the auto filter when inserting
-// or deleting rows or columns.
+// adjustAutoFilter provides a function to update the auto filter when
+// inserting or deleting rows or columns.
 func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 	f.adjustAutoFilterHelper(xlsx, column, rowIndex, offset)
 	f.adjustAutoFilterHelper(xlsx, column, rowIndex, offset)
 
 
@@ -374,7 +386,7 @@ func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset in
 	}
 	}
 }
 }
 
 
-// adjustAutoFilterHelper provides function to update the auto filter when
+// adjustAutoFilterHelper provides a function to update the auto filter when
 // inserting or deleting rows or columns.
 // inserting or deleting rows or columns.
 func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
 	if xlsx.AutoFilter != nil {
 	if xlsx.AutoFilter != nil {

+ 55 - 6
excelize_test.go

@@ -2,6 +2,7 @@ package excelize
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"image/color"
 	_ "image/gif"
 	_ "image/gif"
 	_ "image/jpeg"
 	_ "image/jpeg"
 	_ "image/png"
 	_ "image/png"
@@ -88,7 +89,6 @@ func TestOpenFile(t *testing.T) {
 	xlsx.SetCellValue("Sheet2", "F16", true)
 	xlsx.SetCellValue("Sheet2", "F16", true)
 	xlsx.SetCellValue("Sheet2", "F17", complex64(5+10i))
 	xlsx.SetCellValue("Sheet2", "F17", complex64(5+10i))
 	t.Log(letterOnlyMapF('x'))
 	t.Log(letterOnlyMapF('x'))
-	t.Log(deepCopy(nil, nil))
 	shiftJulianToNoon(1, -0.6)
 	shiftJulianToNoon(1, -0.6)
 	timeFromExcelTime(61, true)
 	timeFromExcelTime(61, true)
 	timeFromExcelTime(62, true)
 	timeFromExcelTime(62, true)
@@ -162,6 +162,24 @@ func TestAddPicture(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Log(err)
 		t.Log(err)
 	}
 	}
+	err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1))
+	if err != nil {
+		t.Log(err)
+	}
+	// Test add picture to worksheet with invalid file data.
+	err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1))
+	if err != nil {
+		t.Log(err)
+	}
+	file, err := ioutil.ReadFile("./test/images/excel.jpg")
+	if err != nil {
+		t.Error(err)
+	}
+	// Test add picture to worksheet from bytes.
+	err = xlsx.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".jpg", file)
+	if err != nil {
+		t.Log(err)
+	}
 	// Test write file to given path.
 	// Test write file to given path.
 	err = xlsx.SaveAs("./test/Book2.xlsx")
 	err = xlsx.SaveAs("./test/Book2.xlsx")
 	if err != nil {
 	if err != nil {
@@ -211,8 +229,13 @@ func TestNewFile(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
-	// Test add picture to worksheet with invalid formatset
+	// Test add picture to worksheet without formatset.
 	err = xlsx.AddPicture("Sheet1", "C2", "./test/images/excel.png", "")
 	err = xlsx.AddPicture("Sheet1", "C2", "./test/images/excel.png", "")
+	if err != nil {
+		t.Error(err)
+	}
+	// Test add picture to worksheet with invalid formatset.
+	err = xlsx.AddPicture("Sheet1", "C2", "./test/images/excel.png", `{`)
 	if err != nil {
 	if err != nil {
 		t.Log(err)
 		t.Log(err)
 	}
 	}
@@ -771,10 +794,6 @@ func TestAddTable(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
-	err = xlsx.AddTable("Sheet2", "A2", "B5", ``)
-	if err != nil {
-		t.Log(err)
-	}
 	err = xlsx.AddTable("Sheet2", "A2", "B5", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
 	err = xlsx.AddTable("Sheet2", "A2", "B5", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
@@ -817,6 +836,11 @@ func TestAddComments(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
+	allComments := xlsx.GetComments()
+	if len(allComments) != 2 {
+		t.Error("Expected 2 comment entry elements.")
+	}
+
 }
 }
 
 
 func TestAutoFilter(t *testing.T) {
 func TestAutoFilter(t *testing.T) {
@@ -1146,6 +1170,31 @@ func TestOutlineLevel(t *testing.T) {
 	xlsx.SetColOutlineLevel("Sheet2", "B", 2)
 	xlsx.SetColOutlineLevel("Sheet2", "B", 2)
 }
 }
 
 
+func TestThemeColor(t *testing.T) {
+	t.Log(ThemeColor("000000", -0.1))
+	t.Log(ThemeColor("000000", 0))
+	t.Log(ThemeColor("000000", 1))
+}
+
+func TestHSL(t *testing.T) {
+	var hsl HSL
+	t.Log(hsl.RGBA())
+	t.Log(hslModel(hsl))
+	t.Log(hslModel(color.Gray16{Y: uint16(1)}))
+	t.Log(HSLToRGB(0, 1, 0.4))
+	t.Log(HSLToRGB(0, 1, 0.6))
+	t.Log(hueToRGB(0, 0, -1))
+	t.Log(hueToRGB(0, 0, 2))
+	t.Log(hueToRGB(0, 0, 1.0/7))
+	t.Log(hueToRGB(0, 0, 0.4))
+	t.Log(hueToRGB(0, 0, 2.0/4))
+	t.Log(RGBToHSL(255, 255, 0))
+	t.Log(RGBToHSL(0, 255, 255))
+	t.Log(RGBToHSL(250, 100, 50))
+	t.Log(RGBToHSL(50, 100, 250))
+	t.Log(RGBToHSL(250, 50, 100))
+}
+
 func trimSliceSpace(s []string) []string {
 func trimSliceSpace(s []string) []string {
 	for {
 	for {
 		if len(s) > 0 && s[len(s)-1] == "" {
 		if len(s) > 0 && s[len(s)-1] == "" {

+ 26 - 14
file.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -8,7 +17,7 @@ import (
 	"os"
 	"os"
 )
 )
 
 
-// NewFile provides function to create new file by default template. For
+// NewFile provides a function to create new file by default template. For
 // example:
 // example:
 //
 //
 //    xlsx := NewFile()
 //    xlsx := NewFile()
@@ -36,19 +45,20 @@ func NewFile() *File {
 	f.WorkBookRels = f.workbookRelsReader()
 	f.WorkBookRels = f.workbookRelsReader()
 	f.Sheet["xl/worksheets/sheet1.xml"] = f.workSheetReader("Sheet1")
 	f.Sheet["xl/worksheets/sheet1.xml"] = f.workSheetReader("Sheet1")
 	f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
 	f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
+	f.Theme = f.themeReader()
 	return f
 	return f
 }
 }
 
 
-// Save provides function to override the xlsx file with origin path.
+// Save provides a function to override the xlsx file with origin path.
 func (f *File) Save() error {
 func (f *File) Save() error {
 	if f.Path == "" {
 	if f.Path == "" {
-		return fmt.Errorf("No path defined for file, consider File.WriteTo or File.Write")
+		return fmt.Errorf("no path defined for file, consider File.WriteTo or File.Write")
 	}
 	}
 	return f.SaveAs(f.Path)
 	return f.SaveAs(f.Path)
 }
 }
 
 
-// SaveAs provides function to create or update to an xlsx file at the provided
-// path.
+// SaveAs provides a function to create or update to an xlsx file at the
+// provided path.
 func (f *File) SaveAs(name string) error {
 func (f *File) SaveAs(name string) error {
 	file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
 	file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
 	if err != nil {
 	if err != nil {
@@ -58,8 +68,14 @@ func (f *File) SaveAs(name string) error {
 	return f.Write(file)
 	return f.Write(file)
 }
 }
 
 
-// Write provides function to write to an io.Writer.
+// Write provides a function to write to an io.Writer.
 func (f *File) Write(w io.Writer) error {
 func (f *File) Write(w io.Writer) error {
+	_, err := f.WriteTo(w)
+	return err
+}
+
+// WriteTo implements io.WriterTo to write the file.
+func (f *File) WriteTo(w io.Writer) (int64, error) {
 	buf := new(bytes.Buffer)
 	buf := new(bytes.Buffer)
 	zw := zip.NewWriter(buf)
 	zw := zip.NewWriter(buf)
 	f.contentTypesWriter()
 	f.contentTypesWriter()
@@ -70,21 +86,17 @@ func (f *File) Write(w io.Writer) error {
 	for path, content := range f.XLSX {
 	for path, content := range f.XLSX {
 		fi, err := zw.Create(path)
 		fi, err := zw.Create(path)
 		if err != nil {
 		if err != nil {
-			return err
+			return 0, err
 		}
 		}
 		_, err = fi.Write(content)
 		_, err = fi.Write(content)
 		if err != nil {
 		if err != nil {
-			return err
+			return 0, err
 		}
 		}
 	}
 	}
 	err := zw.Close()
 	err := zw.Close()
 	if err != nil {
 	if err != nil {
-		return err
-	}
-
-	if _, err := buf.WriteTo(w); err != nil {
-		return err
+		return 0, err
 	}
 	}
 
 
-	return nil
+	return buf.WriteTo(w)
 }
 }

+ 139 - 0
hsl.go

@@ -0,0 +1,139 @@
+// Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 	 * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 	 * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// 	 * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package excelize
+
+import (
+	"image/color"
+	"math"
+)
+
+// HSLModel converts any color.Color to a HSL color.
+var HSLModel = color.ModelFunc(hslModel)
+
+// HSL represents a cylindrical coordinate of points in an RGB color model.
+//
+// Values are in the range 0 to 1.
+type HSL struct {
+	H, S, L float64
+}
+
+// RGBA returns the alpha-premultiplied red, green, blue and alpha values
+// for the HSL.
+func (c HSL) RGBA() (uint32, uint32, uint32, uint32) {
+	r, g, b := HSLToRGB(c.H, c.S, c.L)
+	return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+}
+
+// hslModel converts a color.Color to HSL.
+func hslModel(c color.Color) color.Color {
+	if _, ok := c.(HSL); ok {
+		return c
+	}
+	r, g, b, _ := c.RGBA()
+	h, s, l := RGBToHSL(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+	return HSL{h, s, l}
+}
+
+// RGBToHSL converts an RGB triple to a HSL triple.
+func RGBToHSL(r, g, b uint8) (h, s, l float64) {
+	fR := float64(r) / 255
+	fG := float64(g) / 255
+	fB := float64(b) / 255
+	max := math.Max(math.Max(fR, fG), fB)
+	min := math.Min(math.Min(fR, fG), fB)
+	l = (max + min) / 2
+	if max == min {
+		// Achromatic.
+		h, s = 0, 0
+	} else {
+		// Chromatic.
+		d := max - min
+		if l > 0.5 {
+			s = d / (2.0 - max - min)
+		} else {
+			s = d / (max + min)
+		}
+		switch max {
+		case fR:
+			h = (fG - fB) / d
+			if fG < fB {
+				h += 6
+			}
+		case fG:
+			h = (fB-fR)/d + 2
+		case fB:
+			h = (fR-fG)/d + 4
+		}
+		h /= 6
+	}
+	return
+}
+
+// HSLToRGB converts an HSL triple to a RGB triple.
+func HSLToRGB(h, s, l float64) (r, g, b uint8) {
+	var fR, fG, fB float64
+	if s == 0 {
+		fR, fG, fB = l, l, l
+	} else {
+		var q float64
+		if l < 0.5 {
+			q = l * (1 + s)
+		} else {
+			q = l + s - s*l
+		}
+		p := 2*l - q
+		fR = hueToRGB(p, q, h+1.0/3)
+		fG = hueToRGB(p, q, h)
+		fB = hueToRGB(p, q, h-1.0/3)
+	}
+	r = uint8((fR * 255) + 0.5)
+	g = uint8((fG * 255) + 0.5)
+	b = uint8((fB * 255) + 0.5)
+	return
+}
+
+// hueToRGB is a helper function for HSLToRGB.
+func hueToRGB(p, q, t float64) float64 {
+	if t < 0 {
+		t++
+	}
+	if t > 1 {
+		t--
+	}
+	if t < 1.0/6 {
+		return p + (q-p)*6*t
+	}
+	if t < 0.5 {
+		return q
+	}
+	if t < 2.0/3 {
+		return p + (q-p)*(2.0/3-t)*6
+	}
+	return p
+}

+ 29 - 22
lib.go

@@ -1,9 +1,17 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
 	"archive/zip"
 	"archive/zip"
 	"bytes"
 	"bytes"
-	"encoding/gob"
 	"io"
 	"io"
 	"log"
 	"log"
 	"math"
 	"math"
@@ -26,7 +34,7 @@ func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
 	return fileList, worksheets, nil
 	return fileList, worksheets, nil
 }
 }
 
 
-// readXML provides function to read XML content as string.
+// readXML provides a function to read XML content as string.
 func (f *File) readXML(name string) []byte {
 func (f *File) readXML(name string) []byte {
 	if content, ok := f.XLSX[name]; ok {
 	if content, ok := f.XLSX[name]; ok {
 		return content
 		return content
@@ -34,8 +42,8 @@ func (f *File) readXML(name string) []byte {
 	return []byte{}
 	return []byte{}
 }
 }
 
 
-// saveFileList provides function to update given file content in file list of
-// XLSX.
+// saveFileList provides a function to update given file content in file list
+// of XLSX.
 func (f *File) saveFileList(name string, content []byte) {
 func (f *File) saveFileList(name string, content []byte) {
 	newContent := make([]byte, 0, len(XMLHeader)+len(content))
 	newContent := make([]byte, 0, len(XMLHeader)+len(content))
 	newContent = append(newContent, []byte(XMLHeader)...)
 	newContent = append(newContent, []byte(XMLHeader)...)
@@ -55,7 +63,7 @@ func readFile(file *zip.File) []byte {
 	return buff.Bytes()
 	return buff.Bytes()
 }
 }
 
 
-// ToAlphaString provides function to convert integer to Excel sheet column
+// ToAlphaString provides a function to convert integer to Excel sheet column
 // title. For example convert 36 to column title AK:
 // title. For example convert 36 to column title AK:
 //
 //
 //     excelize.ToAlphaString(36)
 //     excelize.ToAlphaString(36)
@@ -73,9 +81,9 @@ func ToAlphaString(value int) string {
 	return ans
 	return ans
 }
 }
 
 
-// TitleToNumber provides function to convert Excel sheet column title to int
-// (this function doesn't do value check currently). For example convert AK
-// and ak to column title 36:
+// TitleToNumber provides a function to convert Excel sheet column title to
+// int (this function doesn't do value check currently). For example convert
+// AK and ak to column title 36:
 //
 //
 //    excelize.TitleToNumber("AK")
 //    excelize.TitleToNumber("AK")
 //    excelize.TitleToNumber("ak")
 //    excelize.TitleToNumber("ak")
@@ -115,17 +123,6 @@ func intOnlyMapF(rune rune) rune {
 	return -1
 	return -1
 }
 }
 
 
-// deepCopy provides method to creates a deep copy of whatever is passed to it
-// and returns the copy in an interface. The returned value will need to be
-// asserted to the correct type.
-func deepCopy(dst, src interface{}) error {
-	var buf bytes.Buffer
-	if err := gob.NewEncoder(&buf).Encode(src); err != nil {
-		return err
-	}
-	return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
-}
-
 // boolPtr returns a pointer to a bool with the given value.
 // boolPtr returns a pointer to a bool with the given value.
 func boolPtr(b bool) *bool { return &b }
 func boolPtr(b bool) *bool { return &b }
 
 
@@ -137,8 +134,8 @@ func defaultTrue(b *bool) bool {
 	return *b
 	return *b
 }
 }
 
 
-// axisLowerOrEqualThan returns true if axis1 <= axis2
-// axis1/axis2 can be either a column or a row axis, e.g. "A", "AAE", "42", "1", etc.
+// axisLowerOrEqualThan returns true if axis1 <= axis2 axis1/axis2 can be
+// either a column or a row axis, e.g. "A", "AAE", "42", "1", etc.
 //
 //
 // For instance, the following comparisons are all true:
 // For instance, the following comparisons are all true:
 //
 //
@@ -159,7 +156,8 @@ func axisLowerOrEqualThan(axis1, axis2 string) bool {
 	}
 	}
 }
 }
 
 
-// getCellColRow returns the two parts of a cell identifier (its col and row) as strings
+// getCellColRow returns the two parts of a cell identifier (its col and row)
+// as strings
 //
 //
 // For instance:
 // For instance:
 //
 //
@@ -176,3 +174,12 @@ func getCellColRow(cell string) (col, row string) {
 
 
 	return cell, ""
 	return cell, ""
 }
 }
+
+// parseFormatSet provides a method to convert format string to []byte and
+// handle empty string.
+func parseFormatSet(formatSet string) []byte {
+	if formatSet != "" {
+		return []byte(formatSet)
+	}
+	return []byte("{}")
+}

+ 92 - 40
picture.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -14,8 +23,8 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-// parseFormatPictureSet provides function to parse the format settings of the
-// picture with default value.
+// parseFormatPictureSet provides a function to parse the format settings of
+// the picture with default value.
 func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
 func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
 	format := formatPicture{
 	format := formatPicture{
 		FPrintsWithSheet: true,
 		FPrintsWithSheet: true,
@@ -26,7 +35,7 @@ func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
 		XScale:           1.0,
 		XScale:           1.0,
 		YScale:           1.0,
 		YScale:           1.0,
 	}
 	}
-	err := json.Unmarshal([]byte(formatSet), &format)
+	err := json.Unmarshal(parseFormatSet(formatSet), &format)
 	return &format, err
 	return &format, err
 }
 }
 
 
@@ -78,23 +87,66 @@ func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
 // positioning is move and size with cells.
 // positioning is move and size with cells.
 func (f *File) AddPicture(sheet, cell, picture, format string) error {
 func (f *File) AddPicture(sheet, cell, picture, format string) error {
 	var err error
 	var err error
-	var drawingHyperlinkRID int
-	var hyperlinkType string
 	// Check picture exists first.
 	// Check picture exists first.
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 		return err
 		return err
 	}
 	}
 	ext, ok := supportImageTypes[path.Ext(picture)]
 	ext, ok := supportImageTypes[path.Ext(picture)]
 	if !ok {
 	if !ok {
-		return errors.New("Unsupported image extension")
+		return errors.New("unsupported image extension")
+	}
+	file, _ := ioutil.ReadFile(picture)
+	_, name := filepath.Split(picture)
+	return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
+}
+
+// AddPictureFromBytes provides the method to add picture in a sheet by given
+// picture format set (such as offset, scale, aspect ratio setting and print
+// settings), file base name, extension name and file bytes. For example:
+//
+//    package main
+//
+//    import (
+//        "fmt"
+//        _ "image/jpeg"
+//        "io/ioutil"
+//
+//        "github.com/360EntSecGroup-Skylar/excelize"
+//    )
+//
+//    func main() {
+//        xlsx := excelize.NewFile()
+//
+//        file, err := ioutil.ReadFile("./image1.jpg")
+//        if err != nil {
+//            fmt.Println(err)
+//        }
+//        err = xlsx.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file)
+//        if err != nil {
+//            fmt.Println(err)
+//        }
+//        err = xlsx.SaveAs("./Book1.xlsx")
+//        if err != nil {
+//            fmt.Println(err)
+//        }
+//    }
+//
+func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
+	var err error
+	var drawingHyperlinkRID int
+	var hyperlinkType string
+	ext, ok := supportImageTypes[extension]
+	if !ok {
+		return errors.New("unsupported image extension")
 	}
 	}
-	readFile, _ := os.Open(picture)
-	image, _, _ := image.DecodeConfig(readFile)
-	_, file := filepath.Split(picture)
 	formatSet, err := parseFormatPictureSet(format)
 	formatSet, err := parseFormatPictureSet(format)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	image, _, err := image.DecodeConfig(bytes.NewReader(file))
+	if err != nil {
+		return err
+	}
 	// Read sheet data.
 	// Read sheet data.
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
 	// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
 	// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
@@ -110,13 +162,13 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
 		}
 		}
 		drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
 		drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
 	}
 	}
-	f.addDrawingPicture(sheet, drawingXML, cell, file, image.Width, image.Height, drawingRID, drawingHyperlinkRID, formatSet)
-	f.addMedia(picture, ext)
+	f.addDrawingPicture(sheet, drawingXML, cell, name, image.Width, image.Height, drawingRID, drawingHyperlinkRID, formatSet)
+	f.addMedia(file, ext)
 	f.addContentTypePart(drawingID, "drawings")
 	f.addContentTypePart(drawingID, "drawings")
 	return err
 	return err
 }
 }
 
 
-// addSheetRelationships provides function to add
+// addSheetRelationships provides a function to add
 // xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name, relationship
 // xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name, relationship
 // type and target.
 // type and target.
 func (f *File) addSheetRelationships(sheet, relType, target, targetMode string) int {
 func (f *File) addSheetRelationships(sheet, relType, target, targetMode string) int {
@@ -149,9 +201,9 @@ func (f *File) addSheetRelationships(sheet, relType, target, targetMode string)
 	return rID
 	return rID
 }
 }
 
 
-// deleteSheetRelationships provides function to delete relationships in
-// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and relationship
-// index.
+// deleteSheetRelationships provides a function to delete relationships in
+// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
+// relationship index.
 func (f *File) deleteSheetRelationships(sheet, rID string) {
 func (f *File) deleteSheetRelationships(sheet, rID string) {
 	name, ok := f.sheetMap[trimSheetName(sheet)]
 	name, ok := f.sheetMap[trimSheetName(sheet)]
 	if !ok {
 	if !ok {
@@ -169,7 +221,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
 	f.saveFileList(rels, output)
 	f.saveFileList(rels, output)
 }
 }
 
 
-// addSheetLegacyDrawing provides function to add legacy drawing element to
+// addSheetLegacyDrawing provides a function to add legacy drawing element to
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
 func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -178,7 +230,7 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
 	}
 	}
 }
 }
 
 
-// addSheetDrawing provides function to add drawing element to
+// addSheetDrawing provides a function to add drawing element to
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 func (f *File) addSheetDrawing(sheet string, rID int) {
 func (f *File) addSheetDrawing(sheet string, rID int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -187,7 +239,7 @@ func (f *File) addSheetDrawing(sheet string, rID int) {
 	}
 	}
 }
 }
 
 
-// addSheetPicture provides function to add picture element to
+// addSheetPicture provides a function to add picture element to
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 func (f *File) addSheetPicture(sheet string, rID int) {
 func (f *File) addSheetPicture(sheet string, rID int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -196,7 +248,7 @@ func (f *File) addSheetPicture(sheet string, rID int) {
 	}
 	}
 }
 }
 
 
-// countDrawings provides function to get drawing files count storage in the
+// countDrawings provides a function to get drawing files count storage in the
 // folder xl/drawings.
 // folder xl/drawings.
 func (f *File) countDrawings() int {
 func (f *File) countDrawings() int {
 	count := 0
 	count := 0
@@ -208,7 +260,7 @@ func (f *File) countDrawings() int {
 	return count
 	return count
 }
 }
 
 
-// addDrawingPicture provides function to add picture by given sheet,
+// addDrawingPicture provides a function to add picture by given sheet,
 // drawingXML, cell, file name, width, height relationship index and format
 // drawingXML, cell, file name, width, height relationship index and format
 // sets.
 // sets.
 func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) {
 func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) {
@@ -263,8 +315,8 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
 	f.saveFileList(drawingXML, output)
 	f.saveFileList(drawingXML, output)
 }
 }
 
 
-// addDrawingRelationships provides function to add image part relationships in
-// the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index,
+// addDrawingRelationships provides a function to add image part relationships
+// in the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index,
 // relationship type and target.
 // relationship type and target.
 func (f *File) addDrawingRelationships(index int, relType, target, targetMode string) int {
 func (f *File) addDrawingRelationships(index int, relType, target, targetMode string) int {
 	var rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels"
 	var rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels"
@@ -292,8 +344,8 @@ func (f *File) addDrawingRelationships(index int, relType, target, targetMode st
 	return rID
 	return rID
 }
 }
 
 
-// countMedia provides function to get media files count storage in the folder
-// xl/media/image.
+// countMedia provides a function to get media files count storage in the
+// folder xl/media/image.
 func (f *File) countMedia() int {
 func (f *File) countMedia() int {
 	count := 0
 	count := 0
 	for k := range f.XLSX {
 	for k := range f.XLSX {
@@ -304,17 +356,16 @@ func (f *File) countMedia() int {
 	return count
 	return count
 }
 }
 
 
-// addMedia provides function to add picture into folder xl/media/image by given
-// file name and extension name.
-func (f *File) addMedia(file, ext string) {
+// addMedia provides a function to add picture into folder xl/media/image by
+// given file and extension name.
+func (f *File) addMedia(file []byte, ext string) {
 	count := f.countMedia()
 	count := f.countMedia()
-	dat, _ := ioutil.ReadFile(file)
 	media := "xl/media/image" + strconv.Itoa(count+1) + ext
 	media := "xl/media/image" + strconv.Itoa(count+1) + ext
-	f.XLSX[media] = dat
+	f.XLSX[media] = file
 }
 }
 
 
-// setContentTypePartImageExtensions provides function to set the content type
-// for relationship parts and the Main Document part.
+// setContentTypePartImageExtensions provides a function to set the content
+// type for relationship parts and the Main Document part.
 func (f *File) setContentTypePartImageExtensions() {
 func (f *File) setContentTypePartImageExtensions() {
 	var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false}
 	var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false}
 	content := f.contentTypesReader()
 	content := f.contentTypesReader()
@@ -334,7 +385,7 @@ func (f *File) setContentTypePartImageExtensions() {
 	}
 	}
 }
 }
 
 
-// setContentTypePartVMLExtensions provides function to set the content type
+// setContentTypePartVMLExtensions provides a function to set the content type
 // for relationship parts and the Main Document part.
 // for relationship parts and the Main Document part.
 func (f *File) setContentTypePartVMLExtensions() {
 func (f *File) setContentTypePartVMLExtensions() {
 	vml := false
 	vml := false
@@ -352,8 +403,8 @@ func (f *File) setContentTypePartVMLExtensions() {
 	}
 	}
 }
 }
 
 
-// addContentTypePart provides function to add content type part relationships
-// in the file [Content_Types].xml by given index.
+// addContentTypePart provides a function to add content type part
+// relationships in the file [Content_Types].xml by given index.
 func (f *File) addContentTypePart(index int, contentType string) {
 func (f *File) addContentTypePart(index int, contentType string) {
 	setContentType := map[string]func(){
 	setContentType := map[string]func(){
 		"comments": f.setContentTypePartVMLExtensions,
 		"comments": f.setContentTypePartVMLExtensions,
@@ -387,7 +438,7 @@ func (f *File) addContentTypePart(index int, contentType string) {
 	})
 	})
 }
 }
 
 
-// getSheetRelationshipsTargetByID provides function to get Target attribute
+// getSheetRelationshipsTargetByID provides a function to get Target attribute
 // value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
 // value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
 // relationship index.
 // relationship index.
 func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
 func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
@@ -406,9 +457,9 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
 	return ""
 	return ""
 }
 }
 
 
-// GetPicture provides function to get picture base name and raw content embed
-// in XLSX by given worksheet and cell name. This function returns the file name
-// in XLSX and file contents as []byte data types. For example:
+// GetPicture provides a function to get picture base name and raw content
+// embed in XLSX by given worksheet and cell name. This function returns the
+// file name in XLSX and file contents as []byte data types. For example:
 //
 //
 //    xlsx, err := excelize.OpenFile("./Book1.xlsx")
 //    xlsx, err := excelize.OpenFile("./Book1.xlsx")
 //    if err != nil {
 //    if err != nil {
@@ -463,8 +514,9 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte) {
 	return "", []byte{}
 	return "", []byte{}
 }
 }
 
 
-// getDrawingRelationships provides function to get drawing relationships from
-// xl/drawings/_rels/drawing%s.xml.rels by given file name and relationship ID.
+// getDrawingRelationships provides a function to get drawing relationships
+// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
+// relationship ID.
 func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation {
 func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation {
 	_, ok := f.XLSX[rels]
 	_, ok := f.XLSX[rels]
 	if !ok {
 	if !ok {

+ 29 - 19
rows.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -132,7 +141,7 @@ func (err ErrSheetNotExist) Error() string {
 
 
 // Rows return a rows iterator. For example:
 // Rows return a rows iterator. For example:
 //
 //
-//    rows, err := xlsx.GetRows("Sheet1")
+//    rows, err := xlsx.Rows("Sheet1")
 //    for rows.Next() {
 //    for rows.Next() {
 //        for _, colCell := range rows.Columns() {
 //        for _, colCell := range rows.Columns() {
 //            fmt.Print(colCell, "\t")
 //            fmt.Print(colCell, "\t")
@@ -202,7 +211,7 @@ func (f *File) SetRowHeight(sheet string, row int, height float64) {
 	xlsx.SheetData.Row[rowIdx].CustomHeight = true
 	xlsx.SheetData.Row[rowIdx].CustomHeight = true
 }
 }
 
 
-// getRowHeight provides function to get row height in pixels by given sheet
+// getRowHeight provides a function to get row height in pixels by given sheet
 // name and row index.
 // name and row index.
 func (f *File) getRowHeight(sheet string, row int) int {
 func (f *File) getRowHeight(sheet string, row int) int {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -215,7 +224,7 @@ func (f *File) getRowHeight(sheet string, row int) int {
 	return int(defaultRowHeightPixels)
 	return int(defaultRowHeightPixels)
 }
 }
 
 
-// GetRowHeight provides function to get row height by given worksheet name
+// GetRowHeight provides a function to get row height by given worksheet name
 // and row index. For example, get the height of the first row in Sheet1:
 // and row index. For example, get the height of the first row in Sheet1:
 //
 //
 //    xlsx.GetRowHeight("Sheet1", 1)
 //    xlsx.GetRowHeight("Sheet1", 1)
@@ -231,7 +240,7 @@ func (f *File) GetRowHeight(sheet string, row int) float64 {
 	return defaultRowHeightPixels
 	return defaultRowHeightPixels
 }
 }
 
 
-// sharedStringsReader provides function to get the pointer to the structure
+// sharedStringsReader provides a function to get the pointer to the structure
 // after deserialization of xl/sharedStrings.xml.
 // after deserialization of xl/sharedStrings.xml.
 func (f *File) sharedStringsReader() *xlsxSST {
 func (f *File) sharedStringsReader() *xlsxSST {
 	if f.SharedStrings == nil {
 	if f.SharedStrings == nil {
@@ -246,8 +255,9 @@ func (f *File) sharedStringsReader() *xlsxSST {
 	return f.SharedStrings
 	return f.SharedStrings
 }
 }
 
 
-// getValueFrom return a value from a column/row cell, this function is inteded
-// to be used with for range on rows an argument with the xlsx opened file.
+// getValueFrom return a value from a column/row cell, this function is
+// inteded to be used with for range on rows an argument with the xlsx opened
+// file.
 func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
 func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
 	switch xlsx.T {
 	switch xlsx.T {
 	case "s":
 	case "s":
@@ -315,9 +325,9 @@ func (f *File) SetRowOutlineLevel(sheet string, rowIndex int, level uint8) {
 	xlsx.SheetData.Row[rowIndex].OutlineLevel = level
 	xlsx.SheetData.Row[rowIndex].OutlineLevel = level
 }
 }
 
 
-// GetRowOutlineLevel provides a function to get outline level number of a single row by given
-// worksheet name and row index. For example, get outline number of row 2 in
-// Sheet1:
+// GetRowOutlineLevel provides a function to get outline level number of a
+// single row by given worksheet name and row index. For example, get outline
+// number of row 2 in Sheet1:
 //
 //
 //    xlsx.GetRowOutlineLevel("Sheet1", 2)
 //    xlsx.GetRowOutlineLevel("Sheet1", 2)
 //
 //
@@ -329,8 +339,8 @@ func (f *File) GetRowOutlineLevel(sheet string, rowIndex int) uint8 {
 	return xlsx.SheetData.Row[rowIndex].OutlineLevel
 	return xlsx.SheetData.Row[rowIndex].OutlineLevel
 }
 }
 
 
-// RemoveRow provides function to remove single row by given worksheet name and
-// row index. For example, remove row 3 in Sheet1:
+// RemoveRow provides a function to remove single row by given worksheet name
+// and row index. For example, remove row 3 in Sheet1:
 //
 //
 //    xlsx.RemoveRow("Sheet1", 2)
 //    xlsx.RemoveRow("Sheet1", 2)
 //
 //
@@ -349,8 +359,8 @@ func (f *File) RemoveRow(sheet string, row int) {
 	}
 	}
 }
 }
 
 
-// InsertRow provides function to insert a new row before given row index. For
-// example, create a new row before row 3 in Sheet1:
+// InsertRow provides a function to insert a new row before given row index.
+// For example, create a new row before row 3 in Sheet1:
 //
 //
 //    xlsx.InsertRow("Sheet1", 2)
 //    xlsx.InsertRow("Sheet1", 2)
 //
 //
@@ -362,8 +372,8 @@ func (f *File) InsertRow(sheet string, row int) {
 	f.adjustHelper(sheet, -1, row, 1)
 	f.adjustHelper(sheet, -1, row, 1)
 }
 }
 
 
-// checkRow provides function to check and fill each column element for all rows
-// and make that is continuous in a worksheet of XML. For example:
+// checkRow provides a function to check and fill each column element for all
+// rows and make that is continuous in a worksheet of XML. For example:
 //
 //
 //    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
 //    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
 //        <c r="A15" s="2" />
 //        <c r="A15" s="2" />
@@ -416,7 +426,7 @@ func checkRow(xlsx *xlsxWorksheet) {
 	}
 	}
 }
 }
 
 
-// completeRow provides function to check and fill each column element for a
+// completeRow provides a function to check and fill each column element for a
 // single row and make that is continuous in a worksheet of XML by given row
 // single row and make that is continuous in a worksheet of XML by given row
 // index and axis.
 // index and axis.
 func completeRow(xlsx *xlsxWorksheet, row, cell int) {
 func completeRow(xlsx *xlsxWorksheet, row, cell int) {
@@ -448,9 +458,9 @@ func completeRow(xlsx *xlsxWorksheet, row, cell int) {
 	}
 	}
 }
 }
 
 
-// convertRowHeightToPixels provides function to convert the height of a cell
-// from user's units to pixels. If the height hasn't been set by the user we use
-// the default value. If the row is hidden it has a value of zero.
+// convertRowHeightToPixels provides a function to convert the height of a
+// cell from user's units to pixels. If the height hasn't been set by the user
+// we use the default value. If the row is hidden it has a value of zero.
 func convertRowHeightToPixels(height float64) float64 {
 func convertRowHeightToPixels(height float64) float64 {
 	var pixels float64
 	var pixels float64
 	if height == 0 {
 	if height == 0 {

+ 14 - 5
shape.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -7,7 +16,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-// parseFormatShapeSet provides function to parse the format settings of the
+// parseFormatShapeSet provides a function to parse the format settings of the
 // shape with default value.
 // shape with default value.
 func parseFormatShapeSet(formatSet string) (*formatShape, error) {
 func parseFormatShapeSet(formatSet string) (*formatShape, error) {
 	format := formatShape{
 	format := formatShape{
@@ -29,8 +38,8 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
 
 
 // AddShape provides the method to add shape in a sheet by given worksheet
 // AddShape provides the method to add shape in a sheet by given worksheet
 // index, shape format set (such as offset, scale, aspect ratio setting and
 // index, shape format set (such as offset, scale, aspect ratio setting and
-// print settings) and properties set. For example, add text box (rect shape) in
-// Sheet1:
+// print settings) and properties set. For example, add text box (rect shape)
+// in Sheet1:
 //
 //
 //    xlsx.AddShape("Sheet1", "G6", `{"type":"rect","color":{"line":"#4286F4","fill":"#8eb9ff"},"paragraph":[{"text":"Rectangle Shape","font":{"bold":true,"italic":true,"family":"Berlin Sans FB Demi","size":36,"color":"#777777","underline":"sng"}}],"width":180,"height": 90}`)
 //    xlsx.AddShape("Sheet1", "G6", `{"type":"rect","color":{"line":"#4286F4","fill":"#8eb9ff"},"paragraph":[{"text":"Rectangle Shape","font":{"bold":true,"italic":true,"family":"Berlin Sans FB Demi","size":36,"color":"#777777","underline":"sng"}}],"width":180,"height": 90}`)
 //
 //
@@ -272,7 +281,7 @@ func (f *File) AddShape(sheet, cell, format string) error {
 	return err
 	return err
 }
 }
 
 
-// addDrawingShape provides function to add preset geometry by given sheet,
+// addDrawingShape provides a function to add preset geometry by given sheet,
 // drawingXMLand format sets.
 // drawingXMLand format sets.
 func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) {
 func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) {
 	textUnderlineType := map[string]bool{"none": true, "words": true, "sng": true, "dbl": true, "heavy": true, "dotted": true, "dottedHeavy": true, "dash": true, "dashHeavy": true, "dashLong": true, "dashLongHeavy": true, "dotDash": true, "dotDashHeavy": true, "dotDotDash": true, "dotDotDashHeavy": true, "wavy": true, "wavyHeavy": true, "wavyDbl": true}
 	textUnderlineType := map[string]bool{"none": true, "words": true, "sng": true, "dbl": true, "heavy": true, "dotted": true, "dottedHeavy": true, "dash": true, "dashHeavy": true, "dashLong": true, "dashLongHeavy": true, "dotDash": true, "dotDashHeavy": true, "dotDotDash": true, "dotDotDashHeavy": true, "wavy": true, "wavyHeavy": true, "wavyDbl": true}
@@ -397,7 +406,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
 	f.saveFileList(drawingXML, output)
 	f.saveFileList(drawingXML, output)
 }
 }
 
 
-// setShapeRef provides function to set color with hex model by given actual
+// setShapeRef provides a function to set color with hex model by given actual
 // color value.
 // color value.
 func setShapeRef(color string, i int) *aRef {
 func setShapeRef(color string, i int) *aRef {
 	if color == "" {
 	if color == "" {

+ 59 - 43
sheet.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -5,17 +14,19 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"encoding/xml"
 	"encoding/xml"
 	"errors"
 	"errors"
+	"io/ioutil"
 	"os"
 	"os"
 	"path"
 	"path"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"unicode/utf8"
 	"unicode/utf8"
+
+	"github.com/mohae/deepcopy"
 )
 )
 
 
 // NewSheet provides function to create a new sheet by given worksheet name.
 // NewSheet provides function to create a new sheet by given worksheet name.
-// When creating a new XLSX file, the default sheet will be created.
-// Returns the number of sheets in the workbook (file) after appending the new
-// sheet.
+// When creating a new XLSX file, the default sheet will be created. Returns
+// the number of sheets in the workbook (file) after appending the new sheet.
 func (f *File) NewSheet(name string) int {
 func (f *File) NewSheet(name string) int {
 	// Check if the worksheet already exists
 	// Check if the worksheet already exists
 	if f.GetSheetIndex(name) != 0 {
 	if f.GetSheetIndex(name) != 0 {
@@ -35,7 +46,7 @@ func (f *File) NewSheet(name string) int {
 	return f.SheetCount
 	return f.SheetCount
 }
 }
 
 
-// contentTypesReader provides function to get the pointer to the
+// contentTypesReader provides a function to get the pointer to the
 // [Content_Types].xml structure after deserialization.
 // [Content_Types].xml structure after deserialization.
 func (f *File) contentTypesReader() *xlsxTypes {
 func (f *File) contentTypesReader() *xlsxTypes {
 	if f.ContentTypes == nil {
 	if f.ContentTypes == nil {
@@ -46,7 +57,7 @@ func (f *File) contentTypesReader() *xlsxTypes {
 	return f.ContentTypes
 	return f.ContentTypes
 }
 }
 
 
-// contentTypesWriter provides function to save [Content_Types].xml after
+// contentTypesWriter provides a function to save [Content_Types].xml after
 // serialize structure.
 // serialize structure.
 func (f *File) contentTypesWriter() {
 func (f *File) contentTypesWriter() {
 	if f.ContentTypes != nil {
 	if f.ContentTypes != nil {
@@ -55,7 +66,7 @@ func (f *File) contentTypesWriter() {
 	}
 	}
 }
 }
 
 
-// workbookReader provides function to get the pointer to the xl/workbook.xml
+// workbookReader provides a function to get the pointer to the xl/workbook.xml
 // structure after deserialization.
 // structure after deserialization.
 func (f *File) workbookReader() *xlsxWorkbook {
 func (f *File) workbookReader() *xlsxWorkbook {
 	if f.WorkBook == nil {
 	if f.WorkBook == nil {
@@ -66,7 +77,7 @@ func (f *File) workbookReader() *xlsxWorkbook {
 	return f.WorkBook
 	return f.WorkBook
 }
 }
 
 
-// workbookWriter provides function to save xl/workbook.xml after serialize
+// workbookWriter provides a function to save xl/workbook.xml after serialize
 // structure.
 // structure.
 func (f *File) workbookWriter() {
 func (f *File) workbookWriter() {
 	if f.WorkBook != nil {
 	if f.WorkBook != nil {
@@ -75,7 +86,7 @@ func (f *File) workbookWriter() {
 	}
 	}
 }
 }
 
 
-// worksheetWriter provides function to save xl/worksheets/sheet%d.xml after
+// worksheetWriter provides a function to save xl/worksheets/sheet%d.xml after
 // serialize structure.
 // serialize structure.
 func (f *File) worksheetWriter() {
 func (f *File) worksheetWriter() {
 	for path, sheet := range f.Sheet {
 	for path, sheet := range f.Sheet {
@@ -93,7 +104,7 @@ func (f *File) worksheetWriter() {
 	}
 	}
 }
 }
 
 
-// trimCell provides function to trim blank cells which created by completeCol.
+// trimCell provides a function to trim blank cells which created by completeCol.
 func trimCell(column []xlsxC) []xlsxC {
 func trimCell(column []xlsxC) []xlsxC {
 	col := make([]xlsxC, len(column))
 	col := make([]xlsxC, len(column))
 	i := 0
 	i := 0
@@ -131,14 +142,22 @@ func (f *File) setSheet(index int, name string) {
 // allowed in sheet title.
 // allowed in sheet title.
 func (f *File) setWorkbook(name string, rid int) {
 func (f *File) setWorkbook(name string, rid int) {
 	content := f.workbookReader()
 	content := f.workbookReader()
+	rID := 0
+	for _, v := range content.Sheets.Sheet {
+		t, _ := strconv.Atoi(v.SheetID)
+		if t > rID {
+			rID = t
+		}
+	}
+	rID++
 	content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
 	content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
 		Name:    trimSheetName(name),
 		Name:    trimSheetName(name),
-		SheetID: strconv.Itoa(rid),
+		SheetID: strconv.Itoa(rID),
 		ID:      "rId" + strconv.Itoa(rid),
 		ID:      "rId" + strconv.Itoa(rid),
 	})
 	})
 }
 }
 
 
-// workbookRelsReader provides function to read and unmarshal workbook
+// workbookRelsReader provides a function to read and unmarshal workbook
 // relationships of XLSX file.
 // relationships of XLSX file.
 func (f *File) workbookRelsReader() *xlsxWorkbookRels {
 func (f *File) workbookRelsReader() *xlsxWorkbookRels {
 	if f.WorkBookRels == nil {
 	if f.WorkBookRels == nil {
@@ -149,7 +168,7 @@ func (f *File) workbookRelsReader() *xlsxWorkbookRels {
 	return f.WorkBookRels
 	return f.WorkBookRels
 }
 }
 
 
-// workbookRelsWriter provides function to save xl/_rels/workbook.xml.rels after
+// workbookRelsWriter provides a function to save xl/_rels/workbook.xml.rels after
 // serialize structure.
 // serialize structure.
 func (f *File) workbookRelsWriter() {
 func (f *File) workbookRelsWriter() {
 	if f.WorkBookRels != nil {
 	if f.WorkBookRels != nil {
@@ -238,8 +257,8 @@ func (f *File) SetActiveSheet(index int) {
 	}
 	}
 }
 }
 
 
-// GetActiveSheetIndex provides function to get active sheet index of the XLSX. If not
-// found the active sheet will be return integer 0.
+// GetActiveSheetIndex provides a function to get active sheet index of the
+// XLSX. If not found the active sheet will be return integer 0.
 func (f *File) GetActiveSheetIndex() int {
 func (f *File) GetActiveSheetIndex() int {
 	buffer := bytes.Buffer{}
 	buffer := bytes.Buffer{}
 	content := f.workbookReader()
 	content := f.workbookReader()
@@ -260,7 +279,7 @@ func (f *File) GetActiveSheetIndex() int {
 	return 0
 	return 0
 }
 }
 
 
-// SetSheetName provides function to set the worksheet name be given old and new
+// SetSheetName provides a function to set the worksheet name be given old and new
 // worksheet name. Maximum 31 characters are allowed in sheet title and this
 // worksheet name. Maximum 31 characters are allowed in sheet title and this
 // function only changes the name of the sheet and will not update the sheet
 // function only changes the name of the sheet and will not update the sheet
 // name in the formula or reference associated with the cell. So there may be
 // name in the formula or reference associated with the cell. So there may be
@@ -278,7 +297,7 @@ func (f *File) SetSheetName(oldName, newName string) {
 	}
 	}
 }
 }
 
 
-// GetSheetName provides function to get worksheet name of XLSX by given
+// GetSheetName provides a function to get worksheet name of XLSX by given
 // worksheet index. If given sheet index is invalid, will return an empty
 // worksheet index. If given sheet index is invalid, will return an empty
 // string.
 // string.
 func (f *File) GetSheetName(index int) string {
 func (f *File) GetSheetName(index int) string {
@@ -297,7 +316,7 @@ func (f *File) GetSheetName(index int) string {
 	return ""
 	return ""
 }
 }
 
 
-// GetSheetIndex provides function to get worksheet index of XLSX by given sheet
+// GetSheetIndex provides a function to get worksheet index of XLSX by given sheet
 // name. If given worksheet name is invalid, will return an integer type value
 // name. If given worksheet name is invalid, will return an integer type value
 // 0.
 // 0.
 func (f *File) GetSheetIndex(name string) int {
 func (f *File) GetSheetIndex(name string) int {
@@ -316,7 +335,7 @@ func (f *File) GetSheetIndex(name string) int {
 	return 0
 	return 0
 }
 }
 
 
-// GetSheetMap provides function to get worksheet name and index map of XLSX.
+// GetSheetMap provides a function to get worksheet name and index map of XLSX.
 // For example:
 // For example:
 //
 //
 //    xlsx, err := excelize.OpenFile("./Book1.xlsx")
 //    xlsx, err := excelize.OpenFile("./Book1.xlsx")
@@ -342,7 +361,7 @@ func (f *File) GetSheetMap() map[int]string {
 	return sheetMap
 	return sheetMap
 }
 }
 
 
-// getSheetMap provides function to get worksheet name and XML file path map of
+// getSheetMap provides a function to get worksheet name and XML file path map of
 // XLSX.
 // XLSX.
 func (f *File) getSheetMap() map[string]string {
 func (f *File) getSheetMap() map[string]string {
 	maps := make(map[string]string)
 	maps := make(map[string]string)
@@ -352,8 +371,8 @@ func (f *File) getSheetMap() map[string]string {
 	return maps
 	return maps
 }
 }
 
 
-// SetSheetBackground provides function to set background picture by given
-// worksheet name.
+// SetSheetBackground provides a function to set background picture by given
+// worksheet name and file path.
 func (f *File) SetSheetBackground(sheet, picture string) error {
 func (f *File) SetSheetBackground(sheet, picture string) error {
 	var err error
 	var err error
 	// Check picture exists first.
 	// Check picture exists first.
@@ -362,17 +381,18 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
 	}
 	}
 	ext, ok := supportImageTypes[path.Ext(picture)]
 	ext, ok := supportImageTypes[path.Ext(picture)]
 	if !ok {
 	if !ok {
-		return errors.New("Unsupported image extension")
+		return errors.New("unsupported image extension")
 	}
 	}
 	pictureID := f.countMedia() + 1
 	pictureID := f.countMedia() + 1
 	rID := f.addSheetRelationships(sheet, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, "")
 	rID := f.addSheetRelationships(sheet, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, "")
 	f.addSheetPicture(sheet, rID)
 	f.addSheetPicture(sheet, rID)
-	f.addMedia(picture, ext)
+	file, _ := ioutil.ReadFile(picture)
+	f.addMedia(file, ext)
 	f.setContentTypePartImageExtensions()
 	f.setContentTypePartImageExtensions()
 	return err
 	return err
 }
 }
 
 
-// DeleteSheet provides function to delete worksheet in a workbook by given
+// DeleteSheet provides a function to delete worksheet in a workbook by given
 // worksheet name. Use this method with caution, which will affect changes in
 // worksheet name. Use this method with caution, which will affect changes in
 // references such as formulas, charts, and so on. If there is any referenced
 // references such as formulas, charts, and so on. If there is any referenced
 // value of the deleted worksheet, it will cause a file error when you open it.
 // value of the deleted worksheet, it will cause a file error when you open it.
@@ -396,7 +416,7 @@ func (f *File) DeleteSheet(name string) {
 	f.SetActiveSheet(len(f.GetSheetMap()))
 	f.SetActiveSheet(len(f.GetSheetMap()))
 }
 }
 
 
-// deleteSheetFromWorkbookRels provides function to remove worksheet
+// deleteSheetFromWorkbookRels provides a function to remove worksheet
 // relationships by given relationships ID in the file
 // relationships by given relationships ID in the file
 // xl/_rels/workbook.xml.rels.
 // xl/_rels/workbook.xml.rels.
 func (f *File) deleteSheetFromWorkbookRels(rID string) string {
 func (f *File) deleteSheetFromWorkbookRels(rID string) string {
@@ -410,7 +430,7 @@ func (f *File) deleteSheetFromWorkbookRels(rID string) string {
 	return ""
 	return ""
 }
 }
 
 
-// deleteSheetFromContentTypes provides function to remove worksheet
+// deleteSheetFromContentTypes provides a function to remove worksheet
 // relationships by given target name in the file [Content_Types].xml.
 // relationships by given target name in the file [Content_Types].xml.
 func (f *File) deleteSheetFromContentTypes(target string) {
 func (f *File) deleteSheetFromContentTypes(target string) {
 	content := f.contentTypesReader()
 	content := f.contentTypesReader()
@@ -421,7 +441,7 @@ func (f *File) deleteSheetFromContentTypes(target string) {
 	}
 	}
 }
 }
 
 
-// CopySheet provides function to duplicate a worksheet by gave source and
+// CopySheet provides a function to duplicate a worksheet by gave source and
 // target worksheet index. Note that currently doesn't support duplicate
 // target worksheet index. Note that currently doesn't support duplicate
 // workbooks that contain tables, charts or pictures. For Example:
 // workbooks that contain tables, charts or pictures. For Example:
 //
 //
@@ -432,20 +452,17 @@ func (f *File) deleteSheetFromContentTypes(target string) {
 //
 //
 func (f *File) CopySheet(from, to int) error {
 func (f *File) CopySheet(from, to int) error {
 	if from < 1 || to < 1 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" {
 	if from < 1 || to < 1 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" {
-		return errors.New("Invalid worksheet index")
+		return errors.New("invalid worksheet index")
 	}
 	}
-	return f.copySheet(from, to)
+	f.copySheet(from, to)
+	return nil
 }
 }
 
 
-// copySheet provides function to duplicate a worksheet by gave source and
+// copySheet provides a function to duplicate a worksheet by gave source and
 // target worksheet name.
 // target worksheet name.
-func (f *File) copySheet(from, to int) error {
+func (f *File) copySheet(from, to int) {
 	sheet := f.workSheetReader("sheet" + strconv.Itoa(from))
 	sheet := f.workSheetReader("sheet" + strconv.Itoa(from))
-	worksheet := xlsxWorksheet{}
-	err := deepCopy(&worksheet, &sheet)
-	if err != nil {
-		return err
-	}
+	worksheet := deepcopy.Copy(sheet).(*xlsxWorksheet)
 	path := "xl/worksheets/sheet" + strconv.Itoa(to) + ".xml"
 	path := "xl/worksheets/sheet" + strconv.Itoa(to) + ".xml"
 	if len(worksheet.SheetViews.SheetView) > 0 {
 	if len(worksheet.SheetViews.SheetView) > 0 {
 		worksheet.SheetViews.SheetView[0].TabSelected = false
 		worksheet.SheetViews.SheetView[0].TabSelected = false
@@ -453,17 +470,16 @@ func (f *File) copySheet(from, to int) error {
 	worksheet.Drawing = nil
 	worksheet.Drawing = nil
 	worksheet.TableParts = nil
 	worksheet.TableParts = nil
 	worksheet.PageSetUp = nil
 	worksheet.PageSetUp = nil
-	f.Sheet[path] = &worksheet
+	f.Sheet[path] = worksheet
 	toRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(to) + ".xml.rels"
 	toRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(to) + ".xml.rels"
 	fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(from) + ".xml.rels"
 	fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(from) + ".xml.rels"
 	_, ok := f.XLSX[fromRels]
 	_, ok := f.XLSX[fromRels]
 	if ok {
 	if ok {
 		f.XLSX[toRels] = f.XLSX[fromRels]
 		f.XLSX[toRels] = f.XLSX[fromRels]
 	}
 	}
-	return err
 }
 }
 
 
-// SetSheetVisible provides function to set worksheet visible by given worksheet
+// SetSheetVisible provides a function to set worksheet visible by given worksheet
 // name. A workbook must contain at least one visible worksheet. If the given
 // name. A workbook must contain at least one visible worksheet. If the given
 // worksheet has been activated, this setting will be invalidated. Sheet state
 // worksheet has been activated, this setting will be invalidated. Sheet state
 // values as defined by http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx
 // values as defined by http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx
@@ -505,14 +521,14 @@ func (f *File) SetSheetVisible(name string, visible bool) {
 	}
 	}
 }
 }
 
 
-// parseFormatPanesSet provides function to parse the panes settings.
+// parseFormatPanesSet provides a function to parse the panes settings.
 func parseFormatPanesSet(formatSet string) (*formatPanes, error) {
 func parseFormatPanesSet(formatSet string) (*formatPanes, error) {
 	format := formatPanes{}
 	format := formatPanes{}
 	err := json.Unmarshal([]byte(formatSet), &format)
 	err := json.Unmarshal([]byte(formatSet), &format)
 	return &format, err
 	return &format, err
 }
 }
 
 
-// SetPanes provides function to create and remove freeze panes and split panes
+// SetPanes provides a function to create and remove freeze panes and split panes
 // by given worksheet name and panes format set.
 // by given worksheet name and panes format set.
 //
 //
 // activePane defines the pane that is active. The possible values for this
 // activePane defines the pane that is active. The possible values for this
@@ -626,7 +642,7 @@ func (f *File) SetPanes(sheet, panes string) {
 	xlsx.SheetViews.SheetView[len(xlsx.SheetViews.SheetView)-1].Selection = s
 	xlsx.SheetViews.SheetView[len(xlsx.SheetViews.SheetView)-1].Selection = s
 }
 }
 
 
-// GetSheetVisible provides function to get worksheet visible by given worksheet
+// GetSheetVisible provides a function to get worksheet visible by given worksheet
 // name. For example, get visible state of Sheet1:
 // name. For example, get visible state of Sheet1:
 //
 //
 //    xlsx.GetSheetVisible("Sheet1")
 //    xlsx.GetSheetVisible("Sheet1")
@@ -644,7 +660,7 @@ func (f *File) GetSheetVisible(name string) bool {
 	return visible
 	return visible
 }
 }
 
 
-// trimSheetName provides function to trim invaild characters by given worksheet
+// trimSheetName provides a function to trim invaild characters by given worksheet
 // name.
 // name.
 func trimSheetName(name string) string {
 func trimSheetName(name string) string {
 	r := []rune{}
 	r := []rune{}

+ 11 - 2
sheetpr.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 // SheetPrOption is an option of a view of a worksheet. See SetSheetPrOptions().
 // SheetPrOption is an option of a view of a worksheet. See SetSheetPrOptions().
@@ -98,7 +107,7 @@ func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
 	*o = AutoPageBreaks(pr.PageSetUpPr.AutoPageBreaks)
 	*o = AutoPageBreaks(pr.PageSetUpPr.AutoPageBreaks)
 }
 }
 
 
-// SetSheetPrOptions provides function to sets worksheet properties.
+// SetSheetPrOptions provides a function to sets worksheet properties.
 //
 //
 // Available options:
 // Available options:
 //   CodeName(string)
 //   CodeName(string)
@@ -120,7 +129,7 @@ func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
 	return nil
 	return nil
 }
 }
 
 
-// GetSheetPrOptions provides function to gets worksheet properties.
+// GetSheetPrOptions provides a function to gets worksheet properties.
 //
 //
 // Available options:
 // Available options:
 //   CodeName(string)
 //   CodeName(string)

+ 9 - 0
sheetview.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "fmt"
 import "fmt"

+ 160 - 105
styles.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -10,8 +19,8 @@ import (
 )
 )
 
 
 // Excel styles can reference number formats that are built-in, all of which
 // 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.
+// 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{
 var builtInNumFmt = map[int]string{
 	0:  "general",
 	0:  "general",
 	1:  "0",
 	1:  "0",
@@ -798,45 +807,46 @@ var validType = map[string]string{
 
 
 // criteriaType defined the list of valid criteria types.
 // criteriaType defined the list of valid criteria types.
 var criteriaType = map[string]string{
 var criteriaType = map[string]string{
-	"between":      "between",
-	"not between":  "notBetween",
-	"equal to":     "equal",
-	"=":            "equal",
-	"==":           "equal",
-	"not equal to": "notEqual",
-	"!=":           "notEqual",
-	"<>":           "notEqual",
-	"greater than": "greaterThan",
-	">":            "greaterThan",
-	"less than":    "lessThan",
-	"<":            "lessThan",
+	"between":                  "between",
+	"not between":              "notBetween",
+	"equal to":                 "equal",
+	"=":                        "equal",
+	"==":                       "equal",
+	"not equal to":             "notEqual",
+	"!=":                       "notEqual",
+	"<>":                       "notEqual",
+	"greater than":             "greaterThan",
+	">":                        "greaterThan",
+	"less than":                "lessThan",
+	"<":                        "lessThan",
 	"greater than or equal to": "greaterThanOrEqual",
 	"greater than or equal to": "greaterThanOrEqual",
-	">=": "greaterThanOrEqual",
-	"less than or equal to": "lessThanOrEqual",
-	"<=":             "lessThanOrEqual",
-	"containing":     "containsText",
-	"not containing": "notContains",
-	"begins with":    "beginsWith",
-	"ends with":      "endsWith",
-	"yesterday":      "yesterday",
-	"today":          "today",
-	"last 7 days":    "last7Days",
-	"last week":      "lastWeek",
-	"this week":      "thisWeek",
-	"continue week":  "continueWeek",
-	"last month":     "lastMonth",
-	"this month":     "thisMonth",
-	"continue month": "continueMonth",
+	">=":                       "greaterThanOrEqual",
+	"less than or equal to":    "lessThanOrEqual",
+	"<=":                       "lessThanOrEqual",
+	"containing":               "containsText",
+	"not containing":           "notContains",
+	"begins with":              "beginsWith",
+	"ends with":                "endsWith",
+	"yesterday":                "yesterday",
+	"today":                    "today",
+	"last 7 days":              "last7Days",
+	"last week":                "lastWeek",
+	"this week":                "thisWeek",
+	"continue week":            "continueWeek",
+	"last month":               "lastMonth",
+	"this month":               "thisMonth",
+	"continue month":           "continueMonth",
 }
 }
 
 
-// formatToString provides function to return original string by given built-in
-// number formats code and cell string.
+// formatToString provides a function to return original string by given
+// built-in number formats code and cell string.
 func formatToString(i int, v string) string {
 func formatToString(i int, v string) string {
 	return v
 	return v
 }
 }
 
 
-// formatToInt provides function to convert original string to integer format as
-// string type by given built-in number formats code and cell string.
+// formatToInt provides a function to convert original string to integer
+// format as string type by given built-in number formats code and cell
+// string.
 func formatToInt(i int, v string) string {
 func formatToInt(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -845,8 +855,9 @@ func formatToInt(i int, v string) string {
 	return fmt.Sprintf("%d", int(f))
 	return fmt.Sprintf("%d", int(f))
 }
 }
 
 
-// formatToFloat provides function to convert original string to float format as
-// string type by given built-in number formats code and cell string.
+// formatToFloat provides a function to convert original string to float
+// format as string type by given built-in number formats code and cell
+// string.
 func formatToFloat(i int, v string) string {
 func formatToFloat(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -855,8 +866,8 @@ func formatToFloat(i int, v string) string {
 	return fmt.Sprintf("%.2f", f)
 	return fmt.Sprintf("%.2f", f)
 }
 }
 
 
-// formatToA provides function to convert original string to special format as
-// string type by given built-in number formats code and cell string.
+// formatToA provides a function to convert original string to special format
+// as string type by given built-in number formats code and cell string.
 func formatToA(i int, v string) string {
 func formatToA(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -870,8 +881,8 @@ func formatToA(i int, v string) string {
 	return fmt.Sprintf("%d", t)
 	return fmt.Sprintf("%d", t)
 }
 }
 
 
-// formatToB provides function to convert original string to special format as
-// string type by given built-in number formats code and cell string.
+// formatToB provides a function to convert original string to special format
+// as string type by given built-in number formats code and cell string.
 func formatToB(i int, v string) string {
 func formatToB(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -883,8 +894,8 @@ func formatToB(i int, v string) string {
 	return fmt.Sprintf("%.2f", f)
 	return fmt.Sprintf("%.2f", f)
 }
 }
 
 
-// formatToC provides function to convert original string to special format as
-// string type by given built-in number formats code and cell string.
+// formatToC provides a function to convert original string to special format
+// as string type by given built-in number formats code and cell string.
 func formatToC(i int, v string) string {
 func formatToC(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -894,8 +905,8 @@ func formatToC(i int, v string) string {
 	return fmt.Sprintf("%d%%", int(f))
 	return fmt.Sprintf("%d%%", int(f))
 }
 }
 
 
-// formatToD provides function to convert original string to special format as
-// string type by given built-in number formats code and cell string.
+// formatToD provides a function to convert original string to special format
+// as string type by given built-in number formats code and cell string.
 func formatToD(i int, v string) string {
 func formatToD(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -905,8 +916,8 @@ func formatToD(i int, v string) string {
 	return fmt.Sprintf("%.2f%%", f)
 	return fmt.Sprintf("%.2f%%", f)
 }
 }
 
 
-// formatToE provides function to convert original string to special format as
-// string type by given built-in number formats code and cell string.
+// formatToE provides a function to convert original string to special format
+// as string type by given built-in number formats code and cell string.
 func formatToE(i int, v string) string {
 func formatToE(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -915,17 +926,17 @@ func formatToE(i int, v string) string {
 	return fmt.Sprintf("%.e", f)
 	return fmt.Sprintf("%.e", f)
 }
 }
 
 
-// parseTime provides function to returns a string parsed using time.Time.
+// parseTime provides a function to returns a string parsed using time.Time.
 // Replace Excel placeholders with Go time placeholders. For example, replace
 // Replace Excel placeholders with Go time placeholders. For example, replace
-// yyyy with 2006. These are in a specific order, due to the fact that m is used
-// in month, minute, and am/pm. It would be easier to fix that with regular
-// expressions, but if it's possible to keep this simple it would be easier to
-// maintain. Full-length month and days (e.g. March, Tuesday) have letters in
-// them that would be replaced by other characters below (such as the 'h' in
-// March, or the 'd' in Tuesday) below. First we convert them to arbitrary
-// characters unused in Excel Date formats, and then at the end, turn them to
-// what they should actually be.
-// Based off: http://www.ozgrid.com/Excel/CustomFormats.htm
+// yyyy with 2006. These are in a specific order, due to the fact that m is
+// used in month, minute, and am/pm. It would be easier to fix that with
+// regular expressions, but if it's possible to keep this simple it would be
+// easier to maintain. Full-length month and days (e.g. March, Tuesday) have
+// letters in them that would be replaced by other characters below (such as
+// the 'h' in March, or the 'd' in Tuesday) below. First we convert them to
+// arbitrary characters unused in Excel Date formats, and then at the end,
+// turn them to what they should actually be. Based off:
+// http://www.ozgrid.com/Excel/CustomFormats.htm
 func parseTime(i int, v string) string {
 func parseTime(i int, v string) string {
 	f, err := strconv.ParseFloat(v, 64)
 	f, err := strconv.ParseFloat(v, 64)
 	if err != nil {
 	if err != nil {
@@ -983,7 +994,7 @@ func is12HourTime(format string) bool {
 	return strings.Contains(format, "am/pm") || strings.Contains(format, "AM/PM") || strings.Contains(format, "a/p") || strings.Contains(format, "A/P")
 	return strings.Contains(format, "am/pm") || strings.Contains(format, "AM/PM") || strings.Contains(format, "a/p") || strings.Contains(format, "A/P")
 }
 }
 
 
-// stylesReader provides function to get the pointer to the structure after
+// stylesReader provides a function to get the pointer to the structure after
 // deserialization of xl/styles.xml.
 // deserialization of xl/styles.xml.
 func (f *File) stylesReader() *xlsxStyleSheet {
 func (f *File) stylesReader() *xlsxStyleSheet {
 	if f.Styles == nil {
 	if f.Styles == nil {
@@ -994,7 +1005,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
 	return f.Styles
 	return f.Styles
 }
 }
 
 
-// styleSheetWriter provides function to save xl/styles.xml after serialize
+// styleSheetWriter provides a function to save xl/styles.xml after serialize
 // structure.
 // structure.
 func (f *File) styleSheetWriter() {
 func (f *File) styleSheetWriter() {
 	if f.Styles != nil {
 	if f.Styles != nil {
@@ -1003,7 +1014,7 @@ func (f *File) styleSheetWriter() {
 	}
 	}
 }
 }
 
 
-// parseFormatStyleSet provides function to parse the format settings of the
+// parseFormatStyleSet provides a function to parse the format settings of the
 // cells and conditional formats.
 // cells and conditional formats.
 func parseFormatStyleSet(style string) (*formatStyle, error) {
 func parseFormatStyleSet(style string) (*formatStyle, error) {
 	format := formatStyle{
 	format := formatStyle{
@@ -1013,8 +1024,8 @@ func parseFormatStyleSet(style string) (*formatStyle, error) {
 	return &format, err
 	return &format, err
 }
 }
 
 
-// NewStyle provides function to create style for cells by given style format.
-// Note that the color field uses RGB color code.
+// NewStyle provides a function to create style for cells by given style
+// format. Note that the color field uses RGB color code.
 //
 //
 // The following shows the border styles sorted by excelize index number:
 // The following shows the border styles sorted by excelize index number:
 //
 //
@@ -1404,7 +1415,7 @@ func parseFormatStyleSet(style string) (*formatStyle, error) {
 //     173   | $ English (New Zealand)
 //     173   | $ English (New Zealand)
 //     174   | $ English (Singapore)
 //     174   | $ English (Singapore)
 //     175   | $ English (Trinidad & Tobago)
 //     175   | $ English (Trinidad & Tobago)
-//     176   | $ English (U.S. Vigin Islands)
+//     176   | $ English (U.S. Virgin Islands)
 //     177   | $ English (United States)
 //     177   | $ English (United States)
 //     178   | $ French (Canada)
 //     178   | $ French (Canada)
 //     179   | $ Hawaiian (United States)
 //     179   | $ Hawaiian (United States)
@@ -1906,10 +1917,10 @@ func (f *File) NewStyle(style string) (int, error) {
 	return cellXfsID, nil
 	return cellXfsID, nil
 }
 }
 
 
-// NewConditionalStyle provides function to create style for conditional format
-// by given style format. The parameters are the same as function NewStyle().
-// Note that the color field uses RGB color code and only support to set font,
-// fills, alignment and borders currently.
+// NewConditionalStyle provides a function to create style for conditional
+// format by given style format. The parameters are the same as function
+// NewStyle(). Note that the color field uses RGB color code and only support
+// to set font, fills, alignment and borders currently.
 func (f *File) NewConditionalStyle(style string) (int, error) {
 func (f *File) NewConditionalStyle(style string) (int, error) {
 	s := f.stylesReader()
 	s := f.stylesReader()
 	fs, err := parseFormatStyleSet(style)
 	fs, err := parseFormatStyleSet(style)
@@ -1935,7 +1946,8 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
 	return s.Dxfs.Count - 1, nil
 	return s.Dxfs.Count - 1, nil
 }
 }
 
 
-// setFont provides function to add font style by given cell format settings.
+// setFont provides a function to add font style by given cell format
+// settings.
 func setFont(formatStyle *formatStyle) *font {
 func setFont(formatStyle *formatStyle) *font {
 	fontUnderlineType := map[string]string{"single": "single", "double": "double"}
 	fontUnderlineType := map[string]string{"single": "single", "double": "double"}
 	if formatStyle.Font.Size < 1 {
 	if formatStyle.Font.Size < 1 {
@@ -1963,8 +1975,8 @@ func setFont(formatStyle *formatStyle) *font {
 	return &f
 	return &f
 }
 }
 
 
-// setNumFmt provides function to check if number format code in the range of
-// built-in values.
+// setNumFmt provides a function to check if number format code in the range
+// of built-in values.
 func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	dp := "0."
 	dp := "0."
 	numFmtID := 164 // Default custom number format code from 164.
 	numFmtID := 164 // Default custom number format code from 164.
@@ -2011,7 +2023,7 @@ func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	return formatStyle.NumFmt
 	return formatStyle.NumFmt
 }
 }
 
 
-// setCustomNumFmt provides function to set custom number format code.
+// setCustomNumFmt provides a function to set custom number format code.
 func setCustomNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 func setCustomNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	nf := xlsxNumFmt{FormatCode: *formatStyle.CustomNumFmt}
 	nf := xlsxNumFmt{FormatCode: *formatStyle.CustomNumFmt}
 	if style.NumFmts != nil {
 	if style.NumFmts != nil {
@@ -2029,7 +2041,7 @@ func setCustomNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	return nf.NumFmtID
 	return nf.NumFmtID
 }
 }
 
 
-// setLangNumFmt provides function to set number format code with language.
+// setLangNumFmt provides a function to set number format code with language.
 func setLangNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 func setLangNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	numFmts, ok := langNumFmt[formatStyle.Lang]
 	numFmts, ok := langNumFmt[formatStyle.Lang]
 	if !ok {
 	if !ok {
@@ -2056,8 +2068,8 @@ func setLangNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
 	return nf.NumFmtID
 	return nf.NumFmtID
 }
 }
 
 
-// setFills provides function to add fill elements in the styles.xml by given
-// cell format settings.
+// setFills provides a function to add fill elements in the styles.xml by
+// given cell format settings.
 func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
 func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
 	var patterns = []string{
 	var patterns = []string{
 		"none",
 		"none",
@@ -2137,9 +2149,10 @@ func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
 	return &fill
 	return &fill
 }
 }
 
 
-// setAlignment provides function to formatting information pertaining to text
-// alignment in cells. There are a variety of choices for how text is aligned
-// both horizontally and vertically, as well as indentation settings, and so on.
+// setAlignment provides a function to formatting information pertaining to
+// text alignment in cells. There are a variety of choices for how text is
+// aligned both horizontally and vertically, as well as indentation settings,
+// and so on.
 func setAlignment(formatStyle *formatStyle) *xlsxAlignment {
 func setAlignment(formatStyle *formatStyle) *xlsxAlignment {
 	var alignment xlsxAlignment
 	var alignment xlsxAlignment
 	if formatStyle.Alignment != nil {
 	if formatStyle.Alignment != nil {
@@ -2156,7 +2169,7 @@ func setAlignment(formatStyle *formatStyle) *xlsxAlignment {
 	return &alignment
 	return &alignment
 }
 }
 
 
-// setProtection provides function to set protection properties associated
+// setProtection provides a function to set protection properties associated
 // with the cell.
 // with the cell.
 func setProtection(formatStyle *formatStyle) *xlsxProtection {
 func setProtection(formatStyle *formatStyle) *xlsxProtection {
 	var protection xlsxProtection
 	var protection xlsxProtection
@@ -2167,7 +2180,7 @@ func setProtection(formatStyle *formatStyle) *xlsxProtection {
 	return &protection
 	return &protection
 }
 }
 
 
-// setBorders provides function to add border elements in the styles.xml by
+// setBorders provides a function to add border elements in the styles.xml by
 // given borders format settings.
 // given borders format settings.
 func setBorders(formatStyle *formatStyle) *xlsxBorder {
 func setBorders(formatStyle *formatStyle) *xlsxBorder {
 	var styles = []string{
 	var styles = []string{
@@ -2219,7 +2232,7 @@ func setBorders(formatStyle *formatStyle) *xlsxBorder {
 	return &border
 	return &border
 }
 }
 
 
-// setCellXfs provides function to set describes all of the formatting for a
+// setCellXfs provides a function to set describes all of the formatting for a
 // cell.
 // cell.
 func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) int {
 func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) int {
 	var xf xlsxXf
 	var xf xlsxXf
@@ -2246,9 +2259,10 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
 	return style.CellXfs.Count - 1
 	return style.CellXfs.Count - 1
 }
 }
 
 
-// SetCellStyle provides function to add style attribute for cells by given
+// SetCellStyle provides a function to add style attribute for cells by given
 // worksheet name, coordinate area and style ID. Note that diagonalDown and
 // worksheet name, coordinate area and style ID. Note that diagonalDown and
-// diagonalUp type border should be use same color in the same coordinate area.
+// diagonalUp type border should be use same color in the same coordinate
+// area.
 //
 //
 // For example create a borders of cell H9 on Sheet1:
 // For example create a borders of cell H9 on Sheet1:
 //
 //
@@ -2352,9 +2366,10 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) {
 	}
 	}
 }
 }
 
 
-// SetConditionalFormat provides function to create conditional formatting rule
-// for cell value. Conditional formatting is a feature of Excel which allows you
-// to apply a format to a cell or a range of cells based on certain criteria.
+// SetConditionalFormat provides a function to create conditional formatting
+// rule for cell value. Conditional formatting is a feature of Excel which
+// allows you to apply a format to a cell or a range of cells based on certain
+// criteria.
 //
 //
 // The type option is a required parameter and it has no default value.
 // The type option is a required parameter and it has no default value.
 // Allowable type values and their associated parameters are:
 // Allowable type values and their associated parameters are:
@@ -2407,7 +2422,7 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) {
 //
 //
 // The criteria parameter is used to set the criteria by which the cell data
 // The criteria parameter is used to set the criteria by which the cell data
 // will be evaluated. It has no default value. The most common criteria as
 // will be evaluated. It has no default value. The most common criteria as
-// applied to {'type': 'cell'} are:
+// applied to {"type":"cell"} are:
 //
 //
 //    between                  |
 //    between                  |
 //    not between              |
 //    not between              |
@@ -2606,9 +2621,9 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
 	return err
 	return err
 }
 }
 
 
-// drawCondFmtCellIs provides function to create conditional formatting rule for
-// cell value (include between, not between, equal, not equal, greater than and
-// less than) by given priority, criteria type and format settings.
+// drawCondFmtCellIs provides a function to create conditional formatting rule
+// for cell value (include between, not between, equal, not equal, greater
+// than and less than) by given priority, criteria type and format settings.
 func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule {
 	c := &xlsxCfRule{
 	c := &xlsxCfRule{
 		Priority: p + 1,
 		Priority: p + 1,
@@ -2629,8 +2644,8 @@ func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule
 	return c
 	return c
 }
 }
 
 
-// drawCondFmtTop10 provides function to create conditional formatting rule for
-// top N (default is top 10) by given priority, criteria type and format
+// drawCondFmtTop10 provides a function to create conditional formatting rule
+// for top N (default is top 10) by given priority, criteria type and format
 // settings.
 // settings.
 func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule {
 	c := &xlsxCfRule{
 	c := &xlsxCfRule{
@@ -2647,9 +2662,9 @@ func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule {
 	return c
 	return c
 }
 }
 
 
-// drawCondFmtAboveAverage provides function to create conditional formatting
-// rule for above average and below average by given priority, criteria type and
-// format settings.
+// drawCondFmtAboveAverage provides a function to create conditional
+// formatting rule for above average and below average by given priority,
+// criteria type and format settings.
 func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxCfRule {
 	return &xlsxCfRule{
 	return &xlsxCfRule{
 		Priority:     p + 1,
 		Priority:     p + 1,
@@ -2659,7 +2674,7 @@ func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxC
 	}
 	}
 }
 }
 
 
-// drawCondFmtDuplicateUniqueValues provides function to create conditional
+// drawCondFmtDuplicateUniqueValues provides a function to create conditional
 // formatting rule for duplicate and unique values by given priority, criteria
 // formatting rule for duplicate and unique values by given priority, criteria
 // type and format settings.
 // type and format settings.
 func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditional) *xlsxCfRule {
@@ -2670,16 +2685,29 @@ func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditiona
 	}
 	}
 }
 }
 
 
-// drawCondFmtColorScale provides function to create conditional formatting rule
-// for color scale (include 2 color scale and 3 color scale) by given priority,
-// criteria type and format settings.
+// drawCondFmtColorScale provides a function to create conditional formatting
+// rule for color scale (include 2 color scale and 3 color scale) by given
+// priority, criteria type and format settings.
 func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfRule {
+	minValue := format.MinValue
+	if minValue == "" {
+		minValue = "0"
+	}
+	maxValue := format.MaxValue
+	if maxValue == "" {
+		maxValue = "0"
+	}
+	midValue := format.MidValue
+	if midValue == "" {
+		midValue = "50"
+	}
+
 	c := &xlsxCfRule{
 	c := &xlsxCfRule{
 		Priority: p + 1,
 		Priority: p + 1,
 		Type:     "colorScale",
 		Type:     "colorScale",
 		ColorScale: &xlsxColorScale{
 		ColorScale: &xlsxColorScale{
 			Cfvo: []*xlsxCfvo{
 			Cfvo: []*xlsxCfvo{
-				{Type: format.MinType},
+				{Type: format.MinType, Val: minValue},
 			},
 			},
 			Color: []*xlsxColor{
 			Color: []*xlsxColor{
 				{RGB: getPaletteColor(format.MinColor)},
 				{RGB: getPaletteColor(format.MinColor)},
@@ -2687,16 +2715,16 @@ func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfR
 		},
 		},
 	}
 	}
 	if validType[format.Type] == "3_color_scale" {
 	if validType[format.Type] == "3_color_scale" {
-		c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MidType, Val: 50})
+		c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MidType, Val: midValue})
 		c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MidColor)})
 		c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MidColor)})
 	}
 	}
-	c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType})
+	c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType, Val: maxValue})
 	c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)})
 	c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)})
 	return c
 	return c
 }
 }
 
 
-// drawCondFmtDataBar provides function to create conditional formatting rule
-// for data bar by given priority, criteria type and format settings.
+// drawCondFmtDataBar provides a function to create conditional formatting
+// rule for data bar by given priority, criteria type and format settings.
 func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule {
 	return &xlsxCfRule{
 	return &xlsxCfRule{
 		Priority: p + 1,
 		Priority: p + 1,
@@ -2708,8 +2736,8 @@ func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule
 	}
 	}
 }
 }
 
 
-// drawConfFmtExp provides function to create conditional formatting rule for
-// expression by given priority, criteria type and format settings.
+// drawConfFmtExp provides a function to create conditional formatting rule
+// for expression by given priority, criteria type and format settings.
 func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule {
 func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule {
 	return &xlsxCfRule{
 	return &xlsxCfRule{
 		Priority: p + 1,
 		Priority: p + 1,
@@ -2719,7 +2747,34 @@ func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule {
 	}
 	}
 }
 }
 
 
-// getPaletteColor provides function to convert the RBG color by given string.
+// getPaletteColor provides a function to convert the RBG color by given
+// string.
 func getPaletteColor(color string) string {
 func getPaletteColor(color string) string {
 	return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1)
 	return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1)
 }
 }
+
+// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
+// structure after deserialization.
+func (f *File) themeReader() *xlsxTheme {
+	var theme xlsxTheme
+	_ = xml.Unmarshal([]byte(f.readXML("xl/theme/theme1.xml")), &theme)
+	return &theme
+}
+
+// ThemeColor applied the color with tint value.
+func ThemeColor(baseColor string, tint float64) string {
+	if tint == 0 {
+		return "FF" + baseColor
+	}
+	r, _ := strconv.ParseInt(baseColor[0:2], 16, 64)
+	g, _ := strconv.ParseInt(baseColor[2:4], 16, 64)
+	b, _ := strconv.ParseInt(baseColor[4:6], 16, 64)
+	h, s, l := RGBToHSL(uint8(r), uint8(g), uint8(b))
+	if tint < 0 {
+		l *= (1 + tint)
+	} else {
+		l = l*(1-tint) + (1 - (1 - tint))
+	}
+	br, bg, bb := HSLToRGB(h, s, l)
+	return fmt.Sprintf("FF%02X%02X%02X", br, bg, bb)
+}

+ 134 - 0
styles_test.go

@@ -0,0 +1,134 @@
+package excelize
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSetConditionalFormat(t *testing.T) {
+	cases := []struct {
+		label  string
+		format string
+		rules  []*xlsxCfRule
+	}{{
+		label: "3_color_scale",
+		format: `[{
+			"type":"3_color_scale",
+			"criteria":"=",
+			"min_type":"num",
+			"mid_type":"num",
+			"max_type":"num",
+			"min_value": "-10",
+			"mid_value": "0",
+			"max_value": "10",
+			"min_color":"ff0000",
+			"mid_color":"00ff00",
+			"max_color":"0000ff"
+		}]`,
+		rules: []*xlsxCfRule{{
+			Priority: 1,
+			Type:     "colorScale",
+			ColorScale: &xlsxColorScale{
+				Cfvo: []*xlsxCfvo{{
+					Type: "num",
+					Val:  "-10",
+				}, {
+					Type: "num",
+					Val:  "0",
+				}, {
+					Type: "num",
+					Val:  "10",
+				}},
+				Color: []*xlsxColor{{
+					RGB: "FFFF0000",
+				}, {
+					RGB: "FF00FF00",
+				}, {
+					RGB: "FF0000FF",
+				}},
+			},
+		}},
+	}, {
+		label: "3_color_scale default min/mid/max",
+		format: `[{
+			"type":"3_color_scale",
+			"criteria":"=",
+			"min_type":"num",
+			"mid_type":"num",
+			"max_type":"num",
+			"min_color":"ff0000",
+			"mid_color":"00ff00",
+			"max_color":"0000ff"
+		}]`,
+		rules: []*xlsxCfRule{{
+			Priority: 1,
+			Type:     "colorScale",
+			ColorScale: &xlsxColorScale{
+				Cfvo: []*xlsxCfvo{{
+					Type: "num",
+					Val:  "0",
+				}, {
+					Type: "num",
+					Val:  "50",
+				}, {
+					Type: "num",
+					Val:  "0",
+				}},
+				Color: []*xlsxColor{{
+					RGB: "FFFF0000",
+				}, {
+					RGB: "FF00FF00",
+				}, {
+					RGB: "FF0000FF",
+				}},
+			},
+		}},
+	}, {
+		label: "2_color_scale default min/max",
+		format: `[{
+			"type":"2_color_scale",
+			"criteria":"=",
+			"min_type":"num",
+			"max_type":"num",
+			"min_color":"ff0000",
+			"max_color":"0000ff"
+		}]`,
+		rules: []*xlsxCfRule{{
+			Priority: 1,
+			Type:     "colorScale",
+			ColorScale: &xlsxColorScale{
+				Cfvo: []*xlsxCfvo{{
+					Type: "num",
+					Val:  "0",
+				}, {
+					Type: "num",
+					Val:  "0",
+				}},
+				Color: []*xlsxColor{{
+					RGB: "FFFF0000",
+				}, {
+					RGB: "FF0000FF",
+				}},
+			},
+		}},
+	}}
+
+	for _, testCase := range cases {
+		xl := NewFile()
+		const sheet = "Sheet1"
+		const cellRange = "A1:A1"
+
+		err := xl.SetConditionalFormat(sheet, cellRange, testCase.format)
+		if err != nil {
+			t.Fatalf("%s", err)
+		}
+
+		xlsx := xl.workSheetReader(sheet)
+		cf := xlsx.ConditionalFormatting
+		assert.Len(t, cf, 1, testCase.label)
+		assert.Len(t, cf[0].CfRule, 1, testCase.label)
+		assert.Equal(t, cellRange, cf[0].SQRef, testCase.label)
+		assert.EqualValues(t, testCase.rules, cf[0].CfRule, testCase.label)
+	}
+}

+ 29 - 19
table.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import (
 import (
@@ -9,14 +18,14 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-// parseFormatTableSet provides function to parse the format settings of the
+// parseFormatTableSet provides a function to parse the format settings of the
 // table with default value.
 // table with default value.
 func parseFormatTableSet(formatSet string) (*formatTable, error) {
 func parseFormatTableSet(formatSet string) (*formatTable, error) {
 	format := formatTable{
 	format := formatTable{
 		TableStyle:     "",
 		TableStyle:     "",
 		ShowRowStripes: true,
 		ShowRowStripes: true,
 	}
 	}
-	err := json.Unmarshal([]byte(formatSet), &format)
+	err := json.Unmarshal(parseFormatSet(formatSet), &format)
 	return &format, err
 	return &format, err
 }
 }
 
 
@@ -75,8 +84,8 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
 	return err
 	return err
 }
 }
 
 
-// countTables provides function to get table files count storage in the folder
-// xl/tables.
+// countTables provides a function to get table files count storage in the
+// folder xl/tables.
 func (f *File) countTables() int {
 func (f *File) countTables() int {
 	count := 0
 	count := 0
 	for k := range f.XLSX {
 	for k := range f.XLSX {
@@ -87,7 +96,7 @@ func (f *File) countTables() int {
 	return count
 	return count
 }
 }
 
 
-// addSheetTable provides function to add tablePart element to
+// addSheetTable provides a function to add tablePart element to
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
 func (f *File) addSheetTable(sheet string, rID int) {
 func (f *File) addSheetTable(sheet string, rID int) {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -101,8 +110,8 @@ func (f *File) addSheetTable(sheet string, rID int) {
 	xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
 	xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
 }
 }
 
 
-// addTable provides function to add table by given worksheet name, coordinate
-// area and format set.
+// addTable provides a function to add table by given worksheet name,
+// coordinate area and format set.
 func (f *File) addTable(sheet, tableXML string, hxAxis, hyAxis, vxAxis, vyAxis, i int, formatSet *formatTable) {
 func (f *File) addTable(sheet, tableXML string, hxAxis, hyAxis, vxAxis, vyAxis, i int, formatSet *formatTable) {
 	// Correct the minimum number of rows, the table at least two lines.
 	// Correct the minimum number of rows, the table at least two lines.
 	if hyAxis == vyAxis {
 	if hyAxis == vyAxis {
@@ -157,7 +166,7 @@ func (f *File) addTable(sheet, tableXML string, hxAxis, hyAxis, vxAxis, vyAxis,
 	f.saveFileList(tableXML, table)
 	f.saveFileList(tableXML, table)
 }
 }
 
 
-// parseAutoFilterSet provides function to parse the settings of the auto
+// parseAutoFilterSet provides a function to parse the settings of the auto
 // filter.
 // filter.
 func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
 func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
 	format := formatAutoFilter{}
 	format := formatAutoFilter{}
@@ -264,7 +273,7 @@ func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
 	return f.autoFilter(sheet, ref, refRange, hxAxis, formatSet)
 	return f.autoFilter(sheet, ref, refRange, hxAxis, formatSet)
 }
 }
 
 
-// autoFilter provides function to extract the tokens from the filter
+// autoFilter provides a function to extract the tokens from the filter
 // expression. The tokens are mainly non-whitespace groups.
 // expression. The tokens are mainly non-whitespace groups.
 func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *formatAutoFilter) error {
 func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *formatAutoFilter) error {
 	xlsx := f.workSheetReader(sheet)
 	xlsx := f.workSheetReader(sheet)
@@ -282,7 +291,7 @@ func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *fo
 	col := TitleToNumber(formatSet.Column)
 	col := TitleToNumber(formatSet.Column)
 	offset := col - hxAxis
 	offset := col - hxAxis
 	if offset < 0 || offset > refRange {
 	if offset < 0 || offset > refRange {
-		return fmt.Errorf("Incorrect index of column '%s'", formatSet.Column)
+		return fmt.Errorf("incorrect index of column '%s'", formatSet.Column)
 	}
 	}
 	filter.FilterColumn = &xlsxFilterColumn{
 	filter.FilterColumn = &xlsxFilterColumn{
 		ColID: offset,
 		ColID: offset,
@@ -290,7 +299,7 @@ func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *fo
 	re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`)
 	re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`)
 	token := re.FindAllString(formatSet.Expression, -1)
 	token := re.FindAllString(formatSet.Expression, -1)
 	if len(token) != 3 && len(token) != 7 {
 	if len(token) != 3 && len(token) != 7 {
-		return fmt.Errorf("Incorrect number of tokens in criteria '%s'", formatSet.Expression)
+		return fmt.Errorf("incorrect number of tokens in criteria '%s'", formatSet.Expression)
 	}
 	}
 	expressions, tokens, err := f.parseFilterExpression(formatSet.Expression, token)
 	expressions, tokens, err := f.parseFilterExpression(formatSet.Expression, token)
 	if err != nil {
 	if err != nil {
@@ -301,8 +310,8 @@ func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *fo
 	return nil
 	return nil
 }
 }
 
 
-// writeAutoFilter provides function to check for single or double custom filters
-// as default filters and handle them accordingly.
+// writeAutoFilter provides a function to check for single or double custom
+// filters as default filters and handle them accordingly.
 func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []string) {
 func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []string) {
 	if len(exp) == 1 && exp[0] == 2 {
 	if len(exp) == 1 && exp[0] == 2 {
 		// Single equality.
 		// Single equality.
@@ -329,7 +338,7 @@ func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []strin
 	}
 	}
 }
 }
 
 
-// writeCustomFilter provides function to write the <customFilter> element.
+// writeCustomFilter provides a function to write the <customFilter> element.
 func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val string) {
 func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val string) {
 	operators := map[int]string{
 	operators := map[int]string{
 		1:  "lessThan",
 		1:  "lessThan",
@@ -353,8 +362,9 @@ func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val strin
 	}
 	}
 }
 }
 
 
-// parseFilterExpression provides function to converts the tokens of a possibly
-// conditional expression into 1 or 2 sub expressions for further parsing.
+// parseFilterExpression provides a function to converts the tokens of a
+// possibly conditional expression into 1 or 2 sub expressions for further
+// parsing.
 //
 //
 // Examples:
 // Examples:
 //
 //
@@ -394,7 +404,7 @@ func (f *File) parseFilterExpression(expression string, tokens []string) ([]int,
 	return expressions, t, nil
 	return expressions, t, nil
 }
 }
 
 
-// parseFilterTokens provides function to parse the 3 tokens of a filter
+// parseFilterTokens provides a function to parse the 3 tokens of a filter
 // expression and return the operator and token.
 // expression and return the operator and token.
 func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, string, error) {
 func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, string, error) {
 	operators := map[string]int{
 	operators := map[string]int{
@@ -414,7 +424,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
 	operator, ok := operators[strings.ToLower(tokens[1])]
 	operator, ok := operators[strings.ToLower(tokens[1])]
 	if !ok {
 	if !ok {
 		// Convert the operator from a number to a descriptive string.
 		// Convert the operator from a number to a descriptive string.
-		return []int{}, "", fmt.Errorf("Unknown operator: %s", tokens[1])
+		return []int{}, "", fmt.Errorf("unknown operator: %s", tokens[1])
 	}
 	}
 	token := tokens[2]
 	token := tokens[2]
 	// Special handling for Blanks/NonBlanks.
 	// Special handling for Blanks/NonBlanks.
@@ -422,7 +432,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
 	if re {
 	if re {
 		// Only allow Equals or NotEqual in this context.
 		// Only allow Equals or NotEqual in this context.
 		if operator != 2 && operator != 5 {
 		if operator != 2 && operator != 5 {
-			return []int{operator}, token, fmt.Errorf("The operator '%s' in expression '%s' is not valid in relation to Blanks/NonBlanks'", tokens[1], expression)
+			return []int{operator}, token, fmt.Errorf("the operator '%s' in expression '%s' is not valid in relation to Blanks/NonBlanks'", tokens[1], expression)
 		}
 		}
 		token = strings.ToLower(token)
 		token = strings.ToLower(token)
 		// The operator should always be 2 (=) to flag a "simple" equality in
 		// The operator should always be 2 (=) to flag a "simple" equality in

+ 9 - 0
templates.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+//
 // This file contains default templates for XML files we don't yet populated
 // This file contains default templates for XML files we don't yet populated
 // based on content.
 // based on content.
 
 

+ 9 - 0
vmlDrawing.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlChart.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 17 - 0
xmlComments.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"
@@ -53,3 +62,11 @@ type formatComment struct {
 	Author string `json:"author"`
 	Author string `json:"author"`
 	Text   string `json:"text"`
 	Text   string `json:"text"`
 }
 }
+
+// Comment directly maps the comment information.
+type Comment struct {
+	Author   string `json:"author"`
+	AuthorID int    `json:"author_id"`
+	Ref      string `json:"ref"`
+	Text     string `json:"text"`
+}

+ 9 - 0
xmlContentTypes.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlDecodeDrawing.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlDrawing.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlSharedStrings.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlStyles.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 9 - 0
xmlTable.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 149 - 0
xmlTheme.go

@@ -0,0 +1,149 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
+package excelize
+
+import "encoding/xml"
+
+// xlsxTheme directly maps the theme element in the namespace
+// http://schemas.openxmlformats.org/drawingml/2006/main
+type xlsxTheme struct {
+	ThemeElements     xlsxThemeElements     `xml:"themeElements"`
+	ObjectDefaults    xlsxObjectDefaults    `xml:"objectDefaults"`
+	ExtraClrSchemeLst xlsxExtraClrSchemeLst `xml:"extraClrSchemeLst"`
+	ExtLst            *xlsxExtLst           `xml:"extLst"`
+}
+
+// objectDefaults element allows for the definition of default shape, line,
+// and textbox formatting properties. An application can use this information
+// to format a shape (or text) initially on insertion into a document.
+type xlsxObjectDefaults struct {
+	ObjectDefaults string `xml:",innerxml"`
+}
+
+// xlsxExtraClrSchemeLst element is a container for the list of extra color
+// schemes present in a document.
+type xlsxExtraClrSchemeLst struct {
+	ExtraClrSchemeLst string `xml:",innerxml"`
+}
+
+// xlsxThemeElements directly maps the element defines the theme formatting
+// options for the theme and is the workhorse of the theme. This is where the
+// bulk of the shared theme information is contained and used by a document.
+// This element contains the color scheme, font scheme, and format scheme
+// elements which define the different formatting aspects of what a theme
+// defines.
+type xlsxThemeElements struct {
+	ClrScheme  xlsxClrScheme  `xml:"clrScheme"`
+	FontScheme xlsxFontScheme `xml:"fontScheme"`
+	FmtScheme  xlsxFmtScheme  `xml:"fmtScheme"`
+}
+
+// xlsxClrScheme element specifies the theme color, stored in the document's
+// Theme part to which the value of this theme color shall be mapped. This
+// mapping enables multiple theme colors to be chained together.
+type xlsxClrScheme struct {
+	Name     string            `xml:"name,attr"`
+	Children []xlsxClrSchemeEl `xml:",any"`
+}
+
+// xlsxFontScheme element defines the font scheme within the theme. The font
+// scheme consists of a pair of major and minor fonts for which to use in a
+// document. The major font corresponds well with the heading areas of a
+// document, and the minor font corresponds well with the normal text or
+// paragraph areas.
+type xlsxFontScheme struct {
+	Name      string        `xml:"name,attr"`
+	MajorFont xlsxMajorFont `xml:"majorFont"`
+	MinorFont xlsxMinorFont `xml:"minorFont"`
+	ExtLst    *xlsxExtLst   `xml:"extLst"`
+}
+
+// xlsxMajorFont element defines the set of major fonts which are to be used
+// under different languages or locals.
+type xlsxMajorFont struct {
+	Children []xlsxFontSchemeEl `xml:",any"`
+}
+
+// xlsxMinorFont element defines the set of minor fonts that are to be used
+// under different languages or locals.
+type xlsxMinorFont struct {
+	Children []xlsxFontSchemeEl `xml:",any"`
+}
+
+// xlsxFmtScheme element contains the background fill styles, effect styles,
+// fill styles, and line styles which define the style matrix for a theme. The
+// style matrix consists of subtle, moderate, and intense fills, lines, and
+// effects. The background fills are not generally thought of to directly be
+// associated with the matrix, but do play a role in the style of the overall
+// document. Usually, a given object chooses a single line style, a single
+// fill style, and a single effect style in order to define the overall final
+// look of the object.
+type xlsxFmtScheme struct {
+	Name           string             `xml:"name,attr"`
+	FillStyleLst   xlsxFillStyleLst   `xml:"fillStyleLst"`
+	LnStyleLst     xlsxLnStyleLst     `xml:"lnStyleLst"`
+	EffectStyleLst xlsxEffectStyleLst `xml:"effectStyleLst"`
+	BgFillStyleLst xlsxBgFillStyleLst `xml:"bgFillStyleLst"`
+}
+
+// xlsxFillStyleLst element defines a set of three fill styles that are used
+// within a theme. The three fill styles are arranged in order from subtle to
+// moderate to intense.
+type xlsxFillStyleLst struct {
+	FillStyleLst string `xml:",innerxml"`
+}
+
+// xlsxLnStyleLst element defines a list of three line styles for use within a
+// theme. The three line styles are arranged in order from subtle to moderate
+// to intense versions of lines. This list makes up part of the style matrix.
+type xlsxLnStyleLst struct {
+	LnStyleLst string `xml:",innerxml"`
+}
+
+// xlsxEffectStyleLst element defines a set of three effect styles that create
+// the effect style list for a theme. The effect styles are arranged in order
+// of subtle to moderate to intense.
+type xlsxEffectStyleLst struct {
+	EffectStyleLst string `xml:",innerxml"`
+}
+
+// xlsxBgFillStyleLst  element defines a list of background fills that are
+// used within a theme. The background fills consist of three fills, arranged
+// in order from subtle to moderate to intense.
+type xlsxBgFillStyleLst struct {
+	BgFillStyleLst string `xml:",innerxml"`
+}
+
+// xlsxClrScheme maps to children of the clrScheme element in the namespace
+// http://schemas.openxmlformats.org/drawingml/2006/main - currently I have
+// not checked it for completeness - it does as much as I need.
+type xlsxClrSchemeEl struct {
+	XMLName xml.Name
+	SysClr  *xlsxSysClr    `xml:"sysClr"`
+	SrgbClr *attrValString `xml:"srgbClr"`
+}
+
+// xlsxFontSchemeEl directly maps the major and minor font of the style's font
+// scheme.
+type xlsxFontSchemeEl struct {
+	XMLName     xml.Name
+	Script      string `xml:"script,attr,omitempty"`
+	Typeface    string `xml:"typeface,attr"`
+	Panose      string `xml:"panose,attr,omitempty"`
+	PitchFamily string `xml:"pitchFamily,attr,omitempty"`
+	Charset     string `xml:"charset,attr,omitempty"`
+}
+
+// xlsxSysClr element specifies a color bound to predefined operating system
+// elements.
+type xlsxSysClr struct {
+	Val     string `xml:"val,attr"`
+	LastClr string `xml:"lastClr,attr"`
+}

+ 9 - 0
xmlWorkbook.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"

+ 35 - 7
xmlWorksheet.go

@@ -1,3 +1,12 @@
+// Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+//
+// Package excelize providing a set of functions that allow you to write to
+// and read from XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excel™ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.8 or later.
+
 package excelize
 package excelize
 
 
 import "encoding/xml"
 import "encoding/xml"
@@ -18,7 +27,7 @@ type xlsxWorksheet struct {
 	MergeCells            *xlsxMergeCells              `xml:"mergeCells"`
 	MergeCells            *xlsxMergeCells              `xml:"mergeCells"`
 	PhoneticPr            *xlsxPhoneticPr              `xml:"phoneticPr"`
 	PhoneticPr            *xlsxPhoneticPr              `xml:"phoneticPr"`
 	ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"`
 	ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"`
-	DataValidations       *xlsxDataValidations         `xml:"dataValidations"`
+	DataValidations       *xlsxDataValidations         `xml:"dataValidations,omitempty"`
 	Hyperlinks            *xlsxHyperlinks              `xml:"hyperlinks"`
 	Hyperlinks            *xlsxHyperlinks              `xml:"hyperlinks"`
 	PrintOptions          *xlsxPrintOptions            `xml:"printOptions"`
 	PrintOptions          *xlsxPrintOptions            `xml:"printOptions"`
 	PageMargins           *xlsxPageMargins             `xml:"pageMargins"`
 	PageMargins           *xlsxPageMargins             `xml:"pageMargins"`
@@ -294,11 +303,30 @@ type xlsxMergeCells struct {
 // xlsxDataValidations expresses all data validation information for cells in a
 // xlsxDataValidations expresses all data validation information for cells in a
 // sheet which have data validation features applied.
 // sheet which have data validation features applied.
 type xlsxDataValidations struct {
 type xlsxDataValidations struct {
-	Count          int    `xml:"count,attr,omitempty"`
-	DisablePrompts bool   `xml:"disablePrompts,attr,omitempty"`
-	XWindow        int    `xml:"xWindow,attr,omitempty"`
-	YWindow        int    `xml:"yWindow,attr,omitempty"`
-	DataValidation string `xml:",innerxml"`
+	Count          int               `xml:"count,attr,omitempty"`
+	DisablePrompts bool              `xml:"disablePrompts,attr,omitempty"`
+	XWindow        int               `xml:"xWindow,attr,omitempty"`
+	YWindow        int               `xml:"yWindow,attr,omitempty"`
+	DataValidation []*DataValidation `xml:"dataValidation"`
+}
+
+// DataValidation directly maps the a single item of data validation defined
+// on a range of the worksheet.
+type DataValidation struct {
+	AllowBlank       bool    `xml:"allowBlank,attr"`
+	Error            *string `xml:"error,attr"`
+	ErrorStyle       *string `xml:"errorStyle,attr"`
+	ErrorTitle       *string `xml:"errorTitle,attr"`
+	Operator         string  `xml:"operator,attr"`
+	Prompt           *string `xml:"prompt,attr"`
+	PromptTitle      *string `xml:"promptTitle"`
+	ShowDropDown     bool    `xml:"showDropDown,attr"`
+	ShowErrorMessage bool    `xml:"showErrorMessage,attr"`
+	ShowInputMessage bool    `xml:"showInputMessage,attr"`
+	Sqref            string  `xml:"sqref,attr"`
+	Type             string  `xml:"type,attr"`
+	Formula1         string  `xml:"formula1"`
+	Formula2         string  `xml:"formula2"`
 }
 }
 
 
 // xlsxC directly maps the c element in the namespace
 // xlsxC directly maps the c element in the namespace
@@ -446,7 +474,7 @@ type xlsxIconSet struct {
 type xlsxCfvo struct {
 type xlsxCfvo struct {
 	Gte    bool        `xml:"gte,attr,omitempty"`
 	Gte    bool        `xml:"gte,attr,omitempty"`
 	Type   string      `xml:"type,attr,omitempty"`
 	Type   string      `xml:"type,attr,omitempty"`
-	Val    int         `xml:"val,attr"`
+	Val    string      `xml:"val,attr"`
 	ExtLst *xlsxExtLst `xml:"extLst"`
 	ExtLst *xlsxExtLst `xml:"extLst"`
 }
 }