View | Details | Raw Unified | Return to bug 43222
Collapse All | Expand All

(-)src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java (+90 lines)
Line 0 Link Here
1
/* ====================================================================
2
   Copyright 2002-2004   Apache Software Foundation
3
4
   Licensed under the Apache License, Version 2.0 (the "License");
5
   you may not use this file except in compliance with the License.
6
   You may obtain a copy of the License at
7
8
       http://www.apache.org/licenses/LICENSE-2.0
9
10
   Unless required by applicable law or agreed to in writing, software
11
   distributed under the License is distributed on an "AS IS" BASIS,
12
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
   See the License for the specific language governing permissions and
14
   limitations under the License.
15
==================================================================== */
16
17
18
package org.apache.poi.hssf.usermodel;
19
20
import java.io.IOException;
21
import java.util.Iterator;
22
23
import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
24
import org.apache.poi.hssf.record.ObjRecord;
25
import org.apache.poi.poifs.filesystem.DirectoryEntry;
26
import org.apache.poi.poifs.filesystem.Entry;
27
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
28
import org.apache.poi.util.HexDump;
29
30
/**
31
 * Represents binary object (i.e. OLE) data stored in the file.  Eg. A GIF, JPEG etc...
32
 *
33
 * @author Daniel Noll
34
 */
35
public class HSSFObjectData
36
{
37
    /**
38
     * Underlying object record ultimately containing a reference to the object.
39
     */
40
    private ObjRecord record;
41
42
    /**
43
     * Reference to the filesystem, required for retrieving the object data.
44
     */
45
    private POIFSFileSystem poifs;
46
47
    /**
48
     * Constructs object data by wrapping a lower level object record.
49
     *
50
     * @param record the low-level object record.
51
     * @param poifs the filesystem, required for retrieving the object data.
52
     */
53
    public HSSFObjectData(ObjRecord record, POIFSFileSystem poifs)
54
    {
55
        this.record = record;
56
        this.poifs = poifs;
57
    }
58
59
    /**
60
     * Gets the object data.
61
     *
62
     * @return the object data as an OLE2 directory.
63
     * @throws IOException if there was an error reading the data.
64
     */
65
    public DirectoryEntry getDirectory() throws IOException
66
    {
67
        Iterator subRecordIter = record.getSubRecords().iterator();
68
        while (subRecordIter.hasNext())
69
        {
70
            Object subRecord = subRecordIter.next();
71
            if (subRecord instanceof EmbeddedObjectRefSubRecord)
72
            {
73
                int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
74
                String streamName = "MBD" + HexDump.toHex(streamId);
75
76
                Entry entry = poifs.getRoot().getEntry(streamName);
77
                if (entry instanceof DirectoryEntry)
78
                {
79
                    return (DirectoryEntry) entry;
80
                }
81
                else
82
                {
83
                    throw new IOException("Stream " + streamName + " was not an OLE2 directory");
84
                }
85
            }
86
        }
87
88
        throw new IllegalStateException("Object data does not contain a reference to an embedded object OLE2 directory");
89
    }
90
}
(-)src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java (-1 / +46 lines)
Lines 208-214 Link Here
208
        setPropertiesFromWorkbook(workbook);
208
        setPropertiesFromWorkbook(workbook);
209
        int recOffset = workbook.getNumRecords();
209
        int recOffset = workbook.getNumRecords();
210
        int sheetNum = 0;
210
        int sheetNum = 0;
211
        
211
212
        // convert all LabelRecord records to LabelSSTRecord
212
        // convert all LabelRecord records to LabelSSTRecord
213
        convertLabelRecords(records, recOffset);        
213
        convertLabelRecords(records, recOffset);        
214
        while (recOffset < records.size())
214
        while (recOffset < records.size())
Lines 1327-1332 Link Here
1327
     */
1327
     */
1328
    public List getAllPictures()
1328
    public List getAllPictures()
1329
    {
1329
    {
1330
        // The drawing group record always exists at the top level, so we won't need to do this recursively.
1330
        List pictures = new ArrayList();
1331
        List pictures = new ArrayList();
1331
        Iterator recordIter = workbook.getRecords().iterator();
1332
        Iterator recordIter = workbook.getRecords().iterator();
1332
        while (recordIter.hasNext())
1333
        while (recordIter.hasNext())
Lines 1374-1379 Link Here
1374
        }
1375
        }
1375
    }
1376
    }
1376
1377
1378
    /**
1379
     * Gets all embedded OLE2 objects from the Workbook.
1380
     *
1381
     * @return the list of embedded objects (a list of {@link HSSFObjectData} objects.)
1382
     */
1383
    public List getAllEmbeddedObjects()
1384
    {
1385
        List objects = new ArrayList();
1386
        for (int i = 0; i < getNumberOfSheets(); i++)
1387
        {
1388
            getAllEmbeddedObjects(getSheetAt(i).getSheet().getRecords(), objects);
1389
        }
1390
        return objects;
1391
    }
