PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/ppt/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java

https://github.com/minstrelsy/POI-Android
Java | 687 lines | 312 code | 83 blank | 292 comment | 41 complexity | 40d5716cbdc5b9b544fff845bb8e0c6f MD5 | raw file
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hslf;
  16. import java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.FileInputStream;
  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.HashMap;
  26. import java.util.Hashtable;
  27. import java.util.List;
  28. import org.apache.poi.POIDocument;
  29. import org.apache.poi.hpsf.Util;
  30. import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
  31. import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
  32. import org.apache.poi.hslf.exceptions.HSLFException;
  33. import org.apache.poi.hslf.record.CurrentUserAtom;
  34. import org.apache.poi.hslf.record.ExOleObjStg;
  35. import org.apache.poi.hslf.record.PersistPtrHolder;
  36. import org.apache.poi.hslf.record.PersistRecord;
  37. import org.apache.poi.hslf.record.PositionDependentRecord;
  38. import org.apache.poi.hslf.record.Record;
  39. import org.apache.poi.hslf.record.UserEditAtom;
  40. import org.apache.poi.hslf.usermodel.ObjectData;
  41. import org.apache.poi.hslf.usermodel.PictureData;
  42. import org.apache.poi.poifs.filesystem.DirectoryNode;
  43. import org.apache.poi.poifs.filesystem.DocumentEntry;
  44. import org.apache.poi.poifs.filesystem.DocumentInputStream;
  45. import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
  46. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  47. import org.apache.poi.util.LittleEndian;
  48. import org.apache.poi.util.LittleEndianInputStream;
  49. import org.apache.poi.util.POILogFactory;
  50. import org.apache.poi.util.POILogger;
  51. import com.poi.poiandroid.Utils;
  52. import android.graphics.Bitmap;
  53. import android.graphics.Bitmap.CompressFormat;
  54. import android.graphics.BitmapFactory;
  55. import android.util.Log;
  56. /**
  57. * This class contains the main functionality for the Powerpoint file
  58. * "reader". It is only a very basic class for now
  59. *
  60. * @author Nick Burch
  61. */
  62. public final class HSLFSlideShow extends POIDocument {
  63. private static final String TAG = HSLFSlideShow.class.getSimpleName();
  64. // For logging
  65. private POILogger logger = POILogFactory.getLogger(this.getClass());
  66. // Holds metadata on where things are in our document
  67. private CurrentUserAtom currentUser;
  68. // Low level contents of the file
  69. private byte[] _docstream;
  70. // Low level contents
  71. private Record[] _records;
  72. // Raw Pictures contained in the pictures stream
  73. private List<PictureData> _pictures;
  74. // Embedded objects stored in storage records in the document stream, lazily populated.
  75. private ObjectData[] _objects;
  76. /**
  77. * Returns the underlying POIFSFileSystem for the document
  78. * that is open.
  79. */
  80. protected POIFSFileSystem getPOIFSFileSystem() {
  81. return directory.getFileSystem();
  82. }
  83. /**
  84. * Returns the directory in the underlying POIFSFileSystem for the
  85. * document that is open.
  86. */
  87. protected DirectoryNode getPOIFSDirectory() {
  88. return directory;
  89. }
  90. /**
  91. * Constructs a Powerpoint document from fileName. Parses the document
  92. * and places all the important stuff into data structures.
  93. *
  94. * @param fileName The name of the file to read.
  95. * @throws IOException if there is a problem while parsing the document.
  96. */
  97. public HSLFSlideShow(String fileName) throws IOException
  98. {
  99. this(new FileInputStream(fileName));
  100. }
  101. /**
  102. * Constructs a Powerpoint document from an input stream. Parses the
  103. * document and places all the important stuff into data structures.
  104. *
  105. * @param inputStream the source of the data
  106. * @throws IOException if there is a problem while parsing the document.
  107. */
  108. public HSLFSlideShow(InputStream inputStream) throws IOException {
  109. //do Ole stuff
  110. this(new POIFSFileSystem(inputStream));
  111. }
  112. /**
  113. * Constructs a Powerpoint document from a POIFS Filesystem. Parses the
  114. * document and places all the important stuff into data structures.
  115. *
  116. * @param filesystem the POIFS FileSystem to read from
  117. * @throws IOException if there is a problem while parsing the document.
  118. */
  119. public HSLFSlideShow(POIFSFileSystem filesystem) throws IOException
  120. {
  121. this(filesystem.getRoot());
  122. }
  123. /**
  124. * Constructs a Powerpoint document from a POIFS Filesystem. Parses the
  125. * document and places all the important stuff into data structures.
  126. *
  127. * @param filesystem the POIFS FileSystem to read from
  128. * @throws IOException if there is a problem while parsing the document.
  129. */
  130. public HSLFSlideShow(NPOIFSFileSystem filesystem) throws IOException
  131. {
  132. this(filesystem.getRoot());
  133. }
  134. /**
  135. * Constructs a Powerpoint document from a specific point in a
  136. * POIFS Filesystem. Parses the document and places all the
  137. * important stuff into data structures.
  138. *
  139. * @deprecated Use {@link #HSLFSlideShow(DirectoryNode)} instead
  140. * @param dir the POIFS directory to read from
  141. * @param filesystem the POIFS FileSystem to read from
  142. * @throws IOException if there is a problem while parsing the document.
  143. */
  144. @Deprecated
  145. public HSLFSlideShow(DirectoryNode dir, POIFSFileSystem filesystem) throws IOException
  146. {
  147. this(dir);
  148. }
  149. /**
  150. * Constructs a Powerpoint document from a specific point in a
  151. * POIFS Filesystem. Parses the document and places all the
  152. * important stuff into data structures.
  153. *
  154. * @param dir the POIFS directory to read from
  155. * @throws IOException if there is a problem while parsing the document.
  156. */
  157. public HSLFSlideShow(DirectoryNode dir) throws IOException
  158. {
  159. super(dir);
  160. // First up, grab the "Current User" stream
  161. // We need this before we can detect Encrypted Documents
  162. readCurrentUserStream();
  163. // Next up, grab the data that makes up the
  164. // PowerPoint stream
  165. readPowerPointStream();
  166. // Check to see if we have an encrypted document,
  167. // bailing out if we do
  168. boolean encrypted = EncryptedSlideShow.checkIfEncrypted(this);
  169. if(encrypted) {
  170. throw new EncryptedPowerPointFileException("Encrypted PowerPoint files are not supported");
  171. }
  172. // Now, build records based on the PowerPoint stream
  173. buildRecords();
  174. // Look for any other streams
  175. readOtherStreams();
  176. }
  177. /**
  178. * Constructs a new, empty, Powerpoint document.
  179. */
  180. public static final HSLFSlideShow create() {
  181. InputStream is = HSLFSlideShow.class.getResourceAsStream("data/empty.ppt");
  182. if (is == null) {
  183. throw new RuntimeException("Missing resource 'empty.ppt'");
  184. }
  185. try {
  186. return new HSLFSlideShow(is);
  187. } catch (IOException e) {
  188. throw new RuntimeException(e);
  189. }
  190. }
  191. /**
  192. * Extracts the main PowerPoint document stream from the
  193. * POI file, ready to be passed
  194. *
  195. * @throws IOException
  196. */
  197. private void readPowerPointStream() throws IOException
  198. {
  199. // Get the main document stream
  200. DocumentEntry docProps =
  201. (DocumentEntry)directory.getEntry("PowerPoint Document");
  202. // Grab the document stream
  203. _docstream = new byte[docProps.getSize()];
  204. directory.createDocumentInputStream("PowerPoint Document").read(_docstream);
  205. }
  206. /**
  207. * Builds the list of records, based on the contents
  208. * of the PowerPoint stream
  209. */
  210. private void buildRecords()
  211. {
  212. // The format of records in a powerpoint file are:
  213. // <little endian 2 byte "info">
  214. // <little endian 2 byte "type">
  215. // <little endian 4 byte "length">
  216. // If it has a zero length, following it will be another record
  217. // <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz>
  218. // If it has a length, depending on its type it may have children or data
  219. // If it has children, these will follow straight away
  220. // <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>>
  221. // If it has data, this will come straigh after, and run for the length
  222. // <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd>
  223. // All lengths given exclude the 8 byte record header
  224. // (Data records are known as Atoms)
  225. // Document should start with:
  226. // 0F 00 E8 03 ## ## ## ##
  227. // (type 1000 = document, info 00 0f is normal, rest is document length)
  228. // 01 00 E9 03 28 00 00 00
  229. // (type 1001 = document atom, info 00 01 normal, 28 bytes long)
  230. // 80 16 00 00 E0 10 00 00 xx xx xx xx xx xx xx xx
  231. // 05 00 00 00 0A 00 00 00 xx xx xx
  232. // (the contents of the document atom, not sure what it means yet)
  233. // (records then follow)
  234. // When parsing a document, look to see if you know about that type
  235. // of the current record. If you know it's a type that has children,
  236. // process the record's data area looking for more records
  237. // If you know about the type and it doesn't have children, either do
  238. // something with the data (eg TextRun) or skip over it
  239. // If you don't know about the type, play safe and skip over it (using
  240. // its length to know where the next record will start)
  241. //
  242. _records = read(_docstream, (int)currentUser.getCurrentEditOffset());
  243. }
  244. private Record[] read(byte[] docstream, int usrOffset){
  245. ArrayList<Integer> lst = new ArrayList<Integer>();
  246. HashMap<Integer,Integer> offset2id = new HashMap<Integer,Integer>();
  247. while (usrOffset != 0){
  248. UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
  249. lst.add(Integer.valueOf(usrOffset));
  250. int psrOffset = usr.getPersistPointersOffset();
  251. PersistPtrHolder ptr = (PersistPtrHolder)Record.buildRecordAtOffset(docstream, psrOffset);
  252. lst.add(Integer.valueOf(psrOffset));
  253. Hashtable<Integer,Integer> entries = ptr.getSlideLocationsLookup();
  254. for(Integer id : entries.keySet()) {
  255. Integer offset = entries.get(id);
  256. lst.add(offset);
  257. offset2id.put(offset, id);
  258. }
  259. usrOffset = usr.getLastUserEditAtomOffset();
  260. }
  261. //sort found records by offset.
  262. //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
  263. Integer a[] = lst.toArray(new Integer[lst.size()]);
  264. Arrays.sort(a);
  265. Record[] rec = new Record[lst.size()];
  266. for (int i = 0; i < a.length; i++) {
  267. Integer offset = a[i];
  268. rec[i] = Record.buildRecordAtOffset(docstream, offset.intValue());
  269. if(rec[i] instanceof PersistRecord) {
  270. PersistRecord psr = (PersistRecord)rec[i];
  271. Integer id = offset2id.get(offset);
  272. psr.setPersistId(id.intValue());
  273. }
  274. }
  275. return rec;
  276. }
  277. /**
  278. * Find the "Current User" stream, and load it
  279. */
  280. private void readCurrentUserStream() {
  281. try {
  282. currentUser = new CurrentUserAtom(directory);
  283. } catch(IOException ie) {
  284. logger.log(POILogger.ERROR, "Error finding Current User Atom:\n" + ie);
  285. currentUser = new CurrentUserAtom();
  286. }
  287. }
  288. /**
  289. * Find any other streams from the filesystem, and load them
  290. */
  291. private void readOtherStreams() {
  292. // Currently, there aren't any
  293. }
  294. /**
  295. * Find and read in pictures contained in this presentation.
  296. * This is lazily called as and when we want to touch pictures.
  297. */
  298. private void readPictures() throws IOException {
  299. _pictures = new ArrayList<PictureData>();
  300. // byte[] pictstream;
  301. // TODO: 图片太大
  302. DocumentInputStream is = null;
  303. int byteLen = 0;
  304. try {
  305. DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
  306. byteLen = entry.getSize();
  307. // pictstream = new byte[entry.getSize()];
  308. is = directory.createDocumentInputStream("Pictures");
  309. // is.read(pictstream);
  310. } catch (FileNotFoundException e){
  311. // Silently catch exceptions if the presentation doesn't
  312. // contain pictures - will use a null set instead
  313. return;
  314. }
  315. int pos = 0;
  316. // An empty picture record (length 0) will take up 8 bytes
  317. while (pos <= (byteLen-8)) {
  318. int offset = pos;
  319. // Image signature
  320. // int signature = LittleEndian.getUShort(pictstream, pos);
  321. int signature = LittleEndian.readUShort(is);
  322. pos += LittleEndian.SHORT_SIZE;
  323. // Image type + 0xF018
  324. // int type = LittleEndian.getUShort(pictstream, pos);
  325. int type = LittleEndian.readUShort(is);
  326. pos += LittleEndian.SHORT_SIZE;
  327. // Image size (excluding the 8 byte header)
  328. // int imgsize = LittleEndian.getInt(pictstream, pos);
  329. int imgsize = LittleEndian.readInt(is);
  330. pos += LittleEndian.INT_SIZE;
  331. // When parsing the BStoreDelay stream, [MS-ODRAW] says that we
  332. // should terminate if the type isn't 0xf007 or 0xf018->0xf117
  333. if (!((type == 0xf007) || (type >= 0xf018 && type <= 0xf117)))
  334. break;
  335. // The image size must be 0 or greater
  336. // (0 is allowed, but odd, since we do wind on by the header each
  337. // time, so we won't get stuck)
  338. if(imgsize < 0) {
  339. throw new CorruptPowerPointFileException("The file contains a picture, at position " + _pictures.size() + ", which has a negatively sized data length, so we can't trust any of the picture data");
  340. }
  341. // If they type (including the bonus 0xF018) is 0, skip it
  342. if(type == 0) {
  343. logger.log(POILogger.ERROR, "Problem reading picture: Invalid image type 0, on picture with length " + imgsize + ".\nYou document will probably become corrupted if you save it!");
  344. logger.log(POILogger.ERROR, "" + pos);
  345. } else {
  346. // Build the PictureData object from the data
  347. try {
  348. PictureData pict = PictureData.create(type - 0xF018);
  349. Log.d("PictureData", "PictureData imgsize: " + Utils.fileBytesShow(imgsize));
  350. // int bmpOffset = 17;
  351. //
  352. // final BitmapFactory.Options options = new BitmapFactory.Options();
  353. // options.inJustDecodeBounds = true;
  354. // BitmapFactory.decodeByteArray(pictstream, pos + bmpOffset, imgsize, options);
  355. //
  356. // int height = options.outHeight;
  357. // int width = options.outWidth;
  358. // Log.d("PictureData", "PictureData height: " + height + ", height: " + height);
  359. //
  360. // if (height == -1 && width == -1) {
  361. // bmpOffset += 16;
  362. // BitmapFactory.decodeByteArray(pictstream, pos + bmpOffset, imgsize, options);
  363. // }
  364. //
  365. // height = options.outHeight;
  366. // width = options.outWidth;
  367. //
  368. // Log.d("PictureData", "+16 PictureData height: " + height + ", height: " + height);
  369. //
  370. // options.inSampleSize = Utils.calculateInSampleSize(options, 480, 800);
  371. // options.inJustDecodeBounds = false;
  372. // Bitmap bm = BitmapFactory.decodeByteArray(pictstream, pos + bmpOffset, imgsize, options);
  373. //
  374. // if (bm != null) {
  375. // ByteArrayOutputStream bais = new ByteArrayOutputStream();
  376. // bm.compress(CompressFormat.PNG, 80, bais);
  377. // byte[] newimgdata = bais.toByteArray();
  378. //
  379. // Log.d("PictureData", "PictureData new imgsize: "
  380. // + newimgdata.length);
  381. //
  382. // pict.setRawData(newimgdata);
  383. // }
  384. // if (imgsize > 1024 * 1024) {
  385. // Log.w(TAG, "imgsize > 1024 * 1024");
  386. // return;
  387. // }
  388. // byte[] imgdata = new byte[imgsize];
  389. // System.arraycopy(pictstream, pos, imgdata, 0, imgdata.length);
  390. // pict.setRawData(imgdata);
  391. pict.setSteram(is, pos, imgsize);
  392. pict.setOffset(offset);
  393. _pictures.add(pict);
  394. } catch(IllegalArgumentException e) {
  395. logger.log(POILogger.ERROR, "Problem reading picture: " + e + "\nYou document will probably become corrupted if you save it!");
  396. }
  397. }
  398. pos += imgsize;
  399. is.skip(imgsize);
  400. }
  401. }
  402. /**
  403. * Writes out the slideshow file the is represented by an instance
  404. * of this class.
  405. * It will write out the common OLE2 streams. If you require all
  406. * streams to be written out, pass in preserveNodes
  407. * @param out The OutputStream to write to.
  408. * @throws IOException If there is an unexpected IOException from
  409. * the passed in OutputStream
  410. */
  411. public void write(OutputStream out) throws IOException {
  412. // Write out, but only the common streams
  413. write(out,false);
  414. }
  415. /**
  416. * Writes out the slideshow file the is represented by an instance
  417. * of this class.
  418. * If you require all streams to be written out (eg Marcos, embeded
  419. * documents), then set preserveNodes to true
  420. * @param out The OutputStream to write to.
  421. * @param preserveNodes Should all OLE2 streams be written back out, or only the common ones?
  422. * @throws IOException If there is an unexpected IOException from
  423. * the passed in OutputStream
  424. */
  425. public void write(OutputStream out, boolean preserveNodes) throws IOException {
  426. // Get a new Filesystem to write into
  427. POIFSFileSystem outFS = new POIFSFileSystem();
  428. // The list of entries we've written out
  429. List<String> writtenEntries = new ArrayList<String>(1);
  430. // Write out the Property Streams
  431. writeProperties(outFS, writtenEntries);
  432. // For position dependent records, hold where they were and now are
  433. // As we go along, update, and hand over, to any Position Dependent
  434. // records we happen across
  435. Hashtable<Integer,Integer> oldToNewPositions = new Hashtable<Integer,Integer>();
  436. // First pass - figure out where all the position dependent
  437. // records are going to end up, in the new scheme
  438. // (Annoyingly, some powerpoing files have PersistPtrHolders
  439. // that reference slides after the PersistPtrHolder)
  440. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  441. for(int i=0; i<_records.length; i++) {
  442. if(_records[i] instanceof PositionDependentRecord) {
  443. PositionDependentRecord pdr = (PositionDependentRecord)_records[i];
  444. int oldPos = pdr.getLastOnDiskOffset();
  445. int newPos = baos.size();
  446. pdr.setLastOnDiskOffset(newPos);
  447. oldToNewPositions.put(Integer.valueOf(oldPos),Integer.valueOf(newPos));
  448. //System.out.println(oldPos + " -> " + newPos);
  449. }
  450. // Dummy write out, so the position winds on properly
  451. _records[i].writeOut(baos);
  452. }
  453. // No go back through, actually writing ourselves out
  454. baos.reset();
  455. for(int i=0; i<_records.length; i++) {
  456. // For now, we're only handling PositionDependentRecord's that
  457. // happen at the top level.
  458. // In future, we'll need the handle them everywhere, but that's
  459. // a bit trickier
  460. if(_records[i] instanceof PositionDependentRecord) {
  461. // We've already figured out their new location, and
  462. // told them that
  463. // Tell them of the positions of the other records though
  464. PositionDependentRecord pdr = (PositionDependentRecord)_records[i];
  465. pdr.updateOtherRecordReferences(oldToNewPositions);
  466. }
  467. // Whatever happens, write out that record tree
  468. _records[i].writeOut(baos);
  469. }
  470. // Update our cached copy of the bytes that make up the PPT stream
  471. _docstream = baos.toByteArray();
  472. // Write the PPT stream into the POIFS layer
  473. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  474. outFS.createDocument(bais,"PowerPoint Document");
  475. writtenEntries.add("PowerPoint Document");
  476. // Update and write out the Current User atom
  477. int oldLastUserEditAtomPos = (int)currentUser.getCurrentEditOffset();
  478. Integer newLastUserEditAtomPos = (Integer)oldToNewPositions.get(Integer.valueOf(oldLastUserEditAtomPos));
  479. if(newLastUserEditAtomPos == null) {
  480. throw new HSLFException("Couldn't find the new location of the UserEditAtom that used to be at " + oldLastUserEditAtomPos);
  481. }
  482. currentUser.setCurrentEditOffset(newLastUserEditAtomPos.intValue());
  483. currentUser.writeToFS(outFS);
  484. writtenEntries.add("Current User");
  485. // Write any pictures, into another stream
  486. if(_pictures == null) {
  487. readPictures();
  488. }
  489. if (_pictures.size() > 0) {
  490. ByteArrayOutputStream pict = new ByteArrayOutputStream();
  491. for (PictureData p : _pictures) {
  492. p.write(pict);
  493. }
  494. outFS.createDocument(
  495. new ByteArrayInputStream(pict.toByteArray()), "Pictures"
  496. );
  497. writtenEntries.add("Pictures");
  498. }
  499. // If requested, write out any other streams we spot
  500. if(preserveNodes) {
  501. copyNodes(directory.getFileSystem(), outFS, writtenEntries);
  502. }
  503. // Send the POIFSFileSystem object out to the underlying stream
  504. outFS.writeFilesystem(out);
  505. }
  506. /* ******************* adding methods follow ********************* */
  507. /**
  508. * Adds a new root level record, at the end, but before the last
  509. * PersistPtrIncrementalBlock.
  510. */
  511. public synchronized int appendRootLevelRecord(Record newRecord) {
  512. int addedAt = -1;
  513. Record[] r = new Record[_records.length+1];
  514. boolean added = false;
  515. for(int i=(_records.length-1); i>=0; i--) {
  516. if(added) {
  517. // Just copy over
  518. r[i] = _records[i];
  519. } else {
  520. r[(i+1)] = _records[i];
  521. if(_records[i] instanceof PersistPtrHolder) {
  522. r[i] = newRecord;
  523. added = true;
  524. addedAt = i;
  525. }
  526. }
  527. }
  528. _records = r;
  529. return addedAt;
  530. }
  531. /**
  532. * Add a new picture to this presentation.
  533. *
  534. * @return offset of this picture in the Pictures stream
  535. */
  536. public int addPicture(PictureData img) {
  537. // Process any existing pictures if we haven't yet
  538. if(_pictures == null) {
  539. try {
  540. readPictures();
  541. } catch(IOException e) {
  542. throw new CorruptPowerPointFileException(e.getMessage());
  543. }
  544. }
  545. // Add the new picture in
  546. int offset = 0;
  547. if(_pictures.size() > 0) {
  548. PictureData prev = _pictures.get(_pictures.size() - 1);
  549. offset = prev.getOffset() + prev.getRawData().length + 8;
  550. }
  551. img.setOffset(offset);
  552. _pictures.add(img);
  553. return offset;
  554. }
  555. /* ******************* fetching methods follow ********************* */
  556. /**
  557. * Returns an array of all the records found in the slideshow
  558. */
  559. public Record[] getRecords() { return _records; }
  560. /**
  561. * Returns an array of the bytes of the file. Only correct after a
  562. * call to open or write - at all other times might be wrong!
  563. */
  564. public byte[] getUnderlyingBytes() { return _docstream; }
  565. /**
  566. * Fetch the Current User Atom of the document
  567. */
  568. public CurrentUserAtom getCurrentUserAtom() { return currentUser; }
  569. /**
  570. * Return array of pictures contained in this presentation
  571. *
  572. * @return array with the read pictures or <code>null</code> if the
  573. * presentation doesn't contain pictures.
  574. */
  575. public PictureData[] getPictures() {
  576. if(_pictures == null) {
  577. try {
  578. readPictures();
  579. } catch(IOException e) {
  580. throw new CorruptPowerPointFileException(e.getMessage());
  581. }
  582. }
  583. return _pictures.toArray(new PictureData[_pictures.size()]);
  584. }
  585. /**
  586. * Gets embedded object data from the slide show.
  587. *
  588. * @return the embedded objects.
  589. */
  590. public ObjectData[] getEmbeddedObjects() {
  591. if (_objects == null) {
  592. List<ObjectData> objects = new ArrayList<ObjectData>();
  593. for (int i = 0; i < _records.length; i++) {
  594. if (_records[i] instanceof ExOleObjStg) {
  595. objects.add(new ObjectData((ExOleObjStg) _records[i]));
  596. }
  597. }
  598. _objects = objects.toArray(new ObjectData[objects.size()]);
  599. }
  600. return _objects;
  601. }
  602. }