HSLFSlideShowpublic class HSLFSlideShow extends POIDocument This class contains the main functionality for the Powerpoint file
"reader". It is only a very basic class for now |
Fields Summary |
---|
protected POILogger | logger | private InputStream | istream | private CurrentUserAtom | currentUser | private byte[] | _docstream | private Record[] | _records | private PictureData[] | _pictures |
Constructors Summary |
---|
public HSLFSlideShow(String fileName)Constructs a Powerpoint document from fileName. Parses the document
and places all the important stuff into data structures.
this(new FileInputStream(fileName));
| public HSLFSlideShow(InputStream inputStream)Constructs a Powerpoint document from an input stream. Parses the
document and places all the important stuff into data structures.
//do Ole stuff
this(new POIFSFileSystem(inputStream));
istream = inputStream;
| public HSLFSlideShow(POIFSFileSystem filesystem)Constructs a Powerpoint document from a POIFS Filesystem. Parses the
document and places all the important stuff into data structures.
this.filesystem = filesystem;
// First up, grab the "Current User" stream
// We need this before we can detect Encrypted Documents
readCurrentUserStream();
// Next up, grab the data that makes up the
// PowerPoint stream
readPowerPointStream();
// Check to see if we have an encrypted document,
// bailing out if we do
boolean encrypted = EncryptedSlideShow.checkIfEncrypted(this);
if(encrypted) {
throw new EncryptedPowerPointFileException("Encrypted PowerPoint files are not supported");
}
// Now, build records based on the PowerPoint stream
buildRecords();
// Look for Property Streams:
readProperties();
// Look for any other streams
readOtherStreams();
// Look for Picture Streams:
readPictures();
| public HSLFSlideShow()Constructs a new, empty, Powerpoint document.
this(HSLFSlideShow.class.getResourceAsStream("/org/apache/poi/hslf/data/empty.ppt"));
|
Methods Summary |
---|
public void | addPicture(org.apache.poi.hslf.usermodel.PictureData img)Add a new picture to this presentation.
// Copy over the existing pictures, into an array one bigger
PictureData[] lst;
if(_pictures == null) {
lst = new PictureData[1];
} else {
lst = new PictureData[(_pictures.length+1)];
System.arraycopy(_pictures,0,lst,0,_pictures.length);
}
// Add in the new image
lst[lst.length - 1] = img;
_pictures = lst;
| public synchronized int | appendRootLevelRecord(org.apache.poi.hslf.record.Record newRecord)Adds a new root level record, at the end, but before the last
PersistPtrIncrementalBlock.
int addedAt = -1;
Record[] r = new Record[_records.length+1];
boolean added = false;
for(int i=(_records.length-1); i>=0; i--) {
if(added) {
// Just copy over
r[i] = _records[i];
} else {
r[(i+1)] = _records[i];
if(_records[i] instanceof PersistPtrHolder) {
r[i] = newRecord;
added = true;
addedAt = i;
}
}
}
_records = r;
return addedAt;
| private void | buildRecords()Builds the list of records, based on the contents
of the PowerPoint stream
// The format of records in a powerpoint file are:
// <little endian 2 byte "info">
// <little endian 2 byte "type">
// <little endian 4 byte "length">
// If it has a zero length, following it will be another record
// <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz>
// If it has a length, depending on its type it may have children or data
// If it has children, these will follow straight away
// <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>>
// If it has data, this will come straigh after, and run for the length
// <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd>
// All lengths given exclude the 8 byte record header
// (Data records are known as Atoms)
// Document should start with:
// 0F 00 E8 03 ## ## ## ##
// (type 1000 = document, info 00 0f is normal, rest is document length)
// 01 00 E9 03 28 00 00 00
// (type 1001 = document atom, info 00 01 normal, 28 bytes long)
// 80 16 00 00 E0 10 00 00 xx xx xx xx xx xx xx xx
// 05 00 00 00 0A 00 00 00 xx xx xx
// (the contents of the document atom, not sure what it means yet)
// (records then follow)
// When parsing a document, look to see if you know about that type
// of the current record. If you know it's a type that has children,
// process the record's data area looking for more records
// If you know about the type and it doesn't have children, either do
// something with the data (eg TextRun) or skip over it
// If you don't know about the type, play safe and skip over it (using
// its length to know where the next record will start)
//
_records = read(_docstream, (int)currentUser.getCurrentEditOffset());
| public void | close()Shuts things down. Closes underlying streams etc
if(istream != null) {
istream.close();
}
filesystem = null;
| public org.apache.poi.hslf.record.CurrentUserAtom | getCurrentUserAtom()Fetch the Current User Atom of the document return currentUser;
| protected org.apache.poi.poifs.filesystem.POIFSFileSystem | getPOIFSFileSystem()Returns the underlying POIFSFileSystem for the document
that is open.
return filesystem;
| public org.apache.poi.hslf.usermodel.PictureData[] | getPictures()Return array of pictures contained in this presentation
return _pictures;
| public org.apache.poi.hslf.record.Record[] | getRecords()Returns an array of all the records found in the slideshow return _records;
| public byte[] | getUnderlyingBytes()Returns an array of the bytes of the file. Only correct after a
call to open or write - at all other times might be wrong! return _docstream;
| private org.apache.poi.hslf.record.Record[] | read(byte[] docstream, int usrOffset)
ArrayList lst = new ArrayList();
while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
lst.add(new Integer(usrOffset));
int psrOffset = usr.getPersistPointersOffset();
PersistPtrHolder ptr = (PersistPtrHolder)Record.buildRecordAtOffset(docstream, psrOffset);
lst.add(new Integer(psrOffset));
Hashtable entries = ptr.getSlideLocationsLookup();
for (Iterator it = entries.keySet().iterator(); it.hasNext(); ) {
Integer id = (Integer)it.next();
Integer offset = (Integer)entries.get(id);
lst.add(offset);
}
usrOffset = usr.getLastUserEditAtomOffset();
}
//sort found records by offset.
//(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
Object a[] = lst.toArray();
Arrays.sort(a);
Record[] rec = new Record[lst.size()];
for (int i = 0; i < a.length; i++) {
Integer offset = (Integer)a[i];
rec[i] = (Record)Record.buildRecordAtOffset(docstream, offset.intValue());
}
return rec;
| private void | readCurrentUserStream()Find the "Current User" stream, and load it
try {
currentUser = new CurrentUserAtom(filesystem);
} catch(IOException ie) {
logger.log(POILogger.ERROR, "Error finding Current User Atom:\n" + ie);
currentUser = new CurrentUserAtom();
}
| private void | readOtherStreams()Find any other streams from the filesystem, and load them
// Currently, there aren't any
| private void | readPictures()Find and read in pictures contained in this presentation
byte[] pictstream;
try {
DocumentEntry entry = (DocumentEntry)filesystem.getRoot().getEntry("Pictures");
pictstream = new byte[entry.getSize()];
DocumentInputStream is = filesystem.createDocumentInputStream("Pictures");
is.read(pictstream);
} catch (FileNotFoundException e){
// Silently catch exceptions if the presentation doesn't
// contain pictures - will use a null set instead
return;
}
List p = new ArrayList();
int pos = 0;
// An empty picture record (length 0) will take up 8 bytes
while (pos <= (pictstream.length-8)) {
int offset = pos;
// Image signature
int signature = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE;
// Image type + 0xF018
int type = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE;
// Image size (excluding the 8 byte header)
int imgsize = LittleEndian.getInt(pictstream, pos);
pos += LittleEndian.INT_SIZE;
// The image size must be 0 or greater
// (0 is allowed, but odd, since we do wind on by the header each
// time, so we won't get stuck)
if(imgsize < 0) {
throw new CorruptPowerPointFileException("The file contains a picture, at position " + p.size() + ", which has a negatively sized data length, so we can't trust any of the picture data");
}
// If they type (including the bonus 0xF018) is 0, skip it
if(type == 0) {
logger.log(POILogger.ERROR, "Problem reading picture: Invalid image type 0, on picture with length " + imgsize + ".\nYou document will probably become corrupted if you save it!");
logger.log(POILogger.ERROR, "" + pos);
} else {
// Copy the data, ready to pass to PictureData
byte[] imgdata = new byte[imgsize];
if(imgsize > 0) {
System.arraycopy(pictstream, pos, imgdata, 0, imgdata.length);
}
// Build the PictureData object from the data
try {
PictureData pict = PictureData.create(type - 0xF018);
pict.setRawData(imgdata);
pict.setOffset(offset);
p.add(pict);
} catch(IllegalArgumentException e) {
logger.log(POILogger.ERROR, "Problem reading picture: " + e + "\nYou document will probably become corrupted if you save it!");
}
}
pos += imgsize;
}
_pictures = (PictureData[])p.toArray(new PictureData[p.size()]);
| private void | readPowerPointStream()Extracts the main PowerPoint document stream from the
POI file, ready to be passed
// Get the main document stream
DocumentEntry docProps =
(DocumentEntry)filesystem.getRoot().getEntry("PowerPoint Document");
// Grab the document stream
_docstream = new byte[docProps.getSize()];
filesystem.createDocumentInputStream("PowerPoint Document").read(_docstream);
| public void | write(java.io.OutputStream out)Writes out the slideshow file the is represented by an instance of
this class
// Get a new Filesystem to write into
POIFSFileSystem outFS = new POIFSFileSystem();
// Write out the Property Streams
writeProperties(outFS);
// For position dependent records, hold where they were and now are
// As we go along, update, and hand over, to any Position Dependent
// records we happen across
Hashtable oldToNewPositions = new Hashtable();
// First pass - figure out where all the position dependent
// records are going to end up, in the new scheme
// (Annoyingly, some powerpoing files have PersistPtrHolders
// that reference slides after the PersistPtrHolder)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for(int i=0; i<_records.length; i++) {
if(_records[i] instanceof PositionDependentRecord) {
PositionDependentRecord pdr = (PositionDependentRecord)_records[i];
int oldPos = pdr.getLastOnDiskOffset();
int newPos = baos.size();
pdr.setLastOnDiskOffset(newPos);
oldToNewPositions.put(new Integer(oldPos),new Integer(newPos));
//System.out.println(oldPos + " -> " + newPos);
}
// Dummy write out, so the position winds on properly
_records[i].writeOut(baos);
}
// No go back through, actually writing ourselves out
baos.reset();
for(int i=0; i<_records.length; i++) {
// For now, we're only handling PositionDependentRecord's that
// happen at the top level.
// In future, we'll need the handle them everywhere, but that's
// a bit trickier
if(_records[i] instanceof PositionDependentRecord) {
// We've already figured out their new location, and
// told them that
// Tell them of the positions of the other records though
PositionDependentRecord pdr = (PositionDependentRecord)_records[i];
pdr.updateOtherRecordReferences(oldToNewPositions);
}
// Whatever happens, write out that record tree
_records[i].writeOut(baos);
}
// Update our cached copy of the bytes that make up the PPT stream
_docstream = baos.toByteArray();
// Write the PPT stream into the POIFS layer
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
outFS.createDocument(bais,"PowerPoint Document");
// Update and write out the Current User atom
int oldLastUserEditAtomPos = (int)currentUser.getCurrentEditOffset();
Integer newLastUserEditAtomPos = (Integer)oldToNewPositions.get(new Integer(oldLastUserEditAtomPos));
if(newLastUserEditAtomPos == null) {
throw new HSLFException("Couldn't find the new location of the UserEditAtom that used to be at " + oldLastUserEditAtomPos);
}
currentUser.setCurrentEditOffset(newLastUserEditAtomPos.intValue());
currentUser.writeToFS(outFS);
// Write any pictures, into another stream
if (_pictures != null) {
ByteArrayOutputStream pict = new ByteArrayOutputStream();
for (int i = 0; i < _pictures.length; i++ ) {
_pictures[i].write(pict);
}
outFS.createDocument(
new ByteArrayInputStream(pict.toByteArray()), "Pictures"
);
}
// Send the POIFSFileSystem object out to the underlying stream
outFS.writeFilesystem(out);
|
|