workbook.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. //
  5. // Package excelize providing a set of functions that allow you to write to and
  6. // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
  7. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
  8. // Supports complex components by high compatibility, and provided streaming
  9. // API for generating or reading data from a worksheet with huge amounts of
  10. // data. This library needs Go version 1.16 or later.
  11. package excelize
  12. import (
  13. "bytes"
  14. "encoding/xml"
  15. "io"
  16. "path/filepath"
  17. "strconv"
  18. "strings"
  19. )
  20. // SetWorkbookProps provides a function to sets workbook properties.
  21. func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
  22. wb, err := f.workbookReader()
  23. if err != nil {
  24. return err
  25. }
  26. if wb.WorkbookPr == nil {
  27. wb.WorkbookPr = new(xlsxWorkbookPr)
  28. }
  29. if opts == nil {
  30. return nil
  31. }
  32. if opts.Date1904 != nil {
  33. wb.WorkbookPr.Date1904 = *opts.Date1904
  34. }
  35. if opts.FilterPrivacy != nil {
  36. wb.WorkbookPr.FilterPrivacy = *opts.FilterPrivacy
  37. }
  38. if opts.CodeName != nil {
  39. wb.WorkbookPr.CodeName = *opts.CodeName
  40. }
  41. return nil
  42. }
  43. // GetWorkbookProps provides a function to gets workbook properties.
  44. func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
  45. var opts WorkbookPropsOptions
  46. wb, err := f.workbookReader()
  47. if err != nil {
  48. return opts, err
  49. }
  50. if wb.WorkbookPr != nil {
  51. opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
  52. opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
  53. opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
  54. }
  55. return opts, err
  56. }
  57. // ProtectWorkbook provides a function to prevent other users from viewing
  58. // hidden worksheets, adding, moving, deleting, or hiding worksheets, and
  59. // renaming worksheets in a workbook. The optional field AlgorithmName
  60. // specified hash algorithm, support XOR, MD4, MD5, SHA-1, SHA2-56, SHA-384,
  61. // and SHA-512 currently, if no hash algorithm specified, will be using the XOR
  62. // algorithm as default. The generated workbook only works on Microsoft Office
  63. // 2007 and later. For example, protect workbook with protection settings:
  64. //
  65. // err := f.ProtectWorkbook(&excelize.WorkbookProtectionOptions{
  66. // Password: "password",
  67. // LockStructure: true,
  68. // })
  69. func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
  70. wb, err := f.workbookReader()
  71. if err != nil {
  72. return err
  73. }
  74. if wb.WorkbookProtection == nil {
  75. wb.WorkbookProtection = new(xlsxWorkbookProtection)
  76. }
  77. if opts == nil {
  78. opts = &WorkbookProtectionOptions{}
  79. }
  80. wb.WorkbookProtection = &xlsxWorkbookProtection{
  81. LockStructure: opts.LockStructure,
  82. LockWindows: opts.LockWindows,
  83. }
  84. if opts.Password != "" {
  85. if opts.AlgorithmName == "" {
  86. opts.AlgorithmName = "SHA-512"
  87. }
  88. hashValue, saltValue, err := genISOPasswdHash(opts.Password, opts.AlgorithmName, "", int(workbookProtectionSpinCount))
  89. if err != nil {
  90. return err
  91. }
  92. wb.WorkbookProtection.WorkbookAlgorithmName = opts.AlgorithmName
  93. wb.WorkbookProtection.WorkbookSaltValue = saltValue
  94. wb.WorkbookProtection.WorkbookHashValue = hashValue
  95. wb.WorkbookProtection.WorkbookSpinCount = int(workbookProtectionSpinCount)
  96. }
  97. return nil
  98. }
  99. // UnprotectWorkbook provides a function to remove protection for workbook,
  100. // specified the optional password parameter to remove workbook protection with
  101. // password verification.
  102. func (f *File) UnprotectWorkbook(password ...string) error {
  103. wb, err := f.workbookReader()
  104. if err != nil {
  105. return err
  106. }
  107. // password verification
  108. if len(password) > 0 {
  109. if wb.WorkbookProtection == nil {
  110. return ErrUnprotectWorkbook
  111. }
  112. if wb.WorkbookProtection.WorkbookAlgorithmName != "" {
  113. // check with given salt value
  114. hashValue, _, err := genISOPasswdHash(password[0], wb.WorkbookProtection.WorkbookAlgorithmName, wb.WorkbookProtection.WorkbookSaltValue, wb.WorkbookProtection.WorkbookSpinCount)
  115. if err != nil {
  116. return err
  117. }
  118. if wb.WorkbookProtection.WorkbookHashValue != hashValue {
  119. return ErrUnprotectWorkbookPassword
  120. }
  121. }
  122. }
  123. wb.WorkbookProtection = nil
  124. return err
  125. }
  126. // setWorkbook update workbook property of the spreadsheet. Maximum 31
  127. // characters are allowed in sheet title.
  128. func (f *File) setWorkbook(name string, sheetID, rid int) {
  129. wb, _ := f.workbookReader()
  130. wb.Sheets.Sheet = append(wb.Sheets.Sheet, xlsxSheet{
  131. Name: name,
  132. SheetID: sheetID,
  133. ID: "rId" + strconv.Itoa(rid),
  134. })
  135. }
  136. // getWorkbookPath provides a function to get the path of the workbook.xml in
  137. // the spreadsheet.
  138. func (f *File) getWorkbookPath() (path string) {
  139. if rels, _ := f.relsReader("_rels/.rels"); rels != nil {
  140. rels.Lock()
  141. defer rels.Unlock()
  142. for _, rel := range rels.Relationships {
  143. if rel.Type == SourceRelationshipOfficeDocument {
  144. path = strings.TrimPrefix(rel.Target, "/")
  145. return
  146. }
  147. }
  148. }
  149. return
  150. }
  151. // getWorkbookRelsPath provides a function to get the path of the workbook.xml.rels
  152. // in the spreadsheet.
  153. func (f *File) getWorkbookRelsPath() (path string) {
  154. wbPath := f.getWorkbookPath()
  155. wbDir := filepath.Dir(wbPath)
  156. if wbDir == "." {
  157. path = "_rels/" + filepath.Base(wbPath) + ".rels"
  158. return
  159. }
  160. path = strings.TrimPrefix(filepath.Dir(wbPath)+"/_rels/"+filepath.Base(wbPath)+".rels", "/")
  161. return
  162. }
  163. // workbookReader provides a function to get the pointer to the workbook.xml
  164. // structure after deserialization.
  165. func (f *File) workbookReader() (*xlsxWorkbook, error) {
  166. var err error
  167. if f.WorkBook == nil {
  168. wbPath := f.getWorkbookPath()
  169. f.WorkBook = new(xlsxWorkbook)
  170. if _, ok := f.xmlAttr[wbPath]; !ok {
  171. d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath))))
  172. f.xmlAttr[wbPath] = append(f.xmlAttr[wbPath], getRootElement(d)...)
  173. f.addNameSpaces(wbPath, SourceRelationship)
  174. }
  175. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
  176. Decode(f.WorkBook); err != nil && err != io.EOF {
  177. return f.WorkBook, err
  178. }
  179. }
  180. return f.WorkBook, err
  181. }
  182. // workBookWriter provides a function to save workbook.xml after serialize
  183. // structure.
  184. func (f *File) workBookWriter() {
  185. if f.WorkBook != nil {
  186. if f.WorkBook.DecodeAlternateContent != nil {
  187. f.WorkBook.AlternateContent = &xlsxAlternateContent{
  188. Content: f.WorkBook.DecodeAlternateContent.Content,
  189. XMLNSMC: SourceRelationshipCompatibility.Value,
  190. }
  191. }
  192. f.WorkBook.DecodeAlternateContent = nil
  193. output, _ := xml.Marshal(f.WorkBook)
  194. f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))
  195. }
  196. }