Index: src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java =================================================================== --- src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java (revision 1532876) +++ src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java (working copy) @@ -723,53 +723,10 @@ // Add the core records for this new Slide to the record tree org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord(); - int slideRecordPos = _hslfSlideShow.appendRootLevelRecord(slideRecord); - _records = _hslfSlideShow.getRecords(); - - // Add the new Slide into the PersistPtr stuff - int offset = 0; - int slideOffset = 0; - PersistPtrHolder ptr = null; - UserEditAtom usr = null; - for (int i = 0; i < _records.length; i++) { - Record record = _records[i]; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - record.writeOut(out); - } catch (IOException e) { - throw new HSLFException(e); - } - - // Grab interesting records as they come past - if (_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID) { - ptr = (PersistPtrHolder) _records[i]; - } - if (_records[i].getRecordType() == RecordTypes.UserEditAtom.typeID) { - usr = (UserEditAtom) _records[i]; - } - - if (i == slideRecordPos) { - slideOffset = offset; - } - offset += out.size(); - } - - // persist ID is UserEditAtom.maxPersistWritten + 1 - int psrId = usr.getMaxPersistWritten() + 1; + int psrId = addPersistentObject(slideRecord); sp.setRefID(psrId); slideRecord.setSheetId(psrId); - - // Last view is now of the slide - usr.setLastViewType((short) UserEditAtom.LAST_VIEW_SLIDE_VIEW); - usr.setMaxPersistWritten(psrId); // increment the number of persit - // objects - - // Add the new slide into the last PersistPtr - // (Also need to tell it where it is) - slideRecord.setLastOnDiskOffset(slideOffset); - ptr.addSlideLookup(sp.getRefID(), slideOffset); - logger.log(POILogger.INFO, "New slide ended up at " + slideOffset); - + slide.setMasterSheet(_masters[0]); // All done and added return slide; @@ -978,16 +935,6 @@ * @return 0-based index of the movie */ public int addMovie(String path, int type) { - ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); - if (lst == null) { - lst = new ExObjList(); - _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); - } - - ExObjListAtom objAtom = lst.getExObjListAtom(); - // increment the object ID seed - int objectId = (int) objAtom.getObjectIDSeed() + 1; - objAtom.setObjectIDSeed(objectId); ExMCIMovie mci; switch (type) { case MovieShape.MOVIE_MPEG: @@ -1000,11 +947,13 @@ throw new IllegalArgumentException("Unsupported Movie: " + type); } - lst.appendChildRecord(mci); ExVideoContainer exVideo = mci.getExVideo(); - exVideo.getExMediaAtom().setObjectId(objectId); exVideo.getExMediaAtom().setMask(0xE80000); exVideo.getPathAtom().setText(path); + + int objectId = addToObjListAtom(mci); + exVideo.getExMediaAtom().setObjectId(objectId); + return objectId; } @@ -1019,27 +968,18 @@ * @return 0-based index of the control */ public int addControl(String name, String progId) { - ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); - if (lst == null) { - lst = new ExObjList(); - _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); - } - ExObjListAtom objAtom = lst.getExObjListAtom(); - // increment the object ID seed - int objectId = (int) objAtom.getObjectIDSeed() + 1; - objAtom.setObjectIDSeed(objectId); ExControl ctrl = new ExControl(); + ctrl.setProgId(progId); + ctrl.setMenuName(name); + ctrl.setClipboardName(name); + ExOleObjAtom oleObj = ctrl.getExOleObjAtom(); - oleObj.setObjID(objectId); oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE); oleObj.setType(ExOleObjAtom.TYPE_CONTROL); oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT); - - ctrl.setProgId(progId); - ctrl.setMenuName(name); - ctrl.setClipboardName(name); - lst.addChildAfter(ctrl, objAtom); - + + int objectId = addToObjListAtom(ctrl); + oleObj.setObjID(objectId); return objectId; } @@ -1049,19 +989,8 @@ * @return 0-based index of the hyperlink */ public int addHyperlink(Hyperlink link) { - ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); - if (lst == null) { - lst = new ExObjList(); - _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); - } - ExObjListAtom objAtom = lst.getExObjListAtom(); - // increment the object ID seed - int objectId = (int) objAtom.getObjectIDSeed() + 1; - objAtom.setObjectIDSeed(objectId); - ExHyperlink ctrl = new ExHyperlink(); ExHyperlinkAtom obj = ctrl.getExHyperlinkAtom(); - obj.setNumber(objectId); if(link.getType() == Hyperlink.LINK_SLIDENUMBER) { ctrl.setLinkURL(link.getAddress(), 0x30); } else { @@ -1068,9 +997,110 @@ ctrl.setLinkURL(link.getAddress()); } ctrl.setLinkTitle(link.getTitle()); - lst.addChildAfter(ctrl, objAtom); + + int objectId = addToObjListAtom(ctrl); link.setId(objectId); + obj.setNumber(objectId); return objectId; } + + protected int addToObjListAtom(RecordContainer exObj) { + ExObjList lst = (ExObjList) _documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); + if (lst == null) { + lst = new ExObjList(); + _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); + } + ExObjListAtom objAtom = lst.getExObjListAtom(); + // increment the object ID seed + int objectId = (int) objAtom.getObjectIDSeed() + 1; + objAtom.setObjectIDSeed(objectId); + + lst.addChildAfter(exObj, objAtom); + + return objectId; + } + + protected int addPersistentObject(PositionDependentRecord slideRecord) { + slideRecord.setLastOnDiskOffset(-1); + _hslfSlideShow.appendRootLevelRecord((Record)slideRecord); + _records = _hslfSlideShow.getRecords(); + + // 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(); + + // Add the new Slide into the PersistPtr stuff + int slideOffset = 0; + + // 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 (Record record : _records) { + if(record instanceof PositionDependentRecord) { + PositionDependentRecord pdr = (PositionDependentRecord)record; + int oldPos = pdr.getLastOnDiskOffset(); + int newPos = baos.size(); + pdr.setLastOnDiskOffset(newPos); + if (record == slideRecord) { + // set the slideOffset later, to not interfere with exisiting positions + slideOffset = newPos; + } else { + oldToNewPositions.put(Integer.valueOf(oldPos),Integer.valueOf(newPos)); + } + + } + + // Dummy write out, so the position winds on properly + try { + record.writeOut(baos); + } catch (IOException e) { + throw new HSLFException(e); + } + } + + PersistPtrHolder ptr = null; + UserEditAtom usr = 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 + for (Record record : _records) { + if (!(record instanceof PositionDependentRecord)) continue; + + // 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 + int recordType = (int)record.getRecordType(); + if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) { + ptr = (PersistPtrHolder)record; + } else if (recordType == RecordTypes.UserEditAtom.typeID) { + usr = (UserEditAtom)record; + } + } + + // persist ID is UserEditAtom.maxPersistWritten + 1 + int psrId = usr.getMaxPersistWritten() + 1; + + // Last view is now of the slide + usr.setLastViewType((short) UserEditAtom.LAST_VIEW_SLIDE_VIEW); + // increment the number of persistent objects + usr.setMaxPersistWritten(psrId); + _hslfSlideShow.getCurrentUserAtom().setCurrentEditOffset(usr.getLastOnDiskOffset()); + + // Add the new slide into the last PersistPtr + // (Also need to tell it where it is) + slideRecord.setLastOnDiskOffset(slideOffset); + ptr.addSlideLookup(psrId, slideOffset); + logger.log(POILogger.INFO, "New slide/object ended up at " + slideOffset); + + return psrId; + } } Index: src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java =================================================================== --- src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java (revision 1532876) +++ src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java (working copy) @@ -160,4 +160,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); + } } 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); + } + } +}