From 586cf38f1c398e85be197702251392ddabb893b4 Mon Sep 17 00:00:00 2001 From: Greg Woolsey Date: Fri, 26 May 2017 23:14:48 +0000 Subject: [PATCH] Bug 60898 - XSSFColor's getARGB() method returns a wrong color value when a workbook has a custom indexed color teach XSSFColor and most things that create instances about indexed colors. Null is a valid value for IndexedColorMap instances - the existing built-in default colors are used. Whenever a workbook style is accessible in the call hierarchy its color mappings are passed down now. Thanks for the unit test in the issue, it now passes. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1796359 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/usermodel/HSSFExtendedColor.java | 16 +++++ .../poi/ss/usermodel/ExtendedColor.java | 49 +++++++------ .../poi/ss/usermodel/IndexedColors.java | 12 +++- .../apache/poi/xssf/model/StylesTable.java | 56 ++++++++------- .../apache/poi/xssf/model/ThemesTable.java | 12 +++- .../xssf/usermodel/CustomIndexedColorMap.java | 65 +++++++++++++++++ .../usermodel/DefaultIndexedColorMap.java | 44 ++++++++++++ .../poi/xssf/usermodel/IndexedColorMap.java | 31 ++++++++ .../xssf/usermodel/XSSFBorderFormatting.java | 16 +++-- .../poi/xssf/usermodel/XSSFCellStyle.java | 20 +++--- .../apache/poi/xssf/usermodel/XSSFColor.java | 66 ++++++++++++++---- .../usermodel/XSSFColorScaleFormatting.java | 13 ++-- .../XSSFConditionalFormattingRule.java | 25 ++++--- .../xssf/usermodel/XSSFCreationHelper.java | 3 +- .../xssf/usermodel/XSSFDataBarFormatting.java | 6 +- .../xssf/usermodel/XSSFDxfStyleProvider.java | 11 +-- .../apache/poi/xssf/usermodel/XSSFFont.java | 12 +++- .../xssf/usermodel/XSSFFontFormatting.java | 8 ++- .../xssf/usermodel/XSSFPatternFormatting.java | 8 ++- .../apache/poi/xssf/usermodel/XSSFSheet.java | 4 +- .../poi/xssf/usermodel/XSSFTableStyle.java | 5 +- .../usermodel/extensions/XSSFCellBorder.java | 22 +++++- .../usermodel/extensions/XSSFCellFill.java | 9 ++- .../poi/xssf/usermodel/TestXSSFCellStyle.java | 2 +- .../poi/xssf/usermodel/TestXSSFColor.java | 23 +++++- .../poi/xssf/usermodel/TestXSSFFont.java | 2 +- .../poi/xssf/usermodel/TestXSSFSheet.java | 8 +-- .../extensions/TestXSSFCellFill.java | 8 +-- .../spreadsheet/customIndexedColors.xlsx | Bin 0 -> 19534 bytes 29 files changed, 426 insertions(+), 130 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/CustomIndexedColorMap.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/DefaultIndexedColorMap.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/IndexedColorMap.java create mode 100644 test-data/spreadsheet/customIndexedColors.xlsx diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFExtendedColor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFExtendedColor.java index e6c8c1bf5..b17826318 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFExtendedColor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFExtendedColor.java @@ -22,6 +22,7 @@ import static org.apache.poi.hssf.record.common.ExtendedColor.TYPE_INDEXED; import static org.apache.poi.hssf.record.common.ExtendedColor.TYPE_RGB; import static org.apache.poi.hssf.record.common.ExtendedColor.TYPE_THEMED; +import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.ExtendedColor; /** @@ -106,4 +107,19 @@ public class HSSFExtendedColor extends ExtendedColor { public void setTint(double tint) { color.setTint(tint); } + + protected byte[] getIndexedRGB() { + if (isIndexed() && getIndex() > 0) { + int indexNum = getIndex(); + HSSFColor indexed = HSSFColor.getIndexHash().get(indexNum); + if (indexed != null) { + byte[] rgb = new byte[3]; + rgb[0] = (byte) indexed.getTriplet()[0]; + rgb[1] = (byte) indexed.getTriplet()[1]; + rgb[2] = (byte) indexed.getTriplet()[2]; + return rgb; + } + } // else + return null; + } } diff --git a/src/java/org/apache/poi/ss/usermodel/ExtendedColor.java b/src/java/org/apache/poi/ss/usermodel/ExtendedColor.java index 2259bbfc2..616a61709 100644 --- a/src/java/org/apache/poi/ss/usermodel/ExtendedColor.java +++ b/src/java/org/apache/poi/ss/usermodel/ExtendedColor.java @@ -18,77 +18,80 @@ package org.apache.poi.ss.usermodel; import java.util.Locale; -import org.apache.poi.hssf.util.HSSFColor; - /** * Represents a XSSF-style color (based on either a * {@link org.apache.poi.xssf.usermodel.XSSFColor} or a * {@link org.apache.poi.hssf.record.common.ExtendedColor} */ public abstract class ExtendedColor implements Color { + + /** + * + * @param clr awt Color to set + */ protected void setColor(java.awt.Color clr) { setRGB(new byte[]{(byte)clr.getRed(), (byte)clr.getGreen(), (byte)clr.getBlue()}); } /** - * A boolean value indicating the color is automatic + * @return true if the color is automatic */ public abstract boolean isAuto(); /** - * A boolean value indicating the color is indexed + * @return true if the color is indexed */ public abstract boolean isIndexed(); /** - * A boolean value indicating the color is RGB / ARGB + * @return true if the color is RGB / ARGB */ public abstract boolean isRGB(); /** - * A boolean value indicating the color is from a Theme + * @return true if the color is from a Theme */ public abstract boolean isThemed(); /** - * Indexed Color value, if {@link #isIndexed()} is true + * @return Indexed Color index value, if {@link #isIndexed()} is true */ public abstract short getIndex(); /** - * Index of Theme color, if {@link #isThemed()} is true + * @return Index of Theme color, if {@link #isThemed()} is true */ public abstract int getTheme(); /** - * Standard Red Green Blue ctColor value (RGB). + * @return Standard Red Green Blue ctColor value (RGB) bytes. * If there was an A (Alpha) value, it will be stripped. */ public abstract byte[] getRGB(); + /** - * Standard Alpha Red Green Blue ctColor value (ARGB). + * @return Standard Alpha Red Green Blue ctColor value (ARGB) bytes. */ public abstract byte[] getARGB(); /** - * RGB or ARGB or null + * @return RGB or ARGB bytes or null */ protected abstract byte[] getStoredRBG(); /** * Sets the Red Green Blue or Alpha Red Green Blue + * @param rgb bytes */ public abstract void setRGB(byte[] rgb); + /** + * @return RGB or ARGB bytes, either stored or by index + */ protected byte[] getRGBOrARGB() { if (isIndexed() && getIndex() > 0) { - int indexNum = getIndex(); - HSSFColor indexed = HSSFColor.getIndexHash().get(indexNum); - if (indexed != null) { - byte[] rgb = new byte[3]; - rgb[0] = (byte) indexed.getTriplet()[0]; - rgb[1] = (byte) indexed.getTriplet()[1]; - rgb[2] = (byte) indexed.getTriplet()[2]; + byte[] rgb = getIndexedRGB(); + if (rgb != null) { return rgb; } } @@ -96,9 +99,14 @@ public abstract class ExtendedColor implements Color { // Grab the colour return getStoredRBG(); } + + /** + * @return index color RGB bytes, if {@link #isIndexed()} == true, null if not indexed or index is invalid + */ + protected abstract byte[] getIndexedRGB(); /** - * Standard Red Green Blue ctColor value (RGB) with applied tint. + * @return Standard Red Green Blue ctColor value (RGB) bytes with applied tint. * Alpha values are ignored. */ public byte[] getRGBWithTint() { @@ -118,7 +126,7 @@ public abstract class ExtendedColor implements Color { } /** - * Return the ARGB value in hex format, eg FF00FF00. + * @return the ARGB value in hex string format, eg FF00FF00. * Works for both regular and indexed colours. */ public String getARGBHex() { @@ -142,6 +150,7 @@ public abstract class ExtendedColor implements Color { /** * Sets the ARGB value from hex format, eg FF0077FF. * Only works for regular (non-indexed) colours + * @param argb color ARGB hex string */ public void setARGBHex(String argb) { if (argb.length() == 6 || argb.length() == 8) { diff --git a/src/java/org/apache/poi/ss/usermodel/IndexedColors.java b/src/java/org/apache/poi/ss/usermodel/IndexedColors.java index 92ebc08a1..bb5b91169 100644 --- a/src/java/org/apache/poi/ss/usermodel/IndexedColors.java +++ b/src/java/org/apache/poi/ss/usermodel/IndexedColors.java @@ -31,7 +31,15 @@ package org.apache.poi.ss.usermodel; */ public enum IndexedColors { - // 0-7? + // 0-7 duplicates of 8-15 for compatibility (OOXML spec pt.1 sec. 18.8.27) + BLACK1(0), + WHITE1(1), + RED1(2), + BRIGHT_GREEN1(3), + BLUE1(4), + YELLOW1(5), + PINK1(6), + TURQUOISE1(7), BLACK(8), WHITE(9), RED(10), @@ -51,7 +59,7 @@ public enum IndexedColors { CORNFLOWER_BLUE(24), MAROON(25), LEMON_CHIFFON(26), - // 27? + LIGHT_TURQUOISE1(27), ORCHID(28), CORAL(29), ROYAL_BLUE(30), diff --git a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java index 8582cf398..6e1055585 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java @@ -41,6 +41,9 @@ import org.apache.poi.ss.usermodel.FontFamily; import org.apache.poi.ss.usermodel.FontScheme; import org.apache.poi.ss.usermodel.TableStyle; import org.apache.poi.util.Internal; +import org.apache.poi.xssf.usermodel.CustomIndexedColorMap; +import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; +import org.apache.poi.xssf.usermodel.IndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFBuiltinTableStyle; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFFactory; @@ -51,24 +54,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder; import org.apache.poi.xssf.usermodel.extensions.XSSFCellFill; import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorders; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellStyleXfs; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellXfs; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxf; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDxfs; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFills; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFonts; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmt; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTNumFmts; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyle; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableStyles; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; /** * Table of styles shared across all sheets in a workbook. @@ -83,7 +69,9 @@ public class StylesTable extends POIXMLDocumentPart { private final List dxfs = new ArrayList(); private final Map tableStyles = new HashMap(); - + + private IndexedColorMap indexedColors = new DefaultIndexedColorMap(); + /** * The first style id available for use as a custom style */ @@ -170,6 +158,8 @@ public class StylesTable extends POIXMLDocumentPart { public void setTheme(ThemesTable theme) { this.theme = theme; + if (theme != null) theme.setColorMap(getIndexedColors()); + // Pass the themes table along to things which need to // know about it, but have already been created by now for(XSSFFont font : fonts) { @@ -188,7 +178,7 @@ public class StylesTable extends POIXMLDocumentPart { public void ensureThemesTable() { if (theme != null) return; - theme = (ThemesTable)workbook.createRelationship(XSSFRelation.THEME, XSSFFactory.getInstance()); + setTheme((ThemesTable)workbook.createRelationship(XSSFRelation.THEME, XSSFFactory.getInstance())); } /** @@ -204,6 +194,11 @@ public class StylesTable extends POIXMLDocumentPart { CTStylesheet styleSheet = doc.getStyleSheet(); // Grab all the different bits we care about + + // keep this first, as some constructors below want it + IndexedColorMap customColors = CustomIndexedColorMap.fromColors(styleSheet.getColors()); + if (customColors != null) indexedColors = customColors; + CTNumFmts ctfmts = styleSheet.getNumFmts(); if( ctfmts != null){ for (CTNumFmt nfmt : ctfmts.getNumFmtArray()) { @@ -217,7 +212,7 @@ public class StylesTable extends POIXMLDocumentPart { int idx = 0; for (CTFont font : ctfonts.getFontArray()) { // Create the font and save it. Themes Table supplied later - XSSFFont f = new XSSFFont(font, idx); + XSSFFont f = new XSSFFont(font, idx, indexedColors); fonts.add(f); idx++; } @@ -225,14 +220,14 @@ public class StylesTable extends POIXMLDocumentPart { CTFills ctfills = styleSheet.getFills(); if(ctfills != null){ for (CTFill fill : ctfills.getFillArray()) { - fills.add(new XSSFCellFill(fill)); + fills.add(new XSSFCellFill(fill, indexedColors)); } } CTBorders ctborders = styleSheet.getBorders(); if(ctborders != null) { for (CTBorder border : ctborders.getBorderArray()) { - borders.add(new XSSFCellBorder(border)); + borders.add(new XSSFCellBorder(border, indexedColors)); } } @@ -249,7 +244,7 @@ public class StylesTable extends POIXMLDocumentPart { if (ctTableStyles != null) { int idx = 0; for (CTTableStyle style : Arrays.asList(ctTableStyles.getTableStyleArray())) { - tableStyles.put(style.getName(), new XSSFTableStyle(idx, styleDxfs, style)); + tableStyles.put(style.getName(), new XSSFTableStyle(idx, styleDxfs, style, indexedColors)); idx++; } } @@ -716,8 +711,8 @@ public class StylesTable extends POIXMLDocumentPart { fonts.add(xssfFont); CTFill[] ctFill = createDefaultFills(); - fills.add(new XSSFCellFill(ctFill[0])); - fills.add(new XSSFCellFill(ctFill[1])); + fills.add(new XSSFCellFill(ctFill[0], indexedColors)); + fills.add(new XSSFCellFill(ctFill[1], indexedColors)); CTBorder ctBorder = createDefaultBorder(); borders.add(new XSSFCellBorder(ctBorder)); @@ -757,7 +752,7 @@ public class StylesTable extends POIXMLDocumentPart { private static XSSFFont createDefaultFont() { CTFont ctFont = CTFont.Factory.newInstance(); - XSSFFont xssfFont=new XSSFFont(ctFont, 0); + XSSFFont xssfFont=new XSSFFont(ctFont, 0, null); xssfFont.setFontHeightInPoints(XSSFFont.DEFAULT_FONT_SIZE); xssfFont.setColor(XSSFFont.DEFAULT_FONT_COLOR);//setTheme xssfFont.setFontName(XSSFFont.DEFAULT_FONT_NAME); @@ -881,4 +876,11 @@ public class StylesTable extends POIXMLDocumentPart { } return null; } + + /** + * @return default or custom indexed color to RGB mapping + */ + public IndexedColorMap getIndexedColors() { + return indexedColors; + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/model/ThemesTable.java b/src/ooxml/java/org/apache/poi/xssf/model/ThemesTable.java index 9c3b1de40..396b59c94 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/ThemesTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/ThemesTable.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.xssf.usermodel.IndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; @@ -60,6 +61,7 @@ public class ThemesTable extends POIXMLDocumentPart { public final String name; } + private IndexedColorMap colorMap; private ThemeDocument theme; /** @@ -95,6 +97,14 @@ public class ThemesTable extends POIXMLDocumentPart { this.theme = theme; } + /** + * called from {@link StylesTable} when setting theme, used to adjust colors if a custom indexed mapping is defined + * @param colorMap + */ + protected void setColorMap(IndexedColorMap colorMap) { + this.colorMap = colorMap; + } + /** * Convert a theme "index" (as used by fonts etc) into a color. * @param idx A theme "index" @@ -132,7 +142,7 @@ public class ThemesTable extends POIXMLDocumentPart { } else { return null; } - return new XSSFColor(rgb); + return new XSSFColor(rgb, colorMap); } /** diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/CustomIndexedColorMap.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/CustomIndexedColorMap.java new file mode 100644 index 000000000..3cfed531b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/CustomIndexedColorMap.java @@ -0,0 +1,65 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xssf.usermodel; + +import java.util.List; + +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColors; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRgbColor; + +/** + * custom index color map, i.e. from the styles.xml definition + */ +public class CustomIndexedColorMap implements IndexedColorMap { + + private final byte[][] colorIndex; + + /** + * @param colors array of RGB triplets indexed by color index + */ + private CustomIndexedColorMap(byte [][] colors) { + this.colorIndex = colors; + } + + public byte[] getRGB(int index) { + if (colorIndex == null || index < 0 || index >= colorIndex.length) return null; + return colorIndex[index]; + } + + /** + * OOXML spec says if this exists it must have all indexes. + *

