diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 65601488e..bf64050a3 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 50299 - More XSSFColor updates for ARGB vs RGB 50581 - Use stax:stax-api instead of org.apache.geronimo.specs:geronimo-stax-api_1.0_spec 50786 - Fix XSSFColor to fetch the RGB values of old-style indexed colours 50299 - Fix XSSFColor fetching of white and black background themes 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 6358168c6..93b4364d1 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCellStyle.java @@ -1452,6 +1452,7 @@ public class XSSFCellStyle implements CellStyle { */ private void extractColorFromTheme(XSSFColor originalColor){ XSSFColor themeColor = _theme.getThemeColor(originalColor.getTheme()); - originalColor.setRgb(themeColor.getRgb()); + // Set the raw colour, not the adjusted one + originalColor.setRgb(themeColor.getCTColor().getRgb()); } } 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 2194a4db1..af5928fbf 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java @@ -80,46 +80,106 @@ public class XSSFColor implements Color { ctColor.setIndexed(indexed); } + /** + * Standard Red Green Blue ctColor value (RGB). + * If there was an A (Alpha) value, it will be stripped. + */ + public byte[] getRgb() { + byte[] rgb = getRGBOrARGB(); + if(rgb == null) return null; + + if(rgb.length == 4) { + // Need to trim off the alpha + byte[] tmp = new byte[3]; + System.arraycopy(rgb, 1, tmp, 0, 3); + return tmp; + } else { + return rgb; + } + } + + /** + * Standard Alpha Red Green Blue ctColor value (ARGB). + */ + public byte[] getARgb() { + byte[] rgb = getRGBOrARGB(); + if(rgb == null) return null; + + if(rgb.length == 3) { + // Pad with the default Alpha + byte[] tmp = new byte[4]; + tmp[0] = -1; + System.arraycopy(rgb, 0, tmp, 1, 3); + return tmp; + } else { + return rgb; + } + } + + private byte[] getRGBOrARGB() { + byte[] rgb = null; + + if (ctColor.isSetIndexed() && ctColor.getIndexed() > 0) { + HSSFColor indexed = HSSFColor.getIndexHash().get((int) ctColor.getIndexed()); + if (indexed != null) { + rgb = new byte[3]; + rgb[0] = (byte) indexed.getTriplet()[0]; + rgb[1] = (byte) indexed.getTriplet()[1]; + rgb[2] = (byte) indexed.getTriplet()[2]; + return rgb; + } + } + + if (!ctColor.isSetRgb()) { + // No colour is available, sorry + return null; + } + + // Grab the colour + rgb = ctColor.getRgb(); + + if(rgb.length == 4) { + // Good to go, return it as-is + return rgb; + } + + // For RGB colours, but not ARGB (we think...) + // Excel gets black and white the wrong way around, so switch them + if (rgb[0] == 0 && rgb[1] == 0 && rgb[2] == 0) { + rgb = new byte[] {-1, -1, -1}; + } + else if (rgb[0] == -1 && rgb[1] == -1 && rgb[2] == -1) { + rgb = new byte[] {0, 0, 0}; + } + return rgb; + } + /** - * Standard Alpha Red Green Blue ctColor value (ARGB). + * Standard Red Green Blue ctColor value (RGB) with applied tint. + * Alpha values are ignored. */ - public byte[] getRgb() { - if(ctColor.isSetIndexed() && ctColor.getIndexed() > 0) { - HSSFColor indexed = HSSFColor.getIndexHash().get((int)ctColor.getIndexed()); - if(indexed != null) { - // Convert it to ARGB form - byte[] rgb = new byte[4]; - rgb[0] = 0; - rgb[1] = (byte)indexed.getTriplet()[0]; - rgb[2] = (byte)indexed.getTriplet()[1]; - rgb[3] = (byte)indexed.getTriplet()[2]; - return rgb; - } else { - // Your indexed value isn't a standard one, sorry... - return null; - } - } - return ctColor.getRgb(); - } + public byte[] getRgbWithTint() { + byte[] rgb = ctColor.getRgb(); + if (rgb != null) { + if(rgb.length == 4) { + byte[] tmp = new byte[3]; + System.arraycopy(rgb, 1, tmp, 0, 3); + rgb = tmp; + } + for (int i = 0; i < rgb.length; i++){ + rgb[i] = applyTint(rgb[i] & 0xFF, ctColor.getTint()); + } + } + return rgb; + } /** - * Standard Alpha Red Green Blue ctColor value (ARGB) with applied tint. + * Return the ARGB value in hex format, eg FF00FF00. + * Works for both regular and indexed colours. */ - public byte[] getRgbWithTint() { - byte[] rgb =ctColor.getRgb(); - for(int i = 0; i < rgb.length; i++){ - rgb[i] = applyTint(rgb[i] & 0xFF, ctColor.getTint()); - } - return rgb; - } - - /** - * Return the ARGB value in hex format, eg FF00FF00. - * Works for both regular and indexed colours. - */ public String getARGBHex() { StringBuffer sb = new StringBuffer(); - byte[] rgb = getRgb(); + byte[] rgb = getARgb(); if(rgb == null) { return null; } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index b36a92a4d..3b17b9ac0 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -696,7 +696,8 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { } // Check one bit in detail - // TODO Is this correct, shouldn't one be white and one black? + // Check that we get back foreground=0 for the theme colours, + // and background=64 for the auto colouring Sheet s = wb.getSheetAt(0); assertEquals(0, s.getRow(0).getCell(8).getCellStyle().getFillForegroundColor()); assertEquals(64, s.getRow(0).getCell(8).getCellStyle().getFillBackgroundColor()); @@ -721,13 +722,15 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { assertEquals(42, cs.getFillForegroundColor()); assertEquals(42, cs.getFillForegroundColorColor().getIndexed()); assertNotNull(cs.getFillForegroundColorColor().getRgb()); - assertEquals("00CCFFCC", cs.getFillForegroundColorColor().getARGBHex()); + assertEquals("FFCCFFCC", cs.getFillForegroundColorColor().getARGBHex()); } /** * Fonts where their colours come from the theme rather * then being set explicitly still should allow the - * fetching of the RGB + * fetching of the RGB. + * TODO Allow XSSFFont to get at the themes table, so it can do + * the same trick that XSSFCellStyle does with theme colours */ public void DISABLEDtest50784() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("50784-font_theme_colours.xlsx"); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java new file mode 100644 index 000000000..17b1d0d54 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColor.java @@ -0,0 +1,159 @@ +/* ==================================================================== + 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 junit.framework.TestCase; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.XSSFITestDataProvider; +import org.apache.poi.xssf.XSSFTestDataSamples; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBooleanProperty; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontName; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontScheme; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFontSize; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTIntProperty; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTUnderlineProperty; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTVerticalAlignFontProperty; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STFontScheme; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STUnderlineValues; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STVerticalAlignRun; + +public final class TestXSSFColor extends TestCase { + public void testIndexedColour() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48779.xlsx"); + + // Check the CTColor is as expected + XSSFColor indexed = wb.getCellStyleAt((short)1).getFillBackgroundXSSFColor(); + assertEquals(true, indexed.getCTColor().isSetIndexed()); + assertEquals(64, indexed.getCTColor().getIndexed()); + assertEquals(false, indexed.getCTColor().isSetRgb()); + assertEquals(null, indexed.getCTColor().getRgb()); + + // Now check the XSSFColor + // Note - 64 is a special "auto" one with no rgb equiv + assertEquals(64, indexed.getIndexed()); + assertEquals(null, indexed.getRgb()); + assertEquals(null, indexed.getRgbWithTint()); + assertEquals(null, indexed.getARGBHex()); + + // Now move to one with indexed rgb values + indexed.setIndexed(59); + assertEquals(true, indexed.getCTColor().isSetIndexed()); + assertEquals(59, indexed.getCTColor().getIndexed()); + assertEquals(false, indexed.getCTColor().isSetRgb()); + assertEquals(null, indexed.getCTColor().getRgb()); + + assertEquals(59, indexed.getIndexed()); + assertEquals("FF333300", indexed.getARGBHex()); + + assertEquals(3, indexed.getRgb().length); + assertEquals(0x33, indexed.getRgb()[0]); + assertEquals(0x33, indexed.getRgb()[1]); + assertEquals(0x00, indexed.getRgb()[2]); + + assertEquals(4, indexed.getARgb().length); + assertEquals(-1, indexed.getARgb()[0]); + assertEquals(0x33, indexed.getARgb()[1]); + assertEquals(0x33, indexed.getARgb()[2]); + assertEquals(0x00, indexed.getARgb()[3]); + + // You don't get tinted indexed colours, sorry... + assertEquals(null, indexed.getRgbWithTint()); + } + + public void testRGBColour() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("50299.xlsx"); + + // Check the CTColor is as expected + XSSFColor rgb3 = wb.getCellStyleAt((short)25).getFillForegroundXSSFColor(); + assertEquals(false, rgb3.getCTColor().isSetIndexed()); + assertEquals(0, rgb3.getCTColor().getIndexed()); + assertEquals(true, rgb3.getCTColor().isSetTint()); + assertEquals(-0.34999, rgb3.getCTColor().getTint(), 0.00001); + assertEquals(true, rgb3.getCTColor().isSetRgb()); + assertEquals(3, rgb3.getCTColor().getRgb().length); + + // Now check the XSSFColor + assertEquals(0, rgb3.getIndexed()); + assertEquals(-0.34999, rgb3.getTint(), 0.00001); + + assertEquals("FFFFFFFF", rgb3.getARGBHex()); + assertEquals(3, rgb3.getRgb().length); + assertEquals(-1, rgb3.getRgb()[0]); + assertEquals(-1, rgb3.getRgb()[1]); + assertEquals(-1, rgb3.getRgb()[2]); + + assertEquals(4, rgb3.getARgb().length); + assertEquals(-1, rgb3.getARgb()[0]); + assertEquals(-1, rgb3.getARgb()[1]); + assertEquals(-1, rgb3.getARgb()[2]); + assertEquals(-1, rgb3.getARgb()[3]); + + // Tint doesn't have the alpha + assertEquals(3, rgb3.getRgbWithTint().length); + assertEquals(0, rgb3.getRgbWithTint()[0]); + assertEquals(0, rgb3.getRgbWithTint()[1]); + assertEquals(0, rgb3.getRgbWithTint()[2]); + } + + public void testARGBColour() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48779.xlsx"); + + // Check the CTColor is as expected + XSSFColor rgb4 = wb.getCellStyleAt((short)1).getFillForegroundXSSFColor(); + assertEquals(false, rgb4.getCTColor().isSetIndexed()); + assertEquals(0, rgb4.getCTColor().getIndexed()); + assertEquals(true, rgb4.getCTColor().isSetRgb()); + assertEquals(4, rgb4.getCTColor().getRgb().length); + + // Now check the XSSFColor + assertEquals(0, rgb4.getIndexed()); + assertEquals(0.0, rgb4.getTint()); + + assertEquals("FFFF0000", rgb4.getARGBHex()); + assertEquals(3, rgb4.getRgb().length); + assertEquals(-1, rgb4.getRgb()[0]); + assertEquals(0, rgb4.getRgb()[1]); + assertEquals(0, rgb4.getRgb()[2]); + + assertEquals(4, rgb4.getARgb().length); + assertEquals(-1, rgb4.getARgb()[0]); + assertEquals(-1, rgb4.getARgb()[1]); + assertEquals(0, rgb4.getARgb()[2]); + assertEquals(0, rgb4.getARgb()[3]); + + // Tint doesn't have the alpha + assertEquals(3, rgb4.getRgbWithTint().length); + assertEquals(-1, rgb4.getRgbWithTint()[0]); + assertEquals(0, rgb4.getRgbWithTint()[1]); + assertEquals(0, rgb4.getRgbWithTint()[2]); + + + // Turn on tinting, and check it behaves + // TODO These values are suspected to be wrong... + rgb4.setTint(0.4); + assertEquals(0.4, rgb4.getTint()); + + assertEquals(3, rgb4.getRgbWithTint().length); + assertEquals(-1, rgb4.getRgbWithTint()[0]); + assertEquals(102, rgb4.getRgbWithTint()[1]); + assertEquals(102, rgb4.getRgbWithTint()[2]); + } +}