lib.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright 2016 - 2019 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
  6. // and read from XLSX files. Support reads and writes XLSX file generated by
  7. // Microsoft Excel™ 2007 and later. Support save file without losing original
  8. // charts of XLSX. This library needs Go version 1.8 or later.
  9. package excelize
  10. import (
  11. "archive/zip"
  12. "bytes"
  13. "fmt"
  14. "io"
  15. "log"
  16. "strconv"
  17. "strings"
  18. )
  19. // ReadZipReader can be used to read an XLSX in memory without touching the
  20. // filesystem.
  21. func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
  22. fileList := make(map[string][]byte)
  23. worksheets := 0
  24. for _, v := range r.File {
  25. fileList[v.Name] = readFile(v)
  26. if len(v.Name) > 18 {
  27. if v.Name[0:19] == "xl/worksheets/sheet" {
  28. worksheets++
  29. }
  30. }
  31. }
  32. return fileList, worksheets, nil
  33. }
  34. // readXML provides a function to read XML content as string.
  35. func (f *File) readXML(name string) []byte {
  36. if content, ok := f.XLSX[name]; ok {
  37. return content
  38. }
  39. return []byte{}
  40. }
  41. // saveFileList provides a function to update given file content in file list
  42. // of XLSX.
  43. func (f *File) saveFileList(name string, content []byte) {
  44. newContent := make([]byte, 0, len(XMLHeader)+len(content))
  45. newContent = append(newContent, []byte(XMLHeader)...)
  46. newContent = append(newContent, content...)
  47. f.XLSX[name] = newContent
  48. }
  49. // Read file content as string in a archive file.
  50. func readFile(file *zip.File) []byte {
  51. rc, err := file.Open()
  52. if err != nil {
  53. log.Fatal(err)
  54. }
  55. buff := bytes.NewBuffer(nil)
  56. _, _ = io.Copy(buff, rc)
  57. rc.Close()
  58. return buff.Bytes()
  59. }
  60. // SplitCellName splits cell name to column name and row number.
  61. //
  62. // Example:
  63. //
  64. // excelize.SplitCellName("AK74") // return "AK", 74, nil
  65. //
  66. func SplitCellName(cell string) (string, int, error) {
  67. alpha := func(r rune) bool {
  68. return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
  69. }
  70. if strings.IndexFunc(cell, alpha) == 0 {
  71. i := strings.LastIndexFunc(cell, alpha)
  72. if i >= 0 && i < len(cell)-1 {
  73. col, rowstr := cell[:i+1], cell[i+1:]
  74. if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
  75. return col, row, nil
  76. }
  77. }
  78. }
  79. return "", -1, newInvalidCellNameError(cell)
  80. }
  81. // JoinCellName joins cell name from column name and row number
  82. func JoinCellName(col string, row int) (string, error) {
  83. normCol := strings.Map(func(rune rune) rune {
  84. switch {
  85. case 'A' <= rune && rune <= 'Z':
  86. return rune
  87. case 'a' <= rune && rune <= 'z':
  88. return rune - 32
  89. }
  90. return -1
  91. }, col)
  92. if len(col) == 0 || len(col) != len(normCol) {
  93. return "", newInvalidColumnNameError(col)
  94. }
  95. if row < 1 {
  96. return "", newInvalidRowNumberError(row)
  97. }
  98. return fmt.Sprintf("%s%d", normCol, row), nil
  99. }
  100. // ColumnNameToNumber provides a function to convert Excel sheet
  101. // column name to int. Column name case insencitive
  102. // Function returns error if column name incorrect.
  103. //
  104. // Example:
  105. //
  106. // excelize.ColumnNameToNumber("AK") // returns 37, nil
  107. //
  108. func ColumnNameToNumber(name string) (int, error) {
  109. if len(name) == 0 {
  110. return -1, newInvalidColumnNameError(name)
  111. }
  112. col := 0
  113. multi := 1
  114. for i := len(name) - 1; i >= 0; i-- {
  115. r := name[i]
  116. if r >= 'A' && r <= 'Z' {
  117. col += int(r-'A'+1) * multi
  118. } else if r >= 'a' && r <= 'z' {
  119. col += int(r-'a'+1) * multi
  120. } else {
  121. return -1, newInvalidColumnNameError(name)
  122. }
  123. multi *= 26
  124. }
  125. return col, nil
  126. }
  127. // MustColumnNameToNumber provides a function to convert Excel sheet column
  128. // name to int. Column name case insencitive.
  129. // Function returns error if column name incorrect.
  130. //
  131. // Example:
  132. //
  133. // excelize.MustColumnNameToNumber("AK") // returns 37
  134. //
  135. func MustColumnNameToNumber(name string) int {
  136. n, err := ColumnNameToNumber(name)
  137. if err != nil {
  138. panic(err)
  139. }
  140. return n
  141. }
  142. // ColumnNumberToName provides a function to convert integer
  143. // to Excel sheet column title.
  144. //
  145. // Example:
  146. //
  147. // excelize.ColumnNumberToName(37) // returns "AK", nil
  148. //
  149. func ColumnNumberToName(num int) (string, error) {
  150. if num < 1 {
  151. return "", fmt.Errorf("incorrect column number %d", num)
  152. }
  153. var col string
  154. for num > 0 {
  155. col = string((num-1)%26+65) + col
  156. num = (num - 1) / 26
  157. }
  158. return col, nil
  159. }
  160. // CellNameToCoordinates converts alpha-numeric cell name
  161. // to [X, Y] coordinates or retrusn an error.
  162. //
  163. // Example:
  164. // CellCoordinates("A1") // returns 1, 1, nil
  165. // CellCoordinates("Z3") // returns 26, 3, nil
  166. //
  167. func CellNameToCoordinates(cell string) (int, int, error) {
  168. const msg = "cannot convert cell %q to coordinates: %v"
  169. colname, row, err := SplitCellName(cell)
  170. if err != nil {
  171. return -1, -1, fmt.Errorf(msg, cell, err)
  172. }
  173. col, err := ColumnNameToNumber(colname)
  174. if err != nil {
  175. return -1, -1, fmt.Errorf(msg, cell, err)
  176. }
  177. return col, row, nil
  178. }
  179. // MustCellNameToCoordinates converts alpha-numeric cell name
  180. // to [X, Y] coordinates or panics.
  181. //
  182. // Example:
  183. // MustCellNameToCoordinates("A1") // returns 1, 1
  184. // MustCellNameToCoordinates("Z3") // returns 26, 3
  185. //
  186. func MustCellNameToCoordinates(cell string) (int, int) {
  187. c, r, err := CellNameToCoordinates(cell)
  188. if err != nil {
  189. panic(err)
  190. }
  191. return c, r
  192. }
  193. // CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell name or returns an error.
  194. //
  195. // Example:
  196. // CoordinatesToCellName(1, 1) // returns "A1", nil
  197. //
  198. func CoordinatesToCellName(col, row int) (string, error) {
  199. if col < 1 || row < 1 {
  200. return "", fmt.Errorf("invalid cell coordinates [%d, %d]", col, row)
  201. }
  202. colname, err := ColumnNumberToName(col)
  203. if err != nil {
  204. return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
  205. }
  206. return fmt.Sprintf("%s%d", colname, row), nil
  207. }
  208. // MustCoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell name or panics.
  209. //
  210. // Example:
  211. // MustCoordinatesToCellName(1, 1) // returns "A1"
  212. //
  213. func MustCoordinatesToCellName(col, row int) string {
  214. n, err := CoordinatesToCellName(col, row)
  215. if err != nil {
  216. panic(err)
  217. }
  218. return n
  219. }
  220. // boolPtr returns a pointer to a bool with the given value.
  221. func boolPtr(b bool) *bool { return &b }
  222. // defaultTrue returns true if b is nil, or the pointed value.
  223. func defaultTrue(b *bool) bool {
  224. if b == nil {
  225. return true
  226. }
  227. return *b
  228. }
  229. // parseFormatSet provides a method to convert format string to []byte and
  230. // handle empty string.
  231. func parseFormatSet(formatSet string) []byte {
  232. if formatSet != "" {
  233. return []byte(formatSet)
  234. }
  235. return []byte("{}")
  236. }
  237. // namespaceStrictToTransitional provides a method to convert Strict and
  238. // Transitional namespaces.
  239. func namespaceStrictToTransitional(content []byte) []byte {
  240. var namespaceTranslationDic = map[string]string{
  241. StrictSourceRelationship: SourceRelationship,
  242. StrictSourceRelationshipChart: SourceRelationshipChart,
  243. StrictSourceRelationshipComments: SourceRelationshipComments,
  244. StrictSourceRelationshipImage: SourceRelationshipImage,
  245. StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
  246. }
  247. for s, n := range namespaceTranslationDic {
  248. content = bytes.Replace(content, []byte(s), []byte(n), -1)
  249. }
  250. return content
  251. }
  252. // genSheetPasswd provides a method to generate password for worksheet
  253. // protection by given plaintext. When an Excel sheet is being protected with
  254. // a password, a 16-bit (two byte) long hash is generated. To verify a
  255. // password, it is compared to the hash. Obviously, if the input data volume
  256. // is great, numerous passwords will match the same hash. Here is the
  257. // algorithm to create the hash value:
  258. //
  259. // take the ASCII values of all characters shift left the first character 1 bit,
  260. // the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits,
  261. // the highest bit of the 16-bit value is always 0 [signed short])
  262. // XOR all these values
  263. // XOR the count of characters
  264. // XOR the constant 0xCE4B
  265. func genSheetPasswd(plaintext string) string {
  266. var password int64 = 0x0000
  267. var charPos uint = 1
  268. for _, v := range plaintext {
  269. value := int64(v) << charPos
  270. charPos++
  271. rotatedBits := value >> 15 // rotated bits beyond bit 15
  272. value &= 0x7fff // first 15 bits
  273. password ^= (value | rotatedBits)
  274. }
  275. password ^= int64(len(plaintext))
  276. password ^= 0xCE4B
  277. return strings.ToUpper(strconv.FormatInt(password, 16))
  278. }