excelize.go 11 KB

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