001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.ddf;
019:
020: import org.apache.poi.util.HexDump;
021: import org.apache.poi.util.LittleEndian;
022:
023: import java.io.ByteArrayOutputStream;
024:
025: /**
026: * The BSE record is related closely to the <code>EscherBlipRecord</code> and stores
027: * extra information about the blip. A blip record is actually stored inside
028: * the BSE record even though the BSE record isn't actually a container record.
029: *
030: * @author Glen Stampoultzis
031: * @see EscherBlipRecord
032: */
033: public class EscherBSERecord extends EscherRecord {
034: public static final short RECORD_ID = (short) 0xF007;
035: public static final String RECORD_DESCRIPTION = "MsofbtBSE";
036:
037: public static final byte BT_ERROR = 0;
038: public static final byte BT_UNKNOWN = 1;
039: public static final byte BT_EMF = 2;
040: public static final byte BT_WMF = 3;
041: public static final byte BT_PICT = 4;
042: public static final byte BT_JPEG = 5;
043: public static final byte BT_PNG = 6;
044: public static final byte BT_DIB = 7;
045:
046: private byte field_1_blipTypeWin32;
047: private byte field_2_blipTypeMacOS;
048: private byte[] field_3_uid; // 16 bytes
049: private short field_4_tag;
050: private int field_5_size;
051: private int field_6_ref;
052: private int field_7_offset;
053: private byte field_8_usage;
054: private byte field_9_name;
055: private byte field_10_unused2;
056: private byte field_11_unused3;
057: private EscherBlipRecord field_12_blipRecord;
058:
059: private byte[] remainingData;
060:
061: /**
062: * This method deserializes the record from a byte array.
063: *
064: * @param data The byte array containing the escher record information
065: * @param offset The starting offset into <code>data</code>.
066: * @param recordFactory May be null since this is not a container record.
067: * @return The number of bytes read from the byte array.
068: */
069: public int fillFields(byte[] data, int offset,
070: EscherRecordFactory recordFactory) {
071: int bytesRemaining = readHeader(data, offset);
072: int pos = offset + 8;
073: field_1_blipTypeWin32 = data[pos];
074: field_2_blipTypeMacOS = data[pos + 1];
075: System.arraycopy(data, pos + 2, field_3_uid = new byte[16], 0,
076: 16);
077: field_4_tag = LittleEndian.getShort(data, pos + 18);
078: field_5_size = LittleEndian.getInt(data, pos + 20);
079: field_6_ref = LittleEndian.getInt(data, pos + 24);
080: field_7_offset = LittleEndian.getInt(data, pos + 28);
081: field_8_usage = data[pos + 32];
082: field_9_name = data[pos + 33];
083: field_10_unused2 = data[pos + 34];
084: field_11_unused3 = data[pos + 35];
085: bytesRemaining -= 36;
086: int bytesRead = 0;
087: if (bytesRemaining > 0) {
088: field_12_blipRecord = (EscherBlipRecord) recordFactory
089: .createRecord(data, pos + 36);
090: bytesRead = field_12_blipRecord.fillFields(data, pos + 36,
091: recordFactory);
092: }
093: pos += 36 + bytesRead;
094: bytesRemaining -= bytesRead;
095: // if (field_1_blipTypeWin32 == BT_PNG)
096: // {
097: // byte[] uid = new byte[16];
098: // System.arraycopy( data, pos + 36, uid, 0, 16 );
099: // byte[] puid = new byte[16];
100: // System.arraycopy( data, pos + 52, puid, 0, 16 );
101: // byte tag = data[pos+68];
102: // System.out.println( HexDump.dump( data, 0, 0 ) );
103: // byte[] pngBytes = EscherBlipRecord.decompress( data, pos+69, bytesRemaining);
104: // }
105:
106: remainingData = new byte[bytesRemaining];
107: System.arraycopy(data, pos, remainingData, 0, bytesRemaining);
108: return bytesRemaining
109: + 8
110: + 36
111: + (field_12_blipRecord == null ? 0
112: : field_12_blipRecord.getRecordSize());
113:
114: }
115:
116: /**
117: * This method serializes this escher record into a byte array.
118: *
119: * @param offset The offset into <code>data</code> to start writing the record data to.
120: * @param data The byte array to serialize to.
121: * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
122: * @return The number of bytes written.
123: * @see NullEscherSerializationListener
124: */
125: public int serialize(int offset, byte[] data,
126: EscherSerializationListener listener) {
127: listener.beforeRecordSerialize(offset, getRecordId(), this );
128:
129: if (remainingData == null)
130: remainingData = new byte[0];
131:
132: LittleEndian.putShort(data, offset, getOptions());
133: LittleEndian.putShort(data, offset + 2, getRecordId());
134: if (remainingData == null)
135: remainingData = new byte[0];
136: int blipSize = field_12_blipRecord == null ? 0
137: : field_12_blipRecord.getRecordSize();
138: int remainingBytes = remainingData.length + 36 + blipSize;
139: LittleEndian.putInt(data, offset + 4, remainingBytes);
140:
141: data[offset + 8] = field_1_blipTypeWin32;
142: data[offset + 9] = field_2_blipTypeMacOS;
143: for (int i = 0; i < 16; i++)
144: data[offset + 10 + i] = field_3_uid[i];
145: LittleEndian.putShort(data, offset + 26, field_4_tag);
146: LittleEndian.putInt(data, offset + 28, field_5_size);
147: LittleEndian.putInt(data, offset + 32, field_6_ref);
148: LittleEndian.putInt(data, offset + 36, field_7_offset);
149: data[offset + 40] = field_8_usage;
150: data[offset + 41] = field_9_name;
151: data[offset + 42] = field_10_unused2;
152: data[offset + 43] = field_11_unused3;
153: int bytesWritten = 0;
154: if (field_12_blipRecord != null) {
155: bytesWritten = field_12_blipRecord.serialize(offset + 44,
156: data, new NullEscherSerializationListener());
157: }
158: if (remainingData == null)
159: remainingData = new byte[0];
160: System.arraycopy(remainingData, 0, data, offset + 44
161: + bytesWritten, remainingData.length);
162: int pos = offset + 8 + 36 + remainingData.length + bytesWritten;
163:
164: listener.afterRecordSerialize(pos, getRecordId(), pos - offset,
165: this );
166: return pos - offset;
167: }
168:
169: /**
170: * Returns the number of bytes that are required to serialize this record.
171: *
172: * @return Number of bytes
173: */
174: public int getRecordSize() {
175: return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1
176: + field_12_blipRecord.getRecordSize()
177: + (remainingData == null ? 0 : remainingData.length);
178: }
179:
180: /**
181: * The short name for this record
182: */
183: public String getRecordName() {
184: return "BSE";
185: }
186:
187: /**
188: * The expected blip type under windows (failure to match this blip type will result in
189: * Excel converting to this format).
190: */
191: public byte getBlipTypeWin32() {
192: return field_1_blipTypeWin32;
193: }
194:
195: /**
196: * Set the expected win32 blip type
197: */
198: public void setBlipTypeWin32(byte blipTypeWin32) {
199: this .field_1_blipTypeWin32 = blipTypeWin32;
200: }
201:
202: /**
203: * The expected blip type under MacOS (failure to match this blip type will result in
204: * Excel converting to this format).
205: */
206: public byte getBlipTypeMacOS() {
207: return field_2_blipTypeMacOS;
208: }
209:
210: /**
211: * Set the expected MacOS blip type
212: */
213: public void setBlipTypeMacOS(byte blipTypeMacOS) {
214: this .field_2_blipTypeMacOS = blipTypeMacOS;
215: }
216:
217: /**
218: * 16 byte MD4 checksum.
219: */
220: public byte[] getUid() {
221: return field_3_uid;
222: }
223:
224: /**
225: * 16 byte MD4 checksum.
226: */
227: public void setUid(byte[] uid) {
228: this .field_3_uid = uid;
229: }
230:
231: /**
232: * unused
233: */
234: public short getTag() {
235: return field_4_tag;
236: }
237:
238: /**
239: * unused
240: */
241: public void setTag(short tag) {
242: this .field_4_tag = tag;
243: }
244:
245: /**
246: * Blip size in stream.
247: */
248: public int getSize() {
249: return field_5_size;
250: }
251:
252: /**
253: * Blip size in stream.
254: */
255: public void setSize(int size) {
256: this .field_5_size = size;
257: }
258:
259: /**
260: * The reference count of this blip.
261: */
262: public int getRef() {
263: return field_6_ref;
264: }
265:
266: /**
267: * The reference count of this blip.
268: */
269: public void setRef(int ref) {
270: this .field_6_ref = ref;
271: }
272:
273: /**
274: * File offset in the delay stream.
275: */
276: public int getOffset() {
277: return field_7_offset;
278: }
279:
280: /**
281: * File offset in the delay stream.
282: */
283: public void setOffset(int offset) {
284: this .field_7_offset = offset;
285: }
286:
287: /**
288: * Defines the way this blip is used.
289: */
290: public byte getUsage() {
291: return field_8_usage;
292: }
293:
294: /**
295: * Defines the way this blip is used.
296: */
297: public void setUsage(byte usage) {
298: this .field_8_usage = usage;
299: }
300:
301: /**
302: * The length in characters of the blip name.
303: */
304: public byte getName() {
305: return field_9_name;
306: }
307:
308: /**
309: * The length in characters of the blip name.
310: */
311: public void setName(byte name) {
312: this .field_9_name = name;
313: }
314:
315: public byte getUnused2() {
316: return field_10_unused2;
317: }
318:
319: public void setUnused2(byte unused2) {
320: this .field_10_unused2 = unused2;
321: }
322:
323: public byte getUnused3() {
324: return field_11_unused3;
325: }
326:
327: public void setUnused3(byte unused3) {
328: this .field_11_unused3 = unused3;
329: }
330:
331: public EscherBlipRecord getBlipRecord() {
332: return field_12_blipRecord;
333: }
334:
335: public void setBlipRecord(EscherBlipRecord field_12_blipRecord) {
336: this .field_12_blipRecord = field_12_blipRecord;
337: }
338:
339: /**
340: * Any remaining data in this record.
341: */
342: public byte[] getRemainingData() {
343: return remainingData;
344: }
345:
346: /**
347: * Any remaining data in this record.
348: */
349: public void setRemainingData(byte[] remainingData) {
350: this .remainingData = remainingData;
351: }
352:
353: /**
354: * Calculate the string representation of this object
355: */
356: public String toString() {
357: String nl = System.getProperty("line.separator");
358:
359: String extraData;
360: ByteArrayOutputStream b = new ByteArrayOutputStream();
361: try {
362: HexDump.dump(this .remainingData, 0, b, 0);
363: extraData = b.toString();
364: } catch (Exception e) {
365: extraData = e.toString();
366: }
367: return getClass().getName() + ":" + nl + " RecordId: 0x"
368: + HexDump.toHex(RECORD_ID) + nl + " Options: 0x"
369: + HexDump.toHex(getOptions()) + nl
370: + " BlipTypeWin32: " + field_1_blipTypeWin32 + nl
371: + " BlipTypeMacOS: " + field_2_blipTypeMacOS + nl
372: + " SUID: " + HexDump.toHex(field_3_uid) + nl
373: + " Tag: " + field_4_tag + nl + " Size: "
374: + field_5_size + nl + " Ref: " + field_6_ref + nl
375: + " Offset: " + field_7_offset + nl + " Usage: "
376: + field_8_usage + nl + " Name: " + field_9_name + nl
377: + " Unused2: " + field_10_unused2 + nl + " Unused3: "
378: + field_11_unused3 + nl + " blipRecord: "
379: + field_12_blipRecord + nl + " Extra Data:" + nl
380: + extraData;
381: }
382:
383: /**
384: * Retrieve the string representation given a blip id.
385: */
386: public String getBlipType(byte b) {
387: switch (b) {
388: case BT_ERROR:
389: return " ERROR";
390: case BT_UNKNOWN:
391: return " UNKNOWN";
392: case BT_EMF:
393: return " EMF";
394: case BT_WMF:
395: return " WMF";
396: case BT_PICT:
397: return " PICT";
398: case BT_JPEG:
399: return " JPEG";
400: case BT_PNG:
401: return " PNG";
402: case BT_DIB:
403: return " DIB";
404: default:
405: if (b < 32)
406: return " NotKnown";
407: else
408: return " Client";
409: }
410: }
411:
412: }
|