]> source.dussan.org Git - poi.git/commitdiff
Initial Powerpoint support, by Nick Burch
authorAvik Sengupta <avik@apache.org>
Sat, 28 May 2005 05:36:00 +0000 (05:36 +0000)
committerAvik Sengupta <avik@apache.org>
Sat, 28 May 2005 05:36:00 +0000 (05:36 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353701 13f79535-47bb-0310-9956-ffa450edef68

47 files changed:
src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/dev/PPDrawingTextListing.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/dev/SLWTTextListing.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/dev/SlideAndNotesAtomListing.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/dev/SlideShowRecordDumper.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/exceptions/InvalidRecordFormatException.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Notes.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/Slide.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/DummyRecordWithChildren.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/Notes.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/Record.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/RecordAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/Slide.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/util/MutableByteArrayOutputStream.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/TestReWriteSanity.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/TestRecordCounts.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/data/next_test_ppt_file.ppt [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/extractor/TextExtractor.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlidePersistAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextBytesAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextCharsAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSheetText.java [new file with mode: 0644]

diff --git a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
new file mode 100644 (file)
index 0000000..2701bb1
--- /dev/null
@@ -0,0 +1,347 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.util.*;
+import java.io.*;
+
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSDocument;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.MutablePropertySet;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+
+import org.apache.poi.util.LittleEndian;
+
+import org.apache.poi.hslf.record.*;
+
+/**
+ * This class contains the main functionality for the Powerpoint file 
+ * "reader". It is only a very basic class for now
+ *
+ * @author Nick Burch
+ */
+
+public class HSLFSlideShow
+{
+  private InputStream istream;
+  private POIFSFileSystem filesystem;
+
+  // Holds metadata on our document
+  private SummaryInformation sInf;
+  private DocumentSummaryInformation dsInf;
+  private CurrentUserAtom currentUser;
+
+  // Low level contents of the file
+  private byte[] _docstream;
+
+  // Low level contents
+  private Record[] _records;
+
+  /**
+   * 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.
+   */
+  public HSLFSlideShow(String fileName) throws IOException
+  {
+       this(new FileInputStream(fileName));
+  }
+  
+  /**
+   * 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.
+   */
+  public HSLFSlideShow(InputStream inputStream) throws IOException
+  {
+        //do Ole stuff
+               this(new POIFSFileSystem(inputStream));
+        istream = inputStream;
+  }
+
+  /**
+   * 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.
+   */
+  public HSLFSlideShow(POIFSFileSystem filesystem) throws IOException
+  {
+               this.filesystem = filesystem;
+
+        // Go find a PowerPoint document in the stream
+        // Save anything useful we come across
+        readFIB();
+
+               // Look for Property Streams:
+               readProperties();
+  }
+
+
+  /**
+   * Shuts things down. Closes underlying streams etc
+   *
+   * @throws IOException
+   */
+  public void close() throws IOException
+  {
+       if(istream != null) {
+               istream.close();
+       }
+       filesystem = null;
+  }
+
+
+  /**
+   * Extracts the main document stream from the POI file then hands off 
+   * to other functions that parse other areas.
+   *
+   * @throws IOException
+   */
+  private void readFIB() 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);
+
+       // 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)
+       //
+       // For now, this work is handled by Record.findChildRecords
+
+       _records = Record.findChildRecords(_docstream,0,_docstream.length);
+  }
+
+
+  /**
+   * Find the properties from the filesystem, and load them
+   */
+  public void readProperties() {
+       // DocumentSummaryInformation
+       dsInf = (DocumentSummaryInformation)getPropertySet("\005DocumentSummaryInformation");
+
+       // SummaryInformation
+       sInf = (SummaryInformation)getPropertySet("\005SummaryInformation");
+
+       // Current User
+       try {
+               currentUser = new CurrentUserAtom(filesystem);
+       } catch(IOException ie) {
+               System.err.println("Error finding Current User Atom:\n" + ie);
+               currentUser = new CurrentUserAtom();
+       }
+  }
+
+
+  /** 
+   * For a given named property entry, either return it or null if
+   *  if it wasn't found
+   */
+  public PropertySet getPropertySet(String setName) {
+       DocumentInputStream dis;
+       try {
+               // Find the entry, and get an input stream for it
+               dis = filesystem.createDocumentInputStream(setName);
+       } catch(IOException ie) {
+               // Oh well, doesn't exist
+               System.err.println("Error getting property set with name " + setName + "\n" + ie);
+               return null;
+       }
+
+       try {
+               // Create the Property Set
+               PropertySet set = PropertySetFactory.create(dis);
+               return set;
+       } catch(IOException ie) {
+               // Must be corrupt or something like that
+               System.err.println("Error creating property set with name " + setName + "\n" + ie);
+       } catch(org.apache.poi.hpsf.HPSFException he) {
+               // Oh well, doesn't exist
+               System.err.println("Error creating property set with name " + setName + "\n" + he);
+       }
+       return null;
+  }
+
+
+  /**
+   * 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
+   */
+   public void write(OutputStream out) throws IOException {
+       // Get a new Filesystem to write into
+       POIFSFileSystem outFS = new POIFSFileSystem();
+
+       // Write out the Property Streams
+       if(sInf != null) {
+               writePropertySet("\005SummaryInformation",sInf,outFS);
+       }
+       if(dsInf != null) {
+               writePropertySet("\005DocumentSummaryInformation",dsInf,outFS);
+       }
+
+       // Need to take special care of PersistPtrHolder and UserEditAtoms
+       // Store where they used to be, and where they are now
+       Hashtable persistPtrHolderPos = new Hashtable();
+       Hashtable userEditAtomsPos = new Hashtable();
+       int lastUserEditAtomPos = -1;
+
+       // Write ourselves out
+       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+       for(int i=0; i<_records.length; i++) {
+               // If it's a special record, record where it was and now is
+               if(_records[i] instanceof PersistPtrHolder) {
+                       // Update position
+                       PersistPtrHolder pph = (PersistPtrHolder)_records[i];
+                       int oldPos = pph.getLastOnDiskOffset();
+                       int newPos = baos.size();
+                       pph.setLastOnDiskOffet(newPos);
+                       persistPtrHolderPos.put(new Integer(oldPos),new Integer(newPos));
+               }
+               if(_records[i] instanceof UserEditAtom) {
+                       // Update position
+                       UserEditAtom uea = (UserEditAtom)_records[i];
+                       int oldPos = uea.getLastOnDiskOffset();
+                       int newPos = baos.size();
+                       lastUserEditAtomPos = newPos;
+                       uea.setLastOnDiskOffet(newPos);
+                       userEditAtomsPos.put(new Integer(oldPos),new Integer(newPos));
+
+                       // Update internal positions
+                       if(uea.getLastUserEditAtomOffset() != 0) {
+                               Integer ueNewPos = (Integer)userEditAtomsPos.get( new Integer( uea.getLastUserEditAtomOffset() ) );
+                               uea.setLastUserEditAtomOffset(ueNewPos.intValue());
+                       }
+                       if(uea.getPersistPointersOffset() != 0) {
+                               Integer ppNewPos = (Integer)persistPtrHolderPos.get( new Integer( uea.getPersistPointersOffset() ) );
+                               uea.setPersistPointersOffset(ppNewPos.intValue());
+                       }
+               }
+
+               // Finally, write out
+               _records[i].writeOut(baos);
+       }
+       ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+       outFS.createDocument(bais,"PowerPoint Document");
+
+       // Update and write out the Current User atom
+       if(lastUserEditAtomPos != -1) {
+               currentUser.setCurrentEditOffset(lastUserEditAtomPos);
+       }
+       currentUser.writeToFS(outFS);
+
+       // Send the POIFSFileSystem object out
+       outFS.writeFilesystem(out);
+   }
+
+
+  /**
+   * Writes out a given ProperySet
+   */
+  private void writePropertySet(String name, PropertySet set, POIFSFileSystem fs) throws IOException {
+       try {
+               MutablePropertySet mSet = new MutablePropertySet(set);
+               ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+               mSet.write(bOut);
+               byte[] data = bOut.toByteArray();
+               ByteArrayInputStream bIn = new ByteArrayInputStream(data);
+               fs.createDocument(bIn,name);
+               System.out.println("Wrote property set " + name + " of size " + data.length);
+       } catch(org.apache.poi.hpsf.WritingNotSupportedException wnse) {
+               System.err.println("Couldn't write property set with name " + name + " as not supported by HPSF yet");
+       }
+  }
+
+
+  /* ******************* fetching methods follow ********************* */
+
+
+  /**
+   * Returns an array of all the records found in the slideshow
+   */
+  public Record[] getRecords() { return _records; }
+
+  /**
+   * 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!
+   */
+  public byte[] getUnderlyingBytes() { return _docstream; }
+
+  /** 
+   * Fetch the Document Summary Information of the document
+   */
+  public DocumentSummaryInformation getDocumentSummaryInformation() { return dsInf; }
+
+  /** 
+   * Fetch the Summary Information of the document
+   */
+  public SummaryInformation getSummaryInformation() { return sInf; }
+
+ /**
+  * Fetch the Current User Atom of the document
+  */
+ public CurrentUserAtom getCurrentUserAtom() { return currentUser; }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/PPDrawingTextListing.java b/src/scratchpad/src/org/apache/poi/hslf/dev/PPDrawingTextListing.java
new file mode 100644 (file)
index 0000000..680e63e
--- /dev/null
@@ -0,0 +1,86 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.dev;
+
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.usermodel.*;
+
+import java.io.*;
+
+/**
+ * Uses record level code to locate PPDrawing entries.
+ * Having found them, it sees if they have DDF Textbox records, and if so,
+ *  searches those for text. Prints out any text it finds
+ */
+public class PPDrawingTextListing {
+       public static void main(String[] args) throws Exception {
+               if(args.length < 1) {
+                       System.err.println("Need to give a filename");
+                       System.exit(1);
+               }
+
+               HSLFSlideShow ss = new HSLFSlideShow(args[0]);
+
+               // Find PPDrawings at any second level position
+               Record[] records = ss.getRecords();
+               for(int i=0; i<records.length; i++) {
+                       Record[] children = records[i].getChildRecords();
+                       if(children != null && children.length != 0) {
+                               for(int j=0; j<children.length; j++) {
+                                       if(children[j] instanceof PPDrawing) {
+                                               System.out.println("Found PPDrawing at " + j + " in top level record " + i + " (" + records[i].getRecordType() + ")" );
+
+                                               // Look for EscherTextboxWrapper's
+                                               PPDrawing ppd = (PPDrawing)children[j];
+                                               EscherTextboxWrapper[] wrappers = ppd.getTextboxWrappers();
+                                               System.out.println("  Has " + wrappers.length + " textbox wrappers within");
+
+                                               // Loop over the wrappers, showing what they contain
+                                               for(int k=0; k<wrappers.length; k++) {
+                                                       EscherTextboxWrapper tbw = wrappers[k];
+                                                       System.out.println("    " + k + " has " + tbw.getChildRecords().length + " PPT atoms within");
+
+                                                       // Loop over the records, printing the text
+                                                       Record[] pptatoms = tbw.getChildRecords();
+                                                       for(int l=0; l<pptatoms.length; l++) {
+                                                               String text = null;
+                                                               if(pptatoms[l] instanceof TextBytesAtom) {
+                                                                       TextBytesAtom tba = (TextBytesAtom)pptatoms[l];
+                                                                       text = tba.getText();
+                                                               }
+                                                               if(pptatoms[l] instanceof TextCharsAtom) {
+                                                                       TextCharsAtom tca = (TextCharsAtom)pptatoms[l];
+                                                                       text = tca.getText();
+                                                               }
+
+                                                               if(text != null) {
+                                                                       text = text.replace('\r','\n');
+                                                                       System.out.println("        ''" + text + "''");
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/SLWTTextListing.java b/src/scratchpad/src/org/apache/poi/hslf/dev/SLWTTextListing.java
new file mode 100644 (file)
index 0000000..0b0800f
--- /dev/null
@@ -0,0 +1,91 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.dev;
+
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.usermodel.*;
+
+import java.io.*;
+
+/**
+ * Uses record level code to locate SlideListWithText entries.
+ * Having found them, it sees if they have any text, and prints out
+ *  what it finds.
+ */
+public class SLWTTextListing {
+       public static void main(String[] args) throws Exception {
+               if(args.length < 1) {
+                       System.err.println("Need to give a filename");
+                       System.exit(1);
+               }
+
+               HSLFSlideShow ss = new HSLFSlideShow(args[0]);
+
+               // Find the documents, and then their SLWT
+               Record[] records = ss.getRecords();
+               for(int i=0; i<records.length; i++) {
+                       if(records[i].getRecordType() == 1000l) {
+                               Record docRecord = records[i];
+                               Record[] docChildren = docRecord.getChildRecords();
+                               for(int j=0; j<docChildren.length; j++) {
+                                       if(docChildren[j] instanceof SlideListWithText) {
+                                               System.out.println("Found SLWT in document at " + i);
+                                               System.out.println("  Has " + docChildren[j].getChildRecords().length + " children");
+
+                                               // Grab the SlideAtomSet's, which contain 
+                                               //  a SlidePersistAtom and then a bunch of text
+                                               //  + related records
+                                               SlideListWithText slwt = (SlideListWithText)docChildren[j];
+                                               SlideListWithText.SlideAtomsSet[] thisSets = slwt.getSlideAtomsSets();
+                                               System.out.println("  Has " + thisSets.length + " AtomSets in it");
+
+                                               // Loop over the sets, showing what they contain
+                                               for(int k=0; k<thisSets.length; k++) {
+                                                       SlidePersistAtom spa = thisSets[k].getSlidePersistAtom();
+                                                       System.out.println("    " + k + " has slide id " + spa.getSlideIdentifier() );
+                                                       System.out.println("    " + k + " has ref id " + spa.getRefID() );
+
+                                                       // Loop over the records, printing the text
+                                                       Record[] slwtc = thisSets[k].getSlideRecords();
+                                                       for(int l=0; l<slwtc.length; l++) {
+                                                               String text = null;
+                                                               if(slwtc[l] instanceof TextBytesAtom) {
+                                                                       TextBytesAtom tba = (TextBytesAtom)slwtc[l];
+                                                                       text = tba.getText();
+                                                               }
+                                                               if(slwtc[l] instanceof TextCharsAtom) {
+                                                                       TextCharsAtom tca = (TextCharsAtom)slwtc[l];
+                                                                       text = tca.getText();
+                                                               }
+
+                                                               if(text != null) {
+                                                                       text = text.replace('\r','\n');
+                                                                       System.out.println("        ''" + text + "''");
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/SlideAndNotesAtomListing.java b/src/scratchpad/src/org/apache/poi/hslf/dev/SlideAndNotesAtomListing.java
new file mode 100644 (file)
index 0000000..dc23e08
--- /dev/null
@@ -0,0 +1,66 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.dev;
+
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.record.*;
+
+import java.io.*;
+
+/**
+ * Uses record level code to locate Notes and Slide records.
+ * Having found them, it asks their SlideAtom or NotesAtom entries
+ *  what they are all about. Useful for checking the matching between
+ *  Slides, Master Slides and Notes
+ */
+public class SlideAndNotesAtomListing {
+       public static void main(String[] args) throws Exception {
+               if(args.length < 1) {
+                       System.err.println("Need to give a filename");
+                       System.exit(1);
+               }
+
+               HSLFSlideShow ss = new HSLFSlideShow(args[0]);
+               System.out.println("");
+
+               // Find either Slides or Notes
+               Record[] records = ss.getRecords();
+               for(int i=0; i<records.length; i++) {
+                       Record r = records[i];
+
+                       // When we find them, print out their IDs
+                       if(r instanceof Slide) {
+                               Slide s = (Slide)r;
+                               SlideAtom sa = s.getSlideAtom();
+                               System.out.println("Found Slide at " + i);
+                               System.out.println("  Slide's master ID is " + sa.getMasterID());
+                               System.out.println("  Slide's notes ID is  " + sa.getNotesID());
+                               System.out.println("");
+                       }
+                       if(r instanceof Notes) {
+                               Notes n = (Notes)r;
+                               NotesAtom na = n.getNotesAtom();
+                               System.out.println("Found Notes at " + i);
+                               System.out.println("  Notes ID is " + na.getSlideID());
+                               System.out.println("");
+                       }
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/SlideShowRecordDumper.java b/src/scratchpad/src/org/apache/poi/hslf/dev/SlideShowRecordDumper.java
new file mode 100644 (file)
index 0000000..8902823
--- /dev/null
@@ -0,0 +1,167 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.dev;
+
+import java.util.*;
+import java.io.*;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.record.*;
+
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * This class provides a way to view the contents of a powerpoint file.
+ * It will use the recored layer to grok the contents of the file, and
+ *  will print out what it finds.
+ *
+ * @author Nick Burch
+ */
+
+public class SlideShowRecordDumper
+{
+  private HSLFSlideShow doc;
+
+  /**
+   *  right now this function takes one parameter: a ppt file, and outputs
+   *  a dump of what it contains
+   */
+  public static void main(String args[]) throws IOException
+  {
+       if(args.length == 0) {
+               System.err.println("Useage: SlideShowDumper <filename>");
+               return;
+       }
+
+       String filename = args[0];
+
+       SlideShowRecordDumper foo = new SlideShowRecordDumper(filename);
+
+       foo.printDump();
+       foo.close();
+  }
+
+
+  /**
+   * Constructs a Powerpoint dump from fileName. Parses the document 
+   * and dumps out the contents
+   *
+   * @param fileName The name of the file to read.
+   * @throws IOException if there is a problem while parsing the document.
+   */
+  public SlideShowRecordDumper(String fileName) throws IOException
+  {
+       doc = new HSLFSlideShow(fileName);
+  }
+
+  /**
+   * Shuts things down. Closes underlying streams etc
+   *
+   * @throws IOException
+   */
+  public void close() throws IOException
+  {
+       if(doc != null) {
+               doc.close();
+       }
+       doc = null;
+  }
+
+
+  public void printDump() throws IOException {
+       // Prints out the records in the tree
+       walkTree(0,0,doc.getRecords());
+  }
+
+  public String makeHex(int number, int padding) {
+       String hex = Integer.toHexString(number).toUpperCase();
+       while(hex.length() < padding) {
+               hex = "0" + hex;
+       }
+       return hex;
+  }
+
+  public String reverseHex(String s) {
+       StringBuffer ret = new StringBuffer();
+
+       // Get to a multiple of two
+       if((s.length() / 2) * 2 != s.length()) { s = "0" + s; }
+
+       // Break up into blocks
+       char[] c = s.toCharArray();
+       for(int i=c.length; i>0; i-=2) {
+               ret.append(c[i-2]);
+               ret.append(c[i-1]);
+               if(i != 2) { ret.append(' '); }
+       }
+       return ret.toString();
+  }
+
+  public int getDiskLen(Record r) throws IOException {
+       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+       r.writeOut(baos);
+       byte[] b = baos.toByteArray();
+       return b.length;
+  }
+
+
+  public void walkTree(int depth, int pos, Record[] records) throws IOException {
+       int indent = depth;
+       String ind = "";
+       for(int i=0; i<indent; i++) { ind += " "; }
+
+       for(int i=0; i<records.length; i++) {
+               Record r = records[i];
+
+               // Figure out how big it is
+               int len = getDiskLen(r);
+
+               // Grab the type as hex
+               String hexType = makeHex((int)r.getRecordType(),4);
+               String rHexType = reverseHex(hexType);
+
+               // Grab the hslf.record type
+               Class c = r.getClass();
+               String cname = c.toString();
+               if(cname.startsWith("class ")) {
+                       cname = cname.substring(6);
+               }
+               if(cname.startsWith("org.apache.poi.hslf.record.")) {
+                       cname = cname.substring(27);
+               }
+
+               // Display the record
+               System.out.println(ind + "At position " + pos + " (" + makeHex(pos,6) + "):");
+               System.out.println(ind + " Record is of type " + cname);
+               System.out.println(ind + " Type is " + r.getRecordType() + " (" + hexType + " -> " + rHexType + " )");
+               System.out.println(ind + " Len is " + (len-8) + " (" + makeHex((len-8),8) + "), on disk len is " + len );
+               System.out.println();
+
+               // If it has children, show them
+               if(r.getChildRecords() != null) {
+                       walkTree((depth+3),pos+8,r.getChildRecords());
+               }
+
+               // Wind on the position marker
+               pos += len;
+       }
+  }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java b/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java
new file mode 100644 (file)
index 0000000..5fff3fa
--- /dev/null
@@ -0,0 +1,95 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.dev;
+
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.record.*;
+
+import java.io.*;
+
+/**
+ * Uses record level code to locate UserEditAtom records, and other
+ *  persistence related atoms. Tries to match them together, to help
+ *  illuminate quite what all the offsets mean
+ */
+public class UserEditAndPersistListing {
+       public static void main(String[] args) throws Exception {
+               if(args.length < 1) {
+                       System.err.println("Need to give a filename");
+                       System.exit(1);
+               }
+
+               HSLFSlideShow ss = new HSLFSlideShow(args[0]);
+               System.out.println("");
+
+               // Find any persist ones first
+               Record[] records = ss.getRecords();
+               int pos = 0;
+               for(int i=0; i<records.length; i++) {
+                       Record r = records[i];
+
+                       if(r.getRecordType() == 6001l) {
+                               // PersistPtrFullBlock
+                               System.out.println("Found PersistPtrFullBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
+                       }
+                       if(r.getRecordType() == 6002l) {
+                               // PersistPtrIncrementalBlock
+                               System.out.println("Found PersistPtrIncrementalBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
+                       }
+
+                       // Increase the position by the on disk size
+                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                       r.writeOut(baos);
+                       pos += baos.size();
+               }
+
+               System.out.println("");
+
+               pos = 0;
+               // Now look for UserEditAtoms
+               for(int i=0; i<records.length; i++) {
+                       Record r = records[i];
+
+                       if(r instanceof UserEditAtom) {
+                               UserEditAtom uea = (UserEditAtom)r;
+                               System.out.println("Found UserEditAtom at " + pos + " (" + Integer.toHexString(pos) + ")");
+                               System.out.println("  lastUserEditAtomOffset = " + uea.getLastUserEditAtomOffset() );
+                               System.out.println("  persistPointersOffset  = " + uea.getPersistPointersOffset() );
+                               System.out.println("  docPersistRef          = " + uea.getDocPersistRef() );
+                               System.out.println("  maxPersistWritten      = " + uea.getMaxPersistWritten() );
+                       }
+
+                       // Increase the position by the on disk size
+                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                       r.writeOut(baos);
+                       pos += baos.size();
+               }
+
+               System.out.println("");
+
+
+               // Query the CurrentUserAtom
+               CurrentUserAtom cua = ss.getCurrentUserAtom();
+               System.out.println("Checking Current User Atom");
+               System.out.println("  Thinks the CurrentEditOffset is " + cua.getCurrentEditOffset());
+               
+               System.out.println("");
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/exceptions/InvalidRecordFormatException.java b/src/scratchpad/src/org/apache/poi/hslf/exceptions/InvalidRecordFormatException.java
new file mode 100644 (file)
index 0000000..6f195f0
--- /dev/null
@@ -0,0 +1,34 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.exceptions;
+
+/**
+ * This exception is thrown when we try to create a record, and the
+ *  underlying data just doesn't match up
+ *
+ * @author Nick Burch
+ */
+
+public class InvalidRecordFormatException extends Exception
+{
+       public InvalidRecordFormatException(String s) {
+               super(s);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java
new file mode 100644 (file)
index 0000000..9690fdd
--- /dev/null
@@ -0,0 +1,182 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.extractor;
+
+import java.io.*;
+import java.util.HashSet;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.usermodel.*;
+
+/**
+ * This class can be used to extract text from a PowerPoint file.
+ *  Can optionally also get the notes from one.
+ *
+ * @author Nick Burch
+ */
+
+public class PowerPointExtractor
+{
+  private HSLFSlideShow _hslfshow;
+  private SlideShow _show;
+  private Slide[] _slides;
+  private Notes[] _notes;
+
+  /**
+   * Basic extractor. Returns all the text, and optionally all the notes
+   */
+  public static void main(String args[]) throws IOException
+  {
+       if(args.length < 1) {
+               System.err.println("Useage:");
+               System.err.println("\tPowerPointExtractor [-notes] <file>");
+               System.exit(1);
+       }
+
+       boolean notes = false;
+       String file;
+       if(args.length > 1) {
+               notes = true;
+               file = args[1];
+       } else {
+               file = args[0];
+       }
+
+       PowerPointExtractor ppe = new PowerPointExtractor(file);
+       System.out.println(ppe.getText(true,notes));
+       ppe.close();
+  }
+
+  /**
+   * Creates a PowerPointExtractor
+   * @param fileName
+   */
+  public PowerPointExtractor(String fileName) throws IOException {
+       _hslfshow = new HSLFSlideShow(fileName);
+       _show = new SlideShow(_hslfshow);
+       _slides = _show.getSlides();
+       _notes = _show.getNotes();
+  }
+
+  /**
+   * Creates a PowerPointExtractor
+   * @param iStream
+   */
+  public PowerPointExtractor(InputStream iStream) throws IOException {
+       _hslfshow = new HSLFSlideShow(iStream);
+       _show = new SlideShow(_hslfshow);
+       _slides = _show.getSlides();
+       _notes = _show.getNotes();
+  }
+
+  /**
+   * Creates a PowerPointExtractor
+   * @param fs
+   */
+  public PowerPointExtractor(POIFSFileSystem fs) throws IOException {
+       _hslfshow = new HSLFSlideShow(fs);
+       _show = new SlideShow(_hslfshow);
+       _slides = _show.getSlides();
+       _notes = _show.getNotes();
+  }
+
+
+  /**
+   * Shuts down the underlying streams
+   */
+  public void close() throws IOException {
+       _hslfshow.close();
+       _hslfshow = null;
+       _show = null;
+       _slides = null;
+       _notes = null;
+  }
+
+
+  /**
+   * Fetches all the slide text from the slideshow, but not the notes
+   */
+  public String getText() {
+       return getText(true,false);
+  }
+
+  /**
+   * Fetches all the notes text from the slideshow, but not the slide text
+   */
+  public String getNotes() {
+       return getText(false,true);
+  }
+
+  /**
+   * Fetches text from the slideshow, be it slide text or note text
+   * @param getSlideText fetch slide text
+   * @param getNoteText fetch note text
+   */
+  public String getText(boolean getSlideText, boolean getNoteText) {
+       StringBuffer ret = new StringBuffer(); 
+
+       if(getSlideText) {
+               for(int i=0; i<_slides.length; i++) {
+                       Slide slide = _slides[i];
+                       TextRun[] runs = slide.getTextRuns();
+                       for(int j=0; j<runs.length; j++) {
+                               TextRun run = runs[j];
+                               String text = run.getText();
+                               ret.append(text);
+                               if(! text.endsWith("\n")) {
+                                       ret.append("\n");
+                               }
+                       }
+               }
+               if(getNoteText) {
+                       ret.append(" ");
+               }
+       }
+
+       if(getNoteText) {
+               // Not currently using _notes, as that can have the notes of
+               //  master sheets in. Grab Slide list, then work from there,
+               //  but ensure no duplicates
+               HashSet seenNotes = new HashSet();
+               for(int i=0; i<_slides.length; i++) {
+                       Notes notes = _slides[i].getNotesSheet();
+                       if(notes == null) { continue; }
+                       Integer id = new Integer(notes.getSheetNumber());
+                       if(seenNotes.contains(id)) { continue; }
+                       seenNotes.add(id);
+
+                       TextRun[] runs = notes.getTextRuns();
+                       if(runs != null && runs.length > 0) {
+                               for(int j=0; j<runs.length; j++) {
+                                       TextRun run = runs[j];
+                                       String text = run.getText();
+                                       ret.append(text);
+                                       if(! text.endsWith("\n")) {
+                                               ret.append("\n");
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return ret.toString();
+  }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java b/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java
new file mode 100644 (file)
index 0000000..ca1b467
--- /dev/null
@@ -0,0 +1,73 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.model;
+
+import java.util.*;
+
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.record.SlideListWithText.*;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * This class represents a slide's notes in a PowerPoint Document. It 
+ *  allows access to the text within, and the layout. For now, it only 
+ *  does the text side of things though
+ *
+ * @author Nick Burch
+ */
+
+public class Notes extends Sheet
+{
+
+  private int _sheetNo;
+  private org.apache.poi.hslf.record.Notes _notes;
+  private TextRun[] _runs;
+
+  /**
+   * Constructs a Notes Sheet from the given Notes record.
+   * Initialises TextRuns, to provide easier access to the text
+   *
+   * @param notes the Notes record to read from
+   */
+  public Notes (org.apache.poi.hslf.record.Notes notes) {
+       _notes = notes;
+
+       // Grab the sheet number, via the NotesAtom
+       _sheetNo = _notes.getNotesAtom().getSlideID();
+
+       // Now, build up TextRuns from pairs of TextHeaderAtom and
+       //  one of TextBytesAtom or TextCharsAtom, found inside 
+       //  EscherTextboxWrapper's in the PPDrawing
+       _runs = findTextRuns(_notes.getPPDrawing());
+  }
+
+
+  // Accesser methods follow
+
+  /**
+   * Returns an array of all the TextRuns found
+   */
+  public TextRun[] getTextRuns() { return _runs; }
+
+  /**
+   * Returns the sheet number
+   */
+  public int getSheetNumber() { return _sheetNo; }
+} 
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
new file mode 100644 (file)
index 0000000..5a7aa8a
--- /dev/null
@@ -0,0 +1,92 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.model;
+
+import org.apache.poi.hslf.record.*;
+import java.util.Vector;
+
+/**
+ * This class defines the common format of "Sheets" in a powerpoint
+ *  document. Such sheets could be Slides, Notes, Master etc
+ *
+ * @author Nick Burch
+ */
+
+public abstract class Sheet
+{
+  /**
+   * Returns an array of all the TextRuns in the sheet.
+   */
+  public abstract TextRun[] getTextRuns();
+
+  /**
+   * Returns the sheet number
+   */
+  public abstract int getSheetNumber();
+
+  /**
+   * For a given PPDrawing, grab all the TextRuns
+   */
+  public static TextRun[] findTextRuns(PPDrawing ppdrawing) {
+       Vector runsV = new Vector();
+       EscherTextboxWrapper[] wrappers = ppdrawing.getTextboxWrappers();
+       for(int i=0; i<wrappers.length; i++) {
+               findTextRuns(wrappers[i].getChildRecords(),runsV);
+       }
+    TextRun[] runs = new TextRun[runsV.size()];
+       for(int i=0; i<runs.length; i++) {
+               runs[i] = (TextRun)runsV.get(i);
+       }
+       return runs;
+  }
+
+  /**
+   * Scans through the supplied record array, looking for 
+   * a TextHeaderAtom followed by one of a TextBytesAtom or
+   * a TextCharsAtom. Builds up TextRuns from these
+   *
+   * @param records the records to build from
+   * @param found vector to add any found to
+   */
+  protected static void findTextRuns(Record[] records, Vector found) {
+       // Look for a TextHeaderAtom
+       for(int i=0; i<(records.length-1); i++) {
+               if(records[i] instanceof TextHeaderAtom) {
+                       TextRun trun = null;
+                       TextHeaderAtom tha = (TextHeaderAtom)records[i];
+                       if(records[i+1] instanceof TextCharsAtom) {
+                               TextCharsAtom tca = (TextCharsAtom)records[i+1];
+                               trun = new TextRun(tha,tca);
+                       } else if(records[i+1] instanceof TextBytesAtom) {
+                               TextBytesAtom tba = (TextBytesAtom)records[i+1];
+                               trun = new TextRun(tha,tba);
+                       } else if(records[i+1].getRecordType() == 4010l) {
+                               // Safe to ignore
+                       } else {
+                               System.err.println("Found a TextHeaderAtom not followed by a TextBytesAtom or TextCharsAtom: Followed by " + records[i+1].getRecordType());
+                               continue;
+                       }
+                       found.add(trun);
+                       i++;
+               }
+       }
+  }
+
+} 
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java
new file mode 100644 (file)
index 0000000..0cedae0
--- /dev/null
@@ -0,0 +1,118 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.model;
+
+import java.util.*;
+
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.record.SlideListWithText.*;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * This class represents a slide in a PowerPoint Document. It allows 
+ *  access to the text within, and the layout. For now, it only does
+ *  the text side of things though
+ *
+ * @author Nick Burch
+ */
+
+public class Slide extends Sheet
+{
+
+  private int _sheetNo;
+  private org.apache.poi.hslf.record.Slide _slide;
+  private SlideAtomsSet[] _atomSet;
+  private TextRun[] _runs;
+  private TextRun[] _otherRuns; // Any from the PPDrawing, shouldn't really be any though
+  private Notes _notes;
+
+  /**
+   * Constructs a Slide from the Slide record, and the SlideAtomsSets
+   *  for ones not embeded in the PPDrawing.
+   * Initialises TextRuns, to provide easier access to the text
+   *
+   * @param slide the Slide record we're based on
+   * @param atomSet the SlideAtomsSet to get the text from
+   */
+  public Slide(org.apache.poi.hslf.record.Slide slide, Notes notes, SlideAtomsSet[] atomSet) {
+       _slide = slide;
+       _notes = notes;
+       _atomSet = atomSet;
+
+       // Grab the sheet number
+       //_sheetNo = _slide.getSlideAtom().getSheetNumber();
+       _sheetNo = -1;
+
+       // Grab the TextRuns from the PPDrawing
+       _otherRuns = findTextRuns(_slide.getPPDrawing());
+
+
+       // Ensure we've only got only copy of each SlideAtomSet
+       // When in doubt, prefere the later one
+       Hashtable seenSets = new Hashtable();
+       Vector useSets = new Vector();
+       for(int i=0; i<_atomSet.length; i++) {
+               SlideAtomsSet set = _atomSet[i];
+               int id = set.getSlidePersistAtom().getRefID();
+               Integer idI = new Integer(id);
+               if(seenSets.containsKey(idI)) {
+                       // Replace old one
+                       Integer replacePos = (Integer)seenSets.get(idI);
+                       useSets.set(replacePos.intValue(),set);
+               } else {
+                       // Use for now
+                       useSets.add(set);
+                       seenSets.put(idI,new Integer(useSets.size()-1));
+               }
+       }
+
+       // For the text coming in from the SlideAtomsSet:
+       // Build up TextRuns from pairs of TextHeaderAtom and
+       //  one of TextBytesAtom or TextCharsAtom
+       Vector runSets = new Vector();
+       for(int i=0; i<useSets.size(); i++) {
+               SlideAtomsSet set = (SlideAtomsSet)useSets.get(i);
+               findTextRuns(set.getSlideRecords(),runSets);
+       }
+       // Build an array, more useful than a vector
+       _runs = new TextRun[runSets.size()];
+       for(int i=0; i<_runs.length; i++) {
+               _runs[i] = (TextRun)runSets.get(i);
+       }
+  }
+
+
+  // Accesser methods follow
+
+  /**
+   * Returns an array of all the TextRuns found
+   */
+  public TextRun[] getTextRuns() { return _runs; }
+
+  /**
+   * Returns the sheet number
+   */
+  public int getSheetNumber() { return _sheetNo; }
+
+  /**
+   * Returns the Notes Sheet for this slide, or null if there isn't one
+   */
+  public Notes getNotesSheet() { return _notes; }
+} 
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
new file mode 100644 (file)
index 0000000..fd010f8
--- /dev/null
@@ -0,0 +1,141 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.model;
+
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * This class represents a run of text in a powerpoint document. That
+ *  run could be text on a sheet, or text in a note.
+ *  It is only a very basic class for now
+ *
+ * @author Nick Burch
+ */
+
+public class TextRun
+{
+       private TextHeaderAtom _headerAtom;
+       private TextBytesAtom  _byteAtom;
+       private TextCharsAtom  _charAtom;
+       private boolean _isUnicode;
+
+       /**
+       * Constructs a Text Run from a Unicode text block
+       *
+       * @param tha the TextHeaderAtom that defines what's what
+       * @param tca the TextCharsAtom containing the text
+       */
+       public TextRun(TextHeaderAtom tha, TextCharsAtom tca) {
+               _headerAtom = tha;
+               _charAtom = tca;
+               _isUnicode = true;
+       }
+
+       /**
+       * Constructs a Text Run from a Ascii text block
+       *
+       * @param tha the TextHeaderAtom that defines what's what
+       * @param tba the TextBytesAtom containing the text
+       */
+       public TextRun(TextHeaderAtom tha, TextBytesAtom tba) {
+               _headerAtom = tha;
+               _byteAtom = tba;
+               _isUnicode = false;
+       }
+
+
+       // Accesser methods follow
+
+       /**
+        * Returns the text content of the run, which has been made safe
+        * for printing and other use.
+        */
+       public String getText() {
+               String rawText = getRawText();
+
+               // PowerPoint seems to store files with \r as the line break
+               // The messes things up on everything but a Mac, so translate
+               //  them to \n
+               String text = rawText.replace('\r','\n');
+               return text;
+       }
+
+       /**
+       * Returns the raw text content of the run. This hasn't had any
+       *  changes applied to it, and so is probably unlikely to print
+       *  out nicely.
+       */
+       public String getRawText() {
+               if(_isUnicode) {
+                       return _charAtom.getText();
+               } else {
+                       return _byteAtom.getText();
+               }
+       }
+
+       /**
+        * Changes the text. Chance are, this won't work just yet, because
+        *  we also need to update some other bits of the powerpoint file
+        *  to match the change in the Text Atom, especially byte offsets
+        */
+       public void setText(String s) {
+               // If size changed, warn
+               if(s.length() != getText().length()) {
+                       System.err.println("Warning: Your powerpoint file is probably no longer readable by powerpoint, as the text run has changed size!");
+               }
+
+               if(_isUnicode) {
+                       // The atom can safely convert to unicode
+                       _charAtom.setText(s);
+               } else {
+                       // Will it fit in a 8 bit atom?
+                       boolean hasMultibyte = StringUtil.hasMultibyte(s);
+                       if(! hasMultibyte) {
+                               // Fine to go into 8 bit atom
+                               byte[] text = new byte[s.length()];
+                               StringUtil.putCompressedUnicode(s,text,0);
+                               _byteAtom.setText(text);
+                       } else {
+                               throw new RuntimeException("Setting of unicode text is currently only possible for Text Runs that are Unicode in the file, sorry. For now, please convert that text to us-ascii and re-try it");
+                       }
+               }
+               
+       }
+
+       /**
+       * Returns the type of the text, from the TextHeaderAtom.
+       * Possible values can be seen from TextHeaderAtom
+       * @see org.apache.poi.hslf.record.TextHeaderAtom
+       */
+       public int getRunType() { 
+               return _headerAtom.getTextType();
+       }
+
+       /**
+       * Changes the type of the text. Values should be taken
+       *  from TextHeaderAtom. No checking is done to ensure you
+       *  set this to a valid value!
+       * @see org.apache.poi.hslf.record.TextHeaderAtom
+       */
+       public void setRunType(int type) {
+               _headerAtom.setTextType(type);
+       }
+} 
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
new file mode 100644 (file)
index 0000000..2fa6803
--- /dev/null
@@ -0,0 +1,218 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import java.io.*;
+import org.apache.poi.poifs.filesystem.*;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+
+/**
+ * This is a special kind of Atom, becauase it doesn't live inside the
+ *  PowerPoint document. Instead, it lives in a seperate stream in the
+ *  document. As such, it has to be treaded specially
+ *
+ * @author Nick Burch
+ */
+
+public class CurrentUserAtom
+{
+       /** Standard Atom header */
+       public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
+       /** The Powerpoint magic numer */
+       public static final byte[] magicNumber = new byte[] { 95, -64, -111, -29 };
+       /** The Powerpoint 97 version, major and minor numbers */
+       public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
+
+       /** The version, major and minor numbers */
+       private int docFinalVersionA;
+       private int docFinalVersionB;
+       private byte docMajorNo;
+       private byte docMinorNo;
+
+       /** The Offset into the file for the current edit */
+    private long currentEditOffset;
+       /** The Username of the last person to edit the file */
+       private String lastEditUser;
+       /** The document release version */
+       private long releaseVersion;
+
+       /** Only correct after reading in or writing out */
+       private byte[] _contents;
+
+
+       /* ********************* getter/setter follows *********************** */
+
+       public int  getDocFinalVersionA() { return docFinalVersionA; }
+       public int  getDocFinalVersionB() { return docFinalVersionB; }
+       public byte getDocMajorNo()       { return docMajorNo; }
+       public byte getDocMinorNo()       { return docMinorNo; }
+
+       public long getReleaseVersion()  { return releaseVersion; }
+       public void setReleaseVersion(long rv) { releaseVersion = rv; }
+
+       /** Points to the UserEditAtom */
+       public long getCurrentEditOffset() { return currentEditOffset; }
+       public void setCurrentEditOffset(long id ) { currentEditOffset = id; }
+
+       public String getLastEditUsername() { return lastEditUser; }
+       public void setLastEditUsername(String u) { lastEditUser = u; }
+
+
+       /* ********************* real code follows *************************** */
+
+       /**
+        * Create a new Current User Atom
+        */
+       public CurrentUserAtom() {
+               _contents = new byte[0];
+               throw new RuntimeException("Creation support for Current User Atom not complete");
+       }
+
+       /** 
+        * Find the Current User in the filesystem, and create from that
+        */
+       public CurrentUserAtom(POIFSFileSystem fs) throws IOException {
+               // Decide how big it is
+               DocumentEntry docProps =
+                       (DocumentEntry)fs.getRoot().getEntry("Current User");
+               _contents = new byte[docProps.getSize()];
+
+               // Grab the contents
+               InputStream in = fs.createDocumentInputStream("Current User");
+               in.read(_contents);
+
+               // Set everything up
+               init();
+       }
+
+       /** 
+        * Create things from the bytes
+        */
+       public CurrentUserAtom(byte[] b) {
+               _contents = b;
+               init();
+       }
+
+       /**
+        * Actually do the creation from a block of bytes
+        */
+       private void init() {
+               // Grab the edit offset
+               currentEditOffset = LittleEndian.getUInt(_contents,16);
+
+               // Grab the versions
+               docFinalVersionA = LittleEndian.getUShort(_contents,20);
+               docFinalVersionB = LittleEndian.getUShort(_contents,22);
+               docMajorNo = _contents[24];
+               docMinorNo = _contents[25];
+
+               // Get the username length
+               long usernameLen = LittleEndian.getUShort(_contents,20);
+
+               // Use this to grab the revision
+               releaseVersion = LittleEndian.getUInt(_contents,28+(int)usernameLen);
+
+               // Grab the unicode username, if stored
+               int start = 28+(int)usernameLen+4;
+               int len = 2*(int)usernameLen;
+
+               if(_contents.length >= start+len) {
+                       byte[] textBytes = new byte[len];
+                       System.arraycopy(_contents,start,textBytes,0,len);
+                       lastEditUser = StringUtil.getFromUnicodeLE(textBytes);
+               } else {
+                       // Fake from the 8 bit version
+                       byte[] textBytes = new byte[(int)usernameLen];
+                       System.arraycopy(_contents,28,textBytes,0,(int)usernameLen);
+                       lastEditUser = StringUtil.getFromCompressedUnicode(textBytes,0,(int)usernameLen);
+               }
+       }
+
+
+       /**
+        * Writes ourselves back out
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Decide on the size
+               //  8 = atom header
+               //  20 = up to name
+               //  4 = revision
+               //  3 * len = ascii + unicode
+               int size = 8 + 20 + 4 + (3 * lastEditUser.length());
+               _contents = new byte[size];
+
+               // First we have a 8 byte atom header
+               System.arraycopy(atomHeader,0,_contents,0,4);   
+               // Size is 20+user len + revision len(4)
+               int atomSize = 20+4+lastEditUser.length();
+               LittleEndian.putInt(_contents,4,atomSize);
+
+               // Now we have the size of the details, which is 20
+               LittleEndian.putInt(_contents,8,20);
+
+               // Now the ppt magic number (4 bytes)
+               System.arraycopy(magicNumber,0,_contents,12,4);
+
+               // Now the current edit offset
+               LittleEndian.putInt(_contents,16,(int)currentEditOffset);
+
+               // Now the file versions, 2+2+1+1
+               LittleEndian.putShort(_contents,20,(short)docFinalVersionA);
+               LittleEndian.putShort(_contents,22,(short)docFinalVersionB);
+               _contents[24] = docMajorNo;
+               _contents[25] = docMinorNo;
+
+               // 2 bytes blank
+               _contents[26] = 0;
+               _contents[27] = 0;
+
+               // username in bytes in us ascii
+               byte[] asciiUN = new byte[lastEditUser.length()];
+               StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
+               System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
+
+               // 4 byte release version
+               LittleEndian.putInt(_contents,28+asciiUN.length,(int)releaseVersion);
+
+               // username in unicode
+               byte [] ucUN = new byte[lastEditUser.length()*2];
+               StringUtil.putUnicodeLE(lastEditUser,ucUN,0);
+               System.arraycopy(ucUN,0,_contents,28+asciiUN.length+4,ucUN.length);
+
+               // Write out
+               out.write(_contents);
+       }
+
+       /**
+        * Writes ourselves back out to a filesystem
+        */
+       public void writeToFS(POIFSFileSystem fs) throws IOException {
+               // Grab contents
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               writeOut(baos);
+               ByteArrayInputStream bais = 
+                       new ByteArrayInputStream(baos.toByteArray());
+
+               // Write out
+               fs.createDocument(bais,"Current User");
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/DummyRecordWithChildren.java b/src/scratchpad/src/org/apache/poi/hslf/record/DummyRecordWithChildren.java
new file mode 100644 (file)
index 0000000..5a30fae
--- /dev/null
@@ -0,0 +1,70 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * If we come across a record we know has children of (potential)
+ *  interest, but where the record itself is boring, we create one
+ *  of these. It allows us to get at the children, but not much else
+ *
+ * @author Nick Burch
+ */
+
+public class DummyRecordWithChildren extends RecordContainer
+{
+       private Record[] _children;
+       private byte[] _header;
+       private long _type;
+
+       /** 
+        * Create a new holder for a boring record with children
+        */
+       protected DummyRecordWithChildren(byte[] source, int start, int len) {
+               // Just grab the header, not the whole contents
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+               _type = LittleEndian.getUShort(_header,2);
+
+               // Find our children
+               _children = Record.findChildRecords(source,start+8,len-8);
+       }
+
+       /**
+        * Return the value we were given at creation
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * Return any children 
+        */
+       public Record[] getChildRecords() { return _children; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               writeOut(_header[0],_header[1],_type,_children,out);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java
new file mode 100644 (file)
index 0000000..04d388d
--- /dev/null
@@ -0,0 +1,90 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.ddf.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * A wrapper around a DDF (Escher) EscherTextbox Record. Causes the DDF 
+ *  Record to be accessible as if it were a HSLF record.
+ * Note: when asked to write out, will simply put any child records correctly
+ *  into the Escher layer. A call to the escher layer to write out (by the
+ *  parent PPDrawing) will do the actual write out
+ *
+ * @author Nick Burch
+ */
+
+public class EscherTextboxWrapper extends RecordContainer
+{
+       private EscherTextboxRecord _escherRecord;
+       private Record[] _children;
+       private long _type;
+
+       /**
+        * Returns the underlying DDF Escher Record
+        */
+       public EscherTextboxRecord getEscherRecord() { return _escherRecord; }
+
+       /** 
+        * Creates the wrapper for the given DDF Escher Record and children
+        */
+       protected EscherTextboxWrapper(EscherTextboxRecord textbox) {
+               _escherRecord = textbox;
+               _type = (long)_escherRecord.getRecordId();
+
+               // Find the child records in the escher data
+               byte[] data = _escherRecord.getData();
+               _children = Record.findChildRecords(data,0,data.length);
+       }
+
+
+       /**
+        * Return the type of the escher record (normally in the 0xFnnn range)
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * Return any children 
+        */
+       public Record[] getChildRecords() { return _children; }
+
+       /**
+        * Stores the data for the child records back into the Escher layer.
+        * Doesn't actually do the writing out, that's left to the Escher
+        *  layer to do. Must be called before writeOut/serialize is called
+        *  on the underlying Escher object!
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Write out our children, and stuff them into the Escher layer
+
+               // Grab the children's data
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               for(int i=0; i<_children.length; i++) {
+                       _children[i].writeOut(baos);
+               }
+               byte[] data = baos.toByteArray();
+
+               // Save in the escher layer
+               _escherRecord.setData(data);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Notes.java b/src/scratchpad/src/org/apache/poi/hslf/record/Notes.java
new file mode 100644 (file)
index 0000000..86e692b
--- /dev/null
@@ -0,0 +1,95 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Master container for Notes. There is one of these for every page of
+ *  notes, and they have certain specific children
+ *
+ * @author Nick Burch
+ */
+
+public class Notes extends RecordContainer
+{
+       private Record[] _children;
+       private byte[] _header;
+       private static long _type = 1008l;
+
+       // Links to our more interesting children
+       private NotesAtom notesAtom;
+       private PPDrawing ppDrawing;
+
+       /**
+        * Returns the NotesAtom of this Notes
+        */
+       public NotesAtom getNotesAtom() { return notesAtom; }
+       /**
+        * Returns the PPDrawing of this Notes, which has all the 
+        *  interesting data in it
+        */
+       public PPDrawing getPPDrawing() { return ppDrawing; }
+
+
+       /** 
+        * Set things up, and find our more interesting children
+        */
+       protected Notes(byte[] source, int start, int len) {
+               // Grab the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Find our children
+               _children = Record.findChildRecords(source,start+8,len-8);
+
+               // Find the interesting ones in there
+               for(int i=0; i<_children.length; i++) {
+                       if(_children[i] instanceof NotesAtom) {
+                               notesAtom = (NotesAtom)_children[i];
+                               //System.out.println("Found notes for sheet " + notesAtom.getSlideID());
+                       }
+                       if(_children[i] instanceof PPDrawing) {
+                               ppDrawing = (PPDrawing)_children[i];
+                       }
+               }
+       }
+
+
+       /**
+        * We are of type 1008
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * Return any children 
+        */
+       public Record[] getChildRecords() { return _children; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               writeOut(_header[0],_header[1],_type,_children,out);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java
new file mode 100644 (file)
index 0000000..dba5625
--- /dev/null
@@ -0,0 +1,120 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A Notes Atom (type 1009). Holds information on the parent Notes, such
+ *  as what slide it is tied to
+ *
+ * @author Nick Burch
+ */
+
+public class NotesAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 1009l;
+
+       private int slideID;
+       private boolean followMasterObjects;
+       private boolean followMasterScheme;
+       private boolean followMasterBackground;
+       private byte[] reserved;
+
+
+       public int getSlideID() { return slideID; }
+       public void setSlideID(int id) { slideID = id; }
+
+       public boolean getFollowMasterObjects()    { return followMasterObjects; }
+       public boolean getFollowMasterScheme()     { return followMasterScheme; }
+       public boolean getFollowMasterBackground() { return followMasterBackground; }
+       public void setFollowMasterObjects(boolean flag)    { followMasterObjects = flag; }
+       public void setFollowMasterScheme(boolean flag)     { followMasterScheme = flag; }
+       public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
+
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the Notes Atom
+        */
+       protected NotesAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 8) { len = 8; }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Get the slide ID
+               slideID = (int)LittleEndian.getInt(source,start+8);
+
+               // Grok the flags, stored as bits
+               int flags = LittleEndian.getUShort(source,start+12);
+               if((flags&4) == 4) {
+                       followMasterBackground = true;
+               } else {
+                       followMasterBackground = false;
+               }
+               if((flags&2) == 2) {
+                       followMasterScheme = true;
+               } else {
+                       followMasterScheme = false;
+               }
+               if((flags&1) == 1) {
+                       followMasterObjects = true;
+               } else {
+                       followMasterObjects = false;
+               }
+
+               // There might be 2 more bytes, which are a reserved field
+               reserved = new byte[len-14];
+               System.arraycopy(source,start+14,reserved,0,reserved.length);
+       }
+
+       /**
+        * We are of type 1009
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header
+               out.write(_header);
+
+               // Slide ID
+               writeLittleEndian(slideID,out);
+
+               // Flags
+               short flags = 0;
+               if(followMasterObjects)    { flags += 1; }
+               if(followMasterScheme)     { flags += 2; }
+               if(followMasterBackground) { flags += 4; }
+               writeLittleEndian(flags,out);
+
+               // Reserved fields
+               out.write(reserved);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java
new file mode 100644 (file)
index 0000000..e19bc0a
--- /dev/null
@@ -0,0 +1,191 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+
+import org.apache.poi.ddf.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * These are actually wrappers onto Escher drawings. Make use of
+ *  the DDF classes to do useful things with them.
+ * For now, creates a tree of the Escher records, and then creates any
+ *  PowerPoint (hslf) records found within the EscherTextboxRecord 
+ *  (msofbtClientTextbox) records.
+ * Also provides easy access to the EscherTextboxRecords, so that their
+ *  text may be extracted and used in Sheets
+ *
+ * @author Nick Burch
+ */
+
+// For now, pretending to be an atom. Might not always be, but that
+//  would require a wrapping class
+public class PPDrawing extends RecordAtom
+{
+       private byte[] _header;
+       private long _type;
+
+       private EscherRecord[] childRecords;
+       private EscherTextboxWrapper[] textboxWrappers;
+
+
+       /**
+        * Get access to the underlying Escher Records
+        */
+       public EscherRecord[] getEscherRecords() { return childRecords; }
+
+       /**
+        * Get access to the atoms inside Textboxes
+        */
+       public EscherTextboxWrapper[] getTextboxWrappers() { return textboxWrappers; }
+
+
+       /* ******************** record stuff follows ********************** */
+
+       /** 
+        * Sets everything up, groks the escher etc
+        */
+       protected PPDrawing(byte[] source, int start, int len) {
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Get the type
+               _type = LittleEndian.getUShort(_header,2);
+
+               // Get the contents for now
+               byte[] contents = new byte[len];
+               System.arraycopy(source,start,contents,0,len);
+
+
+               // Build up a tree of Escher records contained within
+               DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();
+               Vector escherChildren = new Vector();
+               findEscherChildren(erf,contents,8,len-8,escherChildren);
+
+               childRecords = new EscherRecord[escherChildren.size()];
+               for(int i=0; i<childRecords.length; i++) {
+                       childRecords[i] = (EscherRecord)escherChildren.get(i);
+               }
+
+               // Find and EscherTextboxRecord's, and wrap them up
+               Vector textboxes = new Vector();
+               findEscherTextboxRecord(childRecords, textboxes);
+               textboxWrappers = new EscherTextboxWrapper[textboxes.size()];
+               for(int i=0; i<textboxWrappers.length; i++) {
+                       textboxWrappers[i] = (EscherTextboxWrapper)textboxes.get(i);
+               }
+       }
+
+       /** 
+        * Tree walking way of finding Escher Child Records
+        */
+       private void findEscherChildren(DefaultEscherRecordFactory erf, byte[] source, int startPos, int lenToGo, Vector found) {
+               // Find the record
+               EscherRecord r = erf.createRecord(source,startPos);
+               // Fill it in
+               r.fillFields( source, startPos, erf );
+               // Save it
+               found.add(r);
+
+               // Wind on
+               int size = r.getRecordSize();
+               if(size < 8) {
+                       System.err.println("Hit short DDF record at " + startPos + " - " + size);
+               }
+               startPos += size;
+               lenToGo -= size;
+               if(lenToGo >= 8) {
+                       findEscherChildren(erf, source, startPos, lenToGo, found);
+               }
+       }
+
+       /** 
+        * Look for EscherTextboxRecords
+        */
+       private void findEscherTextboxRecord(EscherRecord[] toSearch, Vector found) {
+               for(int i=0; i<toSearch.length; i++) {
+                       if(toSearch[i] instanceof EscherTextboxRecord) {
+                               EscherTextboxRecord tbr = (EscherTextboxRecord)toSearch[i];
+                               EscherTextboxWrapper w = new EscherTextboxWrapper(tbr);
+                               found.add(w);
+                       } else {
+                               // If it has children, walk them
+                               if(toSearch[i].isContainerRecord()) {
+                                       List childrenL = toSearch[i].getChildRecords();
+                                       EscherRecord[] children = new EscherRecord[childrenL.size()];
+                                       for(int j=0; j< children.length; j++) {
+                                               children[j] = (EscherRecord)childrenL.get(j);
+                                       }
+                                       findEscherTextboxRecord(children,found);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * We are type 1036
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * We're pretending to be an atom, so return null
+        */
+       public Record[] getChildRecords() { return null; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        * Walks the escher layer to get the contents
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Ensure the escher layer reflects the text changes
+               for(int i=0; i<textboxWrappers.length; i++) {
+                       textboxWrappers[i].writeOut(null);
+               }
+
+               // Find the new size of the escher children;
+               int newSize = 0;
+               for(int i=0; i<childRecords.length; i++) {
+                       newSize += childRecords[i].getRecordSize();
+               }
+
+               // Update the size (header bytes 5-8)
+               LittleEndian.putInt(_header,4,newSize);
+
+               // Write out our header
+               out.write(_header);
+
+               // Now grab the children's data
+               byte[] b = new byte[newSize];
+               int done = 0;
+               for(int i=0; i<childRecords.length; i++) {
+                       int written = childRecords[i].serialize( done, b );
+                       done += written;
+               }
+
+               // Finally, write out the children
+               out.write(b);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java b/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java
new file mode 100644 (file)
index 0000000..56390ad
--- /dev/null
@@ -0,0 +1,67 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * General holder for PersistPtrFullBlock and PersistPtrIncrementalBlock
+ *  records. We need to handle them specially, since we have to go around
+ *  updating UserEditAtoms if they shuffle about on disk
+ *
+ * @author Nick Burch
+ */
+
+public class PersistPtrHolder extends PositionDependentRecordAtom
+{
+       private byte[] _contents;
+       private long _type;
+
+       /** 
+        * Create a new holder for a PersistPtr record
+        */
+       protected PersistPtrHolder(byte[] source, int start, int len) {
+               // Sanity Checking - including whole header, so treat
+               //  length as based of 0, not 8 (including header size based)
+               if(len < 4) { len = 4; }
+
+               // Store where we are found on disk
+               myLastOnDiskOffset = start;
+
+               // Treat as an atom, grab and hold everything
+               _contents = new byte[len];
+               System.arraycopy(source,start,_contents,0,len);
+               _type = LittleEndian.getUShort(_contents,2);
+       }
+
+       /**
+        * Return the value we were given at creation
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               out.write(_contents);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/PositionDependentRecordAtom.java
new file mode 100644 (file)
index 0000000..e47e5b7
--- /dev/null
@@ -0,0 +1,44 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+/**
+ * A special (and dangerous) kind of Record Atom that cares about where
+ *  it lives on the disk, or who has other Atoms that care about where
+ *  this is on the disk.
+ *
+ * @author Nick Burch
+ */
+
+public abstract class PositionDependentRecordAtom extends RecordAtom
+{
+       /** Our location on the disk, as of the last write out */
+       protected int myLastOnDiskOffset;
+
+       /** Fetch our location on the disk, as of the last write out */
+       public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
+
+       /** 
+        * Update the Record's idea of where on disk it lives, after a write out.
+        * Use with care...
+        */
+       public void setLastOnDiskOffet(int offset) { 
+               myLastOnDiskOffset = offset;
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Record.java b/src/scratchpad/src/org/apache/poi/hslf/record/Record.java
new file mode 100644 (file)
index 0000000..ea7a3e7
--- /dev/null
@@ -0,0 +1,192 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Vector;
+import org.apache.poi.util.LittleEndian;
+
+
+/**
+ * This abstract class represents a record in the PowerPoint document.
+ * Record classes should extend with RecordContainer or RecordAtom, which
+ *  extend this in turn.
+ *
+ * @author Nick Burch
+ */
+
+public abstract class Record
+{
+       /** 
+        * Is this record type an Atom record (only has data),
+        *  or is it a non-Atom record (has other records)?
+        */
+       public abstract boolean isAnAtom();
+
+       /**
+        * Returns the type (held as a little endian in bytes 3 and 4)
+        *  that this class handles
+        */
+       public abstract long getRecordType();
+
+       /** 
+        * Fetch all the child records of this record
+        * If this record is an atom, will return null
+        * If this record is a non-atom, but has no children, will return 
+        *  an empty array
+        */
+       public abstract Record[] getChildRecords();
+
+       /**
+        * Have the contents printer out into an OutputStream, used when
+        *  writing a file back out to disk
+        * (Normally, atom classes will keep their bytes around, but
+        *  non atom classes will just request the bytes from their 
+        *  children, then chuck on their header and return)
+        */
+       public abstract void writeOut(OutputStream o) throws IOException;
+
+       /**
+        * When writing out, write out a signed int (32bit) in Little Endian format
+        */
+       public static void writeLittleEndian(int i,OutputStream o) throws IOException {
+               byte[] bi = new byte[4];
+               LittleEndian.putInt(bi,i);
+               o.write(bi);
+       }
+       /**
+        * When writing out, write out a signed short (16bit) in Little Endian format
+        */
+       public static void writeLittleEndian(short s,OutputStream o) throws IOException {
+               byte[] bs = new byte[2];
+               LittleEndian.putShort(bs,s);
+               o.write(bs);
+       }
+
+       /**
+        * Default method for finding child records of a given record
+        */
+       public static Record[] findChildRecords(byte[] b, int start, int len) {
+               Vector children = new Vector(5);
+
+               // Jump our little way along, creating records as we go
+               int pos = start;
+               while(pos <= (start+len-8)) {
+                       long type = LittleEndian.getUShort(b,pos+2);
+                       long rlen = LittleEndian.getUInt(b,pos+4);
+
+                       // Sanity check the length
+                       int rleni = (int)rlen;
+                       if(rleni < 0) { rleni = 0; }
+
+//System.out.println("Found a " + type + " at pos " + pos + " (" + Integer.toHexString(pos) + "), len " + rlen);
+                       Record r = createRecordForType(type,b,pos,8+rleni);
+                       children.add(r);
+                       pos += 8;
+                       pos += rlen;
+               }
+
+               // Turn the vector into an array, and return
+               Record[] cRecords = new Record[children.size()];
+               for(int i=0; i < children.size(); i++) {
+                       cRecords[i] = (Record)children.get(i);
+               }
+               return cRecords;
+       }
+
+       /**
+        * For a given type (little endian bytes 3 and 4 in record header),
+        *  byte array, start position and length:
+        *  will return a Record object that will handle that record
+        *
+        * Remember that while PPT stores the record lengths as 8 bytes short
+        *  (not including the size of the header), this code assumes you're
+        *  passing in corrected lengths
+        */
+       protected static Record createRecordForType(long type, byte[] b, int start, int len) {
+               // Default is to use UnknownRecordPlaceholder
+               // When you create classes for new Records, add them here
+               switch((int)type) {
+                       // Document
+                       case 1000:
+                               return new DummyRecordWithChildren(b,start,len);
+                               
+                       // "Slide"
+                       case 1006:
+                               return new Slide(b,start,len);
+
+                       // "SlideAtom"
+                       case 1007:
+                               return new SlideAtom(b,start,len);
+
+                       // "Notes"
+                       case 1008:
+                               return new Notes(b,start,len);
+                               
+                       // "NotesAtom" (Details on Notes sheets)
+                       case 1009:
+                               return new NotesAtom(b,start,len);
+                               
+                       // "SlidePersistAtom" (Details on text for a sheet)
+                       case 1011:
+                               return new SlidePersistAtom(b,start,len);
+                               
+                       // MainMaster (MetaSheet lives inside the PPDrawing inside this)
+                       case 1016:
+                               return new DummyRecordWithChildren(b,start,len);
+
+                       // PPDrawing (MetaSheet lives inside this)
+                       case 1036:
+                               return new PPDrawing(b,start,len);
+
+                       // TextHeaderAtom (Holds details on following text)
+                       case 3999:
+                               return new TextHeaderAtom(b,start,len);
+                               
+                       // TextCharsAtom (Text in Unicode format)
+                       case 4000:
+                               return new TextCharsAtom(b,start,len);
+                               
+                       // TextByteAtom (Text in ascii format)
+                       case 4008:
+                               return new TextBytesAtom(b,start,len);
+                               
+                       // SlideListWithText (Many Sheets live inside here)
+                       case 4080:
+                               return new SlideListWithText(b,start,len);
+
+                       // UserEditAtom (Holds pointers, last viewed etc)
+                       case 4085:
+                               return new UserEditAtom(b,start,len);
+
+                       // PersistPtrFullBlock (Don't know what it holds, but do care about where it lives)
+                       case 6001:
+                               return new PersistPtrHolder(b,start,len);
+                       // PersistPtrIncrementalBlock (Don't know what it holds, but do care about where it lives)
+                       case 6002:
+                               return new PersistPtrHolder(b,start,len);
+                               
+                       default:
+                               return new UnknownRecordPlaceholder(b,start,len);
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordAtom.java
new file mode 100644 (file)
index 0000000..4e961e0
--- /dev/null
@@ -0,0 +1,38 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+/**
+ * Abstract class which all atom records will extend.
+ *
+ * @author Nick Burch
+ */
+
+public abstract class RecordAtom extends Record
+{
+       /** 
+        * We are an atom
+        */
+       public boolean isAnAtom() { return true; }
+
+       /** 
+        * We're an atom, returns null
+        */
+       public Record[] getChildRecords() { return null; }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java
new file mode 100644 (file)
index 0000000..6ecb770
--- /dev/null
@@ -0,0 +1,109 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Abstract class which all container records will extend. Providers
+ *  helpful methods for writing child records out to disk
+ *
+ * @author Nick Burch
+ */
+
+public abstract class RecordContainer extends Record
+{
+       /** 
+        * We're not an atom
+        */
+       public boolean isAnAtom() { return false; }
+
+       /**
+        * Write out our header, and our children.
+        * @param headerA the first byte of the header
+        * @param headerB the second byte of the header
+        * @param type the record type
+        * @param children our child records
+        * @param out the stream to write to
+        */
+       public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
+               // If we have a mutable output stream, take advantage of that
+               if(out instanceof MutableByteArrayOutputStream) {
+                       MutableByteArrayOutputStream mout = 
+                               (MutableByteArrayOutputStream)out;
+
+                       // Grab current size
+                       int oldSize = mout.getBytesWritten();
+
+                       // Write out our header, less the size
+                       mout.write(new byte[] {headerA,headerB});
+                       byte[] typeB = new byte[2];
+                       LittleEndian.putShort(typeB,(short)type);
+                       mout.write(typeB);
+                       mout.write(new byte[4]);
+
+                       // Write out the children
+                       for(int i=0; i<children.length; i++) {
+                               children[i].writeOut(mout);
+                       }
+
+                       // Update our header with the size
+                       // Don't forget to knock 8 more off, since we don't include the
+                       //  header in the size
+                       int length = mout.getBytesWritten() - oldSize - 8;
+                       byte[] size = new byte[4];
+                       LittleEndian.putInt(size,0,length);
+                       mout.overwrite(size, oldSize+4);
+               } else {
+                       // Going to have to do it a slower way, because we have
+                       // to update the length come the end
+
+                       // Create a ByteArrayOutputStream to hold everything in
+                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+                       // Write out our header, less the size
+                       baos.write(new byte[] {headerA,headerB});
+                       byte[] typeB = new byte[2];
+                       LittleEndian.putShort(typeB,(short)type);
+                       baos.write(typeB);
+                       baos.write(new byte[] {0,0,0,0});
+
+                       // Write out our children
+                       for(int i=0; i<children.length; i++) {
+                               children[i].writeOut(baos);
+                       }
+
+                       // Grab the bytes back
+                       byte[] toWrite = baos.toByteArray();
+
+                       // Update our header with the size
+                       // Don't forget to knock 8 more off, since we don't include the
+                       //  header in the size
+                       LittleEndian.putInt(toWrite,4,(toWrite.length-8));
+
+                       // Write out the bytes
+                       out.write(toWrite);
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java
new file mode 100644 (file)
index 0000000..dedfe22
--- /dev/null
@@ -0,0 +1,95 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Master container for Slides. There is one of these for every slide,
+ *  and they have certain specific children
+ *
+ * @author Nick Burch
+ */
+
+public class Slide extends RecordContainer
+{
+       private Record[] _children;
+       private byte[] _header;
+       private static long _type = 1006l;
+
+       // Links to our more interesting children
+       private SlideAtom slideAtom;
+       private PPDrawing ppDrawing;
+
+       /**
+        * Returns the SlideAtom of this Slide
+        */
+       public SlideAtom getSlideAtom() { return slideAtom; }
+
+       /**
+        * Returns the PPDrawing of this Slide, which has all the 
+        *  interesting data in it
+        */
+       public PPDrawing getPPDrawing() { return ppDrawing; }
+
+
+       /** 
+        * Set things up, and find our more interesting children
+        */
+       protected Slide(byte[] source, int start, int len) {
+               // Grab the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Find our children
+               _children = Record.findChildRecords(source,start+8,len-8);
+
+               // Find the interesting ones in there
+               for(int i=0; i<_children.length; i++) {
+                       if(_children[i] instanceof SlideAtom) {
+                               slideAtom = (SlideAtom)_children[i];
+                       }
+                       if(_children[i] instanceof PPDrawing) {
+                               ppDrawing = (PPDrawing)_children[i];
+                       }
+               }
+       }
+
+
+       /**
+        * We are of type 1006
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * Return any children 
+        */
+       public Record[] getChildRecords() { return _children; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               writeOut(_header[0],_header[1],_type,_children,out);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java
new file mode 100644 (file)
index 0000000..6446b17
--- /dev/null
@@ -0,0 +1,206 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A Slide Atom (type 1007). Holds information on the parent Slide, what
+ *  Master Slide it uses, what Notes is attached to it, that sort of thing.
+ *  It also has a SSlideLayoutAtom embeded in it, but without the Atom header
+ *
+ * @author Nick Burch
+ */
+
+public class SlideAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 1007l;
+       public static final int MASTER_SLIDE_ID = 0;
+       public static final int USES_MASTER_SLIDE_ID = -2147483648;
+
+       private int masterID;
+       private int notesID;
+
+       private boolean followMasterObjects;
+       private boolean followMasterScheme;
+       private boolean followMasterBackground;
+       private SSlideLayoutAtom layoutAtom;
+       private byte[] reserved;
+
+
+       /** Get the ID of the master slide used. 0 if this is a master slide, otherwise -2147483648 */
+       public int getMasterID() { return masterID; }
+       /** Get the ID of the notes for this slide. 0 if doesn't have one */
+       public int getNotesID()  { return notesID; }
+       /** Get the embeded SSlideLayoutAtom */
+       public SSlideLayoutAtom getSSlideLayoutAtom() { return layoutAtom; }
+
+       public boolean getFollowMasterObjects()    { return followMasterObjects; }
+       public boolean getFollowMasterScheme()     { return followMasterScheme; }
+       public boolean getFollowMasterBackground() { return followMasterBackground; }
+       public void setFollowMasterObjects(boolean flag)    { followMasterObjects = flag; }
+       public void setFollowMasterScheme(boolean flag)     { followMasterScheme = flag; }
+       public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
+
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the Slide Atom
+        */
+       protected SlideAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 30) { len = 30; }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Grab the 12 bytes that is "SSlideLayoutAtom"
+               byte[] SSlideLayoutAtomData = new byte[12];
+               System.arraycopy(source,start+8,SSlideLayoutAtomData,0,12);
+               // Use them to build up the SSlideLayoutAtom
+               layoutAtom = new SSlideLayoutAtom(SSlideLayoutAtomData);
+
+               // Get the IDs of the master and notes
+               masterID = (int)LittleEndian.getInt(source,start+12+8);
+               notesID = (int)LittleEndian.getInt(source,start+16+8);
+
+               // Grok the flags, stored as bits
+               int flags = LittleEndian.getUShort(source,start+20+8);
+               if((flags&4) == 4) {
+                       followMasterBackground = true;
+               } else {
+                       followMasterBackground = false;
+               }
+               if((flags&2) == 2) {
+                       followMasterScheme = true;
+               } else {
+                       followMasterScheme = false;
+               }
+               if((flags&1) == 1) {
+                       followMasterObjects = true;
+               } else {
+                       followMasterObjects = false;
+               }
+
+               // If there's any other bits of data, keep them about
+               // 8 bytes header + 20 bytes to flags + 2 bytes flags = 30 bytes
+               reserved = new byte[len-30];
+               System.arraycopy(source,start+30,reserved,0,reserved.length);
+       }
+
+       /**
+        * We are of type 1007
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header
+               out.write(_header);
+
+               // SSSlideLayoutAtom stuff
+               layoutAtom.writeOut(out);
+
+               // IDs
+               writeLittleEndian(masterID,out);
+               writeLittleEndian(notesID,out);
+
+               // Flags
+               short flags = 0;
+               if(followMasterObjects)    { flags += 1; }
+               if(followMasterScheme)     { flags += 2; }
+               if(followMasterBackground) { flags += 4; }
+               writeLittleEndian(flags,out);
+
+               // Reserved data
+               out.write(reserved);
+       }
+
+
+       /**
+        * Holds the geometry of the Slide, and the ID of the placeholders
+        *  on the slide.
+        * (Embeded inside SlideAtom is a SSlideLayoutAtom, without the
+        *  usual record header. Since it's a fixed size and tied to 
+        *  the SlideAtom, we'll hold it here.)
+        */
+       public class SSlideLayoutAtom {
+               // The different kinds of geometry
+               public static final int TITLE_SLIDE = 0;
+               public static final int TITLE_BODY_SLIDE = 1;
+               public static final int TITLE_MASTER_SLIDE = 2;
+               public static final int MASTER_SLIDE = 3;
+               public static final int MASTER_NOTES = 4;
+               public static final int NOTES_TITLE_BODY = 5;
+               public static final int HANDOUT = 6; // Only header, footer and date placeholders
+               public static final int TITLE_ONLY = 7;
+               public static final int TITLE_2_COLUMN_BODY = 8;
+               public static final int TITLE_2_ROW_BODY = 9;
+               public static final int TITLE_2_COLUNM_RIGHT_2_ROW_BODY = 10;
+               public static final int TITLE_2_COLUNM_LEFT_2_ROW_BODY = 11;
+               public static final int TITLE_2_ROW_BOTTOM_2_COLUMN_BODY = 12;
+               public static final int TITLE_2_ROW_TOP_2_COLUMN_BODY = 13;
+               public static final int FOUR_OBJECTS = 14;
+               public static final int BIG_OBJECT = 15;
+               public static final int BLANK_SLIDE = 16;
+               public static final int VERTICAL_TITLE_BODY_LEFT = 17;
+               public static final int VERTICAL_TITLE_2_ROW_BODY_LEFT = 17;
+
+               /** What geometry type we are */
+               private int geometry;
+               /** What placeholder IDs we have */
+               private byte[] placeholderIDs;
+
+               /** Retrieve the geometry type */
+               public int getGeometryType() { return geometry; }
+
+               /**
+                * Create a new Embeded SSlideLayoutAtom, from 12 bytes of data
+                */
+               public SSlideLayoutAtom(byte[] data) {
+                       if(data.length != 12) {
+                               throw new RuntimeException("SSlideLayoutAtom created with byte array not 12 bytes long - was " + data.length + " bytes in size");
+                       }
+
+                       // Grab out our data
+                       geometry = (int)LittleEndian.getInt(data,0);
+                       placeholderIDs = new byte[8];
+                       System.arraycopy(data,4,placeholderIDs,0,8);
+               }
+
+               /**
+                * Write the contents of the record back, so it can be written
+                *  to disk. Skips the record header
+                */
+               public void writeOut(OutputStream out) throws IOException {
+                       // Write the geometry
+                       writeLittleEndian(geometry,out);
+                       // Write the placeholder IDs
+                       out.write(placeholderIDs);
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java
new file mode 100644 (file)
index 0000000..c8ed475
--- /dev/null
@@ -0,0 +1,148 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hslf.model.Sheet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Vector;
+
+/**
+ * These are tricky beasts. They contain the text of potentially
+ *  many (normal) slides. They are made up of several sets of
+ *  - SlidePersistAtom
+ *  - TextHeaderAtom
+ *  - TextBytesAtom / TextCharsAtom
+ *  - StyleTextPropAtom (optional)
+ *  - TextSpecInfoAtom (optional)
+ *  - InteractiveInfo (optional)
+ *  - TxInteractiveInfoAtom (optional)
+ * and then the next SlidePersistAtom.
+ *
+ * Eventually, Slides will find the blocks that interest them from all
+ *  the SlideListWithText entries, and refere to them
+ *
+ * For now, we scan through looking for interesting bits, then creating
+ *  the helpful Sheet from model for them
+ *
+ * @author Nick Burch
+ */
+
+// For now, pretend to be an atom
+public class SlideListWithText extends RecordContainer
+{
+       private Record[] _children;
+       private byte[] _header;
+       private static long _type = 4080;
+
+       private SlideAtomsSet[] slideAtomsSets;
+
+       /** 
+        * Create a new holder for slide records
+        */
+       protected SlideListWithText(byte[] source, int start, int len) {
+               // Grab the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Find our children
+               _children = Record.findChildRecords(source,start+8,len-8);      
+
+               // Group our children together into SlideAtomsSets
+               // That way, model layer code can just grab the sets to use, 
+               //  without having to try to match the children together
+               Vector sets = new Vector();
+               for(int i=0; i<_children.length; i++) {
+                       if(_children[i] instanceof SlidePersistAtom) {
+                               // Find where the next SlidePersistAtom is
+                               int endPos = i+1;
+                               while(endPos < _children.length && !(_children[endPos] instanceof SlidePersistAtom)) {
+                                       endPos += 1;
+                               }
+
+                               // Now, if not empty, create a SlideAtomsSets
+                               int clen = endPos - i - 1;
+                               if(clen == 0) { continue; }
+                               Record[] spaChildren = new Record[clen];
+                               System.arraycopy(_children,i+1,spaChildren,0,clen);
+                               SlideAtomsSet set = new SlideAtomsSet((SlidePersistAtom)_children[i],spaChildren);
+                               sets.add(set);
+
+                               // Wind on
+                               i += clen;
+                       }
+               }
+
+               // Turn the vector into an array
+               slideAtomsSets = new SlideAtomsSet[sets.size()];
+               for(int i=0; i<slideAtomsSets.length; i++) {
+                       slideAtomsSets[i] = (SlideAtomsSet)sets.get(i);
+               }
+       }
+
+
+       /**
+        * Get access to the SlideAtomsSets of the children of this record
+        */
+       public SlideAtomsSet[] getSlideAtomsSets() { return slideAtomsSets; }
+
+       /**
+        * Return the value we were given at creation
+        */
+       public long getRecordType() { return _type; }
+
+       /** 
+        * We're pretending to be an atom, so return null
+        */
+       public Record[] getChildRecords() { return _children; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               writeOut(_header[0],_header[1],_type,_children,out);
+       }
+
+
+       /** 
+        * Inner class to wrap up a matching set of records that hold the
+        *  text for a given sheet. Contains the leading SlidePersistAtom,
+        *  and all of the records until the next SlidePersistAtom. This 
+        *  includes sets of TextHeaderAtom and TextBytesAtom/TextCharsAtom,
+        *  along with some others.
+        */
+       public class SlideAtomsSet {
+               private SlidePersistAtom slidePersistAtom;
+               private Record[] slideRecords;
+
+               /** Get the SlidePersistAtom, which gives details on the Slide this text is associated with */
+               public SlidePersistAtom getSlidePersistAtom() { return slidePersistAtom; }
+               /** Get the Text related records for this slide */
+               public Record[] getSlideRecords() { return slideRecords; }
+
+               /** Create one to hold the Records for one Slide's text */
+               public SlideAtomsSet(SlidePersistAtom s, Record[] r) {
+                       slidePersistAtom = s;
+                       slideRecords = r;
+               }
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java
new file mode 100644 (file)
index 0000000..f52bfbf
--- /dev/null
@@ -0,0 +1,114 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A SlidePersist Atom (type 1011). Holds information on the text of a
+ *  given slide, which are stored in the same SlideListWithText
+ *
+ * @author Nick Burch
+ */
+
+public class SlidePersistAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 1011l;
+
+       /** Slide reference ID. A machine readable "page id" */
+       private int refID;
+       private boolean hasShapesOtherThanPlaceholders;
+       /** Number of placeholder texts that will follow in the SlideListWithText */
+       private int numPlaceholderTexts;
+       /** Less useful identifier */
+       private int slideIdentifier;
+       /** Reserved fields. Who knows what they do */
+       private byte[] reservedFields;
+
+       public int getRefID() { return refID; }
+       public int getSlideIdentifier() { return slideIdentifier; }
+       public int getNumPlaceholderTexts() { return numPlaceholderTexts; }
+       public boolean getHasShapesOtherThanPlaceholders() { return hasShapesOtherThanPlaceholders; }
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the SlidePersist Atom
+        */
+       protected SlidePersistAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 8) { len = 8; }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Grab the reference ID
+               refID = (int)LittleEndian.getInt(source,start+8);
+
+               // Next up is a set of flags, but only bit 3 is used!
+               int flags = (int)LittleEndian.getInt(source,start+12);
+               if(flags == 4) {
+                       hasShapesOtherThanPlaceholders = true;
+               } else {
+                       hasShapesOtherThanPlaceholders = false;
+               }
+
+               // Now the number of Placeholder Texts
+               numPlaceholderTexts = (int)LittleEndian.getInt(source,start+16);
+
+               // Last useful one is the unique slide identifier
+               slideIdentifier = (int)LittleEndian.getInt(source,start+20);
+
+               // Finally you have typically 4 or 8 bytes of reserved fields, 
+               //  all zero running from 24 bytes in to the end
+               reservedFields = new byte[len-24];
+               System.arraycopy(source,start+24,reservedFields,0,reservedFields.length);
+       }
+
+       /**
+        * We are of type 1011
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header - size or type unchanged
+               out.write(_header);
+
+               // Compute the flags part - only bit 3 is used
+               int flags = 0;
+               if(hasShapesOtherThanPlaceholders) {
+                       flags = 4;
+               }
+
+               // Write out our fields
+               writeLittleEndian(refID,out);
+               writeLittleEndian(flags,out);
+               writeLittleEndian(numPlaceholderTexts,out);
+               writeLittleEndian(slideIdentifier,out);
+               out.write(reservedFields);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java
new file mode 100644 (file)
index 0000000..a0d4213
--- /dev/null
@@ -0,0 +1,91 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A TextBytesAtom (type 4008). Holds text in ascii form (unknown
+ *  code page, for now assumed to be the default of 
+ *  org.apache.poi.util.StringUtil, which is the Excel default).
+ * The trailing return character is always stripped from this
+ *
+ * @author Nick Burch
+ */
+
+public class TextBytesAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 4008l;
+
+       /** The bytes that make up the text */
+       private byte[] _text;
+
+       /** Grabs the text. Uses the default codepage */
+       public String getText() { 
+               return StringUtil.getFromCompressedUnicode(_text,0,_text.length);
+       }
+
+       /** Updates the text in the Atom. Must be 8 bit ascii */
+       public void setText(byte[] b) { 
+               // Set the text
+               _text = b;
+
+               // Update the size (header bytes 5-8)
+               LittleEndian.putInt(_header,4,_text.length);
+       }
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the TextBytes Atom
+        */
+       protected TextBytesAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 8) { len = 8; }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Grab the text
+               _text = new byte[len-8];
+               System.arraycopy(source,start+8,_text,0,len-8);
+       }
+
+       /**
+        * We are of type 4008
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header - size or type unchanged
+               out.write(_header);
+
+               // Write out our text
+               out.write(_text);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java
new file mode 100644 (file)
index 0000000..852968b
--- /dev/null
@@ -0,0 +1,91 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * A TextCharsAtom (type 4000). Holds text in byte swapped unicode form.
+ * The trailing return character is always stripped from this
+ *
+ * @author Nick Burch
+ */
+
+public class TextCharsAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 4000l;
+
+       /** The bytes that make up the text */
+       private byte[] _text;
+
+       /** Grabs the text. */
+       public String getText() { 
+               return StringUtil.getFromUnicodeLE(_text);
+       }
+
+       /** Updates the text in the Atom. */
+       public void setText(String text) {
+               // Convert to little endian unicode
+               _text = new byte[text.length()*2];
+               StringUtil.putUnicodeLE(text,_text,0);
+
+               // Update the size (header bytes 5-8)
+               LittleEndian.putInt(_header,4,_text.length);
+       }
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the TextChars Atom 
+        */
+       protected TextCharsAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 8) { len = 8; }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Grab the text
+               _text = new byte[len-8];
+               System.arraycopy(source,start+8,_text,0,len-8);
+       }
+
+       /**
+        * We are of type 4000
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header - size or type unchanged
+               out.write(_header);
+
+               // Write out our text
+               out.write(_text);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java
new file mode 100644 (file)
index 0000000..4f5ba7b
--- /dev/null
@@ -0,0 +1,91 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A TextHeaderAtom  (type 3999). Holds information on what kind of
+ *  text is contained in the TextBytesAtom / TextCharsAtom that follows
+ *  straight after
+ *
+ * @author Nick Burch
+ */
+
+public class TextHeaderAtom extends RecordAtom
+{
+       private byte[] _header;
+       private static long _type = 3999l;
+
+       public static final int TITLE_TYPE = 0;
+       public static final int BODY_TYPE = 1;
+       public static final int NOTES_TYPE = 2;
+       public static final int OTHER_TYPE = 4;
+       public static final int CENTRE_BODY_TYPE = 5;
+       public static final int CENTER_TITLE_TYPE = 6;
+       public static final int HALF_BODY_TYPE = 7;
+       public static final int QUARTER_BODY_TYPE = 8;
+
+       /** The kind of text it is */
+       private int textType;
+
+       public int getTextType() { return textType; }
+       public void setTextType(int type) { textType = type; }
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the TextHeader Atom
+        */
+       protected TextHeaderAtom(byte[] source, int start, int len) {
+               // Sanity Checking - we're always 12 bytes long
+               if(len < 12) {
+                       len = 12;
+                       if(source.length - start < 12) {
+                               throw new RuntimeException("Not enough data to form a TextHeaderAtom (always 12 bytes long) - found " + (source.length - start));
+                       }
+               }
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Grab the type
+               textType = (int)LittleEndian.getInt(source,start+8);
+       }
+
+       /**
+        * We are of type 3999
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header - size or type unchanged
+               out.write(_header);
+
+               // Write out our type
+               writeLittleEndian(textType,out);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java b/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java
new file mode 100644 (file)
index 0000000..b4901cf
--- /dev/null
@@ -0,0 +1,64 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * If we come across a record we don't know about, we create one of
+ *  these. It allows us to keep track of what it contains, so we can
+ *  write it back out to disk unchanged
+ *
+ * @author Nick Burch
+ */
+
+public class UnknownRecordPlaceholder extends RecordAtom
+{
+       private byte[] _contents;
+       private long _type;
+
+       /** 
+        * Create a new holder for a record we don't grok
+        */
+       protected UnknownRecordPlaceholder(byte[] source, int start, int len) {
+               // Sanity Checking - including whole header, so treat
+               //  length as based of 0, not 8 (including header size based)
+               if(len < 0) { len = 0; }
+
+               // Treat as an atom, grab and hold everything
+               _contents = new byte[len];
+               System.arraycopy(source,start,_contents,0,len);
+               _type = LittleEndian.getUShort(_contents,2);
+       }
+
+       /**
+        * Return the value we were given at creation
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               out.write(_contents);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java
new file mode 100644 (file)
index 0000000..d379ed3
--- /dev/null
@@ -0,0 +1,141 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+import org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A UserEdit Atom (type 4085). Holds information which bits of the file
+ *  were last used by powerpoint, the version of powerpoint last used etc.
+ *
+ * ** WARNING ** stores byte offsets from the start of the PPT stream to
+ *  other records! If you change the size of any elements before one of
+ *  these, you'll need to update the offsets!
+ *
+ * @author Nick Burch
+ */
+
+public class UserEditAtom extends PositionDependentRecordAtom
+{
+       public static final int LAST_VIEW_NONE = 0;
+       public static final int LAST_VIEW_SLIDE_VIEW = 1;
+       public static final int LAST_VIEW_OUTLINE_VIEW = 2;
+       public static final int LAST_VIEW_NOTES = 3;
+
+       private byte[] _header;
+       private static long _type = 4085l;
+       private byte[] reserved;
+
+       private int lastViewedSlideID;
+       private int pptVersion;
+       private int lastUserEditAtomOffset;
+       private int persistPointersOffset;
+       private int docPersistRef;
+       private int maxPersistWritten;
+       private short lastViewType;
+
+       // Somewhat user facing getters
+       public int getLastViewedSlideID() { return lastViewedSlideID; }
+       public short getLastViewType()    { return lastViewType; }
+
+       // Scary internal getters
+       public int getLastUserEditAtomOffset() { return lastUserEditAtomOffset; }
+       public int getPersistPointersOffset()  { return persistPointersOffset; }
+       public int getDocPersistRef()          { return docPersistRef; }
+       public int getMaxPersistWritten()      { return maxPersistWritten; }
+
+       // More scary internal setters
+       public void setLastUserEditAtomOffset(int offset) { lastUserEditAtomOffset = offset; }
+       public void setPersistPointersOffset(int offset)  { persistPointersOffset = offset; }
+
+       /* *************** record code follows ********************** */
+
+       /** 
+        * For the UserEdit Atom
+        */
+       protected UserEditAtom(byte[] source, int start, int len) {
+               // Sanity Checking
+               if(len < 34) { len = 34; }
+
+               // Store where we currently live on disk
+               myLastOnDiskOffset = start;
+
+               // Get the header
+               _header = new byte[8];
+               System.arraycopy(source,start,_header,0,8);
+
+               // Get the last viewed slide ID
+               lastViewedSlideID = (int)LittleEndian.getInt(source,start+0+8);
+
+               // Get the PPT version
+               pptVersion = (int)LittleEndian.getInt(source,start+4+8);
+
+               // 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 = (int)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 = (int)LittleEndian.getInt(source,start+12+8);
+
+               // Get the persist reference for the document persist object
+               // Normally seems to be 1
+               docPersistRef = (int)LittleEndian.getInt(source,start+16+8);
+
+               // Maximum number of persist objects written
+               maxPersistWritten = (int)LittleEndian.getInt(source,start+20+8);
+               
+               // Last view type
+               lastViewType = (short)LittleEndian.getShort(source,start+24+8);
+
+               // There might be a few more bytes, which are a reserved field
+               reserved = new byte[len-26-8];
+               System.arraycopy(source,start+26+8,reserved,0,reserved.length);
+       }
+
+       /**
+        * We are of type 4085
+        */
+       public long getRecordType() { return _type; }
+
+       /**
+        * Write the contents of the record back, so it can be written
+        *  to disk
+        */
+       public void writeOut(OutputStream out) throws IOException {
+               // Header
+               out.write(_header);
+
+               // Write out the values
+               writeLittleEndian(lastViewedSlideID,out);
+               writeLittleEndian(pptVersion,out);
+               writeLittleEndian(lastUserEditAtomOffset,out);
+               writeLittleEndian(persistPointersOffset,out);
+               writeLittleEndian(docPersistRef,out);
+               writeLittleEndian(maxPersistWritten,out);
+               writeLittleEndian(lastViewType,out);
+
+               // Reserved fields
+               out.write(reserved);
+       }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
new file mode 100644 (file)
index 0000000..c3a789f
--- /dev/null
@@ -0,0 +1,281 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.usermodel;
+
+import java.util.*;
+import java.io.*;
+
+import org.apache.poi.util.LittleEndian;
+
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.SlideAtom;
+import org.apache.poi.hslf.record.SlideListWithText;
+import org.apache.poi.hslf.record.SlideListWithText.*;
+
+/**
+ * This class is a friendly wrapper on top of the more scary HSLFSlideShow.
+ *
+ * TODO:
+ *  - figure out how to match notes to their correct sheet
+ *    (will involve understanding DocSlideList and DocNotesList)
+ *  - handle Slide creation cleaner
+ * 
+ * @author Nick Burch
+ */
+
+public class SlideShow
+{
+  // What we're based on
+  private HSLFSlideShow _hslfSlideShow;
+
+  // Low level contents, as taken from HSLFSlideShow
+  private Record[] _records;
+
+  // Friendly objects for people to deal with
+  private Slide[] _slides;
+  private Notes[] _notes;
+  // private MetaSheets[] _msheets;
+
+  /**
+   *  right now this function takes one parameter: a ppt file, and outputs
+   *  the text it can find for it
+   */
+  public static void main(String args[]) throws IOException
+  {
+       HSLFSlideShow basefoo = new HSLFSlideShow(args[0]);
+       SlideShow foo = new SlideShow(basefoo);
+
+       Slide[] slides = foo.getSlides();
+       for(int i=0; i<slides.length; i++) {
+               Slide slide = slides[i];
+               System.out.println("*Slide " + slide.getSheetNumber() + ":");
+               TextRun[] runs = slide.getTextRuns();
+               for(int j=0; j<runs.length; j++) {
+                       TextRun run = runs[j];
+                       System.out.println("  * Text run " + run.getRunType());
+                       System.out.println("\n" + run.getText() + "\n");
+               }
+       }
+  }
+
+  /**
+   * Constructs a Powerpoint document from the underlying 
+   * HSLFSlideShow object. Finds the model stuff from this
+   *
+   * @param hslfSlideShow the HSLFSlideShow to base on
+   */
+  public SlideShow(HSLFSlideShow hslfSlideShow) throws IOException
+  {
+       // Get useful things from our base slideshow
+    _hslfSlideShow = hslfSlideShow;
+       _records = _hslfSlideShow.getRecords();
+       byte[] _docstream = _hslfSlideShow.getUnderlyingBytes();
+
+
+       // For holding the Slide Records
+       Vector slidesV = new Vector(10);
+       // For holding the Notes Records
+       Vector notesV = new Vector(10);
+       // For holding the Meta Sheet Records
+       Vector metaSheetsV = new Vector(10);
+       // For holding Document Records
+       Vector documentsV = new Vector(10);
+       // For holding SlideListWithText Records
+       Vector slwtV = new Vector(10);
+
+       // Look for Notes, Slides and Documents
+       for(int i=0; i<_records.length; i++) {
+               if(_records[i] instanceof org.apache.poi.hslf.record.Notes) {
+                       notesV.add(_records[i]);
+               }
+               if(_records[i] instanceof org.apache.poi.hslf.record.Slide) {
+                       slidesV.add(_records[i]);
+               }
+               if(_records[i].getRecordType() == 1000l) {
+                       documentsV.add(_records[i]);
+               }
+       }
+
+
+       // Also look for SlideListWithTexts in Documents
+       //
+       // Need to get the SlideAtomsSets for all of these. Then, query the
+       //  SlidePersistAtom, and group stuff together between SLWT blocks
+       //  based on the refID/slideID. Finally, build up a list of all the
+       //  SlideAtomsSets for a given refID / slideID, and pass them on to
+       //  the Slide when creating
+       //
+       // If a notes sheet exists, can normally match the Notes sheet ID
+       //  to the slide ID in the SlidePersistAtom. Since there isn't always,
+       //  and we can't find the ID in the slide, just order on the slide ID,
+       //  and hand off to the Slides in turn.
+       // (Based on output from dev.SLWTTextListing and dev.SlideAndNotesAtomListing)
+       //
+       // There is often duplicate text, especially for the first few
+       //  Slides. Currently, it's up to the Slide model code to detect
+       //  and ignore those
+
+       for(int i=0; i<documentsV.size(); i++) {
+               Record docRecord = (Record)documentsV.get(i);
+               Record[] docChildren = docRecord.getChildRecords();
+               for(int j=0; j<docChildren.length; j++) {
+                       if(docChildren[j] instanceof SlideListWithText) {
+                               //System.out.println("Found SLWT in document " + i);
+                               //System.out.println("  Has " + docChildren[j].getChildRecords().length + " children");
+                               slwtV.add(docChildren[j]);
+                       }
+               }
+       }
+
+       // For now, grab out all the sets of Atoms in the SlideListWithText's
+       // Only store those which aren't empty
+       Vector setsV = new Vector();
+       for(int i=0; i<slwtV.size(); i++) {
+               SlideListWithText slwt = (SlideListWithText)slwtV.get(i);
+               SlideAtomsSet[] thisSets = slwt.getSlideAtomsSets();
+               for(int j=0; j<thisSets.length; j++) {
+                       setsV.add(thisSets[j]);
+               }
+       }
+
+
+       // Now, sort the SlideAtomSets together into groups for the same slide ID,
+       //  and order them by the slide ID
+
+       // Find the unique IDs
+       HashSet uniqueSlideIDs = new HashSet();
+       for(int i=0; i<setsV.size(); i++) {
+               SlideAtomsSet thisSet = (SlideAtomsSet)setsV.get(i);
+               int id = thisSet.getSlidePersistAtom().getSlideIdentifier();
+               Integer idI = new Integer(id);
+               if(! uniqueSlideIDs.contains(idI) ) {
+                       uniqueSlideIDs.add(idI);
+               }
+       }
+       int[] slideIDs = new int[uniqueSlideIDs.size()];
+       int pos = 0;
+       for(Iterator getIDs = uniqueSlideIDs.iterator(); getIDs.hasNext(); pos++) {
+               Integer id = (Integer)getIDs.next();
+               slideIDs[pos] = id.intValue();
+       }
+       // Sort
+       Arrays.sort(slideIDs);
+       // Group
+       Vector[] sortedSetsV = new Vector[slideIDs.length];
+       for(int i=0; i<setsV.size(); i++) {
+               SlideAtomsSet thisSet = (SlideAtomsSet)setsV.get(i);
+               int id = thisSet.getSlidePersistAtom().getSlideIdentifier();
+               int arrayPos = -1;
+               for(int j=0; j<slideIDs.length; j++) {
+                       if(slideIDs[j] == id) { arrayPos = j; }
+               }
+               if(sortedSetsV[arrayPos] == null) { sortedSetsV[arrayPos] = new Vector(); }
+               sortedSetsV[arrayPos].add(thisSet);
+       }
+
+
+       // ******************* Do the real model layer creation ****************
+
+
+       // Create our Notes
+       // (Need to create first, as passed to the Slides)
+       _notes = new Notes[notesV.size()];
+       for(int i=0; i<_notes.length; i++) {
+               _notes[i] = new Notes((org.apache.poi.hslf.record.Notes)notesV.get(i));
+       }
+
+
+       // Create our Slides
+       _slides = new Slide[slidesV.size()];
+       for(int i=0; i<_slides.length; i++) {
+               // Grab the slide Record
+               org.apache.poi.hslf.record.Slide slideRecord = (org.apache.poi.hslf.record.Slide)slidesV.get(i);
+
+               // Do they have a Notes?
+               Notes thisNotes = null;
+               // Find their SlideAtom, and use this to check for a Notes
+               Record[] slideRecordChildren = slideRecord.getChildRecords();           
+               for(int j=0; j<slideRecordChildren.length; j++) {
+                       if(slideRecordChildren[j] instanceof SlideAtom) {
+                               SlideAtom sa = (SlideAtom)slideRecordChildren[j];
+                               int notesID = sa.getNotesID();
+                               if(notesID != 0) {
+                                       for(int k=0; k<_notes.length; k++) {
+                                               if(_notes[k].getSheetNumber() == notesID) {
+                                                       thisNotes = _notes[k];
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Grab the (hopefully) corresponding block of Atoms
+               SlideAtomsSet[] sets;
+               if(sortedSetsV.length > i) {
+                       Vector thisSetsV = sortedSetsV[i];
+                       sets = new SlideAtomsSet[thisSetsV.size()];
+                       for(int j=0; j<sets.length; j++) {
+                               sets[j] = (SlideAtomsSet)thisSetsV.get(j);
+                       }
+                       //System.out.println("For slide " + i + ", found " + sets.length + " Sets of text");
+               } else {
+                       // Didn't find enough SlideAtomSets to give any to this sheet
+                       sets = new SlideAtomsSet[0];
+               }
+
+                       // Create the Slide model layer
+               _slides[i] = new Slide(slideRecord,thisNotes,sets);
+       }
+
+  }
+
+
+  /**
+   * 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
+   */
+   public void write(OutputStream out) throws IOException {
+       _hslfSlideShow.write(out);
+   }
+
+
+  // Accesser methods follow
+
+  /**
+   * Returns an array of all the normal Slides found in the slideshow
+   */
+  public Slide[] getSlides() { return _slides; }
+
+  /**
+   * Returns an array of all the normal Notes found in the slideshow
+   */
+  public Notes[] getNotes() { return _notes; }
+
+  /**
+   * Returns an array of all the meta Sheets (master sheets etc) 
+   * found in the slideshow
+   */
+  //public MetaSheet[] getMetaSheets() { return _msheets; }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/util/MutableByteArrayOutputStream.java b/src/scratchpad/src/org/apache/poi/hslf/util/MutableByteArrayOutputStream.java
new file mode 100644 (file)
index 0000000..de8a652
--- /dev/null
@@ -0,0 +1,42 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.util;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class doesn't work yet, but is here to show the idea of a
+ *  ByteArrayOutputStream where you can track how many bytes you've
+ *  already written, and go back and write over a previous part of the stream
+ *
+ * @author Nick Burch
+ */
+
+public class MutableByteArrayOutputStream extends ByteArrayOutputStream
+{
+       /** Return how many bytes we've stuffed in so far */
+       public int getBytesWritten() { return -1; }
+
+       /** Write some bytes to the array */
+       public void write(byte[] b) {}
+       public void write(int b) {}
+
+       /** Write some bytes to an earlier bit of the array */
+       public void overwrite(byte[] b, int startPos) {}
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java b/src/scratchpad/testcases/org/apache/poi/hslf/TestReWrite.java
new file mode 100644 (file)
index 0000000..c671220
--- /dev/null
@@ -0,0 +1,73 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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 junit.framework.TestCase;
+import java.io.*;
+import org.apache.poi.poifs.filesystem.*;
+
+/**
+ * Tests that HSLFSlideShow writes the powerpoint bit of data back out
+ *  correctly. Currently, that means being the same as what it read in
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestReWrite extends TestCase {
+       // HSLFSlideShow primed on the test data
+       private HSLFSlideShow ss;
+       // POIFS primed on the test data
+       private POIFSFileSystem pfs;
+
+    public TestReWrite() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               FileInputStream fis = new FileInputStream(filename);
+               pfs = new POIFSFileSystem(fis);
+               ss = new HSLFSlideShow(pfs);
+    }
+
+    public void testWritesOutTheSame() throws Exception {
+               // Write out to a byte array
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               ss.write(baos);
+
+               // Build an input stream of it
+               ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+
+               // Use POIFS to query that lot
+               POIFSFileSystem npfs = new POIFSFileSystem(bais);
+
+               // Check that the "PowerPoint Document" sections have the same size
+               DocumentEntry oProps = (DocumentEntry)pfs.getRoot().getEntry("PowerPoint Document");
+               DocumentEntry nProps = (DocumentEntry)npfs.getRoot().getEntry("PowerPoint Document");
+               assertEquals(oProps.getSize(),nProps.getSize());
+
+               // Check that they contain the same data
+               byte[] _oData = new byte[oProps.getSize()];
+               byte[] _nData = new byte[nProps.getSize()];
+               pfs.createDocumentInputStream("PowerPoint Document").read(_oData);
+               npfs.createDocumentInputStream("PowerPoint Document").read(_nData);
+               for(int i=0; i<_oData.length; i++) {
+                       System.out.println(i + "\t" + Integer.toHexString(i));
+                       assertEquals(_oData[i], _nData[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/TestReWriteSanity.java b/src/scratchpad/testcases/org/apache/poi/hslf/TestReWriteSanity.java
new file mode 100644 (file)
index 0000000..1867a20
--- /dev/null
@@ -0,0 +1,100 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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 junit.framework.TestCase;
+import java.io.*;
+import java.util.*;
+import org.apache.poi.hslf.record.*;
+import org.apache.poi.poifs.filesystem.*;
+
+/**
+ * Tests that HSLFSlideShow writes the powerpoint bit of data back out
+ *  in a sane manner - i.e. records end up in the right place
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestReWriteSanity extends TestCase {
+       // HSLFSlideShow primed on the test data
+       private HSLFSlideShow ss;
+       // POIFS primed on the test data
+       private POIFSFileSystem pfs;
+
+    public TestReWriteSanity() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               FileInputStream fis = new FileInputStream(filename);
+               pfs = new POIFSFileSystem(fis);
+               ss = new HSLFSlideShow(pfs);
+    }
+
+       public void testUserEditAtomsRight() throws Exception {
+               // Write out to a byte array
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               ss.write(baos);
+
+               // Build an input stream of it
+               ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+
+               // Create a new one from that
+               HSLFSlideShow wss = new HSLFSlideShow(bais);
+
+               // Find the location of the PersistPtrIncrementalBlocks and 
+               // UserEditAtoms
+               Record[] r = wss.getRecords();
+               Hashtable pp = new Hashtable();
+               Hashtable ue = new Hashtable();
+               ue.put(new Integer(0),new Integer(0)); // Will show 0 if first
+               int pos = 0;
+               int lastUEPos = -1;
+
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof PersistPtrHolder) {
+                               pp.put(new Integer(pos), r[i]);
+                       }
+                       if(r[i] instanceof UserEditAtom) {
+                               ue.put(new Integer(pos), r[i]);
+                               lastUEPos = pos;
+                       }
+                       
+                       ByteArrayOutputStream bc = new ByteArrayOutputStream();
+                       r[i].writeOut(bc);
+                       pos += bc.size();
+               }
+
+               // Check that the UserEditAtom's point to right stuff
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof UserEditAtom) {
+                               UserEditAtom uea = (UserEditAtom)r[i];
+                               int luPos = uea.getLastUserEditAtomOffset();
+                               int ppPos = uea.getPersistPointersOffset();
+
+                               assertTrue(pp.containsKey(new Integer(ppPos)));
+                               assertTrue(ue.containsKey(new Integer(luPos)));
+                       }
+               }
+
+               // Check that the CurrentUserAtom points to the right UserEditAtom
+               CurrentUserAtom cua = wss.getCurrentUserAtom();
+               int listedUEPos = (int)cua.getCurrentEditOffset();
+               assertEquals(lastUEPos,listedUEPos);
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/TestRecordCounts.java b/src/scratchpad/testcases/org/apache/poi/hslf/TestRecordCounts.java
new file mode 100644 (file)
index 0000000..d96ea01
--- /dev/null
@@ -0,0 +1,86 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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 junit.framework.TestCase;
+import org.apache.poi.hslf.record.*;
+
+/**
+ * Tests that HSLFSlideShow returns the right numbers of key records when
+ * it parses the test file
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestRecordCounts extends TestCase {
+       // HSLFSlideShow primed on the test data
+       private HSLFSlideShow ss;
+
+    public TestRecordCounts() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               ss = new HSLFSlideShow(filename);
+    }
+
+    public void testSheetsCount() throws Exception {
+               // Top level
+               Record[] r = ss.getRecords();
+
+               int count = 0;
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof Slide) {
+                               count++;
+                       }
+               }
+               // Currently still sees the Master Sheet, but might not in the future
+               assertEquals(3,count);
+       }
+
+    public void testNotesCount() throws Exception {
+               // Top level
+               Record[] r = ss.getRecords();
+
+               int count = 0;
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof Notes &&
+                       r[i].getRecordType() == 1008l) {
+                               count++;
+                       }
+               }
+               // Two real sheets, plus the master sheet
+               assertEquals(3,count);
+       }
+
+    public void testSlideListWithTextCount() throws Exception {
+               // Second level
+               Record[] rt = ss.getRecords();
+               Record[] r = rt[0].getChildRecords();
+
+               int count = 0;
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof SlideListWithText &&
+                       r[i].getRecordType() == 4080l) {
+                               count++;
+                       }
+               }
+               // Two real sheets, plus the master sheet
+               assertEquals(3,count);
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt
new file mode 100644 (file)
index 0000000..af16393
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/basic_test_ppt_file.ppt differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/next_test_ppt_file.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/next_test_ppt_file.ppt
new file mode 100644 (file)
index 0000000..69b2c47
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/next_test_ppt_file.ppt differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TextExtractor.java b/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TextExtractor.java
new file mode 100644 (file)
index 0000000..a8294a1
--- /dev/null
@@ -0,0 +1,67 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.extractor;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that the extractor correctly gets the text out of our sample file
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TextExtractor extends TestCase {
+       // Extractor primed on the test data
+       private PowerPointExtractor ppe;
+
+    public TextExtractor() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               ppe = new PowerPointExtractor(filename);
+    }
+
+    public void testReadSheetText() throws Exception {
+               String sheetText = ppe.getText();
+               String expectText = "This is a test title\nThis is a test subtitle\nThis is on page 1\nThis is the title on page 2\nThis is page two\nIt has several blocks of text\nNone of them have formatting\n";
+
+               assertEquals(expectText.length(),sheetText.length());
+               char[] st = sheetText.toCharArray();
+               char[] et = expectText.toCharArray();
+               for(int i=0; i<et.length; i++) {
+                       System.out.println(i + "\t" + et[i] + " " + st[i]);
+                       assertEquals(et[i],st[i]);
+               }
+               assertEquals(expectText,sheetText);
+    }
+
+       public void testReadNoteText() throws Exception {
+               String notesText = ppe.getNotes();
+               String expectText = "These are the notes for page 1\nThese are the notes on page two, again lacking formatting\n";
+
+               assertEquals(expectText.length(),notesText.length());
+               char[] nt = notesText.toCharArray();
+               char[] et = expectText.toCharArray();
+               for(int i=0; i<et.length; i++) {
+                       System.out.println(i + "\t" + et[i] + " " + nt[i]);
+                       assertEquals(et[i],nt[i]);
+               }
+               assertEquals(expectText,notesText);
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlidePersistAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlidePersistAtom.java
new file mode 100644 (file)
index 0000000..e3232c9
--- /dev/null
@@ -0,0 +1,60 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests that SlidePersistAtom works properly
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestSlidePersistAtom extends TestCase {
+       // From a real file
+       private byte[] data_a = new byte[] { 0, 0, 0xF3-256, 3, 0x14, 0, 0, 0,
+               4, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0,
+               1, 0, 0, 0, 0, 0, 0 };
+
+    public void testRecordType() throws Exception {
+               SlidePersistAtom spa = new SlidePersistAtom(data_a, 0, data_a.length);
+               assertEquals(1011l, spa.getRecordType());
+       }
+       public void testFlags() throws Exception {
+               SlidePersistAtom spa = new SlidePersistAtom(data_a, 0, data_a.length);
+               assertEquals(4, spa.getRefID() );
+               assertEquals(true, spa.getHasShapesOtherThanPlaceholders() );
+               assertEquals(2, spa.getNumPlaceholderTexts() );
+               assertEquals(256, spa.getSlideIdentifier());
+       }
+
+       public void testWrite() throws Exception {
+               SlidePersistAtom spa = new SlidePersistAtom(data_a, 0, data_a.length);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               spa.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               assertEquals(data_a.length, b.length);
+               for(int i=0; i<data_a.length; i++) {
+                       assertEquals(data_a[i],b[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextBytesAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextBytesAtom.java
new file mode 100644 (file)
index 0000000..93c62db
--- /dev/null
@@ -0,0 +1,83 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests that TextBytesAtom works properly
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestTextBytesAtom extends TestCase {
+       // From a real file
+       private byte[] data = new byte[]  { 0, 0, 0xA8-256, 0x0f, 0x1c, 0, 0, 0, 
+               0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 
+               0x65, 0x20, 0x74, 0x69, 0x74, 0x6C,     0x65, 0x20, 0x6F, 0x6E,
+               0x20, 0x70, 0x61, 0x67, 0x65, 0x20, 0x32 };
+       private String data_text = "This is the title on page 2";
+       private byte[] alt_data = new byte[] { 0, 0, 0xA8-256, 0x0F, 0x14, 0, 0, 0,
+               0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 
+               0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x69, 0x74, 0x6C, 0x65 };
+       private String alt_text = "This is a test title";
+
+    public void testRecordType() throws Exception {
+               TextBytesAtom tba = new TextBytesAtom(data,0,data.length);
+               assertEquals(4008l, tba.getRecordType());
+       }
+
+       public void testTextA() throws Exception {
+               TextBytesAtom tba = new TextBytesAtom(data,0,data.length);
+               assertEquals(data_text, tba.getText());
+       }
+       public void testTextB() throws Exception {
+               TextBytesAtom tba = new TextBytesAtom(alt_data,0,alt_data.length);
+               assertEquals(alt_text, tba.getText());
+       }
+
+       public void testChangeText() throws Exception {
+               TextBytesAtom tba = new TextBytesAtom(data,0,data.length);
+               tba.setText(alt_text.getBytes("ISO-8859-1"));
+
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               tba.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               // Compare the header and the text
+               assertEquals(alt_data.length, b.length);
+               for(int i=0; i<alt_data.length; i++) {
+                       assertEquals(alt_data[i],b[i]);
+               }
+       }
+
+       public void testWrite() throws Exception {
+               TextBytesAtom tba = new TextBytesAtom(data,0,data.length);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               tba.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               assertEquals(data.length, b.length);
+               for(int i=0; i<data.length; i++) {
+                       assertEquals(data[i],b[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextCharsAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextCharsAtom.java
new file mode 100644 (file)
index 0000000..f2aa221
--- /dev/null
@@ -0,0 +1,80 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests that TextCharsAtom works properly
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestTextCharsAtom extends TestCase {
+       // From a real file
+       private byte[] data = new byte[]  { 0, 0, 0xA0-256, 0x0f, 0x08, 0, 0, 0, 
+               0x54, 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00 };
+       private String data_text = "This";
+       private byte[] alt_data = new byte[] { 0, 0, 0xA0-256, 0x0F, 0x0a, 0, 0, 0,
+               0x54, 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0xa3-256, 0x01 };
+       private String alt_text = "This\u01A3";
+
+    public void testRecordType() throws Exception {
+               TextCharsAtom tca = new TextCharsAtom(data,0,data.length);
+               assertEquals(4000l, tca.getRecordType());
+       }
+
+       public void testTextA() throws Exception {
+               TextCharsAtom tca = new TextCharsAtom(data,0,data.length);
+               assertEquals(data_text, tca.getText());
+       }
+       public void testTextB() throws Exception {
+               TextCharsAtom tca = new TextCharsAtom(alt_data,0,alt_data.length);
+               assertEquals(alt_text, tca.getText());
+       }
+
+       public void testChangeText() throws Exception {
+               TextCharsAtom tca = new TextCharsAtom(data,0,data.length);
+               tca.setText(alt_text);
+
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               tca.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               // Compare the header and the text
+               assertEquals(alt_data.length, b.length);
+               for(int i=0; i<alt_data.length; i++) {
+                       assertEquals(alt_data[i],b[i]);
+               }
+       }
+
+       public void testWrite() throws Exception {
+               TextCharsAtom tca = new TextCharsAtom(data,0,data.length);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               tca.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               assertEquals(data.length, b.length);
+               for(int i=0; i<data.length; i++) {
+                       assertEquals(data[i],b[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java
new file mode 100644 (file)
index 0000000..7610b73
--- /dev/null
@@ -0,0 +1,61 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.record;
+
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests that TextHeaderAtom works properly
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestTextHeaderAtom extends TestCase {
+       // From a real file
+       private byte[] notes_data = new byte[] { 0, 0, 0x9f-256, 0x0f, 4, 0, 0, 0, 2, 0, 0, 0};
+       private byte[] title_data = new byte[] { 0, 0, 0x9f-256, 0x0f, 4, 0, 0, 0, 0, 0, 0, 0 };
+       private byte[] body_data = new byte[]  { 0, 0, 0x9f-256, 0x0f, 4, 0, 0, 0, 1, 0, 0, 0 };
+
+    public void testRecordType() throws Exception {
+               TextHeaderAtom tha = new TextHeaderAtom(notes_data,0,12);
+               assertEquals(3999l, tha.getRecordType());
+       }
+       public void testTypes() throws Exception {
+               TextHeaderAtom n_tha = new TextHeaderAtom(notes_data,0,12);
+               TextHeaderAtom t_tha = new TextHeaderAtom(title_data,0,12);
+               TextHeaderAtom b_tha = new TextHeaderAtom(body_data,0,12);
+               assertEquals(TextHeaderAtom.NOTES_TYPE, n_tha.getTextType());
+               assertEquals(TextHeaderAtom.TITLE_TYPE, t_tha.getTextType());
+               assertEquals(TextHeaderAtom.BODY_TYPE, b_tha.getTextType());
+       }
+
+       public void testWrite() throws Exception {
+               TextHeaderAtom tha = new TextHeaderAtom(notes_data,0,12);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               tha.writeOut(baos);
+               byte[] b = baos.toByteArray();
+
+               assertEquals(notes_data.length, b.length);
+               for(int i=0; i<notes_data.length; i++) {
+                       assertEquals(notes_data[i],b[i]);
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java
new file mode 100644 (file)
index 0000000..e934b25
--- /dev/null
@@ -0,0 +1,54 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.usermodel;
+
+
+import junit.framework.TestCase;
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+
+/**
+ * Tests that SlideShow returns the right number of Sheets and MetaSheets
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestCounts extends TestCase {
+       // SlideShow primed on the test data
+       private SlideShow ss;
+
+    public TestCounts() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               HSLFSlideShow hss = new HSLFSlideShow(filename);
+               ss = new SlideShow(hss);
+    }
+
+    public void testSheetsCount() throws Exception {
+               Slide[] slides = ss.getSlides();
+               // Two sheets, plus some crap related to the master sheet
+               assertEquals(3, slides.length);
+       }
+
+    public void testNotesCount() throws Exception {
+               Notes[] notes = ss.getNotes();
+               // Two sheets -> two notes, plus the notes on the slide master
+               assertEquals(3, notes.length);
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java
new file mode 100644 (file)
index 0000000..e4d6590
--- /dev/null
@@ -0,0 +1,61 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.usermodel;
+
+
+import junit.framework.TestCase;
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+
+/**
+ * Tests that SlideShow returns MetaSheets which have the right text in them
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestNotesText extends TestCase {
+       // SlideShow primed on the test data
+       private SlideShow ss;
+
+    public TestNotesText() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               HSLFSlideShow hss = new HSLFSlideShow(filename);
+               ss = new SlideShow(hss);
+    }
+
+    public void testNotesOne() throws Exception {
+               Notes notes = ss.getNotes()[1];
+
+               String[] expectText = new String[] {"These are the notes for page 1"};
+               assertEquals(expectText.length, notes.getTextRuns().length);
+               for(int i=0; i<expectText.length; i++) {
+                       assertEquals(expectText[i], notes.getTextRuns()[i].getText());
+               }
+    }
+
+       public void testNotesTwo() throws Exception {
+               Notes notes = ss.getNotes()[2];
+               String[] expectText = new String[] {"These are the notes on page two, again lacking formatting"};
+               assertEquals(expectText.length, notes.getTextRuns().length);
+               for(int i=0; i<expectText.length; i++) {
+                       assertEquals(expectText[i], notes.getTextRuns()[i].getText());
+               }
+       }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSheetText.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestSheetText.java
new file mode 100644 (file)
index 0000000..ada75fd
--- /dev/null
@@ -0,0 +1,61 @@
+
+/* ====================================================================
+   Copyright 2002-2004   Apache Software Foundation
+
+   Licensed 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.usermodel;
+
+
+import junit.framework.TestCase;
+import org.apache.poi.hslf.*;
+import org.apache.poi.hslf.model.*;
+
+/**
+ * Tests that SlideShow returns Sheets which have the right text in them
+ *
+ * @author Nick Burch (nick at torchbox dot com)
+ */
+public class TestSheetText extends TestCase {
+       // SlideShow primed on the test data
+       private SlideShow ss;
+
+    public TestSheetText() throws Exception {
+               String dirname = System.getProperty("HSLF.testdata.path");
+               String filename = dirname + "/basic_test_ppt_file.ppt";
+               HSLFSlideShow hss = new HSLFSlideShow(filename);
+               ss = new SlideShow(hss);
+    }
+
+    public void testSheetOne() throws Exception {
+               Sheet slideOne = ss.getSlides()[0];
+
+               String[] expectText = new String[] {"This is a test title","This is a test subtitle\nThis is on page 1"};
+               assertEquals(expectText.length, slideOne.getTextRuns().length);
+               for(int i=0; i<expectText.length; i++) {
+                       assertEquals(expectText[i], slideOne.getTextRuns()[i].getText());
+               }
+    }
+
+       public void testSheetTwo() throws Exception {
+               Sheet slideTwo = ss.getSlides()[1];
+               String[] expectText = new String[] {"This is the title on page 2","This is page two\nIt has several blocks of text\nNone of them have formatting"};
+               assertEquals(expectText.length, slideTwo.getTextRuns().length);
+               for(int i=0; i<expectText.length; i++) {
+                       assertEquals(expectText[i], slideTwo.getTextRuns()[i].getText());
+               }
+       }
+}