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
}
0
  + *
91
  + *
(-)src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java (+42 lines)
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
        getAllEmbeddedObjects(workbook.getRecords(), objects);
1387
        return objects;
1388
    }
1389
1390
    /**
1391
     * Gets all embedded OLE2 objects from the Workbook.
1392
     *
1393
     * @param records the list of records to search.
1394
     * @param objects the list of embedded objects to populate.
1395
     */
1396
    private void getAllEmbeddedObjects(List records, List objects)
1397
    {
1398
        Iterator recordIter = records.iterator();
1399
        while (recordIter.hasNext())
1400
        {
1401
            Object obj = recordIter.next();
1402
            if (obj instanceof ObjRecord)
1403
            {
1404
                // TODO: More convenient way of determining if there is stored binary.
1405
                // TODO: Link to the data stored in the other stream.
1406
                Iterator subRecordIter = ((ObjRecord) obj).getSubRecords().iterator();
1407
                while (subRecordIter.hasNext())
1408
                {
1409
                    Object sub = subRecordIter.next();
1410
                    if (sub instanceof EmbeddedObjectRefSubRecord)
1411
                    {
1412
                        objects.add(new HSSFObjectData((ObjRecord) obj, poifs));
1413
                    }
1414
                }
1415
            }
1416
        }
1417
    }
1418
1377
    private byte[] newUID()
1419
    private byte[] newUID()
1378
    {
1420
    {
1379
        byte[] bytes = new byte[16];
1421
        byte[] bytes = new byte[16];
(-)src/java/org/apache/poi/hssf/model/Workbook.java (-13 / +68 lines)
Lines 18-35 Link Here
18
18
19
package org.apache.poi.hssf.model;
19
package org.apache.poi.hssf.model;
20
20
21
import org.apache.poi.ddf.*;
21
import java.util.ArrayList;
22
import org.apache.poi.hssf.record.*;
22
import java.util.Iterator;
23
import java.util.List;
24
import java.util.Locale;
25
26
import org.apache.poi.ddf.EscherBSERecord;
27
import org.apache.poi.ddf.EscherBoolProperty;
28
import org.apache.poi.ddf.EscherContainerRecord;
29
import org.apache.poi.ddf.EscherDggRecord;
30
import org.apache.poi.ddf.EscherOptRecord;
31
import org.apache.poi.ddf.EscherProperties;
32
import org.apache.poi.ddf.EscherRGBProperty;
33
import org.apache.poi.ddf.EscherRecord;
34
import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
35
import org.apache.poi.hssf.record.BOFRecord;
36
import org.apache.poi.hssf.record.BackupRecord;
37
import org.apache.poi.hssf.record.BookBoolRecord;
38
import org.apache.poi.hssf.record.BoundSheetRecord;
39
import org.apache.poi.hssf.record.CodepageRecord;
40
import org.apache.poi.hssf.record.CountryRecord;
41
import org.apache.poi.hssf.record.DSFRecord;
42
import org.apache.poi.hssf.record.DateWindow1904Record;
43
import org.apache.poi.hssf.record.DrawingGroupRecord;
44
import org.apache.poi.hssf.record.EOFRecord;
45
import org.apache.poi.hssf.record.ExtSSTRecord;
46
import org.apache.poi.hssf.record.ExtendedFormatRecord;
47
import org.apache.poi.hssf.record.ExternSheetRecord;
48
import org.apache.poi.hssf.record.ExternSheetSubRecord;
49
import org.apache.poi.hssf.record.FnGroupCountRecord;
50
import org.apache.poi.hssf.record.FontRecord;
51
import org.apache.poi.hssf.record.FormatRecord;
52
import org.apache.poi.hssf.record.HideObjRecord;
53
import org.apache.poi.hssf.record.InterfaceEndRecord;
54
import org.apache.poi.hssf.record.InterfaceHdrRecord;
55
import org.apache.poi.hssf.record.MMSRecord;
56
import org.apache.poi.hssf.record.NameRecord;
57
import org.apache.poi.hssf.record.PaletteRecord;
58
import org.apache.poi.hssf.record.PasswordRecord;
59
import org.apache.poi.hssf.record.PasswordRev4Record;
60
import org.apache.poi.hssf.record.PrecisionRecord;
61
import org.apache.poi.hssf.record.ProtectRecord;
62
import org.apache.poi.hssf.record.ProtectionRev4Record;
63
import org.apache.poi.hssf.record.RecalcIdRecord;
64
import org.apache.poi.hssf.record.Record;
65
import org.apache.poi.hssf.record.RefreshAllRecord;
66
import org.apache.poi.hssf.record.SSTRecord;
67
import org.apache.poi.hssf.record.StyleRecord;
68
import org.apache.poi.hssf.record.SupBookRecord;
69
import org.apache.poi.hssf.record.TabIdRecord;
70
import org.apache.poi.hssf.record.UnicodeString;
71
import org.apache.poi.hssf.record.UseSelFSRecord;
72
import org.apache.poi.hssf.record.WindowOneRecord;
73
import org.apache.poi.hssf.record.WindowProtectRecord;
74
import org.apache.poi.hssf.record.WriteAccessRecord;
23
import org.apache.poi.hssf.util.HSSFColor;
75
import org.apache.poi.hssf.util.HSSFColor;
24
import org.apache.poi.hssf.util.SheetReferences;
76
import org.apache.poi.hssf.util.SheetReferences;
25
import org.apache.poi.util.POILogFactory;
77
import org.apache.poi.util.POILogFactory;
26
import org.apache.poi.util.POILogger;
78
import org.apache.poi.util.POILogger;
27
79
28
import java.util.ArrayList;
29
import java.util.Iterator;
30
import java.util.List;
31
import java.util.Locale;
32
33
/**
80
/**
34
 * Low level model implementation of a Workbook.  Provides creational methods
81
 * Low level model implementation of a Workbook.  Provides creational methods
35
 * for settings and objects contained in the workbook object.
82
 * for settings and objects contained in the workbook object.
Lines 130-146 Link Here
130
        Workbook  retval  = new Workbook();
177
        Workbook  retval  = new Workbook();
131
        ArrayList records = new ArrayList(recs.size() / 3);
178
        ArrayList records = new ArrayList(recs.size() / 3);
132
179
180
        int bofDepth = 0;
133
        for (int k = 0; k < recs.size(); k++) {
181
        for (int k = 0; k < recs.size(); k++) {
134
            Record rec = ( Record ) recs.get(k);
182
            Record rec = ( Record ) recs.get(k);
135
183
136
            if (rec.getSid() == EOFRecord.sid) {
137
                records.add(rec);
138
                if (log.check( POILogger.DEBUG ))
139
                    log.log(DEBUG, "found workbook eof record at " + k);
140
                break;
141
            }
142
            switch (rec.getSid()) {
184
            switch (rec.getSid()) {
143
185
186
                case BOFRecord.sid:
187
                    bofDepth++;
188
                    break;
189
190
                case EOFRecord.sid:
191
                    bofDepth--;
192
                    if (bofDepth == 0) {
193
                        if (log.check( POILogger.DEBUG ))
194
                            log.log(DEBUG, "found workbook eof record at " + k);
195
                        // Don't break out, will prevent finding other records!
196
                    }
197
                    break;
198
144
                case BoundSheetRecord.sid :
199
                case BoundSheetRecord.sid :
145
                    if (log.check( POILogger.DEBUG ))
200
                    if (log.check( POILogger.DEBUG ))
146
                        log.log(DEBUG, "found boundsheet record at " + k);
201
                        log.log(DEBUG, "found boundsheet record at " + k);
(-)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