C#中通过OpenXml库将带Tint的基础色转为System.Drawing.Color问题排查
排查OpenXml获取Excel单元格背景色失败的问题
核心问题分析
1. Tint值范围与计算逻辑错误
Excel的tint值范围是**-1到1**(负数表示加深颜色,正数表示提亮颜色),但原代码强行将tint限制在0-1之间,导致所有深色系主题颜色计算完全错误。同时,原tint计算公式未区分正负场景,无法正确实现Excel的颜色明暗调整逻辑。
2. 未处理Indexed索引颜色
Excel单元格背景色可能使用Indexed索引颜色(通过ColorType.Index属性指定),原代码完全忽略这种情况,导致使用索引颜色的单元格无法获取正确背景色。
3. Theme颜色的空引用风险
如果Excel文件未设置主题(WorkbookPart.ThemePart为null),原代码直接访问ThemePart.Theme会抛出空引用异常,无容错处理。
4. 颜色优先级逻辑错误
Excel中颜色属性的优先级是:Rgb > Indexed > Theme > Auto,但原代码中Theme判断会覆盖之前的Rgb设置,导致同时存在Rgb和Theme属性时,错误使用主题颜色而非指定的RGB颜色。
修复后的代码
private static BooleanResult<System.Drawing.Color> PrintColorType(SpreadsheetDocument sd, DocumentFormat.OpenXml.Spreadsheet.ColorType ct) { try { System.Drawing.Color color = System.Drawing.Color.White; // 优先级1:处理RGB颜色 if (ct.Rgb != null) { color = ColorsHelper.HtmlRgbToColor(ct.Rgb.Value); } // 优先级2:处理Indexed索引颜色 else if (ct.Index != null) { var colorTable = sd.WorkbookPart.Workbook.StylesPart?.Stylesheet?.Colors?.IndexedColors; if (colorTable != null && ct.Index.Value >= 0 && ct.Index.Value < colorTable.ChildElements.Count) { var indexedColor = (DocumentFormat.OpenXml.Spreadsheet.IndexedColor)colorTable.ChildElements[(int)ct.Index.Value]; if (indexedColor.Rgb != null) { color = ColorsHelper.HtmlRgbToColor(indexedColor.Rgb.Value); } } else { color = GetDefaultIndexedColor((int)ct.Index.Value); } } // 优先级3:处理Theme主题颜色 else if (ct.Theme != null && sd.WorkbookPart.ThemePart != null) { var colorScheme = sd.WorkbookPart.ThemePart.Theme.ThemeElements.ColorScheme; DocumentFormat.OpenXml.Drawing.Color2Type c2t = ct.Theme.Value switch { DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Light1 => colorScheme.Light1, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Dark1 => colorScheme.Dark1, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Light2 => colorScheme.Light2, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Dark2 => colorScheme.Dark2, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent1 => colorScheme.Accent1, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent2 => colorScheme.Accent2, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent3 => colorScheme.Accent3, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent4 => colorScheme.Accent4, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent5 => colorScheme.Accent5, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Accent6 => colorScheme.Accent6, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.Hyperlink => colorScheme.Hyperlink, DocumentFormat.OpenXml.Spreadsheet.ThemeColorValues.FollowedHyperlink => colorScheme.FollowedHyperlink, _ => colorScheme.Light1 }; if (c2t?.RgbColorModelHex?.Val != null) { var baseColor = ColorsHelper.HtmlRgbToColor(c2t.RgbColorModelHex.Val); color = ParseRgbColortint(ct.Tint ?? 0, baseColor); } } // 优先级4:处理Auto自动颜色 else if (ct.Auto != null && ct.Auto.Value) { color = System.Drawing.Color.White; } return BooleanResult<System.Drawing.Color>.SuccessResult(color); } catch (ArgumentException ex) { return BooleanResult<System.Drawing.Color>.FailResult( $"PrintColorType: ArgumentException: {ex.GetFullMessage()}"); } catch (InvalidOperationException ex) { return BooleanResult<System.Drawing.Color>.FailResult( $"PrintColorType: InvalidOperationException: {ex.GetFullMessage()}"); } catch (Exception ex) { return BooleanResult<System.Drawing.Color>.FailResult( $"PrintColorType: Exception: {ex.GetFullMessage()}"); } } private static int TintComponent(int component, double tint) { double newValue; if (tint >= 0) { // 正数tint:提亮颜色,混合白色 newValue = component * (1 - tint) + 255 * tint; } else { // 负数tint:加深颜色,降低亮度 newValue = component * (1 + tint); } // 限制值在0-255之间 return Math.Max(0, Math.Min(255, (int)Math.Round(newValue))); } private static System.Drawing.Color ParseRgbColortint(double tint, System.Drawing.Color baseColor) { int tintedRed = TintComponent(baseColor.R, tint); int tintedGreen = TintComponent(baseColor.G, tint); int tintedBlue = TintComponent(baseColor.B, tint); return System.Drawing.Color.FromArgb(tintedRed, tintedGreen, tintedBlue); } // 辅助方法:获取Excel默认索引颜色 private static System.Drawing.Color GetDefaultIndexedColor(int index) { return index switch { 0 => System.Drawing.Color.White, 1 => System.Drawing.Color.Black, 2 => System.Drawing.Color.Red, 3 => System.Drawing.Color.Green, 4 => System.Drawing.Color.Blue, 5 => System.Drawing.Color.Yellow, 6 => System.Drawing.Color.Magenta, 7 => System.Drawing.Color.Cyan, // 可根据OpenXml规范补充更多索引颜色映射 _ => System.Drawing.Color.White }; }
关键修复点
- 修正
tint计算逻辑,区分正负场景,不再强制限制范围; - 新增Indexed索引颜色处理,支持自定义颜色表和默认颜色映射;
- 增加
ThemePart空判断,避免空引用异常; - 调整颜色优先级顺序,确保
Rgb优先于Theme; - 使用switch表达式明确映射Theme枚举到ColorScheme对应元素,避免索引错误。
内容的提问来源于stack exchange,提问作者Pritam Adak
相关产品推荐
相关产品推荐

