diff --git a/src/documentation/xdocs/hssf/quick-guide.xml b/src/documentation/xdocs/hssf/quick-guide.xml
index 7d7156381..e53486d9a 100644
--- a/src/documentation/xdocs/hssf/quick-guide.xml
+++ b/src/documentation/xdocs/hssf/quick-guide.xml
@@ -27,6 +27,7 @@
Fills and color
Merging cells
Working with fonts
+ Custom colors
Reading and writing
Use newlines in cells.
Create user defined data formats.
@@ -280,6 +281,60 @@
fileOut.close();
+
+
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ HSSFRow row = sheet.createRow((short) 0);
+ HSSFCell cell = row.createCell((short) 0);
+ cell.setCellValue("Default Palette");
+
+ //apply some colors from the standard palette,
+ // as in the previous examples.
+ //we'll use red text on a lime background
+
+ HSSFCellStyle style = wb.createCellStyle();
+ style.setFillForegroundColor(HSSFColor.LIME.index);
+ style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
+
+ HSSFFont font = wb.createFont();
+ font.setColor(HSSFColor.RED.index);
+ style.setFont(font);
+
+ cell.setCellStyle(style);
+
+ //save with the default palette
+ FileOutputStream out = new FileOutputStream("default_palette.xls");
+ wb.write(out);
+ out.close();
+
+ //now, let's replace RED and LIME in the palette
+ // with a more attractive combination
+ // (lovingly borrowed from freebsd.org)
+
+ cell.setCellValue("Modified Palette");
+
+ //creating a custom palette for the workbook
+ HSSFPalette palette = wb.getCustomPalette();
+
+ //replacing the standard red with freebsd.org red
+ palette.setColorAtIndex(HSSFColor.RED.index,
+ (byte) 153, //RGB red (0-255)
+ (byte) 0, //RGB green
+ (byte) 0 //RGB blue
+ );
+ //replacing lime with freebsd.org gold
+ palette.setColorAtIndex(HSSFColor.LIME.index, (byte) 255, (byte) 204, (byte) 102);
+
+ //save with the modified palette
+ // note that wherever we have previously used RED or LIME, the
+ // new colors magically appear
+ out = new FileOutputStream("modified_palette.xls");
+ wb.write(out);
+ out.close();
+
+
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index 78fa0d0d9..a1a4f4e41 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -89,6 +89,7 @@ import java.util.Locale;
* @author Sergei Kozello (sergeikozello at mail.ru)
* @author Luc Girardin (luc dot girardin at macrofocus dot com)
* @author Dan Sherman (dsherman at isisph.com)
+ * @author Brian Sanders (bsanders at risklabs dot com) - custom palette
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook
* @version 1.0-pre
*/
@@ -146,6 +147,7 @@ public class Workbook implements Model {
private int backuppos = 0; // holds the position of the backup record.
private int namepos = 0; // holds the position of last name record
private int supbookpos = 0; // holds the position of sup book
+ private int palettepos = 0; // hold the position of the palette, if applicable
private short maxformatid = -1; // holds the max format id
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
@@ -249,7 +251,9 @@ public class Workbook implements Model {
log.log(DEBUG, "found datewindow1904 record at " + k);
retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1;
break;
-
+ case PaletteRecord.sid:
+ log.log(DEBUG, "found palette record at " + k);
+ retval.palettepos = k;
default :
}
records.add(rec);
@@ -328,6 +332,7 @@ public class Workbook implements Model {
{
records.add( retval.createStyle( k ) );
}
+ retval.palettepos = records.size();
records.add( retval.createUseSelFS() );
for ( int k = 0; k < 1; k++ )
{ // now just do 1
@@ -578,6 +583,7 @@ public class Workbook implements Model {
ExtendedFormatRecord xf = createExtendedFormat();
++xfpos;
+ ++palettepos;
++bspos;
records.add(xfpos, xf);
numxfs++;
@@ -1566,6 +1572,16 @@ public class Workbook implements Model {
return retval;
}
+ /**
+ * Creates a palette record initialized to the default palette
+ * @return a PaletteRecord instance populated with the default colors
+ * @see org.apache.poi.hssf.record.PaletteRecord
+ */
+ protected PaletteRecord createPalette()
+ {
+ return new PaletteRecord(PaletteRecord.sid);
+ }
+
/**
* Creates the UseSelFS object with the use natural language flag set to 0 (false)
* @return record containing a UseSelFSRecord
@@ -1864,6 +1880,7 @@ public class Workbook implements Model {
public short createFormat( String format )
{
++xfpos; //These are to ensure that positions are updated properly
+ ++palettepos;
++bspos;
FormatRecord rec = new FormatRecord();
maxformatid = maxformatid >= (short) 0xa4 ? (short) ( maxformatid + 1 ) : (short) 0xa4; //Starting value from M$ empiracle study.
@@ -1941,6 +1958,7 @@ public class Workbook implements Model {
// {
// backuppos += chartRecords.size();
// fontpos += chartRecords.size();
+// palettepos += chartRecords.size();
// bspos += chartRecords.size();
// xfpos += chartRecords.size();
//
@@ -1956,4 +1974,25 @@ public class Workbook implements Model {
public boolean isUsing1904DateWindowing() {
return uses1904datewindowing;
}
+
+ /**
+ * Returns the custom palette in use for this workbook; if a custom palette record
+ * does not exist, then it is created.
+ */
+ public PaletteRecord getCustomPalette()
+ {
+ PaletteRecord palette;
+ Record rec = (Record) records.get(palettepos);
+ if (rec instanceof PaletteRecord)
+ {
+ palette = (PaletteRecord) rec;
+ }
+ else
+ {
+ palette = createPalette();
+ records.add(palettepos, palette);
+ ++bspos;
+ }
+ return palette;
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/PaletteRecord.java b/src/java/org/apache/poi/hssf/record/PaletteRecord.java
index 82763a6c8..f523ccd2d 100644
--- a/src/java/org/apache/poi/hssf/record/PaletteRecord.java
+++ b/src/java/org/apache/poi/hssf/record/PaletteRecord.java
@@ -63,6 +63,7 @@ import org.apache.poi.util.LittleEndian;
/**
* PaletteRecord - Supports custom palettes.
* @author Andrew C. Oliver (acoliver at apache dot org)
+ * @author Brian Sanders (bsanders at risklabs dot com) - custom palette editing
* @version 2.0-pre
*/
@@ -70,13 +71,25 @@ public class PaletteRecord
extends Record
{
public final static short sid = 0x92;
-
+ /** The standard size of an XLS palette */
+ public final static byte STANDARD_PALETTE_SIZE = (byte) 56;
+ /** The byte index of the first color */
+ public final static short FIRST_COLOR_INDEX = (short) 0x8;
+
private short field_1_numcolors;
private List field_2_colors;
public PaletteRecord()
{
}
+
+ /**
+ * Constructs a custom palette with the default set of colors
+ */
+ public PaletteRecord(short id)
+ {
+ super(id, STANDARD_PALETTE_SIZE, getDefaultData());
+ }
/**
* Constructs a PaletteRecord record and sets its fields appropriately.
@@ -131,7 +144,7 @@ public class PaletteRecord
{
StringBuffer buffer = new StringBuffer();
- buffer.append("[Palette]\n");
+ buffer.append("[PALETTE]\n");
buffer.append(" numcolors = ").append(field_1_numcolors)
.append('\n');
for (int k = 0; k < field_1_numcolors; k++) {
@@ -142,16 +155,18 @@ public class PaletteRecord
buffer.append("/*colornum = ").append(k)
.append('\n');
}
- buffer.append("[/Palette]\n");
+ buffer.append("[/PALETTE]\n");
return buffer.toString();
}
public int serialize(int offset, byte [] data)
{
LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short) (getRecordSize() - 4));
+ LittleEndian.putShort(data, 4 + offset, field_1_numcolors);
for (int k = 0; k < field_1_numcolors; k++) {
PColor c = (PColor)field_2_colors.get(k);
- c.serialize(data, (2+offset+(k*4)));
+ c.serialize(data, (6+offset+(k*4)));
}
return getRecordSize();
@@ -159,7 +174,7 @@ public class PaletteRecord
public int getRecordSize()
{
- return 2 + (field_1_numcolors * 4);
+ return 4 + 2 + (field_1_numcolors * 4);
}
public short getSid()
@@ -167,6 +182,115 @@ public class PaletteRecord
return this.sid;
}
+ /**
+ * Returns the color value at a given index
+ *
+ * @return the RGB triplet for the color, or null if the specified index
+ * does not exist
+ */
+ public byte[] getColor(short byteIndex)
+ {
+ int i = byteIndex - FIRST_COLOR_INDEX;
+ if (i < 0 || i >= field_2_colors.size())
+ {
+ return null;
+ }
+ PColor color = (PColor) field_2_colors.get(i);
+ return new byte[] { color.red, color.green, color.blue };
+ }
+
+ /**
+ * Sets the color value at a given index
+ *
+ * If the given index is greater than the current last color index,
+ * then black is inserted at every index required to make the palette continuous.
+ *
+ * @param i the index to set; if this index is less than 0x8 or greater than
+ * 0x40, then no modification is made
+ */
+ public void setColor(short byteIndex, byte red, byte green, byte blue)
+ {
+ int i = byteIndex - FIRST_COLOR_INDEX;
+ if (i < 0 || i >= STANDARD_PALETTE_SIZE)
+ {
+ return;
+ }
+ while (field_2_colors.size() <= i)
+ {
+ field_2_colors.add(new PColor((byte) 0, (byte) 0, (byte) 0));
+ }
+ PColor custColor = new PColor(red, green, blue);
+ field_2_colors.set(i, custColor);
+ }
+
+ /**
+ * Returns the default palette as PaletteRecord binary data
+ *
+ * @see org.apache.poi.hssf.model.Workbook#createPalette
+ */
+ public static byte[] getDefaultData()
+ {
+ return new byte[]
+ {
+ STANDARD_PALETTE_SIZE, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0, //color 0...
+ (byte) 255, (byte) 255, (byte) 255, (byte) 0,
+ (byte) 255, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 255, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 255, (byte) 0,
+ (byte) 255, (byte) 255, (byte) 0, (byte) 0,
+ (byte) 255, (byte) 0, (byte) 255, (byte) 0,
+ (byte) 0, (byte) 255, (byte) 255, (byte) 0,
+ (byte) 128, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 128, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 128, (byte) 0,
+ (byte) 128, (byte) 128, (byte) 0, (byte) 0,
+ (byte) 128, (byte) 0, (byte) 128, (byte) 0,
+ (byte) 0, (byte) 128, (byte) 128, (byte) 0,
+ (byte) 192, (byte) 192, (byte) 192, (byte) 0,
+ (byte) 128, (byte) 128, (byte) 128, (byte) 0,
+ (byte) 153, (byte) 153, (byte) 255, (byte) 0,
+ (byte) 153, (byte) 51, (byte) 102, (byte) 0,
+ (byte) 255, (byte) 255, (byte) 204, (byte) 0,
+ (byte) 204, (byte) 255, (byte) 255, (byte) 0,
+ (byte) 102, (byte) 0, (byte) 102, (byte) 0,
+ (byte) 255, (byte) 128, (byte) 128, (byte) 0,
+ (byte) 0, (byte) 102, (byte) 204, (byte) 0,
+ (byte) 204, (byte) 204, (byte) 255, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 128, (byte) 0,
+ (byte) 255, (byte) 0, (byte) 255, (byte) 0,
+ (byte) 255, (byte) 255, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 255, (byte) 255, (byte) 0,
+ (byte) 128, (byte) 0, (byte) 128, (byte) 0,
+ (byte) 128, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 128, (byte) 128, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 255, (byte) 0,
+ (byte) 0, (byte) 204, (byte) 255, (byte) 0,
+ (byte) 204, (byte) 255, (byte) 255, (byte) 0,
+ (byte) 204, (byte) 255, (byte) 204, (byte) 0,
+ (byte) 255, (byte) 255, (byte) 153, (byte) 0,
+ (byte) 153, (byte) 204, (byte) 255, (byte) 0,
+ (byte) 255, (byte) 153, (byte) 204, (byte) 0,
+ (byte) 204, (byte) 153, (byte) 255, (byte) 0,
+ (byte) 255, (byte) 204, (byte) 153, (byte) 0,
+ (byte) 51, (byte) 102, (byte) 255, (byte) 0,
+ (byte) 51, (byte) 204, (byte) 204, (byte) 0,
+ (byte) 153, (byte) 204, (byte) 0, (byte) 0,
+ (byte) 255, (byte) 204, (byte) 0, (byte) 0,
+ (byte) 255, (byte) 153, (byte) 0, (byte) 0,
+ (byte) 255, (byte) 102, (byte) 0, (byte) 0,
+ (byte) 102, (byte) 102, (byte) 153, (byte) 0,
+ (byte) 150, (byte) 150, (byte) 150, (byte) 0,
+ (byte) 0, (byte) 51, (byte) 102, (byte) 0,
+ (byte) 51, (byte) 153, (byte) 102, (byte) 0,
+ (byte) 0, (byte) 51, (byte) 0, (byte) 0,
+ (byte) 51, (byte) 51, (byte) 0, (byte) 0,
+ (byte) 153, (byte) 51, (byte) 0, (byte) 0,
+ (byte) 153, (byte) 51, (byte) 102, (byte) 0,
+ (byte) 51, (byte) 51, (byte) 153, (byte) 0,
+ (byte) 51, (byte) 51, (byte) 51, (byte) 0
+ };
+ }
}
/**
@@ -191,9 +315,9 @@ class PColor {
public String toString() {
StringBuffer buffer = new StringBuffer();
- buffer.append(" red = ").append(red).append('\n');
- buffer.append(" green = ").append(green).append('\n');
- buffer.append(" blue = ").append(blue).append('\n');
+ buffer.append(" red = ").append(red & 0xff).append('\n');
+ buffer.append(" green = ").append(green & 0xff).append('\n');
+ buffer.append(" blue = ").append(blue & 0xff).append('\n');
return buffer.toString();
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 98cfedae7..777b53f31 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -728,6 +728,11 @@ public class HSSFWorkbook
}
+ public HSSFPalette getCustomPalette()
+ {
+ return new HSSFPalette(workbook.getCustomPalette());
+ }
+
/**
* Copies nodes from one POIFS to the other minus the excepts
* @param source is the source POIFS to copy from
diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java
index 8940dd372..6882169e6 100644
--- a/src/testcases/org/apache/poi/hssf/HSSFTests.java
+++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java
@@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.TestLineFormatRecord;
import org.apache.poi.hssf.record.TestLinkedDataRecord;
import org.apache.poi.hssf.record.TestNumberFormatIndexRecord;
import org.apache.poi.hssf.record.TestObjectLinkRecord;
+import org.apache.poi.hssf.record.TestPaletteRecord;
import org.apache.poi.hssf.record.TestPlotAreaRecord;
import org.apache.poi.hssf.record.TestPlotGrowthRecord;
import org.apache.poi.hssf.record.TestRecordFactory;
@@ -53,6 +54,7 @@ import org.apache.poi.hssf.usermodel.TestCellStyle;
import org.apache.poi.hssf.usermodel.TestFormulas;
import org.apache.poi.hssf.usermodel.TestHSSFCell;
import org.apache.poi.hssf.usermodel.TestHSSFDateUtil;
+import org.apache.poi.hssf.usermodel.TestHSSFPalette;
import org.apache.poi.hssf.usermodel.TestHSSFRow;
import org.apache.poi.hssf.usermodel.TestHSSFSheet;
import org.apache.poi.hssf.usermodel.TestNamedRange;
@@ -86,6 +88,7 @@ public class HSSFTests
suite.addTest(new TestSuite(TestFormulas.class));
suite.addTest(new TestSuite(TestHSSFCell.class));
suite.addTest(new TestSuite(TestHSSFDateUtil.class));
+ suite.addTest(new TestSuite(TestHSSFPalette.class));
suite.addTest(new TestSuite(TestHSSFRow.class));
suite.addTest(new TestSuite(TestHSSFSheet.class));
suite.addTest(new TestSuite(TestNamedRange.class));
@@ -116,6 +119,7 @@ public class HSSFTests
suite.addTest(new TestSuite(TestLinkedDataRecord.class));
suite.addTest(new TestSuite(TestNumberFormatIndexRecord.class));
suite.addTest(new TestSuite(TestObjectLinkRecord.class));
+ suite.addTest(new TestSuite(TestPaletteRecord.class));
suite.addTest(new TestSuite(TestPlotAreaRecord.class));
suite.addTest(new TestSuite(TestPlotGrowthRecord.class));
suite.addTest(new TestSuite(TestRecordFactory.class));