找回密码
 立即注册
首页 业界区 安全 同一个灰色,POI取出来却是白色:一次Excel颜色解析的踩 ...

同一个灰色,POI取出来却是白色:一次Excel颜色解析的踩坑记录

王妍芳 2025-10-1 19:05:40
最近在写一个后端转换功能,导入xlsx文件,给他解析成JSON格式。结果测试时发现一个问题:两个看起来一模一样的灰色单元格,代码读出来的颜色不一样。属实给我整懵逼了。
比如下面这两个单元格
1.png


  • A1:能正常拿到灰值,比如 #808080
  • B1:拿到的是 FFFFFF —— 白的
明明都是手动设置的灰色,样式也一样,拿到的填充颜色确不一样。
我第一反应是前景色/背景色搞混了,于是加了一个判断,前景色拿不到就从背景色拿:
  1. XSSFColor color = (XSSFColor) cellStyle.getFillForegroundColorColor();
  2. if (color == null) {
  3.     color = (XSSFColor) cellStyle.getFillBackgroundColorColor();
  4. }
复制代码
最后一试,还是白的。
折腾半天,最后发现关键在于:B1 是个主题色,而 A1 是个普通颜色(换了三个AI,问了半天结合起来才折腾出来)。
主题色是什么?

在 Excel 里用“主题颜色”面板选的颜色,比如“辅助色 3”、“深色 1”这类,都叫主题色。它们不是固定的 RGB 值,而是指向当前工作簿的主题定义。也就是说,换一套主题,这颜色可能就变了。
POI 的 XSSFColor 提供了一个方法判断:
  1. color.isThemed() // 返回 true 表示是主题色
复制代码
如果是主题色,直接调 .getRGB() 或 .getARGB() 是拿不到有效值的,经常就是 null 或 FFFFFF。
得结合 ThemesTable 把真正的颜色算出来。
但这还没完。
即使你从 ThemesTable 拿到了主题里的基础颜色,结果可能还是不对。因为还有一个东西叫 tint。
什么是 tint

tint 是 Excel 里用来微调主题颜色明暗的一个参数,取值范围是 -1.0 到 1.0:

  • tint = 0:原色
  • tint > 0:越接近 1,颜色越亮(往白色混合)
  • tint < 0:越接近 -1,颜色越暗(往黑色混合)
比如你选了个“辅助色 3”是深灰,但 Excel 默认给它加了个 tint = -0.24,意思是在这个主题色基础上再压暗一点。如果你忽略这个值,直接拿原始主题色,就会偏亮,甚至变成白色。
所以,只处理主题色不处理 tint,拿到的颜色也可能有问题
最终解决方案

核心思路:

  • 判断是否为 isThemed()
  • 如果是,从 ThemesTable 中取出对应索引的基础 RGB
  • 再根据 getTint() 值,对颜色做明暗调整
