excelize.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
  2. // this source code is governed by a BSD-style license that can be found in
  3. // the LICENSE file.
  4. // Package excelize providing a set of functions that allow you to write to
  5. // and read from XLSX files. Support reads and writes XLSX file generated by
  6. // Microsoft Excel™ 2007 and later. Support save file without losing original
  7. // charts of XLSX. This library needs Go version 1.8 or later.
  8. //
  9. // See https://xuri.me/excelize for more information about this package.
  10. package excelize
  11. import (
  12. "archive/zip"
  13. "bytes"
  14. "encoding/xml"
  15. "errors"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "os"
  20. "strconv"
  21. )
  22. // File define a populated XLSX file struct.
  23. type File struct {
  24. checked map[string]bool
  25. sheetMap map[string]string
  26. CalcChain *xlsxCalcChain
  27. Comments map[string]*xlsxComments
  28. ContentTypes *xlsxTypes
  29. DrawingRels map[string]*xlsxWorkbookRels
  30. Drawings map[string]*xlsxWsDr
  31. Path string
  32. SharedStrings *xlsxSST
  33. Sheet map[string]*xlsxWorksheet
  34. SheetCount int
  35. Styles *xlsxStyleSheet
  36. Theme *xlsxTheme
  37. DecodeVMLDrawing map[string]*decodeVmlDrawing
  38. VMLDrawing map[string]*vmlDrawing
  39. WorkBook *xlsxWorkbook
  40. WorkBookRels *xlsxWorkbookRels
  41. WorkSheetRels map[string]*xlsxWorkbookRels
  42. XLSX map[string][]byte
  43. }
  44. // OpenFile take the name of an XLSX file and returns a populated XLSX file
  45. // struct for it.
  46. func OpenFile(filename string) (*File, error) {
  47. file, err := os.Open(filename)
  48. if err != nil {
  49. return nil, err
  50. }
  51. defer file.Close()
  52. f, err := OpenReader(file)
  53. if err != nil {
  54. return nil, err
  55. }
  56. f.Path = filename
  57. return f, nil
  58. }
  59. // OpenReader take an io.Reader and return a populated XLSX file.
  60. func OpenReader(r io.Reader) (*File, error) {
  61. b, err := ioutil.ReadAll(r)
  62. if err != nil {
  63. return nil, err
  64. }
  65. zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
  66. if err != nil {
  67. identifier := []byte{
  68. // checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier
  69. 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00,
  70. 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00,
  71. 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00,
  72. 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00,
  73. 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  74. }
  75. if bytes.Contains(b, identifier) {
  76. return nil, errors.New("not support encrypted file currently")
  77. }
  78. return nil, err
  79. }
  80. file, sheetCount, err := ReadZipReader(zr)
  81. if err != nil {
  82. return nil, err
  83. }
  84. f := &File{
  85. checked: make(map[string]bool),
  86. Comments: make(map[string]*xlsxComments),
  87. DrawingRels: make(map[string]*xlsxWorkbookRels),
  88. Drawings: make(map[string]*xlsxWsDr),
  89. Sheet: make(map[string]*xlsxWorksheet),
  90. SheetCount: sheetCount,
  91. DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
  92. VMLDrawing: make(map[string]*vmlDrawing),
  93. WorkSheetRels: make(map[string]*xlsxWorkbookRels),
  94. XLSX: file,
  95. }
  96. f.CalcChain = f.calcChainReader()
  97. f.sheetMap = f.getSheetMap()
  98. f.Styles = f.stylesReader()
  99. f.Theme = f.themeReader()
  100. return f, nil
  101. }
  102. // setDefaultTimeStyle provides a function to set default numbers format for
  103. // time.Time type cell value by given worksheet name, cell coordinates and
  104. // number format code.
  105. func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
  106. s, err := f.GetCellStyle(sheet, axis)
  107. if err != nil {
  108. return err
  109. }
  110. if s == 0 {
  111. style, _ := f.NewStyle(`{"number_format": ` + strconv.Itoa(format) + `}`)
  112. f.SetCellStyle(sheet, axis, axis, style)
  113. }
  114. return err
  115. }
  116. // workSheetReader provides a function to get the pointer to the structure
  117. // after deserialization by given worksheet name.
  118. func (f *File) workSheetReader(sheet string) (*xlsxWorksheet, error) {
  119. name, ok := f.sheetMap[trimSheetName(sheet)]
  120. if !ok {
  121. return nil, fmt.Errorf("sheet %s is not exist", sheet)
  122. }
  123. if f.Sheet[name] == nil {
  124. var xlsx xlsxWorksheet
  125. _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(name)), &xlsx)
  126. if f.checked == nil {
  127. f.checked = make(map[string]bool)
  128. }
  129. ok := f.checked[name]
  130. if !ok {
  131. checkSheet(&xlsx)
  132. checkRow(&xlsx)
  133. f.checked[name] = true
  134. }
  135. f.Sheet[name] = &xlsx
  136. }
  137. return f.Sheet[name], nil
  138. }
  139. // checkSheet provides a function to fill each row element and make that is
  140. // continuous in a worksheet of XML.
  141. func checkSheet(xlsx *xlsxWorksheet) {
  142. row := len(xlsx.SheetData.Row)
  143. if row >= 1 {
  144. lastRow := xlsx.SheetData.Row[row-1].R
  145. if lastRow >= row {
  146. row = lastRow
  147. }
  148. }
  149. sheetData := xlsxSheetData{}
  150. existsRows := map[int]int{}
  151. for k := range xlsx.SheetData.Row {
  152. existsRows[xlsx.SheetData.Row[k].R] = k
  153. }
  154. for i := 0; i < row; i++ {
  155. _, ok := existsRows[i+1]
  156. if ok {
  157. sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
  158. } else {
  159. sheetData.Row = append(sheetData.Row, xlsxRow{
  160. R: i + 1,
  161. })
  162. }
  163. }
  164. xlsx.SheetData = sheetData
  165. }
  166. // replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
  167. // xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
  168. // Office Excel 2007.
  169. func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
  170. var oldXmlns = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
  171. var newXmlns = []byte(`<worksheet xr:uid="{00000000-0001-0000-0000-000000000000}" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac xr xr2 xr3" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
  172. workbookMarshal = bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
  173. return workbookMarshal
  174. }
  175. // UpdateLinkedValue fix linked values within a spreadsheet are not updating in
  176. // Office Excel 2007 and 2010. This function will be remove value tag when met a
  177. // cell have a linked value. Reference
  178. // https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating
  179. //
  180. // Notice: after open XLSX file Excel will be update linked value and generate
  181. // new value and will prompt save file or not.
  182. //
  183. // For example:
  184. //
  185. // <row r="19" spans="2:2">
  186. // <c r="B19">
  187. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  188. // <v>100</v>
  189. // </c>
  190. // </row>
  191. //
  192. // to
  193. //
  194. // <row r="19" spans="2:2">
  195. // <c r="B19">
  196. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  197. // </c>
  198. // </row>
  199. //
  200. func (f *File) UpdateLinkedValue() error {
  201. for _, name := range f.GetSheetMap() {
  202. xlsx, err := f.workSheetReader(name)
  203. if err != nil {
  204. return err
  205. }
  206. for indexR := range xlsx.SheetData.Row {
  207. for indexC, col := range xlsx.SheetData.Row[indexR].C {
  208. if col.F != nil && col.V != "" {
  209. xlsx.SheetData.Row[indexR].C[indexC].V = ""
  210. xlsx.SheetData.Row[indexR].C[indexC].T = ""
  211. }
  212. }
  213. }
  214. }
  215. return nil
  216. }