excelize.go 11 KB

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