Skip to content

Commit 7938d2f

Browse files
authored
This closes qax-os#314, closes qax-os#1520 and closes qax-os#1521 (qax-os#1574)
- Add new function GetStyle support for get style definition
1 parent bdbb39e commit 7938d2f

File tree

6 files changed

+446
-4
lines changed

6 files changed

+446
-4
lines changed

lib.go

+24
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,30 @@ func float64Ptr(f float64) *float64 { return &f }
430430
// stringPtr returns a pointer to a string with the given value.
431431
func stringPtr(s string) *string { return &s }
432432

433+
// Value extracts string data type text from a attribute value.
434+
func (attr *attrValString) Value() string {
435+
if attr != nil && attr.Val != nil {
436+
return *attr.Val
437+
}
438+
return ""
439+
}
440+
441+
// Value extracts boolean data type value from a attribute value.
442+
func (attr *attrValBool) Value() bool {
443+
if attr != nil && attr.Val != nil {
444+
return *attr.Val
445+
}
446+
return false
447+
}
448+
449+
// Value extracts float64 data type numeric from a attribute value.
450+
func (attr *attrValFloat) Value() float64 {
451+
if attr != nil && attr.Val != nil {
452+
return *attr.Val
453+
}
454+
return 0
455+
}
456+
433457
// MarshalXML convert the boolean data type to literal values 0 or 1 on
434458
// serialization.
435459
func (avb attrValBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error {

lib_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ func TestInStrSlice(t *testing.T) {
238238
assert.EqualValues(t, -1, inStrSlice([]string{}, "", true))
239239
}
240240

241+
func TestAttrValue(t *testing.T) {
242+
assert.Empty(t, (&attrValString{}).Value())
243+
assert.False(t, (&attrValBool{}).Value())
244+
assert.Zero(t, (&attrValFloat{}).Value())
245+
}
246+
241247
func TestBoolValMarshal(t *testing.T) {
242248
bold := true
243249
node := &xlsxFont{B: &attrValBool{Val: &bold}}

numfmt.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4692,7 +4692,7 @@ func (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) {
46924692
if fmtCode, ok := builtInNumFmt[numFmtID]; ok {
46934693
return fmtCode, true
46944694
}
4695-
if (27 <= numFmtID && numFmtID <= 36) || (50 <= numFmtID && numFmtID <= 81) {
4695+
if isLangNumFmt(numFmtID) {
46964696
if f.options.CultureInfo == CultureNameEnUS {
46974697
return f.langNumFmtFuncEnUS(numFmtID), true
46984698
}

styles.go

+304-1
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,303 @@ func (f *File) NewStyle(style *Style) (int, error) {
10321032
return setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection)
10331033
}
10341034

1035+
var (
1036+
// styleBorders list all types of the cell border style.
1037+
styleBorders = []string{
1038+
"none",
1039+
"thin",
1040+
"medium",
1041+
"dashed",
1042+
"dotted",
1043+
"thick",
1044+
"double",
1045+
"hair",
1046+
"mediumDashed",
1047+
"dashDot",
1048+
"mediumDashDot",
1049+
"dashDotDot",
1050+
"mediumDashDotDot",
1051+
"slantDashDot",
1052+
}
1053+
// styleBorderTypes list all types of the cell border.
1054+
styleBorderTypes = []string{
1055+
"left", "right", "top", "bottom", "diagonalUp", "diagonalDown",
1056+
}
1057+
// styleFillPatterns list all types of the cell fill style.
1058+
styleFillPatterns = []string{
1059+
"none",
1060+
"solid",
1061+
"mediumGray",
1062+
"darkGray",
1063+
"lightGray",
1064+
"darkHorizontal",
1065+
"darkVertical",
1066+
"darkDown",
1067+
"darkUp",
1068+
"darkGrid",
1069+
"darkTrellis",
1070+
"lightHorizontal",
1071+
"lightVertical",
1072+
"lightDown",
1073+
"lightUp",
1074+
"lightGrid",
1075+
"lightTrellis",
1076+
"gray125",
1077+
"gray0625",
1078+
}
1079+
// styleFillVariants list all preset variants of the fill style.
1080+
styleFillVariants = []xlsxGradientFill{
1081+
{Degree: 90, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1082+
{Degree: 270, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1083+
{Degree: 90, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},
1084+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1085+
{Degree: 180, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1086+
{Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},
1087+
{Degree: 45, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1088+
{Degree: 255, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1089+
{Degree: 45, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},
1090+
{Degree: 135, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1091+
{Degree: 315, Stop: []*xlsxGradientFillStop{{}, {Position: 1}}},
1092+
{Degree: 135, Stop: []*xlsxGradientFillStop{{}, {Position: 0.5}, {Position: 1}}},
1093+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: "path"},
1094+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: "path", Left: 1, Right: 1},
1095+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: "path", Bottom: 1, Top: 1},
1096+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: "path", Bottom: 1, Left: 1, Right: 1, Top: 1},
1097+
{Stop: []*xlsxGradientFillStop{{}, {Position: 1}}, Type: "path", Bottom: 0.5, Left: 0.5, Right: 0.5, Top: 0.5},
1098+
}
1099+
)
1100+
1101+
// getThemeColor provides a function to convert theme color or index color to
1102+
// RGB color.
1103+
func (f *File) getThemeColor(clr *xlsxColor) string {
1104+
var RGB string
1105+
if clr == nil || f.Theme == nil {
1106+
return RGB
1107+
}
1108+
if clrScheme := f.Theme.ThemeElements.ClrScheme; clr.Theme != nil {
1109+
if val, ok := map[int]*string{
1110+
0: &clrScheme.Lt1.SysClr.LastClr,
1111+
1: &clrScheme.Dk1.SysClr.LastClr,
1112+
2: clrScheme.Lt2.SrgbClr.Val,
1113+
3: clrScheme.Dk2.SrgbClr.Val,
1114+
4: clrScheme.Accent1.SrgbClr.Val,
1115+
5: clrScheme.Accent2.SrgbClr.Val,
1116+
6: clrScheme.Accent3.SrgbClr.Val,
1117+
7: clrScheme.Accent4.SrgbClr.Val,
1118+
8: clrScheme.Accent5.SrgbClr.Val,
1119+
9: clrScheme.Accent6.SrgbClr.Val,
1120+
}[*clr.Theme]; ok && val != nil {
1121+
return strings.TrimPrefix(ThemeColor(*val, clr.Tint), "FF")
1122+
}
1123+
}
1124+
if len(clr.RGB) == 6 {
1125+
return clr.RGB
1126+
}
1127+
if len(clr.RGB) == 8 {
1128+
return strings.TrimPrefix(clr.RGB, "FF")
1129+
}
1130+
if f.Styles.Colors != nil && clr.Indexed < len(f.Styles.Colors.IndexedColors.RgbColor) {
1131+
return strings.TrimPrefix(ThemeColor(strings.TrimPrefix(f.Styles.Colors.IndexedColors.RgbColor[clr.Indexed].RGB, "FF"), clr.Tint), "FF")
1132+
}
1133+
if clr.Indexed < len(IndexedColorMapping) {
1134+
return strings.TrimPrefix(ThemeColor(IndexedColorMapping[clr.Indexed], clr.Tint), "FF")
1135+
}
1136+
return RGB
1137+
}
1138+
1139+
// extractBorders provides a function to extract borders styles settings by
1140+
// given border styles definition.
1141+
func (f *File) extractBorders(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1142+
if xf.ApplyBorder != nil && *xf.ApplyBorder &&
1143+
xf.BorderID != nil && s.Borders != nil &&
1144+
*xf.BorderID < len(s.Borders.Border) {
1145+
if bdr := s.Borders.Border[*xf.BorderID]; bdr != nil {
1146+
1147+
var borders []Border
1148+
extractBorder := func(lineType string, line xlsxLine) {
1149+
if line.Style != "" {
1150+
borders = append(borders, Border{
1151+
Type: lineType,
1152+
Color: f.getThemeColor(line.Color),
1153+
Style: inStrSlice(styleBorders, line.Style, false),
1154+
})
1155+
}
1156+
}
1157+
for i, line := range []xlsxLine{
1158+
bdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal,
1159+
} {
1160+
if i < 4 {
1161+
extractBorder(styleBorderTypes[i], line)
1162+
}
1163+
if i == 4 && bdr.DiagonalUp {
1164+
extractBorder(styleBorderTypes[i], line)
1165+
}
1166+
if i == 5 && bdr.DiagonalDown {
1167+
extractBorder(styleBorderTypes[i], line)
1168+
}
1169+
}
1170+
style.Border = borders
1171+
}
1172+
}
1173+
}
1174+
1175+
// extractFills provides a function to extract fill styles settings by
1176+
// given fill styles definition.
1177+
func (f *File) extractFills(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1178+
if fl := s.Fills.Fill[*xf.FillID]; fl != nil {
1179+
var fill Fill
1180+
if fl.GradientFill != nil {
1181+
fill.Type = "gradient"
1182+
for shading, variants := range styleFillVariants {
1183+
if fl.GradientFill.Bottom == variants.Bottom &&
1184+
fl.GradientFill.Degree == variants.Degree &&
1185+
fl.GradientFill.Left == variants.Left &&
1186+
fl.GradientFill.Right == variants.Right &&
1187+
fl.GradientFill.Top == variants.Top &&
1188+
fl.GradientFill.Type == variants.Type {
1189+
fill.Shading = shading
1190+
break
1191+
}
1192+
}
1193+
for _, stop := range fl.GradientFill.Stop {
1194+
fill.Color = append(fill.Color, f.getThemeColor(&stop.Color))
1195+
}
1196+
}
1197+
if fl.PatternFill != nil {
1198+
fill.Type = "pattern"
1199+
fill.Pattern = inStrSlice(styleFillPatterns, fl.PatternFill.PatternType, false)
1200+
if fl.PatternFill.FgColor != nil {
1201+
fill.Color = []string{f.getThemeColor(fl.PatternFill.FgColor)}
1202+
}
1203+
}
1204+
style.Fill = fill
1205+
}
1206+
}
1207+
1208+
// extractFont provides a function to extract font styles settings by given
1209+
// font styles definition.
1210+
func (f *File) extractFont(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1211+
if xf.ApplyFont != nil && *xf.ApplyFont &&
1212+
xf.FontID != nil && s.Fonts != nil &&
1213+
*xf.FontID < len(s.Fonts.Font) {
1214+
if fnt := s.Fonts.Font[*xf.FontID]; fnt != nil {
1215+
var font Font
1216+
if fnt.B != nil {
1217+
font.Bold = fnt.B.Value()
1218+
}
1219+
if fnt.I != nil {
1220+
font.Italic = fnt.I.Value()
1221+
}
1222+
if fnt.U != nil {
1223+
font.Underline = fnt.U.Value()
1224+
}
1225+
if fnt.Name != nil {
1226+
font.Family = fnt.Name.Value()
1227+
}
1228+
if fnt.Sz != nil {
1229+
font.Size = fnt.Sz.Value()
1230+
}
1231+
if fnt.Strike != nil {
1232+
font.Strike = fnt.Strike.Value()
1233+
}
1234+
if fnt.Color != nil {
1235+
font.Color = strings.TrimPrefix(fnt.Color.RGB, "FF")
1236+
font.ColorIndexed = fnt.Color.Indexed
1237+
font.ColorTheme = fnt.Color.Theme
1238+
font.ColorTint = fnt.Color.Tint
1239+
}
1240+
style.Font = &font
1241+
}
1242+
}
1243+
}
1244+
1245+
// extractNumFmt provides a function to extract number format by given styles
1246+
// definition.
1247+
func (f *File) extractNumFmt(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1248+
if xf.NumFmtID != nil {
1249+
numFmtID := *xf.NumFmtID
1250+
if _, ok := builtInNumFmt[numFmtID]; ok || isLangNumFmt(numFmtID) {
1251+
style.NumFmt = numFmtID
1252+
return
1253+
}
1254+
if s.NumFmts != nil {
1255+
for _, numFmt := range s.NumFmts.NumFmt {
1256+
style.CustomNumFmt = &numFmt.FormatCode
1257+
if strings.Contains(numFmt.FormatCode, ";[Red]") {
1258+
style.NegRed = true
1259+
}
1260+
for numFmtID, fmtCode := range currencyNumFmt {
1261+
if style.NegRed {
1262+
fmtCode += ";[Red]" + fmtCode
1263+
}
1264+
if numFmt.FormatCode == fmtCode {
1265+
style.NumFmt = numFmtID
1266+
}
1267+
}
1268+
}
1269+
}
1270+
}
1271+
}
1272+
1273+
// extractAlignment provides a function to extract alignment format by
1274+
// given style definition.
1275+
func (f *File) extractAlignment(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1276+
if xf.ApplyAlignment != nil && *xf.ApplyAlignment && xf.Alignment != nil {
1277+
style.Alignment = &Alignment{
1278+
Horizontal: xf.Alignment.Horizontal,
1279+
Indent: xf.Alignment.Indent,
1280+
JustifyLastLine: xf.Alignment.JustifyLastLine,
1281+
ReadingOrder: xf.Alignment.ReadingOrder,
1282+
RelativeIndent: xf.Alignment.RelativeIndent,
1283+
ShrinkToFit: xf.Alignment.ShrinkToFit,
1284+
TextRotation: xf.Alignment.TextRotation,
1285+
Vertical: xf.Alignment.Vertical,
1286+
WrapText: xf.Alignment.WrapText,
1287+
}
1288+
}
1289+
}
1290+
1291+
// extractProtection provides a function to extract protection settings by
1292+
// given format definition.
1293+
func (f *File) extractProtection(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
1294+
if xf.ApplyProtection != nil && *xf.ApplyProtection && xf.Protection != nil {
1295+
style.Protection = &Protection{}
1296+
if xf.Protection.Hidden != nil {
1297+
style.Protection.Hidden = *xf.Protection.Hidden
1298+
}
1299+
if xf.Protection.Locked != nil {
1300+
style.Protection.Locked = *xf.Protection.Locked
1301+
}
1302+
}
1303+
}
1304+
1305+
// GetStyle get style details by given style index.
1306+
func (f *File) GetStyle(idx int) (*Style, error) {
1307+
var style *Style
1308+
f.mu.Lock()
1309+
s, err := f.stylesReader()
1310+
if err != nil {
1311+
return style, err
1312+
}
1313+
f.mu.Unlock()
1314+
if idx < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= idx {
1315+
return style, newInvalidStyleID(idx)
1316+
}
1317+
style = &Style{}
1318+
xf := s.CellXfs.Xf[idx]
1319+
if xf.ApplyFill != nil && *xf.ApplyFill &&
1320+
xf.FillID != nil && s.Fills != nil &&
1321+
*xf.FillID < len(s.Fills.Fill) {
1322+
f.extractFills(xf, s, style)
1323+
}
1324+
f.extractBorders(xf, s, style)
1325+
f.extractFont(xf, s, style)
1326+
f.extractAlignment(xf, s, style)
1327+
f.extractProtection(xf, s, style)
1328+
f.extractNumFmt(xf, s, style)
1329+
return style, nil
1330+
}
1331+
10351332
// getXfIDFuncs provides a function to get xfID by given style.
10361333
var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{
10371334
"numFmt": func(numFmtID int, xf xlsxXf, style *Style) bool {
@@ -1406,9 +1703,15 @@ func getCustomNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (customNumFmtID
14061703
return
14071704
}
14081705

1706+
// isLangNumFmt provides a function to returns if a given number format ID is a
1707+
// built-in language glyphs number format code.
1708+
func isLangNumFmt(ID int) bool {
1709+
return (27 <= ID && ID <= 36) || (50 <= ID && ID <= 62) || (67 <= ID && ID <= 81)
1710+
}
1711+
14091712
// setLangNumFmt provides a function to set number format code with language.
14101713
func setLangNumFmt(style *Style) int {
1411-
if (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) {
1714+
if isLangNumFmt(style.NumFmt) {
14121715
return style.NumFmt
14131716
}
14141717
return 0

0 commit comments

Comments
 (0)