Bug 51222 - XSSFColor.getARGBHex() returns wrong color for Excel 2007 xlsx file

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1621393 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-08-29 22:14:55 +00:00
parent 67fcf46d4c
commit 0cbbbfe5d5
6 changed files with 265 additions and 184 deletions

View File

@ -23,20 +23,22 @@ import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme;
import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument;
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
/**
* Class that represents theme of XLSX document. The theme includes specific
* colors and fonts.
*
* @author Petr Udalau(Petr.Udalau at exigenservices.com) - theme colors
*/
public class ThemesTable extends POIXMLDocumentPart {
private ThemeDocument theme;
/**
* Construct a ThemesTable.
* @param part A PackagePart.
* @param rel A PackageRelationship.
*/
public ThemesTable(PackagePart part, PackageRelationship rel) throws IOException {
super(part, rel);
@ -47,34 +49,52 @@ public class ThemesTable extends POIXMLDocumentPart {
}
}
/**
* Construct a ThemesTable from an existing ThemeDocument.
* @param theme A ThemeDocument.
*/
public ThemesTable(ThemeDocument theme) {
this.theme = theme;
}
/**
* Convert a theme "index" into a color.
* @param idx A theme "index"
* @return The mapped XSSFColor, or null if not mapped.
*/
public XSSFColor getThemeColor(int idx) {
// Theme color references are NOT positional indices into the color scheme,
// i.e. these keys are NOT the same as the order in which theme colors appear
// in theme1.xml. They are keys to a mapped color.
CTColorScheme colorScheme = theme.getTheme().getThemeElements().getClrScheme();
CTColor ctColor = null;
int cnt = 0;
for (XmlObject obj : colorScheme.selectPath("./*")) {
if (obj instanceof org.openxmlformats.schemas.drawingml.x2006.main.CTColor) {
if (cnt == idx) {
ctColor = (org.openxmlformats.schemas.drawingml.x2006.main.CTColor) obj;
byte[] rgb = null;
if (ctColor.getSrgbClr() != null) {
// Colour is a regular one
rgb = ctColor.getSrgbClr().getVal();
} else if (ctColor.getSysClr() != null) {
// Colour is a tint of white or black
rgb = ctColor.getSysClr().getLastClr();
}
return new XSSFColor(rgb);
}
cnt++;
}
CTColor ctColor;
switch (idx) {
case 0: ctColor = colorScheme.getLt1(); break;
case 1: ctColor = colorScheme.getDk1(); break;
case 2: ctColor = colorScheme.getLt2(); break;
case 3: ctColor = colorScheme.getDk2(); break;
case 4: ctColor = colorScheme.getAccent1(); break;
case 5: ctColor = colorScheme.getAccent2(); break;
case 6: ctColor = colorScheme.getAccent3(); break;
case 7: ctColor = colorScheme.getAccent4(); break;
case 8: ctColor = colorScheme.getAccent5(); break;
case 9: ctColor = colorScheme.getAccent6(); break;
case 10: ctColor = colorScheme.getHlink(); break;
case 11: ctColor = colorScheme.getFolHlink(); break;
default: return null;
}
return null;
byte[] rgb = null;
if (ctColor.isSetSrgbClr()) {
// Color is a regular one
rgb = ctColor.getSrgbClr().getVal();
} else if (ctColor.isSetSysClr()) {
// Color is a tint of white or black
rgb = ctColor.getSysClr().getLastClr();
} else {
return null;
}
return new XSSFColor(rgb);
}
/**

View File

@ -26,14 +26,14 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor;
*/
public class XSSFColor implements Color {
private CTColor ctColor;
private CTColor ctColor;
/**
* Create an instance of XSSFColor from the supplied XML bean
*/
public XSSFColor(CTColor color) {
this.ctColor = color;
}
this.ctColor = color;
}
/**
* Create an new instance of XSSFColor
@ -56,50 +56,29 @@ public class XSSFColor implements Color {
* A boolean value indicating the ctColor is automatic and system ctColor dependent.
*/
public boolean isAuto() {
return ctColor.getAuto();
}
return ctColor.getAuto();
}
/**
* A boolean value indicating the ctColor is automatic and system ctColor dependent.
*/
public void setAuto(boolean auto) {
ctColor.setAuto(auto);
}
public void setAuto(boolean auto) {
ctColor.setAuto(auto);
}
/**
* Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors.
*/
public short getIndexed() {
return (short)ctColor.getIndexed();
}
return (short)ctColor.getIndexed();
}
/**
* Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors.
*/
public void setIndexed(int indexed) {
ctColor.setIndexed(indexed);
}
/**
* For RGB colours, but not ARGB (we think...)
* Excel gets black and white the wrong way around, so switch them
*/
private byte[] correctRGB(byte[] rgb) {
if(rgb.length == 4) {
// Excel doesn't appear to get these wrong
// Nothing to change
return rgb;
} else {
// 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;
}
}
public void setIndexed(int indexed) {
ctColor.setIndexed(indexed);
}
/**
* Standard Red Green Blue ctColor value (RGB).
@ -158,9 +137,7 @@ public class XSSFColor implements Color {
// Grab the colour
rgb = ctColor.getRgb();
// Correct it as needed, and return
return correctRGB(rgb);
return rgb;
}
/**
@ -186,43 +163,42 @@ public class XSSFColor implements Color {
* 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 = getARgb();
if(rgb == null) {
return null;
}
for(byte c : rgb) {
int i = (int)c;
if(i < 0) {
i += 256;
}
String cs = Integer.toHexString(i);
if(cs.length() == 1) {
sb.append('0');
}
sb.append(cs);
}
return sb.toString().toUpperCase();
}
public String getARGBHex() {
StringBuffer sb = new StringBuffer();
byte[] rgb = getARgb();
if(rgb == null) {
return null;
}
for(byte c : rgb) {
int i = (int)c;
if(i < 0) {
i += 256;
}
String cs = Integer.toHexString(i);
if(cs.length() == 1) {
sb.append('0');
}
sb.append(cs);
}
return sb.toString().toUpperCase();
}
private static byte applyTint(int lum, double tint){
if(tint > 0){
return (byte)(lum * (1.0-tint) + (255 - 255 * (1.0-tint)));
} else if (tint < 0){
return (byte)(lum*(1+tint));
} else {
return (byte)lum;
}
}
private static byte applyTint(int lum, double tint){
if(tint > 0){
return (byte)(lum * (1.0-tint) + (255 - 255 * (1.0-tint)));
} else if (tint < 0){
return (byte)(lum*(1+tint));
} else {
return (byte)lum;
}
}
/**
* Standard Alpha Red Green Blue ctColor value (ARGB).
*/
public void setRgb(byte[] rgb) {
// Correct it and save
ctColor.setRgb(correctRGB(rgb));
}
public void setRgb(byte[] rgb) {
ctColor.setRgb(rgb);
}
/**
* Index into the <clrScheme> collection, referencing a particular <sysClr> or
@ -230,15 +206,15 @@ public class XSSFColor implements Color {
*/
public int getTheme() {
return (int)ctColor.getTheme();
}
}
/**
* Index into the <clrScheme> collection, referencing a particular <sysClr> or
* <srgbClr> value expressed in the Theme part.
*/
public void setTheme(int theme) {
ctColor.setTheme(theme);
}
public void setTheme(int theme) {
ctColor.setTheme(theme);
}
/**
* Specifies the tint value applied to the ctColor.
@ -282,8 +258,8 @@ public class XSSFColor implements Color {
* @return the tint value
*/
public double getTint() {
return ctColor.getTint();
}
return ctColor.getTint();
}
/**
* Specifies the tint value applied to the ctColor.
@ -326,9 +302,9 @@ public class XSSFColor implements Color {
*
* @param tint the tint value
*/
public void setTint(double tint) {
ctColor.setTint(tint);
}
public void setTint(double tint) {
ctColor.setTint(tint);
}
/**
* Returns the underlying XML bean

View File

@ -0,0 +1,78 @@
/* ====================================================================
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.model;
import static org.junit.Assert.assertEquals;
import java.io.FileOutputStream;
import org.apache.commons.codec.binary.Hex;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;
public class TestThemesTable {
private String testFile = "Themes.xlsx";
@Test
public void testThemesTableColors() throws Exception {
XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook(testFile);
String rgbExpected[] = {
"ffffff", // Lt1
"000000", // Dk1
"eeece1", // Lt2
"1f497d", // DK2
"4f81bd", // Accent1
"c0504d", // Accent2
"9bbb59", // Accent3
"8064a2", // Accent4
"4bacc6", // Accent5
"f79646", // Accent6
"0000ff", // Hlink
"800080" // FolHlink
};
boolean createFile = false;
int i=0;
for (Row row : workbook.getSheetAt(0)) {
XSSFFont font = ((XSSFRow)row).getCell(0).getCellStyle().getFont();
XSSFColor color = font.getXSSFColor();
assertEquals("Failed color theme "+i, rgbExpected[i], Hex.encodeHexString(color.getRgb()));
long themeIdx = font.getCTFont().getColorArray(0).getTheme();
assertEquals("Failed color theme "+i, i, themeIdx);
if (createFile) {
XSSFCellStyle cs = (XSSFCellStyle)row.getSheet().getWorkbook().createCellStyle();
cs.setFillForegroundColor(color);
cs.setFillPattern(CellStyle.SOLID_FOREGROUND);
row.createCell(1).setCellStyle(cs);
}
i++;
}
if (createFile) {
FileOutputStream fos = new FileOutputStream("foobaa.xlsx");
workbook.write(fos);
fos.close();
}
}
}

View File

@ -93,15 +93,17 @@ public final class TestXSSFColor extends TestCase {
assertEquals(-1, rgb3.getARgb()[3]);
// Tint doesn't have the alpha
// tint = -0.34999
// 255 * (1 + tint) = 165 truncated
// or (byte) -91 (which is 165 - 256)
assertEquals(3, rgb3.getRgbWithTint().length);
assertEquals(0, rgb3.getRgbWithTint()[0]);
assertEquals(0, rgb3.getRgbWithTint()[1]);
assertEquals(0, rgb3.getRgbWithTint()[2]);
assertEquals(-91, rgb3.getRgbWithTint()[0]);
assertEquals(-91, rgb3.getRgbWithTint()[1]);
assertEquals(-91, rgb3.getRgbWithTint()[2]);
// Set the colour to black, will get translated internally
// (Excel stores 3 colour white and black wrong!)
rgb3.setRgb(new byte[] {-1,-1,-1});
assertEquals("FFFFFFFF", rgb3.getARGBHex());
// Set the color to black (no theme).
rgb3.setRgb(new byte[] {0, 0, 0});
assertEquals("FF000000", rgb3.getARGBHex());
assertEquals(0, rgb3.getCTColor().getRgb()[0]);
assertEquals(0, rgb3.getCTColor().getRgb()[1]);
assertEquals(0, rgb3.getCTColor().getRgb()[2]);

View File

@ -32,33 +32,33 @@ import junit.framework.TestCase;
public class TestXSSFCellFill extends TestCase {
public void testGetFillBackgroundColor() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
CTColor bgColor = ctPatternFill.addNewBgColor();
assertNotNull(cellFill.getFillBackgroundColor());
bgColor.setIndexed(2);
assertEquals(2, cellFill.getFillBackgroundColor().getIndexed());
}
public void testGetFillBackgroundColor() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
CTColor bgColor = ctPatternFill.addNewBgColor();
assertNotNull(cellFill.getFillBackgroundColor());
bgColor.setIndexed(2);
assertEquals(2, cellFill.getFillBackgroundColor().getIndexed());
}
public void testGetFillForegroundColor() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
CTColor fgColor = ctPatternFill.addNewFgColor();
assertNotNull(cellFill.getFillForegroundColor());
fgColor.setIndexed(8);
assertEquals(8, cellFill.getFillForegroundColor().getIndexed());
}
public void testGetFillForegroundColor() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
CTColor fgColor = ctPatternFill.addNewFgColor();
assertNotNull(cellFill.getFillForegroundColor());
fgColor.setIndexed(8);
assertEquals(8, cellFill.getFillForegroundColor().getIndexed());
}
public void testGetSetPatternType() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
ctPatternFill.setPatternType(STPatternType.SOLID);
//assertEquals(FillPatternType.SOLID_FOREGROUND.ordinal(), cellFill.getPatternType().ordinal());
}
public void testGetSetPatternType() {
CTFill ctFill = CTFill.Factory.newInstance();
XSSFCellFill cellFill = new XSSFCellFill(ctFill);
CTPatternFill ctPatternFill = ctFill.addNewPatternFill();
ctPatternFill.setPatternType(STPatternType.SOLID);
//assertEquals(FillPatternType.SOLID_FOREGROUND.ordinal(), cellFill.getPatternType().ordinal());
}
public void testGetNotModifies() {
CTFill ctFill = CTFill.Factory.newInstance();
@ -75,11 +75,16 @@ public class TestXSSFCellFill extends TestCase {
XSSFColor foregroundColor = cellWithThemeColor.getCellStyle().getFillForegroundXSSFColor();
byte[] rgb = foregroundColor.getRgb();
byte[] rgbWithTint = foregroundColor.getRgbWithTint();
assertEquals(rgb[0],-18);
assertEquals(rgb[1],-20);
assertEquals(rgb[2],-31);
assertEquals(rgbWithTint[0],-12);
assertEquals(rgbWithTint[1],-13);
assertEquals(rgbWithTint[2],-20);
// Dk2
assertEquals(rgb[0],31);
assertEquals(rgb[1],73);
assertEquals(rgb[2],125);
// Dk2, lighter 40% (tint is about 0.39998)
// 31 * (1.0 - 0.39998) + (255 - 255 * (1.0 - 0.39998)) = 120.59552 => 120 (byte)
// 73 * (1.0 - 0.39998) + (255 - 255 * (1.0 - 0.39998)) = 145.79636 => -111 (byte)
// 125 * (1.0 - 0.39998) + (255 - 255 * (1.0 - 0.39998)) = 176.99740 => -80 (byte)
assertEquals(rgbWithTint[0],120);
assertEquals(rgbWithTint[1],-111);
assertEquals(rgbWithTint[2],-80);
}
}

Binary file not shown.