styles.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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 set style for cells 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. // Set alignment style for cell H9 on Sheet1:
  43. //
  44. // err = xlsx.SetCellStyle("Sheet2", "H9", "H9", `{"alignment":{"horizontal":"center","ident":1,"justify_last_line":true,"reading_order":0,"relative_indent":1,"shrink_to_fit":true,"text_rotation":45,"vertical":"","wrap_text":true}}`)
  45. // if err != nil {
  46. // fmt.Println(err)
  47. // }
  48. //
  49. // The following shows the border styles sorted by excelize index number:
  50. //
  51. // +-------+---------------+--------+-----------------+
  52. // | Index | Name | Weight | Style |
  53. // +=======+===============+========+=================+
  54. // | 0 | None | 0 | |
  55. // +-------+---------------+--------+-----------------+
  56. // | 1 | Continuous | 1 | ``-----------`` |
  57. // +-------+---------------+--------+-----------------+
  58. // | 2 | Continuous | 2 | ``-----------`` |
  59. // +-------+---------------+--------+-----------------+
  60. // | 3 | Dash | 1 | ``- - - - - -`` |
  61. // +-------+---------------+--------+-----------------+
  62. // | 4 | Dot | 1 | ``. . . . . .`` |
  63. // +-------+---------------+--------+-----------------+
  64. // | 5 | Continuous | 3 | ``-----------`` |
  65. // +-------+---------------+--------+-----------------+
  66. // | 6 | Double | 3 | ``===========`` |
  67. // +-------+---------------+--------+-----------------+
  68. // | 7 | Continuous | 0 | ``-----------`` |
  69. // +-------+---------------+--------+-----------------+
  70. // | 8 | Dash | 2 | ``- - - - - -`` |
  71. // +-------+---------------+--------+-----------------+
  72. // | 9 | Dash Dot | 1 | ``- . - . - .`` |
  73. // +-------+---------------+--------+-----------------+
  74. // | 10 | Dash Dot | 2 | ``- . - . - .`` |
  75. // +-------+---------------+--------+-----------------+
  76. // | 11 | Dash Dot Dot | 1 | ``- . . - . .`` |
  77. // +-------+---------------+--------+-----------------+
  78. // | 12 | Dash Dot Dot | 2 | ``- . . - . .`` |
  79. // +-------+---------------+--------+-----------------+
  80. // | 13 | SlantDash Dot | 2 | ``/ - . / - .`` |
  81. // +-------+---------------+--------+-----------------+
  82. //
  83. // The following shows the borders in the order shown in the Excel dialog:
  84. //
  85. // +-------+-----------------+-------+-----------------+
  86. // | Index | Style | Index | Style |
  87. // +=======+=================+=======+=================+
  88. // | 0 | None | 12 | ``- . . - . .`` |
  89. // +-------+-----------------+-------+-----------------+
  90. // | 7 | ``-----------`` | 13 | ``/ - . / - .`` |
  91. // +-------+-----------------+-------+-----------------+
  92. // | 4 | ``. . . . . .`` | 10 | ``- . - . - .`` |
  93. // +-------+-----------------+-------+-----------------+
  94. // | 11 | ``- . . - . .`` | 8 | ``- - - - - -`` |
  95. // +-------+-----------------+-------+-----------------+
  96. // | 9 | ``- . - . - .`` | 2 | ``-----------`` |
  97. // +-------+-----------------+-------+-----------------+
  98. // | 3 | ``- - - - - -`` | 5 | ``-----------`` |
  99. // +-------+-----------------+-------+-----------------+
  100. // | 1 | ``-----------`` | 6 | ``===========`` |
  101. // +-------+-----------------+-------+-----------------+
  102. //
  103. // The following shows the shading styles sorted by excelize index number:
  104. //
  105. // +-------+-----------------+-------+-----------------+
  106. // | Index | Style | Index | Style |
  107. // +=======+=================+=======+=================+
  108. // | 0 | Horizontal | 3 | Diagonal down |
  109. // +-------+-----------------+-------+-----------------+
  110. // | 1 | Vertical | 4 | From corner |
  111. // +-------+-----------------+-------+-----------------+
  112. // | 2 | Diagonal Up | 5 | From center |
  113. // +-------+-----------------+-------+-----------------+
  114. //
  115. // The following shows the patterns styles sorted by excelize index number:
  116. //
  117. // +-------+-----------------+-------+-----------------+
  118. // | Index | Style | Index | Style |
  119. // +=======+=================+=======+=================+
  120. // | 0 | None | 10 | darkTrellis |
  121. // +-------+-----------------+-------+-----------------+
  122. // | 1 | solid | 11 | lightHorizontal |
  123. // +-------+-----------------+-------+-----------------+
  124. // | 2 | mediumGray | 12 | lightVertical |
  125. // +-------+-----------------+-------+-----------------+
  126. // | 3 | darkGray | 13 | lightDown |
  127. // +-------+-----------------+-------+-----------------+
  128. // | 4 | lightGray | 14 | lightUp |
  129. // +-------+-----------------+-------+-----------------+
  130. // | 5 | darkHorizontal | 15 | lightGrid |
  131. // +-------+-----------------+-------+-----------------+
  132. // | 6 | darkVertical | 16 | lightTrellis |
  133. // +-------+-----------------+-------+-----------------+
  134. // | 7 | darkDown | 17 | gray125 |
  135. // +-------+-----------------+-------+-----------------+
  136. // | 8 | darkUp | 18 | gray0625 |
  137. // +-------+-----------------+-------+-----------------+
  138. // | 9 | darkGrid | | |
  139. // +-------+-----------------+-------+-----------------+
  140. //
  141. // The following the type of horizontal alignment in cells:
  142. //
  143. // +------------------+
  144. // | Style |
  145. // +==================+
  146. // | left |
  147. // +------------------+
  148. // | center |
  149. // +------------------+
  150. // | right |
  151. // +------------------+
  152. // | fill |
  153. // +------------------+
  154. // | justify |
  155. // +------------------+
  156. // | centerContinuous |
  157. // +------------------+
  158. // | distributed |
  159. // +------------------+
  160. //
  161. // The following the type of vertical alignment in cells:
  162. //
  163. // +------------------+
  164. // | Style |
  165. // +==================+
  166. // | top |
  167. // +------------------+
  168. // | center |
  169. // +------------------+
  170. // | justify |
  171. // +------------------+
  172. // | distributed |
  173. // +------------------+
  174. //
  175. func (f *File) SetCellStyle(sheet, hcell, vcell, style string) error {
  176. var styleSheet xlsxStyleSheet
  177. xml.Unmarshal([]byte(f.readXML("xl/styles.xml")), &styleSheet)
  178. formatCellStyle, err := parseFormatStyleSet(style)
  179. if err != nil {
  180. return err
  181. }
  182. borderID := setBorders(&styleSheet, formatCellStyle)
  183. fillID := setFills(&styleSheet, formatCellStyle)
  184. applyAlignment, alignment := setAlignment(&styleSheet, formatCellStyle)
  185. cellXfsID := setCellXfs(&styleSheet, fillID, borderID, applyAlignment, alignment)
  186. output, err := xml.Marshal(styleSheet)
  187. if err != nil {
  188. return err
  189. }
  190. f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpace(string(output)))
  191. f.setCellStyle(sheet, hcell, vcell, cellXfsID)
  192. return err
  193. }
  194. // setFills provides function to add fill elements in the styles.xml by given
  195. // cell format settings.
  196. func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
  197. var patterns = []string{
  198. "none",
  199. "solid",
  200. "mediumGray",
  201. "darkGray",
  202. "lightGray",
  203. "darkHorizontal",
  204. "darkVertical",
  205. "darkDown",
  206. "darkUp",
  207. "darkGrid",
  208. "darkTrellis",
  209. "lightHorizontal",
  210. "lightVertical",
  211. "lightDown",
  212. "lightUp",
  213. "lightGrid",
  214. "lightTrellis",
  215. "gray125",
  216. "gray0625",
  217. }
  218. var variants = []float64{
  219. 90,
  220. 0,
  221. 45,
  222. 135,
  223. }
  224. var fill xlsxFill
  225. switch formatCellStyle.Fill.Type {
  226. case "gradient":
  227. if len(formatCellStyle.Fill.Color) != 2 {
  228. break
  229. }
  230. var gradient xlsxGradientFill
  231. switch formatCellStyle.Fill.Shading {
  232. case 0, 1, 2, 3:
  233. gradient.Degree = variants[formatCellStyle.Fill.Shading]
  234. case 4:
  235. gradient.Type = "path"
  236. case 5:
  237. gradient.Type = "path"
  238. gradient.Bottom = 0.5
  239. gradient.Left = 0.5
  240. gradient.Right = 0.5
  241. gradient.Top = 0.5
  242. default:
  243. break
  244. }
  245. var stops []*xlsxGradientFillStop
  246. for index, color := range formatCellStyle.Fill.Color {
  247. var stop xlsxGradientFillStop
  248. stop.Position = float64(index)
  249. stop.Color.RGB = getPaletteColor(color)
  250. stops = append(stops, &stop)
  251. }
  252. gradient.Stop = stops
  253. fill.GradientFill = &gradient
  254. case "pattern":
  255. if formatCellStyle.Fill.Pattern > 18 || formatCellStyle.Fill.Pattern < 0 {
  256. break
  257. }
  258. if len(formatCellStyle.Fill.Color) < 1 {
  259. break
  260. }
  261. var pattern xlsxPatternFill
  262. pattern.PatternType = patterns[formatCellStyle.Fill.Pattern]
  263. pattern.FgColor.RGB = getPaletteColor(formatCellStyle.Fill.Color[0])
  264. fill.PatternFill = &pattern
  265. }
  266. style.Fills.Count++
  267. style.Fills.Fill = append(style.Fills.Fill, &fill)
  268. return style.Fills.Count - 1
  269. }
  270. // setAlignment provides function to formatting information pertaining to text
  271. // alignment in cells. There are a variety of choices for how text is aligned
  272. // both horizontally and vertically, as well as indentation settings, and so on.
  273. func setAlignment(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) (bool, *xlsxAlignment) {
  274. if formatCellStyle.Alignment == nil {
  275. return false, &xlsxAlignment{}
  276. }
  277. var alignment = xlsxAlignment{
  278. Horizontal: formatCellStyle.Alignment.Horizontal,
  279. Indent: formatCellStyle.Alignment.Indent,
  280. JustifyLastLine: formatCellStyle.Alignment.JustifyLastLine,
  281. ReadingOrder: formatCellStyle.Alignment.ReadingOrder,
  282. RelativeIndent: formatCellStyle.Alignment.RelativeIndent,
  283. ShrinkToFit: formatCellStyle.Alignment.ShrinkToFit,
  284. TextRotation: formatCellStyle.Alignment.TextRotation,
  285. Vertical: formatCellStyle.Alignment.Vertical,
  286. WrapText: formatCellStyle.Alignment.WrapText,
  287. }
  288. return true, &alignment
  289. }
  290. // setBorders provides function to add border elements in the styles.xml by
  291. // given borders format settings.
  292. func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
  293. var styles = []string{
  294. "none",
  295. "thin",
  296. "medium",
  297. "dashed",
  298. "dotted",
  299. "thick",
  300. "double",
  301. "hair",
  302. "mediumDashed",
  303. "dashDot",
  304. "mediumDashDot",
  305. "dashDotDot",
  306. "mediumDashDotDot",
  307. "slantDashDot",
  308. }
  309. var border xlsxBorder
  310. for _, v := range formatCellStyle.Border {
  311. if v.Style > 13 || v.Style < 0 {
  312. continue
  313. }
  314. var color xlsxColor
  315. color.RGB = getPaletteColor(v.Color)
  316. switch v.Type {
  317. case "left":
  318. border.Left.Style = styles[v.Style]
  319. border.Left.Color = &color
  320. case "right":
  321. border.Right.Style = styles[v.Style]
  322. border.Right.Color = &color
  323. case "top":
  324. border.Top.Style = styles[v.Style]
  325. border.Top.Color = &color
  326. case "bottom":
  327. border.Bottom.Style = styles[v.Style]
  328. border.Bottom.Color = &color
  329. case "diagonalUp":
  330. border.Diagonal.Style = styles[v.Style]
  331. border.Diagonal.Color = &color
  332. border.DiagonalUp = true
  333. case "diagonalDown":
  334. border.Diagonal.Style = styles[v.Style]
  335. border.Diagonal.Color = &color
  336. border.DiagonalDown = true
  337. }
  338. }
  339. style.Borders.Count++
  340. style.Borders.Border = append(style.Borders.Border, &border)
  341. return style.Borders.Count - 1
  342. }
  343. // setCellXfs provides function to set describes all of the formatting for a
  344. // cell.
  345. func setCellXfs(style *xlsxStyleSheet, fillID, borderID int, applyAlignment bool, alignment *xlsxAlignment) int {
  346. var xf xlsxXf
  347. xf.FillID = fillID
  348. xf.BorderID = borderID
  349. style.CellXfs.Count++
  350. xf.Alignment = alignment
  351. xf.ApplyAlignment = applyAlignment
  352. style.CellXfs.Xf = append(style.CellXfs.Xf, xf)
  353. return style.CellXfs.Count - 1
  354. }
  355. // setCellStyle provides function to add style attribute for cells by given
  356. // sheet index, coordinate area and style ID.
  357. func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) {
  358. hcell = strings.ToUpper(hcell)
  359. vcell = strings.ToUpper(vcell)
  360. // Coordinate conversion, convert C1:B3 to 2,0,1,2.
  361. hcol := string(strings.Map(letterOnlyMapF, hcell))
  362. hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
  363. hyAxis := hrow - 1
  364. hxAxis := titleToNumber(hcol)
  365. vcol := string(strings.Map(letterOnlyMapF, vcell))
  366. vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
  367. vyAxis := vrow - 1
  368. vxAxis := titleToNumber(vcol)
  369. if vxAxis < hxAxis {
  370. hcell, vcell = vcell, hcell
  371. vxAxis, hxAxis = hxAxis, vxAxis
  372. }
  373. if vyAxis < hyAxis {
  374. hcell, vcell = vcell, hcell
  375. vyAxis, hyAxis = hyAxis, vyAxis
  376. }
  377. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  378. hcell = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1)
  379. vcell = toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
  380. xlsx := f.workSheetReader(sheet)
  381. completeRow(xlsx, vxAxis+1, vyAxis+1)
  382. completeCol(xlsx, vxAxis+1, vyAxis+1)
  383. for r, row := range xlsx.SheetData.Row {
  384. for k, c := range row.C {
  385. if checkCellInArea(c.R, hcell+":"+vcell) {
  386. xlsx.SheetData.Row[r].C[k].S = styleID
  387. }
  388. }
  389. }
  390. }
  391. // getPaletteColor provides function to convert the RBG color by given string.
  392. func getPaletteColor(color string) string {
  393. return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1)
  394. }