excelize.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. // Copyright 2016 - 2020 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 / XLSM / XLTM files. Supports reading and writing
  6. // spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  7. // complex components by high compatibility, and provided streaming API for
  8. // generating or reading data from a worksheet with huge amounts of data. This
  9. // library needs Go version 1.10 or later.
  10. //
  11. // See https://xuri.me/excelize for more information about this package.
  12. package excelize
  13. import (
  14. "archive/zip"
  15. "bytes"
  16. "encoding/xml"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "os"
  22. "path"
  23. "strconv"
  24. "strings"
  25. "golang.org/x/net/html/charset"
  26. )
  27. // File define a populated XLSX file struct.
  28. type File struct {
  29. checked map[string]bool
  30. sheetMap map[string]string
  31. CalcChain *xlsxCalcChain
  32. Comments map[string]*xlsxComments
  33. ContentTypes *xlsxTypes
  34. Drawings map[string]*xlsxWsDr
  35. Path string
  36. SharedStrings *xlsxSST
  37. Sheet map[string]*xlsxWorksheet
  38. SheetCount int
  39. Styles *xlsxStyleSheet
  40. Theme *xlsxTheme
  41. DecodeVMLDrawing map[string]*decodeVmlDrawing
  42. VMLDrawing map[string]*vmlDrawing
  43. WorkBook *xlsxWorkbook
  44. Relationships map[string]*xlsxRelationships
  45. XLSX map[string][]byte
  46. CharsetReader charsetTranscoderFn
  47. }
  48. type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
  49. // OpenFile take the name of an XLSX file and returns a populated XLSX file
  50. // struct for it.
  51. func OpenFile(filename string) (*File, error) {
  52. file, err := os.Open(filename)
  53. if err != nil {
  54. return nil, err
  55. }
  56. defer file.Close()
  57. f, err := OpenReader(file)
  58. if err != nil {
  59. return nil, err
  60. }
  61. f.Path = filename
  62. return f, nil
  63. }
  64. // newFile is object builder
  65. func newFile() *File {
  66. return &File{
  67. checked: make(map[string]bool),
  68. sheetMap: make(map[string]string),
  69. Comments: make(map[string]*xlsxComments),
  70. Drawings: make(map[string]*xlsxWsDr),
  71. Sheet: make(map[string]*xlsxWorksheet),
  72. DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
  73. VMLDrawing: make(map[string]*vmlDrawing),
  74. Relationships: make(map[string]*xlsxRelationships),
  75. CharsetReader: charset.NewReaderLabel,
  76. }
  77. }
  78. // OpenReader take an io.Reader and return a populated XLSX file.
  79. func OpenReader(r io.Reader) (*File, error) {
  80. b, err := ioutil.ReadAll(r)
  81. if err != nil {
  82. return nil, err
  83. }
  84. zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
  85. if err != nil {
  86. identifier := []byte{
  87. // checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier
  88. 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00,
  89. 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00,
  90. 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00,
  91. 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00,
  92. 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  93. }
  94. if bytes.Contains(b, identifier) {
  95. return nil, errors.New("not support encrypted file currently")
  96. }
  97. return nil, err
  98. }
  99. file, sheetCount, err := ReadZipReader(zr)
  100. if err != nil {
  101. return nil, err
  102. }
  103. f := newFile()
  104. f.SheetCount, f.XLSX = sheetCount, file
  105. f.CalcChain = f.calcChainReader()
  106. f.sheetMap = f.getSheetMap()
  107. f.Styles = f.stylesReader()
  108. f.Theme = f.themeReader()
  109. return f, nil
  110. }
  111. // CharsetTranscoder Set user defined codepage transcoder function for open
  112. // XLSX from non UTF-8 encoding.
  113. func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f }
  114. // Creates new XML decoder with charset reader.
  115. func (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) {
  116. ret = xml.NewDecoder(rdr)
  117. ret.CharsetReader = f.CharsetReader
  118. return
  119. }
  120. // setDefaultTimeStyle provides a function to set default numbers format for
  121. // time.Time type cell value by given worksheet name, cell coordinates and
  122. // number format code.
  123. func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
  124. s, err := f.GetCellStyle(sheet, axis)
  125. if err != nil {
  126. return err
  127. }
  128. if s == 0 {
  129. style, _ := f.NewStyle(&Style{NumFmt: format})
  130. _ = f.SetCellStyle(sheet, axis, axis, style)
  131. }
  132. return err
  133. }
  134. // workSheetReader provides a function to get the pointer to the structure
  135. // after deserialization by given worksheet name.
  136. func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) {
  137. var (
  138. name string
  139. ok bool
  140. )
  141. if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok {
  142. err = fmt.Errorf("sheet %s is not exist", sheet)
  143. return
  144. }
  145. if xlsx = f.Sheet[name]; f.Sheet[name] == nil {
  146. if strings.HasPrefix(name, "xl/chartsheets") {
  147. err = fmt.Errorf("sheet %s is chart sheet", sheet)
  148. return
  149. }
  150. xlsx = new(xlsxWorksheet)
  151. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
  152. Decode(xlsx); err != nil && err != io.EOF {
  153. err = fmt.Errorf("xml decode error: %s", err)
  154. return
  155. }
  156. err = nil
  157. if f.checked == nil {
  158. f.checked = make(map[string]bool)
  159. }
  160. if ok = f.checked[name]; !ok {
  161. checkSheet(xlsx)
  162. if err = checkRow(xlsx); err != nil {
  163. return
  164. }
  165. f.checked[name] = true
  166. }
  167. f.Sheet[name] = xlsx
  168. }
  169. return
  170. }
  171. // checkSheet provides a function to fill each row element and make that is
  172. // continuous in a worksheet of XML.
  173. func checkSheet(xlsx *xlsxWorksheet) {
  174. row := len(xlsx.SheetData.Row)
  175. if row >= 1 {
  176. lastRow := xlsx.SheetData.Row[row-1].R
  177. if lastRow >= row {
  178. row = lastRow
  179. }
  180. }
  181. sheetData := xlsxSheetData{Row: make([]xlsxRow, row)}
  182. for _, r := range xlsx.SheetData.Row {
  183. sheetData.Row[r.R-1] = r
  184. }
  185. for i := 1; i <= row; i++ {
  186. sheetData.Row[i-1].R = i
  187. }
  188. xlsx.SheetData = sheetData
  189. }
  190. // addRels provides a function to add relationships by given XML path,
  191. // relationship type, target and target mode.
  192. func (f *File) addRels(relPath, relType, target, targetMode string) int {
  193. rels := f.relsReader(relPath)
  194. if rels == nil {
  195. rels = &xlsxRelationships{}
  196. }
  197. var rID int
  198. for _, rel := range rels.Relationships {
  199. ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
  200. if ID > rID {
  201. rID = ID
  202. }
  203. }
  204. rID++
  205. var ID bytes.Buffer
  206. ID.WriteString("rId")
  207. ID.WriteString(strconv.Itoa(rID))
  208. rels.Relationships = append(rels.Relationships, xlsxRelationship{
  209. ID: ID.String(),
  210. Type: relType,
  211. Target: target,
  212. TargetMode: targetMode,
  213. })
  214. f.Relationships[relPath] = rels
  215. return rID
  216. }
  217. // replaceRelationshipsNameSpaceBytes provides a function to replace
  218. // XML tags to self-closing for compatible Microsoft Office Excel 2007.
  219. func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
  220. var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
  221. var newXmlns = []byte(templateNamespaceIDMap)
  222. return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
  223. }
  224. // UpdateLinkedValue fix linked values within a spreadsheet are not updating in
  225. // Office Excel 2007 and 2010. This function will be remove value tag when met a
  226. // cell have a linked value. Reference
  227. // https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating
  228. //
  229. // Notice: after open XLSX file Excel will be update linked value and generate
  230. // new value and will prompt save file or not.
  231. //
  232. // For example:
  233. //
  234. // <row r="19" spans="2:2">
  235. // <c r="B19">
  236. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  237. // <v>100</v>
  238. // </c>
  239. // </row>
  240. //
  241. // to
  242. //
  243. // <row r="19" spans="2:2">
  244. // <c r="B19">
  245. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  246. // </c>
  247. // </row>
  248. //
  249. func (f *File) UpdateLinkedValue() error {
  250. wb := f.workbookReader()
  251. // recalculate formulas
  252. wb.CalcPr = nil
  253. for _, name := range f.GetSheetList() {
  254. xlsx, err := f.workSheetReader(name)
  255. if err != nil {
  256. return err
  257. }
  258. for indexR := range xlsx.SheetData.Row {
  259. for indexC, col := range xlsx.SheetData.Row[indexR].C {
  260. if col.F != nil && col.V != "" {
  261. xlsx.SheetData.Row[indexR].C[indexC].V = ""
  262. xlsx.SheetData.Row[indexR].C[indexC].T = ""
  263. }
  264. }
  265. }
  266. }
  267. return nil
  268. }
  269. // AddVBAProject provides the method to add vbaProject.bin file which contains
  270. // functions and/or macros. The file extension should be .xlsm. For example:
  271. //
  272. // if err := f.SetSheetPrOptions("Sheet1", excelize.CodeName("Sheet1")); err != nil {
  273. // fmt.Println(err)
  274. // }
  275. // if err := f.AddVBAProject("vbaProject.bin"); err != nil {
  276. // fmt.Println(err)
  277. // }
  278. // if err := f.SaveAs("macros.xlsm"); err != nil {
  279. // fmt.Println(err)
  280. // }
  281. //
  282. func (f *File) AddVBAProject(bin string) error {
  283. var err error
  284. // Check vbaProject.bin exists first.
  285. if _, err = os.Stat(bin); os.IsNotExist(err) {
  286. return err
  287. }
  288. if path.Ext(bin) != ".bin" {
  289. return errors.New("unsupported VBA project extension")
  290. }
  291. f.setContentTypePartVBAProjectExtensions()
  292. wb := f.relsReader("xl/_rels/workbook.xml.rels")
  293. var rID int
  294. var ok bool
  295. for _, rel := range wb.Relationships {
  296. if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
  297. ok = true
  298. continue
  299. }
  300. t, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
  301. if t > rID {
  302. rID = t
  303. }
  304. }
  305. rID++
  306. if !ok {
  307. wb.Relationships = append(wb.Relationships, xlsxRelationship{
  308. ID: "rId" + strconv.Itoa(rID),
  309. Target: "vbaProject.bin",
  310. Type: SourceRelationshipVBAProject,
  311. })
  312. }
  313. file, _ := ioutil.ReadFile(bin)
  314. f.XLSX["xl/vbaProject.bin"] = file
  315. return err
  316. }
  317. // setContentTypePartVBAProjectExtensions provides a function to set the
  318. // content type for relationship parts and the main document part.
  319. func (f *File) setContentTypePartVBAProjectExtensions() {
  320. var ok bool
  321. content := f.contentTypesReader()
  322. for _, v := range content.Defaults {
  323. if v.Extension == "bin" {
  324. ok = true
  325. }
  326. }
  327. for idx, o := range content.Overrides {
  328. if o.PartName == "/xl/workbook.xml" {
  329. content.Overrides[idx].ContentType = ContentTypeMacro
  330. }
  331. }
  332. if !ok {
  333. content.Defaults = append(content.Defaults, xlsxDefault{
  334. Extension: "bin",
  335. ContentType: ContentTypeVBA,
  336. })
  337. }
  338. }