1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   package org.apache.poi.hssf.record;
57   
58   import java.util.ArrayList;
59   import java.util.List;
60   
61   import org.apache.poi.util.LittleEndian;
62   
63   /**
64    * PaletteRecord - Supports custom palettes.
65    * @author Andrew C. Oliver (acoliver at apache dot org)
66    * @author Brian Sanders (bsanders at risklabs dot com) - custom palette editing
67    * @version 2.0-pre
68    */
69   
70   public class PaletteRecord
71       extends Record
72   {
73       public final static short sid = 0x92;
74       /** The standard size of an XLS palette */
75       public final static byte STANDARD_PALETTE_SIZE = (byte) 56;
76       /** The byte index of the first color */
77       public final static short FIRST_COLOR_INDEX = (short) 0x8;
78       
79       private short field_1_numcolors;
80       private List  field_2_colors;
81   
82       public PaletteRecord()
83       {
84       }
85       
86       /**
87        * Constructs a custom palette with the default set of colors
88        */
89       public PaletteRecord(short id)
90       {
91           super(id, STANDARD_PALETTE_SIZE, getDefaultData());
92       }
93   
94       /**
95        * Constructs a PaletteRecord record and sets its fields appropriately.
96        *
97        * @param id     id must be 0x92 or an exception will be throw upon validation
98        * @param size  the size of the data area of the record
99        * @param data  data of the record (should not contain sid/len)
100       */
101  
102      public PaletteRecord(short id, short size, byte [] data)
103      {
104          super(id, size, data);
105      }
106  
107      /**
108       * Constructs a PaletteRecord record and sets its fields appropriately.
109       *
110       * @param id     id must be 0x0A or an exception will be throw upon validation
111       * @param size  the size of the data area of the record
112       * @param data  data of the record (should not contain sid/len)
113       * @param offset of the record's data
114       */
115  
116      public PaletteRecord(short id, short size, byte [] data, int offset)
117      {
118          super(id, size, data, offset);
119      }
120  
121      protected void validateSid(short id)
122      {
123          if (id != sid)
124          {
125              throw new RecordFormatException("NOT An Palette RECORD");
126          }
127      }
128  
129      protected void fillFields(byte [] data, short size, int offset)
130      {
131         field_1_numcolors = LittleEndian.getShort(data,offset+0); 
132         field_2_colors    = new ArrayList(field_1_numcolors);
133         for (int k = 0; k < field_1_numcolors; k++) {
134             field_2_colors.add(new PColor(
135                                           data[2+ offset+(k * 4) +0],
136                                           data[2+ offset+(k * 4) +1],
137                                           data[2+ offset+(k * 4) +2]
138                                          )
139                                );
140         } 
141      }
142  
143      public String toString()
144      {
145          StringBuffer buffer = new StringBuffer();
146  
147          buffer.append("[PALETTE]\n");
148          buffer.append("  numcolors     = ").append(field_1_numcolors)
149                .append('\n');
150          for (int k = 0; k < field_1_numcolors; k++) {
151          PColor c = (PColor) field_2_colors.get(k);
152          buffer.append("* colornum      = ").append(k)
153                .append('\n');
154          buffer.append(c.toString());
155          buffer.append("/*colornum      = ").append(k)
156                .append('\n');
157          }
158          buffer.append("[/PALETTE]\n");
159          return buffer.toString();
160      }
161  
162      public int serialize(int offset, byte [] data)
163      {
164          LittleEndian.putShort(data, 0 + offset, sid);
165          LittleEndian.putShort(data, 2 + offset, (short) (getRecordSize() - 4));
166          LittleEndian.putShort(data, 4 + offset, field_1_numcolors);
167          for (int k = 0; k < field_1_numcolors; k++) {
168            PColor c = (PColor)field_2_colors.get(k);
169            c.serialize(data, (6+offset+(k*4)));
170          }
171  
172          return getRecordSize();
173      }
174  
175      public int getRecordSize()
176      {
177          return 4 + 2 + (field_1_numcolors * 4);
178      }
179  
180      public short getSid()
181      {
182          return this.sid;
183      }
184  
185      /**
186       * Returns the color value at a given index
187       *
188       * @return the RGB triplet for the color, or null if the specified index
189       * does not exist
190       */
191      public byte[] getColor(short byteIndex)
192      {
193          int i = byteIndex - FIRST_COLOR_INDEX;
194          if (i < 0 || i >= field_2_colors.size())
195          {
196              return null;
197          }
198          PColor color = (PColor) field_2_colors.get(i);
199          return new byte[] { color.red, color.green, color.blue };
200      }
201      
202      /**
203       * Sets the color value at a given index
204       *
205       * If the given index is greater than the current last color index,
206       * then black is inserted at every index required to make the palette continuous.
207       *
208       * @param i the index to set; if this index is less than 0x8 or greater than
209       * 0x40, then no modification is made
210       */
211      public void setColor(short byteIndex, byte red, byte green, byte blue)
212      {
213          int i = byteIndex - FIRST_COLOR_INDEX;
214          if (i < 0 || i >= STANDARD_PALETTE_SIZE)
215          {
216              return;
217          }
218          while (field_2_colors.size() <= i)
219          {
220              field_2_colors.add(new PColor((byte) 0, (byte) 0, (byte) 0));
221          }
222          PColor custColor = new PColor(red, green, blue);
223          field_2_colors.set(i, custColor);
224      }
225      
226      /**
227       * Returns the default palette as PaletteRecord binary data
228       *
229       * @see org.apache.poi.hssf.model.Workbook#createPalette
230       */
231      public static byte[] getDefaultData()
232      {
233          return new byte[]
234          {
235              STANDARD_PALETTE_SIZE, (byte) 0,
236              (byte) 0, (byte) 0, (byte) 0, (byte) 0, //color 0...
237              (byte) 255, (byte) 255, (byte) 255, (byte) 0,
238              (byte) 255, (byte) 0, (byte) 0, (byte) 0,
239              (byte) 0, (byte) 255, (byte) 0, (byte) 0,
240              (byte) 0, (byte) 0, (byte) 255, (byte) 0,
241              (byte) 255, (byte) 255, (byte) 0, (byte) 0,
242              (byte) 255, (byte) 0, (byte) 255, (byte) 0,
243              (byte) 0, (byte) 255, (byte) 255, (byte) 0,
244              (byte) 128, (byte) 0, (byte) 0, (byte) 0,
245              (byte) 0, (byte) 128, (byte) 0, (byte) 0,
246              (byte) 0, (byte) 0, (byte) 128, (byte) 0,
247              (byte) 128, (byte) 128, (byte) 0, (byte) 0,
248              (byte) 128, (byte) 0, (byte) 128, (byte) 0,
249              (byte) 0, (byte) 128, (byte) 128, (byte) 0,
250              (byte) 192, (byte) 192, (byte) 192, (byte) 0,
251              (byte) 128, (byte) 128, (byte) 128, (byte) 0,
252              (byte) 153, (byte) 153, (byte) 255, (byte) 0,
253              (byte) 153, (byte) 51, (byte) 102, (byte) 0,
254              (byte) 255, (byte) 255, (byte) 204, (byte) 0,
255              (byte) 204, (byte) 255, (byte) 255, (byte) 0,
256              (byte) 102, (byte) 0, (byte) 102, (byte) 0,
257              (byte) 255, (byte) 128, (byte) 128, (byte) 0,
258              (byte) 0, (byte) 102, (byte) 204, (byte) 0,
259              (byte) 204, (byte) 204, (byte) 255, (byte) 0,
260              (byte) 0, (byte) 0, (byte) 128, (byte) 0,
261              (byte) 255, (byte) 0, (byte) 255, (byte) 0,
262              (byte) 255, (byte) 255, (byte) 0, (byte) 0,
263              (byte) 0, (byte) 255, (byte) 255, (byte) 0, 
264              (byte) 128, (byte) 0, (byte) 128, (byte) 0,
265              (byte) 128, (byte) 0, (byte) 0, (byte) 0,
266              (byte) 0, (byte) 128, (byte) 128, (byte) 0,
267              (byte) 0, (byte) 0, (byte) 255, (byte) 0,
268              (byte) 0, (byte) 204, (byte) 255, (byte) 0,
269              (byte) 204, (byte) 255, (byte) 255, (byte) 0,
270              (byte) 204, (byte) 255, (byte) 204, (byte) 0,
271              (byte) 255, (byte) 255, (byte) 153, (byte) 0,
272              (byte) 153, (byte) 204, (byte) 255, (byte) 0,
273              (byte) 255, (byte) 153, (byte) 204, (byte) 0,
274              (byte) 204, (byte) 153, (byte) 255, (byte) 0,
275              (byte) 255, (byte) 204, (byte) 153, (byte) 0,
276              (byte) 51, (byte) 102, (byte) 255, (byte) 0,
277              (byte) 51, (byte) 204, (byte) 204, (byte) 0,
278              (byte) 153, (byte) 204, (byte) 0, (byte) 0,
279              (byte) 255, (byte) 204, (byte) 0, (byte) 0,
280              (byte) 255, (byte) 153, (byte) 0, (byte) 0,
281              (byte) 255, (byte) 102, (byte) 0, (byte) 0,
282              (byte) 102, (byte) 102, (byte) 153, (byte) 0,
283              (byte) 150, (byte) 150, (byte) 150, (byte) 0,
284              (byte) 0, (byte) 51, (byte) 102, (byte) 0,
285              (byte) 51, (byte) 153, (byte) 102, (byte) 0,
286              (byte) 0, (byte) 51, (byte) 0, (byte) 0,
287              (byte) 51, (byte) 51, (byte) 0, (byte) 0,
288              (byte) 153, (byte) 51, (byte) 0, (byte) 0,
289              (byte) 153, (byte) 51, (byte) 102, (byte) 0,
290              (byte) 51, (byte) 51, (byte) 153, (byte) 0,
291              (byte) 51, (byte) 51, (byte) 51, (byte) 0
292          };
293      }
294  }
295  
296  /**
297   * PColor - element in the list of colors - consider it a "struct"
298   */
299  class PColor {
300    public byte red;
301    public byte green;
302    public byte blue;
303    public PColor(byte red, byte green, byte blue) {
304      this.red=red;
305      this.green=green;
306      this.blue=blue;
307    }
308  
309    public void serialize(byte[] data, int offset) {
310       data[offset + 0] = red;
311       data[offset + 1] = green;
312       data[offset + 2] = blue;
313       data[offset + 3] = 0;
314    }
315  
316    public String toString() {
317          StringBuffer buffer = new StringBuffer();
318          buffer.append("  red           = ").append(red & 0xff).append('\n');
319          buffer.append("  green         = ").append(green & 0xff).append('\n');
320          buffer.append("  blue          = ").append(blue & 0xff).append('\n');
321          return buffer.toString();
322    }
323  }
324