// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to // and read from XLSX / XLSM / XLTM files. Supports reading and writing // spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports // complex components by high compatibility, and provided streaming API for // generating or reading data from a worksheet with huge amounts of data. This // library needs Go version 1.15 or later. package excelize import ( "bytes" "encoding/json" "encoding/xml" "fmt" "io" "log" "math" "reflect" "regexp" "strconv" "strings" ) // Excel styles can reference number formats that are built-in, all of which // have an id less than 164. Note that this number format code list is under // English localization. var builtInNumFmt = map[int]string{ 0: "general", 1: "0", 2: "0.00", 3: "#,##0", 4: "#,##0.00", 9: "0%", 10: "0.00%", 11: "0.00e+00", 12: "# ?/?", 13: "# ??/??", 14: "mm-dd-yy", 15: "d-mmm-yy", 16: "d-mmm", 17: "mmm-yy", 18: "h:mm am/pm", 19: "h:mm:ss am/pm", 20: "hh:mm", 21: "hh:mm:ss", 22: "m/d/yy hh:mm", 37: "#,##0 ;(#,##0)", 38: "#,##0 ;[red](#,##0)", 39: "#,##0.00;(#,##0.00)", 40: "#,##0.00;[red](#,##0.00)", 41: `_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)`, 42: `_("$"* #,##0_);_("$* \(#,##0\);_("$"* "-"_);_(@_)`, 43: `_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)`, 44: `_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)`, 45: "mm:ss", 46: "[h]:mm:ss", 47: "mmss.0", 48: "##0.0e+0", 49: "@", } // langNumFmt defined number format code (with unicode values provided for // language glyphs where they occur) in different language. var langNumFmt = map[string]map[int]string{ "zh-tw": { 27: "[$-404]e/m/d", 28: `[$-404]e"年"m"月"d"日"`, 29: `[$-404]e"年"m"月"d"日"`, 30: "m/d/yy", 31: `yyyy"年"m"月"d"日"`, 32: `hh"時"mm"分"`, 33: `hh"時"mm"分"ss"秒"`, 34: `上午/下午 hh"時"mm"分"`, 35: `上午/下午 hh"時"mm"分"ss"秒"`, 36: "[$-404]e/m/d", 50: "[$-404]e/m/d", 51: `[$-404]e"年"m"月"d"日"`, 52: `上午/下午 hh"時"mm"分"`, 53: `上午/下午 hh"時"mm"分"ss"秒"`, 54: `[$-404]e"年"m"月"d"日"`, 55: `上午/下午 hh"時"mm"分"`, 56: `上午/下午 hh"時"mm"分"ss"秒"`, 57: "[$-404]e/m/d", 58: `[$-404]e"年"m"月"d"日"`, }, "zh-cn": { 27: `yyyy"年"m"月"`, 28: `m"月"d"日"`, 29: `m"月"d"日"`, 30: "m-d-yy", 31: `yyyy"年"m"月"d"日"`, 32: `h"时"mm"分"`, 33: `h"时"mm"分"ss"秒"`, 34: `上午/下午 h"时"mm"分"`, 35: `上午/下午 h"时"mm"分"ss"秒"`, 36: `yyyy"年"m"月"`, 50: `yyyy"年"m"月"`, 51: `m"月"d"日"`, 52: `yyyy"年"m"月"`, 53: `m"月"d"日"`, 54: `m"月"d"日"`, 55: `上午/下午 h"时"mm"分"`, 56: `上午/下午 h"时"mm"分"ss"秒"`, 57: `yyyy"年"m"月"`, 58: `m"月"d"日"`, }, "zh-tw_unicode": { 27: "[$-404]e/m/d", 28: `[$-404]e"5E74"m"6708"d"65E5"`, 29: `[$-404]e"5E74"m"6708"d"65E5"`, 30: "m/d/yy", 31: `yyyy"5E74"m"6708"d"65E5"`, 32: `hh"6642"mm"5206"`, 33: `hh"6642"mm"5206"ss"79D2"`, 34: `4E0A5348/4E0B5348hh"6642"mm"5206"`, 35: `4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2"`, 36: "[$-404]e/m/d", 50: "[$-404]e/m/d", 51: `[$-404]e"5E74"m"6708"d"65E5"`, 52: `4E0A5348/4E0B5348hh"6642"mm"5206"`, 53: `4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2"`, 54: `[$-404]e"5E74"m"6708"d"65E5"`, 55: `4E0A5348/4E0B5348hh"6642"mm"5206"`, 56: `4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2"`, 57: "[$-404]e/m/d", 58: `[$-404]e"5E74"m"6708"d"65E5"`, }, "zh-cn_unicode": { 27: `yyyy"5E74"m"6708"`, 28: `m"6708"d"65E5"`, 29: `m"6708"d"65E5"`, 30: "m-d-yy", 31: `yyyy"5E74"m"6708"d"65E5"`, 32: `h"65F6"mm"5206"`, 33: `h"65F6"mm"5206"ss"79D2"`, 34: `4E0A5348/4E0B5348h"65F6"mm"5206"`, 35: `4E0A5348/4E0B5348h"65F6"mm"5206"ss"79D2"`, 36: `yyyy"5E74"m"6708"`, 50: `yyyy"5E74"m"6708"`, 51: `m"6708"d"65E5"`, 52: `yyyy"5E74"m"6708"`, 53: `m"6708"d"65E5"`, 54: `m"6708"d"65E5"`, 55: `4E0A5348/4E0B5348h"65F6"mm"5206"`, 56: `4E0A5348/4E0B5348h"65F6"mm"5206"ss"79D2"`, 57: `yyyy"5E74"m"6708"`, 58: `m"6708"d"65E5"`, }, "ja-jp": { 27: "[$-411]ge.m.d", 28: `[$-411]ggge"年"m"月"d"日"`, 29: `[$-411]ggge"年"m"月"d"日"`, 30: "m/d/yy", 31: `yyyy"年"m"月"d"日"`, 32: `h"時"mm"分"`, 33: `h"時"mm"分"ss"秒"`, 34: `yyyy"年"m"月"`, 35: `m"月"d"日"`, 36: "[$-411]ge.m.d", 50: "[$-411]ge.m.d", 51: `[$-411]ggge"年"m"月"d"日"`, 52: `yyyy"年"m"月"`, 53: `m"月"d"日"`, 54: `[$-411]ggge"年"m"月"d"日"`, 55: `yyyy"年"m"月"`, 56: `m"月"d"日"`, 57: "[$-411]ge.m.d", 58: `[$-411]ggge"年"m"月"d"日"`, }, "ko-kr": { 27: `yyyy"年" mm"月" dd"日"`, 28: "mm-dd", 29: "mm-dd", 30: "mm-dd-yy", 31: `yyyy"년" mm"월" dd"일"`, 32: `h"시" mm"분"`, 33: `h"시" mm"분" ss"초"`, 34: `yyyy-mm-dd`, 35: `yyyy-mm-dd`, 36: `yyyy"年" mm"月" dd"日"`, 50: `yyyy"年" mm"月" dd"日"`, 51: "mm-dd", 52: "yyyy-mm-dd", 53: "yyyy-mm-dd", 54: "mm-dd", 55: "yyyy-mm-dd", 56: "yyyy-mm-dd", 57: `yyyy"年" mm"月" dd"日"`, 58: "mm-dd", }, "ja-jp_unicode": { 27: "[$-411]ge.m.d", 28: `[$-411]ggge"5E74"m"6708"d"65E5"`, 29: `[$-411]ggge"5E74"m"6708"d"65E5"`, 30: "m/d/yy", 31: `yyyy"5E74"m"6708"d"65E5"`, 32: `h"6642"mm"5206"`, 33: `h"6642"mm"5206"ss"79D2"`, 34: `yyyy"5E74"m"6708"`, 35: `m"6708"d"65E5"`, 36: "[$-411]ge.m.d", 50: "[$-411]ge.m.d", 51: `[$-411]ggge"5E74"m"6708"d"65E5"`, 52: `yyyy"5E74"m"6708"`, 53: `m"6708"d"65E5"`, 54: `[$-411]ggge"5E74"m"6708"d"65E5"`, 55: `yyyy"5E74"m"6708"`, 56: `m"6708"d"65E5"`, 57: "[$-411]ge.m.d", 58: `[$-411]ggge"5E74"m"6708"d"65E5"`, }, "ko-kr_unicode": { 27: `yyyy"5E74" mm"6708" dd"65E5"`, 28: "mm-dd", 29: "mm-dd", 30: "mm-dd-yy", 31: `yyyy"B144" mm"C6D4" dd"C77C"`, 32: `h"C2DC" mm"BD84"`, 33: `h"C2DC" mm"BD84" ss"CD08"`, 34: "yyyy-mm-dd", 35: "yyyy-mm-dd", 36: `yyyy"5E74" mm"6708" dd"65E5"`, 50: `yyyy"5E74" mm"6708" dd"65E5"`, 51: "mm-dd", 52: "yyyy-mm-dd", 53: "yyyy-mm-dd", 54: "mm-dd", 55: "yyyy-mm-dd", 56: "yyyy-mm-dd", 57: `yyyy"5E74" mm"6708" dd"65E5"`, 58: "mm-dd", }, "th-th": { 59: "t0", 60: "t0.00", 61: "t#,##0", 62: "t#,##0.00", 67: "t0%", 68: "t0.00%", 69: "t# ?/?", 70: "t# ??/??", 71: "ว/ด/ปปปป", 72: "ว-ดดด-ปป", 73: "ว-ดดด", 74: "ดดด-ปป", 75: "ช:นน", 76: "ช:นน:ทท", 77: "ว/ด/ปปปป ช:นน", 78: "นน:ทท", 79: "[ช]:นน:ทท", 80: "นน:ทท.0", 81: "d/m/bb", }, "th-th_unicode": { 59: "t0", 60: "t0.00", 61: "t#,##0", 62: "t#,##0.00", 67: "t0%", 68: "t0.00%", 69: "t# ?/?", 70: "t# ??/??", 71: "0E27/0E14/0E1B0E1B0E1B0E1B", 72: "0E27-0E140E140E14-0E1B0E1B", 73: "0E27-0E140E140E14", 74: "0E140E140E14-0E1B0E1B", 75: "0E0A:0E190E19", 76: "0E0A:0E190E19:0E170E17", 77: "0E27/0E14/0E1B0E1B0E1B0E1B 0E0A:0E190E19", 78: "0E190E19:0E170E17", 79: "[0E0A]:0E190E19:0E170E17", 80: "0E190E19:0E170E17.0", 81: "d/m/bb", }, } // currencyNumFmt defined the currency number format map. var currencyNumFmt = map[int]string{ 164: `"CN¥",##0.00`, 165: "[$$-409]#,##0.00", 166: "[$$-45C]#,##0.00", 167: "[$$-1004]#,##0.00", 168: "[$$-404]#,##0.00", 169: "[$$-C09]#,##0.00", 170: "[$$-2809]#,##0.00", 171: "[$$-1009]#,##0.00", 172: "[$$-2009]#,##0.00", 173: "[$$-1409]#,##0.00", 174: "[$$-4809]#,##0.00", 175: "[$$-2C09]#,##0.00", 176: "[$$-2409]#,##0.00", 177: "[$$-1000]#,##0.00", 178: `#,##0.00\ [$$-C0C]`, 179: "[$$-475]#,##0.00", 180: "[$$-83E]#,##0.00", 181: `[$$-86B]\ #,##0.00`, 182: `[$$-340A]\ #,##0.00`, 183: "[$$-240A]#,##0.00", 184: `[$$-300A]\ #,##0.00`, 185: "[$$-440A]#,##0.00", 186: "[$$-80A]#,##0.00", 187: "[$$-500A]#,##0.00", 188: "[$$-540A]#,##0.00", 189: `[$$-380A]\ #,##0.00`, 190: "[$£-809]#,##0.00", 191: "[$£-491]#,##0.00", 192: "[$£-452]#,##0.00", 193: "[$¥-804]#,##0.00", 194: "[$¥-411]#,##0.00", 195: "[$¥-478]#,##0.00", 196: "[$¥-451]#,##0.00", 197: "[$¥-480]#,##0.00", 198: "#,##0.00\\ [$\u058F-42B]", 199: "[$\u060B-463]#,##0.00", 200: "[$\u060B-48C]#,##0.00", 201: "[$\u09F3-845]\\ #,##0.00", 202: "#,##0.00[$\u17DB-453]", 203: "[$\u20A1-140A]#,##0.00", 204: "[$\u20A6-468]\\ #,##0.00", 205: "[$\u20A6-470]\\ #,##0.00", 206: "[$\u20A9-412]#,##0.00", 207: "[$\u20AA-40D]\\ #,##0.00", 208: "#,##0.00\\ [$\u20AB-42A]", 209: "#,##0.00\\ [$\u20AC-42D]", 210: "#,##0.00\\ [$\u20AC-47E]", 211: "#,##0.00\\ [$\u20AC-403]", 212: "#,##0.00\\ [$\u20AC-483]", 213: "[$\u20AC-813]\\ #,##0.00", 214: "[$\u20AC-413]\\ #,##0.00", 215: "[$\u20AC-1809]#,##0.00", 216: "#,##0.00\\ [$\u20AC-425]", 217: "[$\u20AC-2]\\ #,##0.00", 218: "#,##0.00\\ [$\u20AC-1]", 219: "#,##0.00\\ [$\u20AC-40B]", 220: "#,##0.00\\ [$\u20AC-80C]", 221: "#,##0.00\\ [$\u20AC-40C]", 222: "#,##0.00\\ [$\u20AC-140C]", 223: "#,##0.00\\ [$\u20AC-180C]", 224: "[$\u20AC-200C]#,##0.00", 225: "#,##0.00\\ [$\u20AC-456]", 226: "#,##0.00\\ [$\u20AC-C07]", 227: "#,##0.00\\ [$\u20AC-407]", 228: "#,##0.00\\ [$\u20AC-1007]", 229: "#,##0.00\\ [$\u20AC-408]", 230: "#,##0.00\\ [$\u20AC-243B]", 231: "[$\u20AC-83C]#,##0.00", 232: "[$\u20AC-410]\\ #,##0.00", 233: "[$\u20AC-476]#,##0.00", 234: "#,##0.00\\ [$\u20AC-2C1A]", 235: "[$\u20AC-426]\\ #,##0.00", 236: "#,##0.00\\ [$\u20AC-427]", 237: "#,##0.00\\ [$\u20AC-82E]", 238: "#,##0.00\\ [$\u20AC-46E]", 239: "[$\u20AC-43A]#,##0.00", 240: "#,##0.00\\ [$\u20AC-C3B]", 241: "#,##0.00\\ [$\u20AC-482]", 242: "#,##0.00\\ [$\u20AC-816]", 243: "#,##0.00\\ [$\u20AC-301A]", 244: "#,##0.00\\ [$\u20AC-203B]", 245: "#,##0.00\\ [$\u20AC-41B]", 246: "#,##0.00\\ [$\u20AC-424]", 247: "#,##0.00\\ [$\u20AC-C0A]", 248: "#,##0.00\\ [$\u20AC-81D]", 249: "#,##0.00\\ [$\u20AC-484]", 250: "#,##0.00\\ [$\u20AC-42E]", 251: "[$\u20AC-462]\\ #,##0.00", 252: "#,##0.00\\ [$₭-454]", 253: "#,##0.00\\ [$₮-450]", 254: "[$\u20AE-C50]#,##0.00", 255: "[$\u20B1-3409]#,##0.00", 256: "[$\u20B1-464]#,##0.00", 257: "#,##0.00[$\u20B4-422]", 258: "[$\u20B8-43F]#,##0.00", 259: "[$\u20B9-460]#,##0.00", 260: "[$\u20B9-4009]\\ #,##0.00", 261: "[$\u20B9-447]\\ #,##0.00", 262: "[$\u20B9-439]\\ #,##0.00", 263: "[$\u20B9-44B]\\ #,##0.00", 264: "[$\u20B9-860]#,##0.00", 265: "[$\u20B9-457]\\ #,##0.00", 266: "[$\u20B9-458]#,##0.00", 267: "[$\u20B9-44E]\\ #,##0.00", 268: "[$\u20B9-861]#,##0.00", 269: "[$\u20B9-448]\\ #,##0.00", 270: "[$\u20B9-446]\\ #,##0.00", 271: "[$\u20B9-44F]\\ #,##0.00", 272: "[$\u20B9-459]#,##0.00", 273: "[$\u20B9-449]\\ #,##0.00", 274: "[$\u20B9-820]#,##0.00", 275: "#,##0.00\\ [$\u20BA-41F]", 276: "#,##0.00\\ [$\u20BC-42C]", 277: "#,##0.00\\ [$\u20BC-82C]", 278: "#,##0.00\\ [$\u20BD-419]", 279: "#,##0.00[$\u20BD-485]", 280: "#,##0.00\\ [$\u20BE-437]", 281: "[$B/.-180A]\\ #,##0.00", 282: "[$Br-472]#,##0.00", 283: "[$Br-477]#,##0.00", 284: "#,##0.00[$Br-473]", 285: "[$Bs-46B]\\ #,##0.00", 286: "[$Bs-400A]\\ #,##0.00", 287: "[$Bs.-200A]\\ #,##0.00", 288: "[$BWP-832]\\ #,##0.00", 289: "[$C$-4C0A]#,##0.00", 290: "[$CA$-85D]#,##0.00", 291: "[$CA$-47C]#,##0.00", 292: "[$CA$-45D]#,##0.00", 293: "[$CFA-340C]#,##0.00", 294: "[$CFA-280C]#,##0.00", 295: "#,##0.00\\ [$CFA-867]", 296: "#,##0.00\\ [$CFA-488]", 297: "#,##0.00\\ [$CHF-100C]", 298: "[$CHF-1407]\\ #,##0.00", 299: "[$CHF-807]\\ #,##0.00", 300: "[$CHF-810]\\ #,##0.00", 301: "[$CHF-417]\\ #,##0.00", 302: "[$CLP-47A]\\ #,##0.00", 303: "[$CN¥-850]#,##0.00", 304: "#,##0.00\\ [$DZD-85F]", 305: "[$FCFA-2C0C]#,##0.00", 306: "#,##0.00\\ [$Ft-40E]", 307: "[$G-3C0C]#,##0.00", 308: "[$Gs.-3C0A]\\ #,##0.00", 309: "[$GTQ-486]#,##0.00", 310: "[$HK$-C04]#,##0.00", 311: "[$HK$-3C09]#,##0.00", 312: "#,##0.00\\ [$HRK-41A]", 313: "[$IDR-3809]#,##0.00", 314: "[$IQD-492]#,##0.00", 315: "#,##0.00\\ [$ISK-40F]", 316: "[$K-455]#,##0.00", 317: "#,##0.00\\ [$K\u010D-405]", 318: "#,##0.00\\ [$KM-141A]", 319: "#,##0.00\\ [$KM-101A]", 320: "#,##0.00\\ [$KM-181A]", 321: "[$kr-438]\\ #,##0.00", 322: "[$kr-43B]\\ #,##0.00", 323: "#,##0.00\\ [$kr-83B]", 324: "[$kr-414]\\ #,##0.00", 325: "[$kr-814]\\ #,##0.00", 326: "#,##0.00\\ [$kr-41D]", 327: "[$kr.-406]\\ #,##0.00", 328: "[$kr.-46F]\\ #,##0.00", 329: "[$Ksh-441]#,##0.00", 330: "[$L-818]#,##0.00", 331: "[$L-819]#,##0.00", 332: "[$L-480A]\\ #,##0.00", 333: "#,##0.00\\ [$Lek\u00EB-41C]", 334: "[$MAD-45F]#,##0.00", 335: "[$MAD-380C]#,##0.00", 336: "#,##0.00\\ [$MAD-105F]", 337: "[$MOP$-1404]#,##0.00", 338: "#,##0.00\\ [$MVR-465]_-", 339: "#,##0.00[$Nfk-873]", 340: "[$NGN-466]#,##0.00", 341: "[$NGN-467]#,##0.00", 342: "[$NGN-469]#,##0.00", 343: "[$NGN-471]#,##0.00", 344: "[$NOK-103B]\\ #,##0.00", 345: "[$NOK-183B]\\ #,##0.00", 346: "[$NZ$-481]#,##0.00", 347: "[$PKR-859]\\ #,##0.00", 348: "[$PYG-474]#,##0.00", 349: "[$Q-100A]#,##0.00", 350: "[$R-436]\\ #,##0.00", 351: "[$R-1C09]\\ #,##0.00", 352: "[$R-435]\\ #,##0.00", 353: "[$R$-416]\\ #,##0.00", 354: "[$RD$-1C0A]#,##0.00", 355: "#,##0.00\\ [$RF-487]", 356: "[$RM-4409]#,##0.00", 357: "[$RM-43E]#,##0.00", 358: "#,##0.00\\ [$RON-418]", 359: "[$Rp-421]#,##0.00", 360: "[$Rs-420]#,##0.00_-", 361: "[$Rs.-849]\\ #,##0.00", 362: "#,##0.00\\ [$RSD-81A]", 363: "#,##0.00\\ [$RSD-C1A]", 364: "#,##0.00\\ [$RUB-46D]", 365: "#,##0.00\\ [$RUB-444]", 366: "[$S/.-C6B]\\ #,##0.00", 367: "[$S/.-280A]\\ #,##0.00", 368: "#,##0.00\\ [$SEK-143B]", 369: "#,##0.00\\ [$SEK-1C3B]", 370: "#,##0.00\\ [$so\u02BBm-443]", 371: "#,##0.00\\ [$so\u02BBm-843]", 372: "#,##0.00\\ [$SYP-45A]", 373: "[$THB-41E]#,##0.00", 374: "#,##0.00[$TMT-442]", 375: "[$US$-3009]#,##0.00", 376: "[$ZAR-46C]\\ #,##0.00", 377: "[$ZAR-430]#,##0.00", 378: "[$ZAR-431]#,##0.00", 379: "[$ZAR-432]\\ #,##0.00", 380: "[$ZAR-433]#,##0.00", 381: "[$ZAR-434]\\ #,##0.00", 382: "#,##0.00\\ [$z\u0142-415]", 383: "#,##0.00\\ [$\u0434\u0435\u043D-42F]", 384: "#,##0.00\\ [$КМ-201A]", 385: "#,##0.00\\ [$КМ-1C1A]", 386: "#,##0.00\\ [$\u043B\u0432.-402]", 387: "#,##0.00\\ [$р.-423]", 388: "#,##0.00\\ [$\u0441\u043E\u043C-440]", 389: "#,##0.00\\ [$\u0441\u043E\u043C-428]", 390: "[$\u062C.\u0645.-C01]\\ #,##0.00_-", 391: "[$\u062F.\u0623.-2C01]\\ #,##0.00_-", 392: "[$\u062F.\u0625.-3801]\\ #,##0.00_-", 393: "[$\u062F.\u0628.-3C01]\\ #,##0.00_-", 394: "[$\u062F.\u062A.-1C01]\\ #,##0.00_-", 395: "[$\u062F.\u062C.-1401]\\ #,##0.00_-", 396: "[$\u062F.\u0639.-801]\\ #,##0.00_-", 397: "[$\u062F.\u0643.-3401]\\ #,##0.00_-", 398: "[$\u062F.\u0644.-1001]#,##0.00_-", 399: "[$\u062F.\u0645.-1801]\\ #,##0.00_-", 400: "[$\u0631-846]\\ #,##0.00", 401: "[$\u0631.\u0633.-401]\\ #,##0.00_-", 402: "[$\u0631.\u0639.-2001]\\ #,##0.00_-", 403: "[$\u0631.\u0642.-4001]\\ #,##0.00_-", 404: "[$\u0631.\u064A.-2401]\\ #,##0.00_-", 405: "[$\u0631\u06CC\u0627\u0644-429]#,##0.00_-", 406: "[$\u0644.\u0633.-2801]\\ #,##0.00_-", 407: "[$\u0644.\u0644.-3001]\\ #,##0.00_-", 408: "[$\u1265\u122D-45E]#,##0.00", 409: "[$\u0930\u0942-461]#,##0.00", 410: "[$\u0DBB\u0DD4.-45B]\\ #,##0.00", 411: "[$ADP]\\ #,##0.00", 412: "[$AED]\\ #,##0.00", 413: "[$AFA]\\ #,##0.00", 414: "[$AFN]\\ #,##0.00", 415: "[$ALL]\\ #,##0.00", 416: "[$AMD]\\ #,##0.00", 417: "[$ANG]\\ #,##0.00", 418: "[$AOA]\\ #,##0.00", 419: "[$ARS]\\ #,##0.00", 420: "[$ATS]\\ #,##0.00", 421: "[$AUD]\\ #,##0.00", 422: "[$AWG]\\ #,##0.00", 423: "[$AZM]\\ #,##0.00", 424: "[$AZN]\\ #,##0.00", 425: "[$BAM]\\ #,##0.00", 426: "[$BBD]\\ #,##0.00", 427: "[$BDT]\\ #,##0.00", 428: "[$BEF]\\ #,##0.00", 429: "[$BGL]\\ #,##0.00", 430: "[$BGN]\\ #,##0.00", 431: "[$BHD]\\ #,##0.00", 432: "[$BIF]\\ #,##0.00", 433: "[$BMD]\\ #,##0.00", 434: "[$BND]\\ #,##0.00", 435: "[$BOB]\\ #,##0.00", 436: "[$BOV]\\ #,##0.00", 437: "[$BRL]\\ #,##0.00", 438: "[$BSD]\\ #,##0.00", 439: "[$BTN]\\ #,##0.00", 440: "[$BWP]\\ #,##0.00", 441: "[$BYR]\\ #,##0.00", 442: "[$BZD]\\ #,##0.00", 443: "[$CAD]\\ #,##0.00", 444: "[$CDF]\\ #,##0.00", 445: "[$CHE]\\ #,##0.00", 446: "[$CHF]\\ #,##0.00", 447: "[$CHW]\\ #,##0.00", 448: "[$CLF]\\ #,##0.00", 449: "[$CLP]\\ #,##0.00", 450: "[$CNY]\\ #,##0.00", 451: "[$COP]\\ #,##0.00", 452: "[$COU]\\ #,##0.00", 453: "[$CRC]\\ #,##0.00", 454: "[$CSD]\\ #,##0.00", 455: "[$CUC]\\ #,##0.00", 456: "[$CVE]\\ #,##0.00", 457: "[$CYP]\\ #,##0.00", 458: "[$CZK]\\ #,##0.00", 459: "[$DEM]\\ #,##0.00", 460: "[$DJF]\\ #,##0.00", 461: "[$DKK]\\ #,##0.00", 462: "[$DOP]\\ #,##0.00", 463: "[$DZD]\\ #,##0.00", 464: "[$ECS]\\ #,##0.00", 465: "[$ECV]\\ #,##0.00", 466: "[$EEK]\\ #,##0.00", 467: "[$EGP]\\ #,##0.00", 468: "[$ERN]\\ #,##0.00", 469: "[$ESP]\\ #,##0.00", 470: "[$ETB]\\ #,##0.00", 471: "[$EUR]\\ #,##0.00", 472: "[$FIM]\\ #,##0.00", 473: "[$FJD]\\ #,##0.00", 474: "[$FKP]\\ #,##0.00", 475: "[$FRF]\\ #,##0.00", 476: "[$GBP]\\ #,##0.00", 477: "[$GEL]\\ #,##0.00", 478: "[$GHC]\\ #,##0.00", 479: "[$GHS]\\ #,##0.00", 480: "[$GIP]\\ #,##0.00", 481: "[$GMD]\\ #,##0.00", 482: "[$GNF]\\ #,##0.00", 483: "[$GRD]\\ #,##0.00", 484: "[$GTQ]\\ #,##0.00", 485: "[$GYD]\\ #,##0.00", 486: "[$HKD]\\ #,##0.00", 487: "[$HNL]\\ #,##0.00", 488: "[$HRK]\\ #,##0.00", 489: "[$HTG]\\ #,##0.00", 490: "[$HUF]\\ #,##0.00", 491: "[$IDR]\\ #,##0.00", 492: "[$IEP]\\ #,##0.00", 493: "[$ILS]\\ #,##0.00", 494: "[$INR]\\ #,##0.00", 495: "[$IQD]\\ #,##0.00", 496: "[$IRR]\\ #,##0.00", 497: "[$ISK]\\ #,##0.00", 498: "[$ITL]\\ #,##0.00", 499: "[$JMD]\\ #,##0.00", 500: "[$JOD]\\ #,##0.00", 501: "[$JPY]\\ #,##0.00", 502: "[$KAF]\\ #,##0.00", 503: "[$KES]\\ #,##0.00", 504: "[$KGS]\\ #,##0.00", 505: "[$KHR]\\ #,##0.00", 506: "[$KMF]\\ #,##0.00", 507: "[$KPW]\\ #,##0.00", 508: "[$KRW]\\ #,##0.00", 509: "[$KWD]\\ #,##0.00", 510: "[$KYD]\\ #,##0.00", 511: "[$KZT]\\ #,##0.00", 512: "[$LAK]\\ #,##0.00", 513: "[$LBP]\\ #,##0.00", 514: "[$LKR]\\ #,##0.00", 515: "[$LRD]\\ #,##0.00", 516: "[$LSL]\\ #,##0.00", 517: "[$LTL]\\ #,##0.00", 518: "[$LUF]\\ #,##0.00", 519: "[$LVL]\\ #,##0.00", 520: "[$LYD]\\ #,##0.00", 521: "[$MAD]\\ #,##0.00", 522: "[$MDL]\\ #,##0.00", 523: "[$MGA]\\ #,##0.00", 524: "[$MGF]\\ #,##0.00", 525: "[$MKD]\\ #,##0.00", 526: "[$MMK]\\ #,##0.00", 527: "[$MNT]\\ #,##0.00", 528: "[$MOP]\\ #,##0.00", 529: "[$MRO]\\ #,##0.00", 530: "[$MTL]\\ #,##0.00", 531: "[$MUR]\\ #,##0.00", 532: "[$MVR]\\ #,##0.00", 533: "[$MWK]\\ #,##0.00", 534: "[$MXN]\\ #,##0.00", 535: "[$MXV]\\ #,##0.00", 536: "[$MYR]\\ #,##0.00", 537: "[$MZM]\\ #,##0.00", 538: "[$MZN]\\ #,##0.00", 539: "[$NAD]\\ #,##0.00", 540: "[$NGN]\\ #,##0.00", 541: "[$NIO]\\ #,##0.00", 542: "[$NLG]\\ #,##0.00", 543: "[$NOK]\\ #,##0.00", 544: "[$NPR]\\ #,##0.00", 545: "[$NTD]\\ #,##0.00", 546: "[$NZD]\\ #,##0.00", 547: "[$OMR]\\ #,##0.00", 548: "[$PAB]\\ #,##0.00", 549: "[$PEN]\\ #,##0.00", 550: "[$PGK]\\ #,##0.00", 551: "[$PHP]\\ #,##0.00", 552: "[$PKR]\\ #,##0.00", 553: "[$PLN]\\ #,##0.00", 554: "[$PTE]\\ #,##0.00", 555: "[$PYG]\\ #,##0.00", 556: "[$QAR]\\ #,##0.00", 557: "[$ROL]\\ #,##0.00", 558: "[$RON]\\ #,##0.00", 559: "[$RSD]\\ #,##0.00", 560: "[$RUB]\\ #,##0.00", 561: "[$RUR]\\ #,##0.00", 562: "[$RWF]\\ #,##0.00", 563: "[$SAR]\\ #,##0.00", 564: "[$SBD]\\ #,##0.00", 565: "[$SCR]\\ #,##0.00", 566: "[$SDD]\\ #,##0.00", 567: "[$SDG]\\ #,##0.00", 568: "[$SDP]\\ #,##0.00", 569: "[$SEK]\\ #,##0.00", 570: "[$SGD]\\ #,##0.00", 571: "[$SHP]\\ #,##0.00", 572: "[$SIT]\\ #,##0.00", 573: "[$SKK]\\ #,##0.00", 574: "[$SLL]\\ #,##0.00", 575: "[$SOS]\\ #,##0.00", 576: "[$SPL]\\ #,##0.00", 577: "[$SRD]\\ #,##0.00", 578: "[$SRG]\\ #,##0.00", 579: "[$STD]\\ #,##0.00", 580: "[$SVC]\\ #,##0.00", 581: "[$SYP]\\ #,##0.00", 582: "[$SZL]\\ #,##0.00", 583: "[$THB]\\ #,##0.00", 584: "[$TJR]\\ #,##0.00", 585: "[$TJS]\\ #,##0.00", 586: "[$TMM]\\ #,##0.00", 587: "[$TMT]\\ #,##0.00", 588: "[$TND]\\ #,##0.00", 589: "[$TOP]\\ #,##0.00", 590: "[$TRL]\\ #,##0.00", 591: "[$TRY]\\ #,##0.00", 592: "[$TTD]\\ #,##0.00", 593: "[$TWD]\\ #,##0.00", 594: "[$TZS]\\ #,##0.00", 595: "[$UAH]\\ #,##0.00", 596: "[$UGX]\\ #,##0.00", 597: "[$USD]\\ #,##0.00", 598: "[$USN]\\ #,##0.00", 599: "[$USS]\\ #,##0.00", 600: "[$UYI]\\ #,##0.00", 601: "[$UYU]\\ #,##0.00", 602: "[$UZS]\\ #,##0.00", 603: "[$VEB]\\ #,##0.00", 604: "[$VEF]\\ #,##0.00", 605: "[$VND]\\ #,##0.00", 606: "[$VUV]\\ #,##0.00", 607: "[$WST]\\ #,##0.00", 608: "[$XAF]\\ #,##0.00", 609: "[$XAG]\\ #,##0.00", 610: "[$XAU]\\ #,##0.00", 611: "[$XB5]\\ #,##0.00", 612: "[$XBA]\\ #,##0.00", 613: "[$XBB]\\ #,##0.00", 614: "[$XBC]\\ #,##0.00", 615: "[$XBD]\\ #,##0.00", 616: "[$XCD]\\ #,##0.00", 617: "[$XDR]\\ #,##0.00", 618: "[$XFO]\\ #,##0.00", 619: "[$XFU]\\ #,##0.00", 620: "[$XOF]\\ #,##0.00", 621: "[$XPD]\\ #,##0.00", 622: "[$XPF]\\ #,##0.00", 623: "[$XPT]\\ #,##0.00", 624: "[$XTS]\\ #,##0.00", 625: "[$XXX]\\ #,##0.00", 626: "[$YER]\\ #,##0.00", 627: "[$YUM]\\ #,##0.00", 628: "[$ZAR]\\ #,##0.00", 629: "[$ZMK]\\ #,##0.00", 630: "[$ZMW]\\ #,##0.00", 631: "[$ZWD]\\ #,##0.00", 632: "[$ZWL]\\ #,##0.00", 633: "[$ZWN]\\ #,##0.00", 634: "[$ZWR]\\ #,##0.00", } // builtInNumFmtFunc defined the format conversion functions map. Partial format // code doesn't support currently and will return original string. var builtInNumFmtFunc = map[int]func(v string, format string) string{ 0: formatToString, 1: formatToInt, 2: formatToFloat, 3: formatToInt, 4: formatToFloat, 9: formatToC, 10: formatToD, 11: formatToE, 12: formatToString, // Doesn't support currently 13: formatToString, // Doesn't support currently 14: parseTime, 15: parseTime, 16: parseTime, 17: parseTime, 18: parseTime, 19: parseTime, 20: parseTime, 21: parseTime, 22: parseTime, 37: formatToA, 38: formatToA, 39: formatToB, 40: formatToB, 41: formatToString, // Doesn't support currently 42: formatToString, // Doesn't support currently 43: formatToString, // Doesn't support currently 44: formatToString, // Doesn't support currently 45: parseTime, 46: parseTime, 47: parseTime, 48: formatToE, 49: formatToString, } // validType defined the list of valid validation types. var validType = map[string]string{ "cell": "cellIs", "date": "date", // Doesn't support currently "time": "time", // Doesn't support currently "average": "aboveAverage", "duplicate": "duplicateValues", "unique": "uniqueValues", "top": "top10", "bottom": "top10", "text": "text", // Doesn't support currently "time_period": "timePeriod", // Doesn't support currently "blanks": "containsBlanks", // Doesn't support currently "no_blanks": "notContainsBlanks", // Doesn't support currently "errors": "containsErrors", // Doesn't support currently "no_errors": "notContainsErrors", // Doesn't support currently "2_color_scale": "2_color_scale", "3_color_scale": "3_color_scale", "data_bar": "dataBar", "formula": "expression", } // criteriaType defined the list of valid criteria types. var criteriaType = map[string]string{ "between": "between", "not between": "notBetween", "equal to": "equal", "=": "equal", "==": "equal", "not equal to": "notEqual", "!=": "notEqual", "<>": "notEqual", "greater than": "greaterThan", ">": "greaterThan", "less than": "lessThan", "<": "lessThan", "greater than or equal to": "greaterThanOrEqual", ">=": "greaterThanOrEqual", "less than or equal to": "lessThanOrEqual", "<=": "lessThanOrEqual", "containing": "containsText", "not containing": "notContains", "begins with": "beginsWith", "ends with": "endsWith", "yesterday": "yesterday", "today": "today", "last 7 days": "last7Days", "last week": "lastWeek", "this week": "thisWeek", "continue week": "continueWeek", "last month": "lastMonth", "this month": "thisMonth", "continue month": "continueMonth", } // formatToString provides a function to return original string by given // built-in number formats code and cell string. func formatToString(v string, format string) string { return v } // formatToInt provides a function to convert original string to integer // format as string type by given built-in number formats code and cell // string. func formatToInt(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } return fmt.Sprintf("%d", int64(f)) } // formatToFloat provides a function to convert original string to float // format as string type by given built-in number formats code and cell // string. func formatToFloat(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } return fmt.Sprintf("%.2f", f) } // formatToA provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToA(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } if f < 0 { t := int(math.Abs(f)) return fmt.Sprintf("(%d)", t) } t := int(f) return fmt.Sprintf("%d", t) } // formatToB provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToB(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } if f < 0 { return fmt.Sprintf("(%.2f)", f) } return fmt.Sprintf("%.2f", f) } // formatToC provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToC(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } f = f * 100 return fmt.Sprintf("%.f%%", f) } // formatToD provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToD(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } f = f * 100 return fmt.Sprintf("%.2f%%", f) } // formatToE provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToE(v string, format string) string { f, err := strconv.ParseFloat(v, 64) if err != nil { return v } return fmt.Sprintf("%.2E", f) } // parseTime provides a function to returns a string parsed using time.Time. // Replace Excel placeholders with Go time placeholders. For example, replace // yyyy with 2006. These are in a specific order, due to the fact that m is // used in month, minute, and am/pm. It would be easier to fix that with // regular expressions, but if it's possible to keep this simple it would be // easier to maintain. Full-length month and days (e.g. March, Tuesday) have // letters in them that would be replaced by other characters below (such as // the 'h' in March, or the 'd' in Tuesday) below. First we convert them to // arbitrary characters unused in Excel Date formats, and then at the end, // turn them to what they should actually be. Based off: // http://www.ozgrid.com/Excel/CustomFormats.htm func parseTime(v string, format string) string { var ( f float64 err error goFmt string ) f, err = strconv.ParseFloat(v, 64) if err != nil { return v } val := timeFromExcelTime(f, false) if format == "" { return v } goFmt = format if strings.Contains(goFmt, "[") { var re = regexp.MustCompile(`\[.+\]`) goFmt = re.ReplaceAllLiteralString(goFmt, "") } // use only first variant if strings.Contains(goFmt, ";") { goFmt = goFmt[:strings.IndexByte(goFmt, ';')] } replacements := []struct{ xltime, gotime string }{ {"YYYY", "2006"}, {"YY", "06"}, {"MM", "01"}, {"M", "1"}, {"DD", "02"}, {"D", "2"}, {"yyyy", "2006"}, {"yy", "06"}, {"MMMM", "%%%%"}, {"mmmm", "%%%%"}, {"DDDD", "&&&&"}, {"dddd", "&&&&"}, {"DD", "02"}, {"dd", "02"}, {"D", "2"}, {"d", "2"}, {"MMM", "Jan"}, {"mmm", "Jan"}, {"MMSS", "0405"}, {"mmss", "0405"}, {"SS", "05"}, {"ss", "05"}, {"s", "5"}, {"MM:", "04:"}, {"mm:", "04:"}, {":MM", ":04"}, {":mm", ":04"}, {"m:", "4:"}, {":m", ":4"}, {"MM", "01"}, {"mm", "01"}, {"AM/PM", "PM"}, {"am/pm", "PM"}, {"M/", "1/"}, {"m/", "1/"}, {"%%%%", "January"}, {"&&&&", "Monday"}, } replacementsGlobal := []struct{ xltime, gotime string }{ {"\\-", "-"}, {"\\ ", " "}, {"\\.", "."}, {"\\", ""}, {"\"", ""}, } // It is the presence of the "am/pm" indicator that determines if this is // a 12 hour or 24 hours time format, not the number of 'h' characters. var padding bool if val.Hour() == 0 && !strings.Contains(format, "hh") && !strings.Contains(format, "HH") { padding = true } if is12HourTime(format) { goFmt = strings.Replace(goFmt, "hh", "3", 1) goFmt = strings.Replace(goFmt, "h", "3", 1) goFmt = strings.Replace(goFmt, "HH", "3", 1) goFmt = strings.Replace(goFmt, "H", "3", 1) } else { goFmt = strings.Replace(goFmt, "hh", "15", 1) goFmt = strings.Replace(goFmt, "HH", "15", 1) if 0 < val.Hour() && val.Hour() < 12 { goFmt = strings.Replace(goFmt, "h", "3", 1) goFmt = strings.Replace(goFmt, "H", "3", 1) } else { goFmt = strings.Replace(goFmt, "h", "15", 1) goFmt = strings.Replace(goFmt, "H", "15", 1) } } for _, repl := range replacements { goFmt = strings.Replace(goFmt, repl.xltime, repl.gotime, 1) } for _, repl := range replacementsGlobal { goFmt = strings.Replace(goFmt, repl.xltime, repl.gotime, -1) } // If the hour is optional, strip it out, along with the possible dangling // colon that would remain. if val.Hour() < 1 { goFmt = strings.Replace(goFmt, "]:", "]", 1) goFmt = strings.Replace(goFmt, "[03]", "", 1) goFmt = strings.Replace(goFmt, "[3]", "", 1) goFmt = strings.Replace(goFmt, "[15]", "", 1) } else { goFmt = strings.Replace(goFmt, "[3]", "3", 1) goFmt = strings.Replace(goFmt, "[15]", "15", 1) } s := val.Format(goFmt) if padding { s = strings.Replace(s, "00:", "0:", 1) } return s } // is12HourTime checks whether an Excel time format string is a 12 hours form. func is12HourTime(format string) bool { return strings.Contains(format, "am/pm") || strings.Contains(format, "AM/PM") || strings.Contains(format, "a/p") || strings.Contains(format, "A/P") } // stylesReader provides a function to get the pointer to the structure after // deserialization of xl/styles.xml. func (f *File) stylesReader() *xlsxStyleSheet { var err error if f.Styles == nil { f.Styles = new(xlsxStyleSheet) if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))). Decode(f.Styles); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } return f.Styles } // styleSheetWriter provides a function to save xl/styles.xml after serialize // structure. func (f *File) styleSheetWriter() { if f.Styles != nil { output, _ := xml.Marshal(f.Styles) f.saveFileList("xl/styles.xml", f.replaceNameSpaceBytes("xl/styles.xml", output)) } } // sharedStringsWriter provides a function to save xl/sharedStrings.xml after // serialize structure. func (f *File) sharedStringsWriter() { if f.SharedStrings != nil { output, _ := xml.Marshal(f.SharedStrings) f.saveFileList("xl/sharedStrings.xml", f.replaceNameSpaceBytes("xl/sharedStrings.xml", output)) } } // parseFormatStyleSet provides a function to parse the format settings of the // cells and conditional formats. func parseFormatStyleSet(style interface{}) (*Style, error) { fs := Style{} var err error switch v := style.(type) { case string: err = json.Unmarshal([]byte(v), &fs) case *Style: fs = *v default: err = ErrParameterInvalid } if fs.Font != nil { if len(fs.Font.Family) > MaxFontFamilyLength { return &fs, ErrFontLength } if fs.Font.Size > MaxFontSize { return &fs, ErrFontSize } } if fs.CustomNumFmt != nil && len(*fs.CustomNumFmt) == 0 { err = ErrCustomNumFmt } return &fs, err } // NewStyle provides a function to create the style for cells by given JSON or // structure pointer. Note that the color field uses RGB color code. // // The following shows the border styles sorted by excelize index number: // // Index | Name | Weight | Style // -------+---------------+--------+------------- // 0 | None | 0 | // 1 | Continuous | 1 | ----------- // 2 | Continuous | 2 | ----------- // 3 | Dash | 1 | - - - - - - // 4 | Dot | 1 | . . . . . . // 5 | Continuous | 3 | ----------- // 6 | Double | 3 | =========== // 7 | Continuous | 0 | ----------- // 8 | Dash | 2 | - - - - - - // 9 | Dash Dot | 1 | - . - . - . // 10 | Dash Dot | 2 | - . - . - . // 11 | Dash Dot Dot | 1 | - . . - . . // 12 | Dash Dot Dot | 2 | - . . - . . // 13 | SlantDash Dot | 2 | / - . / - . // // The following shows the borders in the order shown in the Excel dialog: // // Index | Style | Index | Style // -------+-------------+-------+------------- // 0 | None | 12 | - . . - . . // 7 | ----------- | 13 | / - . / - . // 4 | . . . . . . | 10 | - . - . - . // 11 | - . . - . . | 8 | - - - - - - // 9 | - . - . - . | 2 | ----------- // 3 | - - - - - - | 5 | ----------- // 1 | ----------- | 6 | =========== // // The following shows the shading styles sorted by excelize index number: // // Index | Style | Index | Style // -------+-----------------+-------+----------------- // 0 | Horizontal | 3 | Diagonal down // 1 | Vertical | 4 | From corner // 2 | Diagonal Up | 5 | From center // // The following shows the patterns styles sorted by excelize index number: // // Index | Style | Index | Style // -------+-----------------+-------+----------------- // 0 | None | 10 | darkTrellis // 1 | solid | 11 | lightHorizontal // 2 | mediumGray | 12 | lightVertical // 3 | darkGray | 13 | lightDown // 4 | lightGray | 14 | lightUp // 5 | darkHorizontal | 15 | lightGrid // 6 | darkVertical | 16 | lightTrellis // 7 | darkDown | 17 | gray125 // 8 | darkUp | 18 | gray0625 // 9 | darkGrid | | // // The following the type of horizontal alignment in cells: // // Style // ------------------ // left // center // right // fill // justify // centerContinuous // distributed // // The following the type of vertical alignment in cells: // // Style // ------------------ // top // center // justify // distributed // // The following the type of font underline style: // // Style // ------------------ // single // double // // Excel's built-in all languages formats are shown in the following table: // // Index | Format String // -------+---------------------------------------------------- // 0 | General // 1 | 0 // 2 | 0.00 // 3 | #,##0 // 4 | #,##0.00 // 5 | ($#,##0_);($#,##0) // 6 | ($#,##0_);[Red]($#,##0) // 7 | ($#,##0.00_);($#,##0.00) // 8 | ($#,##0.00_);[Red]($#,##0.00) // 9 | 0% // 10 | 0.00% // 11 | 0.00E+00 // 12 | # ?/? // 13 | # ??/?? // 14 | m/d/yy // 15 | d-mmm-yy // 16 | d-mmm // 17 | mmm-yy // 18 | h:mm AM/PM // 19 | h:mm:ss AM/PM // 20 | h:mm // 21 | h:mm:ss // 22 | m/d/yy h:mm // ... | ... // 37 | (#,##0_);(#,##0) // 38 | (#,##0_);[Red](#,##0) // 39 | (#,##0.00_);(#,##0.00) // 40 | (#,##0.00_);[Red](#,##0.00) // 41 | _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_) // 42 | _($* #,##0_);_($* (#,##0);_($* "-"_);_(@_) // 43 | _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_) // 44 | _($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_) // 45 | mm:ss // 46 | [h]:mm:ss // 47 | mm:ss.0 // 48 | ##0.0E+0 // 49 | @ // // Number format code in zh-tw language: // // Index | Symbol // -------+------------------------------------------- // 27 | [$-404]e/m/d // 28 | [$-404]e"年"m"月"d"日" // 29 | [$-404]e"年"m"月"d"日" // 30 | m/d/yy // 31 | yyyy"年"m"月"d"日" // 32 | hh"時"mm"分" // 33 | hh"時"mm"分"ss"秒" // 34 | 上午/下午 hh"時"mm"分" // 35 | 上午/下午 hh"時"mm"分"ss"秒" // 36 | [$-404]e/m/d // 50 | [$-404]e/m/d // 51 | [$-404]e"年"m"月"d"日" // 52 | 上午/下午 hh"時"mm"分" // 53 | 上午/下午 hh"時"mm"分"ss"秒" // 54 | [$-404]e"年"m"月"d"日" // 55 | 上午/下午 hh"時"mm"分" // 56 | 上午/下午 hh"時"mm"分"ss"秒" // 57 | [$-404]e/m/d // 58 | [$-404]e"年"m"月"d"日" // // Number format code in zh-cn language: // // Index | Symbol // -------+------------------------------------------- // 27 | yyyy"年"m"月" // 28 | m"月"d"日" // 29 | m"月"d"日" // 30 | m-d-yy // 31 | yyyy"年"m"月"d"日" // 32 | h"时"mm"分" // 33 | h"时"mm"分"ss"秒" // 34 | 上午/下午 h"时"mm"分" // 35 | 上午/下午 h"时"mm"分"ss"秒 // 36 | yyyy"年"m"月 // 50 | yyyy"年"m"月 // 51 | m"月"d"日 // 52 | yyyy"年"m"月 // 53 | m"月"d"日 // 54 | m"月"d"日 // 55 | 上午/下午 h"时"mm"分 // 56 | 上午/下午 h"时"mm"分"ss"秒 // 57 | yyyy"年"m"月 // 58 | m"月"d"日" // // Number format code with unicode values provided for language glyphs where // they occur in zh-tw language: // // Index | Symbol // -------+------------------------------------------- // 27 | [$-404]e/m/ // 28 | [$-404]e"5E74"m"6708"d"65E5 // 29 | [$-404]e"5E74"m"6708"d"65E5 // 30 | m/d/y // 31 | yyyy"5E74"m"6708"d"65E5 // 32 | hh"6642"mm"5206 // 33 | hh"6642"mm"5206"ss"79D2 // 34 | 4E0A5348/4E0B5348hh"6642"mm"5206 // 35 | 4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2 // 36 | [$-404]e/m/ // 50 | [$-404]e/m/ // 51 | [$-404]e"5E74"m"6708"d"65E5 // 52 | 4E0A5348/4E0B5348hh"6642"mm"5206 // 53 | 4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2 // 54 | [$-404]e"5E74"m"6708"d"65E5 // 55 | 4E0A5348/4E0B5348hh"6642"mm"5206 // 56 | 4E0A5348/4E0B5348hh"6642"mm"5206"ss"79D2 // 57 | [$-404]e/m/ // 58 | [$-404]e"5E74"m"6708"d"65E5" // // Number format code with unicode values provided for language glyphs where // they occur in zh-cn language: // // Index | Symbol // -------+------------------------------------------- // 27 | yyyy"5E74"m"6708 // 28 | m"6708"d"65E5 // 29 | m"6708"d"65E5 // 30 | m-d-y // 31 | yyyy"5E74"m"6708"d"65E5 // 32 | h"65F6"mm"5206 // 33 | h"65F6"mm"5206"ss"79D2 // 34 | 4E0A5348/4E0B5348h"65F6"mm"5206 // 35 | 4E0A5348/4E0B5348h"65F6"mm"5206"ss"79D2 // 36 | yyyy"5E74"m"6708 // 50 | yyyy"5E74"m"6708 // 51 | m"6708"d"65E5 // 52 | yyyy"5E74"m"6708 // 53 | m"6708"d"65E5 // 54 | m"6708"d"65E5 // 55 | 4E0A5348/4E0B5348h"65F6"mm"5206 // 56 | 4E0A5348/4E0B5348h"65F6"mm"5206"ss"79D2 // 57 | yyyy"5E74"m"6708 // 58 | m"6708"d"65E5" // // Number format code in ja-jp language: // // Index | Symbol // -------+------------------------------------------- // 27 | [$-411]ge.m.d // 28 | [$-411]ggge"年"m"月"d"日 // 29 | [$-411]ggge"年"m"月"d"日 // 30 | m/d/y // 31 | yyyy"年"m"月"d"日 // 32 | h"時"mm"分 // 33 | h"時"mm"分"ss"秒 // 34 | yyyy"年"m"月 // 35 | m"月"d"日 // 36 | [$-411]ge.m.d // 50 | [$-411]ge.m.d // 51 | [$-411]ggge"年"m"月"d"日 // 52 | yyyy"年"m"月 // 53 | m"月"d"日 // 54 | [$-411]ggge"年"m"月"d"日 // 55 | yyyy"年"m"月 // 56 | m"月"d"日 // 57 | [$-411]ge.m.d // 58 | [$-411]ggge"年"m"月"d"日" // // Number format code in ko-kr language: // // Index | Symbol // -------+------------------------------------------- // 27 | yyyy"年" mm"月" dd"日 // 28 | mm-d // 29 | mm-d // 30 | mm-dd-y // 31 | yyyy"년" mm"월" dd"일 // 32 | h"시" mm"분 // 33 | h"시" mm"분" ss"초 // 34 | yyyy-mm-d // 35 | yyyy-mm-d // 36 | yyyy"年" mm"月" dd"日 // 50 | yyyy"年" mm"月" dd"日 // 51 | mm-d // 52 | yyyy-mm-d // 53 | yyyy-mm-d // 54 | mm-d // 55 | yyyy-mm-d // 56 | yyyy-mm-d // 57 | yyyy"年" mm"月" dd"日 // 58 | mm-dd // // Number format code with unicode values provided for language glyphs where // they occur in ja-jp language: // // Index | Symbol // -------+------------------------------------------- // 27 | [$-411]ge.m.d // 28 | [$-411]ggge"5E74"m"6708"d"65E5 // 29 | [$-411]ggge"5E74"m"6708"d"65E5 // 30 | m/d/y // 31 | yyyy"5E74"m"6708"d"65E5 // 32 | h"6642"mm"5206 // 33 | h"6642"mm"5206"ss"79D2 // 34 | yyyy"5E74"m"6708 // 35 | m"6708"d"65E5 // 36 | [$-411]ge.m.d // 50 | [$-411]ge.m.d // 51 | [$-411]ggge"5E74"m"6708"d"65E5 // 52 | yyyy"5E74"m"6708 // 53 | m"6708"d"65E5 // 54 | [$-411]ggge"5E74"m"6708"d"65E5 // 55 | yyyy"5E74"m"6708 // 56 | m"6708"d"65E5 // 57 | [$-411]ge.m.d // 58 | [$-411]ggge"5E74"m"6708"d"65E5" // // Number format code with unicode values provided for language glyphs where // they occur in ko-kr language: // // Index | Symbol // -------+------------------------------------------- // 27 | yyyy"5E74" mm"6708" dd"65E5 // 28 | mm-d // 29 | mm-d // 30 | mm-dd-y // 31 | yyyy"B144" mm"C6D4" dd"C77C // 32 | h"C2DC" mm"BD84 // 33 | h"C2DC" mm"BD84" ss"CD08 // 34 | yyyy-mm-d // 35 | yyyy-mm-d // 36 | yyyy"5E74" mm"6708" dd"65E5 // 50 | yyyy"5E74" mm"6708" dd"65E5 // 51 | mm-d // 52 | yyyy-mm-d // 53 | yyyy-mm-d // 54 | mm-d // 55 | yyyy-mm-d // 56 | yyyy-mm-d // 57 | yyyy"5E74" mm"6708" dd"65E5 // 58 | mm-dd // // Number format code in th-th language: // // Index | Symbol // -------+------------------------------------------- // 59 | t // 60 | t0.0 // 61 | t#,## // 62 | t#,##0.0 // 67 | t0 // 68 | t0.00 // 69 | t# ?/ // 70 | t# ??/? // 71 | ว/ด/ปปป // 72 | ว-ดดด-ป // 73 | ว-ดด // 74 | ดดด-ป // 75 | ช:น // 76 | ช:นน:ท // 77 | ว/ด/ปปปป ช:น // 78 | นน:ท // 79 | [ช]:นน:ท // 80 | นน:ทท. // 81 | d/m/bb // // Number format code with unicode values provided for language glyphs where // they occur in th-th language: // // Index | Symbol // -------+------------------------------------------- // 59 | t // 60 | t0.0 // 61 | t#,## // 62 | t#,##0.0 // 67 | t0 // 68 | t0.00 // 69 | t# ?/ // 70 | t# ??/? // 71 | 0E27/0E14/0E1B0E1B0E1B0E1 // 72 | 0E27-0E140E140E14-0E1B0E1 // 73 | 0E27-0E140E140E1 // 74 | 0E140E140E14-0E1B0E1 // 75 | 0E0A:0E190E1 // 76 | 0E0A:0E190E19:0E170E1 // 77 | 0E27/0E14/0E1B0E1B0E1B0E1B 0E0A:0E190E1 // 78 | 0E190E19:0E170E1 // 79 | [0E0A]:0E190E19:0E170E1 // 80 | 0E190E19:0E170E17. // 81 | d/m/bb // // Excelize built-in currency formats are shown in the following table, only // support these types in the following table (Index number is used only for // markup and is not used inside an Excel file and you can't get formatted value // by the function GetCellValue) currently: // // Index | Symbol // -------+--------------------------------------------------------------- // 164 | CN¥ // 165 | $ English (China) // 166 | $ Cherokee (United States) // 167 | $ Chinese (Singapore) // 168 | $ Chinese (Taiwan) // 169 | $ English (Australia) // 170 | $ English (Belize) // 171 | $ English (Canada) // 172 | $ English (Jamaica) // 173 | $ English (New Zealand) // 174 | $ English (Singapore) // 175 | $ English (Trinidad & Tobago) // 176 | $ English (U.S. Virgin Islands) // 177 | $ English (United States) // 178 | $ French (Canada) // 179 | $ Hawaiian (United States) // 180 | $ Malay (Brunei) // 181 | $ Quechua (Ecuador) // 182 | $ Spanish (Chile) // 183 | $ Spanish (Colombia) // 184 | $ Spanish (Ecuador) // 185 | $ Spanish (El Salvador) // 186 | $ Spanish (Mexico) // 187 | $ Spanish (Puerto Rico) // 188 | $ Spanish (United States) // 189 | $ Spanish (Uruguay) // 190 | £ English (United Kingdom) // 191 | £ Scottish Gaelic (United Kingdom) // 192 | £ Welsh (United Kindom) // 193 | ¥ Chinese (China) // 194 | ¥ Japanese (Japan) // 195 | ¥ Sichuan Yi (China) // 196 | ¥ Tibetan (China) // 197 | ¥ Uyghur (China) // 198 | ֏ Armenian (Armenia) // 199 | ؋ Pashto (Afghanistan) // 200 | ؋ Persian (Afghanistan) // 201 | ৳ Bengali (Bangladesh) // 202 | ៛ Khmer (Cambodia) // 203 | ₡ Spanish (Costa Rica) // 204 | ₦ Hausa (Nigeria) // 205 | ₦ Igbo (Nigeria) // 206 | ₦ Yoruba (Nigeria) // 207 | ₩ Korean (South Korea) // 208 | ₪ Hebrew (Israel) // 209 | ₫ Vietnamese (Vietnam) // 210 | € Basque (Spain) // 211 | € Breton (France) // 212 | € Catalan (Spain) // 213 | € Corsican (France) // 214 | € Dutch (Belgium) // 215 | € Dutch (Netherlands) // 216 | € English (Ireland) // 217 | € Estonian (Estonia) // 218 | € Euro (€ 123) // 219 | € Euro (123 €) // 220 | € Finnish (Finland) // 221 | € French (Belgium) // 222 | € French (France) // 223 | € French (Luxembourg) // 224 | € French (Monaco) // 225 | € French (Réunion) // 226 | € Galician (Spain) // 227 | € German (Austria) // 228 | € German (Luxembourg) // 229 | € Greek (Greece) // 230 | € Inari Sami (Finland) // 231 | € Irish (Ireland) // 232 | € Italian (Italy) // 233 | € Latin (Italy) // 234 | € Latin, Serbian (Montenegro) // 235 | € Larvian (Latvia) // 236 | € Lithuanian (Lithuania) // 237 | € Lower Sorbian (Germany) // 238 | € Luxembourgish (Luxembourg) // 239 | € Maltese (Malta) // 240 | € Northern Sami (Finland) // 241 | € Occitan (France) // 242 | € Portuguese (Portugal) // 243 | € Serbian (Montenegro) // 244 | € Skolt Sami (Finland) // 245 | € Slovak (Slovakia) // 246 | € Slovenian (Slovenia) // 247 | € Spanish (Spain) // 248 | € Swedish (Finland) // 249 | € Swiss German (France) // 250 | € Upper Sorbian (Germany) // 251 | € Western Frisian (Netherlands) // 252 | ₭ Lao (Laos) // 253 | ₮ Mongolian (Mongolia) // 254 | ₮ Mongolian, Mongolian (Mongolia) // 255 | ₱ English (Philippines) // 256 | ₱ Filipino (Philippines) // 257 | ₴ Ukrainian (Ukraine) // 258 | ₸ Kazakh (Kazakhstan) // 259 | ₹ Arabic, Kashmiri (India) // 260 | ₹ English (India) // 261 | ₹ Gujarati (India) // 262 | ₹ Hindi (India) // 263 | ₹ Kannada (India) // 264 | ₹ Kashmiri (India) // 265 | ₹ Konkani (India) // 266 | ₹ Manipuri (India) // 267 | ₹ Marathi (India) // 268 | ₹ Nepali (India) // 269 | ₹ Oriya (India) // 270 | ₹ Punjabi (India) // 271 | ₹ Sanskrit (India) // 272 | ₹ Sindhi (India) // 273 | ₹ Tamil (India) // 274 | ₹ Urdu (India) // 275 | ₺ Turkish (Turkey) // 276 | ₼ Azerbaijani (Azerbaijan) // 277 | ₼ Cyrillic, Azerbaijani (Azerbaijan) // 278 | ₽ Russian (Russia) // 279 | ₽ Sakha (Russia) // 280 | ₾ Georgian (Georgia) // 281 | B/. Spanish (Panama) // 282 | Br Oromo (Ethiopia) // 283 | Br Somali (Ethiopia) // 284 | Br Tigrinya (Ethiopia) // 285 | Bs Quechua (Bolivia) // 286 | Bs Spanish (Bolivia) // 287 | BS. Spanish (Venezuela) // 288 | BWP Tswana (Botswana) // 289 | C$ Spanish (Nicaragua) // 290 | CA$ Latin, Inuktitut (Canada) // 291 | CA$ Mohawk (Canada) // 292 | CA$ Unified Canadian Aboriginal Syllabics, Inuktitut (Canada) // 293 | CFA French (Mali) // 294 | CFA French (Senegal) // 295 | CFA Fulah (Senegal) // 296 | CFA Wolof (Senegal) // 297 | CHF French (Switzerland) // 298 | CHF German (Liechtenstein) // 299 | CHF German (Switzerland) // 300 | CHF Italian (Switzerland) // 301 | CHF Romansh (Switzerland) // 302 | CLP Mapuche (Chile) // 303 | CN¥ Mongolian, Mongolian (China) // 304 | DZD Central Atlas Tamazight (Algeria) // 305 | FCFA French (Cameroon) // 306 | Ft Hungarian (Hungary) // 307 | G French (Haiti) // 308 | Gs. Spanish (Paraguay) // 309 | GTQ K'iche' (Guatemala) // 310 | HK$ Chinese (Hong Kong (China)) // 311 | HK$ English (Hong Kong (China)) // 312 | HRK Croatian (Croatia) // 313 | IDR English (Indonesia) // 314 | IQD Arbic, Central Kurdish (Iraq) // 315 | ISK Icelandic (Iceland) // 316 | K Burmese (Myanmar (Burma)) // 317 | Kč Czech (Czech Republic) // 318 | KM Bosnian (Bosnia & Herzegovina) // 319 | KM Croatian (Bosnia & Herzegovina) // 320 | KM Latin, Serbian (Bosnia & Herzegovina) // 321 | kr Faroese (Faroe Islands) // 322 | kr Northern Sami (Norway) // 323 | kr Northern Sami (Sweden) // 324 | kr Norwegian Bokmål (Norway) // 325 | kr Norwegian Nynorsk (Norway) // 326 | kr Swedish (Sweden) // 327 | kr. Danish (Denmark) // 328 | kr. Kalaallisut (Greenland) // 329 | Ksh Swahili (kenya) // 330 | L Romanian (Moldova) // 331 | L Russian (Moldova) // 332 | L Spanish (Honduras) // 333 | Lekë Albanian (Albania) // 334 | MAD Arabic, Central Atlas Tamazight (Morocco) // 335 | MAD French (Morocco) // 336 | MAD Tifinagh, Central Atlas Tamazight (Morocco) // 337 | MOP$ Chinese (Macau (China)) // 338 | MVR Divehi (Maldives) // 339 | Nfk Tigrinya (Eritrea) // 340 | NGN Bini (Nigeria) // 341 | NGN Fulah (Nigeria) // 342 | NGN Ibibio (Nigeria) // 343 | NGN Kanuri (Nigeria) // 344 | NOK Lule Sami (Norway) // 345 | NOK Southern Sami (Norway) // 346 | NZ$ Maori (New Zealand) // 347 | PKR Sindhi (Pakistan) // 348 | PYG Guarani (Paraguay) // 349 | Q Spanish (Guatemala) // 350 | R Afrikaans (South Africa) // 351 | R English (South Africa) // 352 | R Zulu (South Africa) // 353 | R$ Portuguese (Brazil) // 354 | RD$ Spanish (Dominican Republic) // 355 | RF Kinyarwanda (Rwanda) // 356 | RM English (Malaysia) // 357 | RM Malay (Malaysia) // 358 | RON Romanian (Romania) // 359 | Rp Indonesoan (Indonesia) // 360 | Rs Urdu (Pakistan) // 361 | Rs. Tamil (Sri Lanka) // 362 | RSD Latin, Serbian (Serbia) // 363 | RSD Serbian (Serbia) // 364 | RUB Bashkir (Russia) // 365 | RUB Tatar (Russia) // 366 | S/. Quechua (Peru) // 367 | S/. Spanish (Peru) // 368 | SEK Lule Sami (Sweden) // 369 | SEK Southern Sami (Sweden) // 370 | soʻm Latin, Uzbek (Uzbekistan) // 371 | soʻm Uzbek (Uzbekistan) // 372 | SYP Syriac (Syria) // 373 | THB Thai (Thailand) // 374 | TMT Turkmen (Turkmenistan) // 375 | US$ English (Zimbabwe) // 376 | ZAR Northern Sotho (South Africa) // 377 | ZAR Southern Sotho (South Africa) // 378 | ZAR Tsonga (South Africa) // 379 | ZAR Tswana (south Africa) // 380 | ZAR Venda (South Africa) // 381 | ZAR Xhosa (South Africa) // 382 | zł Polish (Poland) // 383 | ден Macedonian (Macedonia) // 384 | KM Cyrillic, Bosnian (Bosnia & Herzegovina) // 385 | KM Serbian (Bosnia & Herzegovina) // 386 | лв. Bulgarian (Bulgaria) // 387 | p. Belarusian (Belarus) // 388 | сом Kyrgyz (Kyrgyzstan) // 389 | сом Tajik (Tajikistan) // 390 | ج.م. Arabic (Egypt) // 391 | د.أ. Arabic (Jordan) // 392 | د.أ. Arabic (United Arab Emirates) // 393 | د.ب. Arabic (Bahrain) // 394 | د.ت. Arabic (Tunisia) // 395 | د.ج. Arabic (Algeria) // 396 | د.ع. Arabic (Iraq) // 397 | د.ك. Arabic (Kuwait) // 398 | د.ل. Arabic (Libya) // 399 | د.م. Arabic (Morocco) // 400 | ر Punjabi (Pakistan) // 401 | ر.س. Arabic (Saudi Arabia) // 402 | ر.ع. Arabic (Oman) // 403 | ر.ق. Arabic (Qatar) // 404 | ر.ي. Arabic (Yemen) // 405 | ریال Persian (Iran) // 406 | ل.س. Arabic (Syria) // 407 | ل.ل. Arabic (Lebanon) // 408 | ብር Amharic (Ethiopia) // 409 | रू Nepaol (Nepal) // 410 | රු. Sinhala (Sri Lanka) // 411 | ADP // 412 | AED // 413 | AFA // 414 | AFN // 415 | ALL // 416 | AMD // 417 | ANG // 418 | AOA // 419 | ARS // 420 | ATS // 421 | AUD // 422 | AWG // 423 | AZM // 424 | AZN // 425 | BAM // 426 | BBD // 427 | BDT // 428 | BEF // 429 | BGL // 430 | BGN // 431 | BHD // 432 | BIF // 433 | BMD // 434 | BND // 435 | BOB // 436 | BOV // 437 | BRL // 438 | BSD // 439 | BTN // 440 | BWP // 441 | BYR // 442 | BZD // 443 | CAD // 444 | CDF // 445 | CHE // 446 | CHF // 447 | CHW // 448 | CLF // 449 | CLP // 450 | CNY // 451 | COP // 452 | COU // 453 | CRC // 454 | CSD // 455 | CUC // 456 | CVE // 457 | CYP // 458 | CZK // 459 | DEM // 460 | DJF // 461 | DKK // 462 | DOP // 463 | DZD // 464 | ECS // 465 | ECV // 466 | EEK // 467 | EGP // 468 | ERN // 469 | ESP // 470 | ETB // 471 | EUR // 472 | FIM // 473 | FJD // 474 | FKP // 475 | FRF // 476 | GBP // 477 | GEL // 478 | GHC // 479 | GHS // 480 | GIP // 481 | GMD // 482 | GNF // 483 | GRD // 484 | GTQ // 485 | GYD // 486 | HKD // 487 | HNL // 488 | HRK // 489 | HTG // 490 | HUF // 491 | IDR // 492 | IEP // 493 | ILS // 494 | INR // 495 | IQD // 496 | IRR // 497 | ISK // 498 | ITL // 499 | JMD // 500 | JOD // 501 | JPY // 502 | KAF // 503 | KES // 504 | KGS // 505 | KHR // 506 | KMF // 507 | KPW // 508 | KRW // 509 | KWD // 510 | KYD // 511 | KZT // 512 | LAK // 513 | LBP // 514 | LKR // 515 | LRD // 516 | LSL // 517 | LTL // 518 | LUF // 519 | LVL // 520 | LYD // 521 | MAD // 522 | MDL // 523 | MGA // 524 | MGF // 525 | MKD // 526 | MMK // 527 | MNT // 528 | MOP // 529 | MRO // 530 | MTL // 531 | MUR // 532 | MVR // 533 | MWK // 534 | MXN // 535 | MXV // 536 | MYR // 537 | MZM // 538 | MZN // 539 | NAD // 540 | NGN // 541 | NIO // 542 | NLG // 543 | NOK // 544 | NPR // 545 | NTD // 546 | NZD // 547 | OMR // 548 | PAB // 549 | PEN // 550 | PGK // 551 | PHP // 552 | PKR // 553 | PLN // 554 | PTE // 555 | PYG // 556 | QAR // 557 | ROL // 558 | RON // 559 | RSD // 560 | RUB // 561 | RUR // 562 | RWF // 563 | SAR // 564 | SBD // 565 | SCR // 566 | SDD // 567 | SDG // 568 | SDP // 569 | SEK // 570 | SGD // 571 | SHP // 572 | SIT // 573 | SKK // 574 | SLL // 575 | SOS // 576 | SPL // 577 | SRD // 578 | SRG // 579 | STD // 580 | SVC // 581 | SYP // 582 | SZL // 583 | THB // 584 | TJR // 585 | TJS // 586 | TMM // 587 | TMT // 588 | TND // 589 | TOP // 590 | TRL // 591 | TRY // 592 | TTD // 593 | TWD // 594 | TZS // 595 | UAH // 596 | UGX // 597 | USD // 598 | USN // 599 | USS // 600 | UYI // 601 | UYU // 602 | UZS // 603 | VEB // 604 | VEF // 605 | VND // 606 | VUV // 607 | WST // 608 | XAF // 609 | XAG // 610 | XAU // 611 | XB5 // 612 | XBA // 613 | XBB // 614 | XBC // 615 | XBD // 616 | XCD // 617 | XDR // 618 | XFO // 619 | XFU // 620 | XOF // 621 | XPD // 622 | XPF // 623 | XPT // 624 | XTS // 625 | XXX // 626 | YER // 627 | YUM // 628 | ZAR // 629 | ZMK // 630 | ZMW // 631 | ZWD // 632 | ZWL // 633 | ZWN // 634 | ZWR // // Excelize support set custom number format for cell. For example, set number // as date type in Uruguay (Spanish) format for Sheet1!A6: // // f := excelize.NewFile() // f.SetCellValue("Sheet1", "A6", 42920.5) // exp := "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@" // style, err := f.NewStyle(&excelize.Style{CustomNumFmt: &exp}) // err = f.SetCellStyle("Sheet1", "A6", "A6", style) // // Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017 // func (f *File) NewStyle(style interface{}) (int, error) { var fs *Style var err error var cellXfsID, fontID, borderID, fillID int fs, err = parseFormatStyleSet(style) if err != nil { return cellXfsID, err } if fs.DecimalPlaces == 0 { fs.DecimalPlaces = 2 } s := f.stylesReader() s.Lock() defer s.Unlock() // check given style already exist. if cellXfsID = f.getStyleID(s, fs); cellXfsID != -1 { return cellXfsID, err } numFmtID := newNumFmt(s, fs) if fs.Font != nil { fontID = f.getFontID(s, fs) if fontID == -1 { s.Fonts.Count++ s.Fonts.Font = append(s.Fonts.Font, f.newFont(fs)) fontID = s.Fonts.Count - 1 } } borderID = getBorderID(s, fs) if borderID == -1 { if len(fs.Border) == 0 { borderID = 0 } else { s.Borders.Count++ s.Borders.Border = append(s.Borders.Border, newBorders(fs)) borderID = s.Borders.Count - 1 } } if fillID = getFillID(s, fs); fillID == -1 { if fill := newFills(fs, true); fill != nil { s.Fills.Count++ s.Fills.Fill = append(s.Fills.Fill, fill) fillID = s.Fills.Count - 1 } else { fillID = 0 } } applyAlignment, alignment := fs.Alignment != nil, newAlignment(fs) applyProtection, protection := fs.Protection != nil, newProtection(fs) cellXfsID = setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection) return cellXfsID, nil } var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{ "numFmt": func(numFmtID int, xf xlsxXf, style *Style) bool { if style.CustomNumFmt == nil && numFmtID == -1 { return xf.NumFmtID != nil && *xf.NumFmtID == 0 } if style.NegRed || style.Lang != "" || style.DecimalPlaces != 2 { return false } return xf.NumFmtID != nil && *xf.NumFmtID == numFmtID }, "font": func(fontID int, xf xlsxXf, style *Style) bool { if style.Font == nil { return (xf.FontID == nil || *xf.FontID == 0) && (xf.ApplyFont == nil || !*xf.ApplyFont) } return xf.FontID != nil && *xf.FontID == fontID && xf.ApplyFont != nil && *xf.ApplyFont }, "fill": func(fillID int, xf xlsxXf, style *Style) bool { if style.Fill.Type == "" { return (xf.FillID == nil || *xf.FillID == 0) && (xf.ApplyFill == nil || !*xf.ApplyFill) } return xf.FillID != nil && *xf.FillID == fillID && xf.ApplyFill != nil && *xf.ApplyFill }, "border": func(borderID int, xf xlsxXf, style *Style) bool { if len(style.Border) == 0 { return (xf.BorderID == nil || *xf.BorderID == 0) && (xf.ApplyBorder == nil || !*xf.ApplyBorder) } return xf.BorderID != nil && *xf.BorderID == borderID && xf.ApplyBorder != nil && *xf.ApplyBorder }, "alignment": func(ID int, xf xlsxXf, style *Style) bool { if style.Alignment == nil { return xf.ApplyAlignment == nil || !*xf.ApplyAlignment } return reflect.DeepEqual(xf.Alignment, newAlignment(style)) }, "protection": func(ID int, xf xlsxXf, style *Style) bool { if style.Protection == nil { return xf.ApplyProtection == nil || !*xf.ApplyProtection } return reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection }, } // getStyleID provides a function to get styleID by given style. If given // style is not exist, will return -1. func (f *File) getStyleID(ss *xlsxStyleSheet, style *Style) (styleID int) { styleID = -1 if ss.CellXfs == nil { return } numFmtID, borderID, fillID, fontID := getNumFmtID(ss, style), getBorderID(ss, style), getFillID(ss, style), f.getFontID(ss, style) if style.CustomNumFmt != nil { numFmtID = getCustomNumFmtID(ss, style) } for xfID, xf := range ss.CellXfs.Xf { if getXfIDFuncs["numFmt"](numFmtID, xf, style) && getXfIDFuncs["font"](fontID, xf, style) && getXfIDFuncs["fill"](fillID, xf, style) && getXfIDFuncs["border"](borderID, xf, style) && getXfIDFuncs["alignment"](0, xf, style) && getXfIDFuncs["protection"](0, xf, style) { styleID = xfID return } } return } // NewConditionalStyle provides a function to create style for conditional // format by given style format. The parameters are the same as function // NewStyle(). Note that the color field uses RGB color code and only support // to set font, fills, alignment and borders currently. func (f *File) NewConditionalStyle(style string) (int, error) { s := f.stylesReader() fs, err := parseFormatStyleSet(style) if err != nil { return 0, err } dxf := dxf{ Fill: newFills(fs, false), } if fs.Alignment != nil { dxf.Alignment = newAlignment(fs) } if len(fs.Border) > 0 { dxf.Border = newBorders(fs) } if fs.Font != nil { dxf.Font = f.newFont(fs) } dxfStr, _ := xml.Marshal(dxf) if s.Dxfs == nil { s.Dxfs = &xlsxDxfs{} } s.Dxfs.Count++ s.Dxfs.Dxfs = append(s.Dxfs.Dxfs, &xlsxDxf{ Dxf: string(dxfStr[5 : len(dxfStr)-6]), }) return s.Dxfs.Count - 1, nil } // GetDefaultFont provides the default font name currently set in the // workbook. The spreadsheet generated by excelize default font is Calibri. func (f *File) GetDefaultFont() string { font := f.readDefaultFont() return *font.Name.Val } // SetDefaultFont changes the default font in the workbook. func (f *File) SetDefaultFont(fontName string) { font := f.readDefaultFont() font.Name.Val = stringPtr(fontName) s := f.stylesReader() s.Fonts.Font[0] = font custom := true s.CellStyles.CellStyle[0].CustomBuiltIn = &custom } // readDefaultFont provides an unmarshalled font value. func (f *File) readDefaultFont() *xlsxFont { s := f.stylesReader() return s.Fonts.Font[0] } // getFontID provides a function to get font ID. // If given font is not exist, will return -1. func (f *File) getFontID(styleSheet *xlsxStyleSheet, style *Style) (fontID int) { fontID = -1 if styleSheet.Fonts == nil || style.Font == nil { return } for idx, fnt := range styleSheet.Fonts.Font { if reflect.DeepEqual(*fnt, *f.newFont(style)) { fontID = idx return } } return } // newFont provides a function to add font style by given cell format // settings. func (f *File) newFont(style *Style) *xlsxFont { fontUnderlineType := map[string]string{"single": "single", "double": "double"} if style.Font.Size < 1 { style.Font.Size = 11 } if style.Font.Color == "" { style.Font.Color = "#000000" } fnt := xlsxFont{ Sz: &attrValFloat{Val: float64Ptr(style.Font.Size)}, Color: &xlsxColor{RGB: getPaletteColor(style.Font.Color)}, Name: &attrValString{Val: stringPtr(style.Font.Family)}, Family: &attrValInt{Val: intPtr(2)}, } if style.Font.Bold { fnt.B = &attrValBool{Val: &style.Font.Bold} } if style.Font.Italic { fnt.I = &attrValBool{Val: &style.Font.Italic} } if *fnt.Name.Val == "" { *fnt.Name.Val = f.GetDefaultFont() } if style.Font.Strike { fnt.Strike = &attrValBool{Val: &style.Font.Strike} } val, ok := fontUnderlineType[style.Font.Underline] if ok { fnt.U = &attrValString{Val: stringPtr(val)} } return &fnt } // getNumFmtID provides a function to get number format code ID. // If given number format code is not exist, will return -1. func getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (numFmtID int) { numFmtID = -1 if _, ok := builtInNumFmt[style.NumFmt]; ok { return style.NumFmt } for lang, numFmt := range langNumFmt { if _, ok := numFmt[style.NumFmt]; ok && lang == style.Lang { numFmtID = style.NumFmt return } } if fmtCode, ok := currencyNumFmt[style.NumFmt]; ok { numFmtID = style.NumFmt if styleSheet.NumFmts != nil { for _, numFmt := range styleSheet.NumFmts.NumFmt { if numFmt.FormatCode == fmtCode { numFmtID = numFmt.NumFmtID return } } } } return } // newNumFmt provides a function to check if number format code in the range // of built-in values. func newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int { dp := "0." numFmtID := 164 // Default custom number format code from 164. if style.DecimalPlaces < 0 || style.DecimalPlaces > 30 { style.DecimalPlaces = 2 } for i := 0; i < style.DecimalPlaces; i++ { dp += "0" } if style.CustomNumFmt != nil { if customNumFmtID := getCustomNumFmtID(styleSheet, style); customNumFmtID != -1 { return customNumFmtID } return setCustomNumFmt(styleSheet, style) } _, ok := builtInNumFmt[style.NumFmt] if !ok { fc, currency := currencyNumFmt[style.NumFmt] if !currency { return setLangNumFmt(styleSheet, style) } fc = strings.Replace(fc, "0.00", dp, -1) if style.NegRed { fc = fc + ";[Red]" + fc } if styleSheet.NumFmts != nil { numFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1 nf := xlsxNumFmt{ FormatCode: fc, NumFmtID: numFmtID, } styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf) styleSheet.NumFmts.Count++ } else { nf := xlsxNumFmt{ FormatCode: fc, NumFmtID: numFmtID, } numFmts := xlsxNumFmts{ NumFmt: []*xlsxNumFmt{&nf}, Count: 1, } styleSheet.NumFmts = &numFmts } return numFmtID } return style.NumFmt } // setCustomNumFmt provides a function to set custom number format code. func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int { nf := xlsxNumFmt{FormatCode: *style.CustomNumFmt} if styleSheet.NumFmts != nil { nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1 styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf) styleSheet.NumFmts.Count++ } else { nf.NumFmtID = 164 numFmts := xlsxNumFmts{ NumFmt: []*xlsxNumFmt{&nf}, Count: 1, } styleSheet.NumFmts = &numFmts } return nf.NumFmtID } // getCustomNumFmtID provides a function to get custom number format code ID. // If given custom number format code is not exist, will return -1. func getCustomNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (customNumFmtID int) { customNumFmtID = -1 if styleSheet.NumFmts == nil { return } for _, numFmt := range styleSheet.NumFmts.NumFmt { if style.CustomNumFmt != nil && numFmt.FormatCode == *style.CustomNumFmt { customNumFmtID = numFmt.NumFmtID return } } return } // setLangNumFmt provides a function to set number format code with language. func setLangNumFmt(styleSheet *xlsxStyleSheet, style *Style) int { numFmts, ok := langNumFmt[style.Lang] if !ok { return 0 } var fc string fc, ok = numFmts[style.NumFmt] if !ok { return 0 } nf := xlsxNumFmt{FormatCode: fc} if styleSheet.NumFmts != nil { nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1 styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf) styleSheet.NumFmts.Count++ } else { nf.NumFmtID = style.NumFmt numFmts := xlsxNumFmts{ NumFmt: []*xlsxNumFmt{&nf}, Count: 1, } styleSheet.NumFmts = &numFmts } return nf.NumFmtID } // getFillID provides a function to get fill ID. If given fill is not // exist, will return -1. func getFillID(styleSheet *xlsxStyleSheet, style *Style) (fillID int) { fillID = -1 if styleSheet.Fills == nil || style.Fill.Type == "" { return } fills := newFills(style, true) if fills == nil { return } for idx, fill := range styleSheet.Fills.Fill { if reflect.DeepEqual(fill, fills) { fillID = idx return } } return } // newFills provides a function to add fill elements in the styles.xml by // given cell format settings. func newFills(style *Style, fg bool) *xlsxFill { var patterns = []string{ "none", "solid", "mediumGray", "darkGray", "lightGray", "darkHorizontal", "darkVertical", "darkDown", "darkUp", "darkGrid", "darkTrellis", "lightHorizontal", "lightVertical", "lightDown", "lightUp", "lightGrid", "lightTrellis", "gray125", "gray0625", } var variants = []float64{ 90, 0, 45, 135, } var fill xlsxFill switch style.Fill.Type { case "gradient": if len(style.Fill.Color) != 2 { break } var gradient xlsxGradientFill switch style.Fill.Shading { case 0, 1, 2, 3: gradient.Degree = variants[style.Fill.Shading] case 4: gradient.Type = "path" case 5: gradient.Type = "path" gradient.Bottom = 0.5 gradient.Left = 0.5 gradient.Right = 0.5 gradient.Top = 0.5 } var stops []*xlsxGradientFillStop for index, color := range style.Fill.Color { var stop xlsxGradientFillStop stop.Position = float64(index) stop.Color.RGB = getPaletteColor(color) stops = append(stops, &stop) } gradient.Stop = stops fill.GradientFill = &gradient case "pattern": if style.Fill.Pattern > 18 || style.Fill.Pattern < 0 { break } if len(style.Fill.Color) < 1 { break } var pattern xlsxPatternFill pattern.PatternType = patterns[style.Fill.Pattern] if fg { if pattern.FgColor == nil { pattern.FgColor = new(xlsxColor) } pattern.FgColor.RGB = getPaletteColor(style.Fill.Color[0]) } else { if pattern.BgColor == nil { pattern.BgColor = new(xlsxColor) } pattern.BgColor.RGB = getPaletteColor(style.Fill.Color[0]) } fill.PatternFill = &pattern default: return nil } return &fill } // newAlignment provides a function to formatting information pertaining to // text alignment in cells. There are a variety of choices for how text is // aligned both horizontally and vertically, as well as indentation settings, // and so on. func newAlignment(style *Style) *xlsxAlignment { var alignment xlsxAlignment if style.Alignment != nil { alignment.Horizontal = style.Alignment.Horizontal alignment.Indent = style.Alignment.Indent alignment.JustifyLastLine = style.Alignment.JustifyLastLine alignment.ReadingOrder = style.Alignment.ReadingOrder alignment.RelativeIndent = style.Alignment.RelativeIndent alignment.ShrinkToFit = style.Alignment.ShrinkToFit alignment.TextRotation = style.Alignment.TextRotation alignment.Vertical = style.Alignment.Vertical alignment.WrapText = style.Alignment.WrapText } return &alignment } // newProtection provides a function to set protection properties associated // with the cell. func newProtection(style *Style) *xlsxProtection { var protection xlsxProtection if style.Protection != nil { protection.Hidden = &style.Protection.Hidden protection.Locked = &style.Protection.Locked } return &protection } // getBorderID provides a function to get border ID. If given border is not // exist, will return -1. func getBorderID(styleSheet *xlsxStyleSheet, style *Style) (borderID int) { borderID = -1 if styleSheet.Borders == nil || len(style.Border) == 0 { return } for idx, border := range styleSheet.Borders.Border { if reflect.DeepEqual(*border, *newBorders(style)) { borderID = idx return } } return } // newBorders provides a function to add border elements in the styles.xml by // given borders format settings. func newBorders(style *Style) *xlsxBorder { var styles = []string{ "none", "thin", "medium", "dashed", "dotted", "thick", "double", "hair", "mediumDashed", "dashDot", "mediumDashDot", "dashDotDot", "mediumDashDotDot", "slantDashDot", } var border xlsxBorder for _, v := range style.Border { if 0 <= v.Style && v.Style < 14 { var color xlsxColor color.RGB = getPaletteColor(v.Color) switch v.Type { case "left": border.Left.Style = styles[v.Style] border.Left.Color = &color case "right": border.Right.Style = styles[v.Style] border.Right.Color = &color case "top": border.Top.Style = styles[v.Style] border.Top.Color = &color case "bottom": border.Bottom.Style = styles[v.Style] border.Bottom.Color = &color case "diagonalUp": border.Diagonal.Style = styles[v.Style] border.Diagonal.Color = &color border.DiagonalUp = true case "diagonalDown": border.Diagonal.Style = styles[v.Style] border.Diagonal.Color = &color border.DiagonalDown = true } } } return &border } // setCellXfs provides a function to set describes all of the formatting for a // cell. func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) int { var xf xlsxXf xf.FontID = intPtr(fontID) if fontID != 0 { xf.ApplyFont = boolPtr(true) } xf.NumFmtID = intPtr(numFmtID) if numFmtID != 0 { xf.ApplyNumberFormat = boolPtr(true) } xf.FillID = intPtr(fillID) if fillID != 0 { xf.ApplyFill = boolPtr(true) } xf.BorderID = intPtr(borderID) if borderID != 0 { xf.ApplyBorder = boolPtr(true) } style.CellXfs.Count++ xf.Alignment = alignment if alignment != nil { xf.ApplyAlignment = boolPtr(applyAlignment) } if applyProtection { xf.ApplyProtection = boolPtr(applyProtection) xf.Protection = protection } xfID := 0 xf.XfID = &xfID style.CellXfs.Xf = append(style.CellXfs.Xf, xf) return style.CellXfs.Count - 1 } // GetCellStyle provides a function to get cell style index by given worksheet // name and cell coordinates. func (f *File) GetCellStyle(sheet, axis string) (int, error) { ws, err := f.workSheetReader(sheet) if err != nil { return 0, err } cellData, col, _, err := f.prepareCell(ws, sheet, axis) if err != nil { return 0, err } return f.prepareCellStyle(ws, col, cellData.S), err } // SetCellStyle provides a function to add style attribute for cells by given // worksheet name, coordinate area and style ID. Note that diagonalDown and // diagonalUp type border should be use same color in the same coordinate // area. SetCellStyle will overwrite the existing styles for the cell, it // won't append or merge style with existing styles. // // For example create a borders of cell H9 on Sheet1: // // style, err := f.NewStyle(`{"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}]}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Set gradient fill with vertical variants shading styles for cell H9 on // Sheet1: // // style, err := f.NewStyle(`{"fill":{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":1}}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Set solid style pattern fill for cell H9 on Sheet1: // // style, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Set alignment style for cell H9 on Sheet1: // // style, err := f.NewStyle(`{"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}}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Dates and times in Excel are represented by real numbers, for example "Apr 7 // 2017 12:00 PM" is represented by the number 42920.5. Set date and time format // for cell H9 on Sheet1: // // f.SetCellValue("Sheet1", "H9", 42920.5) // style, err := f.NewStyle(`{"number_format": 22}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Set font style for cell H9 on Sheet1: // // style, err := f.NewStyle(`{"font":{"bold":true,"italic":true,"family":"Times New Roman","size":36,"color":"#777777"}}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // // Hide and lock for cell H9 on Sheet1: // // style, err := f.NewStyle(`{"protection":{"hidden":true, "locked":true}}`) // if err != nil { // fmt.Println(err) // } // err = f.SetCellStyle("Sheet1", "H9", "H9", style) // func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error { hcol, hrow, err := CellNameToCoordinates(hcell) if err != nil { return err } vcol, vrow, err := CellNameToCoordinates(vcell) if err != nil { return err } // Normalize the coordinate area, such correct C1:B3 to B1:C3. if vcol < hcol { vcol, hcol = hcol, vcol } if vrow < hrow { vrow, hrow = hrow, vrow } hcolIdx := hcol - 1 hrowIdx := hrow - 1 vcolIdx := vcol - 1 vrowIdx := vrow - 1 ws, err := f.workSheetReader(sheet) if err != nil { return err } prepareSheetXML(ws, vcol, vrow) makeContiguousColumns(ws, hrow, vrow, vcol) ws.Lock() defer ws.Unlock() for r := hrowIdx; r <= vrowIdx; r++ { for k := hcolIdx; k <= vcolIdx; k++ { ws.SheetData.Row[r].C[k].S = styleID } } return err } // SetConditionalFormat provides a function to create conditional formatting // rule for cell value. Conditional formatting is a feature of Excel which // allows you to apply a format to a cell or a range of cells based on certain // criteria. // // The type option is a required parameter and it has no default value. // Allowable type values and their associated parameters are: // // Type | Parameters // ---------------+------------------------------------ // cell | criteria // | value // | minimum // | maximum // date | criteria // | value // | minimum // | maximum // time_period | criteria // text | criteria // | value // average | criteria // duplicate | (none) // unique | (none) // top | criteria // | value // bottom | criteria // | value // blanks | (none) // no_blanks | (none) // errors | (none) // no_errors | (none) // 2_color_scale | min_type // | max_type // | min_value // | max_value // | min_color // | max_color // 3_color_scale | min_type // | mid_type // | max_type // | min_value // | mid_value // | max_value // | min_color // | mid_color // | max_color // data_bar | min_type // | max_type // | min_value // | max_value // | bar_color // formula | criteria // // The criteria parameter is used to set the criteria by which the cell data // will be evaluated. It has no default value. The most common criteria as // applied to {"type":"cell"} are: // // between | // not between | // equal to | == // not equal to | != // greater than | > // less than | < // greater than or equal to | >= // less than or equal to | <= // // You can either use Excel's textual description strings, in the first column // above, or the more common symbolic alternatives. // // Additional criteria which are specific to other conditional format types are // shown in the relevant sections below. // // value: The value is generally used along with the criteria parameter to set // the rule by which the cell data will be evaluated: // // f.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format)) // // The value property can also be an cell reference: // // f.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"$C$1"}]`, format)) // // type: format - The format parameter is used to specify the format that will // be applied to the cell when the conditional formatting criterion is met. The // format is created using the NewConditionalStyle() method in the same way as // cell formats: // // format, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`) // if err != nil { // fmt.Println(err) // } // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format)) // // Note: In Excel, a conditional format is superimposed over the existing cell // format and not all cell format properties can be modified. Properties that // cannot be modified in a conditional format are font name, font size, // superscript and subscript, diagonal borders, all alignment properties and all // protection properties. // // Excel specifies some default formats to be used with conditional formatting. // These can be replicated using the following excelize formats: // // // Rose format for bad conditional. // format1, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`) // // // Light yellow format for neutral conditional. // format2, err = f.NewConditionalStyle(`{"font":{"color":"#9B5713"},"fill":{"type":"pattern","color":["#FEEAA0"],"pattern":1}}`) // // // Light green format for good conditional. // format3, err = f.NewConditionalStyle(`{"font":{"color":"#09600B"},"fill":{"type":"pattern","color":["#C7EECF"],"pattern":1}}`) // // type: minimum - The minimum parameter is used to set the lower limiting value // when the criteria is either "between" or "not between". // // // Hightlight cells rules: between... // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format)) // // type: maximum - The maximum parameter is used to set the upper limiting value // when the criteria is either "between" or "not between". See the previous // example. // // type: average - The average type is used to specify Excel's "Average" style // conditional format: // // // Top/Bottom rules: Above Average... // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format1)) // // // Top/Bottom rules: Below Average... // f.SetConditionalFormat("Sheet1", "B1:B10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format2)) // // type: duplicate - The duplicate type is used to highlight duplicate cells in a range: // // // Hightlight cells rules: Duplicate Values... // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format)) // // type: unique - The unique type is used to highlight unique cells in a range: // // // Hightlight cells rules: Not Equal To... // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format)) // // type: top - The top type is used to specify the top n values by number or percentage in a range: // // // Top/Bottom rules: Top 10. // f.SetConditionalFormat("Sheet1", "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6"}]`, format)) // // The criteria can be used to indicate that a percentage condition is required: // // f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format)) // // type: 2_color_scale - The 2_color_scale type is used to specify Excel's "2 // Color Scale" style conditional format: // // // Color scales: 2 color. // f.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`) // // This conditional type can be modified with min_type, max_type, min_value, // max_value, min_color and max_color, see below. // // type: 3_color_scale - The 3_color_scale type is used to specify Excel's "3 // Color Scale" style conditional format: // // // Color scales: 3 color. // f.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`) // // This conditional type can be modified with min_type, mid_type, max_type, // min_value, mid_value, max_value, min_color, mid_color and max_color, see // below. // // type: data_bar - The data_bar type is used to specify Excel's "Data Bar" // style conditional format. // // min_type - The min_type and max_type properties are available when the conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_type is available for 3_color_scale. The properties are used as follows: // // // Data Bars: Gradient Fill. // f.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`) // // The available min/mid/max types are: // // min (for min_type only) // num // percent // percentile // formula // max (for max_type only) // // mid_type - Used for 3_color_scale. Same as min_type, see above. // // max_type - Same as min_type, see above. // // min_value - The min_value and max_value properties are available when the // conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The // mid_value is available for 3_color_scale. // // mid_value - Used for 3_color_scale. Same as min_value, see above. // // max_value - Same as min_value, see above. // // min_color - The min_color and max_color properties are available when the // conditional formatting type is 2_color_scale, 3_color_scale or data_bar. // The mid_color is available for 3_color_scale. The properties are used as // follows: // // // Color scales: 3 color. // f.SetConditionalFormat("Sheet1", "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`) // // mid_color - Used for 3_color_scale. Same as min_color, see above. // // max_color - Same as min_color, see above. // // bar_color - Used for data_bar. Same as min_color, see above. // func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { var format []*formatConditional err := json.Unmarshal([]byte(formatSet), &format) if err != nil { return err } drawContFmtFunc := map[string]func(p int, ct string, fmtCond *formatConditional) *xlsxCfRule{ "cellIs": drawCondFmtCellIs, "top10": drawCondFmtTop10, "aboveAverage": drawCondFmtAboveAverage, "duplicateValues": drawCondFmtDuplicateUniqueValues, "uniqueValues": drawCondFmtDuplicateUniqueValues, "2_color_scale": drawCondFmtColorScale, "3_color_scale": drawCondFmtColorScale, "dataBar": drawCondFmtDataBar, "expression": drawConfFmtExp, } ws, err := f.workSheetReader(sheet) if err != nil { return err } cfRule := []*xlsxCfRule{} for p, v := range format { var vt, ct string var ok bool // "type" is a required parameter, check for valid validation types. vt, ok = validType[v.Type] if ok { // Check for valid criteria types. ct, ok = criteriaType[v.Criteria] if ok || vt == "expression" { drawfunc, ok := drawContFmtFunc[vt] if ok { cfRule = append(cfRule, drawfunc(p, ct, v)) } } } } ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{ SQRef: area, CfRule: cfRule, }) return err } // UnsetConditionalFormat provides a function to unset the conditional format // by given worksheet name and range. func (f *File) UnsetConditionalFormat(sheet, area string) error { ws, err := f.workSheetReader(sheet) if err != nil { return err } for i, cf := range ws.ConditionalFormatting { if cf.SQRef == area { ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i], ws.ConditionalFormatting[i+1:]...) return nil } } return nil } // drawCondFmtCellIs provides a function to create conditional formatting rule // for cell value (include between, not between, equal, not equal, greater // than and less than) by given priority, criteria type and format settings. func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule { c := &xlsxCfRule{ Priority: p + 1, Type: validType[format.Type], Operator: ct, DxfID: &format.Format, } // "between" and "not between" criteria require 2 values. _, ok := map[string]bool{"between": true, "notBetween": true}[ct] if ok { c.Formula = append(c.Formula, format.Minimum) c.Formula = append(c.Formula, format.Maximum) } _, ok = map[string]bool{"equal": true, "notEqual": true, "greaterThan": true, "lessThan": true, "greaterThanOrEqual": true, "lessThanOrEqual": true, "containsText": true, "notContains": true, "beginsWith": true, "endsWith": true}[ct] if ok { c.Formula = append(c.Formula, format.Value) } return c } // drawCondFmtTop10 provides a function to create conditional formatting rule // for top N (default is top 10) by given priority, criteria type and format // settings. func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule { c := &xlsxCfRule{ Priority: p + 1, Bottom: format.Type == "bottom", Type: validType[format.Type], Rank: 10, DxfID: &format.Format, Percent: format.Percent, } rank, err := strconv.Atoi(format.Value) if err == nil { c.Rank = rank } return c } // drawCondFmtAboveAverage provides a function to create conditional // formatting rule for above average and below average by given priority, // criteria type and format settings. func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxCfRule { return &xlsxCfRule{ Priority: p + 1, Type: validType[format.Type], AboveAverage: &format.AboveAverage, DxfID: &format.Format, } } // drawCondFmtDuplicateUniqueValues provides a function to create conditional // formatting rule for duplicate and unique values by given priority, criteria // type and format settings. func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditional) *xlsxCfRule { return &xlsxCfRule{ Priority: p + 1, Type: validType[format.Type], DxfID: &format.Format, } } // drawCondFmtColorScale provides a function to create conditional formatting // rule for color scale (include 2 color scale and 3 color scale) by given // priority, criteria type and format settings. func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfRule { minValue := format.MinValue if minValue == "" { minValue = "0" } maxValue := format.MaxValue if maxValue == "" { maxValue = "0" } midValue := format.MidValue if midValue == "" { midValue = "50" } c := &xlsxCfRule{ Priority: p + 1, Type: "colorScale", ColorScale: &xlsxColorScale{ Cfvo: []*xlsxCfvo{ {Type: format.MinType, Val: minValue}, }, Color: []*xlsxColor{ {RGB: getPaletteColor(format.MinColor)}, }, }, } if validType[format.Type] == "3_color_scale" { c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MidType, Val: midValue}) c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MidColor)}) } c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType, Val: maxValue}) c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)}) return c } // drawCondFmtDataBar provides a function to create conditional formatting // rule for data bar by given priority, criteria type and format settings. func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule { return &xlsxCfRule{ Priority: p + 1, Type: validType[format.Type], DataBar: &xlsxDataBar{ Cfvo: []*xlsxCfvo{{Type: format.MinType}, {Type: format.MaxType}}, Color: []*xlsxColor{{RGB: getPaletteColor(format.BarColor)}}, }, } } // drawConfFmtExp provides a function to create conditional formatting rule // for expression by given priority, criteria type and format settings. func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule { return &xlsxCfRule{ Priority: p + 1, Type: validType[format.Type], Formula: []string{format.Criteria}, DxfID: &format.Format, } } // getPaletteColor provides a function to convert the RBG color by given // string. func getPaletteColor(color string) string { return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1) } // themeReader provides a function to get the pointer to the xl/theme/theme1.xml // structure after deserialization. func (f *File) themeReader() *xlsxTheme { var ( err error theme xlsxTheme ) if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))). Decode(&theme); err != nil && err != io.EOF { log.Printf("xml decoder error: %s", err) } return &theme } // ThemeColor applied the color with tint value. func ThemeColor(baseColor string, tint float64) string { if tint == 0 { return "FF" + baseColor } r, _ := strconv.ParseUint(baseColor[:2], 16, 64) g, _ := strconv.ParseUint(baseColor[2:4], 16, 64) b, _ := strconv.ParseUint(baseColor[4:6], 16, 64) var h, s, l float64 if r <= math.MaxUint8 && g <= math.MaxUint8 && b <= math.MaxUint8 { h, s, l = RGBToHSL(uint8(r), uint8(g), uint8(b)) } if tint < 0 { l *= (1 + tint) } else { l = l*(1-tint) + (1 - (1 - tint)) } br, bg, bb := HSLToRGB(h, s, l) return fmt.Sprintf("FF%02X%02X%02X", br, bg, bb) }