Index: src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java (revision 1538068) +++ src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java (working copy) @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.Map; import org.apache.poi.POIDocument; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; @@ -40,12 +41,14 @@ import org.apache.poi.hslf.record.PersistRecord; import org.apache.poi.hslf.record.PositionDependentRecord; import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.hslf.record.UserEditAtom; import org.apache.poi.hslf.usermodel.ObjectData; import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.DocumentInputStream; +import org.apache.poi.poifs.filesystem.EntryUtils; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.LittleEndian; @@ -59,6 +62,8 @@ * @author Nick Burch */ public final class HSLFSlideShow extends POIDocument { + public static final int UNSET_OFFSET = -1; + // For logging private POILogger logger = POILogFactory.getLogger(this.getClass()); @@ -346,6 +351,7 @@ int offset = pos; // Image signature + @SuppressWarnings("unused") int signature = LittleEndian.getUShort(pictstream, pos); pos += LittleEndian.SHORT_SIZE; // Image type + 0xF018 @@ -392,7 +398,88 @@ } } + /** + * This is a helper functions, which is needed for adding new position dependent records + * or finally write the slideshow to a file. + * + * @param os the stream to write to, if null only the references are updated + * @param interestingRecords a map of interesting records (PersistPtrHolder and UserEditAtom) + * referenced by their RecordType. Only the very last of each type will be saved to the map. + * May be null, if not needed. + * @throws IOException + */ + public void updateAndWriteDependantRecords(OutputStream os, Map interestingRecords) + throws IOException { + // 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 + Map oldToNewPositions = new HashMap(); + // First pass - figure out where all the position dependent + // records are going to end up, in the new scheme + // (Annoyingly, some powerpoint files have PersistPtrHolders + // that reference slides after the PersistPtrHolder) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (Record record : _records) { + if(record instanceof PositionDependentRecord) { + PositionDependentRecord pdr = (PositionDependentRecord)record; + int oldPos = pdr.getLastOnDiskOffset(); + int newPos = baos.size(); + pdr.setLastOnDiskOffset(newPos); + if (oldPos != UNSET_OFFSET) { + // new records don't need a mapping, as they aren't in a relation yet + oldToNewPositions.put(Integer.valueOf(oldPos),Integer.valueOf(newPos)); + } + } + + // Dummy write out, so the position winds on properly + record.writeOut(baos); + } + baos = null; + + // 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 + UserEditAtom usr = null; + for (Record record : _records) { + if (record 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)record; + pdr.updateOtherRecordReferences(oldToNewPositions); + + // Grab interesting records as they come past + // this will only save the very last record of each type + RecordTypes.Type saveme = null; + int recordType = (int)record.getRecordType(); + if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) { + saveme = RecordTypes.PersistPtrIncrementalBlock; + } else if (recordType == RecordTypes.UserEditAtom.typeID) { + saveme = RecordTypes.UserEditAtom; + usr = (UserEditAtom)pdr; + } + if (interestingRecords != null && saveme != null) { + interestingRecords.put(saveme,pdr); + } + } + + // Whatever happens, write out that record tree + if (os != null) { + record.writeOut(os); + } + } + + // Update and write out the Current User atom + int oldLastUserEditAtomPos = (int)currentUser.getCurrentEditOffset(); + Integer newLastUserEditAtomPos = oldToNewPositions.get(oldLastUserEditAtomPos); + if(usr == null || newLastUserEditAtomPos == null || usr.getLastOnDiskOffset() != newLastUserEditAtomPos) { + throw new HSLFException("Couldn't find the new location of the last UserEditAtom that used to be at " + oldLastUserEditAtomPos); + } + currentUser.setCurrentEditOffset(usr.getLastOnDiskOffset()); + } + /** * Writes out the slideshow file the is represented by an instance * of this class. @@ -426,49 +513,13 @@ // Write out the Property Streams writeProperties(outFS, writtenEntries); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 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(); + // records we happen across + updateAndWriteDependantRecords(baos, null); - // 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(Integer.valueOf(oldPos),Integer.valueOf(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(); @@ -476,15 +527,6 @@ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); outFS.createDocument(bais,"PowerPoint Document"); writtenEntries.add("PowerPoint Document"); - - - // Update and write out the Current User atom - int oldLastUserEditAtomPos = (int)currentUser.getCurrentEditOffset(); - Integer newLastUserEditAtomPos = (Integer)oldToNewPositions.get(Integer.valueOf(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); writtenEntries.add("Current User"); @@ -506,7 +548,7 @@ // If requested, write out any other streams we spot if(preserveNodes) { - copyNodes(directory.getFileSystem(), outFS, writtenEntries); + EntryUtils.copyNodes(directory.getFileSystem(), outFS, writtenEntries); } // Send the POIFSFileSystem object out to the underlying stream @@ -612,9 +654,9 @@ public ObjectData[] getEmbeddedObjects() { if (_objects == null) { List objects = new ArrayList(); - for (int i = 0; i < _records.length; i++) { - if (_records[i] instanceof ExOleObjStg) { - objects.add(new ObjectData((ExOleObjStg) _records[i])); + for (Record r : _records) { + if (r instanceof ExOleObjStg) { + objects.add(new ObjectData((ExOleObjStg)r)); } } _objects = objects.toArray(new ObjectData[objects.size()]); Index: src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java (working copy) @@ -17,10 +17,14 @@ package org.apache.poi.hslf.record; -import java.io.*; -import java.util.zip.InflaterInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; import java.util.zip.DeflaterOutputStream; -import java.util.Hashtable; +import java.util.zip.InflaterInputStream; import org.apache.poi.util.BoundedInputStream; import org.apache.poi.util.LittleEndian; @@ -180,7 +184,7 @@ myLastOnDiskOffset = offset; } - public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) { + public void updateOtherRecordReferences(Map oldToNewReferencesLookup) { return; } Index: src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java (working copy) @@ -17,13 +17,14 @@ package org.apache.poi.hslf.record; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.POILogger; - import java.io.IOException; import java.io.OutputStream; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Map; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; /** * General holder for PersistPtrFullBlock and PersistPtrIncrementalBlock @@ -181,7 +182,7 @@ * At write-out time, update the references to the sheets to their * new positions */ - public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) { + public void updateOtherRecordReferences(Map oldToNewReferencesLookup) { int[] slideIDs = getKnownSlideIDs(); // Loop over all the slides we know about Index: src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecord.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecord.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecord.java (working copy) @@ -16,7 +16,7 @@ ==================================================================== */ package org.apache.poi.hslf.record; -import java.util.Hashtable; +import java.util.Map; /** * Records which either care about where they are on disk, or have other @@ -47,5 +47,5 @@ * Offer the record the list of records that have changed their * location as part of the writeout. */ - public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup); + public void updateOtherRecordReferences(Map oldToNewReferencesLookup); } Index: src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java (working copy) @@ -16,7 +16,7 @@ ==================================================================== */ package org.apache.poi.hslf.record; -import java.util.Hashtable; +import java.util.Map; /** * A special (and dangerous) kind of Record Atom that cares about where @@ -48,5 +48,5 @@ * Allows records to update their internal pointers to other records * locations */ - public abstract void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup); + public abstract void updateOtherRecordReferences(Map oldToNewReferencesLookup); } Index: src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordContainer.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordContainer.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordContainer.java (working copy) @@ -16,7 +16,7 @@ ==================================================================== */ package org.apache.poi.hslf.record; -import java.util.Hashtable; +import java.util.Map; /** * A special (and dangerous) kind of Record Container, for which other @@ -60,7 +60,7 @@ * Since we're a container, we don't mind if other records move about. * If we're told they have, just return straight off. */ - public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) { + public void updateOtherRecordReferences(Map oldToNewReferencesLookup) { return; } } Index: src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java (revision 1540295) +++ src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java (working copy) @@ -17,10 +17,11 @@ package org.apache.poi.hslf.record; -import org.apache.poi.util.LittleEndian; import java.io.IOException; import java.io.OutputStream; -import java.util.Hashtable; +import java.util.Map; + +import org.apache.poi.util.LittleEndian; /** * A UserEdit Atom (type 4085). Holds information which bits of the file @@ -90,22 +91,22 @@ // Get the offset to the previous incremental save's UserEditAtom // This will be the byte offset on disk where the previous one // starts, or 0 if this is the first one - lastUserEditAtomOffset = LittleEndian.getInt(source,start+8+8); + setLastUserEditAtomOffset(LittleEndian.getInt(source,start+8+8)); // Get the offset to the persist pointers // This will be the byte offset on disk where the preceding // PersistPtrFullBlock or PersistPtrIncrementalBlock starts - persistPointersOffset = LittleEndian.getInt(source,start+12+8); + setPersistPointersOffset(LittleEndian.getInt(source,start+12+8)); // Get the persist reference for the document persist object // Normally seems to be 1 docPersistRef = LittleEndian.getInt(source,start+16+8); // Maximum number of persist objects written - maxPersistWritten = LittleEndian.getInt(source,start+20+8); + setMaxPersistWritten(LittleEndian.getInt(source,start+20+8)); // Last view type - lastViewType = LittleEndian.getShort(source,start+24+8); + setLastViewType(LittleEndian.getShort(source,start+24+8)); // There might be a few more bytes, which are a reserved field reserved = new byte[len-26-8]; @@ -121,7 +122,7 @@ * At write-out time, update the references to PersistPtrs and * other UserEditAtoms to point to their new positions */ - public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) { + public void updateOtherRecordReferences(Map oldToNewReferencesLookup) { // Look up the new positions of our preceding UserEditAtomOffset if(lastUserEditAtomOffset != 0) { Integer newLocation = oldToNewReferencesLookup.get(Integer.valueOf(lastUserEditAtomOffset)); Index: src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java (revision 1553435) +++ src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java (working copy) @@ -25,7 +25,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherContainerRecord; @@ -35,11 +40,43 @@ import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.HSLFException; -import org.apache.poi.hslf.model.*; +import org.apache.poi.hslf.model.HeadersFooters; +import org.apache.poi.hslf.model.Hyperlink; +import org.apache.poi.hslf.model.MovieShape; import org.apache.poi.hslf.model.Notes; +import org.apache.poi.hslf.model.PPFont; +import org.apache.poi.hslf.model.Picture; +import org.apache.poi.hslf.model.Shape; import org.apache.poi.hslf.model.Slide; -import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.model.SlideMaster; +import org.apache.poi.hslf.model.TitleMaster; +import org.apache.poi.hslf.record.Document; +import org.apache.poi.hslf.record.DocumentAtom; +import org.apache.poi.hslf.record.ExAviMovie; +import org.apache.poi.hslf.record.ExControl; +import org.apache.poi.hslf.record.ExEmbed; +import org.apache.poi.hslf.record.ExEmbedAtom; +import org.apache.poi.hslf.record.ExHyperlink; +import org.apache.poi.hslf.record.ExHyperlinkAtom; +import org.apache.poi.hslf.record.ExMCIMovie; +import org.apache.poi.hslf.record.ExObjList; +import org.apache.poi.hslf.record.ExObjListAtom; +import org.apache.poi.hslf.record.ExOleObjAtom; +import org.apache.poi.hslf.record.ExOleObjStg; +import org.apache.poi.hslf.record.ExVideoContainer; +import org.apache.poi.hslf.record.FontCollection; +import org.apache.poi.hslf.record.FontEntityAtom; +import org.apache.poi.hslf.record.HeadersFootersContainer; +import org.apache.poi.hslf.record.PersistPtrHolder; +import org.apache.poi.hslf.record.PositionDependentRecord; +import org.apache.poi.hslf.record.PositionDependentRecordContainer; +import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.record.RecordContainer; +import org.apache.poi.hslf.record.RecordTypes; +import org.apache.poi.hslf.record.SlideListWithText; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; +import org.apache.poi.hslf.record.SlidePersistAtom; +import org.apache.poi.hslf.record.UserEditAtom; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.POILogFactory; @@ -58,15 +95,12 @@ // What we're based on private HSLFSlideShow _hslfSlideShow; - // Low level contents, as taken from HSLFSlideShow - private Record[] _records; - // Pointers to the most recent versions of the core records // (Document, Notes, Slide etc) private Record[] _mostRecentCoreRecords; // Lookup between the PersitPtr "sheet" IDs, and the position // in the mostRecentCoreRecords array - private Hashtable _sheetIdToCoreRecordsLookup; + private Map _sheetIdToCoreRecordsLookup; // Records that are interesting private Document _documentRecord; @@ -97,10 +131,9 @@ public SlideShow(HSLFSlideShow hslfSlideShow) { // Get useful things from our base slideshow _hslfSlideShow = hslfSlideShow; - _records = _hslfSlideShow.getRecords(); // Handle Parent-aware Records - for (Record record : _records) { + for (Record record : _hslfSlideShow.getRecords()) { if(record instanceof RecordContainer){ RecordContainer.handleParentAwareRecords((RecordContainer)record); } @@ -135,25 +168,23 @@ */ private void findMostRecentCoreRecords() { // To start with, find the most recent in the byte offset domain - Hashtable mostRecentByBytes = new Hashtable(); - for (int i = 0; i < _records.length; i++) { - if (_records[i] instanceof PersistPtrHolder) { - PersistPtrHolder pph = (PersistPtrHolder) _records[i]; + Map mostRecentByBytes = new HashMap(); + for (Record record : _hslfSlideShow.getRecords()) { + if (record instanceof PersistPtrHolder) { + PersistPtrHolder pph = (PersistPtrHolder) record; // If we've already seen any of the "slide" IDs for this // PersistPtr, remove their old positions int[] ids = pph.getKnownSlideIDs(); - for (int j = 0; j < ids.length; j++) { - Integer id = Integer.valueOf(ids[j]); + for (int id : ids) { if (mostRecentByBytes.containsKey(id)) { mostRecentByBytes.remove(id); } } // Now, update the byte level locations with their latest values - Hashtable thisSetOfLocations = pph.getSlideLocationsLookup(); - for (int j = 0; j < ids.length; j++) { - Integer id = Integer.valueOf(ids[j]); + Map thisSetOfLocations = pph.getSlideLocationsLookup(); + for (int id : ids) { mostRecentByBytes.put(id, thisSetOfLocations.get(id)); } } @@ -165,54 +196,48 @@ // We'll also want to be able to turn the slide IDs into a position // in this array - _sheetIdToCoreRecordsLookup = new Hashtable(); - int[] allIDs = new int[_mostRecentCoreRecords.length]; - Enumeration ids = mostRecentByBytes.keys(); - for (int i = 0; i < allIDs.length; i++) { - Integer id = ids.nextElement(); - allIDs[i] = id.intValue(); - } + _sheetIdToCoreRecordsLookup = new HashMap(); + Integer[] allIDs = mostRecentByBytes.keySet().toArray(new Integer[mostRecentByBytes.size()]); Arrays.sort(allIDs); for (int i = 0; i < allIDs.length; i++) { - _sheetIdToCoreRecordsLookup.put(Integer.valueOf(allIDs[i]), Integer.valueOf(i)); + _sheetIdToCoreRecordsLookup.put(allIDs[i], i); } // Now convert the byte offsets back into record offsets - for (int i = 0; i < _records.length; i++) { - if (_records[i] instanceof PositionDependentRecord) { - PositionDependentRecord pdr = (PositionDependentRecord) _records[i]; - Integer recordAt = Integer.valueOf(pdr.getLastOnDiskOffset()); + for (Record record : _hslfSlideShow.getRecords()) { + if (record instanceof PositionDependentRecord) { + PositionDependentRecord pdr = (PositionDependentRecord) record; + int recordAt = pdr.getLastOnDiskOffset(); // Is it one we care about? - for (int j = 0; j < allIDs.length; j++) { - Integer thisID = Integer.valueOf(allIDs[j]); - Integer thatRecordAt = mostRecentByBytes.get(thisID); + for (Integer thisID : allIDs) { + int thatRecordAt = mostRecentByBytes.get(thisID); - if (thatRecordAt.equals(recordAt)) { + if (thatRecordAt == recordAt) { // Bingo. Now, where do we store it? Integer storeAtI = _sheetIdToCoreRecordsLookup.get(thisID); int storeAt = storeAtI.intValue(); // Tell it its Sheet ID, if it cares if (pdr instanceof PositionDependentRecordContainer) { - PositionDependentRecordContainer pdrc = (PositionDependentRecordContainer) _records[i]; - pdrc.setSheetId(thisID.intValue()); + PositionDependentRecordContainer pdrc = (PositionDependentRecordContainer) record; + pdrc.setSheetId(thisID); } // Finally, save the record - _mostRecentCoreRecords[storeAt] = _records[i]; + _mostRecentCoreRecords[storeAt] = record; } } } } // Now look for the interesting records in there - for (int i = 0; i < _mostRecentCoreRecords.length; i++) { + for (Record record : _mostRecentCoreRecords) { // Check there really is a record at this number - if (_mostRecentCoreRecords[i] != null) { + if (record != null) { // Find the Document, and interesting things in it - if (_mostRecentCoreRecords[i].getRecordType() == RecordTypes.Document.typeID) { - _documentRecord = (Document) _mostRecentCoreRecords[i]; + if (record.getRecordType() == RecordTypes.Document.typeID) { + _documentRecord = (Document) record; _fonts = _documentRecord.getEnvironment().getFontCollection(); } } else { @@ -296,9 +321,8 @@ ArrayList mmr = new ArrayList(); ArrayList tmr = new ArrayList(); - for (int i = 0; i < masterSets.length; i++) { - Record r = getCoreRecordForSAS(masterSets[i]); - SlideAtomsSet sas = masterSets[i]; + for (SlideAtomsSet sas : masterSets) { + Record r = getCoreRecordForSAS(sas); int sheetNo = sas.getSlidePersistAtom().getSlideIdentifier(); if (r instanceof org.apache.poi.hslf.record.Slide) { TitleMaster master = new TitleMaster((org.apache.poi.hslf.record.Slide) r, @@ -313,11 +337,8 @@ } } - _masters = new SlideMaster[mmr.size()]; - mmr.toArray(_masters); - - _titleMasters = new TitleMaster[tmr.size()]; - tmr.toArray(_titleMasters); + _masters = mmr.toArray(new SlideMaster[mmr.size()]); + _titleMasters = tmr.toArray(new TitleMaster[tmr.size()]); } // Having sorted out the masters, that leaves the notes and slides @@ -326,14 +347,14 @@ // notesSLWT org.apache.poi.hslf.record.Notes[] notesRecords; SlideAtomsSet[] notesSets = new SlideAtomsSet[0]; - Hashtable slideIdToNotes = new Hashtable(); + Map slideIdToNotes = new HashMap(); if (notesSLWT == null) { // None notesRecords = new org.apache.poi.hslf.record.Notes[0]; } else { // Match up the records and the SlideAtomSets notesSets = notesSLWT.getSlideAtomsSets(); - ArrayList notesRecordsL = + List notesRecordsL = new ArrayList(); for (int i = 0; i < notesSets.length; i++) { // Get the right core record @@ -346,8 +367,8 @@ // Record the match between slide id and these notes SlidePersistAtom spa = notesSets[i].getSlidePersistAtom(); - Integer slideId = Integer.valueOf(spa.getSlideIdentifier()); - slideIdToNotes.put(slideId, Integer.valueOf(i)); + int slideId = spa.getSlideIdentifier(); + slideIdToNotes.put(slideId, i); } else { logger.log(POILogger.ERROR, "A Notes SlideAtomSet at " + i + " said its record was at refID " @@ -686,9 +707,8 @@ // (Will stay as null if no SlidePersistAtom exists yet in // the slide, or only master slide's ones do) SlidePersistAtom prev = null; - SlideAtomsSet[] sas = slist.getSlideAtomsSets(); - for (int j = 0; j < sas.length; j++) { - SlidePersistAtom spa = sas[j].getSlidePersistAtom(); + for (SlideAtomsSet sas : slist.getSlideAtomsSets()) { + SlidePersistAtom spa = sas.getSlidePersistAtom(); if (spa.getSlideIdentifier() < 0) { // This is for a master slide // Odd, since we only deal with the Slide SLWT @@ -850,19 +870,16 @@ * found */ public PPFont getFont(int idx) { - PPFont font = null; FontCollection fonts = getDocumentRecord().getEnvironment().getFontCollection(); - Record[] ch = fonts.getChildRecords(); - for (int i = 0; i < ch.length; i++) { - if (ch[i] instanceof FontEntityAtom) { - FontEntityAtom atom = (FontEntityAtom) ch[i]; + for (Record ch : fonts.getChildRecords()) { + if (ch instanceof FontEntityAtom) { + FontEntityAtom atom = (FontEntityAtom) ch; if (atom.getFontIndex() == idx) { - font = new PPFont(atom); - break; + return new PPFont(atom); } } } - return font; + return null; } /** @@ -885,11 +902,10 @@ boolean ppt2007 = "___PPT12".equals(tag); HeadersFootersContainer hdd = null; - Record[] ch = _documentRecord.getChildRecords(); - for (int i = 0; i < ch.length; i++) { - if (ch[i] instanceof HeadersFootersContainer - && ((HeadersFootersContainer) ch[i]).getOptions() == HeadersFootersContainer.SlideHeadersFootersContainer) { - hdd = (HeadersFootersContainer) ch[i]; + for (Record ch : _documentRecord.getChildRecords()) { + if (ch instanceof HeadersFootersContainer + && ((HeadersFootersContainer) ch).getOptions() == HeadersFootersContainer.SlideHeadersFootersContainer) { + hdd = (HeadersFootersContainer) ch; break; } } @@ -912,11 +928,10 @@ boolean ppt2007 = "___PPT12".equals(tag); HeadersFootersContainer hdd = null; - Record[] ch = _documentRecord.getChildRecords(); - for (int i = 0; i < ch.length; i++) { - if (ch[i] instanceof HeadersFootersContainer - && ((HeadersFootersContainer) ch[i]).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer) { - hdd = (HeadersFootersContainer) ch[i]; + for (Record ch : _documentRecord.getChildRecords()) { + if (ch instanceof HeadersFootersContainer + && ((HeadersFootersContainer) ch).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer) { + hdd = (HeadersFootersContainer) ch; break; } } @@ -1107,37 +1122,23 @@ } protected int addPersistentObject(PositionDependentRecord slideRecord) { - int slideRecordPos = _hslfSlideShow.appendRootLevelRecord((Record)slideRecord); - _records = _hslfSlideShow.getRecords(); + slideRecord.setLastOnDiskOffset(HSLFSlideShow.UNSET_OFFSET); + _hslfSlideShow.appendRootLevelRecord((Record)slideRecord); - // Add the new Slide into the PersistPtr stuff - int offset = 0; - int slideOffset = 0; - PersistPtrHolder ptr = null; - UserEditAtom usr = null; - int i = 0; - for (Record record : _records) { - // Grab interesting records as they come past - int recordType = (int)record.getRecordType(); - if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) { - ptr = (PersistPtrHolder)record; - } - if (recordType == RecordTypes.UserEditAtom.typeID) { - usr = (UserEditAtom)record; - } + // 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 + Map interestingRecords = + new HashMap(); - if (i++ == slideRecordPos) { - slideOffset = offset; - } - - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - record.writeOut(out); - offset += out.size(); - } catch (IOException e) { - throw new HSLFException(e); - } - } + try { + _hslfSlideShow.updateAndWriteDependantRecords(null,interestingRecords); + } catch (IOException e) { + throw new HSLFException(e); + } + + PersistPtrHolder ptr = (PersistPtrHolder)interestingRecords.get(RecordTypes.PersistPtrIncrementalBlock); + UserEditAtom usr = (UserEditAtom)interestingRecords.get(RecordTypes.UserEditAtom); // persist ID is UserEditAtom.maxPersistWritten + 1 int psrId = usr.getMaxPersistWritten() + 1; @@ -1149,6 +1150,7 @@ // Add the new slide into the last PersistPtr // (Also need to tell it where it is) + int slideOffset = slideRecord.getLastOnDiskOffset(); slideRecord.setLastOnDiskOffset(slideOffset); ptr.addSlideLookup(psrId, slideOffset); logger.log(POILogger.INFO, "New slide/object ended up at " + slideOffset); Index: src/scratchpad/testcases/org/apache/poi/hslf/HSLFTestDataSamples.java =================================================================== --- src/scratchpad/testcases/org/apache/poi/hslf/HSLFTestDataSamples.java (revision 0) +++ src/scratchpad/testcases/org/apache/poi/hslf/HSLFTestDataSamples.java (working copy) @@ -0,0 +1,74 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hslf; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.hslf.usermodel.SlideShow; + +public class HSLFTestDataSamples { + + private static final POIDataSamples _inst = POIDataSamples.getSlideShowInstance(); + + public static InputStream openSampleFileStream(String sampleFileName) { + return _inst.openResourceAsStream(sampleFileName); + } + public static File getSampleFile(String sampleFileName) { + return _inst.getFile(sampleFileName); + } + public static byte[] getTestDataFileContent(String fileName) { + return _inst.readFile(fileName); + } + + /** + * Writes a slideshow to a ByteArrayOutputStream and reads it back + * from a ByteArrayInputStream.

+ * Useful for verifying that the serialisation round trip + */ + public static HSLFSlideShow writeOutAndReadBack(HSLFSlideShow original) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + original.write(baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + return new HSLFSlideShow(bais); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Writes a slideshow to a ByteArrayOutputStream and reads it back + * from a ByteArrayInputStream.

+ * Useful for verifying that the serialisation round trip + */ + public static SlideShow writeOutAndReadBack(SlideShow original) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + original.write(baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + return new SlideShow(bais); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} Index: src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java =================================================================== --- src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java (revision 1538068) +++ src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java (working copy) @@ -18,15 +18,16 @@ package org.apache.poi.hslf; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; + import junit.framework.TestCase; -import org.apache.poi.hslf.usermodel.SlideShow; -import org.apache.poi.poifs.filesystem.*; import org.apache.poi.POIDataSamples; - -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** * Tests that HSLFSlideShow writes the powerpoint bit of data back out @@ -160,4 +161,16 @@ assertEquals(_oData[i], _nData[i]); } } + + public void test48593() throws Exception { + SlideShow slideShow = new SlideShow(); + slideShow.createSlide(); + slideShow = HSLFTestDataSamples.writeOutAndReadBack(slideShow); + slideShow.createSlide(); + slideShow = HSLFTestDataSamples.writeOutAndReadBack(slideShow); + slideShow.createSlide(); + slideShow = HSLFTestDataSamples.writeOutAndReadBack(slideShow); + slideShow.createSlide(); + slideShow = HSLFTestDataSamples.writeOutAndReadBack(slideShow); + } }