styles.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. package excelize
  2. import (
  3. "encoding/json"
  4. "encoding/xml"
  5. "strconv"
  6. "strings"
  7. )
  8. // parseFormatStyleSet provides function to parse the format settings of the
  9. // borders.
  10. func parseFormatStyleSet(style string) (*formatCellStyle, error) {
  11. var format formatCellStyle
  12. err := json.Unmarshal([]byte(style), &format)
  13. return &format, err
  14. }
  15. // SetCellStyle provides function to get value from cell by given sheet index
  16. // and coordinate area in XLSX file. Note that the color field uses RGB color
  17. // code and diagonalDown and diagonalUp type border should be use same color in
  18. // the same coordinate area.
  19. //
  20. // For example create a borders of cell H9 on Sheet1:
  21. //
  22. // 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}]}`)
  23. // if err != nil {
  24. // fmt.Println(err)
  25. // }
  26. //
  27. // Set gradient fill with vertical variants shading styles for cell H9 on
  28. // Sheet1:
  29. //
  30. // err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":1}]}`)
  31. // if err != nil {
  32. // fmt.Println(err)
  33. // }
  34. //
  35. // Set solid style pattern fill for cell H9 on Sheet1:
  36. //
  37. // err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"fill":[{"type":"pattern","color":["#E0EBF5"],"pattern":1}]}`)
  38. // if err != nil {
  39. // fmt.Println(err)
  40. // }
  41. //
  42. // The following shows the border styles sorted by excelize index number:
  43. //
  44. // +-------+---------------+--------+-----------------+
  45. // | Index | Name | Weight | Style |
  46. // +=======+===============+========+=================+
  47. // | 0 | None | 0 | |
  48. // +-------+---------------+--------+-----------------+
  49. // | 1 | Continuous | 1 | ``-----------`` |
  50. // +-------+---------------+--------+-----------------+
  51. // | 2 | Continuous | 2 | ``-----------`` |
  52. // +-------+---------------+--------+-----------------+
  53. // | 3 | Dash | 1 | ``- - - - - -`` |
  54. // +-------+---------------+--------+-----------------+
  55. // | 4 | Dot | 1 | ``. . . . . .`` |
  56. // +-------+---------------+--------+-----------------+
  57. // | 5 | Continuous | 3 | ``-----------`` |
  58. // +-------+---------------+--------+-----------------+
  59. // | 6 | Double | 3 | ``===========`` |
  60. // +-------+---------------+--------+-----------------+
  61. // | 7 | Continuous | 0 | ``-----------`` |
  62. // +-------+---------------+--------+-----------------+
  63. // | 8 | Dash | 2 | ``- - - - - -`` |
  64. // +-------+---------------+--------+-----------------+
  65. // | 9 | Dash Dot | 1 | ``- . - . - .`` |
  66. // +-------+---------------+--------+-----------------+
  67. // | 10 | Dash Dot | 2 | ``- . - . - .`` |
  68. // +-------+---------------+--------+-----------------+
  69. // | 11 | Dash Dot Dot | 1 | ``- . . - . .`` |
  70. // +-------+---------------+--------+-----------------+
  71. // | 12 | Dash Dot Dot | 2 | ``- . . - . .`` |
  72. // +-------+---------------+--------+-----------------+
  73. // | 13 | SlantDash Dot | 2 | ``/ - . / - .`` |
  74. // +-------+---------------+--------+-----------------+
  75. //
  76. // The following shows the borders in the order shown in the Excel dialog:
  77. //
  78. // +-------+-----------------+-------+-----------------+
  79. // | Index | Style | Index | Style |
  80. // +=======+=================+=======+=================+
  81. // | 0 | None | 12 | ``- . . - . .`` |
  82. // +-------+-----------------+-------+-----------------+
  83. // | 7 | ``-----------`` | 13 | ``/ - . / - .`` |
  84. // +-------+-----------------+-------+-----------------+
  85. // | 4 | ``. . . . . .`` | 10 | ``- . - . - .`` |
  86. // +-------+-----------------+-------+-----------------+
  87. // | 11 | ``- . . - . .`` | 8 | ``- - - - - -`` |
  88. // +-------+-----------------+-------+-----------------+
  89. // | 9 | ``- . - . - .`` | 2 | ``-----------`` |
  90. // +-------+-----------------+-------+-----------------+
  91. // | 3 | ``- - - - - -`` | 5 | ``-----------`` |
  92. // +-------+-----------------+-------+-----------------+
  93. // | 1 | ``-----------`` | 6 | ``===========`` |
  94. // +-------+-----------------+-------+-----------------+
  95. //
  96. // The following shows the shading styles sorted by excelize index number:
  97. //
  98. // +-------+-----------------+-------+-----------------+
  99. // | Index | Style | Index | Style |
  100. // +=======+=================+=======+=================+
  101. // | 0 | Horizontal | 3 | Diagonal down |
  102. // +-------+-----------------+-------+-----------------+
  103. // | 1 | Vertical | 4 | From corner |
  104. // +-------+-----------------+-------+-----------------+
  105. // | 2 | Diagonal Up | 5 | From center |
  106. // +-------+-----------------+-------+-----------------+
  107. //
  108. // The following shows the patterns styles sorted by excelize index number:
  109. //
  110. // +-------+-----------------+-------+-----------------+
  111. // | Index | Style | Index | Style |
  112. // +=======+=================+=======+=================+
  113. // | 0 | None | 10 | darkTrellis |
  114. // +-------+-----------------+-------+-----------------+
  115. // | 1 | solid | 11 | lightHorizontal |
  116. // +-------+-----------------+-------+-----------------+
  117. // | 2 | mediumGray | 12 | lightVertical |
  118. // +-------+-----------------+-------+-----------------+
  119. // | 3 | darkGray | 13 | lightDown |
  120. // +-------+-----------------+-------+-----------------+
  121. // | 4 | lightGray | 14 | lightUp |
  122. // +-------+-----------------+-------+-----------------+
  123. // | 5 | darkHorizontal | 15 | lightGrid |
  124. // +-------+-----------------+-------+-----------------+
  125. // | 6 | darkVertical | 16 | lightTrellis |
  126. // +-------+-----------------+-------+-----------------+
  127. // | 7 | darkDown | 17 | gray125 |
  128. // +-------+-----------------+-------+-----------------+
  129. // | 8 | darkUp | 18 | gray0625 |
  130. // +-------+-----------------+-------+-----------------+
  131. // | 9 | darkGrid | | |
  132. // +-------+-----------------+-------+-----------------+
  133. //
  134. func (f *File) SetCellStyle(sheet, hcell, vcell, style string) error {
  135. var styleSheet xlsxStyleSheet
  136. xml.Unmarshal([]byte(f.readXML("xl/styles.xml")), &styleSheet)
  137. formatCellStyle, err := parseFormatStyleSet(style)
  138. if err != nil {
  139. return err
  140. }
  141. borderID := setBorders(&styleSheet, formatCellStyle)
  142. fillID := setFills(&styleSheet, formatCellStyle)
  143. cellXfsID := setCellXfs(&styleSheet, fillID, borderID)
  144. output, err := xml.Marshal(styleSheet)
  145. if err != nil {
  146. return err
  147. }
  148. f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpace(string(output)))
  149. f.setCellStyle(sheet, hcell, vcell, cellXfsID)
  150. return err
  151. }
  152. // setFills provides function to add fill elements in the styles.xml by given
  153. // cell format settings.
  154. func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
  155. var patterns = []string{
  156. "none",
  157. "solid",
  158. "mediumGray",
  159. "darkGray",
  160. "lightGray",
  161. "darkHorizontal",
  162. "darkVertical",
  163. "darkDown",
  164. "darkUp",
  165. "darkGrid",
  166. "darkTrellis",
  167. "lightHorizontal",
  168. "lightVertical",
  169. "lightDown",
  170. "lightUp",
  171. "lightGrid",
  172. "lightTrellis",
  173. "gray125",
  174. "gray0625",
  175. }
  176. var variants = []float64{
  177. 90,
  178. 0,
  179. 45,
  180. 135,
  181. }
  182. var fill xlsxFill
  183. for _, v := range formatCellStyle.Fill {
  184. switch v.Type {
  185. case "gradient":
  186. if len(v.Color) != 2 {
  187. continue
  188. }
  189. var gradient xlsxGradientFill
  190. switch v.Shading {
  191. case 0, 1, 2, 3:
  192. gradient.Degree = variants[v.Shading]
  193. case 4:
  194. gradient.Type = "path"
  195. case 5:
  196. gradient.Type = "path"
  197. gradient.Bottom = 0.5
  198. gradient.Left = 0.5
  199. gradient.Right = 0.5
  200. gradient.Top = 0.5
  201. default:
  202. continue
  203. }
  204. var stops []*xlsxGradientFillStop
  205. for index, color := range v.Color {
  206. var stop xlsxGradientFillStop
  207. stop.Position = float64(index)
  208. stop.Color.RGB = getPaletteColor(color)
  209. stops = append(stops, &stop)
  210. }
  211. gradient.Stop = stops
  212. fill.GradientFill = &gradient
  213. case "pattern":
  214. if v.Pattern > 18 || v.Pattern < 0 {
  215. continue
  216. }
  217. if len(v.Color) < 1 {
  218. continue
  219. }
  220. var pattern xlsxPatternFill
  221. pattern.PatternType = patterns[v.Pattern]
  222. pattern.FgColor.RGB = getPaletteColor(v.Color[0])
  223. fill.PatternFill = &pattern
  224. }
  225. }
  226. style.Fills.Count++
  227. style.Fills.Fill = append(style.Fills.Fill, &fill)
  228. return style.Fills.Count - 1
  229. }
  230. // setBorders provides function to add border elements in the styles.xml by
  231. // given borders format settings.
  232. func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
  233. var styles = []string{
  234. "none",
  235. "thin",
  236. "medium",
  237. "dashed",
  238. "dotted",
  239. "thick",
  240. "double",
  241. "hair",
  242. "mediumDashed",
  243. "dashDot",
  244. "mediumDashDot",
  245. "dashDotDot",
  246. "mediumDashDotDot",
  247. "slantDashDot",
  248. }
  249. var border xlsxBorder
  250. for _, v := range formatCellStyle.Border {
  251. if v.Style > 13 || v.Style < 0 {
  252. continue
  253. }
  254. var color xlsxColor
  255. color.RGB = getPaletteColor(v.Color)
  256. switch v.Type {
  257. case "left":
  258. border.Left.Style = styles[v.Style]
  259. border.Left.Color = &color
  260. case "right":
  261. border.Right.Style = styles[v.Style]
  262. border.Right.Color = &color
  263. case "top":
  264. border.Top.Style = styles[v.Style]
  265. border.Top.Color = &color
  266. case "bottom":
  267. border.Bottom.Style = styles[v.Style]
  268. border.Bottom.Color = &color
  269. case "diagonalUp":
  270. border.Diagonal.Style = styles[v.Style]
  271. border.Diagonal.Color = &color
  272. border.DiagonalUp = true
  273. case "diagonalDown":
  274. border.Diagonal.Style = styles[v.Style]
  275. border.Diagonal.Color = &color
  276. border.DiagonalDown = true
  277. }
  278. }
  279. style.Borders.Count++
  280. style.Borders.Border = append(style.Borders.Border, &border)
  281. return style.Borders.Count - 1
  282. }
  283. // setCellXfs provides function to set describes all of the formatting for a
  284. // cell.
  285. func setCellXfs(style *xlsxStyleSheet, fillID, borderID int) int {
  286. var xf xlsxXf
  287. xf.FillID = fillID
  288. xf.BorderID = borderID
  289. style.CellXfs.Count++
  290. style.CellXfs.Xf = append(style.CellXfs.Xf, xf)
  291. return style.CellXfs.Count - 1
  292. }
  293. // setCellStyle provides function to add style attribute for cells by given
  294. // sheet index, coordinate area and style ID.
  295. func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) {
  296. hcell = strings.ToUpper(hcell)
  297. vcell = strings.ToUpper(vcell)
  298. // Coordinate conversion, convert C1:B3 to 2,0,1,2.
  299. hcol := string(strings.Map(letterOnlyMapF, hcell))
  300. hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
  301. hyAxis := hrow - 1
  302. hxAxis := titleToNumber(hcol)
  303. vcol := string(strings.Map(letterOnlyMapF, vcell))
  304. vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
  305. vyAxis := vrow - 1
  306. vxAxis := titleToNumber(vcol)
  307. if vxAxis < hxAxis {
  308. hcell, vcell = vcell, hcell
  309. vxAxis, hxAxis = hxAxis, vxAxis
  310. }
  311. if vyAxis < hyAxis {
  312. hcell, vcell = vcell, hcell
  313. vyAxis, hyAxis = hyAxis, vyAxis
  314. }
  315. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  316. hcell = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1)
  317. vcell = toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
  318. xlsx := f.workSheetReader(sheet)
  319. completeRow(xlsx, vxAxis+1, vyAxis+1)
  320. completeCol(xlsx, vxAxis+1, vyAxis+1)
  321. for r, row := range xlsx.SheetData.Row {
  322. for k, c := range row.C {
  323. if checkCellInArea(c.R, hcell+":"+vcell) {
  324. xlsx.SheetData.Row[r].C[k].S = styleID
  325. }
  326. }
  327. }
  328. }
  329. // getPaletteColor provides function to convert the RBG color by given string.
  330. func getPaletteColor(color string) string {
  331. return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1)
  332. }