FileDocCategorySizeDatePackage
HSLFSlideShow.javaAPI DocApache Poi 3.0.116922Thu May 31 18:45:28 BST 2007org.apache.poi.hslf

HSLFSlideShow

public class HSLFSlideShow extends POIDocument
This class contains the main functionality for the Powerpoint file "reader". It is only a very basic class for now
author
Nick Burch

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.

param
fileName The name of the file to read.
throws
IOException if there is a problem while parsing the document.

		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.

param
inputStream the source of the data
throws
IOException if there is a problem while parsing the document.

		//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.

param
filesystem the POIFS FileSystem to read from
throws
IOException if there is a problem while parsing the document.

		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 voidaddPicture(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 intappendRootLevelRecord(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 voidbuildRecords()
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 voidclose()
Shuts things down. Closes underlying streams etc

throws
IOException

		if(istream != null) {
			istream.close();
		}
		filesystem = null;
	
public org.apache.poi.hslf.record.CurrentUserAtomgetCurrentUserAtom()
Fetch the Current User Atom of the document

 return currentUser; 
protected org.apache.poi.poifs.filesystem.POIFSFileSystemgetPOIFSFileSystem()
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
array with the read pictures or null if the presentation doesn't contain pictures.

		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 voidreadCurrentUserStream()
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 voidreadOtherStreams()
Find any other streams from the filesystem, and load them

		// Currently, there aren't any
	
private voidreadPictures()
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 voidreadPowerPointStream()
Extracts the main PowerPoint document stream from the POI file, ready to be passed

throws
IOException

		// 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 voidwrite(java.io.OutputStream out)
Writes out the slideshow file the is represented by an instance of this class

param
out The OutputStream to write to.
throws
IOException If there is an unexpected IOException from the passed in OutputStream

	// 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);