下面是封装好的工具类,可以直接用:
  1. import lombok.extern.slf4j.Slf4j;
  2. import org.apache.poi.xssf.model.ThemesTable;
  3. import org.apache.poi.xssf.usermodel.XSSFColor;
  4. import java.awt.*;
  5. @Slf4j
  6. public class ThemeColorResolver {
  7.     /**
  8.      * 获取颜色的ARGB十六进制字符串表示
  9.      *
  10.      * @param themedColor XSSFColor对象
  11.      * @param theme       主题表
  12.      * @return ARGB格式的十六进制字符串(如 " FFFF0000 " 表示红色),如果无法解析则返回null
  13.      */
  14.     public static String getActualColorHex(XSSFColor themedColor, ThemesTable theme) {
  15.         Color color = resolveActualColor(themedColor, theme);
  16.         return color != null ? toARGBHex(color) : null;
  17.     }
  18.     /**
  19.      * 获取颜色的RGB十六进制字符串表示(不带Alpha通道)
  20.      *
  21.      * @param themedColor XSSFColor对象
  22.      * @param theme       主题表
  23.      * @return RGB格式的十六进制字符串(如 " FF0000 " 表示红色),如果无法解析则返回null
  24.      */
  25.     public static String getActualColorRGBHex(XSSFColor themedColor, ThemesTable theme) {
  26.         Color color = resolveActualColor(themedColor, theme);
  27.         return color != null ? toRGBHex(color) : null;
  28.     }
  29.     /**
  30.      * 获取颜色的java.awt.Color对象
  31.      *
  32.      * @param themedColor XSSFColor对象
  33.      * @param theme       主题表
  34.      * @return 解析后的Color对象,如果无法解析则返回null
  35.      */
  36.     public static Color getActualColor(XSSFColor themedColor, ThemesTable theme) {
  37.         return resolveActualColor(themedColor, theme);
  38.     }
  39.     /**
  40.      * 核心解析方法
  41.      */
  42.     private static Color resolveActualColor(XSSFColor themedColor, ThemesTable theme) {
  43.         // 如果不是主题色,直接返回ARGB颜色
  44.         if (!themedColor.isThemed()) {
  45.             byte[] argb = themedColor.getARGB();
  46.             if (argb == null || argb.length < 3) {
  47.                 log.warn("Non-themed color has invalid ARGB value");
  48.                 return null;
  49.             }
  50.             return new Color(argb[1] & 0xFF, argb[2] & 0xFF, argb[3] & 0xFF, argb[0] & 0xFF);
  51.         }
  52.         // 解析主题颜色
  53.         try {
  54.             int themeIndex = themedColor.getTheme();
  55.             XSSFColor themeColor = theme.getThemeColor(themeIndex);
  56.             byte[] themeRgb = themeColor.getRGB();
  57.             if (themeRgb == null || themeRgb.length < 3) {
  58.                 log.warn("Theme color not found for index: {}", themeIndex);
  59.                 return null;
  60.             }
  61.             // 获取基础颜色(不考虑tint)
  62.             Color baseColor = new Color(themeRgb[0] & 0xFF, themeRgb[1] & 0xFF, themeRgb[2] & 0xFF);
  63.             // 应用tint调整
  64.             double tint = themedColor.getTint();
  65.             if (tint != 0.0) {
  66.                 return applyTint(baseColor, tint);
  67.             }
  68.             return baseColor;
  69.         } catch (Exception e) {
  70.             log.error("Error resolving theme color", e);
  71.             return null;
  72.         }
  73.     }
  74.     /**
  75.      * 应用tint值调整颜色
  76.      */
  77.     private static Color applyTint(Color baseColor, double tint) {
  78.         int r = baseColor.getRed();
  79.         int g = baseColor.getGreen();
  80.         int b = baseColor.getBlue();
  81.         if (tint > 0) {
  82.             // 变亮(混合白色)
  83.             r = (int) (r + (255 - r) * tint);
  84.             g = (int) (g + (255 - g) * tint);
  85.             b = (int) (b + (255 - b) * tint);
  86.         } else if (tint < 0) {
  87.             // 变暗(混合黑色)
  88.             r = (int) (r * (1 + tint));
  89.             g = (int) (g * (1 + tint));
  90.             b = (int) (b * (1 + tint));
  91.         }
  92.         // 确保值在0-255范围内
  93.         r = Math.min(255, Math.max(0, r));
  94.         g = Math.min(255, Math.max(0, g));
  95.         b = Math.min(255, Math.max(0, b));
  96.         return new Color(r, g, b);
  97.     }
  98.     /**
  99.      * 将Color转换为ARGB十六进制字符串
  100.      */
  101.     private static String toARGBHex(Color color) {
  102.         return String.format("%02X%02X%02X%02X",
  103.                 color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue());
  104.     }
  105.     /**
  106.      * 将Color转换为RGB十六进制字符串(不带Alpha)
  107.      */
  108.     private static String toRGBHex(Color color) {
  109.         return String.format("%02X%02X%02X",
  110.                 color.getRed(), color.getGreen(), color.getBlue());
  111.     }
  112. }
复制代码
使用方式
  1. // 获取工作簿的主题表
  2. ThemesTable themes = workbook instanceof XSSFWorkbook ? ((XSSFWorkbook) workbook).getThemesTable() : null;
  3. // 获取单元格样式颜色
  4. XSSFColor fill = (XSSFColor) cellStyle.getFillForegroundColorColor();
  5. Color actualColor = ThemeColorResolver.getActualColor(fill, themes);
  6. if (actualColor != null) {
  7.     String hex = ThemeColorResolver.toHex(actualColor); // 如 #808080
  8. }
复制代码
总结


  • Excel 的“主题色”不是固定值,不能直接 .getRGB()
  • isThemed() 是第一步判断
  • 最终颜色 = 主题基础色 + tint 调整

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册