excelize.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // Copyright 2016 - 2023 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 and
  5. // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
  6. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
  7. // Supports complex components by high compatibility, and provided streaming
  8. // API for generating or reading data from a worksheet with huge amounts of
  9. // data. This library needs Go version 1.16 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. "io"
  18. "os"
  19. "path/filepath"
  20. "strconv"
  21. "strings"
  22. "sync"
  23. "golang.org/x/net/html/charset"
  24. )
  25. // File define a populated spreadsheet file struct.
  26. type File struct {
  27. sync.Mutex
  28. options *Options
  29. xmlAttr map[string][]xml.Attr
  30. checked map[string]bool
  31. sheetMap map[string]string
  32. streams map[string]*StreamWriter
  33. tempFiles sync.Map
  34. sharedStringsMap map[string]int
  35. sharedStringItem [][]uint
  36. sharedStringTemp *os.File
  37. CalcChain *xlsxCalcChain
  38. Comments map[string]*xlsxComments
  39. ContentTypes *xlsxTypes
  40. Drawings sync.Map
  41. Path string
  42. SharedStrings *xlsxSST
  43. Sheet sync.Map
  44. SheetCount int
  45. Styles *xlsxStyleSheet
  46. Theme *xlsxTheme
  47. DecodeVMLDrawing map[string]*decodeVmlDrawing
  48. VMLDrawing map[string]*vmlDrawing
  49. WorkBook *xlsxWorkbook
  50. Relationships sync.Map
  51. Pkg sync.Map
  52. CharsetReader charsetTranscoderFn
  53. }
  54. // charsetTranscoderFn set user-defined codepage transcoder function for open
  55. // the spreadsheet from non-UTF-8 encoding.
  56. type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
  57. // Options define the options for open and reading spreadsheet.
  58. //
  59. // MaxCalcIterations specifies the maximum iterations for iterative
  60. // calculation, the default value is 0.
  61. //
  62. // Password specifies the password of the spreadsheet in plain text.
  63. //
  64. // RawCellValue specifies if apply the number format for the cell value or get
  65. // the raw value.
  66. //
  67. // UnzipSizeLimit specifies the unzip size limit in bytes on open the
  68. // spreadsheet, this value should be greater than or equal to
  69. // UnzipXMLSizeLimit, the default size limit is 16GB.
  70. //
  71. // UnzipXMLSizeLimit specifies the memory limit on unzipping worksheet and
  72. // shared string table in bytes, worksheet XML will be extracted to system
  73. // temporary directory when the file size is over this value, this value
  74. // should be less than or equal to UnzipSizeLimit, the default value is
  75. // 16MB.
  76. type Options struct {
  77. MaxCalcIterations uint
  78. Password string
  79. RawCellValue bool
  80. UnzipSizeLimit int64
  81. UnzipXMLSizeLimit int64
  82. }
  83. // OpenFile take the name of an spreadsheet file and returns a populated
  84. // spreadsheet file struct for it. For example, open spreadsheet with
  85. // password protection:
  86. //
  87. // f, err := excelize.OpenFile("Book1.xlsx", excelize.Options{Password: "password"})
  88. //
  89. // Close the file by Close function after opening the spreadsheet.
  90. func OpenFile(filename string, opts ...Options) (*File, error) {
  91. file, err := os.Open(filepath.Clean(filename))
  92. if err != nil {
  93. return nil, err
  94. }
  95. f, err := OpenReader(file, opts...)
  96. if err != nil {
  97. closeErr := file.Close()
  98. if closeErr == nil {
  99. return f, err
  100. }
  101. return f, closeErr
  102. }
  103. f.Path = filename
  104. return f, file.Close()
  105. }
  106. // newFile is object builder
  107. func newFile() *File {
  108. return &File{
  109. options: &Options{UnzipSizeLimit: UnzipSizeLimit, UnzipXMLSizeLimit: StreamChunkSize},
  110. xmlAttr: make(map[string][]xml.Attr),
  111. checked: make(map[string]bool),
  112. sheetMap: make(map[string]string),
  113. tempFiles: sync.Map{},
  114. Comments: make(map[string]*xlsxComments),
  115. Drawings: sync.Map{},
  116. sharedStringsMap: make(map[string]int),
  117. Sheet: sync.Map{},
  118. DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
  119. VMLDrawing: make(map[string]*vmlDrawing),
  120. Relationships: sync.Map{},
  121. CharsetReader: charset.NewReaderLabel,
  122. }
  123. }
  124. // checkOpenReaderOptions check and validate options field value for open
  125. // reader.
  126. func (f *File) checkOpenReaderOptions() error {
  127. if f.options.UnzipSizeLimit == 0 {
  128. f.options.UnzipSizeLimit = UnzipSizeLimit
  129. if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
  130. f.options.UnzipSizeLimit = f.options.UnzipXMLSizeLimit
  131. }
  132. }
  133. if f.options.UnzipXMLSizeLimit == 0 {
  134. f.options.UnzipXMLSizeLimit = StreamChunkSize
  135. if f.options.UnzipSizeLimit < f.options.UnzipXMLSizeLimit {
  136. f.options.UnzipXMLSizeLimit = f.options.UnzipSizeLimit
  137. }
  138. }
  139. if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
  140. return ErrOptionsUnzipSizeLimit
  141. }
  142. return nil
  143. }
  144. // OpenReader read data stream from io.Reader and return a populated
  145. // spreadsheet file.
  146. func OpenReader(r io.Reader, opts ...Options) (*File, error) {
  147. b, err := io.ReadAll(r)
  148. if err != nil {
  149. return nil, err
  150. }
  151. f := newFile()
  152. f.options = getOptions(opts...)
  153. if err = f.checkOpenReaderOptions(); err != nil {
  154. return nil, err
  155. }
  156. if bytes.Contains(b, oleIdentifier) {
  157. if b, err = Decrypt(b, f.options); err != nil {
  158. return nil, ErrWorkbookFileFormat
  159. }
  160. }
  161. zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
  162. if err != nil {
  163. if len(f.options.Password) > 0 {
  164. return nil, ErrWorkbookPassword
  165. }
  166. return nil, err
  167. }
  168. file, sheetCount, err := f.ReadZipReader(zr)
  169. if err != nil {
  170. return nil, err
  171. }
  172. f.SheetCount = sheetCount
  173. for k, v := range file {
  174. f.Pkg.Store(k, v)
  175. }
  176. if f.CalcChain, err = f.calcChainReader(); err != nil {
  177. return f, err
  178. }
  179. if f.sheetMap, err = f.getSheetMap(); err != nil {
  180. return f, err
  181. }
  182. if f.Styles, err = f.stylesReader(); err != nil {
  183. return f, err
  184. }
  185. f.Theme, err = f.themeReader()
  186. return f, err
  187. }
  188. // getOptions provides a function to parse the optional settings for open
  189. // and reading spreadsheet.
  190. func getOptions(opts ...Options) *Options {
  191. options := &Options{}
  192. for _, opt := range opts {
  193. options = &opt
  194. }
  195. return options
  196. }
  197. // CharsetTranscoder Set user defined codepage transcoder function for open
  198. // XLSX from non UTF-8 encoding.
  199. func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f }
  200. // Creates new XML decoder with charset reader.
  201. func (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) {
  202. ret = xml.NewDecoder(rdr)
  203. ret.CharsetReader = f.CharsetReader
  204. return
  205. }
  206. // setDefaultTimeStyle provides a function to set default numbers format for
  207. // time.Time type cell value by given worksheet name, cell reference and
  208. // number format code.
  209. func (f *File) setDefaultTimeStyle(sheet, cell string, format int) error {
  210. s, err := f.GetCellStyle(sheet, cell)
  211. if err != nil {
  212. return err
  213. }
  214. if s == 0 {
  215. style, _ := f.NewStyle(&Style{NumFmt: format})
  216. err = f.SetCellStyle(sheet, cell, cell, style)
  217. }
  218. return err
  219. }
  220. // workSheetReader provides a function to get the pointer to the structure
  221. // after deserialization by given worksheet name.
  222. func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
  223. f.Lock()
  224. defer f.Unlock()
  225. var (
  226. name string
  227. ok bool
  228. )
  229. if err = checkSheetName(sheet); err != nil {
  230. return
  231. }
  232. if name, ok = f.getSheetXMLPath(sheet); !ok {
  233. err = newNoExistSheetError(sheet)
  234. return
  235. }
  236. if worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {
  237. ws = worksheet.(*xlsxWorksheet)
  238. return
  239. }
  240. for _, sheetType := range []string{"xl/chartsheets", "xl/dialogsheet", "xl/macrosheet"} {
  241. if strings.HasPrefix(name, sheetType) {
  242. err = newNotWorksheetError(sheet)
  243. return
  244. }
  245. }
  246. ws = new(xlsxWorksheet)
  247. if _, ok := f.xmlAttr[name]; !ok {
  248. d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name))))
  249. f.xmlAttr[name] = append(f.xmlAttr[name], getRootElement(d)...)
  250. }
  251. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name)))).
  252. Decode(ws); err != nil && err != io.EOF {
  253. return
  254. }
  255. err = nil
  256. if f.checked == nil {
  257. f.checked = make(map[string]bool)
  258. }
  259. if ok = f.checked[name]; !ok {
  260. checkSheet(ws)
  261. if err = checkRow(ws); err != nil {
  262. return
  263. }
  264. f.checked[name] = true
  265. }
  266. f.Sheet.Store(name, ws)
  267. return
  268. }
  269. // checkSheet provides a function to fill each row element and make that is
  270. // continuous in a worksheet of XML.
  271. func checkSheet(ws *xlsxWorksheet) {
  272. var row int
  273. var r0 xlsxRow
  274. for i, r := range ws.SheetData.Row {
  275. if i == 0 && r.R == 0 {
  276. r0 = r
  277. ws.SheetData.Row = ws.SheetData.Row[1:]
  278. continue
  279. }
  280. if r.R != 0 && r.R > row {
  281. row = r.R
  282. continue
  283. }
  284. if r.R != row {
  285. row++
  286. }
  287. }
  288. sheetData := xlsxSheetData{Row: make([]xlsxRow, row)}
  289. row = 0
  290. for _, r := range ws.SheetData.Row {
  291. if r.R == row && row > 0 {
  292. sheetData.Row[r.R-1].C = append(sheetData.Row[r.R-1].C, r.C...)
  293. continue
  294. }
  295. if r.R != 0 {
  296. sheetData.Row[r.R-1] = r
  297. row = r.R
  298. continue
  299. }
  300. row++
  301. r.R = row
  302. sheetData.Row[row-1] = r
  303. }
  304. for i := 1; i <= row; i++ {
  305. sheetData.Row[i-1].R = i
  306. }
  307. checkSheetR0(ws, &sheetData, &r0)
  308. }
  309. // checkSheetR0 handle the row element with r="0" attribute, cells in this row
  310. // could be disorderly, the cell in this row can be used as the value of
  311. // which cell is empty in the normal rows.
  312. func checkSheetR0(ws *xlsxWorksheet, sheetData *xlsxSheetData, r0 *xlsxRow) {
  313. for _, cell := range r0.C {
  314. if col, row, err := CellNameToCoordinates(cell.R); err == nil {
  315. rows, rowIdx := len(sheetData.Row), row-1
  316. for r := rows; r < row; r++ {
  317. sheetData.Row = append(sheetData.Row, xlsxRow{R: r + 1})
  318. }
  319. columns, colIdx := len(sheetData.Row[rowIdx].C), col-1
  320. for c := columns; c < col; c++ {
  321. sheetData.Row[rowIdx].C = append(sheetData.Row[rowIdx].C, xlsxC{})
  322. }
  323. if !sheetData.Row[rowIdx].C[colIdx].hasValue() {
  324. sheetData.Row[rowIdx].C[colIdx] = cell
  325. }
  326. }
  327. }
  328. ws.SheetData = *sheetData
  329. }
  330. // setRels provides a function to set relationships by given relationship ID,
  331. // XML path, relationship type, target and target mode.
  332. func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
  333. rels, _ := f.relsReader(relPath)
  334. if rels == nil || rID == "" {
  335. return f.addRels(relPath, relType, target, targetMode)
  336. }
  337. rels.Lock()
  338. defer rels.Unlock()
  339. var ID int
  340. for i, rel := range rels.Relationships {
  341. if rel.ID == rID {
  342. rels.Relationships[i].Type = relType
  343. rels.Relationships[i].Target = target
  344. rels.Relationships[i].TargetMode = targetMode
  345. ID, _ = strconv.Atoi(strings.TrimPrefix(rID, "rId"))
  346. break
  347. }
  348. }
  349. return ID
  350. }
  351. // addRels provides a function to add relationships by given XML path,
  352. // relationship type, target and target mode.
  353. func (f *File) addRels(relPath, relType, target, targetMode string) int {
  354. uniqPart := map[string]string{
  355. SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
  356. }
  357. rels, _ := f.relsReader(relPath)
  358. if rels == nil {
  359. rels = &xlsxRelationships{}
  360. }
  361. rels.Lock()
  362. defer rels.Unlock()
  363. var rID int
  364. for idx, rel := range rels.Relationships {
  365. ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
  366. if ID > rID {
  367. rID = ID
  368. }
  369. if relType == rel.Type {
  370. if partName, ok := uniqPart[rel.Type]; ok {
  371. rels.Relationships[idx].Target = partName
  372. return rID
  373. }
  374. }
  375. }
  376. rID++
  377. var ID bytes.Buffer
  378. ID.WriteString("rId")
  379. ID.WriteString(strconv.Itoa(rID))
  380. rels.Relationships = append(rels.Relationships, xlsxRelationship{
  381. ID: ID.String(),
  382. Type: relType,
  383. Target: target,
  384. TargetMode: targetMode,
  385. })
  386. f.Relationships.Store(relPath, rels)
  387. return rID
  388. }
  389. // UpdateLinkedValue fix linked values within a spreadsheet are not updating in
  390. // Office Excel application. This function will be remove value tag when met a
  391. // cell have a linked value. Reference
  392. // https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating
  393. //
  394. // Notice: after opening generated workbook, Excel will update the linked value
  395. // and generate a new value and will prompt to save the file or not.
  396. //
  397. // For example:
  398. //
  399. // <row r="19" spans="2:2">
  400. // <c r="B19">
  401. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  402. // <v>100</v>
  403. // </c>
  404. // </row>
  405. //
  406. // to
  407. //
  408. // <row r="19" spans="2:2">
  409. // <c r="B19">
  410. // <f>SUM(Sheet2!D2,Sheet2!D11)</f>
  411. // </c>
  412. // </row>
  413. func (f *File) UpdateLinkedValue() error {
  414. wb, err := f.workbookReader()
  415. if err != nil {
  416. return err
  417. }
  418. // recalculate formulas
  419. wb.CalcPr = nil
  420. for _, name := range f.GetSheetList() {
  421. ws, err := f.workSheetReader(name)
  422. if err != nil {
  423. if err.Error() == newNotWorksheetError(name).Error() {
  424. continue
  425. }
  426. return err
  427. }
  428. for indexR := range ws.SheetData.Row {
  429. for indexC, col := range ws.SheetData.Row[indexR].C {
  430. if col.F != nil && col.V != "" {
  431. ws.SheetData.Row[indexR].C[indexC].V = ""
  432. ws.SheetData.Row[indexR].C[indexC].T = ""
  433. }
  434. }
  435. }
  436. }
  437. return nil
  438. }
  439. // AddVBAProject provides the method to add vbaProject.bin file which contains
  440. // functions and/or macros. The file extension should be XLSM or XLTM. For
  441. // example:
  442. //
  443. // codeName := "Sheet1"
  444. // if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
  445. // CodeName: &codeName,
  446. // }); err != nil {
  447. // fmt.Println(err)
  448. // return
  449. // }
  450. // file, err := os.ReadFile("vbaProject.bin")
  451. // if err != nil {
  452. // fmt.Println(err)
  453. // return
  454. // }
  455. // if err := f.AddVBAProject(file); err != nil {
  456. // fmt.Println(err)
  457. // return
  458. // }
  459. // if err := f.SaveAs("macros.xlsm"); err != nil {
  460. // fmt.Println(err)
  461. // return
  462. // }
  463. func (f *File) AddVBAProject(file []byte) error {
  464. var err error
  465. // Check vbaProject.bin exists first.
  466. if !bytes.Contains(file, oleIdentifier) {
  467. return ErrAddVBAProject
  468. }
  469. rels, err := f.relsReader(f.getWorkbookRelsPath())
  470. if err != nil {
  471. return err
  472. }
  473. rels.Lock()
  474. defer rels.Unlock()
  475. var rID int
  476. var ok bool
  477. for _, rel := range rels.Relationships {
  478. if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
  479. ok = true
  480. continue
  481. }
  482. t, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
  483. if t > rID {
  484. rID = t
  485. }
  486. }
  487. rID++
  488. if !ok {
  489. rels.Relationships = append(rels.Relationships, xlsxRelationship{
  490. ID: "rId" + strconv.Itoa(rID),
  491. Target: "vbaProject.bin",
  492. Type: SourceRelationshipVBAProject,
  493. })
  494. }
  495. f.Pkg.Store("xl/vbaProject.bin", file)
  496. return err
  497. }
  498. // setContentTypePartProjectExtensions provides a function to set the content
  499. // type for relationship parts and the main document part.
  500. func (f *File) setContentTypePartProjectExtensions(contentType string) error {
  501. var ok bool
  502. content, err := f.contentTypesReader()
  503. if err != nil {
  504. return err
  505. }
  506. content.Lock()
  507. defer content.Unlock()
  508. for _, v := range content.Defaults {
  509. if v.Extension == "bin" {
  510. ok = true
  511. }
  512. }
  513. for idx, o := range content.Overrides {
  514. if o.PartName == "/xl/workbook.xml" {
  515. content.Overrides[idx].ContentType = contentType
  516. }
  517. }
  518. if !ok {
  519. content.Defaults = append(content.Defaults, xlsxDefault{
  520. Extension: "bin",
  521. ContentType: ContentTypeVBA,
  522. })
  523. }
  524. return err
  525. }