+ * From the OOXML Spec, Part 1, section 18.8.27: + *

+ * This element contains a sequence of RGB color values that correspond to color indexes (zero-based). When + * using the default indexed color palette, the values are not written out, but instead are implied. When the color + * palette has been modified from default, then the entire color palette is written out. + * + * @param colors CTColors from styles.xml possibly defining a custom color indexing scheme + * @return custom indexed color map or null if none defined in the document + */ + public static CustomIndexedColorMap fromColors(CTColors colors) { + if (colors == null || ! colors.isSetIndexedColors()) return null; + + List rgbColorList = colors.getIndexedColors().getRgbColorList(); + byte[][] customColorIndex = new byte[rgbColorList.size()][3]; + for (int i=0; i < rgbColorList.size(); i++) { + customColorIndex[i] = rgbColorList.get(i).getRgb(); + } + return new CustomIndexedColorMap(customColorIndex); + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/DefaultIndexedColorMap.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/DefaultIndexedColorMap.java new file mode 100644 index 000000000..0d2980b4b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/DefaultIndexedColorMap.java @@ -0,0 +1,44 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xssf.usermodel; + +import org.apache.poi.hssf.util.HSSFColor; + +/** + * Uses the legacy colors defined in HSSF for index lookups + */ +public class DefaultIndexedColorMap implements IndexedColorMap { + + /** + * @see org.apache.poi.xssf.usermodel.IndexedColorMap#getRGB(int) + */ + public byte[] getRGB(int index) { + return getDefaultRGB(index); + } + + /** + * @param index + * @return RGB bytes from HSSF default color by index + */ + public static byte[] getDefaultRGB(int index) { + HSSFColor hssfColor = HSSFColor.getIndexHash().get(index); + if (hssfColor == null) return null; + short[] rgbShort = hssfColor.getTriplet(); + return new byte[] {(byte) rgbShort[0], (byte) rgbShort[1], (byte) rgbShort[2]}; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/IndexedColorMap.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/IndexedColorMap.java new file mode 100644 index 000000000..eac3a037e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/IndexedColorMap.java @@ -0,0 +1,31 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xssf.usermodel; + +/** + * Interface for color index to RGB mappings. + * May be either the default, built-in mappings + * or custom mappings defined in the document. + */ +public interface IndexedColorMap { + + /** + * @param index color index to look up + * @return the RGB array for the index, or null if the index is invalid/undefined + */ + byte[] getRGB(int index); +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBorderFormatting.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBorderFormatting.java index f700aecf6..621e9434f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBorderFormatting.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFBorderFormatting.java @@ -20,19 +20,21 @@ import org.apache.poi.ss.usermodel.BorderFormatting; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.Color; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STBorderStyle; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorderPr; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STBorderStyle; /** * XSSF high level representation for Border Formatting component * of Conditional Formatting settings */ public class XSSFBorderFormatting implements BorderFormatting { + IndexedColorMap _colorMap; CTBorder _border; - /*package*/ XSSFBorderFormatting(CTBorder border) { + /*package*/ XSSFBorderFormatting(CTBorder border, IndexedColorMap colorMap) { _border = border; + _colorMap = colorMap; } /** @@ -125,7 +127,7 @@ public class XSSFBorderFormatting implements BorderFormatting { if(!_border.isSetBottom()) return null; CTBorderPr pr = _border.getBottom(); - return new XSSFColor(pr.getColor()); + return new XSSFColor(pr.getColor(), _colorMap); } @Override public short getBottomBorderColor() { @@ -139,7 +141,7 @@ public class XSSFBorderFormatting implements BorderFormatting { if(!_border.isSetDiagonal()) return null; CTBorderPr pr = _border.getDiagonal(); - return new XSSFColor(pr.getColor()); + return new XSSFColor(pr.getColor(), _colorMap); } @Override public short getDiagonalBorderColor() { @@ -153,7 +155,7 @@ public class XSSFBorderFormatting implements BorderFormatting { if(!_border.isSetLeft()) return null; CTBorderPr pr = _border.getLeft(); - return new XSSFColor(pr.getColor()); + return new XSSFColor(pr.getColor(), _colorMap); } @Override public short getLeftBorderColor() { @@ -167,7 +169,7 @@ public class XSSFBorderFormatting implements BorderFormatting { if(!_border.isSetRight()) return null; CTBorderPr pr = _border.getRight(); - return new XSSFColor(pr.getColor()); + return new XSSFColor(pr.getColor(), _colorMap); } @Override public short getRightBorderColor() { @@ -181,7 +183,7 @@ public class XSSFBorderFormatting implements BorderFormatting { if(!_border.isSetTop()) return null; CTBorderPr pr = _border.getTop(); - return new XSSFColor(pr.getColor()); + return new XSSFColor(pr.getColor(), _colorMap); } @Override public short getTopBorderColor() { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java index 6e66f9926..7d18fa93b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java @@ -204,14 +204,14 @@ public class XSSFCellStyle implements CellStyle { } private void addFill(CTFill fill) { - int idx = _stylesSource.putFill(new XSSFCellFill(fill)); + int idx = _stylesSource.putFill(new XSSFCellFill(fill,_stylesSource.getIndexedColors())); _cellXf.setFillId(idx); _cellXf.setApplyFill(true); } private void addBorder(CTBorder border) { - int idx = _stylesSource.putBorder(new XSSFCellBorder(border, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(border, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -834,7 +834,7 @@ public class XSSFCellStyle implements CellStyle { if(border == BorderStyle.NONE) ct.unsetBottom(); else pr.setStyle(STBorderStyle.Enum.forInt(border.getCode() + 1)); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme, _stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -864,7 +864,7 @@ public class XSSFCellStyle implements CellStyle { if(border == BorderStyle.NONE) ct.unsetLeft(); else pr.setStyle(STBorderStyle.Enum.forInt(border.getCode() + 1)); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme, _stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -895,7 +895,7 @@ public class XSSFCellStyle implements CellStyle { if(border == BorderStyle.NONE) ct.unsetRight(); else pr.setStyle(STBorderStyle.Enum.forInt(border.getCode() + 1)); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -926,7 +926,7 @@ public class XSSFCellStyle implements CellStyle { if(border == BorderStyle.NONE) ct.unsetTop(); else pr.setStyle(STBorderStyle.Enum.forInt(border.getCode() + 1)); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -957,7 +957,7 @@ public class XSSFCellStyle implements CellStyle { if(color != null) pr.setColor(color.getCTColor()); else pr.unsetColor(); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -1244,7 +1244,7 @@ public class XSSFCellStyle implements CellStyle { if(color != null) pr.setColor(color.getCTColor()); else pr.unsetColor(); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -1299,7 +1299,7 @@ public class XSSFCellStyle implements CellStyle { if(color != null) pr.setColor(color.getCTColor()); else pr.unsetColor(); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); @@ -1356,7 +1356,7 @@ public class XSSFCellStyle implements CellStyle { if(color != null) pr.setColor(color.getCTColor()); else pr.unsetColor(); - int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme)); + int idx = _stylesSource.putBorder(new XSSFCellBorder(ct, _theme,_stylesSource.getIndexedColors())); _cellXf.setBorderId(idx); _cellXf.setApplyBorder(true); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java index 441f42fdb..e7cd869f1 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java @@ -22,6 +22,7 @@ import org.apache.poi.ss.usermodel.Color; import org.apache.poi.ss.usermodel.ExtendedColor; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; /** @@ -29,33 +30,62 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; */ public class XSSFColor extends ExtendedColor { private final CTColor ctColor; + private final IndexedColorMap indexedColorMap; /** - * Create an instance of XSSFColor from the supplied XML bean + * Create an instance of XSSFColor from the supplied XML bean, with default color indexes + * @param color + * @deprecated 3.17 beta 1 - pass the workbook styles indexed color map, if any */ + @Deprecated + @Removal(version="3.19") public XSSFColor(CTColor color) { + this(color, new DefaultIndexedColorMap()); + } + + /** + * Create an instance of XSSFColor from the supplied XML bean, with the given color indexes + * @param color + * @param map + */ + public XSSFColor(CTColor color, IndexedColorMap map) { this.ctColor = color; + this.indexedColorMap = map; } /** - * Create an new instance of XSSFColor + * Create an new instance of XSSFColor, without knowledge of any custom indexed colors. + * This is OK for just transiently setting indexes, etc. but is discouraged in read/get uses */ public XSSFColor() { - this.ctColor = CTColor.Factory.newInstance(); + this(CTColor.Factory.newInstance(), null); } + /** + * TEST ONLY - does not know about custom indexed colors + * @param clr awt Color + */ public XSSFColor(java.awt.Color clr) { this(); setColor(clr); } - public XSSFColor(byte[] rgb) { - this(); + /** + * + * @param rgb bytes + * @param colorMap + */ + public XSSFColor(byte[] rgb, IndexedColorMap colorMap) { + this(CTColor.Factory.newInstance(), colorMap); ctColor.setRgb(rgb); } - public XSSFColor(IndexedColors indexedColor) { - this(); + /** + * @param indexedColor color index (Enum named for default colors) + * @param colorMap + */ + public XSSFColor(IndexedColors indexedColor, IndexedColorMap colorMap) { + this(CTColor.Factory.newInstance(), colorMap); ctColor.setIndexed(indexedColor.index); } @@ -67,7 +97,7 @@ public class XSSFColor extends ExtendedColor { return ctColor.getAuto(); } /** - * A boolean value indicating the ctColor is automatic and system ctColor dependent. + * @param auto true if the ctColor is automatic and system ctColor dependent. */ public void setAuto(boolean auto) { ctColor.setAuto(auto); @@ -82,7 +112,7 @@ public class XSSFColor extends ExtendedColor { } /** - * A boolean value indicating the ctColor is RGB or ARGB based + * @return true if the ctColor is RGB or ARGB based */ @Override public boolean isRGB() { @@ -90,7 +120,7 @@ public class XSSFColor extends ExtendedColor { } /** - * A boolean value indicating the ctColor is Theme based + * @return true if the ctColor is Theme based */ @Override public boolean isThemed() { @@ -98,7 +128,7 @@ public class XSSFColor extends ExtendedColor { } /** - * A boolean value indicating if the ctColor has a alpha or not + * @return true if the ctColor has a alpha */ public boolean hasAlpha() { if (! ctColor.isSetRgb()) { @@ -108,7 +138,7 @@ public class XSSFColor extends ExtendedColor { } /** - * A boolean value indicating if the ctColor has a tint or not + * @return true if the ctColor has a tint */ public boolean hasTint() { if (!ctColor.isSetTint()) { @@ -125,7 +155,7 @@ public class XSSFColor extends ExtendedColor { return (short)ctColor.getIndexed(); } /** - * Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors. + * @return Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors. */ public short getIndexed() { return getIndex(); @@ -133,6 +163,7 @@ public class XSSFColor extends ExtendedColor { /** * Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors. + * @param indexed color index */ public void setIndexed(int indexed) { ctColor.setIndexed(indexed); @@ -185,6 +216,14 @@ public class XSSFColor extends ExtendedColor { return ctColor.getRgb(); } + protected byte[] getIndexedRGB() { + if (isIndexed()) { + if (indexedColorMap != null) return indexedColorMap.getRGB(getIndex()); + return DefaultIndexedColorMap.getDefaultRGB(getIndex()); + } + return null; + } + /** * Standard Alpha Red Green Blue ctColor value (ARGB). */ @@ -205,6 +244,7 @@ public class XSSFColor extends ExtendedColor { /** * Index into the collection, referencing a particular or * value expressed in the Theme part. + * @param theme index */ public void setTheme(int theme) { ctColor.setTheme(theme); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColorScaleFormatting.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColorScaleFormatting.java index 910e8b297..8a6b29d21 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColorScaleFormatting.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColorScaleFormatting.java @@ -30,10 +30,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColorScale; * component of Conditional Formatting settings */ public class XSSFColorScaleFormatting implements ColorScaleFormatting { - CTColorScale _scale; + private CTColorScale _scale; + private IndexedColorMap _indexedColorMap; - /*package*/ XSSFColorScaleFormatting(CTColorScale scale){ + /*package*/ XSSFColorScaleFormatting(CTColorScale scale, IndexedColorMap colorMap){ _scale = scale; + _indexedColorMap = colorMap; } public int getNumControlPoints() { @@ -54,7 +56,7 @@ public class XSSFColorScaleFormatting implements ColorScaleFormatting { CTColor[] ctcols = _scale.getColorArray(); XSSFColor[] c = new XSSFColor[ctcols.length]; for (int i=0; i 1 for stripes + * @param colorMap */ - public XSSFDxfStyleProvider(CTDxf dxf, int stripeSize) { + public XSSFDxfStyleProvider(CTDxf dxf, int stripeSize, IndexedColorMap colorMap) { this.stripeSize = stripeSize; + this.colorMap = colorMap; if (dxf == null) { border = null; font = null; number = null; fill = null; } else { - border = dxf.isSetBorder() ? new XSSFBorderFormatting(dxf.getBorder()) : null; - font = dxf.isSetFont() ? new XSSFFontFormatting(dxf.getFont()) : null; + border = dxf.isSetBorder() ? new XSSFBorderFormatting(dxf.getBorder(), colorMap) : null; + font = dxf.isSetFont() ? new XSSFFontFormatting(dxf.getFont(), colorMap) : null; if (dxf.isSetNumFmt()) { CTNumFmt numFmt = dxf.getNumFmt(); number = new ExcelNumberFormat((int) numFmt.getNumFmtId(), numFmt.getFormatCode()); } else { number = null; } - fill = dxf.isSetFill() ? new XSSFPatternFormatting(dxf.getFill()) : null; + fill = dxf.isSetFill() ? new XSSFPatternFormatting(dxf.getFill(), colorMap) : null; } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFont.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFont.java index 05b49671a..f7ef5fdc7 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFont.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFont.java @@ -60,6 +60,7 @@ public class XSSFFont implements Font { */ public static final short DEFAULT_FONT_COLOR = IndexedColors.BLACK.getIndex(); + private IndexedColorMap _indexedColorMap; private ThemesTable _themes; private CTFont _ctFont; private short _index; @@ -74,9 +75,16 @@ public class XSSFFont implements Font { _index = 0; } - public XSSFFont(CTFont font, int index) { + /** + * Called from parsing styles.xml + * @param font CTFont + * @param index font index + * @param colorMap for default or custom indexed colors + */ + public XSSFFont(CTFont font, int index, IndexedColorMap colorMap) { _ctFont = font; _index = (short)index; + _indexedColorMap = colorMap; } /** @@ -150,7 +158,7 @@ public class XSSFFont implements Font { public XSSFColor getXSSFColor() { CTColor ctColor = _ctFont.sizeOfColorArray() == 0 ? null : _ctFont.getColorArray(0); if(ctColor != null) { - XSSFColor color = new XSSFColor(ctColor); + XSSFColor color = new XSSFColor(ctColor, _indexedColorMap); if(_themes != null) { _themes.inheritFromThemeAsRequired(color); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFontFormatting.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFontFormatting.java index 8b3570ac9..c15d9cdfb 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFontFormatting.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFontFormatting.java @@ -33,10 +33,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STVerticalAlignRun; * @author Yegor Kozlov */ public class XSSFFontFormatting implements FontFormatting { - CTFont _font; + private IndexedColorMap _colorMap; + private CTFont _font; - /*package*/ XSSFFontFormatting(CTFont font){ + /*package*/ XSSFFontFormatting(CTFont font, IndexedColorMap colorMap) { _font = font; + _colorMap = colorMap; } /** @@ -111,7 +113,7 @@ public class XSSFFontFormatting implements FontFormatting { public XSSFColor getFontColor() { if(_font.sizeOfColorArray() == 0) return null; - return new XSSFColor(_font.getColorArray(0)); + return new XSSFColor(_font.getColorArray(0), _colorMap); } @Override diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPatternFormatting.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPatternFormatting.java index e23d61ae1..3cf711c33 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPatternFormatting.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPatternFormatting.java @@ -29,20 +29,22 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType; * @author Yegor Kozlov */ public class XSSFPatternFormatting implements PatternFormatting { + IndexedColorMap _colorMap; CTFill _fill; - XSSFPatternFormatting(CTFill fill){ + XSSFPatternFormatting(CTFill fill, IndexedColorMap colorMap) { _fill = fill; + _colorMap = colorMap; } public XSSFColor getFillBackgroundColorColor() { if(!_fill.isSetPatternFill()) return null; - return new XSSFColor(_fill.getPatternFill().getBgColor()); + return new XSSFColor(_fill.getPatternFill().getBgColor(), _colorMap); } public XSSFColor getFillForegroundColorColor() { if(!_fill.isSetPatternFill() || ! _fill.getPatternFill().isSetFgColor()) return null; - return new XSSFColor(_fill.getPatternFill().getFgColor()); + return new XSSFColor(_fill.getPatternFill().getFgColor(), _colorMap); } public short getFillPattern() { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index ddf8458d0..8d62661e6 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -4007,7 +4007,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { if (!pr.isSetTabColor()) { return null; } - return new XSSFColor(pr.getTabColor()); + return new XSSFColor(pr.getTabColor(), getWorkbook().getStylesSource().getIndexedColors()); } /** @@ -4020,7 +4020,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { @Removal(version="3.17") public void setTabColor(int colorIndex) { IndexedColors indexedColor = IndexedColors.fromInt(colorIndex); - XSSFColor color = new XSSFColor(indexedColor); + XSSFColor color = new XSSFColor(indexedColor, getWorkbook().getStylesSource().getIndexedColors()); setTabColor(color); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java index a46ab2d5b..0b7be4963 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTableStyle.java @@ -42,9 +42,10 @@ public class XSSFTableStyle implements TableStyle { * @param index style definition index or built-in ordinal depending on use * @param dxfs * @param tableStyle + * @param colorMap indexed color map - default or custom * @see TableStyle#getIndex() */ - public XSSFTableStyle(int index, CTDxfs dxfs, CTTableStyle tableStyle) { + public XSSFTableStyle(int index, CTDxfs dxfs, CTTableStyle tableStyle, IndexedColorMap colorMap) { this.name = tableStyle.getName(); this.index = index; for (CTTableStyleElement element : tableStyle.getTableStyleElementList()) { @@ -60,7 +61,7 @@ public class XSSFTableStyle implements TableStyle { } int stripeSize = 0; if (element.isSetSize()) stripeSize = (int) element.getSize(); - if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize); + if (dxf != null) dstyle = new XSSFDxfStyleProvider(dxf, stripeSize, colorMap); } elementMap.put(type, dstyle); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellBorder.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellBorder.java index b100bc210..5d4d67587 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellBorder.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellBorder.java @@ -19,6 +19,7 @@ package org.apache.poi.xssf.usermodel.extensions; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.xssf.model.ThemesTable; +import org.apache.poi.xssf.usermodel.IndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.util.Internal; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder; @@ -31,22 +32,37 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STBorderStyle; * Color is optional. */ public class XSSFCellBorder { + private IndexedColorMap _indexedColorMap; private ThemesTable _theme; private CTBorder border; /** * Creates a Cell Border from the supplied XML definition + * @param border + * @param theme + * @param colorMap */ - public XSSFCellBorder(CTBorder border, ThemesTable theme) { - this(border); + public XSSFCellBorder(CTBorder border, ThemesTable theme, IndexedColorMap colorMap) { + this(border, colorMap); this._theme = theme; } /** * Creates a Cell Border from the supplied XML definition + * @param border */ public XSSFCellBorder(CTBorder border) { + this(border, null); + } + + /** + * + * @param border + * @param colorMap + */ + public XSSFCellBorder(CTBorder border, IndexedColorMap colorMap) { this.border = border; + this._indexedColorMap = colorMap; } /** @@ -117,7 +133,7 @@ public class XSSFCellBorder { CTBorderPr borderPr = getBorder(side); if(borderPr != null && borderPr.isSetColor()) { - XSSFColor clr = new XSSFColor(borderPr.getColor()); + XSSFColor clr = new XSSFColor(borderPr.getColor(), _indexedColorMap); if(_theme != null) { _theme.inheritFromThemeAsRequired(clr); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellFill.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellFill.java index fd6a70ef4..5914b1d73 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellFill.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/extensions/XSSFCellFill.java @@ -20,6 +20,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPatternFill; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType; +import org.apache.poi.xssf.usermodel.IndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.util.Internal; @@ -29,6 +30,7 @@ import org.apache.poi.util.Internal; */ public final class XSSFCellFill { + private IndexedColorMap _indexedColorMap; private CTFill _fill; /** @@ -36,8 +38,9 @@ public final class XSSFCellFill { * * @param fill - fill */ - public XSSFCellFill(CTFill fill) { + public XSSFCellFill(CTFill fill, IndexedColorMap colorMap) { _fill = fill; + _indexedColorMap = colorMap; } /** @@ -57,7 +60,7 @@ public final class XSSFCellFill { if (ptrn == null) return null; CTColor ctColor = ptrn.getBgColor(); - return ctColor == null ? null : new XSSFColor(ctColor); + return ctColor == null ? null : new XSSFColor(ctColor, _indexedColorMap); } /** @@ -91,7 +94,7 @@ public final class XSSFCellFill { if (ptrn == null) return null; CTColor ctColor = ptrn.getFgColor(); - return ctColor == null ? null : new XSSFColor(ctColor); + return ctColor == null ? null : new XSSFColor(ctColor, _indexedColorMap); } /** diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java index c907d40db..7bc283ef5 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFCellStyle.java @@ -73,7 +73,7 @@ public class TestXSSFCellStyle { assertEquals(1, stylesTable.putBorder(borderB)); ctFill = CTFill.Factory.newInstance(); - XSSFCellFill fill = new XSSFCellFill(ctFill); + XSSFCellFill fill = new XSSFCellFill(ctFill, null); long fillId = stylesTable.putFill(fill); assertEquals(2, fillId); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java index 38406c30a..64d27548d 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java @@ -17,12 +17,16 @@ package org.apache.poi.xssf.usermodel; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.IOException; import org.apache.poi.xssf.XSSFTestDataSamples; import org.junit.Test; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColors; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRgbColor; public final class TestXSSFColor { @@ -180,4 +184,21 @@ public final class TestXSSFColor { wb.close(); } + + @Test + public void testCustomIndexedColour() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("customIndexedColors.xlsx"); + XSSFCell cell = wb.getSheetAt(1).getRow(0).getCell(0); + XSSFColor color = cell.getCellStyle().getFillForegroundColorColor(); + CTColors ctColors = wb.getStylesSource().getCTStylesheet().getColors(); + + CTRgbColor ctRgbColor = ctColors.getIndexedColors() + .getRgbColorList() + .get(color.getIndex()); + + String hexRgb = ctRgbColor.getDomNode().getAttributes().getNamedItem("rgb").getNodeValue(); + + assertEquals(hexRgb, color.getARGBHex()); + + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFont.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFont.java index 2eeb5e8bb..58e2f85ff 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFont.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFont.java @@ -256,7 +256,7 @@ public final class TestXSSFFont extends BaseTestFont{ byte[] bytes = Integer.toHexString(0xF1F1F1).getBytes(LocaleUtil.CHARSET_1252); color.setRgb(bytes); - XSSFColor newColor=new XSSFColor(color); + XSSFColor newColor=new XSSFColor(color, null); xssfFont.setColor(newColor); assertEquals(ctFont.getColorArray(0).getRgb()[2],newColor.getRGB()[2]); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index 27bfe5b2a..25de3892c 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -1892,7 +1892,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { try { XSSFSheet sh = wb.createSheet(); assertTrue(sh.getCTWorksheet().getSheetPr() == null || !sh.getCTWorksheet().getSheetPr().isSetTabColor()); - sh.setTabColor(new XSSFColor(IndexedColors.RED)); + sh.setTabColor(new XSSFColor(IndexedColors.RED, null)); assertTrue(sh.getCTWorksheet().getSheetPr().isSetTabColor()); assertEquals(IndexedColors.RED.index, sh.getCTWorksheet().getSheetPr().getTabColor().getIndexed()); @@ -1908,8 +1908,8 @@ public final class TestXSSFSheet extends BaseTestXSheet { XSSFSheet sh = wb.createSheet(); assertTrue(sh.getCTWorksheet().getSheetPr() == null || !sh.getCTWorksheet().getSheetPr().isSetTabColor()); assertNull(sh.getTabColor()); - sh.setTabColor(new XSSFColor(IndexedColors.RED)); - XSSFColor expected = new XSSFColor(IndexedColors.RED); + sh.setTabColor(new XSSFColor(IndexedColors.RED, null)); + XSSFColor expected = new XSSFColor(IndexedColors.RED, null); assertEquals(expected, sh.getTabColor()); } finally { wb.close(); @@ -1925,7 +1925,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertNull(wb.getSheet("default").getTabColor()); // test indexed-colored sheet - XSSFColor expected = new XSSFColor(IndexedColors.RED); + XSSFColor expected = new XSSFColor(IndexedColors.RED, null); assertEquals(expected, wb.getSheet("indexedRed").getTabColor()); // test regular-colored (non-indexed, ARGB) sheet diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/extensions/TestXSSFCellFill.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/extensions/TestXSSFCellFill.java index 832d6bfb2..c9279e26d 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/extensions/TestXSSFCellFill.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/extensions/TestXSSFCellFill.java @@ -40,7 +40,7 @@ public class TestXSSFCellFill { @Test public void testGetFillBackgroundColor() { CTFill ctFill = CTFill.Factory.newInstance(); - XSSFCellFill cellFill = new XSSFCellFill(ctFill); + XSSFCellFill cellFill = new XSSFCellFill(ctFill, null); CTPatternFill ctPatternFill = ctFill.addNewPatternFill(); CTColor bgColor = ctPatternFill.addNewBgColor(); assertNotNull(cellFill.getFillBackgroundColor()); @@ -51,7 +51,7 @@ public class TestXSSFCellFill { @Test public void testGetFillForegroundColor() { CTFill ctFill = CTFill.Factory.newInstance(); - XSSFCellFill cellFill = new XSSFCellFill(ctFill); + XSSFCellFill cellFill = new XSSFCellFill(ctFill, null); CTPatternFill ctPatternFill = ctFill.addNewPatternFill(); CTColor fgColor = ctPatternFill.addNewFgColor(); assertNotNull(cellFill.getFillForegroundColor()); @@ -62,7 +62,7 @@ public class TestXSSFCellFill { @Test public void testGetSetPatternType() { CTFill ctFill = CTFill.Factory.newInstance(); - XSSFCellFill cellFill = new XSSFCellFill(ctFill); + XSSFCellFill cellFill = new XSSFCellFill(ctFill, null); CTPatternFill ctPatternFill = ctFill.addNewPatternFill(); ctPatternFill.setPatternType(STPatternType.SOLID); assertEquals(FillPatternType.SOLID_FOREGROUND.ordinal(), cellFill.getPatternType().intValue()-1); @@ -71,7 +71,7 @@ public class TestXSSFCellFill { @Test public void testGetNotModifies() { CTFill ctFill = CTFill.Factory.newInstance(); - XSSFCellFill cellFill = new XSSFCellFill(ctFill); + XSSFCellFill cellFill = new XSSFCellFill(ctFill, null); CTPatternFill ctPatternFill = ctFill.addNewPatternFill(); ctPatternFill.setPatternType(STPatternType.DARK_DOWN); assertEquals(8, cellFill.getPatternType().intValue()); diff --git a/test-data/spreadsheet/customIndexedColors.xlsx b/test-data/spreadsheet/customIndexedColors.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..62b6e185585c3b2d74d0f9582869d8f03e6e9999 GIT binary patch literal 19534 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 Cq~9h0 literal 0 HcmV?d00001