1392
1393
    /**
1394
     * Gets all embedded OLE2 objects from the Workbook.
1395
     *
1396
     * @param records the list of records to search.
1397
     * @param objects the list of embedded objects to populate.
1398
     */
1399
    private void getAllEmbeddedObjects(List records, List objects)
1400
    {
1401
        Iterator recordIter = records.iterator();
1402
        while (recordIter.hasNext())
1403
        {
1404
            Object obj = recordIter.next();
1405
            if (obj instanceof ObjRecord)
1406
            {
1407
                // TODO: More convenient way of determining if there is stored binary.
1408
                // TODO: Link to the data stored in the other stream.
1409
                Iterator subRecordIter = ((ObjRecord) obj).getSubRecords().iterator();
1410
                while (subRecordIter.hasNext())
1411
                {
1412
                    Object sub = subRecordIter.next();
1413
                    if (sub instanceof EmbeddedObjectRefSubRecord)
1414
                    {
1415
                        objects.add(new HSSFObjectData((ObjRecord) obj, poifs));
1416
                    }
1417
                }
1418
            }
1419
        }
1420
    }
1421
1377
    private byte[] newUID()
1422
    private byte[] newUID()
1378
    {
1423
    {
1379
        byte[] bytes = new byte[16];
1424
        byte[] bytes = new byte[16];
(-)src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java (+184 lines)
Line 0 Link Here
1
2
/* ====================================================================
3
   Licensed to the Apache Software Foundation (ASF) under one or more
4
   contributor license agreements.  See the NOTICE file distributed with
5
   this work for additional information regarding copyright ownership.
6
   The ASF licenses this file to You under the Apache License, Version 2.0
7
   (the "License"); you may not use this file except in compliance with
8
   the License.  You may obtain a copy of the License at
9
10
       http://www.apache.org/licenses/LICENSE-2.0
11
12
   Unless required by applicable law or agreed to in writing, software
13
   distributed under the License is distributed on an "AS IS" BASIS,
14
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
   See the License for the specific language governing permissions and
16
   limitations under the License.
17
==================================================================== */
18
19
20
package org.apache.poi.hssf.record;
21
22
23
24
import org.apache.poi.util.*;
25
26
/**
27
 * A sub-record within the OBJ record which stores a reference to an object
28
 * stored in a separate entry within the OLE2 compound file.
29
 *
30
 * @author Daniel Noll
31
 */
32
public class EmbeddedObjectRefSubRecord
33
    extends SubRecord
34
{
35
    public static final short sid = 0x9;
36
37
    public short   field_1_stream_id_offset;                    // Offset to stream ID from the point after this value.
38
    public short[] field_2_unknown;                             // Unknown stuff at the front.  TODO: Confirm that it's a short[]
39
    // TODO: Consider making a utility class for these.  I've discovered the same field ordering
40
    //       in FormatRecord and StringRecord, it may be elsewhere too.
41
    public short   field_3_unicode_len;                         // Length of Unicode string.
42
    public boolean field_4_unicode_flag;                        // Flags whether the string is Unicode.
43
    public String  field_5_ole_classname;                       // Classname of the embedded OLE document (e.g. Word.Document.8)
44
    public int     field_6_stream_id;                           // ID of the OLE stream containing the actual data.
45
46
    public EmbeddedObjectRefSubRecord()
47
    {
48
    }
49
50
    /**
51
     * Constructs an EmbeddedObjectRef record and sets its fields appropriately.
52
     *
53
     * @param in the record input stream.
54
     */
55
    public EmbeddedObjectRefSubRecord(RecordInputStream in)
56
    {
57
        super(in);
58
    }
59
60
    /**
61
     * Checks the sid matches the expected side for this record
62
     *
63
     * @param id   the expected sid.
64
     */
65
    protected void validateSid(short id)
66
    {
67
        if (id != sid)
68
        {
69
            throw new RecordFormatException("Not a EmbeddedObjectRef record");
70
        }
71
    }
72
73
    public short getSid()
74
    {
75
        return sid;
76
    }
77
78
    protected void fillFields(RecordInputStream in)
79
    {
80
        field_1_stream_id_offset       = in.readShort();
81
        field_2_unknown                = in.readShortArray();
82
        field_3_unicode_len            = in.readShort();
83
        field_4_unicode_flag           = ( in.readByte() & 0x01 ) != 0;
84
85
        if ( field_4_unicode_flag )
86
        {
87
            field_5_ole_classname      = in.readUnicodeLEString( field_3_unicode_len );
88
        }
89
        else
90
        {
91
            field_5_ole_classname      = in.readCompressedUnicode( field_3_unicode_len );
92
        }
93
94
        // Padded with NUL bytes.  The -2 is because field_1_stream_id_offset
95
        // is relative to after the offset field, whereas in.getRecordOffset()
96
        // is relative to the start of this record.
97
        while (in.getRecordOffset() - 2 < field_1_stream_id_offset)
98
        {
99
            in.readByte(); // discard
100
        }
101
102
        field_6_stream_id              = in.readInt();
103
    }
104
105
    public int serialize(int offset, byte[] data)
106
    {
107
        int pos = offset;
108
109
        LittleEndian.putShort(data, pos, field_1_stream_id_offset); pos += 2;
110
        LittleEndian.putShortArray(data, pos, field_2_unknown); pos += field_2_unknown.length * 2 + 2;
111
        LittleEndian.putShort(data, pos, field_3_unicode_len); pos += 2;
112
        data[pos] = field_4_unicode_flag ? (byte) 0x01 : (byte) 0x00; pos++;
113
114
        if ( field_4_unicode_flag )
115
        {
116
            StringUtil.putUnicodeLE( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length() * 2;
117
        }
118
        else
119
        {
120
            StringUtil.putCompressedUnicode( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length();
121
        }
122
123
        // Padded with NUL bytes.
124
        pos = field_1_stream_id_offset;
125
126
        LittleEndian.putInt(data, pos, field_6_stream_id); pos += 4;
127
128
        return getRecordSize();
129
    }
130
131
    /**
132
     * Size of record (exluding 4 byte header)
133
     */
134
    public int getRecordSize()
135
    {
136
        // Conveniently this stores the length of all the crap before the final int value.
137
        return field_1_stream_id_offset + 4;
138
    }
139
140
    /**
141
     * Gets the stream ID containing the actual data.  The data itself
142
     * can be found under a top-level directory entry in the OLE2 filesystem
143
     * under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
144
     * this ID converted into hex (in big endian order, funnily enough.)
145
     * 
146
     * @return the data stream ID.
147
     */
148
    public int getStreamId()
149
    {
150
        return field_6_stream_id;
151
    }
152
153
    public String toString()
154
    {
155
        StringBuffer buffer = new StringBuffer();
156
        buffer.append("[ftPictFmla]\n");
157
        buffer.append("    .streamIdOffset       = ")
158
            .append("0x").append(HexDump.toHex(  field_1_stream_id_offset ))
159
            .append(" (").append( field_1_stream_id_offset ).append(" )")
160
            .append(System.getProperty("line.separator"));
161
        buffer.append("    .unknown              = ")
162
            .append("0x").append(HexDump.toHex(  field_2_unknown ))
163
            .append(" (").append( field_2_unknown ).append(" )")
164
            .append(System.getProperty("line.separator"));
165
        buffer.append("    .unicodeLen           = ")
166
            .append("0x").append(HexDump.toHex(  field_3_unicode_len ))
167
            .append(" (").append( field_3_unicode_len ).append(" )")
168
            .append(System.getProperty("line.separator"));
169
        buffer.append("    .unicodeFlag          = ")
170
            .append("0x").append( field_4_unicode_flag ? 0x01 : 0x00 )
171
            .append(" (").append( field_4_unicode_flag ).append(" )")
172
            .append(System.getProperty("line.separator"));
173
        buffer.append("    .oleClassname         = ")
174
            .append(field_5_ole_classname)
175
            .append(System.getProperty("line.separator"));
176
        buffer.append("    .streamId             = ")
177
            .append("0x").append(HexDump.toHex(  field_6_stream_id ))
178
            .append(" (").append( field_6_stream_id ).append(" )")
179
            .append(System.getProperty("line.separator"));
180
        buffer.append("[/ftPictFmla]");
181
        return buffer.toString();
182
    }
183
184
}
(-)src/java/org/apache/poi/hssf/record/SubRecord.java (+3 lines)
Lines 58-63 Link Here
58
            case CommonObjectDataSubRecord.sid:
58
            case CommonObjectDataSubRecord.sid:
59
                r = new CommonObjectDataSubRecord( in );
59
                r = new CommonObjectDataSubRecord( in );
60
                break;
60
                break;
61
            case EmbeddedObjectRefSubRecord.sid:
62
                r = new EmbeddedObjectRefSubRecord( in );
63
                break;
61
            case GroupMarkerSubRecord.sid:
64
            case GroupMarkerSubRecord.sid:
62
                r = new GroupMarkerSubRecord( in );
65
                r = new GroupMarkerSubRecord( in );
63
                break;
66
                break;
(-)src/java/org/apache/poi/util/HexDump.java (+19 lines)
Lines 269-274 Link Here
269
    }
269
    }
270
270
271
    /**
271
    /**
272
     * Converts the parameter to a hex value.
273
     *
274
     * @param value     The value to convert
275
     * @return          A String representing the array of shorts
276
     */
277
    public static String toHex(final short[] value)
278
    {
279
        StringBuffer retVal = new StringBuffer();
280
        retVal.append('[');
281
        for(int x = 0; x < value.length; x++)
282
        {
283
            retVal.append(toHex(value[x]));
284
            retVal.append(", ");
285
        }
286
        retVal.append(']');
287
        return retVal.toString();
288
    }
289
290
    /**
272
     * <p>Converts the parameter to a hex value breaking the results into
291
     * <p>Converts the parameter to a hex value breaking the results into
273
     * lines.</p>
292
     * lines.</p>
274
     *
293
     *

Return to bug 43222