excelize.go 11 KB

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