diff --git a/src/documentation/xdocs/hssf/quick-guide.xml b/src/documentation/xdocs/hssf/quick-guide.xml index 7d71563816..e53486d9ac 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 78fa0d0d96..a1a4f4e419 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 82763a6c8a..f523ccd2d2 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 98cfedae7b..777b53f31c 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 8940dd3725..6882169e66 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));