git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353701 13f79535-47bb-0310-9956-ffa450edef68tags/BEFORE_RICHTEXT
@@ -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; } | |||
} |
@@ -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 + "''"); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -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 + "''"); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -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(""); | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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(""); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; } | |||
} |
@@ -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++; | |||
} | |||
} | |||
} | |||
} |
@@ -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; } | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; } | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; } | |||
} |
@@ -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) {} | |||
} |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -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]); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |