excelize.go 11 KB

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