styles.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package excelize
  2. import (
  3. "encoding/json"
  4. "encoding/xml"
  5. "strconv"
  6. "strings"
  7. )
  8. // parseFormatBordersSet provides function to parse the format settings of the
  9. // borders.
  10. func parseFormatBordersSet(bordersSet string) (*formatBorder, error) {
  11. var format formatBorder
  12. err := json.Unmarshal([]byte(bordersSet), &format)
  13. return &format, err
  14. }
  15. // SetBorder provides function to get value from cell by given sheet index and
  16. // coordinate area in XLSX file. Note that the color field uses RGB color code
  17. // and diagonalDown and diagonalUp type border should be use same color in the
  18. // same coordinate area.
  19. //
  20. // For example create a borders of cell H9 on
  21. // Sheet1:
  22. //
  23. // err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"border":[{"type":"left","color":"0000FF","style":3},{"type":"top","color":"00FF00","style":4},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":7},{"type":"diagonalUp","color":"A020F0","style":8}]}`)
  24. // if err != nil {
  25. // fmt.Println(err)
  26. // }
  27. //
  28. // The following shows the border styles sorted by excelize index number:
  29. //
  30. // +-------+---------------+--------+-----------------+
  31. // | Index | Name | Weight | Style |
  32. // +=======+===============+========+=================+
  33. // | 0 | None | 0 | |
  34. // +-------+---------------+--------+-----------------+
  35. // | 1 | Continuous | 1 | ``-----------`` |
  36. // +-------+---------------+--------+-----------------+
  37. // | 2 | Continuous | 2 | ``-----------`` |
  38. // +-------+---------------+--------+-----------------+
  39. // | 3 | Dash | 1 | ``- - - - - -`` |
  40. // +-------+---------------+--------+-----------------+
  41. // | 4 | Dot | 1 | ``. . . . . .`` |
  42. // +-------+---------------+--------+-----------------+
  43. // | 5 | Continuous | 3 | ``-----------`` |
  44. // +-------+---------------+--------+-----------------+
  45. // | 6 | Double | 3 | ``===========`` |
  46. // +-------+---------------+--------+-----------------+
  47. // | 7 | Continuous | 0 | ``-----------`` |
  48. // +-------+---------------+--------+-----------------+
  49. // | 8 | Dash | 2 | ``- - - - - -`` |
  50. // +-------+---------------+--------+-----------------+
  51. // | 9 | Dash Dot | 1 | ``- . - . - .`` |
  52. // +-------+---------------+--------+-----------------+
  53. // | 10 | Dash Dot | 2 | ``- . - . - .`` |
  54. // +-------+---------------+--------+-----------------+
  55. // | 11 | Dash Dot Dot | 1 | ``- . . - . .`` |
  56. // +-------+---------------+--------+-----------------+
  57. // | 12 | Dash Dot Dot | 2 | ``- . . - . .`` |
  58. // +-------+---------------+--------+-----------------+
  59. // | 13 | SlantDash Dot | 2 | ``/ - . / - .`` |
  60. // +-------+---------------+--------+-----------------+
  61. //
  62. // The following shows the borders in the order shown in the Excel dialog:
  63. //
  64. // +-------+-----------------+-------+-----------------+
  65. // | Index | Style | Index | Style |
  66. // +=======+=================+=======+=================+
  67. // | 0 | None | 12 | ``- . . - . .`` |
  68. // +-------+-----------------+-------+-----------------+
  69. // | 7 | ``-----------`` | 13 | ``/ - . / - .`` |
  70. // +-------+-----------------+-------+-----------------+
  71. // | 4 | ``. . . . . .`` | 10 | ``- . - . - .`` |
  72. // +-------+-----------------+-------+-----------------+
  73. // | 11 | ``- . . - . .`` | 8 | ``- - - - - -`` |
  74. // +-------+-----------------+-------+-----------------+
  75. // | 9 | ``- . - . - .`` | 2 | ``-----------`` |
  76. // +-------+-----------------+-------+-----------------+
  77. // | 3 | ``- - - - - -`` | 5 | ``-----------`` |
  78. // +-------+-----------------+-------+-----------------+
  79. // | 1 | ``-----------`` | 6 | ``===========`` |
  80. // +-------+-----------------+-------+-----------------+
  81. //
  82. func (f *File) SetBorder(sheet, hcell, vcell, style string) error {
  83. var styleSheet xlsxStyleSheet
  84. xml.Unmarshal([]byte(f.readXML("xl/styles.xml")), &styleSheet)
  85. formatBorder, err := parseFormatBordersSet(style)
  86. if err != nil {
  87. return err
  88. }
  89. borderID := setBorders(&styleSheet, formatBorder)
  90. cellXfsID := setCellXfs(&styleSheet, borderID)
  91. output, err := xml.Marshal(styleSheet)
  92. if err != nil {
  93. return err
  94. }
  95. f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpace(string(output)))
  96. f.setCellStyle(sheet, hcell, vcell, cellXfsID)
  97. return err
  98. }
  99. // setBorders provides function to add border elements in the styles.xml by
  100. // given borders format settings.
  101. func setBorders(style *xlsxStyleSheet, formatBorder *formatBorder) int {
  102. var styles = []string{
  103. "none",
  104. "thin",
  105. "medium",
  106. "dashed",
  107. "dotted",
  108. "thick",
  109. "double",
  110. "hair",
  111. "mediumDashed",
  112. "dashDot",
  113. "mediumDashDot",
  114. "dashDotDot",
  115. "mediumDashDotDot",
  116. "slantDashDot",
  117. }
  118. var border xlsxBorder
  119. for _, v := range formatBorder.Border {
  120. if v.Style > 13 || v.Style < 0 {
  121. continue
  122. }
  123. var color xlsxColor
  124. color.RGB = v.Color
  125. switch v.Type {
  126. case "left":
  127. border.Left.Style = styles[v.Style]
  128. border.Left.Color = &color
  129. case "right":
  130. border.Right.Style = styles[v.Style]
  131. border.Right.Color = &color
  132. case "top":
  133. border.Top.Style = styles[v.Style]
  134. border.Top.Color = &color
  135. case "bottom":
  136. border.Bottom.Style = styles[v.Style]
  137. border.Bottom.Color = &color
  138. case "diagonalUp":
  139. border.Diagonal.Style = styles[v.Style]
  140. border.Diagonal.Color = &color
  141. border.DiagonalUp = true
  142. case "diagonalDown":
  143. border.Diagonal.Style = styles[v.Style]
  144. border.Diagonal.Color = &color
  145. border.DiagonalDown = true
  146. }
  147. }
  148. style.Borders.Count++
  149. style.Borders.Border = append(style.Borders.Border, &border)
  150. return style.Borders.Count - 1
  151. }
  152. // setCellXfs provides function to set describes all of the formatting for a
  153. // cell.
  154. func setCellXfs(style *xlsxStyleSheet, borderID int) int {
  155. var xf xlsxXf
  156. xf.BorderID = borderID
  157. style.CellXfs.Count++
  158. style.CellXfs.Xf = append(style.CellXfs.Xf, xf)
  159. return style.CellXfs.Count - 1
  160. }
  161. // setCellStyle provides function to add style attribute for cells by given
  162. // sheet index, coordinate area and style ID.
  163. func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) {
  164. hcell = strings.ToUpper(hcell)
  165. vcell = strings.ToUpper(vcell)
  166. // Coordinate conversion, convert C1:B3 to 2,0,1,2.
  167. hcol := string(strings.Map(letterOnlyMapF, hcell))
  168. hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
  169. hyAxis := hrow - 1
  170. hxAxis := titleToNumber(hcol)
  171. vcol := string(strings.Map(letterOnlyMapF, vcell))
  172. vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
  173. vyAxis := vrow - 1
  174. vxAxis := titleToNumber(vcol)
  175. if vxAxis < hxAxis {
  176. hcell, vcell = vcell, hcell
  177. vxAxis, hxAxis = hxAxis, vxAxis
  178. }
  179. if vyAxis < hyAxis {
  180. hcell, vcell = vcell, hcell
  181. vyAxis, hyAxis = hyAxis, vyAxis
  182. }
  183. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  184. hcell = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1)
  185. vcell = toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
  186. var xlsx xlsxWorksheet
  187. name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
  188. xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
  189. if f.checked == nil {
  190. f.checked = make(map[string]bool)
  191. }
  192. ok := f.checked[name]
  193. if !ok {
  194. xlsx = checkRow(xlsx)
  195. f.checked[name] = true
  196. }
  197. xlsx = completeRow(xlsx, vxAxis+1, vyAxis+1)
  198. xlsx = completeCol(xlsx, vxAxis+1, vyAxis+1)
  199. for r, row := range xlsx.SheetData.Row {
  200. for k, c := range row.C {
  201. if checkCellInArea(c.R, hcell+":"+vcell) {
  202. xlsx.SheetData.Row[r].C[k].S = styleID
  203. }
  204. }
  205. }
  206. output, _ := xml.Marshal(xlsx)
  207. f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
  208. }